反射、类加载

一、类的加载机制

  Java程序运行需要使用某个类时,如果该类还没有加载到内存中,系统会通过加载、连接、初始化三个步骤来对该类进行初始化。

1.类加载

  当我们运行java.exe命令执行某个Java程序时,由于Java程序本身以.class字节码的形式存在,它不是一个可执行文件,所以需要JVM将类文件加载到内存中。

  类的加载由类加载器完成。JVM本身包含了一个类加载器,称为根类加载器。和JVM一样,根类加载器是用本地代码实现的,它负责加载核心Java类(即所有java.*开头的类)。

  另外,JVM还会提供两个类加载器,它们都是用Java语言编写的,由根类加载器加载。其中,扩展类加载器负责加载扩展的Java类,包括所有java.*开头的类和存放在JRE的扩展目录下中JAR的类包;系统类加载器负责加载应用程序自身的类。

  此外,Java API中还提供了一个ClassLoader抽象类,开发者还可以通过继承ClassLoader基类来创建自定义的类加载器。

  当我们运行Java.exe命令执行一个Java程序时,程序最基本的加载流程如下:

(1)java.exe程序搜索jre目录,寻找JVM.dll,并启动JVM。

(2)JVM运行根类加载器,该根类加载器加载Java核心API。

(3)根类加载器运行后,它会自动加载扩展类加载器和系统类加载器,并将扩展类加载器的父类设置为根类加载器,将应用加载器的父类设置为扩展类加载器。

(4)扩展类加载器加载搜索JAVA_HOME/jre/lib/ext目录,加载扩展API。

(5)应用加载器搜索CLASSPATH目录,加载我们要运行的类。

(6)类的class文件读入内存后,就会创建一个java.lang .Class对象。一旦某个类载入JVM中,同一个类就不会再次被载入。

  一个类加载后,对应的Class对象,可以通过该类的实例的getClass()方法得到。Class对象有一个getClassLoader()方法,可以得到加载该类所用到的类加载器。

2.连接

  当类被加载后,系统就为之创建一个对应的Class对象,接着就会进入连接阶段。连接阶段会负责吧类的二进制数据合并到JRE中。类连接又可以分为如下三个阶段:

(1)验证:检验被加载的类是否有正确的内部结构,并和其它类协调一致。

(2)准备:负责为类的静态属性分配内存,并设置默认初始值。

(3)解析:将类的二进制数据中的符号引用替换成直接引用。

3.初始化

  随后,JVM负责对类进行初始化,也就是对静态属性进行初始化。在Java类中,对静态属性指定初始值的方式有两种:(1)声明静态属性时指定初始值;(2)使用静态初始化块为静态属性指定初始值。

JVM 初始化一个类,一般包含如下几个步骤:

(1)加入这个类还没有被加载和连接,程序先加载并连接该类。

(2)加入该类的直接父类还没有被初始化,则先初始化其直接父类。

(3)加入该类中有初始化语句,则系统一次执行这些初始化语句。

  当执行第二步时,系统对直接父类的初始化步骤也遵循这三个步骤。如果该直接父类又有直接父类,系统再次重复着三个步骤,一次类推。所以,JVM最先初始化的总是java.lang.Object类。当程序主动使用任何一个类时,系统会保证该类以及它的所有父类都会被初始化。

  当java程序首次通过下面六种方式使用某个类或者接口时,系统就会初始化该类或者接口:

(1)创建类的实例。

(2)调用某个类的静态方法。

(3)访问某个类或接口的静态属性,或者为静态属性赋值。

(4)使用反射方式强制创建某个类或接口对应的java.lang.Class对象。

(5)初始化某个类的子类。

(6)直接使用java.exe命令运行某个主类。

二、反射

  Java程序中的对象在运行时会出现两种类型:编译时类型和运行时类型。

  反射是指在Java中,可以在运行期载入、探知和使用编译期完全未知的类。换句话说,Java程序可以装载一个运行期猜得到名称的类,获取其完整结构,并创建对象、或者对类的成员变量设定值、调用方法以及动态创建和访问数组。

1.使用反射查看类信息

  每个Java被加载后,系统都会为该类生产一个对应的java.lang.Class对象。通过Class对象,我们就可以访问JVM中的这个类。在java程序中获得Class对象的方法有三种:

(1)在编译期不知道类名,但是在运行期可以获得该类名的时候,使用Class类的forName()静态方法可以获得Class对象。

(2)如果在编译期知道类名的情况下,可以调用该类的class属性来获得该类对象的Class对象。

(3)如果一个类的实例对象已经得到,则调用该对象的getclass()方法返回该对象所属类对应的Class对象。getClass()方法是java.lang.Object类的方法之一,所以所有对象都可以调用该方法。

  一旦获得了某个类对应的Class对象,程序就可以调用Class对象的方法来获取该对象和该类的真实信息。

2.使用反射生成并操纵对象

(1)创建对象

  第一种:使用Class对象的newInstance()方法来创建该Class对象对应类的实例。这种方式要求Class对象的对应类有默认构造器,执行newInstance()方法时,实际上是调用默认的构造器来创建实例。用这种方式创建对象比较常见,后面我们要学习到很多JavaEE开源框架都是采用这种方式创建对象。

  第二种:先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用某个类的指定构造器来创建实例。

(2)调用方法

  当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()方法或getMethod()方法获取全部方法或者指定方法。getMethods()和getMethod()方法返回值是一个Method对象数组或者Method对象。每个Method对象对应一个方法,获得Method对象后,程序就可以通过该Method对象的invoke()方法来调用对应的方法。

步骤:

第一:通过Class.forName()或者类名.class或者对象名.getClass()任意一种方法,获得Class对象;

第二:通过Class对象的getMethod()方法获取要调用的方法的Method对象;

第三:调用获取的Method对象的invoke()方法,来调用对应的类方法。

(3)访问属性值

  通过Class对象的getFields()方法可以获取一个类全部属性或指定属性。这两个方法 返回值是一个Field对象数组或Field对象提供了如下两组方法来访问属性:

第一种:getXXX(Object o):获取o对象该Field的属性值。这里的XXX对应八种基本数据类型,如果是引用型,则取消get后面的xxx.

第二种:setXXX(Object o,XXX val):将o对象的该Field的值设置为val。这里的XXX对应八种基本数据类型,如果是引用类型,则取消get后面的XXX。

使用这两种方法可以随意访问指定对象的所有属性。

(4)动态创建和访问数组

  当我们不能确定数组的大小时,比如公司的员工,如果我们声明一个固定大小200 数组来存储员工,可能会存在浪费空间或者数组大小不够的情况。正确的做法是应该根据运行时员工的人数,确定数组的大小。利用反射机制,我们可以动态创建数组并访问数组元素。

java.lang.reflect包中的Array类提供了动态创建及访问数组元素数组的方法,这些方法都是静态方法。

  

时间: 2024-10-05 03:15:20

反射、类加载的相关文章

反射类加载

java中的反射技术:运行时探究和使用编译时未知的类. 反射的核心原理:JVM在加载一个类的时候,会把该类的信息存放到一个Class对象中,该对象又被称为模板对象,JVM可以通过检索对象得到这个类的所有信息. JDK提供API,允许程序员获取到类的Class对象,导致程序员也可以检索到这个类的信息, 即使这个类不是程序或程序员所实现的. 获取Class对象又3种方法. 1,通过类型名取Class对象,所有的类型都可以获得Class对象,这种方式没有动态性,以为在编译时已知类型名. 2,通过对象或

反射 类加载

初学反射,学习内容做了一些笔记,比较杂乱: 1.类是java.lang.Class类的实例对象 2.new 创建对象,是静态加载类,在编译时刻就需要加载可能使用到的类. 3.Class c = user.getClass(); //已知该类的对象,可通过getClass方法获取该类的类类型   Class c = Class.forName("类的全称"); //不仅表示了该类的类类型,还代表了动态加载类,运行时加载类是动态加载   Word w = (Word)c.newInstan

java之反射概述

类加载器和反射  类加载器: 1 类的加载过程: 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载.连接.初始化三步骤来实现对这个类进行初始化. 加载:就是指将class文件读入内存,并为之创建一个Class对象.任何类使用时系统都会建立一个Class对象 连接:验证,是否有正确的内部结构,并和其他类协调一致. 准备,负责为类的静态成员分配内存,并设置默认初始化值 解析,将类的二进制数据中的符号引用替换为直接引用 初始化:开辟栈.堆内存空间,创建对象.默认初始化.显示初始化.构

java高级之反射

--- android培训.java培训.期待与您交流! ---- java高级之反射 一 反射(类的加载概述和加载时机) A:类的加载概述 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载 就是指将class文件读入内存,并为之创建一个Class对象.任何类被使用时系统都会建立一个Class对象. 连接 验证 是否有正确的内部结构,并和其他类协调一致 准备 负责为类的静态成员分配内存,并设置默认初始化值 解析 将类的二进制数据

JavaEE基础(二十七)/反射、JDK新特性

1.反射(类的加载概述和加载时机) A:类的加载概述 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载  就是指将class文件读入内存,并为之创建一个Class对象.任何类被使用时系统都会建立一个Class对象. 连接 验证 是否有正确的内部结构,并和其他类协调一致 准备 负责为类的静态成员分配内存,并设置默认初始化值 解析 将类的二进制数据中的符号引用替换为直接引用 初始化 就是我们以前讲过的初始化步骤 B:加载时机 创建类

Java-reflect(反射)初步理解_1

27.01_反射(类的加载概述和加载时机) A:类的加载概述 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载 就是指将class文件读入内存,并为之创建一个Class对象.任何类被使用时系统都会建立一个Class对象. 连接 验证 是否有正确的内部结构,并和其他类协调一致 准备 负责为类的静态成员分配内存,并设置默认初始化值 解析 将类的二进制数据中的符号引用替换为直接引用 初始化 就是我们以前讲过的初始化步骤 B:加载时机

注解与反射-复习

目录 注解 内置注解 元注解 自定义注解 反射机制 简介 Class类 Java内存 类加载的过程 ClassLoader 反射获取运行时类的结构信息 动态创建对象 通过反射创建对象 (一般情况) 通过"构造器对象"创建对象 (不存在无参构造) 通过反射调用成员: method field 测试: 性能分析 反射获取泛型数据 反射获取注解数据 基于B站秦疆老师的课. 感谢! 注解 @since JDK1.5 不是程序本身, 可以对程序做出解释, 可以被其他程序读取(如 编译器) 内置注

2016/5/3 复习

异常处理   try  cathc  finally        Exception表示检查异常    RuntimeException运行时异常     thow抛出异常   thows声明异常 如果父类声明了异常 子类重写父类声明异常 不能比父类的异常多  也不能大余父类异常的声明 常用类  :  System类  系统类     常用方法: currentTimeMillis()返回以毫秒为单位的时间 exit(int status)  终止当前正在运行的JAVA虚拟机     getP

第二十六天笔记

黑马程序员 <a href="http://www.itheima.com" target="blank">java培训</a> 1.反射(类的加载概述和加载时机) A:类的加载概述    当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化.   1)加载          就是指将class文件读入内存,并为之创建一个Class对象.任何类被使用时系统都     会建立一个Class

JAVAOO 11 12 15 13 章

异常处理 异常(exception) 是程序在执行过程中所产生的问题 异常分类: 1  检查异常 2  运行异常 3  错误 对异常的处理,方法有三种: 1.捕获异常,不让它沿着调用闸继续向下抛出 2.捕获异常 并让它向下抛出 3.不捕获异常 RuntimeException以及子类是运行时异常,其它都是检查异常 try没有异常时不会执行catch() try出现异常之后 其它代码不会执行 跳到对应捕获异常的catch() 语句块执行 运行时异常在运行过程中才抛出 先捕获特殊异常 后捕获一般异常