首页 > 代码库 > 类的加载
类的加载
当调用java命令运行某个java程序时,该命令将会启动一个java虚拟机,不管该程序有多么复杂,启动了多少个线程,他们都处于该java虚拟机里,同一个JVM的所有线程,所有变量都处于同一个进程里,都使用该JVM进程的内存区
以下情况JVM被终止:
程序运行到正常结束
程序运行到System.exit()或Runtime.getRuntime().exit()代码处结束程序
程序执行过程中遇到未捕获的异常或者错误
程序所在平台强制结束了JVM进程
JVM进程结束,该进程在内存中的状态将会丢失
系统通过加载,连接,初始化三个步骤加载类到内存中,这三个步骤一般连续完成,统称为类加载或类初始化。指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象
所有类都是java.lang.Class的实例
类的加载通过类加载器完成,通常由JVM提供,是所有程序运行的基础,类被加载后生成一个Class对象,接着进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中,连接分为三个阶段:验证,准备,解析,最后由虚拟机进行类的初始化,主要就是对变量初始化
类初始化时机:
创建类的实例,通过new,反射,反序列号等方式
调用某个类的类方法
访问某个类或接口的类变量,或为该类变量赋值
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象,例如Class.forName(“person”),如果系统还没初始化person类,运行该代码会初始化该类并返回Class对象
初始化某个类的子类,子类的所有父类都会被初始化
直接使用java.exe命令运行某个主类,程序会先初始化该主类
特殊情形:
对于final型的类变量,如果该变量的值在编译时就可以确定,则这个变量相当于“宏变量”,java编译器会在编译时直接把这个类变量出现的地方替换成他的值,因此即使程序使用该静态变量,也不会导致该类的初始化。不能确定则会导致初始化
当使用ClassLoader类的loadClass()方法加载某个类时,该方法只是加载类,并不会执行类的初始化,使用Class的forName()静态方法才会导致强制初始化该类
一个类被类加载器载入JVM,同一个类就不会再次载入,在java中,类的标识是类的全限定类名:包名和类名,在JVM中,类的标识是类的全限定类名和其类加载器。两个ClassLoader的两个不同实例加载的同名类是完全不同,互不兼容的
JVM启动时,会形成三个类加载器组成的初始类加载器层次结构:
Bootstrap ClassLoader:根加载器,特殊的加载器,不是java.lang.ClassLoader的子类,而是由JVM实现的,负责加载Java的核心类
Extension ClassLoader:扩展类加载器,负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext)中的JAR包的类,为java扩展核心类以外的功能
System ClassLoader:系统类加载器,也称为应用类加载器,负责在JVM启动时加载来自java命令的-classpath选项,java.class.path系统属性或CLASSPATH环境变量所指定的JAR包和类路径。默认用户自定义类加载器都已类加载器作为父加载器
JVM类加载机制:
全盘负责:类加载器加载某个Class时,该Class所依赖和引用的其他Class也由该类加载器载入,除非显式使用另一个类加载器
父类委托:先让父类加载器加载Class,无法加载再尝试从自己的类路径加载类
缓存机制: 保证所有被加载过的Class都被缓存,使用某个Class时,优先从缓存里搜寻该Class对象,没有才会读取类对应的二进制数据,转换为Class对象,存入缓存区。这也是修改Class后必须重启JVM修改才会生效的原因
Java许多对象在运行时都会出现两种类型:编译时类型和运行时类型,例如:Person a = new Student();编译时类型为Person,运行时类型为Student。当程序在运行时接收到外部传入的一个对象,该对象编译时类型是Object,但程序要使用运行时类型的方法,程序需要在运行时发现对象和类的真实信息,解决方法有两种:
在编译和运行时,完全知道类型的具体信息,可以利用instanceof运算符进行判断,再利用强制类型转换
在编译时无法预知对象和类的类型,程序只能靠运行时信息来发现该对象和类的真实信息,这就必须使用用反射
获得Class对象:
使用Class类的forName(String className)方法,参数是全限定类名,可能抛出ClassNotFoundException
调用某个类的class属性来获取该类对应的Class对象
调用某个对象的getClass()方法,这是java.lang.Object类的方法,返回该对象所属类对应的Class对象
Class对象常用方法,获得类里的方法、构造器、成员变量:
Constructor<T> getConstructor(Class<?>,...parameterType)
Method getMethod(String name, Class<?>...parameterType)
Annotation[] getAnnotations()
getFileds()
注意有参数的方法必须制定相应参数
无法访问@SuppressWarnnings,因为他只能保存在源码级别上
方法的参数反射:
java8在java.lang.reflect包下Executable抽象类,该对象代表可执行的成员,该类派生了Constructor,Method两个子类。
Executable基类提供大量方法来获取方法或构造器的注解信息,修饰符,形参
int getParameterCount():获取构造器或方法形参个数
Parameter[] getParameters():获取所有形参
获取形参信息:
getModifiers():获取形参修饰符
getName():形参名
isVargs():是否形参个数可变
Class<?> getType():形参类型
通过反射来生成对象:
使用Class对象的newInstance()方法来创建该Class对象对应类的实例,要求该Class对象的对应类有默认构造器,newInstance()就是利用默认构造器来创建该类的实例
Class<?> cls = Class.forName("aaa"); return cls.newInstance();
使用Class对象获得指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例,这种方式可以选择使用指定构造器来创建实例
获取该类对象,利用Class对象的getConstructor()方法来获得指定构造器,利用Constructor的newInstance()方法来创建Java对象
类的加载