什麼是反射
Java反射機制是在運行狀態中,對於任意類,都能知道這個類的全部屬性和方法,對於任意對象,都能夠調用它的任何一個方法或屬性。這種動態獲取的信息以及動態調用對象的方法的功能,稱為Java語言的反射機制。
Class類
Class 是JDK提供的一個類,完整路徑為 java.lang.Class,Class是反射能夠實現的基礎。對於每一個類,Java虛擬機都會初始化出一個Class類型的對象,Java 中的所有類型包括基本類型(int, long, float 等),即使是數組也有與之對應的 Class 類的對象。每當我們編寫並且編譯一個新創建的類就會產生一個對應Class對象,並且這個Class對象會被保存在同名.class文件中。當我們new一個新對象或者引用靜態成員變量時,JVM中的類加載器系統會將對應Class對象加載到JVM中,然後JVM再根據這個類型信息相關的Class對象,創建我們需要實例對象或者提供靜態變量的引用值。
Class類對象實例化
獲取Class對象的三種方式,如下:
- 利用Object類中提供的getClass方法獲取實例化對象,但是需要獲取一個類的實例化對象後,才可以獲取Class類實例。
- 使用“類.class” 形式獲取指定類 或接口的Class實例化對象,但需要導入包,不導包就拋編譯錯誤。
- 使用Class類內部提供的forName(className)獲取Class類對象,動態加載類,className需要是類的全限定名。
Class類對象實例化
<code> public class TestDemo {
public static void main(String args []) {
// new Node() 產生一個Node對象,一個Class對象
Node node = new Node();
// Class 類是反射機制的根源,可以通過Object類中所提供的方法獲取Class實例
// 利用Object類中提供的getClass方法獲取實例化對象,需要獲取一個類的實例化對象後 才可以獲取Class類實例,已經有實例化對象了,再反射沒啥意思。
Class > cla1 = node.getClass();
System.out.println(cla1);
// 使用“類.class” 形式獲取指定類 或接口的Class實例化對象,但需要導入包,不導包就拋編譯錯誤。
Class > cla2 = Node.class; //獲取Class類實例化對象
System.out.println(cla2 == cla1);
// 使用Class類內部提供的forName()獲取Class類對象,簡單,最常用
try {
Class cla3 = Class.forName("Node");
System.out.println(cla2 == cla3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Node{}/<code>
執行上述代碼,輸出結果為:
<code> class Node
true
true/<code>
反射機制與工廠設計模式
通常一個接口可能不止有一個子類,使用工廠設計模式的主要特點是解決接口與子類之間因直接使用關鍵字new所造成的耦合問題,但是傳統的工廠設計操作中會存在兩個嚴重的問題。
- 傳統工廠設計屬於靜態工廠設計,需要根據傳入的參數並結合大量的分支語句來判斷所需要實例化的子類,當一個接口或抽象類擴充子類時必須修改工廠類結構,否則將無法獲取新的子類實例,如下:
<code> class Node{}
class Fatory{
/**
* 因fatory 沒有屬性 因此定義static方法 。
* 獲取IFood接口實例化對象,利用此方法對外隱藏子類
*/
private Fatory(){} // 工廠類,沒有產生實例化的意義,避免產生實例化對象——構造方法私有化
/**
* 傳統工廠設計屬於靜態工廠設計,需要根據傳入的參數並結合大量的分支語句來判
* 斷所需要實例化的子類,當一個接口或抽象類擴充子類時必須修改工廠類結構,
* 否則將無法獲取新的子類實例。
*
*/
public static IFood eFood(String food) {
if ("麵包".equals(food)) {
return new Break();
}else if ("牛奶".equals(food)) {
return new Milk();
}
else {
System.out.println("不存在該食物");
return null ;
}
}
}/<code>
- 工廠設計只能夠滿足一個接口或抽象類獲取實例化對象的需求,如果有更多的接口或抽象類定義時將需要定義更多的工廠類或擴充工廠類中的satic方法。
利用反射機制解決第一個問題
<code> package DemoRunable.example;
import java.io.IOException;
public class FileDemo {
public static void main(String args []) throws IOException, ClassNotFoundException {
System.out.println();
IFood food = Fatory.eFood("DemoRunable.example.Break");
food.eat();
}
}
interface IFood{
public abstract void eat();// 食物核心作用 被食用
}
class Break implements IFood{
@Override
public void eat() {
System.out.println("手撕麵包");
}
}
class Milk implements IFood{
@Override
public void eat() {
System.out.println("喝牛奶");
}
}
class Fatory{
/**
* 因fatory 沒有屬性 因此定義static方法 。
* 獲取IFood接口實例化對象,利用此方法對外隱藏子類
*/
private Fatory(){} // 工廠類,沒有產生實例化的意義,避免產生實例化對象——構造方法私有化
/**
*
* @param className 實例化對象名稱
* @return 如果子類存在則返回指定類的接口實例化對象
*/
public static IFood eFood(String className) {
IFood food = null;
try {
food = (IFood) Class.forName(className).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return food;
}
/*
public static IFood eFood(String food) {
if ("麵包".equals(food)) {
return new Break();
}else if ("牛奶".equals(food)) {
return new Milk();
}
else {
System.out.println("不存在該食物");
return null ;
}
}
}
*/
}/<code>
使用泛型解決解決第二個問題。
<code> package DemoRunable.example;
import java.io.IOException;
public class FileDemo {
public static void main(String args []) throws IOException, ClassNotFoundException {
System.out.println();
IFood food = Fatory.eFood("DemoRunable.example.Break",IFood.class);
food.eat();
IOther other = Fatory.eFood("DemoRunable.example.OtherOne",IOther.class);
other.send();
}
}
interface IFood{
public abstract void eat();// 食物核心作用 被食用
}
class Break implements IFood{
@Override
public void eat() {
System.out.println("手撕麵包");
}
}
class Milk implements IFood{
@Override
public void eat() {
System.out.println("喝牛奶");
}
}
interface IOther{
public abstract void send();
}
class OtherOne implements IOther{
@Override
public void send(){
System.out.println("開心");
}
}
class OtherTwo implements IOther{
@Override
public void send(){
System.out.println("很開心");
}
}
class Fatory{
/**
* 因fatory 沒有屬性 因此定義static方法 。
* 獲取IFood接口實例化對象,利用此方法對外隱藏子類
*/
private Fatory(){} // 工廠類,沒有產生實例化的意義,避免產生實例化對象——構造方法私有化
/**
*
* @param className 實例化對象名稱
* @param clazz 返回實例化對象類型
* @param泛型,使得工廠類適用所有類型 /<code>
* @return 如果子類存在則返回指定類的接口實例化對象
*/
public staticT eFood(String className,Class clazz) {
T food = null;
try {
food = (T) Class.forName(className).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return food;
}
}
閱讀更多 軟件測試開發技術棧 的文章