Java数组类型

在Java中,数组可以说是经常用到的,但关于数组是什么类型的这个问题,我却没有深入研究过。以下内容参考自《Java语言规范》,记录了数组的类型是什么?更进一步地说,数组对象的getClass()返回的是什么?

1.数组更详细的特点

数组具有以下几点特点:

a.在Java编程语言中,数组是动态创建的对象,可以被赋值给Object类型的变量。Object类的所有方法都可以在数组上调用。

b.数组对象包含大量的变量。

c.数组的所有元素都具有相同的类型,称为数组的元素类型。如果数组的元素类型为T,那么数组自身的类型就写作T[ ]。

这里针对数组的特点a进行详细说明:
下面代码片段中,会创建一个数组对象,并且这个对象可以赋值给Object对象。

int[] ai1 = {1,2};//隐式地创建一个新的数组对象
int[] ai = new int[2];//显式地创建一个数组对象
Object o = ai;//数组对象可以赋值给Object类型的变量
o = ai1;

通过上面的代码片段,再联系“数组是动态创建的对象”这句话,我们可以猜测:数组的类型很可能是运行时通过反射动态创建的,并且其类型是Object的子类。

既然知道数组具有特定类型了,那么这个类型具有什么成员呢?下面将进行说明。

2.数组类型的成员

数组类型的成员包括以下所有内容:

  • public final 域 length,它包含了数组的元素数量。length可以是正数或0。
  • public 方法 clone,它覆盖了Object类中的同名的方法,并且不会抛出任何受检异常。
    多维数组的克隆是浅复制,即它只创建单个新数组,子数组是共享的。
  • 所有从Object类继承而来的成员,Object中唯一没有被继承的方法就是clone方法。

因此,数组具有与下面的类相同的public域和方法:

class A<T> implements Cloneable,java.io.Serializable {
    public final int length = X;
    public T[] clone() {
        try{
            return (T[])super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e.getMessage());
        }
    }
}

注意:在上面例子中,如果数组真的以这种方式实现,那么其中向T[ ]的强制类型转换会产生非受检警告。

从上面可以知道,数组是可克隆的

class Test1 {
    public static void main(String[] args) {
        int ia1[] = {1,2};
        int ia2[] = ia1.clone();
        System.out.print((ia1 == ia2) + " ");
        ia1[1]++;
        System.out.println(ia2[1]);
    }
}

这个程序会产生下面的输出:
false 2
该输出展示了ia1和ia2所引用的数组元素是不同的变量。

下面的例子说明多维数组克隆后共享子数组:

class Test2 {
    public static void main(String[] args) throws Throwable {
        int ia[][] = { { 1, 2 }, null };
        int ja[][] = ia.clone();
        System.out.print((ia == ja) + " ");
        System.out.println(ia[0] == ja[0] && ia[1] == ja[1]);
    }
}

这个程序会产生下面的输出:
false true
该输出展示了 int[ ] 数组 ia[0] 和 int[ ] 数组 ja[0] 是同一个数组。

3.数组的Class对象

每个数组都与一个Class对象关联,并与其他具有相同元素类型的数组共享该对象。尽管数组类型不是类,但是每一个数组的Class对象起到的作用看起来都像是:

  • 每个数组类型的直接超类都是Object。
  • 每个数组类型都实现了Cloneable 接口和 java.io.Serializable 接口

    下面的代码用于测试上面的特点:

package com.test;

class A {

}

class B extends A implements Comparable<B>{

    @Override
    public int compareTo(B o) {
        return 0;
    }

}
public class Test2 {
    public static void main(String[] args) throws Throwable {
        B[] ba = new B[2];
        B b = new B();
        B[] ba1 = new B[2];
        //测试数组的Class对象是共享的
        System.out.println(ba == ba1);
        System.out.println(ba.getClass() == ba1.getClass());
        //测试数组ba和b的Class对象是否一样
        System.out.println(ba.getClass() + " | " + b.getClass());
        //测试数组ba和b的超类是否一样
        System.out.println(ba.getClass().getSuperclass() + " | " + b.getClass().getSuperclass());

        //测试数组ba和b实现的接口分别是什么
        for(Class<?>c : ba.getClass().getInterfaces())
            System.out.println("Superinterfaces: " + c);
        System.out.println("-----------");
        for(Class<?>c : b.getClass().getInterfaces())
            System.out.println("Superinterfaces: " + c);
    }
}

上面程序输出是:
false
true
class [Lcom.test.B; | class com.test.B
class java.lang.Object | class com.test.A
Superinterfaces: interface java.lang.Cloneable
Superinterfaces: interface java.io.Serializable
- - - - - - - - - - -
Superinterfaces: interface java.lang.Comparable

其中,字符串“[Lcom.test.B”是“元素类型为com.test.B的数组”的Class对象的运行时类型签名

根据上面的输出结果,可得出以下总结。

  • 数组的Class对象是共享的。

  • 虽然每个数组都与一个 Class 对象关联,但数组的Class对象并不等于数组元素的Class对象
    从上面这个输出可以看出:class [Lcom.test.B; | class com.test.B

  • 数组的类型是Object类的子类,并且数组的类型和数组元素的类型不一样。
    如上面的输出中, B[ ] 的超类是Object,而B的超类是A。B[ ]类型实现的是Cloneable 和 Serializable 接口,而B类实现的是Comparable 接口。

Java 中,数组类型转换是一个涉及数据结构与类型系统的问题。由于 Java 数组具有协变性(covariant),但不支持泛型协变,因此在进行数组类型转换时需要注意类型安全性。 ### 基本类型数组与包装类型数组之间的转换 #### 将 `int[]` 转换为 `Integer[]` 可以使用 Java 8 引入的 Stream API 来完成这一转换: ```java import java.util.Arrays; public class Main { public static void main(String[] args) { int[] array = new int[]{1, 2, 3, 4, 5}; Integer[] arrayInteger = Arrays.stream(array).boxed().toArray(Integer[]::new); System.out.println(Arrays.toString(arrayInteger)); // output: [1, 2, 3, 4, 5] } } ``` #### 将 `Integer[]` 转换为 `int[]` 同样可以借助 Stream 实现反向转换: ```java Integer[] arrayInteger = new Integer[]{1, 2, 3, 4, 5}; int[] array = Arrays.stream(arrayInteger).mapToInt(Integer::intValue).toArray(); System.out.println(Arrays.toString(array)); // output: [1, 2, 3, 4, 5] ``` ### 不同引用类型数组之间的转换 Java 支持在引用类型之间进行数组的强制类型转换,前提是这些类型之间存在合法的继承关系。 #### 合法的数组类型转换示例 ```java String[] strings = new String[]{"a", "b", "c"}; Object[] objects = strings; // 允许将子数组赋值给父数组引用 String[] againStrings = (String[]) objects; // 可以安全地向下转型 ``` #### 非法的数组类型转换示例 如果一个数组最初声明为 `Object[]` 类型,即使它包含的所有元素都是 `String` 对象,也不能将其转换回 `String[]` 类型: ```java Object[] objects = new Object[]{"a", "b", "c"}; String[] strings = (String[]) objects; // 运行时抛出 ClassCastException ``` ### 字符串数组转换为数值型数组 通过 `Stream` 和 `mapToXXX` 方法,可以将字符串数组转换为 `int[]`、`long[]` 或 `double[]` 等基本类型数组[^3]。 ```java String[] str = new String[]{"1", "2", "3", "4", "5"}; int[] ints = Arrays.stream(str).mapToInt(Integer::valueOf).toArray(); long[] longs = Arrays.stream(str).mapToLong(Long::valueOf).toArray(); double[] doubles = Arrays.stream(str).mapToDouble(Double::valueOf).toArray(); ``` ### 使用泛型集合辅助转换 对于更复杂的对象数组转换,例如从 `T[]` 转换为 `E[]`,可以通过先转换为 `List<E>` 再转换为数组的方式实现: ```java <T, E> E[] convertArray(T[] source, Function<T, E> converter, IntFunction<E[]> generator) { return Arrays.stream(source) .map(converter) .toArray(generator); } // 示例调用 Integer[] numbers = {1, 2, 3}; String[] stringNumbers = convertArray(numbers, Object::toString, String[]::new); ``` 上述方法利用了泛型和函数式编程特性,提供了一种通用的数组转换模式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值