Java中数组初始化深度分析

本文深入探讨Java中的数组初始化,分析静态和动态初始化的区别与应用场景。Java数组长度在编译时确定,动态初始化常通过new关键字完成。对于基本类型,未初始化的数组元素会有默认值,而对象数组必须显式创建对象。文章还讨论了使用花括号初始化数组的两种形式,以及避免数组操作错误的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组初始化

在C中初始化数组极易出错,而且相当麻烦。C++通过“集合初始化”使其安全。Java则没有像C++那样的“集合”概念,因为Java中的所有东西都是对象。但它确实有自己的数组,通过数组初始化来提供支持。

数组代表一系列对象或者基本数据类型,定义数组,有两种方式

int[] a1;

int a1[];

后一种方式与C和C++习惯的格式是一致的,但是,最“通顺”的还是前一种语法,因为它指出类型是“一个int数组”。

知道数组的定义有两种方式即可,但自己在定义的时候选择第一种,忽略第二中方式!

在定义数组的过程中,编译器不允许我们告诉它有多大。此时,我们拥有的一切就是指向数组的一个句柄(引用,句柄这个词来自《编程思想》这本书,其实就是引用的意思!),而且尚未给数组分配任何空间。为了给数组创建相应的存储空间,必须编写一个初始化表达式。

对于数组,初始化工作可在代码的任何地方出现,但也可以使用一种特殊的初始化表达式,它必须在数组创建的地方出现。这种特殊的初始化实际上就是静态初始化

int[] a1={1,2,3,4,5};

存储空间的分配(等价于使用new)将由编译器在这种情况下进行。

//:Arrays.java
//Arrays of primitives.

public class Arrays{
    public static void main(String[] args){
        int[] a1={1,2,3,4,5};
        int[] a2;
        a2=a1;
        for(int i=0;i<a2.length;i++){
            a2[i]++;
            prt("a2["+i+"]="+a2[i]);
        }
        for(int i=0;i<a1.length;i++){
            prt("a1["+i+"]="+a1[i]);
        }
        static void prt(String s){
            System.out.println(s);
        }
    }
}

结果为:

a2[0]=2
a2[1]=3
a2[2]=4
a2[3]=5
a2[4]=6
a1[0]=2
a1[1]=3
a1[2]=4
a1[3]=5
a1[4]=6

可以看到,a1获得了一个初始值,而a2没有;a2将在以后赋值----这种情况下是赋给另一个数组。所有数组都有一个本质成员(无论它们是对象数组还是基本类型数组),可对其进行查询—但不是改变,从而获知数组内包含了多少个元素。这个成员就是length。与C 和C++类似,由于Java 数组从元素0 开始计数,所以能索引的最大元素编号是“length-1”。如超出边界,C 和C++会“默默”地接受,并允许我们胡乱使用自己的内存,这正是许多程序错误的根源。然而,Java 可保留我们这受这一问题的损害,方法是一旦超过边界,就生成一个运行期错误(即一个“违例”)。当然,由于需要检查每个数组的访问,所以会消耗一定的时间和多余的代码量,而且没有办法把它关闭。这意味着数组访问可能成为程序效率低下的重要原因——如果它们在关键的场合进行。但考虑到因特网访问的安全,以及程序员的编程效率,Java 设计人员还是应该把它看作是值得的。

程序编写期间,如果不知道在自己的数组里需要多少元素,那么又该怎么办呢?此时,只需简单地用new 在数组里创建元素。在这里,即使准备创建的是一个基本数据类型的数组,new 也能正常地工作(new 不会创建非数组的基本类型);

这里,通过new进行数组初始化,是一种动态初始化

动态初始化也分以下几类:

Object[] arr3 = new Object[]{"a","b","c"};
    Object[] arr2 = new Object[3];  
            arr2[1] = "a";
            arr2[2] = "b";
            arr2[3] = "c";
            //先确认元素个数,一般情况下习惯使用动态创建方式 比较灵活 可以先规定元素个数 后对每个元素进行赋值
    Object[] arr4 = null;  
    arr4 = new Object[3];
            arr4[1] = "a";
            arr4[2] = "b";
            arr4[3] = "c";
            //直接给arr4初始化为null,然后要使用的时候再创建一个新的数组new Object[3],让arr4指向新数组的地址,然后再依次赋值

了解的数组的两种初始化方式,在看下面这个程序:

//:ArrayNew.java
//Creating arrays with new.
import java.util.*;

public class ArrayNew{
    static Random rand=new Random();
    static int pRand(int mod){
        return Math.abs(rand.nextInt())%mod+1;
    }
    public static void main(String[] args){
        int[] a;
        a=new int[pRand(20)];//先初始化,在动态分配内存
        prt("length of a="+a.length);
        for(int i=0;i<a.length;i++){
            prt("a["+i+"]="+a[i]);
        }
    }
 
    static void prt(String s){
            System.out.println(s);
    }
}

数组的定义是在编译期间确定的,一般来说,数组的大小也是编译期间确定的,但是这个程序中,数组的大小由一个方法提供,该数组的大小也在运行期间确定,对于基本数据类型,JVM会自动初始化成“空”值(对于数值,空值就是零;对于char,它是null ;而对于boolean,它却是false)。

C语言中,数组的长度是在编译时确定,这样就能为它分配内寸,但是不会初始化.

若操作的是一个非基本类型对象的数组,那么无论如何都要使用new。在这里,我们会再一次遇到句柄问题,因为我们创建的是一个句柄数组。请大家观察封装器类型Integer,它是一个类,而非基本数据类型:

//:ArrayClassObj.java
//Creating an arrays of non-primitive objects.
import java.util.*;

public class ArrayClassObj{
    static Random rand=new Random();
    static int pRand(int mod){
        return Math.abs(rand.nextInt())%mod+1;
    }
    public static void main(String[] args){
        Integer[] a=new Integer[pFr];
        a=new int[pRand(20)];//先初始化,在动态分配内存
        prt("length of a="+a.length);
        for(int i=0;i<a.length;i++){
            prt("a["+i+"]="+a[i]);
        }
    }
 
    static void prt(String s){
            System.out.println(s);
    }
}

在这儿,甚至在new 调用后才开始创建数组:
Integer[] a = new Integer[pRand(20)];
它只是一个句柄数组,而且除非通过创建一个新的Integer 对象,从而初始化了对象句柄,否则初始化进程不会结束:
a[i] = new Integer(pRand(500));
但若忘记创建对象,就会在运行期试图读取空数组位置时获得一个“违例”错误。
下面让我们看看打印语句中String 对象的构成情况。大家可看到指向Integer 对象的句柄会自动转换,从而产生一个String,它代表着位于对象内部的值。
亦可用花括号封闭列表来初始化对象数组。可采用两种形式,第一种是Java 1.0 允许的唯一形式。第二种(等价)形式自Java 1.1 才开始提供支持:

//: ArrayInit.java
// Array initialization
public class ArrayInit {
	public static void main(String[] args) {
		Integer[] a = {
				new Integer(1),
				new Integer(2),
				new Integer(3),
		};
// Java 1.1 only:
		Integer[] b = new Integer[] {
				new Integer(1),
				new Integer(2),
				new Integer(3),
		};
	}
} ///:~

这种做法大多数时候都很有用,但限制也是最大的,因为数组的大小是在编译期间决定的。初始化列表的最后一个逗号是可选的(这一特性使长列表的维护变得更加容易)。
数组初始化的第二种形式(Java 1.1 开始支持)提供了一种更简便的语法,可创建和调用方法,获得与C 的“变量参数列表”(C 通常把它简称为“变参表”)一致的效果。这些效果包括未知的参数(自变量)数量
以及未知的类型(如果这样选择的话)。由于所有类最终都是从通用的根类Object 中继承的,所以能创建一个方法,令其获取一个Object 数组,并象下面这样调用它:

//: VarArgs.java
// Using the Java 1.1 array syntax to create
// variable argument lists
class A { int i; }
public class VarArgs {
	static void f(Object[] x) {
		for(int i = 0; i < x.length; i++)
			System.out.println(x[i]);
		}
	public static void main(String[] args) {
		f(new Object[] {
		new Integer(47), new VarArgs(),
		new Float(3.14), new Double(11.11) });
		f(new Object[] {"one", "two", "three" });
		f(new Object[] {new A(), new A(), new A()});
	}
} ///:~
问题1

首先因为数组初始化要使用的时候必须确定数组的长度,也就是说数组的长度是不可变的。
在JAVA中创建数组有两种方式 :
(1)静态创建

Object[] arr1 = {"a","b","c"};

(2)动态创建
1

Object[] arr3 = new Object[]{"a","b","c"};

2

    Object[] arr2 = new Object[3];  
            arr2[1] = "a";
            arr2[2] = "b";
            arr2[3] = "c";
            //先确认元素个数,一般情况下习惯使用动态创建方式 比较灵活 可以先规定元素个数 后对每个元素进行赋值

3

    Object[] arr4 = null;  
    arr4 = new Object[3];
            arr4[1] = "a";
            arr4[2] = "b";
            arr4[3] = "c";
            //直接给arr4初始化为null,然后要使用的时候再创建一个新的数组new Object[3],让arr4指向新数组的地址,然后再依次赋值

这种方法:

Object[] params = null;
params = {1, 2, 3, 4};

错误一:这是直接创建一个空数组params,也就是他就是空了,数组的长度不可改变,你这时候往里面加数据,一个空数组自然加不了任何东西,所以编译时就会出错。你应该跟我上面所说的第二种方法一样,重新创建一个长度为4的数组,把新数组的地址给params,也就是Object[] params = null; params = new Object[4];这样就可以创建。
错误二:数组的初始化只有在定义的时候可以连续添加,定义完

Object[] params = null;
params = new Object[4];

之后,你要向里面添加数据,就只能对着数组下标,一个一个添加。

params[1] = 1;
params[2] = 2;
params[23 = 3;
params[4] = 4;

而不能直接params = {1, 2, 3, 4};

问题2
if (array == null || 0 == array.length) {...} // 这种写法正确,因为执行到 “0 == array.length”则说明数组不为空,不会产生空指针异常。
if (0 == array.length || array == null) {...} // 这种写法可能会产生空指针异常。

问题分析:

int[] n; //只声明了一数组变量;
int[] nil = null; //声明一数组变量,并赋值 null,nil是一个数组类型的空引用,不指向任何对象;
int[] zero = new int[0]; //声明并创建一数组对象, zero是一个长度为0的数组,我们称之为“空数组”,空数组也是一个对象,只是包含元素个数为0;

对于上面三条语句,一个比一个做的动作多,系统占用也是后面的多:
语句一变量还没初始化,打印 n 会出错:“可能尚未初始化变量 n”;
语句二虽已初始化,打印“nil.length”会出现异常:NullPointerException;

语句三打印“nil.length”是0,数组内还没有元素。
假设一个方法返回一个数组,如果它返回null,则调用方法必须先判断是否返回null,才能对放回数组进一步处理,而如果返回空数组,则无须null引用检查。鉴于此,返回数组的方法在没有结果时我们通常返回空数组,而不是null,这样做对于函数调用者的处理比较方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值