[Java 反射學習] Java 反射基礎

liziHexiaogou發表於2019-08-28

知識點

類是用來描述物件的,而反射就可以理解為是用來描述類的。

類中的屬性包括:

  • Class 類本身
  • Package 類所在的包
  • Field 類中的屬性
  • Method 類中的方法
  • Constructor 類中的構造方法
  • Annotation 類中的註解

如何獲取Class

1.Class的靜態方法,forName("全類名")

2.類.class關鍵字

3.物件引用.getClass()方法 Object中的方法

Class中的常用方法

/*
0--預設不寫 1--public 2--private 4--protected 8--static 16--final 32--synchronized 64--volatile 128--transient 256--native 512--interface 1024--abstract
*/
int = getModifiers();       //獲取類的修飾符(許可權+特徵)
String = getName();         //獲取類的全類名
String = getSimpleName();    //獲取簡單名(只有類名 )
Class = getSuperClass();     //獲取當前父類的對應Class
Class[] = getInterfaces();   //獲取當前父類的介面
Package p = getPackage();    //獲取當前類所在的包
        p.getName();        //獲取包的名字
Class[] = getClasses();      //獲取類中的內部類

Object = newInstance();     //獲取當前類的物件(相當於呼叫了類中的無引數的構造方法)如果類中不存在無引數的構造方法,就會丟擲NoSuchMethodException異常
Field = getField("屬性名");          //獲取類中的屬性(公有的 自己類+父類)
Field[] = getFields();              //獲取類中的全部屬性(公有的 自己類+父類)
Field = getDeclaredField("屬性名")    //獲取當前類的屬性(公有 + 私有 自己類)
Field = getDeclaredFields()    //獲取當前類的全部屬性(公有 + 私有 自己類)
    如果想修改私有的屬性則需要設定屬性可以被操作
        setAccessible()
public class TestMain {
    public static void main(String[] args) {

        try {
            Class<?> clazz = Class.forName("com.lili.reflect.People");
            Package aPackage = clazz.getPackage();
            int modifiers = clazz.getModifiers();
            System.out.println(modifiers);
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                System.out.println(method.getName());
            }
            System.out.println(aPackage);

            Class<?>[] interfaces = clazz.getInterfaces();
            for (Class c : interfaces) {
                System.out.println(c.getName());
            }

            ArrayList<String> list = new ArrayList<>();
            Class c = ArrayList.class;
            Class superclass = c.getSuperclass();
            while (superclass != null) {
                System.out.println(superclass.getName());
                superclass = superclass.getSuperclass();
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

利用反射修改String型別的值

/*
注意只能是繞過private去修改屬性的值,而不能去修改屬性的長度,因為是final修飾的。
String的不可變指的是長度+值的不可變
*/
public class ChangeString {

    public static void main(String[] args) {

        try {
            String str = new String("abc");
            System.out.println(str);

            //1、利用反射技術獲取String的Class
            Class clazz = str.getClass();
            //2、獲取屬性
            Field f = clazz.getDeclaredField("value");
            //3、設定可以修改屬性的值
            f.setAccessible(true);
            //4、獲取屬性的值
            char[] newChar = (char[])f.get(str);
            //5、修改屬性的值
            newChar[0] = 'xu';
            newChar[1] = 'Li';
            newChar[2] = 'Li';
            System.out.println(str);

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}

利用反射呼叫類中的方法

可以獲取共有的方法,包括自己類的以及父類的

可以找到私有的方法,但是要透過setAccessible(true)方法來執行私有的方法。

/**
 * 測試使用反射獲取類中的方法
 */
public class TestMethod {

    public static void main(String[] args) {
        try {
            //1、獲取People類對應的Class
            Class clazz = People.class;
            //2、獲取物件
            People p = (People) clazz.newInstance();
            //3、透過clazz獲取其中的方法,透過方法名以及方法的引數型別來定位方法。
            Method m = clazz.getMethod("eat", String.class);
            //4、呼叫方法,第一個引數是要執行方法的物件,第二個則是傳進去的引數列表
            String n = (String) m.invoke(p, "lili要開始吃飯啦");
            System.out.println(n);

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}

利用反射執行構造方法

/**
 * 利用反射執行構造方法
 */
public class TestConstructor {
    public static void main(String[] args) {
        try {
            //1、獲取People對應的Class
            Class<People> clazz = People.class;
            //2、獲取People中的構造方法,其中省去了構造方法的名稱,因為是與類同名
            //無參的就是呼叫的無引數的構造方法
            //有引數的就是傳的構造方法中形參的型別.class
            Constructor<People> constructor = clazz.getConstructor(String.class);
            //3、執行構造方法,同理引數就是要傳引數的實參
            People people = constructor.newInstance("哈哈哈哈哈");
            System.out.println(people);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

設計一個小工具

這個小工具可以代替我們自己建立物件的功能,透過傳遞一個字串,來幫我們建立一個物件,同時還能將物件內的所有屬性賦值。

其實這就是簡單的模擬了Spring中IOC思想的原理,IOC(Inversion Of Control)控制反轉:將物件的控制權反轉,交給Spring容器去處理;DI(Dependency Injection)依賴注入:Spring容器建立物件的同時幫我們自動的注入屬性的值。

public class MySpring {
    //設計一個方法,將我們建立物件的過程交給該方法去執行。
    //引數String型別的全類名
    //返回值 建立出來的物件 Object型別--->再新增上DI依賴注入
    public Object getBean(String classPath) {
        Object obj = null;
        //模擬輸入的實參
        Scanner scanner = new Scanner(System.in);
        System.out.println("請給"+ classPath +"的屬性賦值");
        try {
            //1、獲取該路徑下對應的Class
            Class clazz = Class.forName(classPath);
            //2、建立一個物件
            obj = clazz.newInstance();
            //使用set方法對物件的屬性進行賦值,找到每一個不同物件對應的set方法。
            //也就是字串set+屬性的名字
            //3、獲取類中的屬性
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                //獲取屬性的名稱
                String fieldName = field.getName();
                //改變屬性名中第一個字母的大小寫
                String first = fieldName.substring(0, 1).toUpperCase();
                //獲取屬性名中除開第一個字母的欄位
                String last = fieldName.substring(1);
                //拼接set方法
                StringBuilder methodName = new StringBuilder("set");
                methodName.append(first);
                methodName.append(last);
                //4、獲取屬性的型別
                Class fieldType = field.getType();
                //5、獲取方法
                Method method = clazz.getMethod(methodName.toString(), fieldType);
                //接收實參
                System.out.println("請給"+ fieldName +"屬性賦值");
                String value = scanner.nextLine();
                /*為了解決引數型別不一致的問題,可以將引數的型別都設定未相應的包裝類,
                並且將它們都轉換成String的型別,除了Char型別之外需要另外的判斷。
                可以利用其它包裝類帶String型別的構造方法進行處理。
                */
                Constructor con = fieldType.getConstructor(String.class);

                //6、執行方法
                method.invoke(obj, con.newInstance(value));

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結
小狗和莉莉

相關文章