1. 程式人生 > 其它 >Java-反射1

Java-反射1

1.反射

概述:

  • 通過一個Class檔案物件去使用或者修改檔案物件中的成員變數,構造方法,成員方法(無論是否私有,都能獲取並使用)

之前學習:

  • 我們在使用成員之前,得先有一個java檔案,然後在例項化的時候,new一下就完事了。
    Person p = new Person();
    p.eat("漢堡");

現在的船新概念:

  • 總歸來說,無論是呼叫什麼成員,都是有個物件,歸根結底,物件是根據類來的,實際上底層是依賴一個class檔案
    對於java程式來說,依賴的是這個class檔案對應的Class檔案物件。

class類:

  • 成員變數:Field
  • 構造方法:Constructor
  • 成員方法:Method

如何獲取一個類的對應的Class檔案物件呢?

  • 方式1:通過Object類中的getClass()方法,返回此Object類的執行時的類
  • 方式2:在不new的前提下,獲取該類的class檔案物件,java中每一個類都有一個靜態的屬性class
  • 方式3:Class類中有一個靜態的方法,獲取該類的class檔案物件 (最常用的)

獲取物件對應的class檔案物件程式碼

public class ReflexDemo1 {
    public static void main(String[] args) {
        //方式一:通過Object類中的getClass()方法,返回此Object類執行時的類
        Person person1 = new Person();
        Class<? extends Person> c1 = person1.getClass();
        Person person2 = new Person();
        Class<? extends Person> c2 = person2.getClass();
        System.out.println(c1 == c2); //true

        //方式二:在不 new 的情況下,獲取該類的class檔案物件,java中的每一個類都有一個靜態的屬性class
        Class<Person> c3 = Person.class;
        System.out.println(c1 == c3); //true
        System.out.println(c2 == c3); //true

        //方式三:Class類中有一個靜態方法,獲取該類的class檔案物件(最常用的)
        //static Class<?> forName(String className)
        //返回與給定字串名稱的類或介面相關聯的 類物件。
        try {
            Class<?>  c4 = Class.forName("com.bigdat.java.day29.Person");
            System.out.println(c4);
            System.out.println(c1 == c4); //true
            System.out.println(c1 == c4); //true
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

如何通過反射獲取Class檔案物件並使用其中的構造方法建立物件呢?

  • getConstructors() 返回一個包含Constructor物件的陣列,反映由此Constructor物件表示的類的所有公共類函式
  • getDeclaredConstructors() 獲取的是該類中的所有構造方法,包括私有,被保護的,預設的,公共的
  • getConstructor() 返回一個 Constructor物件,該物件反映 Constructor物件表示的類的指定的公共 類函式。
  • getDeclaredConstructor() 獲取單個私有的帶引數的構造方法

注意點:

  • newInstance(Object... initargs)
    使用此 Constructor物件表示的建構函式,使用指定的初始化引數來建立和初始化建構函式的宣告類的新例項。如果使用帶引數的構造方法
    newInstance建立物件,引數必須一樣。形如:Object o = con1.newInstance("張三");
  • 如果獲取帶引數的構造方法,裡面填的引數是:**型別.class**
    如:Constructor<?> con2 = c1.getDeclaredConstructor(String.class, int.class);
public class ReflexDemo2 {
    public static void main(String[] args) {
        //獲取person類的Class檔案物件
        Class<?> c1 = null;
        try {
            c1 = Class.forName("com.bigdatjava.day29.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //獲取Class檔案物件的構造方法
        //public Constructor<?>[] getConstructors() throws SecurityException
        //返回一個包含Constructor物件的陣列,反應由此Constructor物件表示的類的所有的公共類函式
        Constructor<?>[] cons1 = c1.getConstructors();
        for (Constructor<?> c : cons1) {
            System.out.println(c);
        }
        System.out.println("===========================================================");
        //public Constructor<?>[] getDeclaredConstructors() throws SecurityException
        //返回Constructor 物件表名的類宣告的所有Constructor物件的陣列類
        //獲取的是該類中的所有構造方法,包括私有,被保護,預設和公共的
        Constructor<?>[] cons2 = c1.getDeclaredConstructors();
        for (Constructor<?> c : cons2) {
            System.out.println(c);
        }
        System.out.println("=================================================================");
        //獲取單個的構造方法
        //Constructor<T> getConstructor(類<?>... parameterTypes)
        //返回一個 Constructor物件,該物件反映 Constructor物件表示的類的指定的公共 類函式。
        Constructor<?> con1 = null;
        try {
            con1 = c1.getConstructor(String.class);
            System.out.println(con1);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        //獲取單個私有的帶引數的構造方法
//        Constructor<?> con2 = c1.getConstructor(String.class, int.class);
//        System.out.println(con2);
        Constructor<?> con2 = null;
        try {
            con2 = c1.getDeclaredConstructor(String.class, int.class);
            System.out.println(con2);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        System.out.println("======================================================================");
        //使用公共的構造方法建立物件
        //使用公共的構造方法建立物件
        //T newInstance(Object... initargs)
        //使用此 Constructor物件表示的建構函式,使用指定的初始化引數來建立和初始化建構函式的宣告類的新例項。
        try {
            Object o = con1.newInstance("我真蠢!");
            Person p = (Person)o; // 向下轉型
            System.out.println(p);
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            con2.setAccessible(true);
            Object two = con2.newInstance("我是小垃圾", 19);
            // 向下轉型
            Person o1 = (Person) two;
            System.out.println(o1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

通過反射獲取Class檔案物件中私有的構造方法並建立物件

重點

  • 當還是象訪問公共物件去訪問私有變數時會報 IllegalAccessException 非法的訪問異常

解決辦法:暴力訪問

  • 通過setAccessible(true)方法暴力訪問。值是true的化,表示反射物件在使用的時候,取消Java語言的訪問檢查
public class ReflexDemo3 {
    public static void main(String[] args) {
        Class<?> a = null;
        try {
             a = Class.forName("com.bigdat.java.day29.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //獲取私有的構造方法
        Constructor<?> dec1 = null;
        try {
             dec1 = a.getDeclaredConstructor(String.class, int.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        try {
            // .IllegalAccessException 非法的訪問異常
            //構造方法是私有的,可以獲取的到,但是不能這麼使用
            //通過構造方法建立物件
            //暴力訪問
            dec1.setAccessible(true); // 如果這裡是 true 的話,表示反射物件在這裡用的話,取消Java語言的訪問檢查
            Object xh = dec1.newInstance("小花", 100);
            //向下轉型
            Person p = (Person) xh;
            System.out.println(p);
            p.eat("紅燒肉");
        } catch (Exception e) {
            e.printStackTrace();
        }
        }


    }

反射獲取類Class檔案物件中的成員變數並使用

  • getFields() 返回包含一個數組 Field物件反射由此表示的類或介面的所有可訪問的公共欄位 類物件。獲取當前類所有被public修飾的成員變數
  • getDeclaredFields() 返回的陣列 Field物件反映此表示的類或介面宣告的所有欄位 類物件。獲取當前類中所有的成員變數,包括私有的,公共的,被保護的,預設的欄位,不包括繼承的欄位。
  • getField() 獲取單個的成員變數
  • getDeclaredField() 獲取私有的成員變數並賦值
  • 暴力訪問: name.setAccessible(true);
public class ReflexDemo4 {
    public static void main(String[] args)throws Exception {
        Class<?> c1 = null;
        //獲取Class的檔案物件
        try {
          c1 = forName("com.bigdat.java.day29.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //Field[] getFields()
        //返回一個包含一個數組的 Filed 物件反射由此表示的類或介面的所有可訪問的公共欄位 類物件。
        //獲取當前類所有被public修飾的成員變數
        Field[] fields = c1.getFields();
        for (Field field : fields) {
            System.out.println(field); //public java.lang.String com.bigdat.java.day29.Person.id
        }
        System.out.println("==============================================================");
        //Field[] getDeclaredFields()
        //返回的陣列 Field物件反映此表示的類或介面宣告的所有欄位 類物件。
        //獲取當前類中所有的成員變數,包括私有的,公共的,被保護的,預設的欄位,不包括繼承的欄位。
        Field[] dec = c1.getDeclaredFields();
        for (Field field : dec) {
            System.out.println(field);
        }
        System.out.println("-----------------------------------------------------------------");
        //此方法並不能獲得該類繼承的其父類的任何屬性
         Class<?> c2 = null;
        try {
            c2 = forName("com.bigdat.java.day29.B");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Field[] declaredFields = c2.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        System.out.println("========================================================");
        //獲取Class檔案的物件
        Class<?> c = forName("com.bigdat.java.day29.Person");
        //通過 getConstructor 方法來建立一個構造器
        Constructor<?> ctr = c.getConstructor();
        //通過構造方法來建立一個物件
        Object o = ctr.newInstance();

        //獲取單個的成員變數
        Field id = c1.getField("id");
        //void set(Object obj, Object value)
        //將指定物件引數上的此 Field物件表示的欄位設定為指定的新值。
        //id.set(o,"1001");//給物件o中的成員變數id賦值為"1001"
        id.set(o, "1001");
        System.out.println(o);

        //獲取私有的成員變數並賦值
        Field name = c1.getDeclaredField("name");
        name.setAccessible(true); // 在使用私有的成員變數時需要進行暴力訪問
        name.set(o, "名字");
        System.out.println(o); // IllegalAccessException 不暴力訪問的話會報異常
        System.out.println("================================================================");
        Field age = c1.getDeclaredField("age");
        age.set(o, 101);
        System.out.println(o);
        Field address = c1.getDeclaredField("address");
        address.set(o,"中國-雲南-保山");
        System.out.println(o);
    }
}
class A{
    private String aaa;
    public int bbb;
}
class B extends A{
}

通過反射獲取Class檔案物件中的成員方法並使用

-== Method[] getMethods()== 返回包含一個數組 方法物件反射由此表示的類或介面的所有公共方法 類物件,包括那些由類或介面和那些從超類和超介面繼承的宣告。

  • getDeclaredMethods() 獲取本類中的所有方法,包括私有的,被保護的,預設的,公共的。不獲取繼承關係父類中的方法
public class ReflexDemo5 {
    public static void main(String[] args) throws Exception{
        //獲取person類的class檔案物件
        Class<?> c = Class.forName("com.bigdat.java.day29.Person");
        //獲取方法
        //Method[] getMethods()
        //返回包含一個數組 方法物件反射由此表示的類或介面的所有公共方法 類物件,包括那些由類或介面和那些從超類和超介面繼承的宣告。
        //獲取本類以及父類中所有的公共方法(被public所修飾的方法)
        Method[] methods = c.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("===========================================================");
        //獲取本類中的所有方法,包括私有,被保護,預設,公共的方法,但是不獲取繼承關係的父類方法
        Method[] declaredMethods = c.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        System.out.println("======================================================================");
        //建立物件的構造器
        Constructor<?>  con  = c.getConstructor();
        Object o = con.newInstance();

        //獲取單個的方法並使用
        //Object invoke(Object obj, Object... args)
        //在具有指定引數的 方法物件上呼叫此 方法物件表示的底層方法。
        Method fun1 = c.getMethod("fun1" );
        fun1.invoke( o );
        System.out.println("================================================================================");
        //獲取私有的成員方法使用
        Method fun2 = c.getDeclaredMethod("fun2",String.class);
        `fun2.setAccessible(true);`
        fun2.invoke( o ,"你好!");
        System.out.println("==================================================================================");
        Method fun3 = c.getDeclaredMethod("fun3");
        fun3.invoke( o );
        Method fun4 = c.getDeclaredMethod("fun4" );
        fun4.invoke( o );
    }
}