Java-基礎-反射


1. 簡介

JAVA反射機制是在運行狀態中

對於任意一個類,都能夠知道這個類的所有屬性和方法

對於任意一個對象,都能夠調用它的任意一個方法和屬性

這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。

2. Class

Class類其實也是一個Java類,存在於JDK的java.lang包中。

java在運行時的類信息就是通過Class對象表示的。它包含了類的所有信息。

所有的類都是在對其第一次使用時,動態加載到JVM中的(懶加載)。當程序創建第一個對類的靜態成員的引用時,就會加載這個類。使用new創建類對象的時候也會被當作對類的靜態成員的引用。因此java程序程序在它開始運行之前並非被完全加載,其各個類都是在必需時才加載的。

所以,當我們寫的.java文件被加載到JVM的后,會在方法區生成該類的Class實例。此實例包含了該類的所有信息。

2.1 如何獲取Class實例

  1. 類型.class
  2. 類實例.getClass()
  3. Class.forName(類的權限定類名)
public class Test {
   public static void main(String[] args) throws ClassNotFoundException {
      Class<Test> testClass = Test.class;
      Test test = new Test();
      Class<? extends Test> testClass1 = test.getClass();
      Class<Test> testClass2 = (Class<Test>) Class.forName("com.ldx.test.Test");
   }
}

2.2 使用Class實例

創建一個User

package com.ldx.test;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class User {
   private String name;

   private Integer age;

   public boolean isAdmin(String name) {
      if("admin".equals(name)) {
         return true;
      }
      return false;
   }
}

創建一個測試類Test

package com.ldx.test;

import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

@Slf4j
public class Test {
   public static void main(String[] args) throws Exception {
      Class<User> userClass = User.class;
      Field[] declaredFields = userClass.getDeclaredFields();
      log.info("User類的名稱:{}", userClass.getSimpleName());
      log.info("User類的權限定類名:{}", userClass.getName());
      log.info("User類的字段有:{}", Arrays.toString(declaredFields));
      log.info("User類的方法有:{}", Arrays.toString(userClass.getDeclaredMethods()));
      Method isAdmin = userClass.getMethod("isAdmin", String.class);
      log.info("User::isAdmin方法返回值為:" + isAdmin.invoke(userClass.newInstance(), "admin"));
   }
}

輸出內容如下:

com.ldx.test.Test - User類的名稱:User
com.ldx.test.Test - User類的權限定類名:com.ldx.test.User
com.ldx.test.Test - User類的字段有:[private java.lang.String com.ldx.test.User.name, private java.lang.Integer com.ldx.test.User.age]
com.ldx.test.Test - User類的方法有:[public boolean com.ldx.test.User.isAdmin(java.lang.String), public java.lang.Integer com.ldx.test.User.getAge(), public void com.ldx.test.User.setAge(java.lang.Integer), public java.lang.String com.ldx.test.User.toString(), public java.lang.String com.ldx.test.User.getName(), public void com.ldx.test.User.setName(java.lang.String)]
com.ldx.test.Test - User::isAdmin方法返回值為:true

2.3 常用方法摘要

返回值 方法說明
<U> Class<? extends U> asSubclass(Class<U> clazz)
強制轉換該 Class 對象,以表示指定的 class 對象所表示的類的一個子類。
static Class<?> forName(String className)
返回與帶有給定字符串名的類或接口相關聯的 Class 對象。
<A extends Annotation>A getAnnotation(Class<A> annotationClass)
如果存在該元素的指定類型的注解,則返回這些注解,否則返回 null。
Annotation[] getAnnotations()
返回此元素上存在的所有注解。
ClassLoader getClassLoader()
返回該類的類加載器。
Class<?> getComponentType()
返回表示數組組件類型的 Class
Annotation[] getDeclaredAnnotations() 返回直接存在於此元素上的所有注解。
Class<?> getDeclaringClass()
如果此 Class 對象所表示的類或接口是另一個類的成員,則返回的 Class 對象表示該對象的聲明類。
T[] getEnumConstants()
如果此 Class 對象不表示枚舉類型,則返回枚舉類的元素或 null。
Type[] getGenericInterfaces()
返回表示某些接口的 Type,這些接口由此對象所表示的類或接口直接實現。
Type getGenericSuperclass()
返回表示此 Class 所表示的實體(類、接口、基本類型或 void)的直接超類的 Type
Class<?>[] getInterfaces()
確定此對象所表示的類或接口實現的接口。
int getModifiers()
返回此類或接口以整數編碼的 Java 語言修飾符。
String getName()
String 的形式返回此 Class 對象所表示的實體(類、接口、數組類、基本類型或 void)名稱。
String getCanonicalName()
返回 Java Language Specification 中所定義的底層類的規范化名稱。主要用於輸出(toString)或log打印,大多數情況下和getName()一樣,但是在內部類、數組等類型的表示形式就不同了。不能用getCanonicalName()去加載類對象,必須用getName()
Package getPackage()
獲取此類的包。
InputStream getResourceAsStream(String name)
查找具有給定名稱的資源。
String getSimpleName()
返回源代碼中給出的底層類的簡稱。
Class<? super T> getSuperclass()
返回表示此 Class 所表示的實體(類、接口、基本類型或 void)的超類的 Class
boolean isAnnotation()
如果此 Class 對象表示一個注解類型則返回 true。
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
如果指定類型的注解存在於此元素上,則返回 true,否則返回 false。
boolean isAnonymousClass()
當且僅當底層類是匿名類時返回 true
boolean isArray()
判定此 Class 對象是否表示一個數組類。
boolean isAssignableFrom(Class<?> cls)
判定此 Class 對象所表示的類或接口與指定的 Class 參數所表示的類或接口是否相同,或是否是其超類或超接口。
boolean isEnum()
當且僅當該類聲明為源代碼中的枚舉時返回 true。
boolean isInstance(Object obj)
如果obj是這個類的一個實例此方法返回true。
boolean isInterface()
判定指定的 Class 對象是否表示一個接口類型。
boolean isLocalClass()
當且僅當這個類是局部類此方法返回true。
boolean isMemberClass()
當且僅當底層類是成員類時返回 true
boolean isPrimitive()
判定指定的 Class 對象是否表示一個基本類型。
T newInstance()
創建此 Class 對象所表示的類的一個新實例。

3. new Instance

想通過反射創建對象大概有以下幾種方式:

  1. 通過Class.newInstance()直接創建對象。
  2. 通過Class實例獲取到Constructor(構造器),通過構造器創建對象。

獲取構造方法的途徑有以下幾種:

返回值 方法說明
Constructor getConstructor(Class<?>... parameterTypes)
返回一個 Constructor 對象,它反映此 Class 對象所表示的類的public構造方法。
Constructor<?>[] getConstructors()
返回所有 Constructor 對象,它反映此 Class 對象所表示的類的public構造方法。
Constructor getDeclaredConstructor(Class<?>... parameterTypes)
返回一個 Constructor 對象,該對象反映此 Class 對象所表示的類的public/private構造方法。
Constructor<?>[] getDeclaredConstructor()
返回所有 Constructor 對象,該對象反映此 Class 對象所表示的類的public/private構造方法。

示例代碼如下:

@Getter
@Setter
@ToString
public class User {
   private String name;
   private Integer age;
   public User() {
      this.name = "張三";
      this.age = 24;
   }
   public boolean isAdmin(String name) {
      if("admin".equals(name)) {
         return true;
      }
      return false;
   }
}
@Slf4j
public class Test {
   public static void main(String[] args) throws Exception {
      Class<User> userClass = User.class;
      User user = userClass.newInstance();
      Constructor<User> constructor = userClass.getConstructor();
      User user1 = constructor.newInstance();
      Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
      // 如果構造器為private,則可以設置訪問權限為true,即可newInstance
      declaredConstructor.setAccessible(true);
      User user2 = declaredConstructor.newInstance();

      log.info(user.toString());
      log.info(user1.toString());
      log.info(user2.toString());
   }
}

輸出內容如下:

15:59:01.653 [main] INFO com.ldx.test.Test - User(name=張三, age=24)
15:59:01.663 [main] INFO com.ldx.test.Test - User(name=張三, age=24)
15:59:01.663 [main] INFO com.ldx.test.Test - User(name=張三, age=24)

4. Method

返回值 方法說明
Method getMethod(String name, Class<?>... parameterTypes)
返回一個 Method 對象,它反映此 Class 對象所表示的類的public方法。(可以獲取父類的方法)
Method[] getMethods()
返回所有 Method 對象,它反映此 Class 對象所表示的類的public方法。(可以獲取父類的方法)
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回一個 Method 對象,該對象反映此 Class 對象所表示的類的public/private方法。(只獲取當前類的方法)
Method[] getDeclaredMethods()
返回所有 Method 對象,它反映此 Class 對象所表示的類的public/private方法。(只獲取當前類的方法)

示例代碼如下:

@Slf4j
public class Test {
   public static void main(String[] args) throws Exception {
      Class<Man> manClass = Man.class;
      Man man = manClass.newInstance();
      Method getHairColor = manClass.getMethod("getHairColor");
      Method getHairColor1 = manClass.getDeclaredMethod("getHairColor");
      // 如果方法為private,則可以設置訪問權限為true,即可newInstance
      getHairColor1.setAccessible(true);
      log.info(getHairColor.invoke(man).toString());
      log.info(getHairColor1.invoke(man).toString());
      // 可以調用到父類方法
      Method isAdmin = manClass.getMethod("isAdmin", String.class);
      log.info(isAdmin.invoke(man, "admin").toString());
      // 獲取不到父類方法
      Method isAdmin1 = manClass.getDeclaredMethod("isAdmin", String.class);
      log.info(isAdmin1.invoke(man, "admin").toString());

   }
}

class Man extends User {
   public String getHairColor() {
      return "一頭黑色秀發";
   }
}

輸出內容如下:

16:30:49.809 [main] INFO com.ldx.test.Test - 一頭黑色秀發
16:30:49.818 [main] INFO com.ldx.test.Test - 一頭黑色秀發
16:30:49.818 [main] INFO com.ldx.test.Test - true
Exception in thread "main" java.lang.NoSuchMethodException: com.ldx.test.Man.isAdmin(java.lang.String)
	at java.lang.Class.getDeclaredMethod(Class.java:2130)
	at com.ldx.test.Test.main(Test.java:31)

Method類除了有上面的invoke方法,常用方法還有如下:

返回值 方法說明
<T extends Annotation>T getAnnotation(Class<T> annotationClass)
如果存在該元素的指定類型的注解,則返回這些注釋,否則返回 null。
Annotation[] getDeclaredAnnotations()
返回直接存在於此元素上的所有注解。
Class<?> getDeclaringClass()
返回表示聲明由此 Method 對象表示的方法的類或接口的 Class 對象。
Type[] getGenericParameterTypes()
按照聲明順序返回 Type 對象的數組,這些對象描述了此 Method 對象所表示的方法的形參類型的。
Type getGenericReturnType()
返回表示由此 Method 對象所表示方法的正式返回類型的 Type 對象。
int getModifiers()
以整數形式返回此 Method 對象所表示方法的 Java 語言修飾符。
String getName()
String 形式返回此 Method 對象表示的方法名稱。
Annotation[][] getParameterAnnotations()
返回表示按照聲明順序對此 Method 對象所表示方法的形參進行注解的那個數組的數組。
Class<?>[] getParameterTypes()
按照聲明順序返回 Class 對象的數組,這些對象描述了此 Method 對象所表示的方法的形參類型。
Class<?> getReturnType()
返回一個 Class 對象,該對象描述了此 Method 對象所表示的方法的正式返回類型。
Object invoke(Object obj, Object... args)
對帶有指定參數的指定對象調用由此 Method 對象表示的底層方法。
boolean isVarArgs()
如果將此方法聲明為帶有可變數量的參數,則返回 true;否則,返回 false
String toGenericString()
返回描述此 Method 的字符串,包括類型參數。
void setAccessible(boolean flag)
將此對象的 accessible 標志設置為指示的布爾值,即設置其可訪問性。

其中:getReturnType/getGenericReturnType都是獲取Method對象表示的方法的返回類型,只不過前者返回的Class類型后者返回的Type,Type就是一個接口而已,在Java8中新增一個默認的方法實現,返回的就參數類型信息。getParameterTypes/getGenericParameterTypes亦是如此。

其返回結果和Class.getTypeName()放回結果相同,就是輸出返回值的類的name(帶包名)。

public interface Type {
    default String getTypeName() {
        return toString();
    }
}

5. Field

返回值 方法說明
Field getField(String name)
返回一個 Field 對象,它反映此 Class 對象所表示的類的public屬性。(可以獲取父類的方法)
Field[] getFields()
返回所有Field屬性,它反映此 Class 對象所表示的類的public屬性。(可以獲取父類的方法)
Field getDeclaredField(String name)
返回一個 Field 對象,該對象反映此 Class 對象所表示的類的public/private屬性。(只獲取當前類的方法)
Field[] getDeclaredFields()
返回所有Field屬性,該對象反映此 Class 對象所表示的類的public/private屬性。(只獲取當前類的方法)

示例代碼如下:

@Slf4j
public class Test {
   public static void main(String[] args) throws Exception {
      Class<Man> manClass = Man.class;
      Man man = manClass.newInstance();
      Field hair = manClass.getDeclaredField("hair");
      hair.setAccessible(true);
      hair.set(man, "濃密的黑發");
      log.info(hair.get(man).toString());
      log.info(man.getHair());
      Class<?> type = hair.getType();
      Class<?> declaringClass = hair.getDeclaringClass();
      log.info(type.getCanonicalName());
      log.info(declaringClass.getCanonicalName());
   }
}

@Getter
class Man extends User {
   private String hair;
}

輸出內容如下:

17:13:17.903 [main] INFO com.ldx.test.Test - 濃密的黑發
17:13:17.909 [main] INFO com.ldx.test.Test - 濃密的黑發
17:13:17.909 [main] INFO com.ldx.test.Test - java.lang.String
17:13:17.909 [main] INFO com.ldx.test.Test - com.ldx.test.Man

Field類除了有上面寫到的方法,常用方法還有如下:

返回值 方法說明
Object get(Object obj)
返回指定對象上此 Field 表示的字段的值。
<T extends Annotation>T getAnnotation(Class<T> annotationClass)
如果存在該元素的指定類型的注解,則返回這些注釋,否則返回 null。
boolean getBoolean(Object obj)
獲取一個靜態或實例 boolean 字段的值。
byte getByte(Object obj)
獲取一個靜態或實例 byte 字段的值。
char getChar(Object obj)
獲取 char 類型或另一個通過擴展轉換可以轉換為 char 類型的基本類型的靜態或實例字段的值。
Annotation[] getDeclaredAnnotations()
返回直接存在於此元素上的所有注解。
Class<?> getDeclaringClass()
返回表示類或接口的 Class 對象,該類或接口聲明由此 Field 對象表示的字段。
double getDouble(Object obj)
獲取 double 類型或另一個通過擴展轉換可以轉換為 double 類型的基本類型的靜態或實例字段的值。
float getFloat(Object obj)
獲取 float 類型或另一個通過擴展轉換可以轉換為 float 類型的基本類型的靜態或實例字段的值。
Type getGenericType()
返回一個 Type 對象,它表示此 Field 對象所表示字段的聲明類型。
int getInt(Object obj)
獲取 int 類型或另一個通過擴展轉換可以轉換為 int 類型的基本類型的靜態或實例字段的值。
long getLong(Object obj)
獲取 long 類型或另一個通過擴展轉換可以轉換為 long 類型的基本類型的靜態或實例字段的值。
int getModifiers()
以整數形式返回由此 Field 對象表示的字段的 Java 語言修飾符。
String getName()
返回此 Field 對象表示的字段的名稱。
short getShort(Object obj)獲取 short 類型或另一個通過擴展轉換可以轉換為 short 類型的基本類型的靜態或實例字段的值。
Class<?> getType()
返回一個 Class 對象,它標識了此 Field 對象所表示字段的聲明類型。
boolean isEnumConstant()
如果此字段表示枚舉類型的元素,則返回 true;否則返回 false
void set(Object obj, Object value)
將指定對象變量上此 Field 對象表示的字段設置為指定的新值。
void setBoolean(Object obj, boolean z)
將字段的值設置為指定對象上的一個 boolean 值。
void setByte(Object obj, byte b)
將字段的值設置為指定對象上的一個 byte 值。
void setChar(Object obj, char c)
將字段的值設置為指定對象上的一個 char 值。
void setDouble(Object obj, double d)
將字段的值設置為指定對象上的一個 double 值。
void setFloat(Object obj, float f)
將字段的值設置為指定對象上的一個 float 值。
void setInt(Object obj, int i)
將字段的值設置為指定對象上的一個 int 值。
void setLong(Object obj, long l)
將字段的值設置為指定對象上的一個 long 值。
void setShort(Object obj, short s)
將字段的值設置為指定對象上的一個 short 值。
String toGenericString()
返回一個描述此 Field(包括其一般類型)的字符串。
void setAccessible(boolean flag)
將此對象的 accessible 標志設置為指示的布爾值,即設置其可訪問性。

6. 反射機制的執行流程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM