今日目标

  • 网络编程
  • TCP通信
  • 单例设计模式
  • 多例设计模式
  • 工厂设计模式

    1 网络编程

    1.1 软件架构

  • C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件

  • B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等
  • 两种架构各有优势,但是都离不开网络的支持。网络编程 , 就是在一定的协议下,实现两台计算机的通信的程序
    day12 网络编程,单例,多例,工厂设计模式 - 图1

    1.2 什么是网络编程

  • 在网络通信协议下,不同计算机上运行的程序,可以进行数据传输

    1.3 网络编程三要素

  • IP地址 : 设备在网络中的地址,是唯一的标识。

  • 端口 : 是设备中程序的是唯一的标识。
  • 协议:数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。

    1.4 IP地址

  • IP:全称”互联网协议地址”,也称IP地址。是分配给上网设备的数字标签。常见的IP分类为:ipv4和ipv6
    简单来说 : 就是设备在网络中的唯一标识 , 想要连接哪一台电脑 , 就找到此电脑在网络中的ip地址

  • IP地址常见分类 : ipv4和ipv6
  • 常用命令:
    • ipconfig:查看本机IP地址 (mac linux系统 : ifconfig)
    • ping 地址:检查网络是否连通
  • 特殊IP地址:
    • 127.0.0.1:是回送地址也称本地回环地址,可以代表本机的IP地址,一般用来测试使用
  • 为了方便我们对IP地址的获取和操作,Java提供了一个类InetAddress 供我们使用InetAddress:此类表示Internet协议(IP)地址 | static InetAddress getByName(String host) | 在给定主机名的情况下确定主机的 IP 地址 | | —- | —- | | String getHostName() | 获取此 IP 地址的主机名 | | String getHostAddress() | 返回 IP 地址字符串(以文本表现形式)。 |

1.5 端口

  • 端口:应用程序在设备中唯一的标识。
  • 端口号:应用程序的唯一标识方式 , 用两个字节表示的整数,它的取值范围是0~65535。
    其中0~1023之间的端口号用于一些知名的网络服务或者应用。
    我们自己使用1024以上的端口号就可以了。
  • 注意:一个端口号只能被一个应用程序使用。

    1.6 通信协议

  • 协议:计算机网络中,连接和通信的规则被称为网络通信协议

  • UDP协议
    • 用户数据报协议(User Datagram Protocol)
    • UDP是面向无连接通信协议。
    • 速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据。
  • TCP协议

    • 传输控制协议 (Transmission Control Protocol)
    • TCP协议是面向连接的通信协议。
    • 速度慢,没有大小限制,数据安全

      2 TCP通信

      2.1 TCP发送数据

      1. package com.itheima.tcp_demo.demo1;
      2. import java.io.IOException;
      3. import java.io.OutputStream;
      4. import java.net.Socket;
      5. /*
      6. TCP协议 : 面向连接
      7. 客户端 : 发送数据
      8. 发送数据的步骤
      9. 1 创建客户端的Socket对象 : Socket(String host, int port) 与指定服务端连接
      10. 参数说明:
      11. host 表示服务器端的主机名,也可以是服务器端的IP地址,只不过是String类型的
      12. port 表示服务器端的端口
      13. 2 通获Socket对象取网络中的输出流,写数据
      14. OutputStream getOutputStream()
      15. 3 释放资源
      16. void close()
      17. */
      18. public class ClientDemo {
      19. public static void main(String[] args) throws IOException {
      20. //1 创建客户端的Socket对象
      21. Socket socket = new Socket("127.0.0.1",8888);
      22. //启动客户端前,要先启动服务端,这样才能建立连接
      23. //对象能创建成功,意味着三次握手就成立!!
      24. // 2 通获Socket对象取网络中的输出流,写数据
      25. OutputStream netOut = socket.getOutputStream();
      26. //发送信息给服务端
      27. netOut.write("Hello 在么!晚上一起去撸串!".getBytes());
      28. //3 释放资源
      29. socket.close();
      30. }
      31. }

      2.2 TCP接收数据

      1. package com.itheima.tcp_demo.demo1;
      2. import java.io.IOException;
      3. import java.io.InputStream;
      4. import java.net.ServerSocket;
      5. import java.net.Socket;
      6. import java.net.SocketAddress;
      7. /*
      8. 基于TCP协议
      9. 服务端接收数据 :
      10. 1 创建服务器端的Socket对象 : ServerSocket类
      11. ServerSocket(int port) : 构造方法需要绑定一个端口号 , port就是端口号
      12. 2 监听客户端连接,并接受连接,返回一个Socket对象
      13. Socket accept() : 该方法会一直阻塞直到建立连接
      14. 3 获取网络中的输入流,用来读取客户端发送过来的数据
      15. InputStream getInputStream()
      16. 4 释放资源 : 服务端一般不会关闭
      17. void close()
      18. */
      19. public class ServerDemo {
      20. public static void main(String[] args) throws IOException {
      21. // 1 创建服务器端的Socket对象 : ServerSocket类
      22. ServerSocket serverSocket = new ServerSocket(8888);
      23. while (true) { //让服务端持续服务
      24. // 2 监听客户端连接,并接受连接,返回一个Socket对象
      25. System.out.println("等待客户端连接中.......");
      26. Socket socket = serverSocket.accept();
      27. //socket 用来和客户端的socket对象进行交互
      28. // 客户端发送数据,服务端收数据
      29. // 服务端发数据,客户端收数据
      30. //获取客户端的IP信息
      31. SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
      32. System.out.println("remoteSocketAddress = " + remoteSocketAddress);
      33. //3 获取网络中的输入流,用来读取客户端发送过来的数据
      34. InputStream netIn = socket.getInputStream();
      35. //读取数据
      36. byte[] buf = new byte[1024];
      37. int len = netIn.read(buf);
      38. System.out.println("收到客户端发送的数据:" + new String(buf, 0, len));
      39. // 4 释放资源 : 服务端一般不会关闭
      40. socket.close();
      41. //serverSocket.close();//不用关,保证服务端7x24小时不间断执行
      42. }
      43. }
      44. }

      2.3 TCP通信原理分析

      image.png

      2.4 TCP三次握手

      image.png

      2.5 TCP练习1

      实现客户端和服务端的字符串数据交互
      1. package com.itheima.tcp_demo.Demo2;
      2. import java.io.IOException;
      3. import java.io.InputStream;
      4. import java.io.OutputStream;
      5. import java.net.Socket;
      6. /*
      7. TCP协议 : 面向连接
      8. 客户端 : 发送数据
      9. 发送数据的步骤
      10. 1 创建客户端的Socket对象 : Socket(String host, int port) 与指定服务端连接
      11. 参数说明:
      12. host 表示服务器端的主机名,也可以是服务器端的IP地址,只不过是String类型的
      13. port 表示服务器端的端口
      14. 2 通获Socket对象取网络中的输出流,写数据
      15. OutputStream getOutputStream()
      16. 3 通过Socket获取输入流,读取服务端传过来的数据
      17. InputStream getInputStream()
      18. 4 释放资源
      19. void close()
      20. */
      21. public class ClientDemo {
      22. public static void main(String[] args) throws IOException {
      23. //1 创建客户端的Socket对象
      24. Socket socket = new Socket("127.0.0.1", 8888);
      25. //启动客户端前,要先启动服务端,这样才能建立连接
      26. //对象能创建成功,意味着三次握手就成立!!
      27. // 2 通获Socket对象取网络中的输出流,写数据
      28. OutputStream netOut = socket.getOutputStream();
      29. //发送信息给服务端
      30. netOut.write("Hello 在么!晚上一起去撸串!".getBytes());
      31. //3 通过Socket获取输入流,读取服务端传过来的数据
      32. InputStream netIn = socket.getInputStream();
      33. byte[] buf = new byte[1024];
      34. int len = netIn.read(buf);
      35. System.out.println("收到服务端的数据:" + new String(buf, 0, len));
      36. //4 释放资源
      37. socket.close();
      38. }
      39. }
      package com.itheima.tcp_demo.Demo2;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.net.ServerSocket;
      import java.net.Socket;
      import java.net.SocketAddress;
      /*
      基于TCP协议
      服务端接收数据 :
      1 创建服务器端的Socket对象 : ServerSocket类
         ServerSocket(int port)  : 构造方法需要绑定一个端口号 , port就是端口号
      2 监听客户端连接,并接受连接,返回一个Socket对象
         Socket accept() : 该方法会一直阻塞直到建立连接
      3 获取网络中的输入流,用来读取客户端发送过来的数据
         InputStream getInputStream()
      4 获取网络中的输出流,用来发送数据给客户端
         OutputStream getOutputStream()
      5 释放资源 : 服务端一般不会关闭
         void close()
      */
      public class ServerDemo {
      public static void main(String[] args) throws IOException {
         //  1 创建服务器端的Socket对象 : ServerSocket类
         ServerSocket serverSocket = new ServerSocket(8888);
         while (true) { //让服务端持续服务
             // 2 监听客户端连接,并接受连接,返回一个Socket对象
             System.out.println("等待客户端连接中.......");
             Socket socket = serverSocket.accept();
             //socket 用来和客户端的socket对象进行交互
             // 客户端发送数据,服务端收数据
             // 服务端发数据,客户端收数据
             //获取客户端的IP信息
             SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
             System.out.println("remoteSocketAddress = " + remoteSocketAddress);
             //3 获取网络中的输入流,用来读取客户端发送过来的数据
             InputStream netIn = socket.getInputStream();
             //读取数据
             byte[] buf = new byte[1024];
             int len = netIn.read(buf);
             System.out.println("收到客户端发送的数据:" + new String(buf, 0, len));
             // 4 获取网络中的输出流,用来发送数据给客户端
             OutputStream netOut = socket.getOutputStream();
             netOut.write("可以呀!时间,地址,人物!!".getBytes());
             // 5 释放资源 : 服务端一般不会关闭
             socket.close();
             //serverSocket.close();//不用关,保证服务端7x24小时不间断执行
         }
      }
      }
      

      2.6 TCP练习2

      实现文件从客户端上传到服务端
      package com.itheima.tcp_test1;
      import java.io.FileInputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.net.Socket;
      /*
      需求 : 文件上传和下载
      客户端
      */
      public class ClientDemo {
      public static void main(String[] args) throws IOException {
         //1.创建客户端对象
         Socket socket = new Socket("127.0.0.1", 6666);
         //2.获取网络输出流
         OutputStream netOut = socket.getOutputStream();
         //3.获取本地文件的输入流
         FileInputStream localIn = new FileInputStream("day12_demo/client/caocao.jpg");
         //4.边读,边写
         //把文件的信息发送给服务端
         int len;
         byte[] buf = new byte[1024];
         while ((len = localIn.read(buf)) != -1) {
             netOut.write(buf, 0, len);
         }
         //5. 告诉对方,数据发送完毕。关闭输出流
         socket.shutdownOutput();// 对方read就会得到-1
      //        netOut.close();
         //6. 获取网络输入流,读取服务端返回的数据
         InputStream netIn = socket.getInputStream();
         len = netIn.read(buf);
         System.out.println(new String(buf, 0, len));
         //7. 释放资源
         localIn.close();
         socket.close();
      }
      }
      
      package com.itheima.tcp_test1;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.net.ServerSocket;
      import java.net.Socket;
      /*
      需求 : 文件上传和下载
      服务端
      1 创建服务器端的Socket对象 : ServerSocket类
         ServerSocket(int port)  : 构造方法需要绑定一个端口号 , port就是端口号
      2 监听客户端连接,并接受连接,返回一个Socket对象
         Socket accept() : 该方法会一直阻塞直到建立连接
      3 获取网络中的输入流,用来读取客户端发送过来的数据
         InputStream getInputStream()
       获取本地输出流,用来保存客户端发送的数据到文件中
      4 获取网络中的输出流,用来发送数据给客户端
         OutputStream getOutputStream()
      5 释放资源 : 服务端一般不会关闭
         void close()
      */
      public class ServerDemo {
      public static void main(String[] args) throws IOException {
         // 1 创建服务器端的Socket对象 : ServerSocket类
         ServerSocket serverSocket = new ServerSocket(6666);
         while (true) {
             // 2 监听客户端连接,并接受连接,返回一个Socket对象
             System.out.println("等待客户端连接!!");
             Socket socket = serverSocket.accept();
             //3 获取网络中的输入流,用来读取客户端发送过来的数据
             InputStream netIn = socket.getInputStream();
             //获取本地输出流,用来保存客户端发送的数据到文件中
             FileOutputStream localOut = new FileOutputStream("day12_demo/server/caocao.jpg");
             System.out.println("文件上传开始!!");
             byte[] buf = new byte[1024];
             int len;
             while ((len = netIn.read(buf)) != -1) {
                 localOut.write(buf, 0, len);
             }
             System.out.println("文件上传完毕!!");
             // 4 获取网络中的输出流,用来发送数据给客户端
             OutputStream netOut = socket.getOutputStream();
             netOut.write("哥们,你的文件已上传车成功!!".getBytes());
             //5 释放资源 : 服务端一般不会关闭
             localOut.close();//本地流
             socket.close();
         }
      }
      }
      

      2.7 TCP练习3

      文件名覆盖优化,线程优化
      package com.itheima.tcp_test1;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.net.ServerSocket;
      import java.net.Socket;
      import java.text.SimpleDateFormat;
      /*
      优化:文件被覆盖的问题
      解决方案:让每次的名字都发生变化
      */
      public class ServerBetter2 {
      public static void main(String[] args) throws IOException {
         // 1 创建服务器端的Socket对象 : ServerSocket类
         ServerSocket serverSocket = new ServerSocket(6666);
         while (true) {
             // 2 监听客户端连接,并接受连接,返回一个Socket对象
             System.out.println("等待客户端连接!!");
             Socket socket = serverSocket.accept();
             new Thread(new UploadTask(socket)).start();
         }
      }
      }
      /*
      上传任务
      */
      class UploadTask implements Runnable {
      static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
      private final Socket socket;
      public UploadTask(Socket socket) {
         this.socket = socket;
      }
      @Override
      public void run() {
         FileOutputStream localOut = null;
         try {
             //3 获取网络中的输入流,用来读取客户端发送过来的数据
             InputStream netIn = socket.getInputStream();
             //获取本地输出流,用来保存客户端发送的数据到文件中
             /*FileOutputStream localOut =
                     new FileOutputStream("day12_demo/server/"+ UUID.randomUUID()+".jpg");
             //UUID.randomUUID() 生成不同的随机的字符串,让文件名不会重复
             */
             localOut =
                     new FileOutputStream("day12_demo/server/" + sdf.format(System.currentTimeMillis()) + ".jpg");
             System.out.println("文件上传开始!!");
             byte[] buf = new byte[1024];
             int len;
             while ((len = netIn.read(buf)) != -1) {
                 localOut.write(buf, 0, len);
             }
             System.out.println("文件上传完毕!!");
             // 4 获取网络中的输出流,用来发送数据给客户端
             OutputStream netOut = socket.getOutputStream();
             netOut.write("哥们,你的文件已上传车成功!!".getBytes());
         } catch (Exception e) {
             System.out.println("上传有误:" + socket.getRemoteSocketAddress() + e);
         } finally {
             //5 释放资源 : 服务端一般不会关闭
             try {
                 if (localOut != null)
                     localOut.close();//本地流
             } catch (IOException e) {
                 e.printStackTrace();
             }
             try {
                 socket.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
      }
      }
      
      线程池优化
      package com.itheima.tcp_test1;
      import java.io.IOException;
      import java.net.ServerSocket;
      import java.net.Socket;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      /**
      * 线程池优化服务端
      */
      public class ServerBetter3 {
      public static void main(String[] args) throws IOException {
         //先定义一个线程池
         ExecutorService pool = Executors.newFixedThreadPool(50);
         // 1 创建服务器端的Socket对象 : ServerSocket类
         ServerSocket serverSocket = new ServerSocket(6666);
         while (true) {
             // 2 监听客户端连接,并接受连接,返回一个Socket对象
             System.out.println("等待客户端连接!!");
             Socket socket = serverSocket.accept();
             //创建任务给线程
             //new Thread(new UploadTask(socket)).start();
             //创建任务给线程池
             pool.submit(new UploadTask(socket));
         }
      }
      }
      

      3 单例设计模式

      学习目标

  • 能够使用单例设计模式设计代码

    内容讲解

  • 正常情况下一个类可以创建多个对象

    public static void main(String[] args) {
      // 正常情况下一个类可以创建多个对象
      Person p1 = new Person();
      Person p2 = new Person();
      Person p3 = new Person();
    }
    
  • 如果说有时一个对象就能搞定的事情 , 非要创建多个对象 , 浪费内存!!!

    3.1 单例设计模式的作用

  • 单例模式,是一种常用的软件设计模式。通过单例模式可以保证项目中,应用该模式的这个类只有一个实例(对象)。
    即一个类只有一个对象实例。

  • 好处 :可以节省内存,共享数据

    3.2 单例设计模式实现步骤

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

    3.3 单例设计模式的类型

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

  4. 饿汉单例设计模式

  5. 懒汉单例设计模式

    3.4 饿汉单例设计模式

  • 饿汉单例设计模式就是使用类的时候已经将对象创建完毕
    不管以后会不会使用到该实例化对象,先创建了再说。很着急的样子,故被称为“饿汉模式”。
  • 代码如下:

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

    package com.itheima.singledesign;
    /*
      需求 : 使用单例模式(饿汉式) , 要求此类只能有一个对象
      步骤 :
          1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
          2. 在该类内部产生一个唯一的实例化对象,并且将其封装为private static 类型的成员变量。
          3. 定义一个静态方法返回这个唯一对象。
    */
    public class King {
      private String name;
      //1. 将构造方法私有化,
      private King(String name) {//当前类之外不能创建对象
          this.name = name;
      }
      //2. 在该类内部产生一个唯一的实例化对象,
      private static final King king = new King("秦始皇");
      //3. 定义一个静态方法返回这个唯一对象。
      public static King getKing() {
          return king;
      }
      @Override
      public String toString() {
          return "King{" +
                  "name='" + name + '\'' +
                  '}';
      }
    }
    

    =============================================================================================

    3.5 懒汉单例设计模式

  • 懒汉单例设计模式就是调用getInstance()方法时对象才被创建
    也就是说先不急着实例化出对象,等要用的时候才实例化出对象。不着急,故称为“懒汉模式”。
    代码如下:

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;
}

}

- ##### 注意 :
- 懒汉单例设计模式在多线程环境下可能会实例化出多个对象,不能保证单例的状态,所以加上关键字:synchronized,保证其同步安全。
- 需求:使用懒汉单例 ,改写皇帝类的单例模式
```java
package com.itheima.singledesign;
/*
    需求 : 使用单例模式(懒汉式) , 要求此类只能有一个对象
    步骤 :
        1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
        2. 在该类内部定义一个private static修饰的成员变量 . 此变量不需要赋值
        3. 定义一个静态方法返回这个唯一对象。 此方法需要加上synchronized关键字保证在多线程中也只有一个实例对象
 */
public class King2 {
    // 1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
    private King2() {
    }
    // 2. 在该类内部定义一个private static修饰的成员变量 . 此变量不需要赋值
    private static King2 king;// null
    // 3. 定义一个静态方法返回这个唯一对象。 此方法需要加上synchronized关键字保证在多线程中也只有一个实例对象
    public static synchronized King2 getInstance() {
        if (king == null) {
            king = new King2();
        }
        return king;
    }
}

知识小结

  • 单例模式可以保证系统中一个类只有一个对象实例。
  • 实现单例模式的步骤:

    • 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
    • 在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
    • 定义一个静态方法返回这个唯一对象。

      4 多例设计模式

      学习目标

  • 能使用多例设计模式设计代码

    内容讲解

    4.1 多例设计模式的作用

  • 多例模式,是一种常用的设计模式之一。通过多例模式可以保证项目中,应用该模式的类有固定数量的实例(对象)
    多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。

  • 使用场景:线程池

    线程池 = Executors.newFixedThreadPool(3);
    

    4.2.实现步骤

    1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
    2.在类中定义该类被创建对象的总数量
    3.在类中定义存放类实例的list集合
    4.在类中提供静态代码块,在静态代码块中创建类的实例
    5.提供获取类实例的静态方法

    4.3.实现代码

  • 某一个学科有固定3位老师,年级中上该课程的老师就是这三位老师其中一位
    要求使用多例模式 ,每次获取的都是这三位老师其中一位

    package com.itheima.moreinstance_demo;
    import java.util.ArrayList;
    import java.util.Random;
    /*
      需求  : 某一个学科有固定3位老师,年级中上该课程的老师就是这三位老师其中一位
              要求使用多例模式 ,每次获取的都是这三位老师其中一位
      实现步骤 :
          1.创建一个类,  将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
          2.在类中定义该类被创建对象的总数量
          3.在类中定义存放类实例的list集合
          4.在类中提供静态代码块,在静态代码块中创建类的实例
          5.提供获取类实例的静态方法
      枚举类型就是多例模式的一种实现方式
    */
    public class Teacher {
      private String name;
      //1 将构造方法私有化,
      private Teacher(String name) {
          this.name = name;
      }
      //2 创建对象的总数
      private static final int count = 3;
      //3 定义集合存储对象
      private static ArrayList<Teacher> teachers = new ArrayList<>();
      //4 在类中提供静态代码块中初始化
      static {
          teachers.add(new Teacher("小花"));//0
          teachers.add(new Teacher("小红"));//1
          teachers.add(new Teacher("昌老师"));//2
          /*for (int i = 0; i < count; i++) {
              teachers.add(new Teacher("老师" + i));
          }*/
      }
      //5 提供获取类实例的静态方法
      public static Teacher getInstance(){
          //new Random().nextInt(teachers.size())
          int i = new Random().nextInt(count);
          return teachers.get(i);
      }
      @Override
      public String toString() {
          return "Teacher{" +
                  "name='" + name + '\'' +
                  '}';
      }
    }
    class Demo{
      public static void main(String[] args) {
          for (int i = 0; i < 10; i++) {
              Teacher t = Teacher.getInstance();
              System.out.println("t = " + t);
          }
      }
    }
    

    4.4 小结

  • 多例模式作用 : 可以保证项目中一个类有固定个数的实例, 在实现需求的基础上, 能够提高实例的复用性.实现多例模式的步骤 :

    • 创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
    • 在类中定义该类被创建的总数量
    • 在类中定义存放类实例的list集合
    • 在类中提供静态代码块,在静态代码块中创建类的实例
    • 提供获取类实例的静态方法

      5 工厂设计模式

      学习目标

  • 能够使用工厂设计模式设计代码

    内容讲解

    5.1 概述

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

    5.2 作用

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

    5.3案例实践

  • 需求:定义汽车工厂类,生产各种品牌的车

  • 实现步骤
    • 编写一个Car接口, 提供run方法
    • 编写一个Falali类实现Car接口,重写run方法
    • 编写一个Benchi类实现Car接口,重写run方法
    • 提供一个CarFactory(汽车工厂),用于生产汽车对象
    • 定义CarFactoryTest测试汽车工厂

实现代码

package com.itheima.factorydesign_demo;
/*
  - 需求:定义汽车工厂类,生产各种品牌的车
  - 实现步骤
      - 编写一个Car接口, 提供run方法
      - 编写一个Falali类实现Car接口,重写run方法
      - 编写一个Benchi类实现Car接口
      - 提供一个CarFactory(汽车工厂),用于生产汽车对象
      - 定义CarFactoryTest测试汽车工厂
 */
public class CarTest {
    public static void main(String[] args) {
        //Falali f = new Falali();//耦合
        //使用工厂创建对象进行解耦
        Car benchi = CarFactory.makeCar(CarType.BENCHI);
        benchi.run();
        Car falali = CarFactory.makeCar(CarType.FALALI);
        falali.run();
    }
}
interface Car {
    void run();
}
class Falali implements Car {
    @Override
    public void run() {
        System.out.println("法拉利跑车,3秒破百!");
    }
}
class Benchi implements Car {
    @Override
    public void run() {
        System.out.println("奔驰车,5秒破百!");
    }
}
enum CarType {
    FALALI, BENCHI
}
//设计工厂类,生产对象
class CarFactory {
    public static Car makeCar(CarType carType) {
        switch (carType) {
            case BENCHI:
                return new Benchi();
            case FALALI:
                return new Falali();
            default:
                return null;
        }
    }
}

知识小结

  • 工厂模式的存在可以改变创建对象的方式,降低类与类之间的耦合问题.