javac之向前引用

 

可以参考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,然后执行构造块,最后执行构造方法

 

转载于:https://www.cnblogs.com/extjs4/p/7771573.html

1关:Java程序的基本框架:Hello Java World! 100 学习内容 参考答案 记录 评论 任务描述 相关知识 Java程序基本概念 一段可执行的Java代码 编程要求 评测说明 任务描述 本关的任务是编写你人生中第一个Java程序,从此开启你的Java实训之旅,显示效果如下: 为了迈出上述的第一步,下面详细介绍一些基础知识。 相关知识 Java程序基本概念 一个Java程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作,其中: 对象(object):代表现实世界中可以明确标识的一个实体,存在独特的标识、状态和行为。例如,一条鱼是一个对象,它的状态有:颜色、品种;行为有:漫游、鱼跃等。 类(class):是创建对象的模板,用于描述一类对象的行为和状态。 方法:方法就是行为,一个类可以有很多方法。 一段可执行的Java代码 下面给出了一段最简单的Java代码: public class HelloJavaWorld { public static void main(String[] args){ System.out.print("Hello Java World"); } } 其中,我们定义了一个类:public class HelloJavaWorld,这个类是 public 公共类型的,类名是HelloJavaWorld。 在这个类中,我们定义了一个方法即:public static void main(String[] args),它是程序执行的起点,具体功能与C/C++中的main()函数作用是一样的。至于public、static和void的含义,我们在后续实训课程中再深入介绍。 最后,在主方法中我们调用了JDK类包的一个方法:System.out.print(),用来将程序的内容输出到控制台。 如果同学们对上述内容感到困惑,那么请不要担心也不要急躁,坚持跟随我们实训的步伐向前,很快所有的疑惑将迎刃而解。 编程要求 现在,你是否已经迫不及待地要用Java语言向世界打声招呼啦?请直接修改右侧代码编辑器中的Java代码,具体要求是: 补全符合语法规范的主程序入口函数,即main()函数; 向控制台输出:"欢迎来到Java世界"。 编程的道路上充满荆棘,千万不要小看你在Java世界的第一个程序!! 评测说明 当点击评测按钮时,本实训平台会自动编译补全后的Java代码,通过检查编译结果来验证程序的正确性和完整性。接着,平台会进一步自动匹配程序的输出结果与预期结果,从而验证你编写的代码是否满足本关要求。
09-26
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值