可以参考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: nullDervied print name: nullDervied tell name: derviedDervied 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,然后执行构造块,最后执行构造方法

javac之向前引用的更多相关文章

  1. Java向前引用容易出错的地方

    所谓向前引用,就是在定义类.接口.方法.变量之前使用它们,例如, class MyClass { void method() { System.out.println(myvar); } String ...

  2. java向前引用

    根据看书和看得文章,引出了一个关于"向前引用"的问题: public class InstanceInitTest { static { // { a = 6; System.ou ...

  3. python自定义函数可以向前引用不用声明

    #有些编程语言不够"聪明",向这类向前引用的方式会导致报错,但Python足够"醒目",这段代码是正确的! def next():     print('我在n ...

  4. wpf staticresource 是不允许向前引用(forward reference)的

    不允许向前引用(forward reference)在C/C++中中很常见,即在语法上,未定义变量.类之前,不能使用. 没想到wpf中的wpf staticresource也遵循这种规则.资源字典中, ...

  5. 向前引用 ? float VS long ? 这些知识你懂吗?

    thinking in java 读书笔记(感悟): 作者:淮左白衣 : 写于 2018年4月2日18:14:15 目录 基本数据类型 float 和 long 谁更大 System.out.prin ...

  6. Java 9 揭秘(11. Java Shell)

    Tips 做一个终身学习的人. 在本章节中,主要介绍以下内容: 什么是Java shell JShell工具和JShell API是什么 如何配置JShell工具 如何使用JShell工具对Java代 ...

  7. 深入理解Java虚拟机类加载机制

    1.类加载时机 对于类加载的第一个阶段---加载,虚拟机没有强制的约束,但是对于初始化阶段,虚拟机强制规定有且只有以下的5中情况必须开始初始化,当然,加载.验证.准备阶段在初始化前就已经开始. ①使用 ...

  8. 《深入理解Java虚拟机》-----第7章 虚拟机类加载机制——Java高级开发必须懂的

    代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步. 7.1 概述 上一章我们了解了Class文件存储格式的具体细节,在Class文件中描述的各种信息,最终都需要 ...

  9. 深入理解JVM(3)——类加载机制

    1.类加载时机 类的整个生命周期包括了:加载( Loading ).验证( Verification ).准备( Preparation ).解析( Resolution ).初始化( Initial ...

随机推荐

  1. After Upgrade To Release 12.1.3 Users Receive "Function Not Available To This Responsibility" Error While Selecting Sub Menus Under Diagnostics (Doc ID 1200743.1)

    APPLIES TO: Oracle Application Object Library - Version 12.1.3 to 12.1.3 [Release 12.1] Information ...

  2. TSQL--关联更新和删除

    --=================================================== --测试CODE USE tempdb; GO DROP TABLE TB1 GO DROP ...

  3. eCharts IE8兼容性问题

    使用Echart的图表柱状图,里面用了Float32Array,IE8下面会提示无法找到Float32Array,黄色叹号. 网上查找后使用如下方法解决:不明觉厉 在<head></ ...

  4. 数据库访问接口(ODBC、OLE DB、ADO)

    最近在学C#的数据库编程,对于数据库接口技术这块的知识一直比较模糊,网上查了不少资料,看了几天还是朦朦胧胧的,只能做些笔记再研究了. 我们都知道,“数据库”是指一组相关信息的集合,最早的计算机应用之一 ...

  5. linux进程管理(一)

    进程介绍 程序和进程 程序是为了完成某种任务而设计的软件,比如OpenOffice是程序.什么是进程呢?进程就是运行中的程序. 一个运行着的程序,可能有多个进程. 比如自学it网所用的WWW服务器是a ...

  6. 虚拟机搭建ftp环境

    引用http://www.cnblogs.com/xiangxiaodong/archive/2013/12/23/3487028.html,学习. 本人是在windows8系统下,Oracle VM ...

  7. PHP 函数功能参考

    basename() 返回路径中的文件名部分 chgrp() 改变文件组 chmod() 改变文件模式 chown() 改变文件所有者 clearstatcache() 清除文件状态缓存 copy() ...

  8. Maven的Mirror和Repository 的详细讲解

    1 Repository(仓库) 1.1 Maven仓库主要有2种: remote repository:相当于公共的仓库,大家都能访问到,一般可以用URL的形式访问 local repository ...

  9. Linux中的任务调度

    1.crond,linux中的任务调度器 crond的概念和crontab是不可分割的.crontab是一个命令,常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令.该命令从标准输入 ...

  10. LINUX云服务器 安装 nginx

    什么是nginx? 是一个高性能的 HTTP 和反向代理服务器,也是一个IMAP/POP3/SMTP 代理服, 是一个asynchronousservers异步服务器 为什么使用nginx? 因为它的 ...