深入java虚拟机学习 -- 类的加载机制(三)

简介: 类的初始化时机在上篇文章中讲到了类的六种主动使用方式,反射是其中的一种(Class.forName(“com.jack.test”)),这里需要注意一点:当调用ClasLoader类的loadClass方法对类进行加载的时候,并不是对类的主动调用,不会导致类的初始化。

类的初始化时机

在上篇文章中讲到了类的六种主动使用方式,反射是其中的一种(Class.forName(“com.jack.test”)),这里需要注意一点:当调用ClasLoader类的loadClass方法对类进行加载的时候,并不是对类的主动调用,不会导致类的初始化。

那么接下来我继续给大家2个例子,让我们来看看他们的执行结果分别是什么样的,看看你能猜对吗?

public class Test2
{
    public static void main(String[] args)
    {
        System.out.println(FinalTest2.x);
    }
}

class FinalTest2{
    public static final int x=6/2;

    static {
        System.out.println("I am a final x");
    }
}

public class Test2
{
    public static void main(String[] args)
    {
        System.out.println(FinalTest2.x);
    }
}

class FinalTest2{

    public static final int x=new Random().nextInt();

    static {
        System.out.println("I am a final x");
    }
}

是不是看到结果很意外呢,2份代码看起来几乎是一模一样的,而且都是static修饰的,为什么和上篇文章讲的不一样了呢,为什么第一个demo里面的静态块没有执行呢? 下面让我们带着这一系列问题来解决下。

讲解

如果大家细心的话可以看到多了一个final修饰符,是的,结果的造成就是它在起作用。

public static final int x=6/2; 这行代码,java虚拟机在编译期就可以知道x的值是什么,因此在编译期就已经把3放到了常量池,所以在main方法中调用的时候不会触发类的初始化  即此时的x为编译期的常量
public static final int x=new Random().nextInt();  这行代码中的x在编译期不能确定具体的值,需要等到运行的时候才能确定x的值,所以在运行时会触发类的初始化,即此时的x为编译器的变量

接口和父类

前面讲的类主动加载的7种方式,都是再说单个类的情况,下面我们来介绍下接口和父类

当Java虚拟机初始化一个类的时候,要求他的所有父类都已经被初始化,但是如果此类实现的有接口,则:

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

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

只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用。这句话是什么意思呢?下面让我们看下这个Demo

public class Test3
{
    public static void main(String[] args)
    {
        System.out.println(Child.x);
    }
}

class Parent{
    public static int x=3;
    static {
        System.out.println("this is a parent");
    }
}

class Child extends Parent{

    static {
        System.out.println("this is a Child");
    }
}

public class Test3
{
    public static void main(String[] args)
    {
        System.out.println(Child.x);
    }
}

class Parent{
    public static int x=3;
    static {
        System.out.println("this is a parent");
    }
}

class Child extends Parent{
    public static int x=3;
    static {
        System.out.println("this is a Child");
    }
}

上面两个例子同样是相差一行代码,结果却差别很大,一个Child发生了初始化,另一个没有。

双亲委派模型

类加载器用来把类加载到Java虚拟机中,从JDK1.2版本开始,类的加载过程采用双亲委派模型机制,这种机制能更好的保证Java平台的安全,在这种机制中,除了java虚拟机自带的根加载器以外(根加载器由c++实现,没有父加载器),其余的类加载器有且只有一个父加载器。当Java程序请求加载Loader1加载一个类的时候,loader1首先会委托自己的父加载器去加载这个类,若父加载器还有父加载器的话,依次类推,如果父加载器能加在,则有父加载器完成加载,否则才会有loader1本身加载。

需要注意的事,这里的加载器之间的父子关系实际上指的是加载器对象之间的包装关系,并不是类之间的继承关系。一对父子加载器可能是同一个加载器类的2个实例,也可能不是,只是在子加载器对象中包装了一个父加载器对象。

若有一个类加载器能成功加载Sample类,那么这个类加载器被成为定义类加载器,所有能成功返回Clas对象的引用的类加载器(包括定义类加载器)都被称为初始类加载器。

双亲委派模型的有点是能够提高软件系统的安全性。在此机制下,用户自定义的类加载器不可能加载应该由父加载器加载的可靠类,从而防止不可靠甚至恶意的代码代替由父加载器加载的可靠代码。例如:java.lang.Object类总是由根加载器加载,其他任何用户自定义的类加载器都不可能加载该类。

运行时包

由同一类加载器加载的属于相同包的类组成了运行时包,决定两个类是不是属于同一个运行时包,不仅要看他们的包名是否相同,还要看定义类加载器是否相同。只有属于同一个运行时包的类才能相互访问包可见(即默认访问级别)的类和类成员。这样的限制能避免用户自定义的类冒充核心类库的类去访问核心类库的包可见成员。

假设用户自定义了一个类:java.lang.Spy,并由用户自定义的类加载器加载,由于java.lang.Spy和核心类库java.lang.*由不同的加载器加载, 所以他们属于不同的运行时包,因此java.lang.Spy不能访问核心类库java.lang包中的可见成员。

我们下一篇来着重说下如何来实现一个自定义的类加载器

 

开开心心编码,快快乐乐生活。
目录
相关文章
|
5月前
|
存储 Oracle Java
java零基础学习者入门课程
本课程为Java零基础入门教程,涵盖环境搭建、变量、运算符、条件循环、数组及面向对象基础,每讲配示例代码与实践建议,助你循序渐进掌握核心知识,轻松迈入Java编程世界。
448 0
|
5月前
|
负载均衡 Java API
grpc-java 架构学习指南
本指南系统解析 grpc-java 架构,涵盖分层设计、核心流程与源码结构,结合实战路径与调试技巧,助你从入门到精通,掌握高性能 RPC 开发精髓。
529 7
|
5月前
|
IDE Java 编译器
java编程最基础学习
Java入门需掌握:环境搭建、基础语法、面向对象、数组集合与异常处理。通过实践编写简单程序,逐步深入学习,打牢编程基础。
312 1
|
6月前
|
Java
Java基础学习day08-作业
本作业涵盖Java中Lambda表达式的应用,包括Runnable与Comparator接口的简化实现、自定义函数式接口NumberProcessor进行加减乘及最大值操作,以及通过IntProcessor处理整数数组,实现遍历、平方和奇偶判断等功能,强化函数式编程实践。
98 5
|
6月前
|
Java API 容器
Java基础学习day08-2
本节讲解Java方法引用与常用API,包括静态、实例、特定类型方法及构造器引用的格式与使用场景,并结合代码示例深入解析。同时介绍String和ArrayList的核心方法及其实际应用。
182 1
|
6月前
|
Java 程序员
Java基础学习day08
本节讲解Java中的代码块(静态与实例)及其作用,深入介绍内部类(成员、静态、局部及匿名)的定义与使用,并引入函数式编程思想,重点阐述Lambda表达式及其在简化匿名内部类中的应用。
177 5
|
6月前
|
Java
Java基础学习day07-作业
本作业包含六个Java编程案例:1)动物类继承与多态;2)加油卡支付系统;3)员工管理类设计;4)学生信息统计接口;5)USB设备控制;6)家电智能控制。综合运用抽象类、接口、继承、多态等面向对象技术,强化Java基础编程能力。
233 3
|
6月前
|
Java
Java基础学习day06-作业
本内容为Java基础学习作业,涵盖两个案例:一是通过Card类及其子类GoldenCard、SilverCard实现加油卡系统,体现封装与继承;二是通过Shape类及子类Circle、Rectangle演示多态与方法重写,强化面向对象编程理解。
116 1
|
6月前
|
设计模式 存储 Java
Java基础学习day07
本节讲解Java中的final关键字、单例设计模式、枚举类、抽象类与接口。涵盖常量定义、单例写法(饿汉式/懒汉式)、枚举特点及应用场景,以及抽象类与接口的使用与区别,助力掌握核心面向对象编程思想。
219 1
|
6月前
|
Java
Java基础学习day05-作业
本文为Java基础学习第五天作业,通过五个案例练习类与对象的定义、构造方法、set/get方法及成员方法的应用。涵盖女友、学生、教师、手机和电影等类的设计与测试,强化面向对象编程基础。
108 2