原文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()); }