• Java反射概述
  • 示例
    • Code
    • 分析
  • 类装载器ClassLoader
    • 工作机制
    • ClassLoader分类
    • 全盘负责委托机制
    • 重要方法
      • loadClassString name
      • defineClassString name byte b int off int len
      • findSystemClassString name
      • findLoadedClassString name
      • getParent
    • 总结
  • Java反射机制
    • 三个主要的反射类

      • Constructor
      • Method
      • Field

Java反射概述

Java语言允许通过程序化的方式间接对Class进行操作。

Class文件由类装载器装载后,在JVM中形成一份描述Class结构的元信息对象,通过该元对象可以获知Class的结构信息,如构造函数、属性和方法等。

Java允许用户借由这个与Class相关的元信息对象间接调用Class对象的功能, 这就为使用程序化方式操作Class对象开辟了途径。

使用反射不同于常规的Java编程,其中它与 元数据–描述其它数据的数据协作。Java语言反射接入的特殊类型的原数据是JVM中类和对象的描述。

Java反射机制可以让我们在编译期(Compile Time)之外的运行期(Runtime)获得任何一个类的字节码。包括接口、变量、方法等信息。还可以让我们在运行期实例化对象,通过调用get/set方法获取变量的值。


示例

Code

我们将用下面这个例子来了解Java反射机制。

package com.xgj.master.ioc.reflect;public class Car {private String brand ;private String color;private int speed;public String getBrand() {return brand;}public void setBrand(String brand) {this.brand = brand;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}/*** * @Title:Car* @Description:默认构造函数*/public Car(){}/*** * @Title:Car* @Description:带参构造函数* @param brand* @param color* @param speed*/public Car(String brand ,String color ,int speed){this.brand = brand;this.color = color;this.speed = speed;}public void introduceCar(){System.out.println("Car : " + this.brand + " , " + this.color + " , " + this.speed);}}

通常情况下,我们实例化类,调用类中的方法如下:

    Car car = new Car("BMW","Black",180);car.introduceCar();

输出: Car : BMW , Black , 180

这是使用传统的方式来直接调用目标类的方法。

如果使用Java的反射机制 该如何控制目标类呢?

来看代码

package com.xgj.master.ioc.reflect;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;public class ReflectTest {public static Car initCarByDefaultConstrut() throws Exception {// (1)通过类装载器获取Car类对象ClassLoader loader = Thread.currentThread().getContextClassLoader();Class claz = loader.loadClass("com.xgj.master.ioc.reflect.Car");// (2)获取类的默认构造函数,并通过它实例化CarConstructor constructor = claz.getDeclaredConstructor(null);Car car = (Car) constructor.newInstance();// (3)通过反射方法设置属性Method method = claz.getMethod("setBrand", String.class);method.invoke(car, "BMW");Method method2 = claz.getMethod("setColor", String.class);method2.invoke(car, "black");Method method3 = claz.getMethod("setSpeed", int.class);method3.invoke(car, 100);return car;}public static void main(String[] args) throws Exception {initCarByDefaultConstrut().introduceCar();}}

运行结果: Car : BMW , black , 100


分析

我们完全可以通过编程方式调用Class的各项功能,与通过构造函数和方法直接调用类的功能的效果是一致的,只不过是间接调用罢了。

几个重要的反射类

  • ClassLoader
  • Class
  • Constructor
  • Method .
    通过这些反射类我们就可以间接的调用目标Class的各项功能。

在(1)处,我们获取当前线程的ClassLoader, 然后通过指全限定类名com.xgj.master.ioc.reflect.Car 来装载Car类对应的反射实例。

在(2)处,我们通过Car的反射类对象获取Car的默认构造函数对象,通过构造函数对象的newInstance()方法实例化Car对象,等同于 new Car()

在(3)处,我们又通过Car的反射类对象的getMethod(String methodName, Class paramsClass)获取属性的Setter方法对象,其中第一个参数是目标Class的方法名,第二个参数是方法入参的对象类型。

在获取到方法反射对象后,就可以通过invoke(Object ob, Object param)方法调用目标类的方法了。 该方法的第一个禅师是操作的目标类对象实例,第二个参数目标方法的入参。


类装载器ClassLoader

工作机制

类装载器就是寻找类的字节码文件并构造类在JVM内部表示对象的组件。

类装载器把一个类装入JVM中,步骤如下:

  1. 装载:查找和导入Class
  2. 链接:执行校验、准备和解析步骤(解析步骤可选)
  3. 初始化:对类的静态变量、静态代码块执行初始化工作
    其中第二步操作包括:
    (1). 检验:检查载入Class文件数据的正确性
    (2). 准备:给类的静态变量分配存储空间
    (3). 解析:将符号引用转换为直接引用

类装载工作由ClassLoader及其子类负责,负责在运行时查找和装入Class直接码文件。


ClassLoader分类

JVM运行期间会产生3个ClassLoader

  • 根装载器
  • ExtClassLoader(扩展类装载器)
  • AppClassLoader(应用类装载器)

    其中 ExtClassLoader和AppClassLoader 是 ClassLoader的子类
    根装载器不是ClassLoader的子类,它是C++编写。

  • 根装载器负责装载JRE的核心类库,比如JRE目标下的JAR

  • ExtClassLoader负责装载JRE扩展目录ext中的JAR包

  • AppClassLoader负责装载应用Classpath路径下的类包

三者关系: 根装载器是ExtClassLoader的父装载器,ExtClassLoader是AppClassLoader的父装载器。 默认情况下,使用AppClassLoader来装载应用程序的类。

验证下:

package com.xgj.master.ioc.classloader;public class ClassLoaderTest {public static void main(String[] args) {// TODO Auto-generated method stubClassLoader classLoader = Thread.currentThread().getContextClassLoader();System.out.println("current loader:" + classLoader);System.out.println("parent laoder:" + classLoader.getParent());System.out.println("grandparent laoder:" + classLoader.getParent().getParent());}}

输出:

current loader:sun.misc.Launcher$AppClassLoader@8391b0c
parent laoder:sun.misc.Launcher$ExtClassLoader@5d1eb50b
grandparent laoder:null

根装载器在Java中无法获取到它的句柄,因此返回null .

全盘负责委托机制

JVM装载类时使用“全盘负责委托机制”。

全盘负责:是指当一个ClassLoader装载一个类时,除非显示地使用另外一个ClassLoader,该类所依赖以及引用的类也由这个ClassLoader载入。

委托机制:是指先委托父类装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。

这一点是从安全角度考虑,举个例子,比如有人恶意编写了一个基础类如java.lang.String 并装载到JVM中,如果没有委托机制,jvm就会读取了这个恶意基础类,全盘负责委托机制保证了java.lang.String永远由根装载器来装载,避免了安全隐患的发生。

如何查看JVM从哪个JAR包中加载指定类呢?

请看 Java-查看JVM从哪个JAR包中加载指定类


重要方法

loadClass(String name)

 public Class<?> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);}
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}

name参数指定类装载器需要装载的类的名字,必须使用全限定类名。

该方法有一个重载方法 loadClass(String name ,boolean resolve) .resolve参数告诉类装载器是否需要解析该类, 如果JVM只需要知道该类是否存在或者找出该类的超类,那么就不需要进行解析。


defineClass(String name, byte[] b, int off, int len)

将类文件的字节数组转换成JVM内部的java.lang.Class对象。 参数name为字节数组对应的全限定类名。


findSystemClass(String name)

protected final Class<?> findSystemClass(String name)throws ClassNotFoundException{ClassLoader system = getSystemClassLoader();if (system == null) {if (!checkName(name))throw new ClassNotFoundException(name);Class cls = findBootstrapClass(name);if (cls == null) {throw new ClassNotFoundException(name);}return cls;}return system.loadClass(name);}

从本地文件系统装载Class文件,不存在则抛出ClassNotFoundException。 该方法为AJVM默认使用的装载机制。

findLoadedClass(String name)

调用该方法查看ClassLoader是否已经载入某个类,如果载入,返回java.lang.Class对象,否则返回null.

如果强行装载已经存在的类,将抛出链接错误。


getParent()

  @CallerSensitivepublic final ClassLoader getParent() {if (parent == null)return null;SecurityManager sm = System.getSecurityManager();if (sm != null) {checkClassLoaderPermission(parent, Reflection.getCallerClass());}return parent;}

获取类装载器的父装载器。 除了根装载器外,所有的类装载器都有且有且只有一个父装载器。 ExtClassLoader的父装载器是根装载器。 因为根装载器非Java语言编写,因此无法获得,返回null.


总结

除了JVM默认的3个ClassLoader外,用户也可以编写自己的第三方类装载器,以实现一些特殊的需求。

类文件被装载并解析后,在JVM内将拥有一个对应的java.lang.Class类描述对象,该类的实例都拥有指向这个类描述对象的引用,而类描述对象又拥有指向关联ClassLoader的引用。

如下图《类实例、类描述对象及装载器的关系》所示

每个类在JVM中都有一个对应的java.lang.Class对象。它提供了类结构信息的描述。

Class没有public的构造方法,Class对象是在装载类时由JVM通过调用类装载器中的defineClass()方法自动构造的。


Java反射机制

Class反射对象描述类定义结构,可以从Class对象中获取构造函数、成员变量、方法类等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。

这些反射对象定义在java.lang.reflect包中。

三个主要的反射类

Constructor

类的构造函数反射类。

通过Class#getConstructors()方法可以获取类的所有构造函数反射对象数组。

在Java5.0中,还可以通过getConstructor(Class...parameterTypes)获取拥有特定入参的构造函数反射对象。

Constructor的一个主要方法是newInstance(Object[] initargs),通过该方法可以创建一个对象类的实例。相当于new关键字。

在Java5.0中,该方法演化为更为灵活的形式:newInstance(Object...initargs)


Method

类方法的反射类。

通过Class#getDeclaredMethods()方法可以获取类的所有方法反射类对象数组Method[].

在Java5.0中,可以通过getDeclaredMethod(String name,Class...parameterTypes)获取特定签名的方法。其中name为方法名,Class...为方法入参类型列表。

Method最主要的方法是invoke(Object obj , Object[] args) , 其中obj表示操作的目标对象;args为方法入参。

在Java5.0中,该方法调整为invoke(Object obj, Object...args) .

此外,其他比较常用的方法:

  • Class getReturnType():获取方法的返回值烈性
  • Class[] getParamaterTypes():获取方法的入参类型数组
  • Class[] getExceptionTypes() 获取该方法的异常类型数组
  • Annotation[][] getParameterAnnotations() 获取方法的注解洗洗,是Java5.0中新增的方法。

Field

类的成员变量的反射类。

通过Class#getDeclaredFields()方法可以获取类的成员变量反射对象数组,
通过Class#getDeclaredField(String name)则可以获取某个特定名称的成员变量反射对象。

Field类的主要方法是set(Object obj , Object value) 其中obj表示操作的目标对象,通过value为目标对象的成员变量设置值。

如果成员变量为基础类型,则可以使用Field类中提供的带类型名的值设置方法,比如setBoolean(Object obj , Object value)、setInt(Object obj , Object value)等。

此外Java还未包提供了Package反射类,在Java5.0中还未注解提供了AnnotatedElement反射类。

对于private或者procted成员变量和方法,只要JVM的安全机制允许,也可以通过反射调用。比如:

package com.xgj.master.ioc.reflect;public class PrivateCar {private String brand;protected void introduce() {System.out.println("brand:" + brand);}}
package com.xgj.master.ioc.reflect;import java.lang.reflect.Field;
import java.lang.reflect.Method;public class PrivateCarTest {public static void main(String[] args) throws Exception {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Class claz = classLoader.loadClass("com.xgj.master.ioc.reflect.PrivateCar");PrivateCar pcar = (PrivateCar) claz.newInstance();Field field = claz.getDeclaredField("brand");// 取消Java语言访问检查以便访问private变量field.setAccessible(true);field.set(pcar, "BMW");Method method = claz.getDeclaredMethod("introduce", (Class[]) null);// 取消Java语言访问检查以便访问protected方法method.setAccessible(true);method.invoke(pcar, (Object[]) null);}}

在访问private 或者 protected成员变量和方法时,必须通过setAccessible(boolean access)方法取消Java语言检查,否则将抛出IllegalAccessException. 如果JVM的安全管理器(SecurityManager)设置了相应的安全机制,那么调用该方法会抛出SecurityException

Java-Java反射相关推荐

  1. 【Java】反射( reflection)机制 详解

    目录 1. 定义 2. 用途(了解即可) 3. 反射的基本信息 4. 反射相关的类(重要) 4.1 Class类(反射机制的起源 ) 4.2 Class类中的相关方法(方法的使用方法在后边的示例当中) ...

  2. java 泛型反射_Java使用反射来获取泛型信息示例

    本文实例讲述了Java使用反射来获取泛型信息.分享给大家供大家参考,具体如下: 一 点睛 获得了Field对象后,就可以很容易地获得该Field的数据类型,即使用如下代码即可获得指定Field的类型: ...

  3. java初反射_初始 java 反射机制 (一)

    反射机制详解 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为j ...

  4. Java中反射的三种常用方式

    Java中反射的三种常用方式 package com.xiaohao.test; public class Test{ public static void main(String[] args) t ...

  5. Java的反射机制 工厂模式综合讲解【转载自51CTO】

    2019独角兽企业重金招聘Python工程师标准>>> Java的反射机制 工厂模式综合讲解 1.什么叫反射 Java.lang.reflect包下 正常情况下我们可以通过类实例化一 ...

  6. java反射机制(三)---java的反射和代理实现IOC模式 模拟spring

    IOC(Inverse of Control)可翻译为"控制反转",但大多数人都习惯将它称为"依赖注入".在Spring中,通过IOC可以将实现类.参数信息等配 ...

  7. [Java基础] 反射机制汇总

    引言 初学Java反射机制的时候,只是感觉很神奇,但是不知道学了该怎么用,所以过了一段时间就忘得差不多了:最近接触到了框架,在学习中遇到了反射,深深体会到了反射机制的神奇,回来复习了一下反射机制,写一 ...

  8. Java 利用反射实现C#的委托

    一, 观察者模式的缺点 在之前的博文的介绍过观察者模式了. 观察者模式可以让多个观察者同时观察1个被观察者. 也就说被观察者可以1次过执行所有观察者的update()方法. 再通俗d来讲, 就是多个观 ...

  9. sqlite字段是否存在_学习廖雪峰的JAVA教程---反射(访问字段)

    对任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息. 我们先看看如何通过Class实例获取字段信息.Class类提供了以下几个方法来获取字段: Field getFie ...

  10. 深入分析 Java 方法反射的实现原理

    2019独角兽企业重金招聘Python工程师标准>>> 方法反射实例 public class ReflectCase { public static void main(Strin ...

最新文章

  1. Android 水波效果 | 涟漪效果 实现
  2. ping 不通 华为三层交换机vlan_华为交换机常用的三种vlan划分方法,一文了解清楚vlan...
  3. python培训出来的有公司要吗-参加Python培训到底需要学什么?好程序员
  4. 如何设计高可用的微服务架构
  5. C语言学习之求1+2+3+···+100的值
  6. maven设置本地仓库路径
  7. 内核中用于数据接收的结构体struct msghdr以及iovec介绍
  8. C++的Matlab接口
  9. mips j指令_MIPS的基本实现
  10. 浅析“高斯白噪声”,“泊松噪声”,“椒盐噪声”的区别
  11. CentOS 7.0 yum安装Apache、PHP和MySQL
  12. caffeine 读操作源码走读 为什么读这么快
  13. linux load average,理解Linux中的Load Average
  14. 【Oracle】ERROR: ORA-28000: the account is locked
  15. linux下keepalived+nginx的负载均衡搭建
  16. html5系列:notification api升级——从webkitNotifications到Notification
  17. RHCSA与RHCE红帽认证考试心得
  18. 基于Python的深度学习的中文语音识别系统
  19. html怎么自动弹出模态框,纯CSS实现带点击模态框外部自动关闭的模态框
  20. 计算机在表格顶端添加标题,电脑Excel表格在打印时怎么设置每页有相同的顶端标题和底端标题...

热门文章

  1. mit risc-v 资料
  2. 运行caffe自带的mnist实例教程
  3. CNN 卷积神经网络TensorFlow简单实现
  4. mysql 最长字符串_那些年的Mysql
  5. spark计算TF-IDF值(中文)
  6. geyser学习记录(day1):将任务拆分的架构
  7. 文巾解题 567. 字符串的排列
  8. tableau必知必会之教你做品牌树的树梢(分类桑基图补丁)
  9. 成为指标的“绝地武士”:tableau创建指标的 10 个技巧和窍门
  10. 一行代码制作你的专属动态二维码-Python实现