由淺入深瞭解反射機制一:Class類

Class類的作用,反射的源頭

 在Object類中定義了以下的方法,此方法將被所有子類繼承:
 public final Class getClass()
 以上方法返回值的類型是一個Class類,實際上此類是Java反射的源頭,
 實際上所謂反射從程序的運行結果來看也很好理解,即:
 可以通過對象反射求出類的名稱
 正常方式:引入需要的包、類名稱,通過new實例化,取得實例化對象
 反射方式:實例化對象,getClass()方法,取得完整的包、類名稱

實例化Class類對象的方法有三種:
 1.通過forName()方法
 2.類.class
 3.對象.getClass()

package org.lxh.demo15.getclassdemo ;
class X{
};
public class GetClassDemo02{
	public static void main(String args[]){
		Class<?> c1 = null ;		// 指定泛型
		Class<?> c2 = null ;		// 指定泛型
		Class<?> c3 = null ;		// 指定泛型
		try{
			// 以下的操作形式是在開發中最常用的一種形式
			c1 = Class.forName("org.lxh.demo15.getclassdemo.X") ;
		}catch(ClassNotFoundException e){
			e.printStackTrace() ;
		}
		c2 = new X().getClass() ;		// 通過Object類中的方法實例化
		c3 = X.class ;	// 通過類.class實例化
		System.out.println("類名稱:" + c1.getName())  ;	// 得到類的名稱
		System.out.println("類名稱:" + c2.getName())  ;	// 得到類的名稱
		System.out.println("類名稱:" + c3.getName())  ;	// 得到類的名稱
	}
};

Class類的使用

Class主要是反射的源頭,不光可以取得對象所在類的信息,也可以哦那天通過Class類的方法進行對象的實例化操作,正常情況下,使用new關鍵字爲對象實例化,如果現在已經實例化好了class對象,則就可以通過Class類中提供的

public T newInstance()

              throws InstantiationException,

                     IllegalAccessException

 

package org.lxh.demo15.instancedemo ;
class Person{
	private String name ;	// name屬性
	private int age ;		// age屬性
	public void setName(String name){
		this.name = name ;
	}
	public void setAge(int age){
		this.age = age ;
	}
	public String getName(){
		return this.name ;
	}
	public int getAge(){
		return this.age ;
	}
	public String toString(){	// 覆寫toString()方法
		return "姓名:" + this.name + ",年齡:" + this.age  ;
	}
};
public class InstanceDemo01{
	public static void main(String args[]){
		Class<?> c = null ;		// 聲明Class對象
		try{
			c = Class.forName("org.lxh.demo15.instancedemo.Person") ;
		}catch(ClassNotFoundException e){
			e.printStackTrace() ;
		}
		Person per = null ;	// 聲明Person對象
		try{
			per = (Person)c.newInstance() ;	// 實例化對象
		}catch(Exception e){
			e.printStackTrace() ;
		}
		per.setName("李興華") ;		// 設置姓名
		per.setAge(30) ;				// 設置年齡
		System.out.println(per) ;	// 內容輸出,調用toString()
	}
};

通過以上代碼,可以發現,即使不使用關鍵字new對象也可以進行實例化操作,反射的作用。但是,在使用以上操作的時候有一點要注意,在操作類中必須存在無參構造方法,否則無法實例化。

package org.lxh.demo15.instancedemo ;
class Person{
	private String name ;	// name屬性
	private int age ;		// age屬性
	public Person(String name,int age){
		this.setName(name) ;
		this.setAge(age);
	}
	public void setName(String name){
		this.name = name ;
	}
	public void setAge(int age){
		this.age = age ;
	}
	public String getName(){
		return this.name ;
	}
	public int getAge(){
		return this.age ;
	}
	public String toString(){	// 覆寫toString()方法
		return "姓名:" + this.name + ",年齡:" + this.age  ;
	}
};
public class InstanceDemo02{
	public static void main(String args[]){
		Class<?> c = null ;		// 聲明Class對象
		try{
			c = Class.forName("org.lxh.demo15.instancedemo.Person") ;
		}catch(ClassNotFoundException e){
			e.printStackTrace() ;
		}
		Person per = null ;	// 聲明Person對象
		try{
			per = (Person)c.newInstance() ;	// 實例化對象
		}catch(Exception e){
			e.printStackTrace() ;
		}
		per.setName("李興華") ;		// 設置姓名
		per.setAge(30) ;				// 設置年齡
		System.out.println(per) ;	// 內容輸出,調用toString()
	}
};

出現以下錯誤:

java.lang.ClassNotFoundException: org.lxh.demo15.instancedemo.Person
	at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:188)
	at fanshe.InstanceDemo01.main(InstanceDemo01.java:29)
java.lang.NullPointerException
	at fanshe.InstanceDemo01.main(InstanceDemo01.java:35)
Exception in thread "main" java.lang.NullPointerException
	at fanshe.InstanceDemo01.main(InstanceDemo01.java:39)

所以說,發現,使用以上的方式實際上還是需要類中構造方法的支持,符合於對象的實例化要求。如果要想解決這樣的問題,則必須明確的指定要調用的構造方法,並傳遞參數,但是從實際的開發角度講,一般使用反射實例化對象的時候,類中都最好存在一個無參構造,這樣的操作比較合理。

如果要想調用有參,則必須按照以下的步驟進行:

  1. 通過Class類中的getConstructors()取得本類中的全部構造方法。
  2. 向構造方法中傳遞一個對象數組進去,裏面包含了構造方法中鎖需的各個參數。
  3. 之後通過Constructor實例化對象

在Constructor類中存在一個方法:

public T newInstance(Object... initargs)

              throws InstantiationException,

                     IllegalAccessException,

                     IllegalArgumentException,

                     InvocationTargetException

傳遞初始化參數,以進行對象的實例化操作。

package org.lxh.demo15.instancedemo ;
import java.lang.reflect.Constructor ;	// 導入反射機制包
class Person{
	private String name ;	// name屬性
	private int age ;		// age屬性
	public Person(String name,int age){
		this.setName(name) ;
		this.setAge(age);
	}
	public void setName(String name){
		this.name = name ;
	}
	public void setAge(int age){
		this.age = age ;
	}
	public String getName(){
		return this.name ;
	}
	public int getAge(){
		return this.age ;
	}
	public String toString(){	// 覆寫toString()方法
		return "姓名:" + this.name + ",年齡:" + this.age  ;
	}
};
public class InstanceDemo03{
	public static void main(String args[]){
		Class<?> c = null ;		// 聲明Class對象
		try{
			c = Class.forName("org.lxh.demo15.instancedemo.Person") ;
		}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){
			e.printStackTrace() ;
		}
		System.out.println(per) ;	// 內容輸出,調用toString()
	}
};

但是,從實際角度看,如果要使用反射進行對象的實例化操作,最好在類中存在無參構造。

總結:

在使用Class實例化對象的時候必須保證類中存在一個無參構造,否則無法使用。

如果要想調用有參構造進行對象的實例化操作,則必須使用constructor類完成,此類表示構造方法,並通過可變參數傳遞要求的內容。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章