java反射學習(一)

LJWLgl發表於2019-01-06

原文地址
題主剛學Java的時候就瞭解過Java反射,但是在實踐開發中使用的並不是很多,所以也一直未深入瞭解過,最近在看一些公司內部框架的原始碼,發現了很多功能都是通過Java反射來實現的。 本篇文章主要介紹Java反射的基本知識,以供自己日後查閱。先介紹一下Java反射的定義

JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。 Java反射的核心是JVM在執行時才動態載入類或呼叫方法/訪問屬性,它在編譯期不需要知道執行的物件是誰,Java反射實際操作物件是.class檔案(位元組碼檔案)

獲得Class物件

1.通過Class類的forName靜態方法,JDBC開發中常用此方法載入驅動

Class<?> c1 = Class.forName("java.lang.Integer");
複製程式碼

2.通過呼叫物件的getClass()方法

Integer i1 = 1;
Class<?> c2 = i1.getClass();
複製程式碼

3.直接獲取類的class

Class<?> c3 = Integer.class;
複製程式碼

判斷是否為某個類的例項

判斷是否是某個類的例項一般用instanceof關鍵字,同時我們也可藉助Class物件的isInstance()方法來判斷,如下面的程式碼

Object obj = "Java";
// 使用instanceof關鍵字
System.out.println(obj instanceof  String);
// 使用isInstance方法
System.out.println(String.class.isInstance(obj));
複製程式碼

isInstance()方法是一個native方法,它的方法簽名如下:

public native boolean isInstance(Object obj);
複製程式碼

建立例項

通過Java反射建立物件有兩種方法: 1.通過Class物件的newInstance方法來建立Class物件對應的例項

Class<?> c = String.class;
Object s = c.newInstance();
複製程式碼

2.先通過Class物件獲取指定的Constructor物件,再呼叫Constructor物件的newInstance方法來建立物件的例項,通過這種方法可以指定構造器來構造物件。

Class<?> cc = String.class;
Constructor constructor =  cc.getConstructor(String.class);
Object ss = constructor.newInstance("Java");
System.out.println(ss);
複製程式碼

獲取類名及變數

直接呼叫Class物件的getName()即可獲取類名(包含package)

Class<?> c = String.class;
System.out.println(c.getName());
複製程式碼

獲取類的變數有下面兩種方法 1.通過Class物件的getField方法獲取類的所有變數,需要注意是getField()會獲取該類及其父類的全部公有變數

// Book是自定義的類
Class<?> c = Book.class;
Field[] fields = c.getFields();

複製程式碼

2.如果還想獲取物件的私有變數,可以通過getDeclaredFields()方法,該方法會返回該類的所有變數,不論訪問許可權

// Book是自定義的類
Class<?> c = Book.class;
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
    // 獲取訪問許可權
    System.out.println(field.getModifiers());
    // 獲取變數型別及名稱
    System.out.println(field.getType() + " " + field.getName());
}
複製程式碼

獲取方法

獲取某個Class物件的方法,主要有以下幾種方法: 1.getDeclaredMethods方法返回類或介面宣告的所有方法,包括公共、保護、預設訪問和私有方法,但不包括繼續的方法。

public Method[] getDeclaredMethods() throws SecurityException
複製程式碼

2.getMethods,返回該類所有的公共的方法以及繼續的類公用方法

public Method[] getMethods() throws SecurityException 
複製程式碼

3.getMethod返回一個特定的方法,第一個引數為方法名稱,後面的引數是方法引數對應的Class物件

public Method getMethod(String name, Class<?>... parameterTypes)
複製程式碼

獲取Class物件的具體列子

Class<?> cc = Book.class;
Method[] methods = cc.getMethods();
for (Method method : methods) {
    //獲取並輸出方法的訪問許可權(Modifiers:修飾符)
    int modifiers = method.getModifiers();
    System.out.print(Modifier.toString(modifiers) + " ");
    //獲取並輸出方法的返回值型別
    Class returnType = method.getReturnType();
    System.out.print(returnType.getName() + " " + method.getName() + "( ");
    //獲取並輸出方法的所有引數
    Parameter[] parameters = method.getParameters();
    for (Parameter parameter: parameters) {
        System.out.print(parameter.getType().getName() + " " + parameter.getName() + ",");
    }
    //獲取並輸出方法丟擲的異常
    Class[] exceptionTypes = method.getExceptionTypes();
    if (exceptionTypes.length == 0){
        System.out.println(" )");
    } else {
        for (Class c : exceptionTypes) {
            System.out.println(" ) throws " + c.getName());
        }
    }
}
複製程式碼

獲取構造器

通過getConstructor方法可以獲取Class物件的構造器,具體的例子在上文建立例項中已經介紹過,在此不再贅述

呼叫方法

當我們從一個類獲取到方法之後,可以用invoke()方法來呼叫這個方法。invoke方法的簽名如下:

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
複製程式碼

invoke具體的例子:

public class Client {
    public static void main(String[] args) throws Exception {
        Class<?> c = Calculation.class;
        Object obj = c.newInstance();
        Method method = c.getMethod("add", int.class, int.class);
        int res = (int) method.invoke(obj, 1, 2);
        System.out.println(res);
    }
}
class Calculation {
    public int add(int x, int y) {
        return x + y;
    }
}
複製程式碼

獲取註解

首先介紹一下註解,註解是Java5中的新特性,它是插入你程式碼中的一種註釋或者說是一種後設資料(meta data)。這些註解資訊可以在編譯期使用預編譯工具進行處理,也可以在執行期使用Java反射機制進行處理。註解可以分為以下四種:

  • 類註解
  • 方法註解
  • 變數註解
  • 引數註解 由於篇幅限制,在此只介紹獲取類的註解,其它註解的獲取可以參考:Java Reflection(八):註解
public class Client {
    public static void main(String[] args) throws Exception {
        Class<?> c = Calculation.class;
        // 獲取所有註解
        // Annotation[] annotations = c.getAnnotations();
        // 指定獲取MyAnnotation註解
        Annotation annotation = c.getAnnotation(MyAnnotation.class);
        if (annotation instanceof MyAnnotation) {
            MyAnnotation myAnnotation = (MyAnnotation) annotation;
            System.out.println(myAnnotation.id());
            System.out.println(myAnnotation.desc());
        }
    }
}

@MyAnnotation(id ="1", desc = "calc")
class Calculation {
    public int add(int x, int y) {
        return x + y;
    }
}

// 自定義註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
    public String id();
    public String desc();
}
複製程式碼

Java反射用途

上文都是介紹java反射的基本用法,在業務開發確實很少使用反射,但是優秀的框架必然會使用到反射來實現很多複雜的功能,比如Spring的IOC(控制反轉),通過解析bean的xml檔案,Spring在執行期根據xml配置,通過Java反射生成相應的bean,放入到Spring容器中。

Java反射的功能

  • 獲取一個物件的類資訊.
  • 獲取一個類的訪問修飾符、成員、方法、構造方法以及超類的資訊.
  • 檢獲屬於一個介面的常量和方法宣告.
  • 建立一個直到程式執行期間才知道名字的類的例項.
  • 獲取並設定一個物件的成員,甚至這個成員的名字是在程式執行期間才知道.
  • 檢測一個在執行期間才知道名字的物件的方法

參考資料

相關文章