关于java内部类加载顺序的问题

时间:2020-12-31 16:51:22

今天看了单例模式,对内部类的加载顺序产生了疑问。所以来请教大家。
我们知道,java当中,类的加载顺序是:类静态块-类静态属性-类内部属性-类构造方法。
但是当有内部类的时候会怎样呢?我们先看一下代码。

public class Singleton {

    public static class Inner{
        static {
            System.out.println("TestInner Static!");
        }
        public final static Singleton testInstance = new Singleton(3);
    }

    public static Singleton getInstance(){
        return Inner.testInstance;
    }

    public Singleton(int i ) {
        System.out.println("Test " + i +" Construct! ");
    }

    //类静态块
    static {
        System.out.println("Test Static");
    }

    //类静态属性
    public static Singleton testOut = new Singleton(1);

    public static void main(String args[]){
        Singleton t = new Singleton(2);
        Singleton.getInstance();
    }

}



这个时候,输出的结果是: 
Test Static
Test 1 Construct! 
Test 2 Construct! 
TestInner Static!
Test 3 Construct!



但是,当我把内部类Inner的内部静态块和内部静态属性 testInstance 互换位置后,如下: 
public class Singleton {

    public static class Inner{
        public final static Singleton testInstance = new Singleton(3);
        static {
            System.out.println("TestInner Static!");
        }
    }

    public static Singleton getInstance(){
        return Inner.testInstance;
    }

    public Singleton(int i ) {
        System.out.println("Test " + i +" Construct! ");
    }

    //类静态块
    static {
        System.out.println("Test Static");
    }

    //类静态属性
    public static Singleton testOut = new Singleton(1);

    public static void main(String args[]){
        Singleton t = new Singleton(2);
        Singleton.getInstance();
    }

}



输出为:

Test Static
Test 1 Construct! 
Test 2 Construct! 
Test 3 Construct! 
TestInner Static!



难道java内部类的加载顺序只是简单的位置问题吗??

对jvm的内部机制不清楚,所以来请教大家。万分感谢。






我想说的是,会不会静态块和静态属性是同一级别的,他们是按顺序加载,你把下面那两个也换换试试--- 共有 3 条评论 ---
wangfabo:  初始化的时候,静态块和静态变量是按顺序的,你理解错啦  5个月前
vapy:  正解  6个月前
Archer2ee:  谢谢回复。现在关键在于后面2个输出,前面的都是按照上面我说的那种初始化顺序。貌似到了内部类就有点变化了我只是简单的互换了内部类里面的顺序,就出现了这种情况。  2年前

这种问题应该能百度到吧。。。我碰到这种地方会去百度 一般都不会同时用到这些 

一般好像也不会用到public的静态内部类

其实没必要搞那么明白 平时用不到的东西 过一段时间你又会搞混了或者搞忘了 需要用就查资料


--- 共有 7 条评论 ---
黑狗:  回复 @Archer2ee : 嗯 学的时候好好学 不过等到工作了 你会非常多的听到一个单词“业务”... 然后他会分掉你非常多的精力  2年前
Archer2ee:  回复 @黑狗 : 感谢。学习开始的时候就多想嘛。不然越到后期就越懒了哈。  2年前
Archer2ee:  回复 @黑狗 : 感谢耐心的回复。调用顺序我验证过了,这所说的这样子。至于private和public的内部类,我理解也不太深入。  2年前
纠结名字:  回复 @Archer2ee : 他的意思是这种问题最好自己查一下,自己查能了解更多细节,也更系统。你问这种问题,别人也只能说个大概,或者直接给你个链接,实际还是查。。。  2年前
黑狗:  回复 @Archer2ee : 看输出只是进行验证,还是要根据官方文档上的,类和方法的作用域和可见性,还有你所说的调用顺序,理论上想明白了 再进行验证 可能更适合学习一些  2年前



内部类也是个单独的Class文件,只有用到的时候才会动态加载。也就是调用Singleton.getInstance()时加载; 静态内容的初始化是在JVM内部的一个<clinit>方法内生成的,从你运行的结果看,是和static的顺序相关的。另外,你类加载过程应该还不包括调用构造方法这一步,类加载过程包括加载类文件(Loading),链接到JVM(Linking),最后执行<clinit>方法。具体的内容建议查看《  The Java Machine Specification》第五章。--- 共有 1 条评论 ---
Archer2ee:  谢谢耐心的回复。首先普通的java类会有一个单独的Class文件,应用跑起来的时候jvm就会经历加载、链接、初始化这3个过程。然而内部类,也是有一个单独的Class文件,那么应用跑起来的时候,jvm应该没有给它初始化,至于3个过程,也还不清楚执行了哪几步。  2年前
评论(1) 引用此答案 举报

有规律的,你这还不算复杂,如果包含父类以及内部类(非静态)就更复查,但是万变不离其宗。

规律一、初始化构造时,先父后子;只有在父类所有都构造完后子类才被初始化

规律二、类加载先是静态、后非静态、最后是构造函数

静态构造块、静态类属性按出现在类定义里面的先后顺序初始化,同理非静态的也是一样的,只是静态的只在加载字节码是执行一次,不管你new多少次,非静态会在new多少次就执行多少次

规律三、java中的类只有在被用到的时候才会被加载

规律四、java类只有在类字节码被加载后才可以被构造成对象实例

上面四条规律就解析你上面不能理解代码执行结果的原因了

--- 共有 3 条评论 ---
xdev:  回复 @Archer2ee : 非静态是在new申请内存空间并初始化其值。java类是必须先加载字节码,然后通过new(有些不需要new,比如枚举和字符串,可以看jvm规范说明)根据字节码信息申请类的内存空间。上面的四种规律都是java虚拟机的规范严格规定的,只有实现规范的虚拟机才可以叫java虚拟机,所以ibm、oracle的虚拟机都符合上面四个规律。  2年前
xdev:  回复 @Archer2ee : 哈哈,确实,这个地方没有说的详细,类初始化化是分,静态,和非静态,静态是在类的字节码加载到jvm时,仅且只执行一次,按照先父后子,父子静态都初始化完后,跟据需要再初始化父子非静态部分(也就是说静态和非静态是分开的,但往往因为new的时候类即加载类同时也申请内存空间,可以先通过类名加静态方法或者静态属性代码调用来分开两个过程)。  2年前
Archer2ee:  感谢回复。验证过,的确如此。不过规律一中你说"只有在父类所有都构造完后子类才被初始化”,这句话有点无法理解。按我的理解,应该是在父类所有都加载完毕,并且初始化后子类才被初始化。初始化和构造是不同的吧?  2年前
评论(3)

类静态块-类静态属性-类内部属性-类构造方法。这个不正确

顺序是:父类静态属性-》父类静态代码块-》子类静态变量-》子类静态代码块-》父类非静态变量-》父类非静态代码块-》父类构造函数-》子类非静态变量-》子类非静态代码块-》-》子类构造函数

这样的加载顺序不是绝对的 因为静态变量和静态代码块跟声明顺序有关。

对于如果静态代码块中调用静态变量,那么静态变量必须在静态代码块前面声明;如果静态代码块中没有调用静态变量,那么就跟顺序有关了,谁先声明谁先被加载。说白了还是顺序加载,之所以会出现“如果静态代码块中调用静态变量,那么静态变量必须在静态代码块前面声明”,是因为变量是声明,所以出现编译错误。

应用到内部类中 静态变量和静态代码块跟声明顺序有关。 这样就可以解释你的问题了。内部类也是类。

测试所用jdk版本1.8.0_20

类静态块-类静态属性这个跟顺序有关系 如果类静态属性在类静态代码块之前 那么类静态属性先初始化


package cn.canon.Single;

public class StaticInnerClassLoaderTime {
	
	public static class Inner {
		static {
			System.out.println("TestInner Static!");
		}
		public final static StaticInnerClassLoaderTime testInstance = 
				new StaticInnerClassLoaderTime(3);
	}

	public static StaticInnerClassLoaderTime getInstance() {
		return Inner.testInstance;
	}

	public StaticInnerClassLoaderTime(int i) {
		System.out.println("Test " + i + " Construct! ");
	}
	
	// 类静态属性
	public static StaticInnerClassLoaderTime testOut =
			new StaticInnerClassLoaderTime(1);

	public static int value = 3;
	// 类静态块
	static {
		System.out.println("Test Static" + value);
	}

	public static void main(String args[]) {
		StaticInnerClassLoaderTime t = new StaticInnerClassLoaderTime(2);
		StaticInnerClassLoaderTime.getInstance();
	}

}



Test 1 Construct! 
Test Static3
Test 2 Construct! 
TestInner Static!
Test 3 Construct!