在正常情況下,需要先有一個類的完整路徑引入之後才可以按照固定的格式產生執行個體話對象,但是在Java中也允許通過一個執行個體話對象找到一個類的完整資訊。那麼這就是Class類的功能。
實際上類是Java反射的源頭,實際上所謂反射從程式的運行結果來看也很好理解,即可以通過對象的反射求出類的名稱。
執行個體化Class類,獲得位元組碼檔案的方法有三種:
第一種:通過forName()方法;第二種:類.class第三種:對象.getClass()
package toto.learn;
class X1{}
publicclass GetClassDemo02 {
publicstaticvoid main(String[] args) {
Class c1=null;//指定泛型
Class c2=null;//指定泛型
Class c3=null;//指定泛型
try{
c1=Class.forName("toto.learn.X");//最常用的形式,這種方式將位元組碼檔案載入到記憶體中。
}catch(ClassNotFoundException e){
e.printStackTrace();
}
c2 = new X1().getClass();//通過Object類中的方法執行個體
c3 = X1.class;//通過類class執行個體化
System.out.println("類名稱:"+c1.getName());//得到類的名稱
System.out.println("類名稱:"+c2.getName());//得到類的名稱
System.out.println("類名稱:"+c3.getName());//得到類的名稱
}
}
通過以上方法獲得類名稱的方式得到的是包名+類名
如果要想通過Class類本身執行個體化其他類的對象,則可以使用newInstance()方法,但是必須要保證被執行個體化的類中必須存在一個無參夠造方法。
被執行個體化對象的類中必須存在無參構造方法,如果不存在的話,則肯定是無法執行個體化的。
1、 通過Class類中的getConstructors()取得本類中的全部構造方法
2、 向構造方法中傳遞一個對象數組進去,裡麵包含了構造方法中所需的各個參數
3、 之後通過Constructor執行個體化對象。
package org.lxh.demo15.instancedemo;
import java.lang.reflect.Constructor;
publicclass InstanceDemo03 {
publicstaticvoid main(String[] args) {
Class c = null;
try {
c = Class.forName("org.lxh.demo15.instancedemo.Person"); // 聲明Class對象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Person per = null; // 聲明Person對象
Constructor cons[] = null; // 聲明一個表示構造方法的數組
cons = c.getConstructors(); // 通過反射,取得全部構造
try {// 向構造方法中傳遞參數,此方法使用可變參數接收,並執行個體化對象
per = (Person) cons[0].newInstance("李興華", 30);
} catch (Exception e) { // 因為只有一個構造,所以數組下標為0
e.printStackTrace();
}
System.out.println(per); // 輸出對象
}
}
per = (Person)cons[0].newInstance("李興華", 30); //此處是調用並使用構造方法的部分。
在聲明對象數組的時候,必須考慮到構造方法中參數的類型順序,所以第一個參數的類型為Stirng,第二個參數的類型WieInteger(在使用是可以自動拆箱)
Constructorcons[]=null;//執行個體化構造方法的數組
Cons =c.getConstructors();//取得全部構造
//向構造方法中傳遞參數,此方使用可變參數接收,並執行個體化對象
Per = (Person)cons[0].newInstance(“李興華”,30);
設定構造方法的參數內容
publicPerson(String name,int age){//通過構造設定屬性內容
}
反射的應用
可以使用反射取得實現的全部介面
可以使用反射取得一個類所繼承的父類
可以使用反射取得一個類中的全部構造方法
可以使用反射取得一個類中的全部方法
可以使用反射取得一個類中的全部屬性
在實際開發中發,以上的程式就是反射應用最多的地方,當然反射機制所提供的功能遠不如此,還可以通過反射得到一個類中的完整構造,那麼這就要使用到java.lang.reflect包中的一下幾個類。
Constructor:表示類中的構造方法
Field:表示類中的屬性
Method:表示類中的方法
這三個類都是AccessibleObject類中的子類。
要想取得一個類中所實現的全部介面,則必須使用Class類中的getInterfaces()方法。此方法定義如下:
publicClass[] getInterfaces();
此方法返回一個Class類的對象數組,之後就可以直接利用Class類中的getName()方法輸出即可。
通過反射取得實現的全部介面
package org.lxh.demo15;
publicclass GetInterfaceDemo {
publicstaticvoid main(String[] args) {
Class c1 =null;//聲明Class對象
try{
c1 = Class.forName("org.lxh.demo15.Person");//執行個體化Class對象
}catch(ClassNotFoundException e){
e.printStackTrace();
}
Class c[] = c1.getInterfaces();//取得實現的全部介面
for(int i=0;i
System.out.println("實現的介面名稱:"+c[i].getName());//輸出介面名稱
}
}
}
一個類中可以實現多個介面,但是只能繼承一個父類,所以如果要想取得一個類的父類,可以直接使用Class類中的getSuperclass()方法。此方法定義如下:
PublicClass getSuperclass()
此方法返回的是Class執行個體,和之前的得到介面一樣,可以通過getName()方法取得名稱。
取得構造方法的例子:
package org.lxh.demo15;
import java.lang.reflect.Constructor;//匯入反射操作包
publicclass GetConstructorDemo01 {
publicstaticvoid main(String[] args) {
Class c1 = null;//聲明Class對象
try{
c1 = Class.forName("org.lxh.demo15.Person");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
Constructor con[]=c1.getConstructors();//得到全部構造方法
for(int i=0;i
System.out.println("構造方法:"+con[i]);//直接列印輸出
}
}
}
還原修飾符
在整個Java中對於方法的修飾符使用一定的數字表示出來的,而如果要想把這個數字還原成使用者可以看懂的關鍵字,則必須依靠Modifier類完成,此類定義在java.lang.reflect包中。直接使用Modifer類的一下方法可修飾符:
publicstatic String toString(int mod)
int mo = con[i].getModifiers();
System.out.print(Modifier.toString(mo)+””); //還原許可權
getDeclaredMethods()方法,此方法返回一個Method類的對象數組,而如果要想進一步取得方法具體資訊,例如:方法的參數,拋出的異常聲明等等,則就是必須依靠Method類
再反射操作中同樣可以取得一個類中的全部屬性,但是在取得屬性的時候有以下兩種不同的操作:
得到實現的介面或父類中的公用屬性:public Field[] getFields() throwsSecurityException
得到本類中自己定義的的全部屬性:public Field[] getDeclaredFields() throws SecurityException
如果要使用反射調用類中的方法可以通過Method類完成,操作步驟如下:
1、 通過Class類的getMethod(Stringname,Class…parameterTypes)方法取得一個Method的對象,並設定此方法操作時所需的參數類型。
2、 之後才可以使用invoke進行調用,並向方法中傳遞要設定的參數。
在Proxy類中的newProxyInstance()方法中,需要一個ClassLoader類的執行個體,ClassLoader實際上對應的是類載入器,在Java中主要有以下三種類載入器:
BootstrapClassLoader:此載入器採用C++ 編寫,一般開發中是看不到的;
ExtensionClassLoader:用來進行擴充類的載入,一般對應的是jre\lib\ext目錄中的類;
AppClassLoader:載入classpath指定的類,是最常用使用的一種載入器。
通過forName()載入類兩次時,此時的類只載入了一次
如果有以下代碼:
Object obj= new VipUser();//這裡VipUser是User的子類,它繼承了User
if(obj instanceof User){
System.out.println("instanceof判斷是一個user");
User user = (User)obj;
}
當使用以上代碼中的instanceof來判斷時,此時obj就是一個User了,但實際上它不是。它只是User的一個子類。
總結:使用instanceof時主要用來判斷是否實現了後面的介面。
if(obj.getClass()==User.class){
System.out.println("getClass判斷,是一個user");
User user = (User)obj;
}
而getClass()用於判斷對象的準確類型。
在後面用到的類:
package toto.learn1;
publicclass User {
private String name;
private String password;
private String gender;
public User(String name, String password, String gender) {
super();
this.name = name;
this.password = password;
this.gender = gender;
}
public User() {
super();
// TODO Auto-generatedconstructor stub
}
public User(String name, String password) {
super();
this.name = name;
this.password = password;
}
public String getName() {
returnname;
}
publicvoid setName(String name) {
this.name = name;
}
public String getPassword() {
returnpassword;
}
publicvoid setPassword(String password) {
this.password = password;
}
public String getGender() {
returngender;
}
publicvoid setGender(String gender) {
this.gender = gender;
}
publicvoid run(String str, int num){
System.out.println("run");
}
publicvoid run(String name){
System.out.println("hello:"+name);
}
}
以下是關於反射的部分。
package toto.learn1;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
publicclass Demo2 {
/**
* 反射
* @throwsIllegalAccessException
* @throwsInstantiationException
* @throwsNoSuchMethodException
* @throwsSecurityException
*/
publicstaticvoid main(String[] args) throws Exception {
Class clazz = User.class;
//構造方法
//獲得構造方法
Constructor[] cons = clazz.getConstructors();
for(Constructor con : cons){
//參數列表,獲得的約定是完整的參數類型,包括這種類型坐在的包。
Class[] types = con.getParameterTypes();
System.out.println("參數類型為:");
for(Class type:types){
System.out.println(type.getName()+"...");
}
System.out.println();
}
//獲得指定的構造方法 建立對象
try {
Object obj = clazz.newInstance();//預設調用無參的構造方法
System.out.println(obj.getClass());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
/**參數類型為:
java.lang.String...
java.lang.String...
參數類型為:
java.lang.String...
java.lang.String...
java.lang.String...
class toto.learn1.User*/
System.out.println("-------------------------");
//獲得指定的構造方法2 建立對象
Object obj = clazz.newInstance();//預設調用無參的構造方法
Constructor constructor = clazz.getConstructor(String.class,String.class);//此處調用有兩個參數的構造方法
//String.class是因為構造方法中有帶有年個參數的構造方法的形參都是String類的。
User usr = (User)constructor.newInstance("toto","查看");//傳遞兩個參數。
System.out.println(usr.getName()+" "+usr.getPassword());//結果:toto 查看
//方法
//獲得類所有的方法
Method [] methods = clazz.getMethods();
for(Method method:methods){
System.out.println(method.getName());
}
/**通過getMethods()方法獲得的類中包含有父類的方法,並且拿到的是共有的方法
* run
run
getName
setName
getPassword
setPassword
getGender
setGender
wait
wait
wait
hashCode
getClass
equals
toString
notify
notifyAll
* */
//如果想獲得聲明的所有方法,包括非public的,不包括繼承的,可以使用一下途徑實現
System.out.println("-----------------------");
Method [] methods2 = clazz.getDeclaredMethods();
for(Method method:methods2){
System.out.println(method.getName());
}
/**結果是:
* run
run
getName
setName
getPassword
setPassword
getGender
setGender*/
//獲得指定的方法,調用方法
Method runMethod = clazz.getMethod("run", String.class); /**第一個表示要調用那個方法
第二個參數是可變參數,表示調用的潤方法的參數類型。*/
//獲得了方法後,要執行時,得給出一個對象,在這裡要執行的對象是User,故作如下執行個體
User usr1 = new User();
runMethod.invoke(usr1, "toto");//表示執行usr1對象中的runMethod方法,並且向方法中傳遞了參數。
/**
* 運行結果:hello:toto 此為User類中run(String name);方法輸出的結果,其中name即為toto*/
//屬性
//獲得對象的屬性,先建立一個對象
Object object = new User("toto","123","male");//調用User中的構造方法:
//由於獲得對象的私人屬性,得獲得get方法,故得先獲得對象的位元組碼檔案,通過這個檔案獲得get方法
Class objectClazz = object.getClass();
//獲得get方法
Method runMethod2 = clazz.getMethod("getName"); //由於該方法沒有參數,故不用在向裡面傳遞參數
//執行此方法,輸出參數
Object name = runMethod2.invoke(object);
System.out.println(name);//由於上面傳遞了name值,故此處返回的結果值為:toto
//獲得所有屬性
System.out.println("-------------------------------");
//Field[] fields =clazz.getFields();//此方法拿到的是public的屬性,由於User類中沒有共有的屬性
Field[] fields = clazz.getDeclaredFields();//拿到聲明的屬性
for(Field fd : fields){
System.out.println(fd.getName()+"屬性類型: "+fd.getType());
}
/** 結果為:
* name屬性類型: class java.lang.String
password屬性類型: class java.lang.String
gender屬性類型: class java.lang.String*/
//為對象的password屬性賦值:
User user2 = new User();
String fieldName = "password";//作為變數來處理的,可以傳遞任意的屬性名稱
//getpw是通過反射拿到的屬性,其實就是password
Field getpw = user2.getClass().getDeclaredField(fieldName);
//判斷屬性是否為私人:
int num = getpw.getModifiers();//返回的是整型的值。
if(num == Modifier.PRIVATE){//獲得的是
System.out.println("屬性是私人的");
//讓java虛擬機器不檢查存取權限
//getpw.setAccessible(true);
}
//System.out.println(getpw);//結果private java.lang.Stringtoto.learn1.User.password
getpw.set(user2, 234567);//前一個參數表示的是為那個對象賦密碼。
System.out.println(user2.getPassword());
}
}
l 通過Field類訪問對象屬性時,需要注意存取權限的問題,若對象
的屬性被聲明為私人,則無法訪問,此時需要開啟java語言檢查
filed.setAccessible(true);
l Method類代表某個類中的一個成員方法
l 通過invoke方法可以調用Method表示的方法,此時同樣需要傳遞
對象給Method,若被調用的方法為靜態,則傳入一個null值
l 注意:jdk1.4和jdk1.5的invoke方法的區別:
l Jdk1.5:public Object invoke(Objectobj,Object... args)
l Jdk1.4:public Object invoke(Objectobj,Object[] args)
l 當傳入一個數組時,為了向後相容,虛擬機器會優先考慮考慮
調用JDK1.4的方法