Java反射機制

什麼是Java的反射機制?

Java 反射機制是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲 Java 語言的反射機制。html

簡單來講,反射就是能夠在程序運行的時候動態裝載類,查看類的信息,生成對象,或操做生成的對象。java

Java反射機制相關API

Class類介紹

Class 類的實例表示正在運行的 Java 應用程序中的類和接口。JVM 中有 N 多的實例,每一個類的實例都有 Class 對象。(包括基本數據類型)api

Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及經過調用類加載器中的 defineClass 方法自動構造的。也就是這不須要咱們本身去處理建立,JVM 已經幫咱們建立好了。數組

Java 類的加載過程:ide

在 Java 中,類裝載器把一個類裝入 Java 虛擬機中,要通過三個步驟來完成:裝載、連接和初始化,其中連接又能夠分紅校驗、準備和解析三步:函數

  • 裝載:查找和導入類或接口的二進制數據;
  • 連接:執行下面的校驗、準備和解析步驟,其中解析步驟是能夠選擇的;
  • 校驗:檢查導入類或接口的二進制數據的正確性;
  • 準備:給類的靜態變量分配並初始化存儲空間;
  • 解析:將符號引用轉成直接引用;
  • 初始化:激活類的靜態變量的初始化 Java 代碼和靜態 Java 代碼塊。

若是知道一個實例,那麼能夠經過實例的getClass()方法得到運行實例的 Class(該類型的字節碼文件對象),若是你知道一個類型,那麼你也可使用.class的方法得到運行實例的 Class。測試

部分相關api以下所示this

//返回與給定字符串名稱的類或接口相關聯的類對象。
public static Class <?> forName(String className)throws ClassNotFoundException

//返回類表示此所表示的實體(類,接口,基本類型或void)的超類類 。
public Class<? super T> getSuperclass()

//肯定由該對象表示的類或接口實現的接口。 
public Class< ? >[] getInterfaces()

//建立此 Class 對象所表示的類的一個新實例
public T newInstance() throws InstantiationException, IllegalAccessException

//返回表示此類公共構造方法的 Constructor 對象數組
public Constructor< ? >[] getConstructors() throws SecurityException

//返回 Constructor 對象的一個數組
public Constructor<?>[] getDeclaredConstructors() throws SecurityException

//返回表示公共字段的 Field 對象的數組
public Field[] getFields()  throws SecurityException

//返回表示此類全部已聲明字段的 Field 對象的數組
public Field[] getDeclaredFields() throws SecurityException

//表示此類中公共方法的 Method 對象的數組
public Method[] getMethods() throws SecurityException

//表示此類全部聲明方法的 Method 對象的數組
public Method[] getDeclaredMethods()  throws SecurityException

//返回該類的類加載器。
public ClassLoader getClassLoader()

//返回:使用參數 args 在 obj 上指派該對象所表示方法的結果
public Object invoke(Object obj, Object... args)  throws IllegalAccessException, IllegalArgumentException,  InvocationTargetException

//查找具備給定名稱的資源。
public InputStream getResourceAsStream(String name)

//返回一個指定接口的代理類實例,該接口能夠將方法調用指派到指定的調用處理程序。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

具體相關方法及其使用方法能夠查看API文檔spa

Java 反射機制舉例

如下例子基於Person類.net

package DateTest;

public class Person implements Comparable{
	public String name;
	private int age;
	public int id;
	protected String phone;
	public Person() {
		System.out.println("默認的無參構造方法執行了");
	}
	
	public Person(String name) {
		System.out.println("姓名:"+name);
	}
	
	public Person(String name,int age) {
		System.out.println("姓名:"+name+"年齡:"+age);
	}
	
	//受保護的構造方法
	protected Person(boolean b) {
		System.out.println("這是一個受保護的構造方法:b="+b);
	}
	
	//私有構造方法
	private Person(int age) {
		System.out.println("這是一個私有構造方法,年齡="+age);
	}

	@Override
	public int compareTo(Object arg0) {
		// TODO Auto-generated method stub
		return 0;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
}

經過一個對象獲取某個類的完整包名和類名

經過實例的 getClass() 方法得到運行實例的字節碼文件對象,而後經過 getName() 方法得到類的完整包名和類名。

public void test1() {
		Person person = new Person();
		System.out.println(person.getClass().getName());
}
//輸出結果:
默認的無參構造方法執行了
DateTest.Person

獲取 Class 對象

  • 方式1:經過 Class 類的靜態方法獲取 Class 類對象
  • 方式2:由於全部類都繼承 Object 類。於是可經過調用 Object 類中的 getClass 方法來獲取
  • 方式3:任何數據類型(包括基本數據類型)都有一個「靜態」的 class 屬性
public void test2() throws ClassNotFoundException {
		Class<?> class1 = null;
		Class<?> class2 = null;
		Class<?> class3 = null;
		class1 = Class.forName("DateTest.Person");
		class2 = new Person().getClass();
		class3 = Person.class;
		System.out.println("類名稱1:"+class1.getName());
		System.out.println("類名稱2:"+class2.getName());
		System.out.println("類名稱3:"+class3.getName());
}
//輸出結果
默認的無參構造方法執行了
類名稱1:DateTest.Person
類名稱2:DateTest.Person
類名稱3:DateTest.Person

獲取一個對象的父類和實現的接口

爲了測試getInterfaces()方法,此處將Person類繼承Comparable接口

public void test3() throws ClassNotFoundException {
		Class<?> clazz = Class.forName("DateTest.Person");
		//獲取父類
		Class<?> parentClass = clazz.getSuperclass();
		System.out.println("父類爲:"+parentClass.getName());
		//獲取全部接口
		Class<?> interf[] = clazz.getInterfaces();
		System.out.println("實現的接口有:");
		for(int i = 0; i < interf.length; i++) {
			System.out.println((i+1)+":"+interf[i].getName());
		}
}
//輸出結果
父類爲:java.lang.Object
實現的接口有:
1:java.lang.Comparable

獲取某個類的所有構造函數並調用私有構造方法

獲取 Student 類的所有構造函數,並使用 Class 對象的 newInstance() 方法來建立 Class 對象對應類 Person 的實例來調用私有構造方法。

public void test4() throws Exception {
		//1.加載Class對象
		Class clazz = Class.forName("DateTest.Person");
		//2.獲取全部公有構造方法
		System.out.println("全部公有構造方法");
		Constructor[] conArray = clazz.getConstructors();
		for(Constructor c : conArray) {
			System.out.println(c);
		}
		
		System.out.println("----------------------------");
		System.out.println("全部構造方法");
		conArray = clazz.getDeclaredConstructors();
		for(Constructor c : conArray) {
			System.out.println(c);
		}
		
		System.out.println("----------------------------");
		System.out.println("公有、無參的構造方法");
		Constructor con = clazz.getConstructor(null);
        System.out.println("con = " + con);
        //調用構造方法  
        Object obj = con.newInstance();
        System.out.println("obj = " + obj);
        
        System.out.println("----------------------------");
		System.out.println("獲取私有的構造方法,並調用");
		con = clazz.getDeclaredConstructor(int.class);
        System.out.println(con);
        //調用構造方法  
        con.setAccessible(true);
        obj = con.newInstance(20);
	}
//輸出結果
全部公有構造方法
public DateTest.Person(java.lang.String,int)
public DateTest.Person(java.lang.String)
public DateTest.Person()
----------------------------
全部構造方法
private DateTest.Person(int)
protected DateTest.Person(boolean)
public DateTest.Person(java.lang.String,int)
public DateTest.Person(java.lang.String)
public DateTest.Person()
----------------------------
公有、無參的構造方法
con = public DateTest.Person()
默認的無參構造方法執行了
obj = DateTest.Person@7a4f0f29
----------------------------
獲取私有的構造方法,並調用
private DateTest.Person(int)
這是一個私有構造方法,年齡=20

獲取某個類的所有屬性

獲取 Person類的所有屬性並調用:

public void test5() throws Exception {
		//1.獲取Class對象
		Class pclass = getClass().forName("DateTest.Person");
		//2.獲取字段
		System.out.println("獲取全部公有字段");
		Field[] fieldArray = pclass.getFields();
		for(Field f : fieldArray) {
			System.out.println(f);
		}
		System.out.println("----------------------------");
		System.out.println("獲取全部字段");
		fieldArray = pclass.getDeclaredFields();
		for(Field f : fieldArray) {
			System.out.println(f);
		}
		System.out.println("----------------------------");
		Field f = pclass.getField("name");
		System.out.println(f);
		//獲取一個對象
		Object obj = pclass.getConstructor().newInstance();
		f.set(obj,"張三");
		//驗證
		Person p = (Person)obj;
		System.out.println("驗證的姓名:"+p.getName());
		System.out.println("----------------------------");
		f = pclass.getDeclaredField("age");
		System.out.println(f);
		f.setAccessible(true);
		f.set(obj, 21);
		System.out.println("驗證年齡:"+p);
	}
//輸出結果
獲取全部公有字段
public java.lang.String DateTest.Person.name
public int DateTest.Person.id
----------------------------
獲取全部字段
public java.lang.String DateTest.Person.name
private int DateTest.Person.age
public int DateTest.Person.id
protected java.lang.String DateTest.Person.phone
----------------------------
public java.lang.String DateTest.Person.name
默認的無參構造方法執行了
驗證的姓名:張三
----------------------------
private int DateTest.Person.age
驗證年齡:Person [name=張三, age=21]

獲取某個類的所有方法

public void test6() throws Exception {
        //1.獲取Class對象
        Class pClass = Class.forName("DateTest.Person");
        //2.獲取全部公有方法
        System.out.println("獲取全部的公有方法*");
        pClass.getMethods();
        Method[] methodArray = pClass.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("----------------------------");
        System.out.println("獲取全部的方法");
        methodArray = pClass.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("獲取公有的test1()方法");
        Method m = pClass.getMethod("test1", String.class);
        System.out.println(m);
        //實例化一個Student對象
        Object obj = pClass.getConstructor().newInstance();
        m.invoke(obj, "lisi");

        System.out.println("獲取私有的test4()方法*");
        m = pClass.getDeclaredMethod("test4", int.class);
        System.out.println(m);
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj, 20);//須要兩個參數,一個是要調用的對象(獲取有反射),一個是實參
        System.out.println("返回值:" + result);
}
//輸出結果
獲取全部的公有方法*
public java.lang.String DateTest.Person.toString()
public int DateTest.Person.compareTo(java.lang.Object)
public java.lang.String DateTest.Person.getName()
public int DateTest.Person.getId()
public void DateTest.Person.setName(java.lang.String)
public void DateTest.Person.test1(java.lang.String)
public int DateTest.Person.getAge()
public java.lang.String DateTest.Person.getPhone()
public void DateTest.Person.setAge(int)
public void DateTest.Person.setPhone(java.lang.String)
public void DateTest.Person.setId(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 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()
----------------------------
獲取全部的方法
public java.lang.String DateTest.Person.toString()
public int DateTest.Person.compareTo(java.lang.Object)
public java.lang.String DateTest.Person.getName()
public int DateTest.Person.getId()
public void DateTest.Person.setName(java.lang.String)
public void DateTest.Person.test1(java.lang.String)
protected void DateTest.Person.test2()
private java.lang.String DateTest.Person.test4(int)
void DateTest.Person.test3()
public int DateTest.Person.getAge()
public java.lang.String DateTest.Person.getPhone()
public void DateTest.Person.setAge(int)
public void DateTest.Person.setPhone(java.lang.String)
public void DateTest.Person.setId(int)
獲取公有的test1()方法
public void DateTest.Person.test1(java.lang.String)
默認的無參構造方法執行了
調用了:公有的,String參數的test1(): name = lisi
獲取私有的test4()方法*
private java.lang.String DateTest.Person.test4(int)
調用了,私有的,而且有返回值的,int參數的test4(): age = 20
返回值:abcd

調用某個類的方法

Java 反射獲取 Class 對象並使用invoke()方法調用方法 reflect1() 和 reflect2():

public void test7() throws Exception{
        Class<?> clazz = Class.forName("DateTest.ReflectTest");
        Method method = clazz.getMethod("show1");
        method.invoke(clazz.newInstance());
        method = clazz.getMethod("show2", int.class, String.class);
        method.invoke(clazz.newInstance(), 20, "張三");
    }

    public void show1() {
        System.out.println("調用show1()");
    }
    public void show2(int age, String name) {
        System.out.println("調用show2()");
        System.out.println("age: " + age + "name: " + name);
    }
//輸出結果
調用show1()
調用show2()
age: 20name: 張三

反射機制的動態代理

首先須要定義一個 InvocationHandler 接口的子類,完成代理的具體操做

package DateTest;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler{
	 private Object obj = null;

	 public Object bind(Object obj) {
	     this.obj = obj;
	     return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
	 }

	 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	     Object temp = method.invoke(this.obj, args);
	     return temp;
	 }
}
package DateTest;

public interface Animal {
	public void eat(String name);
}
package DateTest;

public class Dog implements Animal{
	public void eat(String name) {
		System.out.println(name+"eat!");
	}
}

新建類實現接口Animal,使用 MyInvocationHandler.bind() 方法獲取便可實現調用。

public void test8(){
        MyInvocationHandler invo = new MyInvocationHandler();
        Animal sub = (Animal) invo.bind(new Dog());
        sub.eat("小狗");
    }
//輸出結果
小狗eat!

經過反射越過泛型檢查

泛型做用在編譯期,編譯事後泛型擦除(消失掉)。因此是能夠經過反射越過泛型檢查的。

public void test9() throws Exception{
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(111);
        list.add(222);
        Method method = list.getClass().getMethod("add", Object.class);
        method.invoke(list, "越過泛型檢查");
        //遍歷集合  
        for(Object obj : list){  
            System.out.println(obj);  
        }
    }
//輸出結果
111
222
越過泛型檢查

經過反射機制得到數組信息並修改數組的大小和值

經過反射機制分別修改int和String類型的數組的大小並修改int數組的第一個值。

public static void test10() throws Exception{
         int[] temp = { 12,45,65,5,1,32,4,56,12};
         int[] newTemp = (int[]) arrayInc(temp, 15);
         print(newTemp);
         Array.set(newTemp, 0, 100);
         System.out.println("修改以後數組第一個元素爲: " + Array.get(newTemp, 0));
         print(newTemp);
         String[] atr = { "a", "b", "c" };
         String[] str1 = (String[]) arrayInc(atr, 8);
         print(str1);
    }
    // 修改數組大小
    public static Object arrayInc(Object obj, int len) {
        Class<?> arr = obj.getClass().getComponentType();
        Object newArr = Array.newInstance(arr, len);
        int co = Array.getLength(obj);
        System.arraycopy(obj, 0, newArr, 0, co);
        return newArr;
    }
    // 打印
    public static void print(Object obj) {
        Class<?> c = obj.getClass();
        if (!c.isArray()) {
            return;
        }
        Class<?> arr = obj.getClass().getComponentType();
        System.out.println("數組類型: " + arr.getName());
        System.out.println("數組長度爲: " + Array.getLength(obj));
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i) + " ");
        }
        System.out.println();
    }
//輸出結果
數組類型: int
數組長度爲: 15
12 45 65 5 1 32 4 56 12 0 0 0 0 0 0 
修改以後數組第一個元素爲: 100
數組類型: int
數組長度爲: 15
100 45 65 5 1 32 4 56 12 0 0 0 0 0 0 
數組類型: java.lang.String
數組長度爲: 8
a b c null null null null null

將反射機制應用於工廠模式

對於普通的工廠模式當咱們在添加一個子類的時候,就須要對應的修改工廠類。 當咱們添加不少的子類的時候,會很麻煩。

package DateTest;

public interface Animal {
	public abstract void sleep();
}
package DateTest;

public class Dog implements Animal{
	@Override
	public void sleep() {
		System.out.println("dog");
	}
}
package DateTest;

public class Cat implements Animal{
	@Override
	public void sleep() {
		System.out.println("cat");
	}
}
package DateTest;

public class Factory {
    public static Animal getInstance(String ClassName) {
        Animal a = null;
        try {
            a = (Animal) Class.forName(ClassName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return a;
    }
}
public void test11()throws Exception{
        Animal a = Factory.getInstance("DateTest.Dog");
        if (a != null) {
            a.sleep();
        }
    }
//輸出結果
dog

加載配置文件

//配置文件內容爲
//className = DateTest.ReflectTest
public void test13() throws IOException {
    	 Properties pro = new Properties();//獲取配置文件的對象  
         InputStream in=new Person().getClass().getResourceAsStream("/pro.txt");
         pro.load(in);//將流加載到配置文件對象中  
         in.close();  
         System.out.println(pro.getProperty("className"));
    }
//輸出結果
默認的無參構造方法執行了
DateTest.ReflectTest

獲取 ClassLoader 類加載器

public void test12() throws ClassNotFoundException {  
        //一、獲取一個系統的類加載器  
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();  
        System.out.println(classLoader);  

        //二、獲取系統類加載器的父類加載器  
        classLoader = classLoader.getParent();  
        System.out.println(classLoader);  

        //三、獲取擴展類加載器的父類加載器  
        //輸出爲Null,引導類加載器沒法被Java程序直接引用  
        classLoader = classLoader.getParent();  
        System.out.println(classLoader);  

        //四、測試當前類由哪一個類加載器進行加載 ,結果是系統的類加載器  
        classLoader = Class.forName("DateTest.ReflectTest").getClassLoader();  
        System.out.println(classLoader);  

        //五、測試JDK提供的Object類由哪一個類加載器負責加載的  
        classLoader = Class.forName("java.lang.Object").getClassLoader();  
        System.out.println(classLoader);  
    }
//輸出結果
sun.misc.Launcher$AppClassLoader@6bc7c054
sun.misc.Launcher$ExtClassLoader@2ef1e4fa
null
sun.misc.Launcher$AppClassLoader@6bc7c054
null

總結

反射機制的應用雖然有不少優勢,可是反射會額外消耗必定的系統資源,所以,反射操做的效率要比那些非反射操做低得多。另外,反射容許代碼執行通常狀況下不被容許的操做,因此通常能用其它方式實現的就儘可能不去用反射。

參考文章

http://www.javashuo.com/article/p-fwuoucep-dx.html

https://www.cnblogs.com/tech-bird/p/3525336.html

原文出處:https://www.cnblogs.com/kiwenzhang/p/10981169.html

相關文章
相關標籤/搜索