原文java
使用java的反射,可让咱们检查(或者修改)类,接口,字段,方法的特性。当你在编译期不知道他们的名字的时候很是有用。数据库
除此以外,可使用反射来建立实例,调用方法或者get/set 字段值。dom
须要作的只有导个包。ide
import java.lang.reflect.*; //根据使用的状况导特定的,好比reflect.constructor等
先加一下junit的依赖,而后添加一个Person类,两个字段。函数
public class Person { private String name; private int age; }
写个测试方法来获取这个类的全部字段。测试
/** * 包含全部字段 */ @Test public void containAllFields() { Object p = new Person(); Field[] declaredFields = p.getClass().getDeclaredFields(); List<String> actualFields = Arrays.stream(declaredFields).map(field -> field.getName()).collect(Collectors.toList()); List<String> exceptFields = new ArrayList<>(); exceptFields.add("age"); exceptFields.add("name"); Assert.assertTrue(exceptFields.containsAll(actualFields)); }
最经常使用的使用场景为数据库表和实体类作字段映射。this
下面来作一些测试,用来获取前面提到过的好比类名,修饰符,字段,方法,实现的接口之类的东西。code
先建立一个Eating接口继承
public interface Eating { String eats(); }
建立一个抽象Animal类来实现这个Eating接口递归
package com.lou.reflect.test; public abstract class Animal implements Eating { public static String CATEGORY = "domestic"; private String name; protected abstract String getSound(); public Animal(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
建立一个Locomotion
接口用来描述动物的行动方式。
package com.lou.reflect.test; public interface Locomotion { String getLocomotion(); }
建立一个具体的Goat
类,继承自Animal
同时实现Locomotion
接口。
package com.lou.reflect.test; public class Goat extends Animal implements Locomotion { public Goat(String name) { super(name); } @Override protected String getSound() { return "山羊叫"; } @Override public String eats() { return "吃草"; } @Override public String getLocomotion() { return "行走"; } }
建立一个Bird类,继承自Animal
public class Bird extends Animal { private boolean walks; public Bird() { super("鸟"); } public Bird(String name) { super(name); } public Bird(String name, boolean walks) { super(name); this.walks = walks; } @Override protected String getSound() { return null; } @Override public String eats() { return null; } public boolean isWalks() { return walks; } public void setWalks(boolean walks) { this.walks = walks; } }
作好准备工做以后开始下面的测试。
获取类名。
/** * simpleName为Goat, * name为com.lou.reflect.test.Goat * cannocalName为com.lou.reflect.test.Goat */ @Test public void givenObjectThenGetNameTest() { Object goat = new Goat("山羊"); Class<?> goatClazz = goat.getClass(); Assert.assertEquals("Goat", goatClazz.getSimpleName()); Assert.assertEquals("com.lou.reflect.test.Goat", goatClazz.getName()); Assert.assertEquals("com.lou.reflect.test.Goat",goatClazz.getCanonicalName()); }
/** * 经过获取类的modifier来判断类的特性 * @throws Exception */ @Test public void givenObjectThenGetModifiersTest() throws Exception { Class<?> animalClazz = Class.forName("com.lou.reflect.test.Animal"); Class<?> goatClazz = Class.forName("com.lou.reflect.test.Goat"); //获取两个类的修饰符 int animalModifiers = animalClazz.getModifiers(); int goatModifiers = goatClazz.getModifiers(); //判断是不是公有 Assert.assertTrue(Modifier.isPublic(goatModifiers)); //判断是不是抽象 Assert.assertTrue(Modifier.isAbstract(animalModifiers)); //判断是否为公有 Assert.assertTrue(Modifier.isPublic(animalModifiers)); }
/** * 获取包信息 */ @Test public void givenClassThenGetPackageNameTest() { Goat goat = new Goat("山羊"); Package goatPackage = goat.getClass().getPackage(); Assert.assertEquals("com.lou.reflect.test", goatPackage.getName()); }
/** * 获取父类信息 */ @Test public void givenClassThenGetSupperClassTest() { Goat goat = new Goat("山羊"); String str = "hello"; Class<?> goatSupperClazz = goat.getClass().getSuperclass(); Class<?> strSupperClazz = str.getClass().getSuperclass(); Assert.assertEquals("com.lou.reflect.test.Animal",goatSupperClazz.getName()); Assert.assertEquals("java.lang.Object",strSupperClazz.getName()); }
/** * 获取实现的接口信息 * * @throws Exception */ @Test public void givenClassThenGetImpMethodsTest() throws Exception { Class<?> goatClazz = Class.forName("com.lou.reflect.test.Goat"); Class<?> animalClazz = Class.forName("com.lou.reflect.test.Animal"); Class<?>[] goatInterfaces = goatClazz.getInterfaces(); Class<?>[] animalInterfaces = animalClazz.getInterfaces(); //实现的接口,都是1个,虽然goat的父类实现了一个,goat本身也实现了一个。 //只能获取用implements显式实现的接口。若是要所有就只能递归了。 Assert.assertEquals(1, goatInterfaces.length); Assert.assertEquals(1, animalInterfaces.length); Assert.assertEquals("Locomotion", goatInterfaces[0].getSimpleName()); Assert.assertEquals("Eating", animalInterfaces[0].getSimpleName()); }
/** * 构造函数,方法,字段 */ @Test public void givenClassThenGetConstructorMethodField() throws Exception { Class<?> goatClazz = Class.forName("com.lou.reflect.test.Goat"); Class<?> animalClazz = Class.forName("com.lou.reflect.test.Animal"); Constructor<?>[] goatCtors = goatClazz.getConstructors(); Assert.assertEquals(1, goatCtors.length); Field[] animalFields = animalClazz.getDeclaredFields(); //一个静态的CATEGORY,一个name,因此是2个。 Assert.assertEquals(2, animalFields.length); //3个。这里获取到的是显式申明的方法,不包括那些从object继承下来的 Method[] animalMethods = animalClazz.getDeclaredMethods(); Assert.assertEquals(3, animalMethods.length); //getName,setName,getSound, List<String> methodNames = Arrays.stream(animalMethods).map(method -> method.getName()).collect(Collectors.toList()); Assert.assertTrue(methodNames.containsAll(Arrays.asList("getName", "setName", "getSound"))); }
/** * 获取bird的3个构造函数,而后分别调用~。 * @throws Exception */ @Test public void getConstructorThenCreateInstance() throws Exception { Class<?> birdClazz = Class.forName("com.lou.reflect.test.Bird"); //获取无参的构造函数 Constructor<?> birdCtro1 = birdClazz.getConstructor(); //获取有一个String类型的参数的构造函数 Constructor<?> birdCtro2 = birdClazz.getConstructor(String.class); //获取有一个String类型,一个boolean类型参数的构造函数 Constructor<?> birdCtro3 = birdClazz.getConstructor(String.class, boolean.class); //调用无参的 Bird bird1 = (Bird) birdCtro1.newInstance(); Assert.assertEquals("鸟", bird1.getName()); //调用有一个参数的 Bird bird2 = (Bird) birdCtro2.newInstance("bird2"); Assert.assertEquals("bird2", bird2.getName()); //调用两个参数的构造函数 Bird bird3 = (Bird) birdCtro3.newInstance("bird3", true); Assert.assertEquals("bird3", bird3.getName()); Assert.assertEquals(true, bird3.isWalks()); }
/** * 经过获取name字段,而后动态修改。 * 经过动态调用setName方法,修改name的值 * @throws Exception */ @Test public void givenClassThenModifyFields() throws Exception { Class<?> birdClazz = Class.forName("com.lou.reflect.test.Bird"); Bird bird =(Bird) birdClazz.newInstance(); //定义在父类 Field nameField = birdClazz.getSuperclass().getDeclaredField("name"); //先设置为可访问 nameField.setAccessible(true); nameField.set(bird,"一只bird"); Assert.assertEquals("一只bird",bird.getName()); Method setNameMethod = birdClazz.getSuperclass().getDeclaredMethod("setName", String.class); setNameMethod.invoke(bird,"又一只"); Assert.assertEquals("又一只",bird.getName()); }