1、概述
2、类加载的时机

/**
* @author ljy
* @version V1.0
* @Package PACKAGE_NAME
* @Description:
* 被动使用类字段演示一
* 通过子类引用父类的静态字段,不会导子类初始化
* @date 2018/10/26 13:20
*/
class SuperClass{
static {
System.out.println("SuperClass init!");
}
public static int value = 123;
}
class SubClass extends SuperClass{
static {
System.out.println("SubClass init!");
}
}
/**
* 非主动使用类字段演示
*/
public class NoInitialization {
public static void main(String[] args) {
System.out.println(SubClass.value);
}
}
运行
/**
* @author ljy
* @version V1.0
* @Package PACKAGE_NAME
* @Description:
* 被动使用类字段演示二
* 通过数组定义引用类,不会触发此类的初始化
* @date 2018/10/26 13:20
*/
class SuperClass{
static {
System.out.println("SuperClass init!");
}
public static int value = 123;
}
class SubClass extends SuperClass{
static {
System.out.println("SubClass init!");
}
}
/**
* 非主动使用类字段演示
*/
public class NoInitialization {
public static void main(String[] args) {
SuperClass[] sca = new SuperClass[10];
}
}
运行
可以看出没有触发SuperClass的初始化
/**
* @author ljy
* @version V1.0
* @Package PACKAGE_NAME
* @Description:
* 被动使用类字段演示三
* 常量在编译阶段会存入调用类的常量池,本质上并没有直接引用到定义常量的类,
* 因此不会触发定义常量的类的初始化
* @date 2018/10/26 13:20
*/
/**
* 非主动使用类字段演示
*/
class ConstClass{
static {
System.out.println("ConstClass init !");
}
public static final String HELLOWORLD = "hello world !";
}
public class NoInitialization {
public static void main(String[] args) {
System.out.println(ConstClass.HELLOWORLD);
}
}
运行后
3、类加载过程
3.1、加载
3.2、验证
3.3、准备
3.4、解析
public class FieldResolution {
interface interface0{
int A=0;
}
interface interface1 extends interface0{
int A=1;
}
interface interface2{
int A=2;
}
static class Parent implements interface1{
public static int A = 3;
}
static class Sub extends Parent implements interface2{
public static int A = 4;
}
public static void main(String[] args) {
System.out.println(Sub.A);
}
}
jdk8下我可以正常运行
关于解析 就到这了
3.5、初始化
public class Test {
static {
i=0;//给变量赋值可以正常通过
System.out.println(i);//这句编译会提示“非法引用向前”
}
static int i =1;
public static void main(String[] args) {
}
}
运行
public class Test {
//父类的client方法优先执行
static class Parent{
public static int A=1;
static {
A=2;
}
}
static class Sub extends Parent{
public static int B=A;
}
public static void main(String[] args) {
System.out.println(Sub.A);
}
}
运行
4、类加载器
4.1、类与类加载器
import java.io.InputStream;
/**
* 类加载器与instanceof关键字演示
*/
public class ClassLoaderTest {
public static void main(String[] args)throws Exception {
ClassLoader myLoader = new ClassLoader(){
public Class<?> loadClass(String name) throws ClassNotFoundException{
try{
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if(is == null){
return super.loadClass(name);
}
byte [] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0,b.length);
}catch(Exception e){
throw new ClassNotFoundException(name);
}
}
};
Object obj = myLoader.loadClass("ClassLoaderTest").newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof ClassLoaderTest);
}
}
运行
4.2、双亲委派模型
从Java虚拟机的角度来说,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现(HotSpot虚拟机中),是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些类加载器都有Java语言实现,独立于虚拟机外部,并且全部继承自java.lang.ClassLoader。
从开发者的角度,类加载器可以细分为:
启动(Bootstrap)类加载器:负责将 Java_Home/lib下面的类库加载到内存中(比如rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
标准扩展(Extension)类加载器:是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将Java_Home /lib/ext或者由系统变量 java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
应用程序(Application)类加载器:是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,因此一般称为系统(System)加载器。
除此之外,还有自定义的类加载器,它们之间的层次关系被称为类加载器的双亲委派模型。该模型要求除了顶层的启动类加载器外,其余的类加载器都应该有自己的父类加载器,而这种父子关系一般通过组合(Composition)关系来实现,而不是通过继承(Inheritance)。
4.3 、破坏双亲委派模型