[java 基礎]反射入門

原文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

檢查java類

下面來作一些測試,用來獲取前面提到過的好比類名,修飾符,字段,方法,實現的接口之類的東西。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());                                       
}

運行期間修改field值,調用method

/**
 * 經過獲取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());                                                         
}
相關文章
相關標籤/搜索