整理类加载过程以及涉及到的一些知识点

首先描述一下什么是类加载、以及加载过程


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分为:加载-》连接(验证、准备、解析)-》初始化-》使用-》卸载

加载:

就像<深入理解java虚拟机>一书说的一样,千万不要混淆“类加载”与“类加载过程”。
进行一个类的加载及初始化 两种方法:继承ClassLoader并重写findClass因为类加载是通过双亲委派方式,重写findClass可以在父加载器无法找到需要加载的类时可以通过类本身查找,这时候就会通过getData找到Class文件并读取二进制流进行加载了、forname()
类加载分两步:
(1)根据类的全限定名(包名.类名)取出class文件中的二进制字节流.
(2)将二进制字节流中的静态数据转为运行时数据结构。
(3)在堆中生产一个class对象,用来访问方法区中存储的类变量,以及类信息。

注意:
1、这块就涉及到了jvm内存分配(堆、栈、本地方法栈、虚拟机栈、方法区)jdk1.8就没有了方法区取而代之的是一个叫元数据的东西。堆存储【对象、数组】、栈存储【局部变量、引用】、方法区存储【类变量、类信息、静态变量、常量】
2、反射中的newInstance方法必须等待类加载完成以后才可以初始化。
3、加载两种方式:Class.forname与ApploadClass.loadClass() (这两种区别在于Class.foraname(xx)加载完默认会自动初始化 、还有一个区别为.loadClass可以加载磁盘任何位置的class文件而forname只能加载项目路径下的,具体可以看文章最下方)如下图:
4、静态代码是与class绑定的,class只会被加载一次,所以静态代码也只会初始化一次。
true的意思就是加载完直接进行初始化。

连接:

连接内部分为(验证、准备、解析)

验证:确保加载的类信息符合JVM规范,没有安全方面的问题。
准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。注意此时的设置初始值为默认值,具体赋值在初始化阶段完成(比如int类型为0,long类型为0L)
解析:将符号引用解析为直接引用。

初始化:

到了初始化阶段,用户定义的 Java 程序代码才真正开始执行。在这个阶段,JVM 会根据语句执行顺序对类对象进行初始化,一般来说当 JVM 遇到下面 5 种情况的时候会触发初始化:
1、遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
2、使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
3、当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
4、当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
5、当使用 JDK1.7 动态语言支持时,如果一个 java.lang.invoke.MethodHandle实例最后的解析结果 REF_getstatic,REF_putstatic,REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先出触发其初始化。

下面一句话很重要!!!!!!!

(1)在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完成类的初始化。在类初始化过程中或初始化完毕后,根据具体情况才会去对类进行实例化

(2)类初始化阶段是执行类构造器()方法的过程。类初始化方法。编译器会按照其出现顺序,【收集类变量的赋值语句、静态代码块,最终组成类初始化方法】。类初始化方法一般在类初始化的时候执行。

(3)对象初始化方法。编译器会按照其出现顺序,【收集成员变量的赋值语句、普通代码块,最后收集构造函数的代码】,最终组成对象初始化方法。对象初始化方法一般在实例化类对象的时候执行
为了更好理解上面的意思请看下面的面试题:

class Grandpa
{
    static
    {
        System.out.println("爷爷在静态代码块");
    }

    public Grandpa() {
        System.out.println("我是爷爷~");
    }
}
class Father extends Grandpa
{
    static int a = 2;
    static
    {
        System.out.println("爸爸在静态代码块");
    }
    public Father()
    {
        System.out.println("我是爸爸~");
    }
}
class Son extends Father
{
    static
    {
        System.out.println("儿子在静态代码块");
    }
    public Son()
    {
        System.out.println(a);
        System.out.println("我是儿子~");
    }
}
public class InitializationDemo
{
    public static void main(String[] args)
    {
        System.out.println(Son.a);
        //new Son(); 	//入口
    }
}

各位可以看看上面会输出什么数据:

爷爷在静态代码块
爸爸在静态代码块
2

分析一下:首先上面是调用的静态变量(Son.a)这一点就只会加载Father类(如果子类调用父类静态变量,只会加载父类,这时候会初始化Father,然后发现Grandpa也没有初始化则会继续初始化Grandpa,这时属于类初始化 所以按照上面说的 只会收集静态)所以会执行 Grandpa静态代码块–》Father静态代码块–》打印a的值

下面再加大深度:

class Grandpa
{
    static
    {
        System.out.println("爷爷在静态代码块");
    }
   {
        System.out.println("我是Grandpa普通代码块");
    }
    public Grandpa() {
        System.out.println("我是爷爷~");
    }
}
class Father extends Grandpa
{
    static int a = 2;
    static
    {
        System.out.println("爸爸在静态代码块");
    }
    {
        System.out.println("我是Father普通代码块");
    }
    public Father()
    {
        System.out.println("我是爸爸~");
    }
}
class Son extends Father
{
    static
    {
        System.out.println("儿子在静态代码块");
    }
   {
        System.out.println("我是Son普通代码块");
    }
    public Son()
    {
        System.out.println(a);
        System.out.println("我是儿子~");
    }
}
public class InitializationDemo
{
    public static void main(String[] args)
    {
        new Son(); 	//入口
    }
}

结果

爷爷在静态代码块
爸爸在静态代码块
儿子在静态代码块
我是Grandpa普通代码块
我是爷爷~
我是Father普通代码块
我是爸爸~
我是Son普通代码块
2
我是儿子~

解析一下:这个和上面的区别就是 new Son(); 这是进行对象的实例化,这时候就会先执行类的加载与初始化(不明白的可以看上面的解释说明),这时候就需要按照顺序来 类的初始化(类初始化构造器)–》对象实例化(对象实例化构造器) 根据上面的解释 执行 Grandpa静态代码块–》Father静态代码块–》Son静态代码块–》Grandpa普通代码块–》Grandpa构造方法–》Father普通代码块–》Father构造方法–》Son普通代码块–》Son构造方法。

使用:

package com.hansight.atom.system.util;


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestShicz extends ClassLoader{
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class shicz =  null;
        byte[] classData = getData();
        shicz = defineClass(name,classData,0,classData.length);
        return shicz;
    }

    private byte[] getData() {
        File file = new File("D://test.class");
        if (file.exists()){
            FileInputStream in = null;
            ByteArrayOutputStream out = null;
            try {
                in = new FileInputStream(file);
                out = new ByteArrayOutputStream();

                byte[] buffer = new byte[1024];
                int size = 0;
                while ((size = in.read(buffer)) != -1) {
                    out.write(buffer, 0, size);
                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    in.close();
                } catch (IOException e) {

                    e.printStackTrace();
                }
            }
            return out.toByteArray();
        }else{
            return null;
        }
    }
}

package com.hansight.atom.system.util;

import java.lang.reflect.Method;

public class Ceshi {
    public static void main(String[] args) throws Exception{
        TestShicz testShicz = new TestShicz();
        Class<?> Shicz =  testShicz.loadClass("com.hansight.atom.system.Test");
        System.out.println("类加载器是:" + Shicz.getClassLoader());
        //利用反射获取main方法
        Method method = Shicz.getDeclaredMethod("sy", String.class);
        Object object = Shicz.newInstance();
        String name = "123";
        method.invoke(object, (Object) name);

    }
}

下面为需要加载的类 可以进行javac 编译后将Class文件放在D盘

package com.hansight.atom.system;

public class Test {
    static {
        System.out.println("22222222222222222222");
    }

    public void sy(String name){
        System.out.println("++++++++++++:"+name);
    }
}

在这里插入图片描述

然后执行Ceshi 的main方法 可以看到控制台打印了加载器为TestShicz

类加载器是:com.hansight.atom.system.util.TestShicz@2d38eb89
22222222222222222222
++++++++++++:123
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值