可以参考JLS7:https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.2.3
public class Test5 {
int a = m1();
public int m1() {
System.out.println(i); // 0
return i;
}
int b = (new Object() {
public int t() {
System.out.println(i); // 0
return i;
}
}).t();
{
i = 100;
System.out.println(this.i); // 100
//i = i+1; // error Cannot reference a field before it is defined
//System.out.println(i); // error Cannot reference a field before it is defined
}
//int k = i+1; // error Cannot reference a field before it is defined
int i = 2;
public static void main(String[] args) {
Test5 t = new Test5();
System.out.println(t.i); // 2
}
}
public class Test6 {
static int a = m1();
public static int m1() {
System.out.println(i); // 0
return i;
}
static int b = (new Object() {
public int t() {
System.out.println(i); // 0
return i;
}
}).t();
static {
i = j = 10;
System.out.println(Test6.j); // 10
//System.out.println(i); // error Cannot reference a field before it is defined
//i = j + 2; // error Cannot reference a field before it is defined
}
static int i, j;
public static void main(String[] args) {
}
}
1、类的加载执行顺序
public class Dervied extends Base {
private String name = "dervied";
public Dervied() {
tellName();
printName();
}
public void tellName() {
System.out.println("Dervied tell name: " + name);
}
public void printName() {
System.out.println("Dervied print name: " + name);
}
public static void main(String[] args){
new Dervied();
}
}
class Base {
private String name = "base";
public Base() {
tellName();
printName();
}
public void tellName() {
System.out.println("Base tell name: " + name);
}
public void printName() {
System.out.println("Base print name: " + name);
}
}
先初始化父类然后再初始化子类(这个初始化包括静态和非静态变量、静态和非静态的方法块、构造函数)
Dervied tell name: null
Dervied print name: null
Dervied tell name: dervied
Dervied print name: dervied
再看一下如下的例子:
class ParentClass {
public static int a=2;
public int b=3;
{
System.out.println("this is anonymity b="+b);
}
static {
a=4;
System.out.println("this is static and a="+a);
}
public ParentClass() {
System.out.println("this is parent gozao");
this.s();
}
public void s() {
System.out.println("this is parent");
}
}
public class Son extends ParentClass {
public Son(){
System.out.println("this is son gozao");
}
public static void main(String[] args) {
ParentClass d = new Son();
d.s();
}
public void s() {
//super.s();
System.out.println("this is son");
}
}
this is static and a=4
this is anonymity b=3
this is parent gozao
this is son
this is son gozao
this is son
可以看出类内的加载顺序为:
(1)
静态变量 对于静态变量肯定要首先进行初始化,因为后面的方法可能会使用这个变量,或者构造函数中也可能用到。而对于非静态变量而言,由于匿名块内、非静态方法和构造函数都可以进行操作(不仅仅是初始化),所以要提前进行加载和赋默认值。
静态代码块 多个静态代码块按顺序加载,这里需要注意:在这个顺序不难是类内书写的顺序,也是类加载的顺序,也就是说如果子类也有静态代码块,则子类的也加载。由于静态代码块可能会负责变量的初始化,或者是对象等的初始化,这样在构造函数或者方法中就变得可用了。而顺序加载多半是由于Java是按顺序执行代码的原因。
静态方法 一般静态方法中禁止引用还未初始化的非静态变量,如果引用了静态变量,则静态变量必须放到这个静态方法的前面,以保证在使用时已经被正确地初始化。
一般如上要按顺序执行加载。
如果静态代码块中用到了静态变量,则静态变量必须在前面,如果在后会出现编译错误,而静态代码块中不可以出现非静态的变量。
public static int a=2; // 必须放到静态代码块前
// public int a=3; // 代码块中会报错
{
System.out.println("this is anonymity b="+b);
}
static {
System.out.println("this is static and a="+a);
}
静态方法与静态变量的关系和上面一样。
(2)
匿名代码块 这个要后初始化于静态代码块和静态变量,因为其依然属于实例对象,而不属于类。在这里可以对非静态成员变量进行初始化工作,同样也可以引用静态变量,因为已经被初始化过了。
非静态变量 这个要后初始化于静态代码块和静态变量,如果在匿名代码块中有对非静态变量的引用,则非静态变量必须在前面,以保证先被初始化。对静态变量的位置不做要求。举例:
public int b=3; // 必须放到匿名代码块的前面,以保证先被初始化后使用
{
System.out.println("this is anonymity b="+b);
}
public static int b=2; // 可以放到匿名代码块的任意位置
如果两者没有引用,则按顺序执行加载。
(3)
构造函数 这里需要解释一下,为什么初始化子类必先初始化父类,由于子类可能会继承父类的属性或方法,所以肯定要先初始化父类了,而初始化父类则必须要调用父类的构造函数。
至于方法不用考虑,因为方法不用初始化,所以无论是静态还是不静态,和这个没有关系。
其实如上的代码还能说明一些问题,可以看到,在父类中通过this.s()调用的是子类的方法,子类的s()方法覆盖了父类的方法后,无论在哪里调用,都是调用子类的方法。
下面再来看一个比较复杂的面试题(阿里巴巴),如下:
public class InitializeDemo {
private static int k = 1;
private static InitializeDemo t1 = new InitializeDemo("t1");
private static InitializeDemo t2 = new InitializeDemo("t2");
private static int i = print("i");
private static int n = 99;
static {
print("静态块");
}
private int j = print("j");
{
print("构造块");
}
public InitializeDemo(String str) {
System.out.println((k++) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}
public static int print(String str) {
System.out.println((k++) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
}
public static void main(String args[]) {
new InitializeDemo("init");
}
}
1:j i=0 n=0
2:构造块 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:构造块 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:静态块 i=7 n=99
9:j i=8 n=100
10:构造块 i=9 n=101
11:init i=10 n=102
我们来解释一下:
1. 运行main方法的时候,JVM会调用ClassLoader来加载Test类,那么一起源于这次加载
2. 上面有四个静态属性,所以会按顺序逐一初始化这四个静态属性
3.private static int k = 1; 此时将k初始化为1
4.private static Test t1 = new Test("t1"); 创建Test对象,那么按照核心理念中的顺序
先执行 private int j = print("j"); 打印出j,然后执行构造块,最后执行构造方法
5.private static Test t2 = new Test("t2"); 同步骤4
6.private static int i = print("i"); 打印i
7.private static int n = 99; 直到这一步,n才被赋值为99,之前是从默认的0开始++的
8. 静态属性初始化完毕,代码走到静态块,打印出静态块,此时n=99
9. 静态属性和静态块执行完毕,然后执行main方法中的代码new Test("init");
10.main方法中创建对象,先初始化非静态属性,private int j = print("j");打印j,然后执行构造块,最后执行构造方法
702

被折叠的 条评论
为什么被折叠?



