java类的加载以及ClassLoader

一:关于类的加载机制,先看几个面试代码

  • -----------------------------------------------1------------------------------------------------
class YeYe{
    static {
        System.out.println("YeYe静态代码块");
    }
}

class Father extends YeYe{
    public static String strFather="HelloJVM_Father";

    static{
        System.out.println("Father静态代码块");
    }
}

class Son extends Father{
    public static String strSon="HelloJVM_Son";

    static{
        System.out.println("Son静态代码块");
    }
}

public class day2 {
    public static void main(String[] args) {
        System.out.println(Son.strFather);
    }
}
控制台输出结果
YeYe静态代码块
Father静态代码块
HelloJVM_Father
  • -----------------------------------------------2------------------------------------------------
class YeYe1{
    static {
        System.out.println("YeYe静态代码块");
    }
}

class Father1 extends YeYe1{
    public final static String strFather="HelloJVM_Father";

    static{
        System.out.println("Father静态代码块");
    }
}

class Son1 extends Father1{
    public static String strSon="HelloJVM_Son";

    static{
        System.out.println("Son静态代码块");
    }
}

public class day3 {
    public static void main(String[] args) {
        System.out.println(Son1.strFather);

    }
}

控制台输出结果
HelloJVM_Father
  • -----------------------------------------------3------------------------------------------------
class Father2{
    public static String strFather="HelloJVM_Father";

    static{
        System.out.println("Father静态代码块");
    }
}

class Son2 extends Father2{
    public static String strSon="HelloJVM_Son";

    static{
        System.out.println("Son静态代码块");
    }
}

public class InitativeUseTest2 {
    public static void main(String[] args) {

       System.out.println(Son2.strSon);
    }
}
控制台输出结果
        Father静态代码块
        Son静态代码块
        HelloJVM_Son
  • -----------------------------------------------4------------------------------------------------
class Test{
    static {
        System.out.println("static 静态代码块");
    }

//    public static final String str= UUID.randomUUID().toString();
    public static final double str=Math.random();  //编译期不确定
}


public class FinalUUidTest {
    public static void main(String[] args) {
        System.out.println(Test.str);
    }
}

解释:其实final不是重点,重点是编译器把结果放入常量池!当一个常量的值并非编译期可以确定的,那么这个值就不会被放到调用类的常量池中,这时在程序运行时,会导致主动使用这个常量所在的类,所以这个类会被初始化

控制台输出结果
static 静态代码块
0.8328688978347876
  • -----------------------------------------------5------------------------------------------------
public class ClassAndObjectLnitialize {

        public static void main(String[] args) {
            System.out.println("输出的打印语句");
        }

      public ClassAndObjectLnitialize(){

            System.out.println("构造方法");
            System.out.println("我是熊孩子我的智商=" + ZhiShang +",情商=" + QingShang);
        }

        {
            System.out.println("普通代码块");
        }

        int ZhiShang = 250;
        static int QingShang = 666;
        
        static
        {
            System.out.println("静态代码块");
        }     

}
控制台输出结果
静态代码块
输出的打印语句
  • -----------------------------------------------6------------------------------------------------
public class ClassAndObjectLnitialize {

        public static void main(String[] args) {
            new ClassAndObjectLnitialize();
            System.out.println("输出的打印语句");
        }

      public ClassAndObjectLnitialize(){

            System.out.println("构造方法");
             System.out.println("我是熊孩子我的智商=" + ZhiShang +",情商=" + QingShang);
        }

        {
            System.out.println("普通代码块");
        }

        int ZhiShang = 250;
        static int QingShang = 666;
        
        static
        {
            System.out.println("静态代码块");
        }      
}

控制台输出结果
		静态代码块
		普通代码块
		构造方法
		我是熊孩子我的智商=250,情商=666
		输出的打印语句
  • -----------------------------------------------7------------------------------------------------
class Father6{
    public static int a = 1;
    static {
        System.out.println("父类粑粑静态代码块");
    }
}
class Son6{
    public static int b = 2;
    static {
        System.out.println("子类熊孩子静态代码块");
    }
}

public class OverallTest {
    static {
        System.out.println("Main方法静态代码块");
    }

    public static void main(String[] args) {
        Father6 father6;
        System.out.println("======");

         father6=new Father6();
        System.out.println("======");

        System.out.println(Father6.a);
        System.out.println("======");

        System.out.println(Son6.b);

    }
}

控制台输出结果
		Main方法静态代码块
		======
		父类粑粑静态代码块
		======
		1
		======
		子类熊孩子静态代码块
		2
  • -----------------------------------------------8------------------------------------------------
class Demo{
    static {
        System.out.println("static 静态代码块");
    }
}

public class day4 {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader classLoader=day4.class.getClassLoader();
        System.out.println(classLoader.getResource(""));
        //1、使用ClassLoader.loadClass()来加载类,不会执行初始化块
        classLoader.loadClass("com.zdp.demo.practice.test_20191115.Demo");

        //2、使用Class.forName()来加载类,默认会执行初始化块
        Class.forName("com.zdp.demo.practice.test_20191115.Demo");

        //3、使用Class.forName()来加载类,并指定ClassLoader,初始化时不执行静态块
        Class.forName("com.zdp.demo.practice.test_20191115.Demo",false,classLoader);
    }
    
}

二:总结

1:(类的加载,连接与初始化)

1、加载:查找并加载类的二级制数据到java虚拟机中
2、连接:

验证:确保被加载的类的正确性
准备:为类的静态变量分配内存,并将其初始化为默认值,但是到达初始化之前类变量都没有初始化为真正的初始值(如果是被 final 修饰的类变量,则直接会被初始成用户想要的值。)
把类中的符号引用转换为直接引用,就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用替换成直接引用的过程

3、初始化:为类的静态变量赋予正确的初始值
类从磁盘上加载到内存中要经历五个阶段:加载、连接(验证,准备,解析)初始化、使用、卸载

2:(Java程序对类的使用方式可分为两种)

(1)主动使用
(2)被动使用

所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”时才能初始化他们
  • 主动使用
    • (1)创建类的实例
    • (2)访问某个类或接口的静态变量 getstatic(助记符),或者对该静态变量赋值 putstatic
    • (3)调用类的静态方法 invokestatic
    • (4)反射(Class.forName(“com.test.Test”))
    • (5)初始化一个类的子类
    • (6)Java虚拟机启动时被标明启动类的类以及包含Main方法的类
    • (7)JDK1.7开始提供的动态语言支持(了解)
  • 被动使用
    除了上面七种情况外,其他使用java类的方式都被看做是对类的被动使用,都不会导致类的初始化

3:初始化类构造器和初始化对象构造器

始化入口方法。当进入类加载的初始化阶段后,JVM 会寻找整个 main 方法入口,从而初始化 main 方法所在的整个类。当需要对一个类进行初始化时,会首先初始化类构造器(),之后初始化对象构造器()。

初始化类构造器:

JVM 会按顺序收集类变量的赋值语句、静态代码块,最终组成类构造器由 JVM 执行。

初始化对象构造器:

JVM 会按照收集成员变量的赋值语句、普通代码块,最后收集构造方法,将它们组成对象构造器,最终由 JVM 执行。值得特别注意的是,如果没有监测或者收集到构造函数的代码,则将不会执行对象初始化方法。对象初始化方法一般在实例化类对象的时候执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值