摘要:中的反射反射能夠分析類所擁有的能力的程序稱為反射。獲取類的名稱獲取類的修飾符獲取類所在的包獲取父類的屬性獲取類的構造器等等獲得的構造器的使用獲取構造器有兩種方法。
Java中的反射
反射:能夠分析類所擁有的能力的程序稱為反射。
反射的作用當我們在使用一個已有的類的時候,在主代碼的main()方法中使用別的已有的類的時候,如果被使用的類發生了改變,那么導致我們的main()方法中也要修改大量的代碼,以適應被使用的類的修改。這個時候,如果在main()方法使用了反射(reflect),那么就可以通過被使用的類的名字來獲取這個被使用的類的所有情況(方法、Field等),這樣就不必修改我們的主程序了。只需在被使用類的配置文件中寫上被使用類的名字,在mian()方法中通過讀取這個配置文件獲得這個被使用類的名字即可通過反射的方法使用這個類。即使修改了要使用的類的名字或者實現,所有的修改也只體現在這個類的配置文件中,將需要修改的地方減至最少,提升了類的可擴展性。
反射作用使用示例:
被使用類與使用類:
import java.io.*; interface eatable { public void eat(); } class person implements eatable { public void eat() { System.out.println("person EAT"); } } class animal implements eatable { public void eat() { System.out.println("animal EAT"); } } public class ClassTest { public static void main(String[] args)throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("file.txt")) )); Class className = Class.forName(br.readLine()); //使用了anima類 eatable ea= (eatable)className.newInstance(); ea.eat(); } }
被使用類的配置文件:file.txt
animal
各種框架里面大量使用了反射,但是只對用戶暴露了類名與該類的配置文件,防止被修改。
Java類的class屬性程序運行的時候,每一個類(如Person類)在加載的時候,JVM都會為這個類創建一個static的class成員屬性,這個class屬性里面記載的都是這個程序里面所有Person類對象的信息。
同時我們可以通過Person類的class屬性獲得這個Person類的所有情況,如:Person類里面的所有方法對象,Person類里面的所有屬性,Person類里面的所有構造方法對象。
這個class屬性的類型就是Class.
獲取類的Class對象。
Java中的所有類型包括基本類型(int, long, float等等),即使是數組都有與之關聯的Class類的對象。
如果你在編譯期知道一個類的名字的話,那么你可以使用如下的方式獲取一個類的Class對象:
Class myObjectClass = MyObject.class;
如果你在編譯期不知道類的名字,但是你可以在運行期獲得到類名的字符串,那么你則可以這么做來獲取Class對象:
String className = ... ;//在運行期獲取的類名字符串
Class class = Class.forName(className);
在使用Class.forName()方法時,你必須提供一個類的全名,這個全名包括類所在的包的名字。例如MyObject類位于com.jenkov.myapp包,那么他的全名就是com.jenkov.myapp.MyObject。
類的class屬性的作用用于動態獲取類中的成員:
1. 可以獲取這個類的所有方法對象(注意:Java中一切都是對象,包括方法,一個類中的成員,即使連方法也是對象。) 可以通過這個方法對象來使這個方法被執行,將一個類對象作為方法對象的參數傳入即可。
2. 也可以獲取這個類的所有成員。
3. 獲取類的名稱
4. 獲取類的修飾符
5. 獲取類所在的包
6. 獲取父類的class屬性
7. 獲取類的構造器
等等
獲取構造器有兩種方法。
獲取構造器的參數
使用構造器來創建類對象
訪問類的私有變量即私有方法訪問私有變量
要想獲取私有變量你可以調用Class.getDeclaredField(String name)方法或者Class.getDeclaredFields()方法。Class.getField(String name)和Class.getFields()只會返回公有的變量,無法獲取私有變量。下面例子定義了一個包含私有變量的類,在它下面是如何通過反射獲取私有變量的例子:
public class PrivateObject { private String privateString = null; public PrivateObject(String privateString) { this.privateString = privateString; } } PrivateObject privateObject = new PrivateObject("The Private Value"); Field privateStringField = PrivateObject.class. getDeclaredField("privateString"); privateStringField.setAccessible(true); String fieldValue = (String) privateStringField.get(privateObject); System.out.println("fieldValue = " + fieldValue);
這個例子會輸出”fieldValue = The Private Value”,The Private Value是PrivateObject實例的privateString私有變量的值,注意調用 PrivateObject.class.getDeclaredField(“privateString”)方法會返回一個私有變量,這個方法返回的變量是定義在PrivateObject類中的而不是在它的父類中定義的變量。 注意privateStringField.setAccessible(true)這行代碼,通過調用setAccessible()方法會關閉指定類Field實例的反射訪問檢查,這行代碼執行之后不論是私有的、受保護的以及包訪問的作用域,你都可以在任何地方訪問,即使你不在他的訪問權限作用域之內。但是你如果你用一般代碼來訪問這些不在你權限作用域之內的代碼依然是不可以的,在編譯的時候就會報錯。
訪問私有方法
訪問一個私有方法你需要調用 Class.getDeclaredMethod(String name, Class[] parameterTypes)或者Class.getDeclaredMethods() 方法。 Class.getMethod(String name, Class[] parameterTypes)和Class.getMethods()方法,只會返回公有的方法,無法獲取私有方法。下面例子定義了一個包含私有方法的類,在它下面是如何通過反射獲取私有方法的例子:
public class PrivateObject { private String privateString = null; public PrivateObject(String privateString) { this.privateString = privateString; } private String getPrivateString(){ return this.privateString; } } PrivateObject privateObject = new PrivateObject("The Private Value"); Method privateStringMethod = PrivateObject.class. getDeclaredMethod("getPrivateString", null); privateStringMethod.setAccessible(true); String returnValue = (String) privateStringMethod.invoke(privateObject, null); System.out.println("returnValue = " + returnValue);
這個例子會輸出”returnValue = The Private Value”,The Private Value是PrivateObject實例的getPrivateString()方法的返回值。
PrivateObject.class.getDeclaredMethod(“privateString”)方法會返回一個私有方法,這個方法是定義在PrivateObject類中的而不是在它的父類中定義的。
同樣的,注意Method.setAcessible(true)這行代碼,通過調用setAccessible()方法會關閉指定類的Method實例的反射訪問檢查,這行代碼執行之后不論是私有的、受保護的以及包訪問的作用域,你都可以在任何地方訪問,即使你不在他的訪問權限作用域之內。但是你如果你用一般代碼來訪問這些不在你權限作用域之內的代碼依然是不可以的,在編譯的時候就會報錯。
例如:在重寫一個方法的時候,在方法上面加上@Override,在編譯階段就保證編譯成功。這就是注解(Anotation)的作用,這是和注釋不一樣的。
Anotation是一個接口,Override之類的都是它的實現類。
獲取注解對象只有一種方法:反射。
使用反射技術創建數組
int[] intArray = (int[]) Array.newInstance(int.class, 3);
這個例子創建一個int類型的數組。Array.newInstance()方法的第一個參數表示了我們要創建一個什么類型的數組。第二個參數表示了這個數組的空間是多大。
使用反射技術訪問數組內的內容
具體可以使用Array.get(…)和Array.set(…)方法來訪問數組內的內容。下面是一個例子:
int[] intArray = (int[]) Array.newInstance(int.class, 3);
Array.set(intArray, 0, 123);
Array.set(intArray, 1, 456);
Array.set(intArray, 2, 789);
System.out.println("intArray[0] = " + Array.get(intArray, 0));
System.out.println("intArray[1] = " + Array.get(intArray, 1));
System.out.println("intArray[2] = " + Array.get(intArray, 2));
獲取數組對象的class屬性
如果不通過反射的話你可以這樣來獲取數組的Class對象:
>
Class stringArrayClass = String[].class;
如果使用Class.forName()方法來獲取數組的Class對象則不是那么簡單。比如你可以像這樣來獲得一個原生數據類型(primitive)int數組的Class對象:
>
Class intArray = Class.forName("[I");
在JVM中字母I代表int類型,左邊的‘[’代表我想要的是一個int類型的數組,這個規則同樣適用于其他的原生數據類型。
對于普通對象類型的數組有一點細微的不同:
Class stringArrayClass = Class.forName("[Ljava.lang.String;");
注意‘[L’的右邊是類名,類名的右邊是一個‘;’符號。這個的含義是一個指定類型的數組。
獲取普通原生數據類型的class屬性
需要注意的是,你不能通過Class.forName()方法獲取一個原生數據類型的Class對象。下面這兩個例子都會報ClassNotFoundException:
Class intClass1 = Class.forName("I");
Class intClass2 = Class.forName("int");
通常會用下面這個方法來獲取普通對象以及原生對象的Class對象:
public Class getClass(String className){
if("int" .equals(className)) return int.class;
if("long".equals(className)) return long.class;
...
return Class.forName(className);
}
一旦你獲取了類型的Class對象,你就有辦法輕松的獲取到它的數組的Class對象,你可以通過指定的類型創建一個空的數組,然后通過這個空的數組來獲取數組的Class對象。這樣做有點討巧,不過很有效。如下例:
Class theClass = getClass(theClassName); Class stringArrayClass = Array.newInstance(theClass, 0).getClass();
這是一個特別的方式來獲取指定類型的指定數組的Class對象。無需使用類名或其他方式來獲取這個Class對象。
為了確保Class對象是不是代表一個數組,你可以使用Class.isArray()方法來進行校驗:
Class stringArrayClass = Array.newInstance(String.class, 0).getClass(); System.out.println("is array: " + stringArrayClass.isArray());
獲取數組的成員的class屬性
一旦你獲取了一個數組的class屬性,你就可以通過class.getComponentType()方法獲取這個數組的成員類型。
成員類型就是數組存儲的數據類型。例如,數組int[]的成員類型就是一個Class對象int.class。String[]的成員類型就是java.lang.String類的Class對象。
下面是一個訪問數組成員的class屬性的例子:
String[] strings = new String[3];
Class stringArrayClass = strings.getClass();
Class stringArrayComponentType = stringArrayClass.getComponentType();
System.out.println(stringArrayComponentType);
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64187.html
摘要:反射機制一結合官方通過編寫的反射教程,復習一下反射的知識。反射的概念反射是一種在運行時獲取以及修改應用行為的一種工具。因為反射需要動態的解析類的信息,相比于非反射使用的方式要慢。反射需要獲取一定的運行時權限,在特定的安全環境下不一定存在。 Java反射機制(一) 結合Oracle官方通過JDK8編寫的反射教程,復習一下反射的知識。結尾篇補一個小例子。 主要內容 這次博客的主要內容就是簡...
摘要:通過反射獲取帶參無返回值成員方法并使用設置安全檢查,訪問私有構造函數必須創建實例這種不行,注意和方法需要傳遞參數測試復制這個功能獲取私有方法,同樣注意和的區別賦予訪問權限調用方法。 反射 目錄介紹 1.反射概述 1.1 反射概述 1.2 獲取class文件對象的三種方式 1.3 反射常用的方法介紹 1.4 反射的定義 1.5 反射的組成 1.6 反射的作用有哪些 2.反射的...
摘要:反射使用類對象提供的基本元數據,能從類對象中找出方法或字段的名稱,然后獲取表示方法或字段的對象。常見的反射手段有反射和反射。以之前的反射為例其中指定了方法的返回類型,其實不止如此。 Java反射機制主要提供了以下功能: 在運行時判斷任意一個對象所屬的類 在運行時構造任意一個類的對象 在運行時判斷任意一個類所具有的成員變量和方法 在運行時調用任意一個對象的方法 生成動態代理 很多框架...
近期在維護公司項目的時候遇到一個問題,因為實體類中的 set 方法涉及到了業務邏輯,因此在給對象賦值的過程中不能夠使用 set 方法,為了實現功能,所以采用了反射的機制給對象屬性賦值,借此機會也了解了反射的一些具體用法和使用場景,分以下兩點對反射進行分析: 反射的優勢和劣勢 反射的應用場景 反射的優勢和劣勢 ??個人理解,反射機制實際上就是上帝模式,如果說方法的調用是 Java 正確的打開方式...
摘要:一反射機制概念程序運行時,允許改變程序結構或變量類型,這種語言稱為動態語言,如,是動態語言顯然,,不是動態語言,但是有著一個非常突出的動態相關機制。相關的為二獲取源頭重點打開權限所有類的對象其實都是的實例。 一、Java反射機制概念 程序運行時,允許改變程序結構或變量類型,這種語言稱為動態語言,如Python, Ruby是動態語言;顯然C++,Java,C#不是動態語言,但是JAVA有...
閱讀 2889·2021-11-17 09:33
閱讀 3662·2021-11-16 11:42
閱讀 3488·2021-10-26 09:50
閱讀 1318·2021-09-22 15:49
閱讀 3046·2021-08-10 09:44
閱讀 3670·2019-08-29 18:36
閱讀 3925·2019-08-29 16:43
閱讀 2208·2019-08-29 14:10