Java反射机制是在 运行状态 中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
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
以下的测试都是假设我们从磁盘上或者网络中获取一个类的字节,得知这个类的包名(reflcet)和类名(Reflect)和相关字段名称和方法名称,并通过热加载已经加载到工程中。
待测类
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(); } } }
测试方法
@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(); } }
测试方法
@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()
假设开发者从磁盘文件,或者网络连接中获取一串字节,并且被告知这些字节代表了一个类。既然这个类在程序编译后很久才出现,若想使用这个类,就需要采用发射机制。热加载就属于这种场景。
在自己写框架时候,开发者肯定会用到反射,很简单的例子就是事件总线和注解框架。
反射很灵活,在日常开发中,慎用少用反射,反射会牺牲部分性能。在写框架时,不避讳反射,在关键时利用反射助自己一臂之力。