1. 程式人生 > 其它 >Java學習——反射機制

Java學習——反射機制

技術標籤:javaSEjava反射

1.Class類物件例項化

  1. 呼叫Object類中的getClass()方法,但是如果要使用此類操作則必須要有例項化物件。

  2. 使用“類.class”取得,此時可以不需要通過指定類的例項物件取得。

  3. 呼叫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.反射例項化物件

  1. 任何類都至少會存在一個構造方法,如果利用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()方法呼叫時,第一個引數時一個例項化物件。