1. 程式人生 > 其它 >JavaSE -進階基礎---反射技術

JavaSE -進階基礎---反射技術

反射常見用法:

 Java 反射機制是在執行狀態中,對於任意一個類,都能夠獲得這個類的所有屬性和方法,對於任意一個物件都能夠呼叫它的任意一個屬性和方法。這種在執行時動態的獲取資訊以及動態呼叫物件的方法的功能稱為 Java 的反射機制。
一個實體類

@Data
public class Student {
    private String name;
    private int color;

    @Name("方法")
    public void sayHello(String Kname) {
        this.name = Kname;
        System.out.println(Kname);
    }
}

Java 反射的主要組成部分有4個:

  • Class:任何執行在記憶體中的所有類都是該 Class 類的例項物件,每個 Class 類物件內部都包含了本來的所有資訊。

  • Field:描述一個類的屬性,內部包含了該屬性的所有資訊,例如資料型別,屬性名,訪問修飾符······

  • Constructor:描述一個類的構造方法,內部包含了構造方法的所有資訊,例如引數型別,引數名字,訪問修飾符······

  • Method:描述一個類的所有方法(包括抽象方法),內部包含了該方法的所有資訊,與Constructor類似,不同之處是 Method 擁有返回值型別資訊,因為構造方法是沒有返回值的。

一切java反射操作是基於目標類的類物件完成

java獲取這個類物件的三種方式

	//方式一(通過建立物件)
        Student stu = new Student() ;
        Class classobj1 = stu. getClass();
        System.out.print1n(classobj1.getName());
        //方式二(所在通過路徑-相對路徑)
        Class classobj2 = Class.forName ("com.gton.entity.Student") ;
        System.out.println(classobj2.getName()) ;
        //方式三(通過類名)
        Class classobj3 = Student.class;
        System.out.println(classobj3.getName()) ;

類比拓展java建立物件的幾種方式

- 使用關鍵字  new  呼叫類的建構函式 建立物件
- 使用反射技術:
 	Class<?> forName = Class.forName("com.entity.Student");
	//JDK1.8 及以前 class.newInstance() 可以例項化一個物件
        Student student = (Student) forName.newInstance();
        //JDK9之後,包括11
        Object newInstance = forName.getDeclaredConstructor().newInstance();
- 使用關鍵字 clone clone,jvm建立一個例項物件,將資料複製進去,不呼叫建構函式,實現介面Cloneable,重寫 clone方法,clone需要捕獲異常
- 反序列化 重檔案建立  實現介面Serializable,需要捕獲異常 運用反序列化手段,呼叫java.io.ObjectInputStream物件的readObject()方法.

**具體如下**
 // 方法一:new object();
        TestOneView first = new TestOneView();
        // 方法二:使用Object.clone()
        // 因為clone放回的是Object類,所以需要強轉一下
        TestOneView second = (TestOneView) first.clone();
        // 方法三:反射
        TestOneView third_1 = (TestOneView) Class.forName("ys.manufacture.ipc.action.TestOneView").newInstance();
        TestOneView third_2 = second.getClass().newInstance();
        // 方法四:構造方法
        TestOneView forth = second.getClass().getConstructor().newInstance();
        // 方法五:序列化
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("TestOneView.per"));
        outputStream.writeObject(first);
        outputStream.writeObject(second);
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("TestOneView.per"));
        TestOneView fifth = (TestOneView) inputStream.readObject();

總結建立物件的方法:

1.用new語句建立物件,這是最常用的建立物件的方式。
2.運用反射手段,呼叫Java.lang.Class或者java.lang.reflect.Constructor類的newInstance()例項方法。
3.呼叫物件的clone()方法。
4.運用反序列化手段,呼叫java.io.ObjectInputStream物件的readObject()方法.

反射基本運用

反射操作這個類

 @SneakyThrows
    @Test
    public void Star() throws ClassNotFoundException {
        Class<?> forName = Class.forName("com.entity.Student");
        //利用反射建立物件 JDK1.8 及以前 class.newInstance() 可以例項化一個物件
        Student student = (Student) forName.newInstance();
        //JDK9之後,包括11
        Object newInstance = forName.getDeclaredConstructor().newInstance();
        //獲取所有的方法
        Method[] methods = forName.getMethods();
        for (Method method : methods) {
            System.out.print(method.getName() + ":");
            //反射呼叫方法
            if ("sayHello".equals(method.getName())) {
                //獲取指定方法:第一個引數方法名。第二個引數是引數型別
                Method sayHello = forName.getMethod("sayHello", String.class);
                sayHello.invoke(student, "Hello World");
                //獲取構造器
                Constructor<?> constructor = forName.getConstructor();
                Student o = (Student) constructor.newInstance();
                o.setName("Hello");
                System.out.println(o);
                //獲取get-set
            }
        }
        //獲取指定方法:第一個引數方法名。第二個引數是引數型別
        Method sayHello = forName.getMethod("setName", String.class);
        sayHello.invoke(student, "Hello setName");
        System.out.println(student);

        //獲取方法上的註解
        sayHello = forName.getMethod("sayHello", String.class);
        Name annotation = sayHello.getAnnotation(Name.class);
        //獲取註解的值
        String value = annotation.value();
        System.out.println(value);

        //獲取欄位
        Field[] fields = forName.getDeclaredFields();
        for (Field field : fields) {
            //屬性是私有的,需要設定跳過安全模式取消
            field.setAccessible(true);
            System.out.println(field);
        }
        //獲取當前本類屬性 作用域 屬性型別 屬性名
        Field name = forName.getDeclaredField("name");
        System.out.println(name);

        //生成一個空白的物件
        Object newObj = forName.newInstance();
        //允許私有屬性被操作
        name.setAccessible(true);
        //反射給這個物件的·私有屬性設定值
        name.set(newObj, "設定的名字");
        System.out.println(newObj);
    }

反射的優點:

  • 可擴充套件性 :應用程式可以利用全限定名建立可擴充套件物件的例項,來使用來自外部的使用者自定義類。

  • 類瀏覽器和視覺化開發環境 :一個類瀏覽器需要可以列舉類的成員。視覺化開發環境(如 IDE)可以從利用反射中可用的型別資訊中受益,以幫助程式設計師編寫正確的程式碼。

  • 偵錯程式和測試工具 : 偵錯程式需要能夠檢查一個類裡的私有成員。測試工具可以利用反射來自動地呼叫類裡定義的可被發現的 API 定義,以確保一組測試中有較高的程式碼覆蓋率。

反射的缺點:

儘管反射非常強大,但也不能濫用。如果一個功能可以不用反射完成,那麼最好就不用。在我們使用反射技術時,下面幾條內容應該牢記於心。

  • 效能開銷 :反射涉及了動態型別的解析,所以 JVM 無法對這些程式碼進行優化。因此,反射操作的效率要比那些非反射操作低得多。我們應該避免在經常被執行的程式碼或對效能要求很高的程式中使用反射。

  • 安全限制 :使用反射技術要求程式必須在一個沒有安全限制的環境中執行。如果一個程式必須在有安全限制的環境中執行,如 Applet,那麼這就是個問題了。

  • 內部暴露 :由於反射允許程式碼執行一些在正常情況下不被允許的操作(比如訪問私有的屬性和方法),所以使用反射可能會導致意料之外的副作用,這可能導致程式碼功能失調並破壞可移植性。反射程式碼破壞了抽象性,因此當平臺發生改變的時候,程式碼的行為就有可能也隨著變化。