可以参考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. Hdu4280 Island Transport 2017-02-15 17:10 44人阅读 评论(0) 收藏

    Island Transport Problem Description In the vast waters far far away, there are many islands. People ...

  2. TCP的几个状态(SYN/FIN/ACK/PSH/RST)

    在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG. 其中,对于我们日常的分析有用的就是前面的五个字段. 含义: SYN 表示建立连接, ...

  3. 封装MongoDB的 asp.net 链接类

    using System;using System.Collections.Generic;using System.Linq;using MongoDB; /// <summary>// ...

  4. vim 命令全

    1 简介 vim是文本编辑器.代码补完.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用.下面讲述一些必要的基本命令,而掌握好这些命令,您就能够很容易将vim当作一个通用的万能编辑器来使用 ...

  5. sql--CONVERT、FOR XML PATH解决实际问题

    需求:每个平台分类下的门店,每个门店的名称.图片路径.评分,每个门店下的四个产品的名称.图片路径.评分 思路: 一开始门店动态化好写,用Ajax就行了.但是每个门店下面的产品,每个去请求一次查询有点不 ...

  6. Android TextView 嵌套图片及其点击,TextView 部分文字点击,文字多颜色

    1. TextView 中嵌套图片的方法 TextView textView... textView.setText("..."); textView.append(Html.fr ...

  7. 基于JSP的在线考试系统-JavaWeb项目-有源码

    开发工具:Myeclipse/Eclipse + MySQL + Tomcat 系统简介: 网络考试系统主要用于实现高校在线考试,基本功能包括:自动组卷.试卷发布.试卷批阅.试卷成绩统计等.本系统结构 ...

  8. 20165219 Exp1 PC平台逆向破解

    20165219 Exp1 PC平台逆向破解 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串 ...

  9. Jmeter非GUI运行,生成html报告

    一.JMete执行方式 JMeter执行方式有2种,一种是GUI模式,一种是非GUI模式.GUI模式就是界面模式,非GUI模式就是命令行模式.界面模式主要用来编写和调试脚本用的,项目的真正执行最好是采 ...

  10. kinect 2(ubuntu16.04)

    安装libfreenect2 参考 https://github.com/OpenKinect/libfreenect2/blob/master/README.md#linux 如果安装后找不到有关库 ...