Java反射机制浅析

时间:2022-09-27 17:11:34

概念

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

  Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含了FieldMethod以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时创建的,用以表示未知类里对应的成员。这样开发人员就可以使用Constructor创建新的对象,用get()set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields()getMethods()getConstructors()等很便利的方法,以返回表示字段、方法以及构造器的对象的数组。这样,匿名对象的类信息就能在运行时被完全确认下来,而在编译时不需要知道任何事情。

Class

  类是程序的一部分,每个类都有一个Class对象。换言之,每当编写并且编译一个新类,就会产生一个Class对象(更恰当地说,是被保存在一个同名的.class文件中),它包含了与类有关的信息。为了生成这个类的对象,运行这个对象的虚拟机(JVM)将使用被称为“类加载器(ClassLoader)”的子系统。下面测试类的一些最基本信息。

public class ClassInfo {

    /**
* @description 输出不同格式类名
* @param clazz
*/
public static void printName(Class<?> clazz) {
System.out.println("getName: " + clazz.getName());
System.out.println("getCanonicalName: " + clazz.getCanonicalName());
System.out.println("getSimpleName: " + clazz.getSimpleName());
} /**
* @description 输出类的父类和接口
* @param clazz
*/
public static void printClassIntf(Class<?> clazz) {
Class<?> superClass = clazz.getSuperclass();
Class<?>[] interfaces = clazz.getInterfaces();
if(superClass != null) {
System.out.print(clazz.getSimpleName() + " extends " + superClass.getSimpleName());
}
if(interfaces.length > 0) {
System.out.print(" implements ");
for(int i = 0; i < interfaces.length - 1; i++) {
System.out.print(interfaces[i].getSimpleName() + ", ");
}
System.out.println(interfaces[interfaces.length - 1].getSimpleName());
}
}
}

测试类 测试用例:ArrayList

import org.junit.After;
import org.junit.Before;
import org.junit.Test; public class ClassInfoTest { private Class<?> clazz; private String className = "java.util.ArrayList"; /**
* forName()是获取Class对象的引用的一种方法。
* 它是用一个包含目标类的文本名的String作输入参数,返回的是一个Class对象的引用。
*/
@Before
public void before() {
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} @After
public void after() {
clazz = null;
} @Test
public void testGetName() {
ClassInfo.printName(clazz);
} @Test
public void testPrintClassIntf() {
ClassInfo.printClassIntf(clazz);
} }

测试结果

getName: java.util.ArrayList
getCanonicalName: java.util.ArrayList
getSimpleName: ArrayList
ArrayList extends AbstractList implements List, RandomAccess, Cloneable, Serializable

Constructor

  Constructor类是对Java普通类中的构造器的抽象。通过Class类的getConstructors()方法可以取得表示构造器的对象的数组,通过getConstructor(Class<?>... parameterTypes)可以取得指定参数类型的构造器。通过newInstance(Object... initargs)方法可以构建一个实例对象。需要注意的是:newInstance()方法的参数要和getConstructor()方法参数相对应。例如 getConstructor(String.class) --- getInstance("Jack")

  以下的测试都是假设我们从磁盘上或者网络中获取一个类的字节,得知这个类的包名(reflcet)和类名(Reflect)和相关字段名称和方法名称,并通过热加载已经加载到工程中。

  method.invoke()会在下文Method中讲到。

待测类

package reflect;

/**
* @description 运行时获取的类
* @author Administrator
*/
public class Reflect { public int id;
private String name; public Reflect() {
this.name = "Tom";
} public Reflect(String name) {
this.name = name;
} public Reflect(int id, String name) {
this.id = id;
this.name = name;
} public void setName(String name) {
this.name = name;
} public String getName() {
return name;
} @SuppressWarnings("unused")
private void setId(int id) {
this.id = id;
} public int getId() {
return id;
} @Override
public String toString() {
return "id:" + id + ", name:" + name;
} }

测试类

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import org.junit.After;
import org.junit.Before;
import org.junit.Test; public class ReflectTest { Class<?> clazz; /**
* className = "包名.类名"
*/
String className = "reflect.Reflect"; @Before
public void before() {
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} @After
public void after() {
clazz = null;
} @Test
public void testConstructor() {
try {
/**
* 获取无参构造器
*/
Constructor<?> constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
Method method = clazz.getMethod("getName");
assertThat((String)method.invoke(obj), containsString("Tom"));
/**
* 获取带参构造器
*/
constructor = clazz.getConstructor(String.class);
obj = constructor.newInstance("Jack");
assertThat((String)method.invoke(obj), containsString("Jack"));
/**
* 获取多个参数构造器
*/
constructor = clazz.getConstructor(int.class, String.class);
obj = constructor.newInstance(6, "Rose");
method = clazz.getMethod("toString");
assertThat((String)method.invoke(obj), allOf(containsString("id:6"), containsString("name:Rose")));
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} } }

Field

  Field类是对Java普通类中的属性或者称字段的抽象。通过Class类的getFields()方法可以取得表示字段的对象的数组,通过getField(String name)获取给定名称的字段的对象,如果字段修饰符为privateprotected,则getField()方法会抛出java.lang.NoSuchFieldException异常。对于非公有的属性的设定,可以使用getDeclaredField()方法,并调用setAccessible(true),使属性可获得。

测试方法

@Test
public void testField() {
try {
/**
* Class类的newInstance()方法会调用默认构造函数创建一个实例对象
*/
Object obj = clazz.newInstance();
Method method = clazz.getMethod("getName");
assertThat((String)method.invoke(obj), containsString("Tom"));
/**
* 设定private属性的值
*/
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(obj, "Jack");
assertThat((String)method.invoke(obj), containsString("Jack"));
/**
* 设定public属性的值
*/
field = clazz.getField("id");
field.setInt(obj, 9);
method = clazz.getMethod("getId");
assertThat(String.valueOf(method.invoke(obj)), containsString("9"));
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}

Method

  Method类是对Java普通类中的方法的抽象。通过Class类的getMethods()方法可以取得表示方法的对象的数组,通过getMethod(String name, Class<?>... parameterTypes)方法可以取得指定方法名称以及方法参数类型的方法的对象,如果方法修饰符为private或protected,getMethod()方法会抛出java.lang.NoSuchMethodException异常。对于非公有的方法,可以通过getDeclaredMethod()方法,并调用setAccessible(true),使方法可获得。调用method.invoke(Object obj, Object... args)方法,实现obj对象对方法method的调用,参数为args。和构造器同样的道理,getMethod()方法和invoke方法要相对应。例如getMethod("setName", String.class) --- invoke(obj, "Rose")

测试方法

@Test
public void testMethod() {
try {
/**
* 调用无参公有方法
*/
Object obj = clazz.newInstance();
Method method1 = clazz.getMethod("getName");
assertThat((String)method1.invoke(obj), containsString("Tom"));
/**
* 调用带参公有方法
*/
Method method2 = clazz.getMethod("setName", String.class);
method2.invoke(obj, "Jack");
assertThat((String)method1.invoke(obj), containsString("Jack"));
/**
* 调用带参私有方法
*/
Method method3 = clazz.getDeclaredMethod("setId", int.class);
method3.setAccessible(true);
method3.invoke(obj, 5);
Method method = clazz.getMethod("getId");
assertThat(String.valueOf(method.invoke(obj)), containsString("5"));
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}

类方法提取器

  当开发者学习一个类(比如:ArrayList)时,通过浏览实现了类定义的源代码或是其JDK文档,只能找到在这个类定义中被定义或被覆盖的方法。但对开发者来说,可能有数十个更有用的方法都是继承自基类的。要找出这些方法可能很乏味且费时。幸运的是,反射机制提供了一个方法,使开发者能够编写可以自动展示完整接口的简单工具。工作方式如下:

/**
* @description 类方法提取器
* @param clazz
*/
public static void printClassInfo(Class<?> clazz) {
Pattern pattern = Pattern.compile(("\\w+\\."));
Constructor<?>[] constructors = clazz.getConstructors();
Method[] methods = clazz.getMethods();
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields) {
System.out.println(pattern.matcher(field.toGenericString()).replaceAll(""));
}
for(Constructor<?> constructor : constructors) {
System.out.println(pattern.matcher(constructor.toGenericString()).replaceAll(""));
}
for(Method method : methods) {
System.out.println(pattern.matcher(method.toGenericString()).replaceAll(""));
}
}

测试方法 测试用例:ArrayList

@Test
public void testPrintClassInfo() {
try {
ClassInfo.printClassInfo(Class.forName("java.util.ArrayList"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

测试结果:

private static final long serialVersionUID
private static final int DEFAULT_CAPACITY
private static final Object[] EMPTY_ELEMENTDATA
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA
transient Object[] elementData
private int size
private static final int MAX_ARRAY_SIZE
public ArrayList(Collection<? extends E>)
public ArrayList()
public ArrayList(int)
public boolean add(E)
public void add(int,E)
public boolean remove(Object)
public E remove(int)
public E get(int)
public Object clone()
public int indexOf(Object)
public void clear()
public boolean contains(Object)
public boolean isEmpty()
public Iterator<E> iterator()
public int lastIndexOf(Object)
public void replaceAll(UnaryOperator<E>)
public int size()
public List<E> subList(int,int)
public <T> T[] toArray(T[])
public Object[] toArray()
public Spliterator<E> spliterator()
public boolean addAll(int,Collection<? extends E>)
public boolean addAll(Collection<? extends E>)
public void forEach(Consumer<? super E>)
public E set(int,E)
public void ensureCapacity(int)
public void trimToSize()
public ListIterator<E> listIterator(int)
public ListIterator<E> listIterator()
public boolean removeAll(Collection<?>)
public boolean removeIf(Predicate<? super E>)
public boolean retainAll(Collection<?>)
public void sort(Comparator<? super E>)
public boolean equals(Object)
public int hashCode()
public String toString()
public boolean containsAll(Collection<?>)
public final void wait() throws InterruptedException
public final void wait(long,int) throws InterruptedException
public final native void wait(long) throws InterruptedException
public final native Class<?> getClass()
public final native void notify()
public final native void notifyAll()
public default Stream<E> stream()
public default Stream<E> parallelStream()

JVM

  反射机制并没有什么神奇之处。当通过反射与一个未知类型的对象打交道时,JVM只是简单的检查这个对象。因此,那个类的.class文件对于JVM来说必须是可获取的:要么在本地机器上,要么可以通过网络取得。对已反射机制来说,.class文件在编译时时不可获取的 所以是在运行时打开和检查.class文件。

应用

  假设开发者从磁盘文件,或者网络连接中获取一串字节,并且被告知这些字节代表了一个类。既然这个类在程序编译后很久才出现,若想使用这个类,就需要采用发射机制。热加载就属于这种场景。

  在运行时获取类的信息的另一个场景,开发者希望提供在跨网络的远程平台上创建和运行对象的能力。这被称为远程方法调用(RMI) 它允许一个Java程序将对象分布到多台机器上。

  在自己写框架时候,开发者肯定会用到反射,很简单的例子就是事件总线和注解框架。

总结

  反射很灵活,在日常开发中,慎用少用反射,反射会牺牲部分性能。在写框架时,不避讳反射,在关键时利用反射助自己一臂之力。  

Java反射机制浅析的更多相关文章

  1. Java 反射机制浅析

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

  2. &lt&semi; 转&gt&semi;Java 反射机制浅析

    一.什么是反射: 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.这一概念的提出很快引发了计算机科学领 域关于应用反射性的研究.它首先被程序 ...

  3. 浅析Java反射机制

    目前,在项目中使用Java反射机制(除Spring框架)的地方不多,但为后续准备,简单将最近的反射体会总结如下: 1. 按光学中的反射,可以将java中的反射理解为“镜像”.有以下用途: Java反射 ...

  4. 第28章 java反射机制

    java反射机制 1.类加载机制 1.1.jvm和类 运行Java程序:java 带有main方法的类名 之后java会启动jvm,并加载字节码(字节码就是一个类在内存空间的状态) 当调用java命令 ...

  5. Java反射机制

    Java反射机制 一:什么事反射机制 简单地说,就是程序运行时能够通过反射的到类的所有信息,只需要获得类名,方法名,属性名. 二:为什么要用反射:     静态编译:在编译时确定类型,绑定对象,即通过 ...

  6. java基础知识(十一)java反射机制(上)

    java.lang.Class类详解 java Class类详解 一.class类 Class类是java语言定义的特定类的实现,在java中每个类都有一个相应的Class对象,以便java程序运行时 ...

  7. java基础知识(十一)java反射机制(下)

    1.什么是反射机制? java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象都能够调用他的属性和方法,这种动态获取属性和方法的功能称为java的反射机制. ...

  8. Java反射机制专题

    ·Java Reflection Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方 ...

  9. java反射机制深入详解

    java反射机制深入详解  转自:http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html 一.概念 反射就是把Java的各种成分映射成 ...

随机推荐

  1. Redis-cli命令最新总结

    资料来源: http://redisdoc.com/ http://redis.io/commands 连接操作相关的命令 默认直接连接  远程连接-h 192.168.1.20 -p 6379 pi ...

  2. 使用JavaMail实现发送邮件功能

    package com.dunn.model; import java.util.Properties; import javax.mail.Address; import javax.mail.Me ...

  3. dubbo的安装和使用

    dubbo的安装和使用

  4. hdu 4744 最小费用流

    #include <cstdio> #include <queue> #include <cstring> #include <cmath> #defi ...

  5. PHP详解&dollar;&lowbar;SEVER常用变量

    $_SERVER['HTTP_ACCEPT_LANGUAGE']//浏览器语言 $_SERVER['HTTP_ACCEPT'] //当前请求的Accept头部的内容,例如text/html,appli ...

  6. Zkui安装

    是一个允许在zookeeper上进行增删查改操作的图形管理工具,与zkdash类似. 1.拉取代码 #git clone https://github.com/DeemOpen/zkui.git 2. ...

  7. maven项目中的pom&period;xml

    需要配置的内容 1.配置头(自动生成) 2.maven项目的坐标(自动生成) <modelVersion>4.0.0</modelVersion> <groupId&gt ...

  8. mysql CAPI 接口 读取中文乱码的解决方案&lpar;转&rpar;

      最近的yymysqlsdk的开源项目里,对中文的支持不到位,因此用了1.5天的时间,对中文处理的各个情况进行了分析.     1.首先确认你的MySQL配置文件,my.ini (只针对window ...

  9. 每日英语:As World&&num;39&semi;s Kids Get Fatter&comma; Doctors Turn To The Knife

    Daifailluh al-Bugami was just a year old when his parents noticed that his lips turned blue as he sl ...

  10. 10 -- 深入使用Spring -- 5&period;&period;&period; 实现任务的自动调度

    10.5 实现任务的自动调度 10.5.1 使用Quartz 10.5.2 在Spring中使用Quartz