Java的反射機制初步(Java基礎複習歸納系列)
目錄
一、通過反射檢視類資訊
Java程式中的許多物件在執行時都會出現兩種型別:編譯時型別和執行時型別,如Person p = new Student(),p變數編譯時型別為Person,而執行時型別為Student。我們常常需要在程式執行時獲取物件和類的真實資訊,這就有兩種可能:
-
一是在編譯和執行時完全知道型別的具體資訊,可以直接使用instanceof運算子進行判斷。instanceof運算子的前一個運算元通常是一個引用型變數,後一個運算元可以是一個類或介面,它用於判斷前面的物件是否是後面的類或者其子類、實現類的例項。如:
public class InstanceofTest { public static void main(String[] args) { // 宣告hello時使用Object類,則hello的編譯型別是Object,但hello變數的實際型別是String Object hello = "Hello"; // 以下三條語句的返回結果均為true // String是Object類的子類,可以進行instanceof運算。 System.out.println("字串是否是Object類的例項:" + (hello instanceof Object)); System.out.println("字串是否是String類的例項:" + (hello instanceof String)); // String實現了Comparable介面,所以返回true。 System.out.println("字串是否是Comparable介面的例項:" + (hello instanceof Comparable)); } }
- 二是在編譯時根本無法知道該物件可能屬於哪些類,程式只能依靠執行時資訊來發現該物件和類的真實資訊,這就需要用到反射。
1.獲得Class物件
每個類在被載入之後系統都會為該類生成一個對應的Class物件,通過該Class物件就可以訪問到JVM中的這個類。獲得Class物件通常有3種方式:
-
使用 static Class<?> forName(String className) throws ClassNotFoundException返回與帶有給定字串名的類或介面相關聯的 Class 物件。其中,className必須是某個類的全限定類名(即需新增完整包名)。
-
呼叫某個類的class屬性來獲取該類對應的Class物件。如:Person.class。
-
呼叫某個物件的getClass()方法。該方法是java.lang.Object包中的一個方法,所有物件均可呼叫,該方法會返回該物件所屬類對應的Class物件。
package reflect; public class TestGetClass { public static void main(String[] args) throws ClassNotFoundException { Person p = new Person(); System.out.println(Class.forName("reflect.Person").getName()); System.out.println(Person.class.getName()); System.out.println(p.getClass().getName()); } } class Person { }
執行結果:
其中,b.方法由於在編譯階段就可以檢查需要訪問的Class物件是否存在,且無需呼叫方法,所以更加安全,效能更好。
2.獲取Class中的資訊
Class類中提供了大量的例項方法來獲得該Class物件的所對應類的詳細資訊,包括:獲取Class對應類所包含的構造方法、獲取Class對應類所包含的方法、獲取Class對應類所包含的Field、獲取Class對應類所包含的Annotation、獲取Class對應類所包含的內部類、獲取Class對應類所繼承的父類和所實現的介面、獲取Class對應類的修飾符、所在包以及類名等基本資訊,除此之外,還有方法可以判斷該類是否為介面、列舉、註釋型別等。
import java.lang.reflect.Constructor;
public class GetClassInfo {
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
Class c = Student.class;
// 返回對應形參為String型別的public構造方法
System.out.println(c.getConstructor(String.class));
// 返回無形參的構造方法,無論其訪問許可權為何
System.out.println(c.getDeclaredConstructor());
// 返回所有的構造方法,無論其訪問許可權為何
Constructor[] csts = c.getDeclaredConstructors();
for (Constructor cst : csts) {
System.out.println(cst);
}
}
}
class Student {
private String id;
private Student() {
}
public Student(String id) {
this.id = id;
}
}
執行結果:
二、使用反射生成並操作物件
java.lang.reflect包java 1.8之前及java 1.8的改變:
在java.lang.reflect包中包含了Method類、Constructor類和Field類,這三個類都實現了java.lang.reflect.Member介面。程式可以通過Method物件來執行對應的方法,通過Constructor物件來呼叫對應的構造方法建立例項,通過Field物件直接訪問並修改物件的屬性值。
1.建立物件
通過反射建立物件有兩種方式:
-
直接使用Class物件的newInstance()方法。該方式需要Class物件對應的類有預設的構造方法。
-
先使用Class物件通過getConstructor()方法獲取指定的Constructor物件,再呼叫Constructor物件的newInstance()方法建立Java物件。通過這種方法可以選擇指定的構造方法來建立例項。
import java.util.*;
import java.io.*;
import java.lang.reflect.Constructor;
/**
* @Title ObjectPoolFactory.java
* @Description TODO
* @Author 15643
* @Time 2018年8月28日 上午10:25:24
* @Other obj.txt中的內容:a=java.util.Date
*/
public class ObjectPoolFactory {
// 定義一個物件池,前面是物件名,後面是實際物件
private Map<String, Object> objectPool = new HashMap<>();
// 該方法只要傳入一個字串類名,程式可以根據該類名生成Java物件
private static Object createObject(String clazzName)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
// 根據字串來獲取對應的Class物件
Class<?> clazz = Class.forName(clazzName);
// 使用clazz對應類的預設構造器建立例項
return clazz.newInstance();
}
// 該方法根據指定檔案來初始化物件池,並根據配置檔案來建立物件
public void initPool(String fileName)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
try (FileInputStream fis = new FileInputStream(fileName)) {
Properties props = new Properties();
props.load(fis);
for (String name : props.stringPropertyNames()) {
// 根據鍵值對的值建立一個物件,並將物件新增到物件池中
objectPool.put(name, createObject(props.getProperty(name)));
}
} catch (IOException ex) {
System.out.println("讀取" + fileName + "異常");
}
}
public Object getObject(String name) {
// 從objectPool中取出指定name對應的物件。
return objectPool.get(name);
}
public static void main(String[] args) throws Exception {
ObjectPoolFactory pf = new ObjectPoolFactory();
pf.initPool("obj.txt");
System.out.println(pf.getObject("a"));
// 直接在這裡示範了,使用指定的構造方法建立物件
Constructor<?> c = pf.getObject("a").getClass().getConstructor(long.class);
System.out.println(c.newInstance(1000));
}
}
執行結果:
2.呼叫方法
在Class類裡面有四個用來獲取方法的方法,getMethod()、getDeclaredMethod()、getDeclaredMethods()、getMethods(),前兩個返回Method物件,後兩個返回Method物件陣列。
Method類的常用方法:
Object invoke(Object obj, Object... args) |
對帶有指定引數的指定物件呼叫由此 Method 物件表示的底層方法。obj - 從中呼叫底層方法的物件,args - 執行方法時傳入的實參 |
boolean equals(Object obj) | 將此 Method 與指定物件進行比較。 |
String getName() | 以 String 形式返回此 Method 物件表示的方法名稱。 |
Class<?>[] getParameterTypes() | 按照宣告順序返回 Class 物件的陣列,這些物件描述了此 Method 物件所表示的方法的形參型別。 |
Class<?> getReturnType() | 返回一個 Class 物件,該物件描述了此 Method 物件所表示的方法的正式返回型別。 |
import java.lang.reflect.*;
public class TestMethod {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InvocationTargetException,
InstantiationException, IllegalAccessException {
String field = "id";
Class<?> cl = Employee.class;
Object obj = cl.newInstance();
// 使用get+首字母大寫的Field來匹配方法
Method getMt = cl.getMethod("get" + field.substring(0, 1).toUpperCase() + field.substring(1));
Method setMt = cl.getMethod("set" + field.substring(0, 1).toUpperCase() + field.substring(1), String.class);
// 使用invoke()呼叫Method物件對應表示的方法
setMt.invoke(obj, "100001");
System.out.println("StudentID is " + getMt.invoke(obj));
}
}
class Employee {
private String id;
public Employee() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
若程式需要呼叫某個物件的private方法,則可以先用Method物件呼叫setAccessible(boolean flag),當flag為true時,則指示反射的物件在使用時應該取消 Java 語言訪問許可權檢查。值為 false 則指示反射的物件應該實施 Java 語言訪問許可權檢查。 (setAccessible()方法是屬於AccessibleObject類的,由於它是Method、Constructor、Field的父類,所以它們都可以呼叫該方法從而通過反射來呼叫私有的方法、屬性等。)
3.訪問Field
Field類提供了兩組方法來訪問、設定Field的值:
getXxx(Object obj) | 獲取obj物件的該Field的屬性值。Xxx對應8個基本型別,若為引用型別,則為get(Object obj)方法 |
setXxx(Object obj,Xxx value) | 將obj物件的該Field設定成value值。Xxx對應8個基本型別,若為引用型別,則為set(Object obj,Object value)方法 |
import java.lang.reflect.*;
public class FieldTest {
public static void main(String[] args) throws Exception {
Class cl = Employee.class;
Object p = cl.newInstance();
// 獲取Person的名為id的Field,使用getDeclaredField無視訪問控制符獲取field
Field idField = cl.getDeclaredField("id");
// 設定通過反射訪問該Field時取消訪問許可權檢查,否則IllegalAccessException
idField.setAccessible(true);
idField.set(p, "1000001");
System.out.println(p);
}
}
class Employee {
private String id;
public Employee() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "StudentID is "+id;
}
}
參考資料:《Java瘋狂講義》
相關推薦
Java的反射機制初步(Java基礎複習歸納系列)
目錄 一、通過反射檢視類資訊 Java程式中的許多物件在執行時都會出現兩種型別:編譯時型別和執行時型別,如Person p = new Student(),p變數編譯時型別為Person,而執行時型別為Student。我們常常需
Java的File類(Java基礎複習歸納系列)
參考資料:THU 諶衛軍教學PPT 目錄 一、檔案 什麼是檔案 檔案的屬性 目錄 一、檔案 什麼是檔案 檔案是一種抽象機制,它提供了一種把資訊儲存在磁碟等儲存裝置上,並且便於以後訪問的方法。 在一個作業系統中,負責處理檔案相關
Java的多執行緒(Java基礎複習歸納系列)
目錄 1.程序 2.執行緒 一、執行緒概述 1.程序 在一個作業系統中,每個獨立執行的程式都可稱之為一個程序,也就是“正在執行的程式”。 程序和程式 A proce
Java的Scanner類(Java基礎複習歸納系列)
參考資料:Java doc、THU 諶衛軍老師的教學PPT Scanner類 作用:一個可以使用正則表示式來解析基本型別和字串的簡單文字掃描器。可以方便地對文字檔案中的每一個不同型別的資料單元進行訪問; Scanner 使用分隔
Java反射機制demo(五)—獲得並調用一個類中的方法
color 擁有 oca logs over super getmethod equals() bool 這個demo在使用反射機制操作屬性之前,主要原因是因為在.class文件字節碼中,方法排在屬性的前面。 1,獲得一個類中的方法 先看一下方法和運行結果。獲取所有的方
JAVA中反射機制六(java.lang.reflect包)
instance 檢查 item 類繼承 final win 基類 cte member 一、簡介 java.lang.reflect包提供了用於獲取類和對象的反射信息的類和接口。反射API允許對程序訪問有關加載類的字段,方法和構造函數的信息進行編程訪問。它允許在安全限制
java反射機制一(反射的基本概念和理解)
反射就是對一個類進行解剖,把一個類中的各種成分對映成一個類 java程式執行位元組碼檔案的過程 1啟動jvm程序 2把所有相關的位元組碼加入記憶體(類載入器) 3.系統為每個位元組碼生成一個class物件 4初始化(靜態程式碼塊) 5靜態成員變數 得到一個類的C
基於NACOS和JAVA反射機制動態更新JAVA靜態常量非@Value註解
1.前言 專案中都會使用常量類檔案, 這些值如果需要變動需要重新提交程式碼,或者基於@Value註解實現動態重新整理, 如果常量太多也是很麻煩; 那麼 能不能有更加簡便的實現方式呢? 本文講述的方式是, 一個JAVA類對應NACOS中的一個配置檔案,優先使用nacos中的配置,不配置則使用程式中的預設值; 2
JAVA中反射機制五(JavaBean的內省與BeanUtils庫)
getc ron 輸出結果 下載 比較 static 完成 自動完成 規則 內省(Introspector) 是Java 語言對JavaBean類屬性、事件的一種缺省處理方法。 JavaBean是一種特殊的類,主要用於傳遞數據信息,這種類中的方法主要用於訪問私有的
Java基礎——Java反射機制
靜態 load super actual 返回 generic ref 基本 有一個 Reflection(反射)是Java被視為動態語言的關鍵,反射機制允許程序在執行期借助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法 Java
Java學習總結(十四)——java反射機制,利用反射動態創建對象
Java反射 動態創建對象 一.Java反射機制1.什麽是反射:反射就是把Java類中的各種成份影射成一個個的Java對象。例:一個類有:成員變量,方法,構造方法等,包等等信息,利用反射技術可以對一個類進行剖析,把各個組成部分影射成一個個對象。2.Java反射常用類:(1)Class類—可獲取類和類的
Java 基礎之詳解 Java 反射機制
一行代碼 strac classname for 內部 系統資源 用戶 管理 ann 一、什麽是 Java 的反射機制? ??反射(Reflection)是Java的高級特性之一,是框架實現的基礎,定義:JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有
Java反射機制詳解(由淺入深)
一、什麼是Java反射機制? Java 反射機制在程式執行時,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性。這種 動態的獲取資訊 以及 動態呼叫物件的方法 的功能稱為 java 的反
Java 反射機制詳解(一)
Java 反射機制詳解(一) 主要介紹以下幾方面內容 理解 Class 類 理解 Java 的類載入機制 學會使用 ClassLoader 進行類載入 理解反射的機制 掌握 Constructor、Method、Field 類的用法 理解並掌握動態代理&n
Java 反射機制詳解(四)
Java 反射機制詳解(四) 4. 反射與泛型 定義一個泛型類: public class DAO<T> { //根據id獲取一個物件 T get(Integer id){ return null; }
Java反射機制(超詳細)
文章目錄 反射機制是什麼 反射機制能做什麼 案例 通過一個物件獲得完整的包名和類名 例項化Class類物件 獲取一個物件的父類與實現的介面 通過反射機制例項化一個類的物件 獲取某個類的全部屬性 獲得某個
java反射機制的複習
1、Class類是被final修飾的類不能被繼承 2、Class類的例項表示正在執行的Java應用程式中的類和介面。列舉是一種類,註釋是一種介面。每個陣列屬於被對映為Class物件的一個類,所有具有相同元素型別和維數的陣列都共享該Class物件 。基本的Java型別(bo
【Java 基礎】15 Java 反射機制
反射 為什麼使用反射 需求:我公司定義了一組介面,然後第三方公司按照我公司的介面 實現了一套功能,然後交給我們,但是我們公司的專案已經結束,如何實現動態載入第三方公司提供的功能。 什麼是反射 反射就是把 Java 類中的各種成分對映成一個個的 Java 物件。例
Spring學習5(1):IoC容器之IoC概述,JAVA反射機制,資源訪問器
spring學習5(1) 在經過了對spring框架基本開發的瞭解以及對spring boot流程的學習,《精通spring4.x…》這本書正式開始了spring的講解,我也跟隨著這本書的腳步進行學習。 IoC概述 首先需要學習的是spring的IoC技術,
軟體測試---私有方法的呼叫(Java反射機制)
1.私有構造方法如何例項化類? public class MSD { private int a; private String b; //無參構造方法 private MSD() { } //有參構造方法 private MSD(int a, Strin