类的加载机制
类的加载机制
1.Java开发工具可以自动帮我们将java代码编译为class字节码。
2.类加载器加载class字节码,将字节码里面的指令放到内存执行,并将数据动态分配到jvm内存模型中。
3.jvm分区
类的加载流程
1.类的加载指的就是将class文件中的二进制数据读取到内存中。类,方法,常量,静态内容都放在方法区。
2.但是要在堆上面创建一个class对象,用来封装存储在方法区上面的数据结构。
3.类加载最终的结果就是在堆产生了一个Class对象(平时创建对象需要用到的内容)、当前对象提供了访问方法区内容的接口。
白话:创建一个类的时候,在堆内会自动创建一个java.lang,class对象 而不是类的实体对象,然后在你new一个新的对象的时候会找到这个Class对象然后去加载。任何一个类 new的时候一定会用Class对象去加载。这个Class对象就是类加载器。它并不需要等你去new的时候才产生,java虚拟机预料到你某个类要用这个对象加载的时候,会预先产生这个类加载器。
类加载器加载class文件,加载class文件有哪些方式?
1.从本地的系统读取的,读取过后直接加载
2.通过网络路径去下载的(class文件来自网络,可能会有错。本地的class文件一般是安全的,因为是经过本地的编译器编译过的)
3.从Zip、jar包中读取文档中的class文件
4.从某些数据库中去获取class文件
5.将java源文件动态编译成class文件。
类的生命周期:
加载:查找并加载类的二进制的数据
1.通过类的全限定名来获取定义的二进制数据
2,。将这个字节流的所代表的静态存储内容放入方法区
3.Java堆中生成一个代表当前对象的java.lang.Class对象,作为方法区的访问接口。整个类的生命周期中,最容易控制的阶段就是当前加载阶段。程序员最好控制这块、你可以自己定义类加载器来加载数据。
验证:验证的目的主要是确保被加载的类的正确性
验证主要的内容是验证class文件中的信息是符合java虚拟机加载的要求。并且你的信息不能够危害java虚拟机的安全。
验证的内容:
1.文件格式的验证:常量池中的数据是否有不被支持的类型
2.元数据的验证:保证类所描述的信息是符合java规范的。当前这个类是否有父类,除了java.lang.Object之外
3.字节码校验:校验语法的正确性,是否符合逻辑
4.符号引用校验:确保解析的动作能够正确运行
校验阶段是非常重要的阶段,但是又不是必须的。
准备:为类的静态变量分配内存,并且进行初始化为默认值。
为类变量(所有static静态的东西都称为类变量,属于类的属性。private等声明的是对象的属性)分配内存、并且默认初始化
1.进行内存分配的时候仅包含static变量,不包括实例变量。实例变量对象创建的时候才分配内存。Static变量在方法区 实例变量–堆
2.这是所设置的初始值包括数据类型默认的值 int(0),String(null),Object(null),这个阶段数据默认值,髌骨会使用你自己定义的值 如public static int numbe=10
解析:将类中符号转化为直接引用
虚拟机将常量池中的符号引用转化为直接引用。解析的动作主要包括:类、接口、字段、接口方法、方法类型。
直接引用:直接指向目标对象的指针。寻址操作。
解析阶段顺序并不一定按照图中的顺序执行,有可能在初始化过后才会执行这个动作。
初始化:给类的静态变量进行赋值,jvm负责对类进行初始化,主要是对类的变量进行初始化
1.声明类变量的时候指定初始化值。
2.使用静态快来初始化值。
jvm 判断:
假如当前类的父类还没有初始化,先初始化父类。
假如类中有初始化语句,依次执行初始化操作。
成员变量初始化:new的时候,反射的时候、初始化子类的时候,父类也会初始化。
类的销毁&对象的销毁
类的销毁:java虚拟机中的内容和 java.lang.class的销毁。
比如:system.exit(0) 退出虚拟机
程序正常运行结束。Java,lang,class。
程序遇到异常终止的情况。
如果深入了解本块内容 详情参考《深入理解java虚拟机》
《think in java》
类加载器
一共分为三类(系统架构提供的、不包括自己写的):
1.启动类加载器(Bootstrap ClassLoder)(用于加载系统的):主要负责加载存放在JDK/jre/lib文件夹下面的内容。并且能被jvm识别为类库。当前启动类加载器是无法直接被java程序引用的。
2.扩展类加载器:(Extension ClassLoder)(用于加载系统的):主要负责加载JDK/jre/lib/ext 目录下的内容。比如(javax.*包)
3.应用程序类加载器(Application ClassLoader)(用于加载自己的):负责加载用户类路径下面的类(自己写的类),操作系统配置的环境变量,自己在开发中写的java类。若没有自定义类加载器,默认一般都是用它来加载应用程序。
应用程序在运行的过程中,是由上面三种加载器相互配合执行的,并非由哪一个单独完成的。
注意:图中的方向并不是表示继承关系,而是组合关系。
加载机制
1.全盘负责:当一个类加载器负责加载某个类(Class)的时候,如果这个Class所依赖的其他class也由当前这个类加载器来完成。
2.父类委托:先让父加载器加载该内容,只有父加载器无法加载的时候再尝试自己加载
3.缓存机制:缓存机制主要是保证已经被加载过的类被缓存起来,当下次要再加载这个类的时候,直接从缓存区获取即可。一旦修改了Class,必须重启jvm才能生效。
类加载器既然可以加载本地类,创建一个本地类(里面只输出了一句话”正在执行静态块“)
加载类要用到类的全限定名称
返回的就是一个Class 并不是student,这时候堆里面并没有student 一个对象呗类加载器加载,会在堆里面产生一个class class就是你访问变量 访问方法区的一个访问入口。以后new 一个student 就是通过这个class来产生的,但是lcass只有一个。,
发现可以强转
newInstance就相当于创建对象,不用new。 这种创建方式称为动态加载(new 属于静态加载)
类加载:
1.Jvm启动的时候有Jvm来加载(自动加载)
2.ClassLoader.loadClass();(手动加载)
3.Class.forName();(手动加载)
1。TestMain.class.getClassLoader—类加载器
Load.loadClass();
2.Class.forname(”com.project.bean.StudentBean“);也可以加载类
双亲委派模型:
工作流程:
1.当ApplationClassloader加载一个class的时候,首先自己不会尝试加载这个类,而是将类加载委托给父类,父类交给extClassLoader,
2.当EXTClassload加载一个Class的时候,它首先不会尝试自己去加载,而是将当前类交给
BootStrapClassload,
3.此刻交给BootStrapClassloader加载,但是如果加载失败,?Jre/lib获取不到类信息,又会委托EXTBoostrap来加载
4.EXT加载失败,这个时候才会交给ApplicationClassloader加载。如果ApplicationClassloader还加载失败了,抛异常。显示ClassNotFoundException
双亲委派模型的意义:
防止内存中出现多分同样的字节码,保证java程序运行稳定,一个Class只加载一次。
先委托俩父元素加载,不行了再回来,保证整个系统内存中只有一个字节码、。
反射
1.类加载 Class.forname(“类的名称”) ClassLoader.loadclass(“类名”)
类表现了面向对象的封装特征。万物皆对象。
问:静态成员变量,普通的数据类型是否是对象?
int m =10;
普通的数据类型不是对象,但是java提供了包装类来表示基本数据类型,就是一种对象。
Integer number =10; Interger number = new Integer();
静态成员变量可以看成类的属性。
类:是?的对象
类是对象,类是java.lang,Class的对象。类的数据类型还是类
这里的类指的是我们创建出来的类
2.什么是动态加载、什么是静态加载?
在java中,类的加载 或者对象的加载分为两种,一种是静态加载,一种是动态加载。
静态加载:在你类编译的时候,就已经在内存中加载了。比如new一个对象
动态加载:在类运行的时候才会去主动加载。比如反序列化、反射产生对象。
优缺点:
静态加载,在编译的时候就加载,以后要用的时候不需要再编译了。但是消耗内存。
动态加载,在编译的时候不会加载对象,而是需要用某个功能的时候再加载。不会浪费内存资源,效率更低。
在项目开发中,两种方式都会用,一般来说框架的底层大量要用动态加载。
3.Class类的获取方式
Java.lang Class存放在堆中,在程序中要获取当前对象提供了三种方式:
Object—getClass() 所有对象都有当前方法
对象.class 静态属性 class静态属性任何一个对象都有。
Class C= Class forName(“类的全路径”);
总结:在运行期间,一个类只会产生一个Class 对象,第一个方式获取类的对象意义不大,必须得由对象才能反向获取。第二种方式需要导入当前对象(导入javabean包),依赖性太强。第三种方式只需要知道类的名称(String)就可以获取类的类型。
反射的操作:
1.通过反射可以获取到构造方法并使用
通过Class对象获取到某个类中:构造方法、成员变量、成员方法
UserBean:两个公共的有参无参构造器,一个私有的有参构造器(图中未显示)
访问对象私有的构造器:调用
发现可以获取到了
获取到了,能不能用?正式传实参进去
提示是无法访问的,构造器是私有的。接下来使用暴力**:
发现就可以获取了。
获取到所有的公有属性
获取到某一个公有属性
获取到一个私有的属性
给私有属性设值 set方法