JAVA_反射機制(照鏡子)

反射

首先反射,當你照鏡子的時候你的面部的個個部位都會通過鏡子反射出來,讓你看道你自己。而JAVA也會像我們一樣,也有一面鏡子,它可以通過這面鏡子,看到自己的一些信息。JAVA反射機制就是對於一段程序,在運行的時候才知道要準備操作的類是什麼,並可以在運行的時候獲取類的完整構造,並調用對應的方法,獲取.class文件。而我們就可以通過java反射機制得到一些程序中的類的一些具體信息,參數,方法等。JAVA反射機制用在並不知道初始化的類對象是什麼,然而我們無法使用new關鍵字來創建一個對象對它的方法屬性進行調用。這時我們就利用JAVA反射機制,動態獲取對象的信息,和調用方法,這一功能就是反射機制。

Class

對於java反射機制的原理就是在Class類中,首先簡單講解一下class類這裏並不會主要解釋,主要是涉及一些方法去使用。首先官方解釋爲,**Class 類的實例表示正在運行的 Java 應用程序中的類和接口。枚舉是一種類,註釋是一種接口。每個數組屬於被映射爲 Class 對象的一個類,所有具有相同元素類型和維數的數組都共享該 Class 對象。基本的 Java 類型(boolean、byte、char、short、int、long、float 和 double)和關鍵字 void 也表示爲 Class 對象。**也就是說Class類保存這些類型信息的類。當加載一個.class文件時,就創建這個類的class對象,之後的對象都是由這個class對象創建的,所以對於同一個類的所有對象,class對象只有一個。

A a = new A(); A b = new A(); a.class = b.class;

Class無公共構造方法,Class 對象是在加載類時由 Java 虛擬機以及通過調用類加載器中的 defineClass 方法自動構造的。而對於反射機制來說,就是將java類中的各種成分映射成一個個java類的對象,對於一個類的方法,構造方法,變量等,這些成員在加入類中都會有一個Class類來描述。
接下來主要簡單講解一下我們通過正常new()的方式去創建一個對象是如何實現的。
首先我們一個類中的構造方法,變量,方法等相關信息都是在class類中,當我們對類Anew() 一個對象aJVM 會去加載A.class,將會在磁盤找到A.class文件,並加載到JVM內存中,這時我們創建的A類的對象空間就會去指向A類中的相關信息,而A類的信息都是由class對象指向的,所以也可這樣說A類的對象可以有多個,但是class的對象只有一個。
接下來就將簡單介紹幾個Class類的方法。

1.static Class<?> forName(String className) //返回與帶有給定字符串名的類或接相關聯的 Class 對象 
2.String getName() //以 String 的形式返回此 Class 對象所表示的實體(類、接口、數組類、基本類型或 void)名稱。
3. T newInstance() //創建此 Class 對象所表示的類的一個新實例。  

並不是Class類的方法只有這幾個,而是對於反射機制,我們會用到這幾個方法,但是進入反射實戰之前,我們先簡單說一下 newInstance() ;
該方法可以創建class對象所表示的類的新實例,而他和我們去new()一個方法有什麼區別呢??
首先 newInstance() :弱類型,低效率,只能調用無參的構造方法。
new():強類型,相對高效,能調用任何public構造。
下面我還是通過一段代碼看看 newInstance()具體如何實現的。

package java_反射機制之class類創建一個實例;
import java.util.concurrent.Callable;
import java.lang.Class;
import static java.lang.Class.forName;
public class test {
    public static void main(String[] args) throws Exception{
        //test2 t2 = new test2();
        Class c = Class.forName("java_反射機制之class類創建一個實例.test2");//必須傳入一個包下的一個路徑以包.類。
        test2 t = (test2) c.newInstance();//並將類實例化,只能調用無參構造方法。
        t.show();
    }
}
interface test1{
    abstract void show();
}
class test2 implements test1{
    @Override
    public void show() {
        System.out.println("使用接口");
    }
}
運行結果:使用接口

這裏去使用 newInstance()去創建test2()類的一個實例,然後通過該實例去調用接口中的方法。然後對於newInstance()所返回的類型是Object類型,所以我們要其進行強制轉型。

反射實戰

首先對於反射我們先要

  1. 獲取反射的class對象
  2. 2.通過反射創建類對象
  3. 3.通過反射獲取類屬性及構造器
    首先對於第一部最重要就是我們如何獲取反射的class類的對象。這裏我們比較常用的就是 **Class.forName(String className) **
    接下來我們先用反射去獲取不同修飾類型的構造方法,在這之前先了解一下相關方法:
/*1.獲取構造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"構造方法
            public Constructor[] getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護、默認、公有)

 * 		2).獲取單個的方法,並調用:
 * 			public Constructor getConstructor(Class... parameterTypes):獲取單個的"公有的"構造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):獲取"某個構造方法"可以是私有的,或受保護、默認、公有;
 *
 * 			調用構造方法:
 * 			Constructor-->newInstance(Object... initargs)
 * /
package java_反射機制獲取構造方法;
import java.lang.Class;
import java.lang.reflect.Constructor;

public class test {
    test(String str){
        System.out.println("默認的構造方法"+str);
    }
    public test(){
        System.out.println("調用共有無參構造方法並執行");
    }
    public test(int age){
        System.out.println("調用共有有參構造方法 "+ age);
    }
    private test(char name){
        System.out.println("調用私有有參構造方法"+name);
    }
    protected test(boolean n){
        System.out.println("調用受保護的構造方法 "+n);
    }
    public static void main(String[] args) throws Exception {
     Class c = Class.forName("java_反射機制獲取構造方法.test");
        System.out.println("****所有共有構造方法******");
        Constructor[] con = c.getConstructors();
        for(Constructor i:con){
            System.out.println(i);
        }
        System.out.println("*****獲取單個共有構造方法並調用******");
        Constructor cn = c.getConstructor(int.class);
        System.out.println(cn);
        //cn.setAccessible(true);//若是在另外一個類中去調用時必須使用,這裏再同一個類下不需要暴力反射
        cn.newInstance(10);
        System.out.println("*****獲取某個受保護的構造方法並調用");
        Constructor cn1 = c.getDeclaredConstructor(boolean.class);
        System.out.println(cn1);
        cn1.newInstance(true);
    }
}
class test2{
    public static void main(String[] args) throws Exception{
        Class c = Class.forName("java_反射機制獲取構造方法.test");
        System.out.println("***單個獲取私有構造方法並調用*****");
        Constructor cn = c.getDeclaredConstructor(char.class);
        cn.setAccessible(true);//這裏使用到了暴力訪問,強制忽略私有修飾符。
        cn.newInstance('a');
    }
}
運行結果:
****所有共有構造方法******
public java_反射機制獲取構造方法.test(int)
public java_反射機制獲取構造方法.test()
*****獲取單個共有構造方法並調用******
public java_反射機制獲取構造方法.test(int)
調用共有有參構造方法 10
*****獲取某個受保護的構造方法並調用
protected java_反射機制獲取構造方法.test(boolean)
調用受保護的構造方法 true
test2運行結果:
***單個獲取私有構造方法並調用*****
調用私有有參構造方法a

這裏主要簡單講解一下關於如何獲取構造方法,並去調用。這裏能我創建了兩個類,在不同的類下去反射獲取構造方法並去調用,我們先來簡單講解一下在test類下的調用方法:
首先就是先獲取class類的對象,然後通過調用獲取單個構造方法的方法去獲取構造方法,之後去調用構造方法,這是簡單的流程。
對於test2 類中對於去調用私有構造方法時,使用到了**setAccessible()**暴力訪問,強制忽略私有修飾符。
接下來我們對於反射獲取方法並調用的代碼演示
先讓我們看一下使用到相關方法:

 /*
 * 獲取成員方法並調用:
 *
 * 1.批量的:
 * 		public Method[] getMethods():獲取所有"公有方法";(包含了父類的方法也包含Object類)
 * 		public Method[] getDeclaredMethods():獲取所有的成員方法,包括私有的(不包括繼承的)
 * 2.獲取單個的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					參數:
 * 						name : 方法名;
 * 						Class ... : 形參的Class類型對象
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 *
 * 	 調用方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					參數說明:
 * 					obj : 要調用方法的對象;
 * 					args:調用方式時所傳遞的實參;
):
 */
package java_反射機制獲取成員方法;
import java.lang.reflect.Method;
public class test {
    public void show(String s){
        System.out.println("獲取有參公開方法"+s);
    }
    public void show1(){
        System.out.println("獲取無參公開方法");
    }
    private void show2(int i){
        System.out.println("獲取私有構造方法"+i);
    }
    public static void main(String[] args)throws Exception{
     Class c = Class.forName("java_反射機制獲取成員方法.test");
        System.out.println("或許所有公開方法");//可能獲取包括obiect方法
        Method[] m = c.getMethods();
        for(Method i:m){
            System.out.println(i);
        }
        System.out.println("獲取公開方法並調用");
        Method m1 = c.getMethod("show", String.class);
        test t = (test)c.newInstance();
        m1.invoke(t,"***");
    }
}
class test1 {
    public static void main(String[] args)throws Exception{
        Class c = Class.forName("java_反射機制獲取成員方法.test");
        System.out.println("異類獲取並調用私有方法");
        Method m = c.getDeclaredMethod("show", String.class);
        test t = (test)c.newInstance();
        m.setAccessible(true);//強制獲取私有方法
        m.invoke(t,"這裏是異類");
    }
}
運行結果:
public static void java_反射機制獲取成員方法.test.main(java.lang.String[]) throws java.lang.Exception
public void java_反射機制獲取成員方法.test.show(java.lang.String)
public void java_反射機制獲取成員方法.test.show1()
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() 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()
獲取公開方法並調用
獲取有參公開方法***

從運行結果也不難看出,我們獲取的方法包含了父類的方法,也包括了Object類的方法。就主要簡單講解一下如何調用方法的,首先也是獲取單個你需要的方法,之後這裏就使用到了剛剛通過 newInstance() 去創建一個新的實列,然後通過該實例去對獲取的方法調用,並賦值。而對於異類中對於獲取到的私有方法的調用也是用到了**setAccessible()**去暴力訪問。
接下來就是關於成員變量的獲取和調用
先看一下相關方法:

/*
         * 獲取成員變量並調用:
         *
         * 1.批量的
         * 		1).Field[] getFields():獲取所有的"公有字段"
         * 		2).Field[] getDeclaredFields():獲取所有字段,包括:私有、受保護、默認、公有;
         * 2.獲取單個的:
         * 		1).public Field getField(String fieldName):獲取某個"公有的"字段;
         * 		2).public Field getDeclaredField(String fieldName):獲取某個字段(可以是私有的)
         *
         * 	 設置字段的值:
         * 		Field --> public void set(Object obj,Object value):
         * 					參數說明:
         * 					1.obj:要設置的字段所在的對象;
         * 					2.value:要爲字段設置的值;
         *
         */
package java_反射機制獲取成員變量;
import java.lang.reflect.Field;
public class test {
    public String name;//設置公共屬性姓名
    protected int age;
    private String phone;
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    @Override
    public String toString() {
        return "test{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                '}';
    }
    public static void main(String[] args)throws Exception{
	Class c = Class.forName("java_反射機制獲取成員變量.test");
        System.out.println("*****獲取共有字段*****");
        Field[] fi = c.getFields();
        for(Field i:fi){
            System.out.println(i);
        }
        System.out.println("*****單個獲取共有字段並賦值*******");
        Field fi1 = c.getField("name");
        System.out.println(fi1);
        test t = (test)c.newInstance();//創建一個對象
        fi1.set(t,"***");//通過引用該對象去賦值
        System.out.println(t.name);
    }
}
class test2{//不同類中使用反射機制
    public static void main(String[] args)throws Exception{
        Class c = Class.forName("java_反射機制獲取成員變量.test");
        System.out.println("*****不同類下獲取私有字段並賦值******");
        Field fi = c.getDeclaredField("phone");
        System.out.println(fi);
        test t2 = (test)c.newInstance();
        fi.setAccessible(true);//暴力反射,解除私有限定
        fi.set(t2,"**");
        System.out.println(t2.getPhone());
        }
}
運行結果:
*****獲取共有字段*****
public java.lang.String java_反射機制獲取成員變量.test.name
*****單個獲取共有字段並賦值*******
public java.lang.String java_反射機制獲取成員變量.test.name
***

這裏同理也是使用到了 newInstance()去創建一個新的實例,然後對於獲取到的變量進行賦值操作,對於異類中,對於私有變量的調用,也是使用到了setAccessible()進行暴力訪問。
接下來我個人覺得較爲有用的就是關於反射機制越過泛型直接看例子

package java_反射機制越過泛型;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

public class test {
    public static void main(String[] args)throws Exception{
        ArrayList<String>  l = new ArrayList<>();
        l.add("a");
        l.add("b");
        Class c= l.getClass();//因爲已經創建對象了。
        Method m = c.getMethod("add",Object.class);
        m.invoke(l,10);
        for(Object i:l){
            System.out.println(i);
        }
    }
}
運行結果:
a
b
10

程序只要就是起先定義一個String類型的集合,但此時我想要往集合內存Integer類型的變量,這裏我就將通過反射機制獲取ArrayList下的add()方法,然後進行賦值,之後再對集合進行遍歷。這裏我們使用到getClass()方法去獲取Class類的對象,因爲之前已經創建好了ArrayList的一個對象。
好了到這爲止對於java反射機制的相關使用簡單的整理了一下,唯一還有就是通過反射機制獲取main下的方法這裏就通過一段代碼展示,與上面的原理相同

package java_反射機制反射mian的方法;

import java.lang.reflect.Method;

public class test {
    public static void main(String[] args){
        System.out.println("這裏是main函數的方法");
        for(int i=0;i<args.length;i++){
            System.out.println(args[i]);
        }
    }
}
class test1{
    public static void main(String[] args)throws Exception{
        Class c = Class.forName("java_反射機制反射mian的方法.test");
        Method m = c.getMethod("main", String[].class);
        m.invoke(null,(Object)new String[]{"a,b,c"});
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章