一个据说第一次接触都会做错的Java面试题和类加载器的介绍

首先什么话都不说,先把这个很变态的面试题放上来,大家有情趣自己分析一下,然后再运行一下看看结果,据说第

一次遇到这个题目的Java程序员都是会做错的。

package com.bird.classLoad;

public class Test1 {
	
	@SuppressWarnings("static-access")
	public static void main(String[] args) {
		Singleton s = Singleton.getSingleton();
		System.out.println("counter1 = "+ s.counter1);
		System.out.println("counter2 = "+s.counter2);
	}
}


class Singleton{
	
	private static Singleton singleton = new Singleton();
	
	public static int counter1;
	
	public static int counter2 = 0;
	
	public Singleton(){
		counter1++;
		counter2++;
	}
	
	public static Singleton getSingleton(){
		return singleton;
	}
	
	
}

如果你没有分析出来结果是1,0那么请您继续往下看。

首先介绍一下JVM对一个类的字节码class文件是如果装载到内存中然后被虚拟机执行的。

1.加载:查找并加载类的二进制数据
2.连接
2.1:确保被加载类的正确性
2.2:为类的静态变量分配内存,并将其初始化为默认值
2.3:把类中的符号引用转换为直接引用
3.初始化:为类的静态变量赋予正确的初始值


请注意,在连接的时候,为类的静态变量分配内存,初始化为默认值,然后才把类中的符号转换为指定的引用。

当然了,JVM只有在主动调用类的时候才回去加载一个类,主动调用一共有六种方式

Java虚拟机主动使用一个类的六种情况
1.创建类的实例
2.访问某个类或接口的静态变量,或者对该静态变量赋值
3.调用类的静态方法
4.反射
5.初始化一个类的子类
6.Java虚拟机启动时被标明为启动类的类
除了以上六种情况,其他都为被动使用,都不会执行类的初始化


然后对于类加载器

有两种类型的类加载器
1.根类加载器(使用C++编写,程序员无法在Java代码中获得该类)
2.扩展类加载器
3.系统类加载器


用户自定义类加载器
ClassLoader的子类


比如

package com.bird.classLoad;

public class Test2 {
	
	public static void main(String[] args) throws Exception {
		Class clazz = Class.forName("java.lang.String");
		System.out.println(clazz.getClassLoader());
		
		Class clazz2 = Class.forName("com.bird.classLoad.A");
		System.out.println(clazz2.getClassLoader());
	}
}

class A{}
运行结果

null
sun.misc.Launcher$AppClassLoader@addbf1

说明String类是JDK自带的,是使用默认根加载器加载的,它是使用C++编写的,所以才会返回null,SUN不允许直接

访问这个根加载器的。

然后对于自己写的类,当然是通过内部类application加载器加载的,虽然都能使用,但是底层加载方式各不相同。


最后我们来分析程序

首先执行Singleton s = Singleton.getSingleton();

主动调用 Singleton,这之后初始化Singleton类,为static变量初始化内存空间然后赋予默认值。所以,singleton为

null,counter1为0,counter2为0.

然后赋予引用值。singleton调用构造函数,这时候counter1等于1,counter2等于1.

然后顺次执行,counter1没有被赋值,counter2被赋值为0.

这次就返回这个对象了,所以最终结果为,1,0

总结一下,这道题真坑爹。


你可能感兴趣的:(java,虚拟机,面试,ClassLoader,String,Class)