Web學習筆記 - 第009天

創建對象

1. 構造器創建對象

        Student stu1 = new Student("kygo", 100);
        System.out.println(stu1);

2. 通過克隆創建對象(內存復制)
實現Cloneable接口,重寫clone()方法

    @Override
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException();
        }
    }

測試

        Student stu2 = (Student) stu1.clone();
        System.out.println(stu2);

3. 通過反序列化創建對象(從流中讀取對象)
實現序列化接口Serializable

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(stu2);
        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(in);
        Student stu3 = (Student) ois.readObject();
        System.out.println(stu3);

4. 通過反射(reflection)創建對象
拿到一個類的原始數據類對象
1.根據路徑

        Class<?> clazz1 = Class.forName("com.kygo.Student");

2.用類.class

        Class<?> clazz2 = Student.class;

3.用類對象實例的getClass()方法

        Class<?> clazz3 = stu3.getClass();

根據類的類對象可以創建這個類的對象實例

        Student stu4 = (Student) clazz1.newInstance();

根據類的類對象使用getConstructor()方法的到這個類的構造器,使用構造器可以創建對象

        Constructor<?> constructor = clazz2.getConstructor(String.class, int.class);
        Student stu5 = (Student) constructor.newInstance("wang Dachui", 18);

測試:

        Class<?> clazz1 = Class.forName("com.kygo.Student");
        Class<?> clazz2 = Student.class;
        Class<?> clazz3 = stu3.getClass();
        // System.out.println(clazz1 == clazz2);   // true
        // System.out.println(clazz2 == clazz3);   // true
        Student stu4 = (Student) clazz1.newInstance();
        System.out.println(stu4);
        // System.out.println(stu4.getName());
        Constructor<?> constructor = clazz2.getConstructor(String.class, int.class);
        Student stu5 = (Student) constructor.newInstance("wang Dachui", 18);
        System.out.println(stu5);
        // System.out.println(stu5.getName());
        // System.out.println(stu5.getAge());
        Constructor<?>[] constructors = clazz3.getConstructors();
        for (Constructor<?> temp : constructors) {
            Student stu6 = (Student) temp.newInstance();
            System.out.println(stu6);
        }

設計模式

單例

單例 - 讓一個類只能創建出一個對象(服務器上常用)

  1. 將構造器私有(不允許直接使用構造器創建對象)
  2. 通過公開的靜態的方法向外界返回該類的唯一實例
餓漢式單例
public class StudentManager {
    private static StudentManager instance = new StudentManager();
    
    // ...
    
    private StudentManager() {
    }
    
    public static StudentManager getInstance() {
        return instance;
    }
    
    // ...
}
懶漢式單例(懶加載單例)
public class StudentManager {
    private static StudentManager instance = null;
    
    // ...
    
    private StudentManager() {
    }
    
    public static synchronized StudentManager getInstance() {
        if (instance == null) {
            instance = new StudentManager();
        }
        return instance;
    }
    
    // ...
}
登記式單例
public class ServiceFactory {
    private static Map<Class<?>, Object> map = new HashMap<>();
    // 登記式單例
    static {
        map.put(DeptService.class, 
                ServiceProxy.getProxyInstance(new DeptServiceImpl()));
        map.put(EmpService.class, 
                ServiceProxy.getProxyInstance(new EmpServiceImpl()));
    }
    
    private ServiceFactory() {
        throw new AssertionError();
    }
    
    public static Object factory(Class<?> serviceType) {
        return map.get(serviceType);
    }
}

一般類里面沒有狀態屬性都可以改造成單例模式類

簡單工廠模式

簡單工廠模式(靜態工廠模式)
將對象的實例化過通過工廠方法隱藏起來
使得對象的使用者可以和具體的對象以及創建對象的過程解耦合

例子1:在工廠用構造器創建對象

工廠類:

public class FruitFactory {
    
    public static Fruit factory(String type) {
        Fruit fruit = null;
        switch (type.toLowerCase()) {
        case "apple":
            fruit = new Apple();
            break;
        case "banana":
            fruit = new Banana();
            break;
        case "durian":
            fruit = new Durian();
            break;
        }
        return fruit;
    }
}

測試:

        Fruit apple = FruitFactory.factory("apple");
        Fruit durian = FruitFactory.factory("durian");
        Fruit grape = FruitFactory.factory("grape");
        System.out.println(apple);
        System.out.println(durian);
        System.out.println(grape);

例子2:使用反射

工廠類:

public class FruitFactory {
    
    public static Fruit factory(String className) {
        try {
            Class<?> clazz = Class.forName(className);
            return (Fruit) clazz.newInstance();
        } catch (Exception e) {
            // throw new RuntimeException();
            return null;
        }
    }
    
    public static Fruit factory(Class<?> fruitType) {
        try {
            return (Fruit) fruitType.newInstance();
        } catch (Exception e) {
            throw new RuntimeException();
        }
    }
}

測試:

        Fruit apple = FruitFactory.factory(Apple.class);
        Fruit durian = FruitFactory.factory(Durian.class);
        Fruit grape = FruitFactory.factory("com.kygo.Grape");
        System.out.println(apple);
        System.out.println(durian);
        System.out.println(grape);

動態代理

JDK 1.3引入了動態代理機制 - 可以通過反射的方式動態生成代理對象
InvocationHandler - invoke(Object, Method, Object[])
Proxy類 - newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)

舊做法:創建代理類并實現要代理的類相同的接口,重寫接口方法,在要代理的方法,添加自己的東西(日志等)
例子1:考生找槍手代考
考生接口:

public interface Candidate {

    public void joinExam();
}

人類:

public class Person {
    protected String name;
    
    public Person(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
}

懶學生:

public class LazyStudent extends Person implements Candidate {
    
    public LazyStudent(String name) {
        super(name);
    }

    @Override
    public void joinExam() {
        System.out.println("姓名: " + name);
    }
}

槍手:

public class ExamAgent extends Person implements Candidate {
    private Candidate target;
    
    public ExamAgent(String name) {
        super(name);
    }
    
    public void setTarget(Candidate target) {
        this.target = target;
    }
    
    @Override
    public void joinExam() {
        target.joinExam();
        System.out.println(name + "奮筆疾書答案.");
        System.out.println(name + "答題完畢交卷.");
    }
}

測試:

        LazyStudent student = new LazyStudent("王大錘");
        ExamAgent agent = new ExamAgent("駱昊");
        agent.setTarget(student);
        agent.joinExam();

現在:

  • 第一步 創建代理類并實現InvocationHandler的接口
class ListProxy<E> implements InvocationHandler
  • 第二步 添加被代理對象的屬性,和代理對象的構造器 - 單例
        private Object target; // 被代理的對象(目標對象)
        
        private ListProxy(Object target) {
            this.target = target;
        }
  • 第二步 實現靜態方法getProxyInstance()拿到代理類對象
    通過Proxy工具類的newProxyInstance方法生成動態代理
        public static <E> Object getProxyInstance(Object target) {
            Class<?> clazz = target.getClass();
            // 通過Proxy工具類的newProxyInstance方法生成動態代理
            // 第一個參數是被代理對象的類加載器
            // 第二個參數是被代理對象實現的接口(被代理對象和代理對象要實現相同的接口)
            // 第三個參數是實現了InvocationHandler接口的對象
            return Proxy.newProxyInstance(clazz.getClassLoader(), 
                    clazz.getInterfaces(), new ListProxy<E>(target));
        }
  • 第三步 重寫回調方法invoke()
    InvocationHandler接口(單方法接口/函數式接口)中的invoke方法是一個回調方法
    當執行被代理對象target的任何一個方法時不是直接執行而是回調代理對象的invoke方法
    該方法的第二個參數代表了需要執行的方法 第三個參數是執行方法時傳入的參數
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        // InvocationHandler接口(單方法接口/函數式接口)中的invoke方法是一個回調方法
        // 當執行被代理對象target的任何一個方法時不是直接執行而是回調代理對象的invoke方法
        // 該方法的第二個參數代表了需要執行的方法 第三個參數是執行方法時傳入的參數
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 被代理對象的所有方法都要通過代理對象來幫它調用執行
            // 調用被代理對象的方法 第一個參數是被代理的對象 第二個參數是方法參數
            // 如果被代理對象當前要執行的方法沒有參數那么args的值就是null
            // 如果被代理對象的方法有返回值那么retValue就保存該返回值
            // 如果被代理對象的方法沒有返回值那么retValue的值就是null
            Object retValue = method.invoke(target, args);
            // 如果執行的方法名以add、remove、clear、retain打頭
            // 那么通過下面代碼給這些方法增加在控制臺打印日志的功能
            String methodName = method.getName();
            // System.out.println("正在執行" + methodName + "方法");
            if (methodName.startsWith("add") || methodName.startsWith("remove")
                    || methodName.startsWith("clear") || methodName.startsWith("retain")) {
                if (target instanceof List) {
                    List<E> list = (List<E>) target;
                    // 以下代碼就是代理對象附加的行為(被代理對象沒有的行為)
                    System.out.println("Size = " + list.size());
                    System.out.println(Arrays.toString(list.toArray()));
                }
            }
            // 將之前執行被代理對象的方法得到的返回值返回
            return retValue;
        }   
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 國家電網公司企業標準(Q/GDW)- 面向對象的用電信息數據交換協議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 11,178評論 6 13
  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,776評論 0 9
  • 從三月份找實習到現在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發崗...
    時芥藍閱讀 42,372評論 11 349
  • 一、概述 1、概念: 1、生活中的代理:就是常說的代理商,從廠商將商品賣給消費者,消費者不用很麻煩的到廠商在購買了...
    玉圣閱讀 277評論 0 0
  • 原文: Dyanmic Proxy Classes 介紹 一個動態代理類是實現了多個接口存在于運行時的類,這樣,一...
    半黑月缺閱讀 978評論 0 0