成员初始化
局部变量须初始化,否则编译错误。
类的字段若为基本类型,则会自动初始化,char为空,对象引用为null,boolean为false,其余为0。
指定初始化
C++中所不允许,可直接在类的定义的时候给出初值,并可以通过函数等各种方式进行赋值。
public class InitialValues2 {
boolean bool = true;
char ch = 'x';
byte b = 47;
double d = 3.14159;
int i = f();
int j = g(i);
int f() { return 11; }
int g(int n) { return n * 10; }
}
构造器初始化
- 自动初始化在构造器初始化前发生,当然也发生在指定初始化前。
初始化顺序
- 在类的内部,定义变量的先后顺序决定了自动初始化的顺序,且均在构造器初始化前初始化。
静态数据的初始化
- 初始化的顺序为:先静态对象,再非静态对象。
- 对象创建过程:
- 构造器实际上也是静态方法,所以类型为 Dog 的一个对象首次创建时,或者 Dog 类的 static 方法/ static 字段首次访问时,Java 解释器必须找到 Dog.class
- 找到 Dog.class 后(它会创建一个 Class 对象,这将在后面学到),它的所有 static 初始化模块都会运行。因此, static 初始化仅发生一次—— 在 Class 对象首次载入的时候。
- 创建一个 new Dog()时, Dog 对象的构建进程首先会在内存堆( Heap)里为一个 Dog 对象分配足够多的存储空间。
- 这种存储空间会清为零,将 Dog 中的所有基本类型设为它们的默认值(零用于数字,以及 boolean 和
char 的等价设定)。 - 进行字段定义时发生的所有初始化都会执行。
- 执行构建器。
显示静态初始化
-
静态初始化块,与静态初始化动作相同。给静态成员进行初始化,只初始化一次。
public class Spoon { static int i; static { i = 47; } }
-
实例初始化,无static关键字,这对于支持匿名内部类是必须的,且在构造器前被调用。每次创建新的对象是都会调用。
import static net.mindview.util.Print.*; class Mug { Mug(int marker) { print("Mug(" + marker + ")"); } void f(int marker) { print("f(" + marker + ")"); } } public class Mugs { Mug mug1; Mug mug2; { mug1 = new Mug(1); //每次都会调用 mug2 = new Mug(2); print("mug1 & mug2 initialized"); } Mugs() { print("Mugs()"); } Mugs(int i) { print("Mugs(int)"); } public static void main(String[] args) { print("Inside main()"); new Mugs(); print("new Mugs() completed"); new Mugs(1); print("new Mugs(1) completed"); } } /* Output: Inside main() Mug(1) Mug(2) mug1 & mug2 initialized Mugs() new Mugs() completed Mug(1) Mug(2) mug1 & mug2 initialized Mugs(int) new Mugs(1) completed *///:~
初始化的可能位置
- 定义对象的地方
- 类的构造器中
- 就在正要使用这些对象前。这被称为惰性初始化。在生成对象不值得及不必每次都生成对象的情况下,这样可以减少负担。
- 如在toString()中进行初始化
- 使用实例初始化(静态/非静态)
数组初始化
-
数组的 = 赋值并不分配新的空间,而是一种别名
-
数组的创建时不需要明确有多少个元素,可运行时给出。数组初始化时若是基本类型会给出对应的空值,若是对象数组,则仅仅创建了引用数组,需要一次创建新的对象。
public static void main(String[] args) { //Method1 Integer[] a = { new Integer(1), new Integer(2), 3, // Autoboxing }; //Method2 Random rand = new Random(47); Integer[] a = new Integer[rand.nextInt(20)]; for(int i = 0; i < a.length; i++) a[i] = rand.nextInt(500); // Autoboxing }
-
可变参数列表
-
获得的仍然是一个数组,可用foreach迭代。当然,如果传进去的就是数组,则不用进行转化
-
可变参数处可以不传入实参。这个特性可以用于作为可选的尾随参数
public class OptionalTrailingArguments { static void f(int required, String... trailing) { System.out.print("required: " + required + " "); for(String s : trailing) System.out.print(s + " "); System.out.println(); } public static void main(String[] args) { f(1, "one"); f(2, "two", "three"); f(0); } }/* Output: required: 1 one required: 2 two three required: 0 *///:~
-
当可变参数列表中的类(Integer)遇到基本数据类型(int)时,会自动进行参数提升
-
对于可变参数的重载,f()会产生ambiguous问题,须对于每个含有可变参数的重载函数提供一个非可变参数。实际上,应尽量避免在多个重载函数中使用可变参数!
-