Java學習——反射機制
1.Class類物件例項化
-
呼叫Object類中的getClass()方法,但是如果要使用此類操作則必須要有例項化物件。
-
使用“類.class”取得,此時可以不需要通過指定類的例項物件取得。
-
呼叫Class類提供的方法:publics static Class<?> forName(String className) throws ClassNotFoundException。
package Reflect;
import java.util.Date;
public class TestGetClass {
public static void main(String[] args) throws ClassNotFoundException {
Date date = new Date();
System.out.println(date.getClass());
//1.呼叫Object類中的getClass()方法,這個類必須要例項化,必須要有例項化物件
Class<?> cls = date.getClass();
System.out.println("第一種方法:"+cls.getName( ));
//2.使用“類.class”取得,此時可以不需要通過指定類的例項化物件取得
Class<?> cls1 = java.util.Date.class;
System.out.println("第二種方法:"+cls1.getName());
//3.呼叫Class類提供的方法,Class.forName
Class<?> cls2 = Class.forName("java.util.Date");
System.out.println ("第三種方式:"+cls2.getName());
}
}
執行結果:
2.反射例項化物件
- 任何類都至少會存在一個構造方法,如果利用Class類中的newInstance()方法反射例項化類物件,則類中一定要提供無參的構造方法,否則會出現語法錯誤。
package Reflect;
class Book{
public Book(){
System.out.println("book類的無參構造方法");
}
@Override
public String toString() {
return "《肖申克的救贖》";
}
}
public class TestnewInstance {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("Reflect.Book"); //設定要操作物件的類的名稱
// 反射例項化後的物件返回的結果都是Object型別
//利用newInstance例項化物件,一定要有午餐構造方法
Object obj = cls.newInstance(); //相當於使用new 呼叫無參構造
Book book = (Book) obj; //向下轉型
System.out.println(book);
}
}
執行結果:
這樣做的意義:
利用反射機制例項化物件可以實現更好的解耦合操作
通常我們都是通過new關鍵字進行物件的例項化操作。但是new例項化物件需要明確地指定類的構造方法,所以new是造成耦合的最大元凶,而要想解決程式碼的耦合問題,首先就要解決的就是關鍵字new例項化物件的操作。
下面是一個利用反射機制解決問題的示例:
package test;
//工廠設計模式
public class testFactory {
public static void main(String[] args) {
fruit f = Factory.getInstance("test.Apple");
f.eat();
f = Factory.getInstance("test.Orange");
f.eat();
}
}
//定義工廠類,此類不再產生新的屬性
class Factory{
/**
* 取得指定型別的介面物件
* @param className 要取得的類例項化物件標記
* @return 如果指定標記存在,則返回fruit介面的例項化物件,否則返回null
*/
public static fruit getInstance(String className){
fruit f = null;
try { //反射例項化,子類物件可以使用fruit接受
f = (fruit) Class.forName(className).newInstance();
}catch (Exception e){
e.printStackTrace();
}
return f;
}
}
interface fruit{
public abstract void eat(); //定義抽象方法
}
class Apple implements fruit{ //定義介面子類
@Override
public void eat() { //重寫抽象方法
System.out.println("jack正在吃蘋果");
}
}
class Orange implements fruit{
@Override
public void eat() {
System.out.println("tom正在吃橘子");
}
}
利用反射機制,這樣就可以在增加水果介面的子類的時候,不需要去修改工廠類。
3.使用反射呼叫構造
利用Class類的newInstance()方法可以實現反射例項化物件,但是這個方法的前提是類中必須要有一個無參的構造方法。所以當類中只提供了有參的構造方法的時候,就必須使用java.lang.reflect.Constructor類來實現物件的反射例項化操作
示例程式碼:
package Reflect;
import java.lang.reflect.Constructor;
class Product{
private String name;
private Double price;
public Product(String name,Double price){
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
public class TestConstructor {
public static void main(String[] args) throws Exception{
Class<?> cls = Class.forName("Reflect.Product");
//明確地找到Product類中的兩個引數構造,第一個引數型別是String,第二個是Double
Constructor<?> con = cls.getConstructor(String.class,Double.class);
Object obj = con.newInstance("奧特曼玩偶",20.00); //例項化物件
System.out.println(obj);
}
}
執行結果:
因為Product類中只提供了有兩個引數的構造方法,所以首先要利用Class類物件取得此構造方法(返回Constuctor類物件),然後利用Constuctor類中的newInstance()方法傳遞指定的資料,就可以呼叫有參構造進行物件的反射例項化了。
4.反射呼叫方法,成員
傳統呼叫方法都是:物件.方法名()的形式。現在利用反射進行方法的呼叫。除此之外,還可以利用反射進行成員操作。
示例程式碼:
package Reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
class Fruit{
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
public class TestMethod {
public static void main(String[] args) throws Exception {
String filedName = "name"; //要操作的成員的名稱
Class<?> cls = Class.forName("Reflect.Fruit");//取得要操作類的反射物件
Object obj = cls.newInstance(); //例項化物件(必須要做的)
//取得類中的setName()方法,由於name需要首字母大寫,所以呼叫init()處理,引數型別為String
Method setMet = cls.getMethod("set"+initCap(filedName),String.class);
//取得類中的getTitle()方法,本方法不接收引數並且沒有返回值型別宣告
Method getMet = cls.getMethod("get"+initCap(filedName));
setMet.invoke(obj,"橘子"); //等價於:Fruit類物件.setName("橘子");
System.out.println(getMet.invoke(obj)); //等價於,Fruit類物件.getName();
//反射呼叫成員
Field nameFiled = cls.getDeclaredField("name"); //取得類中的name屬性
nameFiled.setAccessible(true); //取消封裝
nameFiled.set(obj,"蘋果");
System.out.println(nameFiled.get(obj));
}
public static String initCap(String str){
//首字母大寫操作
return str.substring(0,1).toUpperCase()+str.substring(1);
}
}
執行結果:
需要注意的地方是:呼叫類中的普通方法的時候,必須要例項化物件,才能正常完成。看也看到在Method類的invoke()方法呼叫時,第一個引數時一個例項化物件。