跟王老师学反射:Java类的加载连接和初始化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跟王老师学反射:Java类的加载连接和初始化相关的知识,希望对你有一定的参考价值。

跟王老师学反射(二):Java类的加载、连接和初始化

主讲教师:王少华   QQ群号:483773664

学习内容:

了解类的加载、连接和初始化


一、类的生命周期

技术分享

当我们编写一个java的源文件后,经过编译会生成一个后缀名为class的文件,这种文件叫做字节码文件,只有这种字节码文件才能够在java虚拟机中运行,java类的生命周期就是指一个class文件从加载到卸载的全过程。一个java类的完整的生命周期会经历加载、连接、初始化、使用、和卸载五个阶段,当然也有在加载或者连接之后没有被初始化就直接被使用的情况,这里我们主要来研究类加载器所执行的部分,也就是加载,链接和初始化。

二、类的加载

(一)概念

类的加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象

备注:

前面我们讲过:类是某一某对象的抽象,类是概念层次的东西。但是类也是一种对象。就像我们说概念主要用于定义、描述其他事物,但概念本身也是一种事物,那么概念本身也需要被描述。

每个类是一批具有相同特征的对象的抽象,而系统中所有的类,它们实际上也是对象,它们都是java.lang.Class实例。

(二)类加载器

类加载器是负责加载类的对象。Java虚拟机给我们提供了两种类加载器:

1、Java虚拟机自带的加载器

1)根类加载器(使用C++编写,程序员无法在Java代码中获得该类) 

2)扩展加载器,使用Java代码实现 

3)系统加载器(应用加载器),使用Java代码实现 


2、用户自定义的类加载器 :开发者可以通过继承ClassLoader类来创建自己的类加载器


ClassLoader 类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。每个class对象都包含一个对定义它的 ClassLoader 的引用。

我们再来看一下Class类的一个方法getClassLoader

1
public ClassLoader getClassLoader()

返回该类的类加载器。如果该类由根类加载器加载,则此方法在这类实现中将返回 null。

测试getClassLoader方法

1
2
public class A {
}
1
2
3
4
5
6
7
8
public class ClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException {
        Class clazz = Class.forName("java.lang.String"); 
        System.out.println(clazz.getClassLoader()); 
        Class clazz2 = Class.forName("chapter09_02.A"); 
        System.out.println(clazz2.getClassLoader());
    }
}

技术分享


(三)加载.class文件的方式

1、从本地系统中直接加载 

2、通过网络下载.class文件 

3、从zip,jar等归档文件中加载.class文件 

4、从专有数据库中提取.class文件 

5、将Java源文件动态编译为.class文件

(三)类的加载过程

  1. 类的加载的最终产品是位于堆区中的Class对象

  2. Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口 

技术分享

三、类的连接


类被加载后,就进入连接阶段。连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去。它包括三个阶段:

验证:确保被加载的类的正确性 

准备:为类的静态变量分配内存,并将其初始化为默认值 

解析:把类中的符号引用转换为直接引用 

四、类初始化

(一)概念

为类的静态变量赋予正确的初始值。

在Java类中,为静态属性指定初始值有两种方式

  1. 声明静态属性时指定初始值

  2. 使用静态初始化块为静态属性指定初始值

1
2
3
4
5
6
7
8
9
public class A {
    //声明时赋值
    public static int a = 9;
    public static int b;
    //使用静态化块赋值
    static{
        b=12;
    }
}

(二)面试题:下面代码的输出结果是什么

1
2
3
4
5
6
7
8
9
public class ClassLoaderTest2 {
    static{
        b=12;
    }
    static int b = 9;
    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println(b);
    }
}

技术分享

(三)JVM初始化一个类的步骤

JVM初始化一个类包含如下几个步骤:

  1. 假如这个类还没有被加载和连接,程序先加载连接该类;

  2. 假如该类的直接父类还没有被始化,则先初始化其直接父类;

  3. 假如类中有初始化语句,则系统依次执行这些初始化语句。

当执行第二步时,系统对直接父类的初始化步骤也遵循此三步,如果直接父类又有直接父类,系统再次重复这三个步骤来先初始化这个父类....依次类推。因此,当java虚拟机初始化一个类时,要求它的所有的父类都已经被初始化。

但这条规则并不适用于接口。

       在初始化一个类时,并不会先初始化它所实现的接口

       在初始化一个接口时,并不会先初始化它的父接口

      因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态变量时,才会导致该接口的初始化

总结

技术分享





以上是关于跟王老师学反射:Java类的加载连接和初始化的主要内容,如果未能解决你的问题,请参考以下文章

Java 学习 类的加载及反射

跟王老师学反射:使用反射操作数组

跟王老师学反射:Class类:获得Class对象

Java学习:反射

一起重新开始学大数据-java篇-DAY26反射,正则表达式

类的加载机制和反射——类的加载连接和初始化