黑馬程式設計師-------Java高階特性--------反射
黑馬程式設計師—–Java高階特性—–反射
一.概述
Java 反射是Java語言的一個很重要的特徵,它使得Java具體了“動態性”。
這個機制允許程式在執行時透過Reflection APIs取得任何一個已知名稱的class的內部資訊,包括其modifiers(諸如public, static 等等)、superclass(例如Object)、實現之interfaces(例如Serializable),也包括fields和methods的所有資訊,並可於執行時改變fields內容或呼叫methods。
Java程式可以載入一個執行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其物件實體、或對其fields設值、或喚起其methods。這種“看透class”的能力(the ability of the program to examine itself)被稱為introspection(內省、內觀、反省)。Reflection(反射)
二.Java 反射的API
反射中常常用的幾個類如下所示:
java.lang 包中:
–Class 類:代表一個類
Java 中,無論生成某個類的多少個物件,這些物件都會對應同一個 Class 物件
Java.lang.reflect 包中:
–Field 類:代表類的成員變數(類的屬性)
–Method 類:代表類的方法,一個方法對應一個 Method 物件
–Constructor 類:代表類的構造方法
–Array 類:提供動態建立陣列,以及訪問陣列的元素的靜態方法
三.使用JAVA反射綜合示例
1. Class物件的獲取及例項化
//1.獲取該類的 Class 物件
//常用方法有三種:
//a)通過 Class 的 forName 的靜態方法獲得(引數:包名 + 類名):
Class<?> classType = Class.forName("java.lang.String");
//b)通過類名 + 點 class 的方法獲得:
Class<?> c = String.class;
//c)通過類的物件的 getClass 的方法獲得:
String str = "test";
Class<?> typeClass = str.getClass();
//2.例項化一個該類的物件
//兩種方法:
//a)通過 Class 物件的 newInstance 的方法建立(該方法只對無參構造方法適用):
Class<?> classType = Class.forName("java.lang.String");
Object obj = = classType.newInstance();
//b)通過獲取 Constructor 構造方法類的物件,然後通過該物件的 newInstance (Object ... initargs) 方法獲得:
Class<?> classType = cus.getClass();
Constructor con= classType.getConstructor(new Class[]{String.class,int.class});
Object object= con.newInstance(new Object[]{cus.getName(),cus.getAge()});
該方法中的若寫成兩個引數寫成 Class[]{} 和 new Object[ ]{} ,就等同於 a) 的方法
2.訪問被private 修飾的方法和屬性
public class Private
{
private String name = "張三";
private String getName()
{
return name;
}
}
把該類中private修飾的 name 值改成“李四”,並且用反射機制呼叫 getName 方法,返回修改後的值。
package com.wangzhuo.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class PrivateTest
{
public static void main(String[] args)throws Exception
{
//獲取Private類的Class物件
Class<?> classType = Class.forName("com.wangzhuo.reflect.Private");
//獲取其構造方法對應的Constructor物件
Constructor con = classType.getDeclaredConstructor(new Class[]{});
//建立Private的物件
Object object =con.newInstance(new Object[]{});
//獲取Private類中name屬性對應的Field物件
Field field = classType.getDeclaredField("name");
//設定避開java訪問控制檢測
field.setAccessible(true);
//獲取修改前的值
Object str = field.get(object);
System.out.println("修改之前name的值:"+(String)str);
//給name屬性賦值
field.set(object, "李四");
//獲取getName方法對應的Method物件
Method getNameMethod = classType.getDeclaredMethod("getName", new Class[]{});
//設定避開java訪問控制檢測
getNameMethod.setAccessible(true);
//呼叫方法,返回值
Object o = getNameMethod.invoke(object, new Object[]{});
System.out.println("修改之後name的值:"+(String)o);
}
}
3.執行時複製物件(重要)
ReflectTester 類進一步演示了Reflection API的基本使用方法。ReflectTester類有一個copy(Object object)方法,這個方法能夠建立一個和引數object 同樣型別的物件,然後把object物件中的所有屬性拷貝到新建的物件中,並將它返回。
public class ReflectTester {
public Object copy(Object object) throws Exception {
// 獲得物件的型別
Class<?> classType = object.getClass();
System.out.println("Class:" + classType.getName());
// 通過預設構造方法建立一個新的物件
Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
// 獲得物件的所有屬性
Field fields[] = classType.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
String fieldName = field.getName();
String firstLetter = fieldName.substring(0, 1).toUpperCase();
// 獲得和屬性對應的getXXX()方法的名字
String getMethodName = "get" + firstLetter + fieldName.substring(1);
// 獲得和屬性對應的setXXX()方法的名字
String setMethodName = "set" + firstLetter + fieldName.substring(1);
// 獲得和屬性對應的getXXX()方法
Method getMethod = classType.getMethod(getMethodName, new Class[]{});
// 獲得和屬性對應的setXXX()方法
Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()});
// 呼叫原物件的getXXX()方法
Object value = getMethod.invoke(object, new Object[]{});
System.out.println(fieldName + ":" + value);
// 呼叫拷貝物件的setXXX()方法
setMethod.invoke(objectCopy, new Object[]{value});
}
return objectCopy;
}
public static void main(String[] args) throws Exception {
Customer customer = new Customer("Tom", 21);
customer.setId(new Long(1));
Customer customerCopy = (Customer) new ReflectTester().copy(customer);
System.out.println("Copy information:" + customerCopy.getId() + " " + customerCopy.getName() + " "
+ customerCopy.getAge());
}
}
class Customer {
private Long id;
private String name;
private int age;
public Customer() {
}
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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;
}
}
輸出結果:
Class:com.langsin.reflection.Customer
id:1
name:Tom
age:21
Copy information:1 Tom 21
示例四.執行時變更field內容
public class RefFiled {
public double x;
public Double y;
public static void main(String args[]) throws NoSuchFieldException, IllegalAccessException {
Class c = RefFiled.class;
Field xf = c.getField("x");
Field yf = c.getField("y");
RefFiled obj = new RefFiled();
System.out.println("變更前x=" + xf.get(obj));
//變更成員x值
xf.set(obj, 1.1);
System.out.println("變更後x=" + xf.get(obj));
System.out.println("變更前y=" + yf.get(obj));
//變更成員y值
yf.set(obj, 2.1);
System.out.println("變更後y=" + yf.get(obj));
}
}
執行結果:
變更前x=0.0
變更後x=1.1
變更前y=null
變更後y=2.1
示例五.用反射機制呼叫物件的方法
public class InvokeTester {
public int add(int param1, int param2) {
return param1 + param2;
}
public String echo(String msg) {
return "echo: " + msg;
}
public static void main(String[] args) throws Exception {
Class<?> classType = InvokeTester.class;
Object invokeTester = classType.newInstance();
// Object invokeTester = classType.getConstructor(new
// Class[]{}).newInstance(new Object[]{});
//獲取InvokeTester類的add()方法
Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});
//呼叫invokeTester物件上的add()方法
Object result = addMethod.invoke(invokeTester, new Object[]{new Integer(100), new Integer(200)});
System.out.println((Integer) result);
//獲取InvokeTester類的echo()方法
Method echoMethod = classType.getMethod("echo", new Class[]{String.class});
//呼叫invokeTester物件的echo()方法
result = echoMethod.invoke(invokeTester, new Object[]{"Hello"});
System.out.println((String) result);
}
}
在例程InvokeTester類的main()方法中,運用反射機制呼叫一個InvokeTester物件的add()和echo()方法
add()方法的兩個引數為int 型別,獲得表示add()方法的Method物件的程式碼如下:
Method addMethod=classType.getMethod(“add”,new Class[]{int.class,int.class});
Method類的invoke(Object obj,Object args[])方法接收的引數必須為物件,如果引數為基本型別資料,必須轉換為相應的包裝型別的物件。invoke()方法的返回值總是物件,如果實際被呼叫的方法的返回型別是基本型別資料,那麼invoke()方法會把它轉換為相應的包裝型別的物件,再將其返回。
在本例中,儘管InvokeTester 類的add()方法的兩個引數以及返回值都是int型別,呼叫add Method 物件的invoke()方法時,只能傳遞Integer 型別的引數,並且invoke()方法的返回型別也是Integer 型別,Integer 類是int 基本型別的包裝類:
Object result=addMethod.invoke(invokeTester,
new Object[]{new Integer(100),new Integer(200)});
System.out.println((Integer)result); //result 為Integer型別
六.動態建立和訪問陣列
ArrayTester1 類的main()方法建立了一個長度為10 的字串陣列,接著把索引位置為5 的元素設為“hello”,然後再讀取索引位置為5 的元素的值。
public class ArrayTester1 {
public static void main(String args[]) throws Exception {
Class<?> classType = Class.forName("java.lang.String");
// 建立一個長度為10的字串陣列
Object array = Array.newInstance(classType, 10);
// 把索引位置為5的元素設為"hello"
Array.set(array, 5, "hello");
// 獲得索引位置為5的元素的值
String s = (String) Array.get(array, 5);
System.out.println(s);
}
}
ArrayTester2 類的main()方法建立了一個 5 x 10 x 15 的整型陣列,並把索引位置為[3][5][10] 的元素的值為設37。
public class ArrayTester2 {
public static void main(String args[]) {
int[] dims = new int[]{5, 10, 15};
//建立一個具有指定的元件型別和維度的新陣列。
Object array = Array.newInstance(Integer.TYPE, dims);
Object arrayObj = Array.get(array, 3);
Class<?> cls = arrayObj.getClass().getComponentType();
System.out.println(cls);
arrayObj = Array.get(arrayObj, 5);
Array.setInt(arrayObj, 10, 37);
int arrayCast[][][] = (int[][][]) array;
System.out.println(arrayCast[3][5][10]);
}
}