溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java中的反射有什么用途

發(fā)布時(shí)間:2021-12-21 13:43:14 來源:億速云 閱讀:157 作者:iii 欄目:編程語言

這篇文章主要講解了“Java中的反射有什么用途”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Java中的反射有什么用途”吧!

回顧:什么是反射?

反射(Reflection)是Java 程序開發(fā)語言的特征之一,它允許運(yùn)行中的 Java 程序獲取自身的信息,并且可以操作類或?qū)ο蟮膬?nèi)部屬性。
Oracle官方對(duì)反射的解釋是

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.

?簡而言之,通過反射,我們可以在運(yùn)行時(shí)獲得程序或程序集中每一個(gè)類型的成員和成員的信息。

程序中一般的對(duì)象的類型都是在編譯期就確定下來的,而Java反射機(jī)制可以動(dòng)態(tài)地創(chuàng)建對(duì)象并調(diào)用其屬性,這樣的對(duì)象的類型在編譯期是未知的。所以我們可以通過反射機(jī)制直接創(chuàng)建對(duì)象,即使這個(gè)對(duì)象的類型在編譯期是未知的。

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

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

1.在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類;

2.在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象;

3.在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法(通過反射甚至可以調(diào)用private方法);

4.在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法

重點(diǎn):是運(yùn)行時(shí)而不是編譯時(shí)

反射的主要用途

?很多人都認(rèn)為反射在實(shí)際的Java開發(fā)應(yīng)用中并不廣泛,其實(shí)不然。

?當(dāng)我們?cè)谑褂肐DE(如Eclipse,IDEA)時(shí),當(dāng)我們輸入一個(gè)對(duì)象或類并想調(diào)用它的屬性或方法時(shí),一按點(diǎn)號(hào),編譯器就會(huì)自動(dòng)列出它的屬性或方法,這里就會(huì)用到反射。

?反射最重要的用途就是開發(fā)各種通用框架。

?很多框架(比如Spring)都是配置化的(比如通過XML文件配置JavaBean,Action之類的),為了保證框架的通用性,它們可能需要根據(jù)配置文件加載不同的對(duì)象或類,調(diào)用不同的方法,這個(gè)時(shí)候就必須用到反射——運(yùn)行時(shí)動(dòng)態(tài)加載需要加載的對(duì)象。

?舉一個(gè)例子,在運(yùn)用Struts 2框架的開發(fā)中我們一般會(huì)在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建立了一種映射關(guān)系,當(dāng)View層發(fā)出請(qǐng)求時(shí),請(qǐng)求會(huì)被StrutsPrepareAndExecuteFilter攔截,然后StrutsPrepareAndExecuteFilter會(huì)去動(dòng)態(tài)地創(chuàng)建Action實(shí)例。

——比如我們請(qǐng)求login.action,那么StrutsPrepareAndExecuteFilter就會(huì)去解析struts.xml文件,檢索action中name為login的Action,并根據(jù)class屬性創(chuàng)建SimpleLoginAction實(shí)例,并用invoke方法來調(diào)用execute方法,這個(gè)過程離不開反射。

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

反射的基礎(chǔ):關(guān)于Class類

更多關(guān)于Class類和Object類的原理和介紹請(qǐng)見上一節(jié)

1、Class是一個(gè)類,一個(gè)描述類的類(也就是描述類本身),封裝了描述方法的Method,描述字段的Filed,描述構(gòu)造器的Constructor等屬性

2、對(duì)象照鏡子后(反射)可以得到的信息:某個(gè)類的數(shù)據(jù)成員名、方法和構(gòu)造器、某個(gè)類到底實(shí)現(xiàn)了哪些接口。

3、對(duì)于每個(gè)類而言,JRE 都為其保留一個(gè)不變的 Class 類型的對(duì)象。一個(gè)Class對(duì)象包含了特定某個(gè)類的有關(guān)信息。

4、Class 對(duì)象只能由系統(tǒng)建立對(duì)象

5、一個(gè)類在 JVM 中只會(huì)有一個(gè)Class實(shí)例

//總結(jié)一下就是,JDK有一個(gè)類叫做Class,這個(gè)類用來封裝所有Java類型,包括這些類的所有信息,JVM中類信息是放在方法區(qū)的。
//所有類在加載后,JVM會(huì)為其在堆中創(chuàng)建一個(gè)Class<類名稱>的對(duì)象,并且每個(gè)類只會(huì)有一個(gè)Class對(duì)象,這個(gè)類的所有對(duì)象都要通過Class<類名稱>來進(jìn)行實(shí)例化。
//上面說的是JVM進(jìn)行實(shí)例化的原理,當(dāng)然實(shí)際上在Java寫代碼時(shí)只需要用 類名稱就可以進(jìn)行實(shí)例化了。
public final class Class<T> implements java.io.Serializable,
                          GenericDeclaration,
                          Type,
                          AnnotatedElement {
虛擬機(jī)會(huì)保持唯一一
            //通過類名.class獲得唯一的Class對(duì)象。
            Class<UserBean> cls = UserBean.class;
            //通過integer.TYPEl來獲取Class對(duì)象
            Class<Integer> inti = Integer.TYPE;
          //接口本質(zhì)也是一個(gè)類,一樣可以通過.class獲取
            Class<User> userClass = User.class;

JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語言的反射機(jī)制。

要想解剖一個(gè)類,必須先要獲取到該類的字節(jié)碼文件對(duì)象。而解剖使用的就是Class類中的方法.所以先要獲取到每一個(gè)字節(jié)碼文件對(duì)應(yīng)的Class類型的對(duì)象.

以上的總結(jié)就是什么是反射
反射就是把java類中的各種成分映射成一個(gè)個(gè)的Java對(duì)象
例如:一個(gè)類有:成員變量、方法、構(gòu)造方法、包等等信息,利用反射技術(shù)可以對(duì)一個(gè)類進(jìn)行解剖,把個(gè)個(gè)組成部分映射成一個(gè)個(gè)對(duì)象。(其實(shí):一個(gè)類中這些成員方法、構(gòu)造方法、在加入類中都有一個(gè)類來描述)
如圖是類的正常加載過程:反射的原理在與class對(duì)象。
熟悉一下加載的時(shí)候:Class對(duì)象的由來是將class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)Class對(duì)象。

Java中的反射有什么用途

Java為什么需要反射?反射要解決什么問題?

Java中編譯類型有兩種:

靜態(tài)編譯:在編譯時(shí)確定類型,綁定對(duì)象即通過。
動(dòng)態(tài)編譯:運(yùn)行時(shí)確定類型,綁定對(duì)象。動(dòng)態(tài)編譯最大限度地發(fā)揮了Java的靈活性,體現(xiàn)了多態(tài)的應(yīng)用,可以減低類之間的耦合性。
Java反射是Java被視為動(dòng)態(tài)(或準(zhǔn)動(dòng)態(tài))語言的一個(gè)關(guān)鍵性質(zhì)。這個(gè)機(jī)制允許程序在運(yùn)行時(shí)透過Reflection APIs取得任何一個(gè)已知名稱的class的內(nèi)部信息,包括其modifiers(諸如public、static等)、superclass(例如Object)、實(shí)現(xiàn)之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于運(yùn)行時(shí)改變fields內(nèi)容或喚起methods。

Reflection可以在運(yùn)行時(shí)加載、探知、使用編譯期間完全未知的classes。即Java程序可以加載一個(gè)運(yùn)行時(shí)才得知名稱的class,獲取其完整構(gòu)造,并生成其對(duì)象實(shí)體、或?qū)ζ鋐ields設(shè)值、或喚起其methods。

反射(reflection)允許靜態(tài)語言在運(yùn)行時(shí)(runtime)檢查、修改程序的結(jié)構(gòu)與行為。
在靜態(tài)語言中,使用一個(gè)變量時(shí),必須知道它的類型。在Java中,變量的類型信息在編譯時(shí)都保存到了class文件中,這樣在運(yùn)行時(shí)才能保證準(zhǔn)確無誤;換句話說,程序在運(yùn)行時(shí)的行為都是固定的。如果想在運(yùn)行時(shí)改變,就需要反射這東西了。

實(shí)現(xiàn)Java反射機(jī)制的類都位于java.lang.reflect包中:

Class類:代表一個(gè)類
Field類:代表類的成員變量(類的屬性)
Method類:代表類的方法
Constructor類:代表類的構(gòu)造方法
Array類:提供了動(dòng)態(tài)創(chuàng)建數(shù)組,以及訪問數(shù)組的元素的靜態(tài)方法
一句話概括就是使用反射可以賦予jvm動(dòng)態(tài)編譯的能力,否則類的元數(shù)據(jù)信息只能用靜態(tài)編譯的方式實(shí)現(xiàn),例如熱加載,Tomcat的classloader等等都沒法支持。

反射的基本運(yùn)用

上面我們提到了反射可以用于判斷任意對(duì)象所屬的類,獲得Class對(duì)象,構(gòu)造任意一個(gè)對(duì)象以及調(diào)用一個(gè)對(duì)象。這里我們介紹一下基本反射功能的實(shí)現(xiàn)(反射相關(guān)的類一般都在java.lang.relfect包里)。

1、獲得Class對(duì)象方法有三種

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

 public static Class<?> forName(String className)
```
在JDBC開發(fā)中常用此方法加載數(shù)據(jù)庫驅(qū)動(dòng):
要使用全類名來加載這個(gè)類,一般數(shù)據(jù)庫驅(qū)動(dòng)的配置信息會(huì)寫在配置文件中。加載這個(gè)驅(qū)動(dòng)前要先導(dǎo)入jar包
```java
 Class.forName(driver);

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

//Class<?>是一個(gè)泛型表示,用于獲取一個(gè)類的類型。
Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;

(3)調(diào)用某個(gè)對(duì)象的getClass()方法,比如:

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

判斷是否為某個(gè)類的實(shí)例

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

==public native boolean isInstance(Object obj);==

創(chuàng)建實(shí)例

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

(1)使用Class對(duì)象的newInstance()方法來創(chuàng)建Class對(duì)象對(duì)應(yīng)類的實(shí)例。

注意:利用newInstance創(chuàng)建對(duì)象:調(diào)用的類必須有無參的構(gòu)造器

//Class<?>代表任何類的一個(gè)類對(duì)象。
//使用這個(gè)類對(duì)象可以為其他類進(jìn)行實(shí)例化
//因?yàn)閖vm加載類以后自動(dòng)在堆區(qū)生成一個(gè)對(duì)應(yīng)的*.Class對(duì)象
//該對(duì)象用于讓JVM對(duì)進(jìn)行所有*對(duì)象實(shí)例化。
Class<?> c = String.class;
//Class<?> 中的 ? 是通配符,其實(shí)就是表示任意符合泛類定義條件的類,和直接使用 Class
//效果基本一致,但是這樣寫更加規(guī)范,在某些類型轉(zhuǎn)換時(shí)可以避免不必要的 unchecked 錯(cuò)誤。
Object str = c.newInstance();

(2)先通過Class對(duì)象獲取指定的Constructor對(duì)象,再調(diào)用Constructor對(duì)象的newInstance()方法來創(chuàng)建實(shí)例。這種方法可以用指定的構(gòu)造器構(gòu)造類的實(shí)例。

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

獲取方法

獲取某個(gè)Class對(duì)象的方法集合,主要有以下幾個(gè)方法:

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

public Method[] getDeclaredMethods() throws SecurityException

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

public Method[] getMethods() throws SecurityException

getMethod方法返回一個(gè)特定的方法,其中第一個(gè)參數(shù)為方法名稱,后面的參數(shù)為方法的參數(shù)對(duì)應(yīng)Class的對(duì)象

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

只是這樣描述的話可能難以理解,我們用例子來理解這三個(gè)方法:
本文中的例子用到了以下這些類,用于反射的測試。

//注解類,可可用于表示方法,可以通過反射獲取注解的內(nèi)容。
    //Java注解的實(shí)現(xiàn)是很多注框架實(shí)現(xiàn)注解配置的基礎(chǔ)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Invoke {
}

userbean的父類personbean

public class PersonBean {
private String name;
int id;
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}

}

接口user

public interface User {
    public void login ();
}

userBean實(shí)現(xiàn)user接口,繼承personbean

public class UserBean extends PersonBean implements User{
    @Override
    public void login() {
    }
    class B {
    }
    public String userName;
    protected int i;
    static int j;
    private int l;
    private long userId;
    public UserBean(String userName, long userId) {
        this.userName = userName;
        this.userId = userId;
    }
    public String getName() {
        return userName;
    }
    public long getId() {
        return userId;
    }
    @Invoke
    public static void staticMethod(String devName,int a) {
        System.out.printf("Hi %s, I'm a static method", devName);
    }
    @Invoke
    public void publicMethod() {
        System.out.println("I'm a public method");
    }
    @Invoke
    private void privateMethod() {
        System.out.println("I'm a private method");
    }
}

1 getMethods和getDeclaredMethods的區(qū)別

public class 動(dòng)態(tài)加載類的反射 {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("com.javase.反射.UserBean");
            for (Field field : clazz.getDeclaredFields()) {
//                field.setAccessible(true);
                System.out.println(field);
            }
            //getDeclaredMethod*()獲取的是類自身聲明的所有方法,包含public、protected和private方法。
            System.out.println("------共有方法------");
//        getDeclaredMethod*()獲取的是類自身聲明的所有方法,包含public、protected和private方法。
//            getMethod*()獲取的是類的所有共有方法,這就包括自身的所有public方法,和從基類繼承的、從接口實(shí)現(xiàn)的所有public方法。
            for (Method method : clazz.getMethods()) {
                String name = method.getName();
                System.out.println(name);
                //打印出了UserBean.java的所有方法以及父類的方法
            }
            System.out.println("------獨(dú)占方法------");
            for (Method method : clazz.getDeclaredMethods()) {
                String name = method.getName();
                System.out.println(name);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

2 打印一個(gè)類的所有方法及詳細(xì)信息:

public class 打印所有方法 {
    public static void main(String[] args) {
        Class userBeanClass = UserBean.class;
        Field[] fields = userBeanClass.getDeclaredFields();
        //注意,打印方法時(shí)無法得到局部變量的名稱,因?yàn)閖vm只知道它的類型
        Method[] methods = userBeanClass.getDeclaredMethods();
        for (Method method : methods) {
            //依次獲得方法的修飾符,返回類型和名稱,外加方法中的參數(shù)
            String methodString = Modifier.toString(method.getModifiers()) + " " ; // private static
            methodString += method.getReturnType().getSimpleName() + " "; // void
            methodString += method.getName() + "("; // staticMethod
            Class[] parameters = method.getParameterTypes();
            Parameter[] p = method.getParameters();
            for (Class parameter : parameters) {
                methodString += parameter.getSimpleName() + " " ; // String
            }
            methodString += ")";
            System.out.println(methodString);
        }
        //注意方法只能獲取到其類型,拿不到變量名
/*        public String getName()
        public long getId()
        public static void staticMethod(String int )
        public void publicMethod()
        private void privateMethod()*/
    }
}

獲取構(gòu)造器信息

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

public class 打印構(gòu)造方法 {
    public static void main(String[] args) {
        // constructors
        Class<?> clazz = UserBean.class;
        Class userBeanClass = UserBean.class;
        //獲得所有的構(gòu)造方法
        Constructor[] constructors = userBeanClass.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            String s = Modifier.toString(constructor.getModifiers()) + " ";
            s += constructor.getName() + "(";
            //構(gòu)造方法的參數(shù)類型
            Class[] parameters = constructor.getParameterTypes();
            for (Class parameter : parameters) {
                s += parameter.getSimpleName() + ", ";
            }
            s += ")";
            System.out.println(s);
            //打印結(jié)果//public com.javase.反射.UserBean(String, long, )
        }
    }
}

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

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

getFiled: 訪問公有的成員變量
getDeclaredField:所有已聲明的成員變量。但不能得到其父類的成員變量
getFileds和getDeclaredFields用法同上(參照Method)

public class 打印成員變量 {
    public static void main(String[] args) {
        Class userBeanClass = UserBean.class;
        //獲得該類的所有成員變量,包括static private
        Field[] fields = userBeanClass.getDeclaredFields();
        for(Field field : fields) {
            //private屬性即使不用下面這個(gè)語句也可以訪問
//            field.setAccessible(true);
            //因?yàn)轭惖乃接杏蛟诜瓷渲心J(rèn)可訪問,所以flag默認(rèn)為true。
            String fieldString = "";
            fieldString += Modifier.toString(field.getModifiers()) + " "; // `private`
            fieldString += field.getType().getSimpleName() + " "; // `String`
            fieldString += field.getName(); // `userName`
            fieldString += ";";
            System.out.println(fieldString);
            //打印結(jié)果
//            public String userName;
//            protected int i;
//            static int j;
//            private int l;
//            private long userId;
        }
    }
}

調(diào)用方法

當(dāng)我們從類中獲取了一個(gè)方法后,我們就可以用invoke()方法來調(diào)用這個(gè)方法。invoke方法的原型為:

public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
public class 使用反射調(diào)用方法 {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException {
        Class userBeanClass = UserBean.class;
        //獲取該類所有的方法,包括靜態(tài)方法,實(shí)例方法。
        //此處也包括了私有方法,只不過私有方法在用invoke訪問之前要設(shè)置訪問權(quán)限
        //也就是使用setAccessible使方法可訪問,否則會(huì)拋出異常
//       // IllegalAccessException的解釋是
//        * An IllegalAccessException is thrown when an application tries
// * to reflectively create an instance (other than an array),
// * set or get a field, or invoke a method, but the currently
// * executing method does not have access to the definition of
// * the specified class, field, method or constructor.
//        getDeclaredMethod*()獲取的是類自身聲明的所有方法,包含public、protected和private方法。
//            getMethod*()獲取的是類的所有共有方法,這就包括自身的所有public方法,和從基類繼承的、從接口實(shí)現(xiàn)的所有public方法。
        //就是說,當(dāng)這個(gè)類,域或者方法被設(shè)為私有訪問,使用反射調(diào)用但是卻沒有權(quán)限時(shí)會(huì)拋出異常。
        Method[] methods = userBeanClass.getDeclaredMethods(); // 獲取所有成員方法
        for (Method method : methods) {
            //反射可以獲取方法上的注解,通過注解來進(jìn)行判斷
            if (method.isAnnotationPresent(Invoke.class)) { // 判斷是否被 @Invoke 修飾
                //判斷方法的修飾符是是static
                if (Modifier.isStatic(method.getModifiers())) { // 如果是 static 方法
                    //反射調(diào)用該方法
                    //類方法可以直接調(diào)用,不必先實(shí)例化
                    method.invoke(null, "wingjay",2); // 直接調(diào)用,并傳入需要的參數(shù) devName
                } else {
                    //如果不是類方法,需要先獲得一個(gè)實(shí)例再調(diào)用方法
                    //傳入構(gòu)造方法需要的變量類型
                    Class[] params = {String.class, long.class};
                    //獲取該類指定類型的構(gòu)造方法
                    //如果沒有這種類型的方法會(huì)報(bào)錯(cuò)
                    Constructor constructor = userBeanClass.getDeclaredConstructor(params); // 獲取參數(shù)格式為 String,long 的構(gòu)造函數(shù)
                    //通過構(gòu)造方法的實(shí)例來進(jìn)行實(shí)例化
                    Object userBean = constructor.newInstance("wingjay", 11); // 利用構(gòu)造函數(shù)進(jìn)行實(shí)例化,得到 Object
                    if (Modifier.isPrivate(method.getModifiers())) {
                        method.setAccessible(true); // 如果是 private 的方法,需要獲取其調(diào)用權(quán)限
//                        Set the {@code accessible} flag for this object to
//     * the indicated boolean value.  A value of {@code true} indicates that
//     * the reflected object should suppress Java language access
//     * checking when it is used.  A value of {@code false} indicates
//                                * that the reflected object should enforce Java language access checks.
                        //通過該方法可以設(shè)置其可見或者不可見,不僅可以用于方法
                        //后面例子會(huì)介紹將其用于成員變量
                                            //打印結(jié)果
//            I'm a public method
// Hi wingjay, I'm a static methodI'm a private method
                    }
                    method.invoke(userBean); // 調(diào)用 method,無須參數(shù)
                }
            }
        }
    }
}

利用反射創(chuàng)建數(shù)組

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

public class 用反射創(chuàng)建數(shù)組 {
    public static void main(String[] args) {
        Class<?> cls = null;
        try {
            cls = Class.forName("java.lang.String");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Object array = Array.newInstance(cls,25);
        //往數(shù)組里添加內(nèi)容
        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");
        //獲取某一項(xiàng)的內(nèi)容
        System.out.println(Array.get(array,3));
        //Scala
    }
}

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

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

而newArray()方法是一個(gè)Native方法,它在Hotspot JVM里的具體實(shí)現(xiàn)我們后邊再研究,這里先把源碼貼出來

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

Java反射常見面試題

什么是反射?

反射是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為 Java 語言的反射機(jī)制。

哪里用到反射機(jī)制?

JDBC中,利用反射動(dòng)態(tài)加載了數(shù)據(jù)庫驅(qū)動(dòng)程序。
Web服務(wù)器中利用反射調(diào)用了Sevlet的服務(wù)方法。
Eclispe等開發(fā)工具利用反射動(dòng)態(tài)刨析對(duì)象的類型與結(jié)構(gòu),動(dòng)態(tài)提示對(duì)象的屬性和方法。
很多框架都用到反射機(jī)制,注入屬性,調(diào)用方法,如Spring。

什么叫對(duì)象序列化,什么是反序列化,實(shí)現(xiàn)對(duì)象序列化需要做哪些工作?

對(duì)象序列化,將對(duì)象中的數(shù)據(jù)編碼為字節(jié)序列的過程。
反序列化;將對(duì)象的編碼字節(jié)重新反向解碼為對(duì)象的過程。
JAVA提供了API實(shí)現(xiàn)了對(duì)象的序列化和反序列化的功能,使用這些API時(shí)需要遵守如下約定:
被序列化的對(duì)象類型需要實(shí)現(xiàn)序列化接口,此接口是標(biāo)志接口,沒有聲明任何的抽象方法,JAVA編譯器識(shí)別這個(gè)接口,自動(dòng)的為這個(gè)類添加序列化和反序列化方法。
為了保持序列化過程的穩(wěn)定,建議在類中添加序列化版本號(hào)。
不想讓字段放在硬盤上就加transient
以下情況需要使用 Java 序列化:
想把的內(nèi)存中的對(duì)象狀態(tài)保存到一個(gè)文件中或者數(shù)據(jù)庫中時(shí)候;
想用套接字在網(wǎng)絡(luò)上傳送對(duì)象的時(shí)候;
想通過RMI(遠(yuǎn)程方法調(diào)用)傳輸對(duì)象的時(shí)候。

反射機(jī)制的優(yōu)缺點(diǎn)?

優(yōu)點(diǎn):可以動(dòng)態(tài)執(zhí)行,在運(yùn)行期間根據(jù)業(yè)務(wù)功能動(dòng)態(tài)執(zhí)行方法、訪問屬性,最大限度發(fā)揮了java的靈活性。
缺點(diǎn):對(duì)性能有影響,這類操作總是慢于直接執(zhí)行java代碼。

動(dòng)態(tài)代理是什么?有哪些應(yīng)用?

動(dòng)態(tài)代理是運(yùn)行時(shí)動(dòng)態(tài)生成代理類。
動(dòng)態(tài)代理的應(yīng)用有 Spring AOP數(shù)據(jù)查詢、測試框架的后端 mock、rpc,Java注解對(duì)象獲取等。

怎么實(shí)現(xiàn)動(dòng)態(tài)代理?

JDK 原生動(dòng)態(tài)代理和 cglib 動(dòng)態(tài)代理。
JDK 原生動(dòng)態(tài)代理是基于接口實(shí)現(xiàn)的,而 cglib 是基于繼承當(dāng)前類的子類實(shí)現(xiàn)的。

Java反射機(jī)制的作用

在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類
在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象
在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法
在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法

如何使用Java的反射?

通過一個(gè)全限類名創(chuàng)建一個(gè)對(duì)象

Class.forName(“全限類名”); 例如:com.mysql.jdbc.Driver Driver類已經(jīng)被加載到 jvm中,并且完成了類的初始化工作就行了
類名.class; 獲取Class<?> clz 對(duì)象
對(duì)象.getClass();

獲取構(gòu)造器對(duì)象,通過構(gòu)造器new出一個(gè)對(duì)象

Clazz.getConstructor([String.class]);
Con.newInstance([參數(shù)]);
通過class對(duì)象創(chuàng)建一個(gè)實(shí)例對(duì)象(就相當(dāng)與new類名()無參構(gòu)造器)
Cls.newInstance();

通過class對(duì)象獲得一個(gè)屬性對(duì)象

Field c=cls.getFields():獲得某個(gè)類的所有的公共(public)的字段,包括父類中的字段。
Field c=cls.getDeclaredFields():獲得某個(gè)類的所有聲明的字段,即包括public、private和proteced,但是不包括父類的聲明字段

通過class對(duì)象獲得一個(gè)方法對(duì)象

Cls.getMethod(“方法名”,class……parameaType);(只能獲取公共的)
Cls.getDeclareMethod(“方法名”);(獲取任意修飾的方法,不能執(zhí)行私有)
M.setAccessible(true);(讓私有的方法可以執(zhí)行)
讓方法執(zhí)行
1). Method.invoke(obj實(shí)例對(duì)象,obj可變參數(shù));——-(是有返回值的)

感謝各位的閱讀,以上就是“Java中的反射有什么用途”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Java中的反射有什么用途這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI