3/31day23_设计模式


复习

SAXReader read方法  
    public class TestDemo {
    public static void main(String[] args) throws DocumentException {
        SAXReader reader = new SAXReader();
        // 根目录下的books.xml
        Document document1 = reader.read(new File("books.xml")); 
        //根目录下day23模块下的books.xml
        Document document2 = reader.read(new File("day23/books.xml"));
        //在src下的books.xml
        InputStream in = TestDemo.class.getResourceAsStream("/books.xml");
        Document document3 = reader.read(in); 
    }
}

1.XML语法
2.XML约束(DTD,Schema)
    我们重点是根据约束写出符合规则的文档(IDEA提示)
3.XML的解析
    a.直接解析
    SAXReader reader = new SAXReader();
    Document document = reader.read(new File("books.xml")); 
    Element rootElement = document.getRootElement();
    List elements = rootElement.elements();
    getName();//获取标签名
    getText();//获取标签内容
    attributeValue(String attrName);//获取标签某个属性值

    b.集合XPath
    List elements = selectNodes(String xpath);  
    Element element = selectSingleNode(String xpath);

今日内容

设计模式!【重点】
1.单例设计模式
2.多例设计模式
3.动态代理【超难,现在1次,web阶段1次,框架阶段1次】
今日目标: 把格式学会(怎么用)
4.工厂设计模式(解耦)
5.Lombok【自学】


day23

单例设计模式

单例设计模式介绍

单例设计模式的作用就是为了想让一个类只能有一个实例对象

单例设计模式的实现步骤

  1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
  2. 在该类内部产生一个唯一的实例化对象,并且将其封装为private static final类型的成员变量。(private为了不让外部访问,static因为是要让改变量变成静态的。因为返回唯一对象的方法是静态的。因为不用静态方法的话,外部不能创建对象,会访问不了实例方法)
  3. 定义一个静态方法返回这个唯一对象。

单例设计模式的类型

根据实例化对象的时机单例设计模式又分为以下两种:

  • 懒汉式

    不立即创建本类的对象, 当别人调用静态方法获取本类的对象时, 才去创建

  • 饿汉式

    先把本类对象创建出来, 当别人调用静态方法获取本类的对象时, 直接返回对象即可

饿汉式单例设计模式

public class Singleton {
    // 1.将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
    private Singleton() {}
 
    // 2.在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
    private static final Singleton instance = new Singleton();
    
    // 3.定义一个静态方法返回这个唯一对象。
    public static Singleton getInstance() {
        return instance;
    }
}

缺点: 容易造成内存浪费

懒汉式单例设计模式

注意

必须要对返回对象方法用 synchronized 修饰, 否则多线程访问时, 不能保证原子性的话, 会出现创建多个对象的情况

public class Singleton {
    // 1.将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
    private Singleton() {}
 
    // 2.在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
    private static Singleton instance;
     
    // 3.定义一个静态方法返回这个唯一对象。要用的时候才例化出对象
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}        

上述代码会造成线程阻塞

懒汉式单例模式的优化, 加上volatile保证可见性, 和防止重排, 双重检验不会造成线程阻塞

public class Singleton {
    // 1.将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
    private Singleton() {}
 
    // 2.在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
    private static volatile Singleton instance;

public static Singleton getInstance() {
        //采取双重检验, 把锁加在第二次检验上, 不会造成线程阻塞
        if(instance == null) {
            synchronized(Singleton.class){
                instance = new Singleton();
            }          
        }
        return instance;
    }
}

多例设计模式

多例设计模式的介绍

  • 作用 多例设计模式保证我们的类具有指定个数的对象

多例设计模式的实现步骤

  1. 私有化构造(不让别人可以正常创建对象)

  2. 在类内部创建一个private static修饰的集合, 用于保存我们自己创建的对象

  3. 使用静态代码块 static{} 向集合中添加指定个数的对象

  4. 通过一个public static 静态方法, 随机返回集合中的某个对象

多里设计模式的代码实现

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Multition {
    // 定义该类被创建的总数量
    private static final int maxCount = 3;
    // 定义存放类实例的list集合
    private static List instanceList = new ArrayList()
    // 构造方法私有化,不允许外界创建本类对象
    private Multition() {
    }
    static {
        // 创建本类的多个实例,并存放到list集合中
        for (int i = 0; i < maxCount; i++) {
            Multition multition = new Multition();
            instanceList.add(multition);
        }
    }
    // 给外界提供一个获取类对象的方法
    public static Multition getMultition(){
        Random random = new Random();
        // 生成一个随机数
        int i = random.nextInt(maxCount);
        // 从list集合中随机取出一个进行使用
        return (Multition)instanceList.get(i);
    }
}

动态代理

代理模式介绍

代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事.为了让保存用户的集合不会被用户进行直接的增删改操作, 在用户和保存信息集合之间增加一个集合的代理

静态代理入门案例

静态代理是指代理类ArrayListProxy在编译时期已经定义好了

代理类步骤:

  1. 和被代理类实现相同接口(List). 并且重写所有接口内的方法
  2. 代理类内保存被代理类对象.并且用private修饰
/**
 * 此类就是普通ArrayList类的代理
 */
//1.让代理对象实现被代理对象一样的接口,目的是让代理对象和被代理对象具有一样的方法
public class ArrayListProxy implements List{
    //2.要在代理对象中保存一个被代理对象
    private ArrayList list;

    public ArrayListProxy(ArrayList list) {
        this.list = list;
    }

    //3.下面的一堆方法中,如果是不修改集合数据的方法,就调用list的方法
    //如果是修改集合的方法,直接抛出异常
    @Override
    public boolean add(String s) {
        //直接抛出异常
        throw new UnsupportedOperationException("不允许调用add方法");
    }
    @Override
    public String get(int index) {
        return list.get(index);
    }
    @Override
    public String set(int index, String element) {
        //直接抛出异常
        throw new UnsupportedOperationException("不允许调用set方法");
    }
    @Override
    public String remove(int index) {
        //直接抛出异常
        throw new UnsupportedOperationException("不允许调用remove方法");
    }
}

public class TestProxyDemo {
    public static void main(String[] args) {
        //1.创建一个ArrayList
        ArrayList arr = new ArrayList();
        //保存用户的信息
        arr.add("张三");
        arr.add("18");
        arr.add("北京中关村");
        arr.add("188888块");

        //2.要集合arr先交给代理对象
        List list = getArrayListProxy(arr);

        //3.操作
//        list.add("李四");  抛出异常!
//        list.remove(1); 抛出异常!
//        list.set(1, "28"); 抛出异常!
        System.out.println(list.get(2)); //OK 正常操作
    }

    public static List getArrayListProxy(ArrayList arr) {
        //创建一个ArrayList的代理
        ArrayListProxy arrayListProxy = new ArrayListProxy(arr);
        //返回代理对象
        return arrayListProxy;
    }
}

动态代理概述

  • 概述

    动态代理和静态代理区别: 在于代理类是否已经写好

    动态代理是指代理类在运行时期通过代码动态生成.

  • 作用

    动态代理简单来说是:拦截对真实对象方法的直接访问,增强真实对象方法的功能

    动态代理可以对被代理对象的方法进行增强,可以在不修改方法源码的情况下,增强被代理对象方法的功能,在方法执行前后做任何你想做的事情。

动态代理案例

  • 动态代理需要使用的方法在Collections工具类定义好了

    • 这个方法底层采用了动态代理

    • 在JAVA的Collections工具类中, 已经定义好了一个生成代理类的方法unmodifiableList. 该方法返回的是一个list对象, 该对象不允许被增删改操作, 只能被查询. 这一步是在运行时期创建出来的list对象

java.util.Collections:操作集合的工具类
 
static  List unmodifiableList(List list)
        返回指定列表的不可修改视图。
        此方法允许模块为用户提供对内部列表的“只读”访问。
        在返回的列表上执行的查询操作将“读完”指定的列表。
        试图修改返回的列表(不管是直接修改还是通过其迭代器进行修改)将导致抛出         
        UnsupportedOperationException:不支持操作异常
unmodifiableList作用:传递List接口,方法内部对List接口进行代理,返回一个被代理后的List接口
        对List进行代理之后,调用List接口的方法会被拦截
        如果使用的size,get方法,没有对集合进行修改,则允许执行
        如果使用的add,remove,set方法,对集合进行了修改,则抛出运行时异常
  • 动态代理代码实现

    java.util.Collections:操作集合的工具类
     
    static  List unmodifiableList(List list)
            返回指定列表的不可修改视图。
            此方法允许模块为用户提供对内部列表的“只读”访问。
            在返回的列表上执行的查询操作将“读完”指定的列表。
            试图修改返回的列表(不管是直接修改还是通过其迭代器进行修改)将导致抛出         
            UnsupportedOperationException:不支持操作异常
    unmodifiableList作用:传递List接口,方法内部对List接口进行代理,返回一个被代理后的List接口
            对List进行代理之后,调用List接口的方法会被拦截
            如果使用的size,get方法,没有对集合进行修改,则允许执行
            如果使用的add,remove,set方法,对集合进行了修改,则抛出运行时异常public class Demo01Proxy {
        @Test
        public void show(){
            //使用多态创建List集合,并添加元素
            List list = new ArrayList();
            list.add("a");
            list.add("b");
            list.add("c");
            //调用Collections中的方法unmodifiableList对List集合进行代理
            list = Collections.unmodifiableList(list);
            //如果使用的size,get方法,没有对集合进行修改,则允许执行
            System.out.println(list.size());
            System.out.println(list.get(1));
            //如果使用的add,remove,set方法,对集合进行了修改,则抛出运行时异常
            //list.add("d");//UnsupportedOperationException
            //list.remove(0);//UnsupportedOperationException
            list.set(1, "www");//UnsupportedOperationException
        }
    }
    

动态代理的重点类和方法

java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,这个类提供了一个静态方法来为一组接口的实现类动态地生成代理类及其对象。(这个类必须是为有接口的被代理类提供动态代理)

  • newProxyInstance方法的三个参数的详解:
    该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例

    public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
     
    三个参数:
    1、第一个参数, 是类加载器, 一般使用和被代理对象一样类加载器(其实可以使用任何类的加载器) obj.getClass().getClassLoader()目标对象通过getClass方法获取类的所有信息后,调用getClassLoader() 方法来获取类加载器。获取类加载器后,可以通过这个类型的加载器,在程序运行时,将生成的代理类加载到JVM即Java虚拟机中,以便运行时需要! 
    
    2、第二个参数, 被代理对象实现的所有接口的字节码文件数组obj.getClass().getInterfaces()获取被代理类的所有接口信息,以便于生成的代理类可以具有代理类接口中的所有方法. 
    
    3、第三个参数, 处理类对象(用接口多态接收的), 用于拦截我们调用的所有方法, 判断是否应该处理代理对象的真实方法. InvocationHandler 这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类方法的处理以及访问.
    
    
    • InvocationHandler接口的invoke方法及参数详解

      public Object invoke(Object proxy, Method method, Object[] args) 这个方法就是用于拦截的
      1、Object proxy: 方法内部创建的代理人对象。
      2、Method method:方法内部会使用反射技术,自动被代理人使用的方法。
      3、Object[] args:被代理方法中的参数。这里因为参数个数不定,所以用一个对象数组来表示。

动态代理模拟的综合案例

模仿Collections中定义的动态代理方法unmodifiableList

public class DynamicProxy {
    public static void main(String[] args) {
        ArrayList arr = new ArrayList<>();
        arr.add("小昧");
        arr.add("12");
        arr.add("唐人街");
        arr.add("爱跳");
        arr.add("爱吃");
        List list = unmodifiableList(arr);
     //   list.add("dddd");
     //   list.set(2,"wwww");
        System.out.println(list.get(3));

    }

    public static List unmodifiableList(List arr) {
      List list = (List) Proxy.newProxyInstance(
                arr.getClass().getClassLoader(),
                arr.getClass().getInterfaces(),
                new InvocationHandler() {
                    //这个方法就是用来拦截真实对象方法
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        String name = method.getName();
                        //拦截 add,set,remove开头的方法
                        if (name.startsWith("add")) {
                            throw new UnsupportedOperationException("add方法禁止访问");
                        }
                        if (name.startsWith("set")) {
                            throw new UnsupportedOperationException("set方法禁止访问");
                        }
                        if (name.startsWith("remove")) {
                            throw new UnsupportedOperationException("remove方法禁止访问");
                        }

                        //正常执行的查询方法
                        Object result = method.invoke(arr, args);
                        return result;
                    }
                });
      return list;
    }
    //集合工具类自带的unmodifiableList方法
    public static void method() {
        ArrayList arr = new ArrayList<>();
        arr.add("小昧");
        arr.add("12");
        arr.add("唐人街");
        arr.add("爱跳");
        arr.add("爱吃");

        List list = Collections.unmodifiableList(arr);
     // list.add("haha");
        System.out.println(list.get(3));
     // list.remove(2);
    }
}

动态代理的优缺点

  • 优点:

    动态代理非常的灵活,可以为任意的接口实现类对象做代理

    动态代理可以为被代理对象的所有接口的所有方法做代理,动态代理可以在不改变方法源码的情况下,实现对方法功能的增强,

    动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。 动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。

    动态代理同时也提高了开发效率。

  • 缺点:

    只能针对接口的实现类做代理对象,普通类是不能做代理对象的。(以后所学的第三方类 CGLib 可以为没有接口的普通类做代理)

Lombok

介绍

是个第三方jar包

Lombok通过增加一些“处理程序”,可以让java变得简洁、快速。

Lombok能以注解形式来简化java代码,提高开发效率。开发中经常需要写的javabean,都需要花时间去添加相应的getter/setter,也许还要去写构造器、equals等方法,而且需要维护。

Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。出现的神奇就是在源码中没有getter和setter方法,但是在编译生成的字节码文件中有getter和setter方法。这样就省去了手动重建这些代码的麻烦,使代码看起来更简洁些。

工厂设计模式

工厂设计模式的概述

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。之前我们创建类对象时, 都是使用new 对象的形式创建, 除new 对象方式以外, 工厂模式也可以创建对象.

把创建对象的过程交给工厂执行, 只从工厂中获取对象即可.

工厂模式作用

解决类与类之间的耦合问题

工厂模式步骤

  1. 提供一个所有类的父类/接口,
  2. 令所有类都实现这个接口并且重写接口内的方法
  3. 提供一个专门生产这些实现类对象的类(称为工厂类)
  4. 在测试类中调用工厂类的生产实现类对象的方法

工厂模式代码实现

  1. 编写一个Car接口, 提供run方法
public interface Car {
    public void run();
}
  1. 编写一个Falali类实现Car接口,重写run方法
public class Falali implements Car {
    @Override
    public void run() {
        System.out.println("法拉利以每小时500公里的速度在奔跑.....");
    }
}
  1. 编写一个Benchi类实现Car接口
public class Benchi implements Car {
    @Override
    public void run() {
        System.out.println("奔驰汽车以每秒1米的速度在挪动.....");
    }
}
  1. 提供一个CarFactory(汽车工厂),用于生产汽车对象
public class CarFactory {
    /**
     * @param id : 车的标识
     *           benchi : 代表需要创建Benchi类对象
     *           falali : 代表需要创建Falali类对象
     *           如果传入的车标识不正确,代表当前工厂生成不了当前车对象,则返回null
     * @return
     */
    public Car createCar(String id){
        if("falali".equals(id)){
            return new Falali();
        }else if("benchi".equals(id)){
            return new Benchi();
        }
        return null;
    }
}
  1. 定义CarFactoryTest测试汽车工厂
public class CarFactoryTest {
    public static void main(String[] args) {
        CarFactory carFactory = new CarFactory();
        Car benchi = carFactory.createCar("benchi");
        benchi.run();
        Car falali = carFactory.createCar("falali");
        falali.run();
    }
}

可以使用配置文件达到0耦合

car.properties
id=3
       
public class TestCar {
    public static void main(String[] args) throws IOException {
        //0.从配置文件去读id
        Properties ps = new Properties();
        //加载配置文件
        ps.load(new FileInputStream("day23/car.properties"));
        //取出数据
        String id = ps.getProperty("id");
        //转成int
        int ID = Integer.parseInt(id);
        //1.创建一辆汽车 高耦合  低耦合
        Car aCar = CarFactory.getACar(ID);
        aCar.run();
    }
}   

今日总结

  • 什么是设计模式

    设计模式是软件开发人员在软件开发过程中面临的一般性问题的解决方案。
    
    使用设计模式是为了重用代码,保证代码可靠性和架构的可扩展性。
        
    设计模式总共有23种。这些模式可以分为以下三大类:
        创建型模式,
        结构型模式,
        行为型模式
    
    设计模式的六大原则
    1、开闭原则
      对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,需要使用接口和抽象类。
    
    2、里氏代换原则
      里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。里氏代换原则是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
    
    3、依赖倒转原则
      这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
    
    4、接口隔离原则
      意思是使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
    
    5、迪米特法则
      又称最少知道原则,是指一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
    
    6、合成复用原则
      尽量使用合成/聚合的方式,而不是使用继承。
    
  • 单例模式

    • 饿汉式

      public class Singleton {
      
          //1.创建静态不可变对象
          private static final Singleton single = new Singleton();
      
          //2.私有构造方法
          private Singleton(){}
      
          //3.提供静态方法,获取该单例对象
          public static Singleton getInstance(){
              return single;
          }
      
      }
      
      优点:
          实现简单,可以保证多线程下的单例。
          因为对象在类加载时已经完成创建,所以getInstance方法性能也较高。
          
      缺点:
          无法实现懒加载,对象创建出来后不一定使用,
          如果类中的成员比较多,而且占用资源比较大,则会造成空间浪费。
      
    • 懒汉式

      • (同步方法)
      /*
          懒汉式单例:同步方法,保证多线程下安全的单例
       */
      public class ThreadSafeSingleton {
          //1. 定义单例对象(只定义,使用时才创建)
          private static ThreadSafeSingleton single;
      
          //2. 私有构造方法
          private ThreadSafeSingleton(){}
      
          //3. 提供静态方法,获取该单例对象
          //(为了保证线程安全,使用synchronized对方法加锁)
          public static synchronized ThreadSafeSingleton getInstance(){
              //第一次调用该方法时创建对象,以后都不会再次创建
              if(single==null){
                  single = new ThreadSafeSingleton();
              }
              return single;
          }
      }
      
      优点:
          实现懒加载,对象使用时才创建,节约内存空间。
          保证多线程下也只会创建一个对象。
          
      缺点:
          getInstance使用了方法锁,每次调用都会造成阻塞,高并发下性能低下。
      
      • 双重检验
      //双重校验加锁机制,提升方法效率
      public class DoubleCheckSingleton {
      
          //为了保证对象可见性和在创建的时候不会出现指令重排,需要加上volatile
          private static volatile DoubleCheckSingleton single =null;
      
          private DoubleCheckSingleton(){}
      
          public static DoubleCheckSingleton getInstance(){
      
              //只有第一次创建时才会进入第一层判断,进行锁的竞争
              //解决了每次调用都会阻塞的问题,很大程度提高了性能
              if(single==null) {
                  synchronized (DoubleCheckSingleton.class) {
                      if (single == null) {
                          single = new DoubleCheckSingleton();
                      }
                  }
              }
              return single;
          }
      }
      
  • 多例模式

    特点:
    1:多例可以有多个实例
    2: 多例类必须能够自我创建并管理自己的实例,并且向外界提供自己的实例
       
        
    public class Pig {
        private static final  int COUNT = 4;
    
        private Pig(){}
    
        private static ArrayList list = new ArrayList<>();
    
        static {
            for (int i = 0; i < COUNT; i++) {
                list.add(new Pig());
            }
        }
    
        public static Pig getInstance(){
            int index = new Random().nextInt(COUNT);
            Pig pig = list.get(index);
            return pig;
        }
    }
    
  • 动态代理

    JDK动态代理 :基于接口的代码。
    cglib动态代理 : 基于继承的代理,动态生成一个要代理类的子类
    
    JDK动态代理
    要求:JDK代理只能对实现接口的类生成代理。
    原理:利用反射机制在运行时创建代理类。
    
    java.lang.reflect.Proxy
      这是Java动态代理机制的主类,它提供了一个静态方法来为一组接口的实现类动态地生成代理类及其对象。
      
    newProxyInstance方法:
      该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
      
    public static Object newProxyInstance(ClassLoader loader, 
                                       Class[] interfaces,
                                       InvocationHandler handle)
    参数详解:
    1、ClassLoader loader :类加载器
      传参时通常这样写:obj.getClass().getClassLoader()
      被代理对象obj,通过getClass方法获取类的所有信息后,调用getClassLoader()方法来获取类加载器。
      获取类加载器后,可以通过这个类型的加载器,在程序运行时,将生成的代理类加载到JVM即Java虚拟机中,以便运行时需要!
      
      
    2、Class[] interfaces :代理类实现的所有接口的Class数组
      传参时通常这样写:obj.getClass().getInterfaces()
      被代理对象obj,获取obj的所有接口信息,以便于生成的代理类可以具有代理类接口中的所有方法。
      
      
    3、InvocationHandler handle : 处理器接口
      一般传入该接口的匿名内部类,实现里面的invoke方法。
      InvocationHandler处理器接口,它有一个invoke方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对被代理类方法的处理以及访问。
      
    InvocationHandler接口的invoke方法:
    方法定义:
      public Object invoke(Object proxy, Method method, Object[] args)
    参数说明:    
      Object proxy: 方法内部创建的代理对象。(通常不会去使用)
      Method method:被代理类中的方法的Method类型,用于获取和调用被代理类原来的方法。
      Object[] args:被代理方法中的参数。因为参数个数和类型不定,所以用Object数组表示。
    
    • 代理卖房子的例子

      //接口,定义了卖房子的功能
      public interface SaleHourse {
          //卖房子
          void sale();
          
          //其他工作
          void doWork();
      }
      
      
      //被代理类:需要卖房子的业主
      public class Person implements SaleHourse {
          @Override
          public void sale() {
              System.out.println("周末还要自己约人看房,卖房子...");
          }
      
          //自己的工作
          @Override
          public void doWork(){
              System.out.println("周一到周五上班中...");
          }
      }
      
      
      //代理卖房子的业务
      public class InvocationHandleImpl implements InvocationHandler {
          private SaleHourse sale;
          public InvocationHandleImpl(SaleHourse sale){
              this.sale = sale;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              //获取执行的方法名字,只针对卖房子的事情进行代理
              String name = method.getName();
              if("sale".equals(name)){
                  System.out.println("中介代理人介入,代理卖房子的事情...");
                  //如果此处需要执行原来的功能,就用反射调用,否则就返回就行
                  //return method.invoke(sale);
                  return null;
              }
              //反射调用,让被代理类Person的其他方法可以正常执行
              return method.invoke(sale,args);
          }
      }
      
      
      //测试类
      public class ProxyDemo {
          public static void main(String[] args) {
              //创建被代理对象
              SaleHourse p = new Person();
              
              //创建代理器(代理的流程)
              InvocationHandler handler = new InvocationHandleImpl(p);
              
              //调用Proxy的方法,产生代理对象
              SaleHourse zhongjie = (SaleHourse)Proxy.newProxyInstance(
                                      //p.getClass().getClassLoader(),
                                      ProxyDemo.class.getClassLoader(),
                                      p.getClass().getInterfaces(),
                                      handler);
      
              //代理对象执行代理功能
              zhongjie.sale();
              zhongjie.doWork();
          }
      }
      
  • 工程模式

    • 简单工厂模式

      简单工厂模式优缺点
      好处:
          将对象的创建交给专门的工厂类负责,隐藏了对象创建的细节,实现对象创建和对象使用的分离。
      缺点:
          不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。
          
          
      关于工厂类的方法是否是静态方法的区别?
          如果创建对象的工厂方法是静态的,我们称为静态工厂。静态工厂的好处是不需要创建对象就可以调用方法。缺点是无法通过继承来改变创建方法的行为
      
    • 举例:使用手机工厂创建不同的手机类对象

      //手机类接口
      public interface Phone {
          void call();
      }
      
      //苹果手机
      public class IPhone implements Phone {
          @Override
          public void call() {
              System.out.println("苹果手机打电话...");
          }
      }
      
      //小米手机
      public class MiPhone implements Phone {
          @Override
          public void call() {
              System.out.println("小米手机打电话");
          }
      }
      
      //简单工厂模式
      //手机工厂,产生IPhone和MiPhone对象
      public class PhoneFactory {
          //根据标识(手机名称),产生对应的对象
          public static Phone makePhone(String name){
              if("IPhone".equals(name)){
                  return new IPhone();
              }else if("MiPhone".equals(name)){
                  return new MiPhone();
              }
              return null;
          }
      }
      
      //测试类
      public class SimpleFactoryDemo {
          public static void main(String[] args) {
              //使用手机工厂创建苹果手机
              Phone iPhone = PhoneFactory.makePhone("IPhone");
              //使用手机工厂创建小米手机
              Phone miPhone = PhoneFactory.makePhone("MiPhone");
              iPhone.call();
              miPhone.call();
          }
      }
      
      
  • 工厂方法模式

      对比简单工厂模式,该模式将具体的工厂换成抽象的工厂类,工厂中创建对象的方法也是抽象的,用于规定该工厂的职责和功能,具体的创建对象的事情交给具体的工厂去完成。
      工厂方法的好处是当系统需要新增一个产品时,无需修改现有系统代码,只需添加一个具体产品类和其对应的具体工厂即可。这样很好的提高了系统的扩展性,符合面向对象编程的开闭原则。
    
    
    • 举例:使用工厂方法模式创建不同的手机类对象

      //手机类接口
      public interface Phone {
          void call();
      }
      
      //苹果手机
      public class IPhone implements Phone {
          @Override
          public void call() {
              System.out.println("苹果手机打电话信号稳定...");
          }
      }
      
      //小米手机
      public class MiPhone implements Phone {
          @Override
          public void call() {
              System.out.println("小米手机打电话音质清晰...");
          }
      }
      
      
      /*
      工厂方法模式:
         工厂类定义成抽象类,提供抽象的创建手机的工厂方法。
         具体生产手机的功能由具体的手机工厂实现。
      */
      
      //抽象手机工厂
      public abstract class PhoneFactory {
          //抽象的生产手机方法,由具体手机工厂实现
          public abstract Phone makePhone();
      }
      
      //苹果手机工厂
      public class IPhoneFactory extends PhoneFactory {
          @Override
          public Phone makePhone() {
              return new IPhone();
          }
      }
      
      //小米手机工厂
      public class MiPhoneFactory extends PhoneFactory {
          @Override
          public Phone makePhone() {
              return new MiPhone();
          }
      }
      
      //测试类
      public class MethodFactoryDemo {
          public static void main(String[] args) {
              //创建苹果手机工厂
              PhoneFactory ipFactory = new IPhoneFactory();
              //创建小米手机工厂
              PhoneFactory miFactory = new MiPhoneFactory();
      
              //调用各自工厂的方法生产手机对象
              Phone iphone = ipFactory.makePhone();
              Phone miPhone = miFactory.makePhone();
      
              //执行打电话功能
              iphone.call();
              miPhone.call();
          }
      }
      
  • 抽象工厂模式

    此模式是对工厂方法模式的进一步扩展。
      在工厂方法模式中,一个具体的工厂负责生产一类具体的产品,即一对一的关系,但是,如果需要一个具体的工厂生产多种产品对象,那就需要用到抽象工厂模式。
    
  • 直播问答

    Q1、北京昌平-陈骥问:已发布老师 放入src包里的文件我用绝对路径可以read么
    王一平老师答:可以
    
    Q2、西安-杨森问:已发布创建本类的对象时, 为什么对其封装成private static
    王一平老师答:private为了不让外部访问,static因为是要让改变量变成静态的。因为返回唯一对象的方法是静态的。因为不用静态方法的话,外部不能创建对象,会访问不了实例方法
    
    Q3、北京顺义-乔丙田问:已发布工程里src目录对应bin目录(存放.class文件的目录),还是out文件夹对应bin目录啊?刚才詹老师不是说所有字节码文件都在out文件夹吗?
    张云飞老师答:idea会把每个模块打包成一个产品,这个产品里面就有src下所有的文件,这个产品就在out目录下。
    
    Q4、广州-谢港问:已发布如果不用static修饰的话,是不是创建的就是不同的对象,(不考虑能不能访问的问题)
    张云飞老师答:是的。静态不属于对象,属于类。因此是唯一的一个。
    
    Q5、北京顺义-孟博甜问:已发布老师可以讲一下懒汉模式的双检查锁版本
    詹英鹏答:可以课下单独聊
    
    Q6、郑州_张欢问:已发布dog对象和cat对象的地址值怎么一样?
    张云飞老师答:两个对象同时存在的话。不会一样的。
    
    Q7、北京昌平-薛宇新问:已发布静态代码块比创建list运行的早,为什么不报错
    张云飞老师答:因为list也是静态的。执行时间是一样的。
    
    Q8、李俊邦问:已发布多例模式 会不会 10个结果里 只重复输出三个不同的地址 ,只输出0 1 2这三个索引的pig的地址值
    张云飞老师答:只是随机返回的话。有可能是这样。
    
    Q9、北京顺义-宋洪枭问:已发布静态代码块优先构造方法执行,为什么静态代码块可以获取比它后执行的变量?
    张云飞老师答:并没有,静态只能使用静态,不能使用非静态。代码款中的集合也是静态的。可以使用。
    
    Q10、深圳-张鸿阳问:已发布快捷键,按啥,出现所有方法?
    张云飞老师答:Ctrl+”+/-”,当前方法展开、折叠
    Ctrl+Shift+”+/-”,全部展开、折叠
    
    Q11、北京顺义-乔丙田问:已发布我为什么总感觉这个代理像个绑架者,它绑架了原来的对象(也就是被代理者),所有对被代理对象的操作都要经过绑架者的确认,绑架者让干啥,他才能干啥
    张云飞老师答:很形象,就是这个意思。
    
    Q12、北京顺义-宋洪枭问:已发布静态代码块优先构造方法执行,可以在静态代码块中构造对象?可以的话是为什么?
    张云飞老师答:静态不能调用非静态是针对的一个类的成员来说的。静态方法不能调用非静态的方法。但是创建对象这个动作在哪里都是可以的。没有静态非静态的区别。
    
    Q13、深圳-高增良问:已发布代理模式什么情况下允许调用什么情况下不允许调用
    张云飞老师答:这个是根据自己的业务设计的。不是惟一的。
    
    Q14、广州-张耿豪问:已发布既然不让调,为什么不直接给一个查看的方法就好? 还要给其它方法?
    张云飞老师答:这里重点演示的是动态代理有能力实现这样的功能。我们不是集合类的定义者。我们无法改变已经定义好的集合类。但是我们可以动过动态代理修改它的功能。
    
    Q15、北京昌平-韩克宇问:已发布在类里重写方法不用继承就可以直接用么?
    张云飞老师答:既然是重写,没有继承就会有实现。这两个满足一个就行。
    要不就没有重写的发生。
    
    Q16、北京昌平-韩克宇问:已发布用多态接收就是可以直接调用他重写的方法么
    张云飞老师答:多态:编译看左边,运行看右边。运行时刻走的是子类重写后的方法。
    
    Q17、北京顺义-张奥问:已发布类加载器 是什么
    张云飞老师答:类加载器是用于把class文件加载进内存的对象。
    可以复习一下,反射那天一开始就讲了。类加载器。
    
    

你可能感兴趣的:(3/31day23_设计模式)