(一)构造器初始化
(1) 牢记:无法阻止自动初始化的进行,它将在构造器被调用之前发生。
(2)在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布在方法定义之间,它们仍然会在任何方法(包括构造器)被调用之前得到初始化。
(3)示例
class Window {
Window(int marker) {
System.out.println("Window(" + marker + ")");
}
}
class House {
Window w1 = new Window(1);
House() {
System.out.println("House");
w3 = new Window(33);
}
Window w2 = new Window(2);
void f() {
System.out.println("f()");
}
Window w3 = new Window(2);
}
public class OrderOfInitialization {
public static void main(String[] args) {
House h = new House();
h.f();
}
}
运行结果
Window(1)
Window(2)
Window(2)
House
Window(33)
f()
(二)静态数据的初始化
(1)无论创建多少个对象,静态数据都只占用一份存储区域
(2)静态初始化只有在必要的时候才会进行
(3)静态对象不会再次被初始化
(4)初始化的顺序是先静态对象(如果它们尚未因前面的对象创建过程而被初始化),而后是“非静态“对象。
(5)示例
class Bowl {
Bowl(int marker) {
System.out.println("Bowl(" + marker + ")");
}
void f1(int marker) {
System.out.println("f1(" + marker + ")");
}
}
class Table {
static Bowl bowl1 = new Bowl(1);
Table() {
System.out.println("Table");
bowl2.f1(1);
}
void f2(int marker) {
System.out.println("f2(" + marker + ")");
}
static Bowl bowl2 = new Bowl(2);
}
class Cupboard {
Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
public Cupboard() {
System.out.println("Cupboard()");
bowl4.f1(2);
}
void f3(int marker) {
System.out.println("f3(" + marker + ")");
}
static Bowl bowl5 = new Bowl(5);
}
public class StativInitial {//执行main方法(静态方法),必须加载类,然后静态域table和cupboard被初始化,导致他们对应的类也被加载
public static void main(String[] args) {
System.out.println("***********");
new Cupboard();
System.out.println("************");
new Cupboard();
table.f2(1);
cupboard.f3(1);
}
static Table table = new Table();
static Cupboard cupboard = new Cupboard();
}
运行结果
Bowl(1)
Bowl(2)
Table
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
***********
Bowl(3)
Cupboard()
f1(2)
************
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)
总结一下对象的创建过程,假设有一个名为Dog的类
- 即使没有显式的调用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看成静态方法),或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件
- 然后载入Dog.class,有关静态初始化的所有动作都会被执行。因此,静态初始化只在Class对象首次加载的时候进行一次
- 当用new Dog()创建对象的时候,首先将在堆上为Dog分配足够的存储空间
- 这块存储空间会被清零,这就自动的将Dog对象中所有基本类型的数据都设置成了默认值,引用类型变成了null
- 执行所有出现于字段定义处的初始化动作
- 执行构造器
(三)显示静态初始化
(1)它只是一段跟在static关键字后面的代码
(2)这段代码只执行一次(与静态初始化动作一样),当首次生成这个类的一个对象时,或者首次访问那个类的静态数据成员时(即便从未生成过那个类的对象)
(3)示例:
class Cup {
public Cup(int marker) {
System.out.println("Cup(" + marker + ")");
}
void f(int marker) {
System.out.println("f(" + marker + ")");
}
}
class Cups {
static Cup cup1;
static Cup cup2;
static {
cup1 = new Cup(1);
cup2 = new Cup(2);
}
public Cups() {
System.out.println("Cups()");
}
}
public class ExplicitStatic {
public static void main(String[] args) {
System.out.println("main");
// Cups.cup1.f(99);
}
static Cups cups1 = new Cups();
static Cups cups2 = new Cups();
}
运行结果
Cup(1)
Cup(2)
Cups()
Cups()
main
(四)非静态实例初始化
(1)与静态初始化子句一模一样,只是少了static关键字
(2)可以保证无论调用那个显式构造器,某些操作都会发生
(3)示例
class Mug {
Mug(int marker) {
System.out.println("Mug(" + marker + ")");
}
void f(int marker) {
System.out.println("f(" + marker + ")");
}
}
public class Mugs {
Mug mug1;
Mug mug2;
{
mug1 = new Mug(1);
mug2 = new Mug(2);
System.out.println("****************");
}
public Mugs() {
System.out.println("Mugs()");
}
public Mugs(int i) {
System.out.println("Mugs(i)");
}
public static void main(String[] args) {
System.out.println("main");
new Mugs();
System.out.println("&&&&");
new Mugs(1);
System.out.println("&&&&");
}
}
运行结果
main
Mug(1)
Mug(2)
****************
Mugs()
&&&&
Mug(1)
Mug(2)
****************
Mugs(i)
&&&&
(五)数组初始化
(1)初始化时若如下,存储空间的分配(等价于使用new)将由编译器负责
int [] a={1,2,3,4,5};
(2)注意:每次访问数组的时候都要检查边界的做法在时间和代码上都是需要开销的,但是无法禁用这个功能
(3)如下代码可以证明:数组的创建确实是在运行时刻进行的
import java.util.Arrays;
import java.util.Random;
public class ArrayOfPrimitives {
public static void main(String[] args) {
int[] a;
Random rand = new Random(49);
a = new int[rand.nextInt(20)];
System.out.println(a.length);
System.out.println(Arrays.toString(a));
}
}
运行结果
14
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]