JAVA深化篇_40—— 反射機制介紹
反射機制介紹
什么是反射
Java 反射機制是Java語言一個很重要的特性,它使得Java具有了“動態性”。在Java程序運行時,對于任意的一個類,我們能不能知道這個類有哪些屬性和方法呢?對于任意的一個對象,我們又能不能調用它任意的方法?答案是肯定的!這種動態獲取類的信息以及動態調用對象方法的功能就來自于Java 語言的反射(Reflection)機制。
反射的作用
簡單來說兩個作用,RTTI(運行時類型識別)和DC(動態創建)。
我們知道反射機制允許程序在運行時取得任何一個已知名稱的class的內部信息,包括其modifiers(修飾符),fields(屬性),methods(方法)等,并可于運行時改變fields內容或調用methods。那么我們便可以更靈活的編寫代碼,代碼可以在運行時裝配,無需在組件之間進行源代碼鏈接,降低代碼的耦合度;還有動態代理的實現等等;但是需要注意的是反射使用不當會造成很高的資源消耗!
創建對象過程
Java創建對象的三個階段
創建對象時內存結構
Users user = new Users();
實際上,我們在加載任何一個類時都會在方法區中建立“這個類對應的Class對象”,由于==“Class對象”包含了這個類的整個結構信息==,所以我們可以通過這個“Class對象”來操作這個類。
我們要使用一個類,首先要加載類;加載完類之后,在堆內存中,就產生了一個 Class 類型的對象(一個類只有一個 Class 對象),這個對象就包含了完整的類的結構信息。我們可以通過這個對象知道類的結構。這個對象就像一面鏡子,透過這個鏡子可以看到類的結構,所以,我們形象的稱之為:反射。 因此,“Class對象”是反射機制的核心。
反射的具體實現
獲取Class對象的三種方式
- 通過getClass()方法;
- 通過.class 靜態屬性;
- 通過Class類中的靜態方法forName();
創建Users類
public class Users {
private String username;
private int userage;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getUserage() {
return userage;
}
public void setUserage(int userage) {
this.userage = userage;
}
}
通過getClass()方法獲取Class對象
/*
* 通過getClass()方法獲取該類的Class對象
getClass()為Object類下的非靜態方法,在使用時需要先實例化對象
*/
public class GetClass1 {
public static void main(String[] args) {
Users users = new Users();
Users users1 = new Users();
Class clazz = users.getClass();
System.out.println(clazz);
System.out.println(clazz.getName());
System.out.println(users.getClass() == users1.getClass());
}
}
通過forName()獲取Class對象
/**
* 通過Class.forName("class Name")獲取Class對象
*/
public class GetClass3 {
public static void main(String[] args)throws Exception {
Class clazz = Class.forName("com.java.Users");
Class clazz2 = Class.forName("com.java.Users");
System.out.println(clazz);
System.out.println(clazz.getName());
System.out.println(clazz == clazz2);
}
}
通過.class 靜態屬性獲取Class對象
/**
* .class靜態屬性獲取Class對象
*/
public class GetClass2 {
public static void main(String[] args) {
Class clazz = Users.class;
Class clazz2 = Users.class;
System.out.println(clazz);
System.out.println(clazz.getName());
System.out.println(clazz == clazz2);
}
}
獲取類的構造方法
方法介紹
| 方法名 | 描述 |
|---|---|
| getDeclaredConstructors() | 返回 Constructor 對象的一個數組,這些對象反映此 Class 對象表示的類聲明的所有構造方法。 |
| getConstructors() | 返回一個包含某些 Constructor 對象的數組,這些對象反映此 Class 對象所表示的類的所有公共(public)構造方法。 |
| getConstructor(Class<?>… parameterTypes) | 返回一個 Constructor 對象,它反映此 Class 對象所表示的類的指定公共(public)構造方法。 |
| getDeclaredConstructor(Class<?>… parameterTypes) | 返回一個 Constructor 對象,該對象反映此 Class 對象所表示的類或接口的指定構造方法。 |
方法使用
修改Users類
public class Users {
private String username;
private int userage;
public Users(){
}
public Users(String username,int userage){
this.username= username;
this.userage=userage;
}
public Users(String username){
this.username= username;
}
private Users(int userage){
this.userage = userage;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getUserage() {
return userage;
}
public void setUserage(int userage) {
this.userage = userage;
}
}
獲取構造方法
public class GetConstructor {
public static void main(String[] args)throws Exception {
Class clazz = Users.class;
Constructor[] arr = clazz.getDeclaredConstructors();
for(Constructor c:arr){
System.out.println(c);
}
System.out.println("---------------------");
Constructor[] arr1 = clazz.getConstructors();
for(Constructor c:arr1){
System.out.println(c);
}
System.out.println("------------------------");
Constructor c = clazz.getDeclaredConstructor(int.class);
System.out.println(c);
System.out.println("------------------------");
Constructor c1 = clazz.getConstructor(null);
System.out.println(c1);
}
}
通過構造方法創建對象 newInstance()
/**
**通過反射實例化對象
*/
public class GetConstructor2 {
public static void main(String[] args) throws Exception{
//創建類對象
Class<Users> usersClass = Users.class;
//通過類對象獲取指定的構造方法對象
Constructor<Users> constructor = usersClass.getConstructor(String.class, int.class);
//通過指定的構造方法對象創建對象
Users users = constructor.newInstance("zhangsan", 20);
System.out.println(users);
}
}
獲取類的成員變量
方法介紹
| 方法名 | 描述 |
|---|---|
| getFields() | 返回Field類型的一個數組,其中包含 Field對象的所有公共(public)字段。 |
| getDeclaredFields() | 返回Field類型的一個數組,其中包含 Field對象的所有字段。 |
| getField(String fieldName) | 返回一個公共成員的Field指定對象。 |
| getDeclaredField(String fieldName) | 返回一個 Field指定對象。 |
方法使用
修改Users類
public class Users {
private String username;
public int userage;
public Users(){
}
public Users(String username,int userage){
this.username= username;
this.userage=userage;
}
public Users(String username){
this.username= username;
}
private Users(int userage){
this.userage = userage;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getUserage() {
return userage;
}
public void setUserage(int userage) {
this.userage = userage;
}
}
獲取成員變量
public class GetField {
public static void main(String[] args) throws Exception{
Class<Users> usersClass = Users.class;
Field[] fields = usersClass.getFields();
for(Field field:fields){
System.out.println(field);
}
System.out.println("===================");
Field[] declaredFields = usersClass.getDeclaredFields();
for(Field field:declaredFields){
System.out.println(field);
}
System.out.println("=================");
Field userName = usersClass.getDeclaredField("userName");
System.out.println(userName);
System.out.println("=================");
Field userAge = usersClass.getField("userAge");
System.out.println(userAge);
}
}
操作成員變量 先實例化對象
public class GetField2 {
public static void main(String[] args)throws Exception {
//獲取Users類的類對象
Class<Users> usersClass = Users.class;
//獲取類的成員變量
Field userAge = usersClass.getField("userAge");
//通過構造方法實例化users對象
Users users = usersClass.getConstructor(null).newInstance();
//給指定成員變量賦值
userAge.set(users,20);
System.out.println(userAge.get(users));
}
}
獲取類的方法
方法介紹
| 方法名 | 描述 |
|---|---|
| getMethods() | 返回一個Method類型的數組,其中包含 所有公共(public)方法。包含父類中的(public)方法!!!!! |
| getDeclaredMethods() | 返回一個Method類型的數組,其中包含 所有方法。 |
| getMethod(String name, Class<?>… parameterTypes) | 返回一個公共的Method方法對象。 |
| getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回一個方法Method對象 |
方法使用
修改Users類
public class Users {
private String userName;
public int userAge;
private Users(String userName){
this.userName = userName;
}
public Users(String userName, int userAge){
this.userName = userName;
this.userAge = userAge;
}
public Users(){
}
public Users(int userAge){
this.userAge = userAge;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getUserAge() {
return userAge;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
private void sing(){
System.out.println("zhangsan愛唱歌");
}
@Override
public String toString() {
return "Users{" +
"userName='" + userName + '\'' +
", userAge=" + userAge +
'}';
}
}
獲取方法
public class GetMethod {
public static void main(String[] args) throws Exception{
Class<Users> usersClass = Users.class;
Method[] classMethods = usersClass.getMethods();
for(Method method : classMethods){
System.out.println(method);
System.out.println(method.getName());
}
System.out.println("----------------");
Method[] declaredMethods = usersClass.getDeclaredMethods();
for(Method method : declaredMethods){
System.out.println(method);
System.out.println(method.getName());
}
System.out.println("=================");
Method setUserAge = usersClass.getMethod("setUserAge", int.class);
System.out.println(setUserAge.getName());
System.out.println(setUserAge);
System.out.println("===============");
Method sing = usersClass.getDeclaredMethod("sing");
System.out.println(sing);
System.out.println(sing.getName());
}
}
調用方法 invoke( )
public class GetMethod2 {
public static void main(String[] args)throws Exception {
//實例化類對象
Class<Users> usersClass = Users.class;
Method setUserName = usersClass.getMethod("setUserName", String.class);
//實例化對象
Users users = usersClass.getConstructor(null).newInstance();
//通過setUserName賦值
setUserName.invoke(users,"zhangsan");
//通過getUserName獲取值
Method getUserName = usersClass.getMethod("getUserName");
Object userName = getUserName.invoke(users);
System.out.println(userName);
}
}
獲取類的其他信息
public class GetClassInfo {
public static void main(String[] args) {
Class<Users> usersClass = Users.class;
//獲取包名
Package usersClassPackage = usersClass.getPackage();
System.out.println(usersClassPackage);
System.out.println(usersClassPackage.getName());
//獲取類名
String usersClassName = usersClass.getName();
System.out.println(usersClassName);
//獲取超類
Class<? super Users> superclass = usersClass.getSuperclass();
System.out.println(superclass.getName());
//獲取所有接口
Class<?>[] classInterfaces = usersClass.getInterfaces();
for(Class interfaces:classInterfaces){
System.out.println(interfaces.getName());
}
}
}
反射機制的效率
由于Java反射是要解析字節碼,將內存中的對象進行解析,包括了一些動態類型,而JVM無法對這些代碼進行優化。因此,反射操作的效率要比那些非反射操作低得多!
接下來我們做個簡單的測試來直接感受一下反射的效率。
反射機制的效率測試
public class Test {
public static void main(String[] args) throws Exception{
Class<?> aClass = Class.forName("com.itbaizhan.Users");
Object o = aClass.getConstructor(null).newInstance();
Method setUsername = aClass.getMethod("setUserName",String.class);
long reflectStart = System.currentTimeMillis();
for(int i =0;i<100000000;i++){
setUsername.invoke(o,"zhangsan");
}
long reflectEnd = System.currentTimeMillis();
long start = System.currentTimeMillis();
Users u =new Users();
for(int i=0;i<100000000;i++){
u.setUserName("zhangsan");
}
long end = System.currentTimeMillis();
System.out.println("反射執行時間:"+(reflectEnd-reflectStart));
System.out.println("普通方式執行時間:"+(end-start));
}
}
反射執行時間:169
普通方式執行時間:0
setAccessible方法
setAccessible()方法:
setAccessible是啟用和禁用訪問安全檢查的開關。值為 true 則指示反射的對象在使用時應該取消 Java 語言訪問檢查。值為 false 則指示反射的對象應該實施 Java 語言訪問檢查;默認值為false。
由于JDK的安全檢查耗時較多.所以通過setAccessible(true)的方式關閉安全檢查就可以達到提升反射速度的目的。
public class Test2 {
public static void main(String[] args) throws Exception{
Class<Users> usersClass = Users.class;
Users users = usersClass.getConstructor(null).newInstance();
Field userName = usersClass.getDeclaredField("userName");
//忽略安全檢查
userName.setAccessible(true);
userName.set(users,"zhangsan");
Object o = userName.get(users);
System.out.println(o);
Method sing = usersClass.getDeclaredMethod("sing");
//忽略安全檢查
sing.setAccessible(true);
sing.invoke(users);
}
}
反射總結
- Java 反射機制是Java語言一個很重要的特性,它使得Java具有了==“動態性”。==
- 反射機制的優點:
- 更靈活。
- 更開放。
- 反射機制的缺點:
- 降低程序執行的效率。
- 增加代碼維護的困難。
- 獲取Class類的對象的三種方式:
- 運用getClass()。(非靜態方法,需要先實例化)
- 運用.class 語法。
- 運用Class.forName()(最常被使用)。
- 反射機制的常見操作
- 動態加載類、動態獲取類的信息(屬性、方法、構造器)。
- 動態構造對象。
- 動態調用類和對象的任意方法。
- 動態調用和處理屬性。
- 獲取泛型信息。
- 處理注解。
浙公網安備 33010602011771號