Java-Java反射
- 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中,步骤如下:
- 装载:查找和导入Class
- 链接:执行校验、准备和解析步骤(解析步骤可选)
- 初始化:对类的静态变量、静态代码块执行初始化工作
其中第二步操作包括:
(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反射相关推荐
- 【Java】反射( reflection)机制 详解
目录 1. 定义 2. 用途(了解即可) 3. 反射的基本信息 4. 反射相关的类(重要) 4.1 Class类(反射机制的起源 ) 4.2 Class类中的相关方法(方法的使用方法在后边的示例当中) ...
- java 泛型反射_Java使用反射来获取泛型信息示例
本文实例讲述了Java使用反射来获取泛型信息.分享给大家供大家参考,具体如下: 一 点睛 获得了Field对象后,就可以很容易地获得该Field的数据类型,即使用如下代码即可获得指定Field的类型: ...
- java初反射_初始 java 反射机制 (一)
反射机制详解 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为j ...
- Java中反射的三种常用方式
Java中反射的三种常用方式 package com.xiaohao.test; public class Test{ public static void main(String[] args) t ...
- Java的反射机制 工厂模式综合讲解【转载自51CTO】
2019独角兽企业重金招聘Python工程师标准>>> Java的反射机制 工厂模式综合讲解 1.什么叫反射 Java.lang.reflect包下 正常情况下我们可以通过类实例化一 ...
- java反射机制(三)---java的反射和代理实现IOC模式 模拟spring
IOC(Inverse of Control)可翻译为"控制反转",但大多数人都习惯将它称为"依赖注入".在Spring中,通过IOC可以将实现类.参数信息等配 ...
- [Java基础] 反射机制汇总
引言 初学Java反射机制的时候,只是感觉很神奇,但是不知道学了该怎么用,所以过了一段时间就忘得差不多了:最近接触到了框架,在学习中遇到了反射,深深体会到了反射机制的神奇,回来复习了一下反射机制,写一 ...
- Java 利用反射实现C#的委托
一, 观察者模式的缺点 在之前的博文的介绍过观察者模式了. 观察者模式可以让多个观察者同时观察1个被观察者. 也就说被观察者可以1次过执行所有观察者的update()方法. 再通俗d来讲, 就是多个观 ...
- sqlite字段是否存在_学习廖雪峰的JAVA教程---反射(访问字段)
对任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息. 我们先看看如何通过Class实例获取字段信息.Class类提供了以下几个方法来获取字段: Field getFie ...
- 深入分析 Java 方法反射的实现原理
2019独角兽企业重金招聘Python工程师标准>>> 方法反射实例 public class ReflectCase { public static void main(Strin ...
最新文章
- Android 水波效果 | 涟漪效果 实现
- ping 不通 华为三层交换机vlan_华为交换机常用的三种vlan划分方法,一文了解清楚vlan...
- python培训出来的有公司要吗-参加Python培训到底需要学什么?好程序员
- 如何设计高可用的微服务架构
- C语言学习之求1+2+3+···+100的值
- maven设置本地仓库路径
- 内核中用于数据接收的结构体struct msghdr以及iovec介绍
- C++的Matlab接口
- mips j指令_MIPS的基本实现
- 浅析“高斯白噪声”,“泊松噪声”,“椒盐噪声”的区别
- CentOS 7.0 yum安装Apache、PHP和MySQL
- caffeine 读操作源码走读 为什么读这么快
- linux load average,理解Linux中的Load Average
- 【Oracle】ERROR: ORA-28000: the account is locked
- linux下keepalived+nginx的负载均衡搭建
- html5系列:notification api升级——从webkitNotifications到Notification
- RHCSA与RHCE红帽认证考试心得
- 基于Python的深度学习的中文语音识别系统
- html怎么自动弹出模态框,纯CSS实现带点击模态框外部自动关闭的模态框
- 计算机在表格顶端添加标题,电脑Excel表格在打印时怎么设置每页有相同的顶端标题和底端标题...