最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入解析Java反射之基礎篇

 更新時間:2021年11月18日 11:41:01   作者:sczyh30  
JAVA反射機制是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法,這篇文章主要給大家介紹了關于Java反射之基礎篇的相關資料,需要的朋友可以參考下

前言

因為本人最近正籌備Samsara框架的開發(fā),而其中的IOC部分非常依靠反射,因此趁這個機會來總結一下關于Java反射的一些知識。本篇為基本篇,基于JDK 1.8。

一、回顧:什么是反射?

反射 (Reflection) 是 Java 的特征之一,它允許運行中的 Java 程序獲取自身的信息,并且可以操作類或對象的內部屬性。

Oracle 官方對反射的解釋是:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

簡而言之,通過反射,我們可以在運行時獲得程序或程序集中每一個類型的成員和成員的信息。程序中一般的對象的類型都是在編譯期就確定下來的,而 Java 反射機制可以動態(tài)地創(chuàng)建對象并調用其屬性,這樣的對象的類型在編譯期是未知的。所以我們可以通過反射機制直接創(chuàng)建對象,即使這個對象的類型在編譯期是未知的。

反射的核心是 JVM 在運行時才動態(tài)加載類或調用方法/訪問屬性,它不需要事先(寫代碼的時候或編譯期)知道運行對象是誰。

Java 反射主要提供以下功能:

  • 在運行時判斷任意一個對象所屬的類;
  • 在運行時構造任意一個類的對象;
  • 在運行時判斷任意一個類所具有的成員變量和方法(通過反射甚至可以調用private方法);
  • 在運行時調用任意一個對象的方法

重點:是運行時而不是編譯時

二、反射的主要用途

很多人都認為反射在實際的 Java 開發(fā)應用中并不廣泛,其實不然。當我們在使用 IDE(如 Eclipse,IDEA)時,當我們輸入一個對象或類并想調用它的屬性或方法時,一按點號,編譯器就會自動列出它的屬性或方法,這里就會用到反射。

反射最重要的用途就是開發(fā)各種通用框架。很多框架(比如 Spring)都是配置化的(比如通過 XML 文件配置 Bean),為了保證框架的通用性,它們可能需要根據配置文件加載不同的對象或類,調用不同的方法,這個時候就必須用到反射,運行時動態(tài)加載需要加載的對象。

舉一個例子,在運用 Struts 2 框架的開發(fā)中我們一般會在 struts.xml 里去配置 Action,比如:

<action name="login"
               class="org.ScZyhSoft.test.action.SimpleLoginAction"
               method="execute">
           <result>/shop/shop-index.jsp</result>
           <result name="error">login.jsp</result>
       </action>

配置文件與 Action 建立了一種映射關系,當 View 層發(fā)出請求時,請求會被 StrutsPrepareAndExecuteFilter 攔截,然后 StrutsPrepareAndExecuteFilter 會去動態(tài)地創(chuàng)建 Action 實例。比如我們請求 login.action,那么 StrutsPrepareAndExecuteFilter就會去解析struts.xml文件,檢索action中name為login的Action,并根據class屬性創(chuàng)建SimpleLoginAction實例,并用invoke方法來調用execute方法,這個過程離不開反射。

對與框架開發(fā)人員來說,反射雖小但作用非常大,它是各種容器實現的核心。而對于一般的開發(fā)者來說,不深入框架開發(fā)則用反射用的就會少一點,不過了解一下框架的底層機制有助于豐富自己的編程思想,也是很有益的。

三、反射的基本運用

上面我們提到了反射可以用于判斷任意對象所屬的類,獲得 Class 對象,構造任意一個對象以及調用一個對象。這里我們介紹一下基本反射功能的使用和實現(反射相關的類一般都在 java.lang.relfect 包里)。

1、獲得 Class 對象

方法有三種:

(1) 使用 Class 類的 forName 靜態(tài)方法:

 public static Class<?> forName(String className)
``` 
比如在 JDBC 開發(fā)中常用此方法加載數據庫驅動:
```java
 Class.forName(driver);

(2)直接獲取某一個對象的 class,比如:

Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;

(3)調用某個對象的 getClass() 方法,比如:

StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();

2、判斷是否為某個類的實例

一般地,我們用 instanceof 關鍵字來判斷是否為某個類的實例。同時我們也可以借助反射中 Class 對象的 isInstance() 方法來判斷是否為某個類的實例,它是一個 native 方法:

public native boolean isInstance(Object obj);

3、創(chuàng)建實例

通過反射來生成對象主要有兩種方式。

使用Class對象的newInstance()方法來創(chuàng)建Class對象對應類的實例。

Class<?> c = String.class;
Object str = c.newInstance();

先通過Class對象獲取指定的Constructor對象,再調用Constructor對象的newInstance()方法來創(chuàng)建實例。這種方法可以用指定的構造器構造類的實例。

//獲取String所對應的Class對象
Class<?> c = String.class;
//獲取String類帶一個String參數的構造器
Constructor constructor = c.getConstructor(String.class);
//根據構造器創(chuàng)建實例
Object obj = constructor.newInstance("23333");
System.out.println(obj);

4、獲取方法

獲取某個Class對象的方法集合,主要有以下幾個方法:

getDeclaredMethods 方法返回類或接口聲明的所有方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。

public Method[] getDeclaredMethods() throws SecurityException

getMethods 方法返回某個類的所有公用(public)方法,包括其繼承類的公用方法。

public Method[] getMethods() throws SecurityException

getMethod 方法返回一個特定的方法,其中第一個參數為方法名稱,后面的參數為方法的參數對應Class的對象。

public Method getMethod(String name, Class<?>... parameterTypes)

只是這樣描述的話可能難以理解,我們用例子來理解這三個方法:

package org.ScZyhSoft.common;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class test1 {
 public static void test() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
         Class<?> c = methodClass.class;
         Object object = c.newInstance();
         Method[] methods = c.getMethods();
         Method[] declaredMethods = c.getDeclaredMethods();
         //獲取methodClass類的add方法
         Method method = c.getMethod("add", int.class, int.class);
         //getMethods()方法獲取的所有方法
         System.out.println("getMethods獲取的方法:");
         for(Method m:methods)
             System.out.println(m);
         //getDeclaredMethods()方法獲取的所有方法
         System.out.println("getDeclaredMethods獲取的方法:");
         for(Method m:declaredMethods)
             System.out.println(m);
     }
    }
class methodClass {
    public final int fuck = 3;
    public int add(int a,int b) {
        return a+b;
    }
    public int sub(int a,int b) {
        return a+b;
    }
}

程序運行的結果如下:

getMethods獲取的方法:
public int org.ScZyhSoft.common.methodClass.add(int,int)
public int org.ScZyhSoft.common.methodClass.sub(int,int)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
getDeclaredMethods獲取的方法:
public int org.ScZyhSoft.common.methodClass.add(int,int)
public int org.ScZyhSoft.common.methodClass.sub(int,int)

可以看到,通過 getMethods() 獲取的方法可以獲取到父類的方法,比如 java.lang.Object 下定義的各個方法。

5、獲取構造器信息

獲取類構造器的用法與上述獲取方法的用法類似。主要是通過Class類的getConstructor方法得到Constructor類的一個實例,而Constructor類有一個newInstance方法可以創(chuàng)建一個對象實例:

public T newInstance(Object ... initargs)

此方法可以根據傳入的參數來調用對應的Constructor創(chuàng)建對象實例。

6、獲取類的成員變量(字段)信息

主要是這幾個方法,在此不再贅述:

  • getFiled:訪問公有的成員變量
  • getDeclaredField:所有已聲明的成員變量,但不能得到其父類的成員變量

getFileds 和 getDeclaredFields 方法用法同上(參照 Method)。

7、調用方法

當我們從類中獲取了一個方法后,我們就可以用 invoke() 方法來調用這個方法。invoke 方法的原型為:

public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException

下面是一個實例:

public class test1 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> klass = methodClass.class;
        //創(chuàng)建methodClass的實例
        Object obj = klass.newInstance();
        //獲取methodClass類的add方法
        Method method = klass.getMethod("add",int.class,int.class);
        //調用method對應的方法 => add(1,4)
        Object result = method.invoke(obj,1,4);
        System.out.println(result);
    }
}
class methodClass {
    public final int fuck = 3;
    public int add(int a,int b) {
        return a+b;
    }
    public int sub(int a,int b) {
        return a+b;
    }
}

關于 invoke 方法的詳解,后面我會專門寫一篇文章來深入解析 invoke 的過程。

8、利用反射創(chuàng)建數組

數組在Java里是比較特殊的一種類型,它可以賦值給一個Object Reference。下面我們看一看利用反射創(chuàng)建數組的例子:

public static void testArray() throws ClassNotFoundException {
        Class<?> cls = Class.forName("java.lang.String");
        Object array = Array.newInstance(cls,25);
        //往數組里添加內容
        Array.set(array,0,"hello");
        Array.set(array,1,"Java");
        Array.set(array,2,"fuck");
        Array.set(array,3,"Scala");
        Array.set(array,4,"Clojure");
        //獲取某一項的內容
        System.out.println(Array.get(array,3));
    }

其中的Array類為java.lang.reflect.Array類。我們通過Array.newInstance()創(chuàng)建數組對象,它的原型是:

public static Object newInstance(Class<?> componentType, int length)
        throws NegativeArraySizeException {
        return newArray(componentType, length);
    }

而 newArray 方法是一個 native 方法,它在 HotSpot JVM 里的具體實現我們后邊再研究,這里先把源碼貼出來:

private static native Object newArray(Class<?> componentType, int length)
        throws NegativeArraySizeException;

源碼目錄:openjdk\hotspot\src\share\vm\runtime\reflection.cpp

arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) {
  if (element_mirror == NULL) {
    THROW_0(vmSymbols::java_lang_NullPointerException());
  }
  if (length < 0) {
    THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
  }
  if (java_lang_Class::is_primitive(element_mirror)) {
    Klass* tak = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL);
    return TypeArrayKlass::cast(tak)->allocate(length, THREAD);
  } else {
    Klass* k = java_lang_Class::as_Klass(element_mirror);
    if (k->oop_is_array() && ArrayKlass::cast(k)->dimension() >= MAX_DIM) {
      THROW_0(vmSymbols::java_lang_IllegalArgumentException());
    }
    return oopFactory::new_objArray(k, length, THREAD);
  }
}

另外,Array 類的 set 和 get 方法都為 native 方法,在 HotSpot JVM 里分別對應 Reflection::array_set 和 Reflection::array_get 方法,這里就不詳細解析了。

四、反射的一些注意事項

由于反射會額外消耗一定的系統(tǒng)資源,因此如果不需要動態(tài)地創(chuàng)建一個對象,那么就不需要用反射。

另外,反射調用方法時可以忽略權限檢查,因此可能會破壞封裝性而導致安全問題。

到此這篇關于Java反射之基礎篇的文章就介紹到這了,更多相關Java反射之基礎篇內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 詳解Java線程池隊列中的延遲隊列DelayQueue

    詳解Java線程池隊列中的延遲隊列DelayQueue

    這篇文章主要為大家詳細介紹了Java線程池隊列中的延遲隊列DelayQueue的相關資料,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2022-12-12
  • SpringBoot實現無限級評論回復的項目實踐

    SpringBoot實現無限級評論回復的項目實踐

    本文主要介紹了SpringBoot實現無限級評論回復的項目實踐,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03
  • springboot3.0整合mybatis-flex實現逆向工程的示例代碼

    springboot3.0整合mybatis-flex實現逆向工程的示例代碼

    逆向工程先創(chuàng)建數據庫表,由框架負責根據數據庫表,自動生成mybatis所要執(zhí)行的代碼,本文就來介紹一下springboot mybatis-flex逆向工程,感興趣的可以了解一下
    2024-06-06
  • Hyperlane 文件分塊上傳服務端的解決方案

    Hyperlane 文件分塊上傳服務端的解決方案

    在現代Web應用中,文件上傳是一個核心功能,尤其是對于大文件,傳統(tǒng)的上傳方式常常因網絡中斷或超時而失敗,為了解決這一痛點,我們推出了基于 Hyperlane 的文件分塊上傳服務端代碼,為開發(fā)者提供了一個高效、可靠的大文件上傳解決方案,感興趣的朋友一起看看吧
    2025-04-04
  • 一文詳解Spring事務的實現與本質

    一文詳解Spring事務的實現與本質

    這篇文章主要介紹了Spring中事務的兩種實現方式:聲明式事務、編程式事務以及他們的本質。文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-04-04
  • JAVA使用quartz添加定時任務,并依賴注入對象操作

    JAVA使用quartz添加定時任務,并依賴注入對象操作

    這篇文章主要介紹了JAVA使用quartz添加定時任務,并依賴注入對象操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Calcite使用SQL實現查詢excel內容

    Calcite使用SQL實現查詢excel內容

    因為calcite本身沒有excel的適配器,?所以本文將模仿calcite-file,?搞一個calcite-file-excel實現查詢excel內容,感興趣的小伙伴可以跟隨小編一起學習一下
    2025-01-01
  • Tomcat能起開,但是訪問不進8080首頁的問題解決方案

    Tomcat能起開,但是訪問不進8080首頁的問題解決方案

    這篇文章主要介紹了Tomcat能起開,但是訪問不進8080首頁的問題解決方案的相關資料,需要的朋友可以參考下
    2016-10-10
  • Redis高并發(fā)場景防止庫存數量超賣少賣

    Redis高并發(fā)場景防止庫存數量超賣少賣

    商品超賣是銷售數量超過實際庫存的情況,常因庫存管理不當引發(fā),傳統(tǒng)庫存管理在高并發(fā)環(huán)境下易出錯,可通過線程加鎖或使用Redis同步庫存狀態(tài)解決,本文就來詳細的介紹一下,感興趣的可以了解一下
    2024-09-09
  • Java數據結構之對象比較詳解

    Java數據結構之對象比較詳解

    這篇文章主要為大家詳細介紹了Java中對象的比較、集合框架中PriorityQueue的比較方式以及PriorityQueue的模擬實現,感興趣的可以了解一下
    2022-07-07

最新評論

黄平县| 新乡县| 南川市| 江口县| 浏阳市| 乌审旗| 仁怀市| 梅河口市| 阳春市| 华坪县| 凤城市| 永善县| 清水河县| 广元市| 延川县| 乌鲁木齐市| 呼图壁县| 绥化市| 上犹县| 社旗县| 海晏县| 海晏县| 井研县| 东兰县| 桂阳县| 镇沅| 五大连池市| 永修县| 孙吴县| 兴城市| 江川县| 德州市| 宁德市| 镇江市| 丰镇市| 伊宁县| 霞浦县| 汾阳市| 田阳县| 丰镇市| 新巴尔虎右旗|