类的加载

当调用java命令运行某个java程序时,该命令将会启动一个java虚拟机,不管该程序有多么复杂,启动了多少个线程,他们都处于该java虚拟机里,同一个JVM的所有线程,所有变量都处于同一个进程里,都使用该JVM进程的内存区

以下情况JVM被终止:

  程序运行到正常结束

  程序运行到System.exit()或Runtime.getRuntime().exit()代码处结束程序

  程序执行过程中遇到未捕获的异常或者错误

  程序所在平台强制结束了JVM进程

JVM进程结束,该进程在内存中的状态将会丢失

系统通过加载,连接,初始化三个步骤加载类到内存中,这三个步骤一般连续完成,统称为类加载或类初始化。指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象

所有类都是java.lang.Class的实例

类的加载通过类加载器完成,通常由JVM提供,是所有程序运行的基础,类被加载后生成一个Class对象,接着进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中,连接分为三个阶段:验证,准备,解析,最后由虚拟机进行类的初始化,主要就是对变量初始化

加载:唯一用户可以参与的阶段,通过全限定类名把定义类的二进制字节流转化为方法区运行时数据结构,在内存中生成对应的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

连接-验证:确保Class文件的字节流帮的信息符合当前虚拟机要求,并且安全

连接-准备:在方法区为类变量(static)分配内存并设置数据类型的初始零值(不是初始化)

连接-解析:虚拟机将常量池内的符号引用(与内存布局无关,能定位到目标的字面量)替换为直接引用(与内存布局相关,可以是直接指向目标的指针,相对偏移量或者能间接定位到目标的句柄)

初始化:根据程序员定制的计划去初始化类变量和其他资源,即是执行类构造器<clinit>()方法的过程

类初始化时机:

  创建类的实例,通过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对象

posted on 2017-07-13 20:15  zawjdbb  阅读(145)  评论(0编辑  收藏  举报

导航