1. 程式人生 > 其它 >Java進階學習之反射與動態繫結(5)

Java進階學習之反射與動態繫結(5)

目錄

1.反射

1.1.概述

Java的反射機制是指在程式的執行狀態時,可以構建任何一個類的物件,對任何一個物件可以瞭解其所屬的成員變數和方法,這種動態獲取程式資訊以及動態呼叫物件的功能稱為Java語言的反射機制。

1.2.實現方式

在最初的 helloworld案例 中,我們知道原始碼會編譯成位元組碼檔案.class,然後交給JVM執行,這裡的位元組碼檔案其實就是類的模板,所有的物件都需要通過這個模板進行建立。所以在使用反射之前需要獲取位元組碼檔案。

1.2.1.獲取Class

方式一:通過物件獲取
方式二:通過 類.class 獲取
方式三:通過Class類的靜態方法forName(String className)獲取
例子:
實體類:

public class Person {
    private String name;
    public Integer age;
    
    public Person() {}

    public Person(String name) {
        this.name = name;
    }
    
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String say(String name) {
        return "hello, " + name;
    }

    private String secretlySay(Integer time) {
        return "I love you " + time + " thousand times!";
    }
}

測試類:

public class TestReflection {
    public static void main(String[] args) {
        //方式一
        Person person = new Person("zhangsan", 22);
        Class clazz1 = person.getClass();
        System.out.println(clazz1);


        //方式一
        Class clazz2 = Person.class;
        System.out.println(clazz2);

        //方式三
        try {
            Class clazz3 = Class.forName("com.liquor.reflection.Person");
            System.out.println(clazz3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

執行結果:

我們知道這裡的Class物件其實就是類的模板,因此有且只有一個。

1.2.2.通過反射獲取屬性、設定屬性

以下都是用第一種方式獲取Class的。

public class TestReflection {
    public static void main(String[] args) {
        Person person = new Person("zhangsan", 22);
        Class clazz1 = person.getClass();
        System.out.println(clazz1);
        System.out.println("---------------------------");

        //獲取public屬性
        Field[] fields = clazz1.getFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
        System.out.println("---------------------------");

        //獲取包含private的所有屬性
        Field[] declaredFields = clazz1.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field.getName());
        }

        try {
            //獲取指定name的public屬性
            Field age = clazz1.getField("age");
            System.out.println(age);
            //獲取指定name的包含private的屬性
            Field name = clazz1.getDeclaredField("name");
            System.out.println(name);

            //獲取物件
            Object o = clazz1.newInstance();
            //設定屬性不校驗
            name.setAccessible(true);
            //設定屬性值,私有屬性需要關閉校驗
            name.set(o, "lisi");
            age.set(o, 33);
            System.out.println(o);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

執行結果:

1.2.3.通過反射獲取方法、執行方法
public class TestReflection {
    public static void main(String[] args) {
        Person person = new Person("zhangsan", 22);
        Class clazz1 = person.getClass();

        //獲取類及其父類的public方法
        Method[] methods = clazz1.getMethods();
        for (Method method : methods) {
            System.out.print(method.getName() + " ");
        }
        System.out.println();

        //獲取類包含private的所有方法
        Method[] methods2 = clazz1.getDeclaredMethods();
        for (Method method : methods2) {
            System.out.print(method.getName() + " ");
        }
        System.out.println();

        try {
            //獲取指定name的public方法
            Method m1 = clazz1.getMethod("say", String.class);
            System.out.println(m1);
            //獲取指定name的包含private的方法
            Method m2 = clazz1.getDeclaredMethod("say", String.class);
            System.out.println(m2);
            Method m3 = clazz1.getDeclaredMethod("secretlySay", Integer.class);
            System.out.println(m3);

            //獲取物件
            Object o = clazz1.newInstance();
            //設定方法不校驗
            m3.setAccessible(true);
            //呼叫方法
            Object rel1 = m1.invoke(o, "lisi");
            System.out.println(rel1);
            Object rel2 = m3.invoke(o, 3);
            System.out.println(rel2);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

執行結果:

1.2.4.通過反射獲取構造器並呼叫
public class TestReflection {
    public static void main(String[] args) {
        Person person = new Person("zhangsan", 22);
        Class clazz1 = person.getClass();

        //獲取類的public構造器
        Constructor[] constructors = clazz1.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("------------------------");

        try {
            //獲取指定name的public構造器
            Constructor c1 = clazz1.getConstructor(String.class);
            System.out.println(c1);
            Constructor c2 = clazz1.getConstructor(String.class, Integer.class);
            System.out.println(c2);

            //通過構造器獲取物件
            Object o1 = c1.newInstance("zhangsan");
            System.out.println(o1);
            Object o2 = c2.newInstance("lisi", 33);
            System.out.println(o2);

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}

執行結果:

2.動態代理

2.1.代理模式

使用一個類代表另一個類的功能,是一種設計模式。

2.2.靜態代理

編譯時代理類已經生成。
例子:
介面HelloService.java

public interface HelloService {
    String say(String name);
}

實際實現HelloServiceImpl

public class HelloServiceImpl implements HelloService {
    @Override
    public String say(String name) {
        return "Hello, " + name;
    }
}

代理類HelloStaticProxy.java

public class HelloStaticProxy implements HelloService {

    private HelloService helloService = new HelloServiceImpl();

    @Override
    public String say(String name) {
        System.out.println("--------------------------");
        return helloService.say(name);
    }
}

測試類:

public class TestProxy {
    public static void main(String[] args) {
        HelloStaticProxy helloStaticProxy = new HelloStaticProxy();
        System.out.println(helloStaticProxy.say("zhangsan"));
    }
}

執行結果:

靜態代理實現步驟:

  1. 建立介面
  2. 實現介面
  3. 建立代理類實現介面並且將原實現類引入,呼叫實現的方法
    缺點:
  • 由於實現同一個介面,介面變動則修改成本增加
  • 如果介面實現方法很多的話再實現工作量很大

2.3.動態代理

相比靜態代理,動態代理就是執行時動態建立代理的方式。
介面和被代理類與前面一樣。
代理類:

public class HelloProxy implements InvocationHandler {

    private Object obj;

    public HelloProxy(Object obj) {
        this.obj = obj;
    }

    public Object getNewInstance() {
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(obj, args);
        return invoke;
    }
}

測試類:

public class TestProxy {
    public static void main(String[] args) {
        HelloProxy helloProxy = new HelloProxy(new HelloServiceImpl());
        HelloService helloService = (HelloService) helloProxy.getNewInstance();
        System.out.println(helloService.say("zhangsan"));

    }
}

執行結果:

這是使用JDK原始碼實現的動態代理,使用介面等實現的。而如何被代理類沒有實現介面呢?
被代理類HelloServiceClass.java

public class HelloServiceClass {
    public String say(String name) {
        return "Hello, " + name;
    }
}

代理類:

public class HelloProxy implements InvocationHandler {

    private Object obj;

    public HelloProxy(Object obj) {
        this.obj = obj;
    }

    public Object getNewInstance() {
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(obj, args);
        return invoke;
    }
}

測試類:

public class TestProxy {
    public static void main(String[] args) {
        HelloServiceClass helloServiceClass = (HelloServiceClass) new CglibInterceptor().getNewInstance(HelloServiceClass.class);
        System.out.println(helloServiceClass.say("zhangsan"));
    }
}

執行結果:

靜態代理相對於動態代理從程式碼的實現上更加容易理解和編寫,代理類中呼叫的是真正的實現類。動態代理中,當我們需要使用到時才會建立類的模板然後建立物件呼叫方法。很顯然反射在動態代理中舉足輕重。