Java反射

反射技術java

  • 其實就是動態加載一個指定的類,並獲取該類中的全部的內容。c++

  • 並且將字節碼文件封裝成對象,並將字節碼文件中的內容都封裝成對象,它容許程序在運行時(注意不是編譯的時候)來進行自我檢查而且對內部的成員進行操做sql

  • 簡單說:反射技術能夠對一個類進行解剖。數組

反射的好處:大大的加強了程序的擴展性。app

反射的基本步驟框架

  1. 得到Class對象,就是獲取到指定的名稱的字節碼文件對象。函數

  2. 實例化對象,得到類的屬性、方法或構造函數。this

  3. 訪問屬性、調用方法、調用構造函數建立對象。編碼

類類型  class Class   spa

用於描述程序中的各個類屬於同一類事物的Java類,它封裝了類的不少信息。

查看JDK中的源碼:

public final
    class Class<T> implements java.io.Serializable,
                              java.lang.reflect.GenericDeclaration,
                              java.lang.reflect.Type,
                              java.lang.reflect.AnnotatedElement {
    private static final int ANNOTATION= 0x00002000;
    private static final int ENUM      = 0x00004000;
    private static final int SYNTHETIC = 0x00001000;

    private static native void registerNatives();
    static {
        registerNatives();
    }

    /*
     * Constructor. Only the Java Virtual Machine creates Class
     * objects.
     */
    private Class() {}

發現:Class類有構造器,而且它的構造方法是private的(多是爲了禁止開發者去本身建立Class類的實例)。

看到註釋咱們知道,這個類是JVM來建立的。若是咱們拿到一個類的類型信息,就能夠利用反射獲取其各類成員以及方法了。

那麼咱們怎麼拿到一個類的信息呢?【獲取Class對象能夠有3種方式】

若是沒有對象實例的時候,主要有兩種辦法能夠獲取類類型:

Class cls1 = Show.class;//每個數據類型(基本數據類型和引用數據類型)都有一個靜態的屬性class。弊端:必需要先明確該類。
Class cls2 = Class.forName("Show");//【推薦這種方法】這種方式的擴展性最強,只要將類名的字符串傳入便可。若是類是在某個包中,要帶上包名。
	public static void main(String[] args) throws Exception {
		Class<Show> cls1 = Show.class;
		Object obj1 = cls1.newInstance();
		System.out.println(obj1);

		Class<?> cls2 = Class.forName("Show");
		Object obj2 = cls2.newInstance();
		System.out.println(obj2);
	}

這樣就建立了一個對象,缺點是:

第一,咱們只能利用默認構造函數,由於Class的newInstance是不接受參數的;

第二,若是類的構造函數是private的,好比Class,咱們仍舊不能實例化其對象。

若是有對象實例的話,除了上面的兩種方法來獲取類的信息外,還有第三種方法:對象.getClass()。弊端:必需要建立該類對象,才能夠調用getClass方法。

反射就是把Java類中的各類成分映射成相應的Java類:

例如,一個Java類用一個Class類的對象來表示,一個類中的組成部分:成員變量,方法,構造方法,包等等信息也用一個個的Java類來表示【就像汽車是一個類,汽車中的發動機,變速箱也是一個個的類】。表示Java類的Class類中提供了一系列的方法來獲取其中的變量(Field),方法(Method),構造方法(Contructor),修飾符(Modifiers),包(Package)等信息。

反射的用法

1.須要得到java類的各個組成部分,首先須要得到類的Class對象,得到Class對象的三種方式:

  • Class.forName(classname) 用於作類加載
  • obj.getClass() 用於得到對象的類型
  • 類名.class     用於得到指定的類型,傳參用

2.反射類的成員方法:

  • public Method getMethod(String name,Class<?>... parameterTypes)//返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法。
  • public Method[] getMethods()//返回一個包含某些 Method 對象的數組,這些對象反映此 Class 對象所表示的類或接口的公共 member 方法。

3.反射類的構造函數:

  • public Constructor<?>[] getConstructors()      返回類中全部的public構造器集合,默認構造器的下標爲0
  • public Constructor<T> getConstructor(Class<?>... parameterTypes)   返回指定public構造器,參數爲構造器參數類型集合
  • public Constructor<?>[] getDeclaredConstructors()  返回類中全部的構造器,包括私有
  • public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回任意指定的構造器

發現帶上Declared的都是得到全部的構造方法,包括私有,這下咱們就能夠調用本來不容許調用的私有構造器了^_^

eg:		Class cls1 = Show.class;

		// 獲取全部的構造方法集合
		Constructor[] con1 = cls1.getDeclaredConstructors();
		con1[1].setAccessible(true);// 設置可訪問的權限
		Object obj1 = con1[1].newInstance(new Object[] { "adanac" });
		System.out.println(obj1);

		// 指定參數列表獲取特定的方法
		Constructor con = cls1.getDeclaredConstructor(new Class[] { String.class });
		con.setAccessible(true);
		Object obj2 = con.newInstance(new Object[] { "lfz" });
		System.out.println(obj2);

4.獲取類的成員變量:

  • public Field getDeclaredField(String name)  獲取任意指定名字的成員
  • public Field[] getDeclaredFields()             獲取全部的成員變量
  • public Field getField(String name)           獲取任意public成員變量
  • public Field[] getFields()                          獲取全部的public成員變量
		Class cls1 = Show.class;

		// 獲取全部的構造方法集合
		Constructor[] con1 = cls1.getDeclaredConstructors();
		con1[1].setAccessible(true);// 設置可訪問的權限
		Object obj1 = con1[1].newInstance(new Object[] { "adanac" });

		Field nameField = cls1.getDeclaredField("name");
		nameField.setAccessible(true);
		System.out.println(nameField.get(obj1));// 打印結果:adanac

如今私有變量也能夠訪問到了哈~~

獲取了字節碼文件對象後,最終都須要建立指定類的對象,建立對象的兩種方式(其實就是對象在進行實例化時的初始化方式):

  1. 調用空參數的構造函數:使用了Class類中的newInstance()方法。
  2. 調用帶參數的構造函數:先要獲取指定參數列表的構造函數對象,而後經過該構造函數的對象的newInstance(實際參數) 進行對象的初始化。

注意:第二種方式必需要先明確具體的構造函數的參數類型,不便於擴展。因此通常狀況下,被反射的類,內部一般都會提供一個公有的空參數的構造函數。

Object obj = clazz.newInstance();//該實例化對象的方法調用就是指定類中的空參數構造函數,給建立對象進行初始化。

當指定類中沒有空參數構造函數時,該如何建立該類對象呢?

Class<?> clazz = Class.forName("heimablog.Person");

// 既然類中沒有空參數的構造函數,那麼只有獲取指定參數的構造函數,用該函數來進行實例化。
// 獲取一個帶參數的構造器。
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
// 想要對對象進行初始化,使用構造器的方法newInstance();
Object obj = constructor.newInstance("zhagnsan", 30);
		
// 獲取全部構造器。
Constructor[] constructors = clazz.getConstructors();// 只包含公共的
constructors = clazz.getDeclaredConstructors();// 包含私有的
for (Constructor<?> con : constructors) {
	System.out.println(con);
}        

【案例】

經過class取得一個類的所有框架

package heimablog;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

class hello {
    public static void main(String[] args) {
        Class<?> demo = null;
        try {
            demo = Class.forName("heimablog.Person");
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("===============本類屬性========================");
        // 取得本類的所有屬性
        Field[] field = demo.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            // 權限修飾符
            int mo = field[i].getModifiers();//返回此類或接口以整數編碼的 Java 語言修飾符。
            String priv = Modifier.toString(mo);
            // 屬性類型
            Class<?> type = field[i].getType();
            System.out.println(priv + " " + type.getName() + " "
                    + field[i].getName() + ";");
        }
        System.out.println("===============實現的接口或者父類的屬性========================");
        // 取得實現的接口或者父類的屬性
        Field[] filed1 = demo.getFields();
        for (int j = 0; j < filed1.length; j++) {
            // 權限修飾符
            int mo = filed1[j].getModifiers();
            String priv = Modifier.toString(mo);
            // 屬性類型
            Class<?> type = filed1[j].getType();
            System.out.println(priv + " " + type.getName() + " "
                    + filed1[j].getName() + ";");
        }
    }
}

經過反射操做屬性

package heimablog;

import java.lang.reflect.Field;

class hello {
	public static void main(String[] args) throws Exception {
		Class<?> cls1 = Class.forName("heimablog.Person");
		Object object = cls1.newInstance();
		
		Field field = cls1.getDeclaredField("name");
		field.setAccessible(true);
		field.set(object, "男");
		System.out.println(field.get(object));//打印結果:男
	}
}

經過反射取得並修改數組的信息:

package heimablog;

import java.lang.reflect.Array;

class hello {
	public static void main(String[] args) throws Exception {
		int[] temp={1,2,3,4,5};
        Class<?>demo=temp.getClass().getComponentType();
        System.out.println("數組類型: "+demo.getName());
        System.out.println("數組長度  "+Array.getLength(temp));
        System.out.println("數組的第一個元素: "+Array.get(temp, 0));
        Array.set(temp, 0, 100);
        System.out.println("修改以後數組第一個元素爲: "+Array.get(temp, 0));
	}
}
/**
 * 數組類型: int
 *數組長度  5
 *數組的第一個元素: 1
 *修改以後數組第一個元素爲: 100
 */

經過反射修改數組大小

package heimablog;

import java.lang.reflect.Array;

class hello{
    public static void main(String[] args) {
        int[] temp={1,2,3,4,5,6,7,8,9};
        int[] newTemp=(int[])arrayInc(temp,12);
        print(newTemp);
        System.out.println("^^^^^");
        String[] atr={"a","b","c"};
        String[] str1=(String[])arrayInc(atr,5);
        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;
        }
        System.out.println("數組長度爲: "+Array.getLength(obj));
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i)+" ");
        }
    }
}
/**
 *數組長度爲: 10
 *1 2 3 4 5 6 7 8 9 0 ^^^^^
 *數組長度爲: 5
 *a b c null null 
 */

得到類加載器:

package heimablog;

class test{
     
}
class hello{
    public static void main(String[] args) {
        test t=new test();
        System.out.println("類加載器  "+t.getClass().getClassLoader().getClass().getName());
    }
}
//類加載器  sun.misc.Launcher$AppClassLoader

反射小例子:

package heimablog;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.Timestamp;
import java.util.Calendar;

public class ClassTest {

	public static void main(String[] args) {
		Calendar birthday = Calendar.getInstance();
		birthday.set(1966, 9, 11, 0, 0, 0);
		Student s1 = new Student("007", "Jack Brawer", true, new Timestamp(
				birthday.getTimeInMillis()), 1.80);

		ClassInfo classInfo = new ClassInfo();
		System.out.println(classInfo.getClassInfo(s1));
	}

}

// 該類能夠獲取任何類的元數據信息
class ClassInfo {
	public String getClassInfo(Object obj) {
		StringBuffer result = new StringBuffer();
		// 獲得參數類變量的Class引用變量
		Class cls = obj.getClass();

		// 獲得參數類變量的屬性信息
		Field[] fields = cls.getDeclaredFields();

		// 獲得參數類變量的完整類名(含有包的名稱)
		String fullName = cls.getName();

		// 獲得去除包名稱的類名
		String className = fullName.substring(fullName.lastIndexOf('.') + 1);

		// 若是有包的定義,能夠獲得包的名稱
		int packagePosition = fullName.lastIndexOf('.');
		String packageName = null;
		if (packagePosition < 0)
			packageName = "";
		else
			packageName = fullName.substring(0, fullName.lastIndexOf('.'));

		// 輸出包名和類名
		result.append("包的名稱爲:" + packageName + "\r\n");
		result.append("類的名稱爲:" + className + "\r\n");

		// 輸出類中全部的屬性信息
		for (Field field : fields) {
			// 容許訪問私有成員
			field.setAccessible(true);

			try {
				// 輸出私有屬性信息
				if (field.getModifiers() == Modifier.PRIVATE)
					result.append("私有屬性" + field.getName() + ":值爲"
							+ field.get(obj) + "\n");

				// 輸出受保護屬性信息
				if (field.getModifiers() == Modifier.PROTECTED)
					result.append("受保護屬性" + field.getName() + ":值爲"
							+ field.get(obj) + "\n");
			} catch (Exception e) {
				System.out.println(e.getMessage());
			}
		}
		return result.toString();
	}
}

// 學生類
class Student {
	// 學號
	private String number = null;

	// 姓名
	private String name = null;

	// 性別
	private boolean sex = false;

	// 生日
	private Timestamp birthday = null;

	// 身高
	private double height = 0;

	// 默認構造函數
	public Student() {
	}

	// 該構造函數能夠對學生的屬性進行初始化
	public Student(String number, String name, boolean sex, Timestamp birthday,
			double height) {
		setNumber(number);
		setName(name);
		setSex(sex);
		setBirthday(birthday);
		setHeight(height);
	}

	// 學號的讀取函數
	public String getNumber() {
		return number;
	}

	// 學號的設置函數
	public void setNumber(String number) {
		this.number = number;
	}

	// 姓名的讀取函數
	public String getName() {
		return name;
	}

	// 姓名的設置函數
	public void setName(String name) {
		this.name = name;
	}

	// 性別的讀取函數
	public boolean isSex() {
		return sex;
	}

	// 性別的設置函數
	public void setSex(boolean sex) {
		this.sex = sex;
	}

	// 生日的讀取函數
	public Timestamp getBirthday() {
		return birthday;
	}

	// 生日的設置函數
	public void setBirthday(Timestamp birthday) {
		this.birthday = birthday;
	}

	// 身高的讀取函數
	public double getHeight() {
		return height;
	}

	// 身高的設置函數
	public void setHeight(double height) {
		this.height = height;
	}

	// 格式化字符信息輸出
	public String toString() {
		return "學號:" + number + "/n姓名:" + name + "/n是否爲男生:" + sex + "/n生日:"
				+ birthday + "/n身高:" + height;
	}
}
/*
 包的名稱爲:heimablog
類的名稱爲:Student
私有屬性number:值爲007
私有屬性name:值爲Jack Brawer
私有屬性sex:值爲true
私有屬性birthday:值爲1966-10-11 00:00:00.267
私有屬性height:值爲1.8
*/

在java中有三種類類加載器:

  1. Bootstrap ClassLoader 此加載器採用c++編寫,通常開發中不多見。

  2. Extension ClassLoader 用來進行擴展類的加載,通常對應的是jre\lib\ext目錄中的類

  3. AppClassLoader 加載classpath指定的類,是最經常使用的加載器。同時也是java中默認的加載器。

相關文章
相關標籤/搜索