反射機制初探 走進沼澤 在正常的程式中,先有類,然後再有對象。 取得Class對象(類對象) 實例觀察: 運行結果 發現:調用getClass()方法輸出的是類的完整名稱,等於找到了對象出處: ——這就是“ 反 ” Class 類對象實例化 java.lang.Class是一個類,這個類就是反射操作 ...
反射機制初探 *
走進沼澤
在正常的程式中,先有類,然後再有對象。
- 取得Class對象(類對象)
public final Class<?> getClass() ;
- 實例觀察:
public class TestDemo {
public static void main(String [] args) {
Date date = new Date() ; // 產生對象
System.out.println(date.getClass());
// 因為所有類都是 Object的子類,而getClass是final型所以所有類都有getClass()方法
}
}
- 運行結果
class java.util.Date
發現:調用getClass()方法輸出的是類的完整名稱,等於找到了對象出處:
——這就是“反”
Class 類對象實例化
java.lang.Class是一個類,這個類就是反射操作的源頭,即:所有的反射都要從Class類開始運行 三種實例化方法;
- 調用Object中的Class方法
Class<?> cls = date.getClass(); // 此代碼將Class類進行了實例化;getClass()是class類的實例化方法
- 使用 類.class 方法
Class<?> cls = Date.class;
前者是產生了類的實例化對象之後取得Class類對象,但是類.class方法並沒有實例化對象產生
- 調用Class類提供的 forName() 方法
Class<?> cls = Class.forName(String ClassName) ;
此時可以不使用 import 語句導入一個明確的類名稱,而類名稱採用字元串的方式進行描述
反射實例化對象
當獲得一個類時,使用關鍵字 new 進行類對象實例化操作;但是如果有了Class對象,那麼就可以利用反射進行對象實例化操作。
實例化對象的方法:
public T newInstance();
- 實例分析:
class Book {
public Book() {
System.out.println("**********Book類 ***********");
}
@Override
public String toString() {
return "Hello,World!";
}
}
public class TestDemo {
public static void main(String [] args) throws InstantiationException, ReflectiveOperationException {
Class<?> cls = Class.forName("helloworld.Book");
@SuppressWarnings("deprecation")
Object obj = cls.newInstance();
Book book = (Book) obj ;// 向下轉型
System.out.println(book);// 調用了Book類的對象
// 此過程沒有 new 關鍵字實例化Book類,而是通過反射來實例化對象
}
}
在反射機制的加持下,類的對象實例化則不再單獨依靠 new關鍵字 。
- new 與 反射 實例化對象的區別
package helloworld;
interface Fruit { // 函數是介面
public void eat() ;
}
class Apple implements Fruit {
@Override
public void eat() {
System.out.println("蘋果");
}
}
class Factory {
public static Fruit getInstance(String className) {
if ("apple".equals(className)) {
return new Apple();
}
return null ;
}
}
public class TestFactory {
public static void main(String[] args) {
Fruit f = Factory.getInstance("apple");// 實例化Fruit對象
f.eat();
}
}
- 如果Fruit介面子類被增加了一個,那麼就表示程式需要修改工廠類
package helloworld;
interface Fruit { // 函數是介面
public void eat() ;
}
class Apple implements Fruit {
@Override
public void eat() {
System.out.println("蘋果");
}
}
class Orange implements Fruit{
@Override
public void eat() {
System.out.println("橘子");
}
}
class Factory {// 工廠類
public static Fruit getInstance(String className) {
if ("apple".equals(className)) {
return new Apple();
} else if ("orange".equals(className)) {
return new Orange();
} else {
return null ;
}
}
}
public class TestFactory {
public static void main(String[] args) {
Fruit f = Factory.getInstance("apple");// 實例化Fruit對象
f.eat();
}
}
如上兩則程式可以看出:
每增加一個類,就要修改工廠類,非常的繁瑣複雜;因為工廠類中的對象都是通過new直接實例化,而new就造成了這種高耦合的問題。
解決方法:
依靠反射實例化對象來完成
package helloworld; interface Fruit { // 函數是介面 public void eat() ; } class Apple implements Fruit { @Override public void eat() { System.out.println("蘋果"); } } class Orange implements Fruit{ @Override public void eat() { System.out.println("橘子"); } } class Factory {// 工廠類 @SuppressWarnings("deprecation") public static Fruit getInstance(String className) throws InstantiationException, ReflectiveOperationException, ClassNotFoundException { Fruit f = null ; try { f = ((Fruit) Class.forName(className).newInstance()); // 因為 Class.forName()方法返回的是Object(預設/推薦),所以需要向下轉型為Fruit } catch (Exception e) { } return f ; } } public class TestFactory { public static void main(String[] args) throws InstantiationException, ClassNotFoundException, ReflectiveOperationException { Fruit f = Factory.getInstance("helloworld.Apple"); f.eat(); } } // 程式中出現疑慮,可以參照"實例分析"
此時的程式就完成瞭解耦和的目的,而且可擴展性非常的強
反射調用方法
- 定義Book類
package helloworld;
public class Book {
private String title ;
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
}
類中有 無參構造方法 所以實例化對象的時候可以直接利用Class類中的newInstance()方法
在Class類中定義有如下的取得類中Method的操作:
- 取得類中的全部方法:
public Method [] getMethods();
- 取得指定類中方法:
public Method getMethod(String className , Class<?>... parameterTypes);
以上返回的是:java.lang.reflect.Method 類的對象
Methos類中的調用方法:
public Object invoke(Object obj , Object … args);
實例:
package helloworld;
import java.lang.reflect.Method;
public class TestDemo {
public static void main(String [] args) throws InstantiationException, ReflectiveOperationException {
String classname = "title" ;
Class<?> cls = Class.forName("helloworld.Book");
Object obj = cls.newInstance(); // 給出了Book類的實例化對象
Method setMet = cls.getMethod("set" + initcap(classname),String.class);
Method getMed = cls.getMethod("get" + initcap(classname));
setMet.invoke(obj, "Java");
System.out.println(getMed.invoke(obj));
}
private static String initcap(String str) { // 首字母大寫
return str.substring(0,1).toUpperCase() + str.substring(1) ;
}
}
反射調用成員
類中的屬性,一定要在本類實例化對象產生之後才可以分配記憶體空間。
取得全部成員:
public Field [] getDeclaredFields() ;
取得指定成員:
public Field getDeclaredFields() ;
返回的類型:java.lang.reflect.Field 類對象
- 取得屬性內容:
public Object get(Object obj);
- 設置屬性內容:
public void set(Object obj , Object Value);
定義Book類:
package helloworld;
public class Book {
public String title ;
}
package helloworld;
import java.lang.reflect.Field;;
public class TestDemo {
public static void main(String [] args) throws InstantiationException, ReflectiveOperationException {
String classname = "title" ;
Class<?> cls = Class.forName("helloworld.Book");
Object obj = cls.newInstance(); // 給出了Book類的實例化對象
java.lang.reflect.Field titleField = cls.getDeclaredField("title") ;
titleField.set(obj, "Java");
System.out.println(titleField.get(obj));
}
}
- 另外,還有一個訪問private私有成員的方法,利用 setAccessible() 方法取消封裝,然後調用成員。
package helloworld;
import java.lang.reflect.Field;;
public class TestDemo {
public static void main(String [] args) throws InstantiationException, ReflectiveOperationException {
String classname = "title" ;
Class<?> cls = Class.forName("helloworld.Book");
Object obj = cls.newInstance(); // 給出了Book類的實例化對象
Field titleField = cls.getDeclaredField("title") ;
titleField.setAccessible(true);// 取消封裝
titleField.set(obj, "Java");
System.out.println( titleField.get(obj) );
}
}
構造方法和普通方法,也同樣可以取消封裝,但很少使用。
總結
- 實例化對象的方法增加一種“反射”
- 反射調用類機構只是一個開始