反射
正常情況下,我們必須知道一個類的完整路徑後纔可以實例化對象,但是在Java也可以通過一個對象來找到其所在類的信息,這其實就是Class的功能。
可以看到此時的所有操作都是反着來,這就是反射。
package reflect;
class X{
}
public class GetClassDemo01 {
public static void main(String[] args) {
X x=new X();
System.out.println(x.getClass().getName());
}
}
Class
Class本身表示一個類的本身,通過Class可以完整的得到一個類的完整結構,包括此類中的方法定義、屬性定義等。
此類在jdk中文檔中沒有發現任何構造方法,所以此類的構造方法是被私有化了。
實例化Class 類對象的三種方式
♥ 第一種:通過forName()方法
♥ 第二種: 通過類class
♥ 第三種:通過對象.getClass()
package reflect;
class X{
}
public class GetClassDemo01 {
public static void main(String[] args) {
Class <?> c1=null;
Class <?> c2=null;
Class <?> c3=null;
try {
//通過第一種方式實例化Class對象,這種方法也是最常用的一種形式
c1=Class.forName("reflect.X");
//通過Object類中的放過實例化Class對象
c2=new X().getClass();
//通過類.cass實例化Class
c3=X.class;
System.out.println("類名稱:" + c1.getName() );
System.out.println("類名稱:" + c2.getName() );
System.out.println("類名稱:" + c3.getName() );
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
一旦可以實例化Class對象之後就可以進行反射的進一步操作了。
Class的使用
Class主要是反射的源頭,不光可以取得對象所在類的信息,也可以直接通過Class類的方法進行對象的實例化對象操作,使用關鍵字new爲對象實例化,如果現在已經實例化好Class對象,則可以通過Class類提供的newInstance()方法實例化對象。
package reflect;
class Person{
private String name;
private int age;
@Override
public String toString(){//爲了操作方便,複寫toString放過
return "姓名:"+this.name+",年齡"+this.age;
}
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 class InstanceDemo01 {
public static void main(String[] args) {
Class<?> c=null;
try {
c=Class.forName("reflect.Person");
Person per=null;
per=(Person) c.newInstance();//實例化對象
per.setName("月芽之上");
per.setAge(24);
System.out.println(per.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
通過以上的代碼可以發現,即使不適用 new關鍵字對象也可以進行實例化操作,這也就是反射的作用。但是我們也發現在使用以上操作的時候有一點需要注意,就是所操作的類中必須有午餐構造函數,否則無法進行實例化!
爲了解決這一問題,我們需要明確的指明要調用的方法,並傳遞參數,(實際開發中一般會有午無參構造)。步驟如下:
❤ 1、通過Class類中的getConstructors()取得本類中的全部構造方法。
❤ 2、向構造方法中傳遞一個對象數組進去,裏面包含了構造方法中所需的各個參數
❤ 3、之後通過Constructor實例化對象
在Construction類中存在一個方法:
public T newInstance(Object... initargs)
throws InstantiationException,
傳遞參數初始化,以進行對象的實例化操作。
Constru中常用方法:
調用帶參數的構造方法示例
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
class Person{
private String name;
private int age;
public Person(String name,int age){//定義一個有兩個參數的構造函數,此時當前類不再存在無參構造
this.name=name;
this.age=age;
}
@Override
public String toString(){//爲了操作方便,複寫toString放過
return "姓名:"+this.name+",年齡"+this.age;
}
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 class InstanceDemo01 {
public static void main(String[] args) {
Class<?> c=null;
try {
c=Class.forName("reflect.Person");
Constructor<?> cons[]=null;
cons=c.getConstructors();
Person per=null;
per=(Person) cons[0].newInstance("月芽之上",24);//實例化對象
System.out.println(per.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
通過反射獲取類的結構
在實際開發中,上面介紹的程序就是反射用的最多的情況,當然反射機制所提供的功能遠不止這些,還可以通過反射獲取一個類的完整結構,那麼這是就要使用到java.lang.reflect保重的一些幾個類。
❤ 1、Constructor :表示類中的構造方法
❤ 2、Filed :表示類中的屬性
❤ 3、Method :表示類中的方法
這三個類都是AccessibleObject的子類
package reflect.construction;
interface China{
String NATIONAL="China";//定義全局變量
String AUTHOR="月芽之上";//定義全局變量
void sayChina();
String sayHello(String name,int age);
}
public class Person implements China {
private String name;
private int age;
Person(){//無參構造
}
Person(String name){//設置name
this.name=name;
}
Person(String name,int age){//設置name
this.name=name;
this.age=age;
}
@Override
public void sayChina() {
System.out.println("作者:"+AUTHOR+",國籍"+NATIONAL);
}
@Override
public String sayHello(String name, int age) {
return name+",你好,我今年"+ age+"歲了!";
}
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類中的getInterfaces()方法。此方法定義如下:
public Class<?>[] getInterfaces()
方法的返回值是一個class類的對象數組,之後就可以直接利用Class類中的getName()方法輸出即可、
public class GetInterfaceDemo {
public static void main(String[] args) {
Class<?> c1=null;
try {
c1=Class.forName("reflect.construction.Person");
Class<?> c[]=c1.getInterfaces();
for(int i=0;i<c.length;i++){
System.out.println(c[i].getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
取得該類所繼承的父類
一個類可與有多個實現,但是卻只能有一個直接父類,所以想要取得一個類的父類,可以使用Class類中的getSuperclass()方法。
public Class<? super T> getSuperclass()
此方法返回的是Class實例,和之前取得接口一樣,可以通過getName(0方法取得名次
public class GetSuperClassDemo {
public static void main(String[] args) {
Class<?> c1 = null;
try {
c1 = Class.forName("reflect.construction.Person");
Class<?> c = c1.getSuperclass();
System.out.println(c.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
取得全部構造函數
import java.lang.reflect.Constructor;
public class GetConstructorDemo {
public static void main(String[] args) {
Class<?> c1=null;
try {
c1=Class.forName("reflect.construction.Person");//實例化Class對象
Constructor<?>[] cons=c1.getConstructors();//得到全部構造方法
for(int i=0;i<cons.length;i++){
System.out.println(cons[i]);//打印所以構造方法
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
取得類中的方法
要取出一個類中的全部方法,可以使用Class類中的getDeclaredMethods()方法,此方法返回的是一個Method類的對象數組。而想取得方法的具體信息例如:方法參數、拋出的異常等等,就必須依靠Mehod類。
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 取出本類的全部方法
throws NoSuchMethodException,SecurityException
public Method[] getMethods() 取得全部方法
throws SecurityException
public class GetMethodDemo {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> c1=null;
c1=Class.forName("reflect.construction.Person");
Method m[]=c1.getMethods();
for(int i=0;i<m.length;i++){
System.out.println(m[i]);
}
}
}
取出類中的屬性:
依賴的方法:
public Field[] getFields() throws SecurityException
public Field[] getDeclaredFields() throws SecurityException
public class GetFiledDemo {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> c1=null;
c1=Class.forName("reflect.construction.Person");
Field f[]=c1.getFields();
for(int i=0;i<f.length;i++)
{
System.out.println(f[i]);
}
}
}
通過反射調用類中的方法
在正常情況下一個類對象產生後就可以直接調用類中的方法了.如果要調用的話肯定必須清楚的指定調用的方法名是什麼,之後通過Class 中的getMethod()方法。
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException
該方法得到的Method對象,之後通過此Method對象來執行方法,但是在調用的時候因爲會涉及到參數的問題,所以通過getMehod()取得的時候需要要設置好對應的參數類型。
比如調用Person中sayChina()方法,因爲該方法中沒有任何參數,所以只需要調用Method 中invoke()方法。
public Object invoke(Object obj,
Object... args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
執行的時候需要還需要傳遞參數進去, 而且需要實例化對象。
public class InvokeSayChinaDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
Class<?> c1=null;
c1=Class.forName("reflect.construction.Person");//實例化Class對象
Method met=c1.getMethod("sayChina");//找到sayChina()方法
met.invoke(c1.newInstance());//調用方法
}
}
調用有參數的方法
public class InvokeSayChinaDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
Class<?> c1=null;
c1=Class.forName("reflect.construction.Person");//實例化Class對象
Method met=c1.getMethod("sayHello",String.class,int.class);//找到sayHello()方法
String returnValue=(String) met.invoke(c1.newInstance(),"李佔祥",24);//調用方法
System.out.println(returnValue);
}