1
成员初始化
Java 尽量保证所有变量在使用前都能得到恰当的初始化。
1.1
局部变量
以编译时错误的方式呈现。编译器可以为 i 赋一个默认值,但是未初始化的局部变量更可能是程序员的疏忽,所以强制程序员提供一个初始值,往往能帮助找出程序里的 bug。
1.2
类的成员变量
类的成员变量是基本类型时,每个基本类型数据成员保证都会有一个初始值。
引用的默认值为 null。
1.3
指定初始化
给一个变量赋初值,一种很直接的方法是在定义类成员变量的地方为其赋值。
也可以通过调用某个方法来提供初值:
2
构造器初始化
灵活的方式。因为可以在运行时调用方法进行初始化。但这无法阻止自动初始化,它会在构造器被调用前发生。因此,如果使用如下代码:
public class Counter {
int i;
Counter() {
i = 7;
}
// ...
}
i 首先会被初始化为 0,然后变为 7。
这种情形对于所有的基本类型和引用及在定义时已指定初值的变量,都成立。
因此,编译器不会强制你要在构造器的某个地方或在使用它们前初始化——初始化早已得到了保证。
2.1
初始化的顺序
在类中变量定义的顺序决定了它们初始化的顺序。
即使将变量的定义分散在了各个方法的定义之间,变量们仍会在任何方法(包括构造器)被调用前得到初始化。
2.2
static数据的初始化
静态数据只占用一份存储区域。
static 不能用于局部变量,只能作用于属字段。
如果在定义时进行初始化,那么静态变量看起来就跟非静态变量一样。
静态初始化只有在必要时刻才会进行。如果不创建实例,也不引用静态类,那么静态的类对象永远不会被创建。只有在第一个实例对象被创建(或被访问)时,它们才会被初始化。
初始化的顺序是先静态对象(如果之前没有被初始化),然后是非静态对象。
2.3
创建对象的过程
假设有个类 Dog,总结:
new Dog()
创建,首先在堆给 Dog 分配存储空间2.4
显式的静态初始化
可以将一组静态初始化动作放在静态块,一段跟在 static 关键字后面的代码块。当首次创建这个类的对象或首次访问这个类的静态成员(甚至不需要创建该类的对象)时
2.5
非静态实例初始化
实例初始化的类似语法,初始化每个对象的非静态变量。
看起来像静态代码块,只不过没static。实例初始化子句是在两个构造器之前执行的。
public class Mugs {
Mug mug1;
Mug mug2;
{
mug1 = new Mug(1);
mug2 = new Mug(2);
System.out.println("mug1 & mug2 initialized");
}
}
3
数组初始化
数组是相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。通过方括号下标操作符 [] 来定义和使用。
要定义一个数组引用,只需在类型名加上方括号,方括号也可在标识符后。
int[] a1; // 更合理,表明是"一个 int 数组类型
int a1[]; // 符合 C/C++ 习惯
编译器不允许指定数组的大小。你所有的只是对数组的一个引用(已为该引用分配了足够存储空间),还没给数组对象分配空间。
为了给数组创建相应的存储空间,必须写初始化表达式。对于数组,初始化动作可以出现在代码的任何地方。
也可以使用一种特殊的初始化表达式,必须在创建数组地方。由一对花括号括起来的值组成。这时,存储空间的分配(相当于 new) 由编译器负责。
int[] a1 = {1, 2, 3, 4, 5};
在 Java 中可以将一个数组赋值给另一个数组,其实真正做的只是复制了一个引用。
数组都有一个固定成员 length:数组有多少元素,无法修改。
发生越界访问时:
3.1
动态数组创建
不确定数组中需要多少元素怎么办?
可以直接使用 new 在数组中创建元素。下面例子中,尽管创建的是基本类型数组,new 仍然可以工作(不能用 new 创建单个的基本类型数组):
int[] a = new int[rand.nextInt(20)];
创建了一个非基本类型的数组,那么其实创建的是一个引用数组。如果忘记了创建对象,但试图使用数组中的空引用,就会在运行时产生异常。
可以用花括号括起来的列表来初始化数组:
Integer[] a = {
1, 2,
3, // Autoboxing 初始化列表的最后逗号可选
// 维护长列表更容易,但只能用于数组定义处。
};
Integer[] b = new Integer[] {
1, 2,
3, // Autoboxing
};
第二种和第三种形式可以用在任何地方,甚至用在方法的内部。