• 【Java】网络编程,JUnit单元测试,设计模式


    day13-网络编程

    主要内容如下

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

    1 网络编程

    1.1 软件架构

    • C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件
    • B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等
    • 两种架构各有优势,但是都离不开网络的支持。网络编程 , 就是在一定的协议下,实现两台计算机的通信的程序

    1.2 什么是网络编程

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

    1.3 网络编程三要素

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

    1.4 IP地址

    • IP:全称”互联网协议地址”,也称IP地址。是分配给上网设备的数字标签。常见的IP分类为:ipv4和ipv6

      简单来说 : 就是设备在网络中的唯一标识 , 想要连接哪一台电脑 , 就找到此电脑在网络中的ip地址

    • IP地址常见分类 : ipv4和ipv6

    • 常用命令:

      • ipconfig:查看本机IP地址
      • IP地址:检查网络是否连通
    • 特殊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发送数据

    package com.itheima.tcp_demo.demo1;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.Socket;
    
    /*
        客户端 :
    
        发送数据的步骤
            1 创建客户端的Socket对象 : Socket(String host, int port) 与指定服务端连接
                参数说明:
                host 表示服务器端的主机名,也可以是服务器端的IP地址,只不过是String类型的
                port 表示服务器端的端口
    
            2 通获Socket对象取网络中的输出流,写数据
                OutputStream getOutputStream()
    
            3 释放资源
                void close()
    
     */
    public class ClientDemo {
        public static void main(String[] args) throws IOException {
            // 创建客户端的Socket对象(Socket) 与指定服务端连接
            Socket socket = new Socket("127.0.0.1", 10010);
    
            // 通获Socket对象取网络中的输出流,写数据
            OutputStream os = socket.getOutputStream();
            os.write("hello".getBytes());
    
            // while(true){}
    
            // 释放资源
            os.close();
            socket.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    2.2 TCP接收数据

    package com.itheima.tcp_demo.demo1;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /*
         服务端接收数据 :
    
        1 创建服务器端的Socket对象 : ServerSocket类
            ServerSocket(int port)  : 构造方法需要绑定一个端口号 , port就是端口号
    
        2 监听客户端连接,并接受连接,返回一个Socket对象
            Socket accept() : 该方法会一直阻塞直到建立连接
    
        3 获取网络中的输入流,用来读取客户端发送过来的数据
            InputStream getInputStream()
    
        4 释放资源 : 服务端一般不会关闭
            void close()
     */
    public class ServerDemo {
        public static void main(String[] args) throws IOException {
    //        1 创建服务器端的Socket对象 : ServerSocket类
    //        ServerSocket(int port)  : 构造方法需要绑定一个端口号 , port就是端口号
            ServerSocket serverSocket = new ServerSocket(10010);
    
    //        2 监听客户端连接,并接受连接,返回一个Socket对象
    //        Socket accept() : 该方法会一直阻塞直到建立连接
            Socket socket = serverSocket.accept();
    //
    //        3 获取网络中的输入流,用来读取客户端发送过来的数据
    //        InputStream getInputStream()
            InputStream is = socket.getInputStream();
            int by;
            System.out.println("read方法执行前");
            while ((by = is.read()) != -1) {
                System.out.print((char) by);
            }
            System.out.println("read方法执行后");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    2.5 TCP练习1

    package com.itheima.tcp_demo.test1;
    
    import java.io.*;
    import java.net.Socket;
    
    /*
        客户端 :
    
        发送数据的步骤
            1 创建客户端的Socket对象 : Socket(String host, int port) 与指定服务端连接
                参数说明:
                host 表示服务器端的主机名,也可以是服务器端的IP地址,只不过是String类型的
                port 表示服务器端的端口
    
            2 通获Socket对象取网络中的输出流,写数据
                OutputStream getOutputStream​()
    
            3 释放资源
                void close​()
    
     */
    public class ClientDemo {
        public static void main(String[] args) throws IOException {
            // 创建客户端的Socket对象(Socket) 与指定服务端连接
            Socket socket = new Socket("127.0.0.1", 10010);
    
            // 通获Socket对象取网络中的输出流,写数据
            OutputStream os = socket.getOutputStream();
            os.write("hello".getBytes());
            // 像服务端写入结束标记
            socket.shutdownOutput();
    
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line = br.readLine();
            System.out.println(line);
    
            // 释放资源
            br.close();
            os.close();
            socket.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    package com.itheima.tcp_demo.test1;
    
    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /*
         服务端接收数据 :
    
        1 创建服务器端的Socket对象 : ServerSocket类
            ServerSocket​(int port)  : 构造方法需要绑定一个端口号 , port就是端口号
    
        2 监听客户端连接,并接受连接,返回一个Socket对象
            Socket accept​() : 该方法会一直阻塞直到建立连接
    
        3 获取网络中的输入流,用来读取客户端发送过来的数据
            InputStream getInputStream​()
    
        4 释放资源 : 服务端一般不会关闭
            void close​()
     */
    public class ServerDemo {
        public static void main(String[] args) throws IOException {
    //        1 创建服务器端的Socket对象 : ServerSocket类
    //        ServerSocket​(int port)  : 构造方法需要绑定一个端口号 , port就是端口号
            ServerSocket serverSocket = new ServerSocket(10010);
    
    //        2 监听客户端连接,并接受连接,返回一个Socket对象
    //        Socket accept​() : 该方法会一直阻塞直到建立连接
            Socket socket = serverSocket.accept();
    //
    //        3 获取网络中的输入流,用来读取客户端发送过来的数据
    //        InputStream getInputStream​()
            InputStream is = socket.getInputStream();
            int by;
    
            while ((by = is.read()) != -1) {
                System.out.print((char) by);
            }
    
            BufferedWriter bos = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bos.write("你谁啊");
    
            bos.close();
            is.close();
            socket.close();
            serverSocket.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    2.6 TCP练习2

    package com.itheima.tcp_demo.test2;
    
    import java.io.*;
    import java.net.Socket;
    
    public class ClientDemo {
        public static void main(String[] args) throws IOException {
            // 创建客户端Socket对象
            Socket socket = new Socket("127.0.0.1", 10086);
    
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg"));
    
            BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
    
            int by;
            while ((by = bis.read()) != -1) {// 从本地中读一个字节
                bos.write(by);// 往服务器写一个字节
                bos.flush();
            }
            // 写结束标记
            socket.shutdownOutput();
    
            // 把网络中的字节输入流 , 封装成高效的字符输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    //        String line;
    //        while ((line = br.readLine()) != null) {
    //            System.out.println(line);
    //        }
            String msg = br.readLine();// 读到换行才叫读到一行, 所以必须写服务器必须写newLine
            System.out.println(msg);
    
            // 释放资源
            bis.close();
            socket.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    package com.itheima.tcp_demo.test2;
    
    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class ServerDemo {
        public static void main(String[] args) throws IOException {
            // 创建服务端的连接对象
            ServerSocket serverSocket = new ServerSocket(10086);
            Socket socket = null;
            BufferedInputStream bis = null;
            BufferedWriter socketBw = null;
    
            while (true) {
                // 获取Socket对象
                socket = serverSocket.accept();
    
                // 获取网络中的字节输入流  在封装成高效的字节输入流对象
                bis = new BufferedInputStream(socket.getInputStream());
    
                // 创建本地的字节输出流 , 封装成高效的字节输出流
                BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("day13_demo\\图片\\a.jpg"));
    
                int by;
                while ((by = bis.read()) != -1) {
                    bw.write(by);
                    bw.flush();
                }
    
                //关闭本地流
                bw.close();
    
                // 获取网络中的字节输出流 , 封装成高效的字符输出流
                socketBw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                socketBw.write("谢谢你");
                socketBw.newLine();// 必须有换行 , 因为readLine读到换行结束
                socketBw.flush();
            }
    
            // 释放资源
    //        socketBw.close();
    //        bis.close();
    //        socket.close();
    //        serverSocket.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    2.7 TCP练习3

    package com.itheima.tcp_demo.test3;
    
    import java.io.*;
    import java.net.Socket;
    
    public class ClientDemo {
        public static void main(String[] args) throws IOException {
            // 创建客户端Socket对象
            Socket socket = new Socket("127.0.0.1", 10086);
    
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg"));
    
            BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
    
            int by;
            while ((by = bis.read()) != -1) {// 从本地中读一个字节
                bos.write(by);// 往服务器写一个字节
                bos.flush();
            }
            // 写结束标记
            socket.shutdownOutput();
    
            // 把网络中的字节输入流 , 封装成高效的字符输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    //        String line;
    //        while ((line = br.readLine()) != null) {
    //            System.out.println(line);
    //        }
            String msg = br.readLine();// 读到换行才叫读到一行, 所以必须写服务器必须写newLine
            System.out.println(msg);
    
            // 释放资源
            bis.close();
            socket.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    package com.itheima.tcp_demo.test3;
    
    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.UUID;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ServerDemo {
        public static void main(String[] args) throws IOException {
            // 创建服务端的连接对象
            ServerSocket serverSocket = new ServerSocket(10086);
    
            ExecutorService executorService = Executors.newFixedThreadPool(10);
            while (true) {
                // 获取Socket对象
                Socket socket = serverSocket.accept();
                executorService.submit(new ServerThread(socket));
            }
    
            // 释放资源
    //        socketBw.close();
    //        bis.close();
    //        socket.close();
    //        serverSocket.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    package com.itheima.tcp_demo.test3;
    
    import javax.management.relation.RoleUnresolved;
    import java.io.*;
    import java.net.Socket;
    import java.util.UUID;
    
    public class ServerThread implements Runnable {
        Socket socket = null;
        BufferedOutputStream bw = null;
    
        public ServerThread(Socket socket) {
            this.socket = socket;
        }
    
        @Override
        public void run() {
    
            try {
                // 获取网络中的字节输入流  在封装成高效的字节输入流对象
                BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
    
                // 创建本地的字节输出流 , 封装成高效的字节输出流
                bw = new BufferedOutputStream(new FileOutputStream("day13_demo\\图片\\" + UUID.randomUUID() + ".jpg"));
    
                int by;
                while ((by = bis.read()) != -1) {
                    bw.write(by);
                    bw.flush();
                }
                // 获取网络中的字节输出流 , 封装成高效的字符输出流
                BufferedWriter socketBw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                socketBw.write("谢谢你");
                socketBw.newLine();// 必须有换行 , 因为readLine读到换行结束
                socketBw.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //关闭本地流
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    3 Junit单元测试

    3.1 Junit4单元测试概述

    • 单元测试就是编写测试代码,可以准确、快速地保证程序的正确性,Junit是Java单元测试的框架。

    • JUnit4可以通过注解的方式来标记方法 , 让方法存某种意义 ,常见的注解有:

      • @BeforeClass 全局只会执行一次,而且是第一个运行(标记的方法需要是一个静态无参无返回值方法)
      • @Before 在测试方法运行之前运行(非静态无参无返回值方法)
      • **@Test 测试方法(此方法必须是非静态无参无返回值方法), 主要用于测试的方法 **
      • @After 在测试方法运行之后运行(非静态无参无返回值方法)
      • @AfterClass 全局只会执行一次,而且是最后一个运行(标记的方法需要是一个静态无参无返回值方法)
      • @Ignore 忽略此方法

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

    3.2 Junit的基本使用

    • 已知存在需要测试的类Calculator ,这是一个能够简单实现加减乘除、平方、开方的计算器类,然后对这些功能进行单元测试。
    // 计算器类
    public class Calculator {
        // 静态变量,用于存储运行结果
        private static int result; // 20
      
        // 加法运算
        public void add(int n) {
            result = result + n;
        }
        
        // 减法运算
        public void subtract(int n) {
            // Bug: 正确的应该是 result = result - n
            result = result - 1;  
        }
      
        // 乘法运算
        public void multiply(int n) {
            // 此方法尚未写好
        }         
      
        // 除法运算
        public void divide(int n) {
            result = result / n;
        }
        
        // 平方运算
        public void square(int n) {
            result = n * n;
        }
        
        // 平方根运算
        public void squareRoot(int n) {
            // Bug : 死循环
            for (; ;) ;            
        }
        
        // 将结果清零
        public void clear() {     
            result = 0;
        }
        
        // 返回运算结果
        public int getResult(){
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 引入Junit4的jar包到模块中
      • 第一步 : 在模块中新建文件夹lib,拷贝今天资料中junit4的jar包到模块中
        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pcffYzoW-1659453849344)(\imgs\03_通讯协议.png)]

      • 第二步 : 选中jar文件 , 右键选择 Add as Library

    • 生成Junit测试框架
      • 使用IDEA能够直接给需要测试的类生成测试框架,如下:

      • 选中本类任何位置右键选择 Generate(Alt+Insert)/ go to 选项 , 选择Test…

      • 选择需要进行测试的方法

      • 在上一步OK后系统会自动生成一个新类CalculatorTest,里面包含一些空的测试用例。

        你只需要将这些测试用例稍作修改即可使用,完整的CalculatorTest代码如下:

        
        
        • 1
      • 限时测试

        对于那些逻辑很复杂,循环嵌套比较深的程序,很有可能出现死循环,因此一定要采取一些预防措施。限时测试是一个很好的解决方案。我们给这些测试函数设定一个执行时间,超过了这个时间,他们就会被系统强行终止,并且系统还会向你汇报该函数结束的原因是因为超时,这样你就可以发现这些Bug了。要实现这一功能,只需要给@Test标注加一个参数即可,代码如下:

        被测方法:

        public void squareRoot(int n) {
            //Bug : 死循环
            for (; ; ) ;            
        }
        
        • 1
        • 2
        • 3
        • 4
        测试方法:
        @Test(timeout = 1000)
        // Timeout参数表明了你要设定的时间,单位为毫秒,因此1000就代表1秒。
        public void squareRoot() {
            calculator.squareRoot(4);
            assertEquals(2 , calculator.getResult());
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6

    3.3 断言

    • 概述 :

    ​ 预先判断某个条件一定成立,如果条件不成立,则直接报错。

    • 使用 :
      //第一个参数表示期望值
      //第二个参数表示实际值
      // 如果实际值和期望值相同,说明结果正确就测试通过,如果不相同,说明结果是错误的,就会报错
      Assert.assertEquals( 期望值, 实际值);
      Assert.assertEquals("异常原因", 期望值, 实际值);
      
      //例如:
      int result = add(100,200);
      Assert.assertEquals(300, result);  
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • 小结 : 如何进行断言
      Assert.assertEquals(期望值,实际值)
      
      • 1

    4 单例设计模式

    学习目标

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

    内容讲解

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

    4.1 单例设计模式的作用

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

      即一个类只有一个对象实例。

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

    4.2 单例设计模式实现步骤

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

    4.3 单例设计模式的类型

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

    1. 饿汉单例设计模式

    2. 懒汉单例设计模式

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

    4.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;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    • 需求:定义一个皇帝类,要求对象只能存在一个。

      package com.itheima.singledesign;
      /*
          需求 : 使用单例模式(饿汉式) , 要求此类只能有一个对象
      
          步骤 :
              1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
              2. 在该类内部产生一个唯一的实例化对象,并且将其封装为private static 类型的成员变量。
              3. 定义一个静态方法返回这个唯一对象。
       */
      public class King {
          // 1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
          private King(){
      
          }
      
          // 2. 在该类内部产生一个唯一的实例化对象,并且将其封装为private static 类型的成员变量。
          private static final King KING = new King();
      
          // 3. 定义一个静态方法返回这个唯一对象。
          public static King getInstance(){
              return KING;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23

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

    4.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;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

      }

      
      - ##### 注意 :
      
      - 懒汉单例设计模式在多线程环境下可能会实例化出多个对象,不能保证单例的状态,所以加上关键字: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 king2;
      
          // 3. 定义一个静态方法返回这个唯一对象。 此方法需要加上synchronized关键字保证在多线程中也只有一个实例对象
          public static synchronized King2 getInstance() {
              if (king2 == null) {
                  king2 = new King2();
              }
              return king2;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36

      知识小结

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

      • 实现单例模式的步骤:

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

      5 多例设计模式

      学习目标

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

      内容讲解

      5.1 多例设计模式的作用

      • 多例模式,是一种常用的设计模式之一。通过多例模式可以保证项目中,应用该模式的类有固定数量的实例

        多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。

      • 使用场景:线程池

        线程池 = Executors.newFixedThreadPool(3);
        
        • 1

      5.2.实现步骤

      ​ 1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。

      ​ 2.在类中定义该类被创建对象的总数量

      ​ 3.在类中定义存放类实例的list集合

      ​ 4.在类中提供静态代码块,在静态代码块中创建类的实例

      ​ 5.提供获取类实例的静态方法

      5.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 {
          // 1.创建一个类,  将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
          private Teacher() {
          }
      
          // 2.在类中定义该类被创建对象的总数量
          private static int maxCount = 3;
      
          // 3.在类中定义存放类实例的list集合
          private static ArrayList<Teacher> list = new ArrayList<>();// {teacher1  , teacher2 , teacher3}
      
          // 4.在类中提供静态代码块,在静态代码块中创建类的实例
          static {
              for (int i = 0; i < maxCount; i++) {
                  list.add(new Teacher());
              }
          }
      
          // 5.提供获取类实例的静态方法
          public static Teacher getInstance() {
              int index = new Random().nextInt(3);// [0 - 2]
              return list.get(index);
          }
      
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42

      5.4 小结

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

        实现多例模式的步骤 :

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

      6 工厂设计模式

      学习目标

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

      内容讲解

      6.1 概述

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

      6.2 作用

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

      6.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) {
              Car benchi = CarFactory.getInstance(Brand.BENCHI);
              System.out.println(benchi);
          }
      }
      
      // 汽车接口
      interface Car {
          public abstract void run();
      }
      
      // 编写一个Falali类实现Car接口,重写run方法
      class Falali implements Car {
          public Falali() {
          }
      
          @Override
          public void run() {
              System.out.println("法拉利破百需要3秒...");
          }
      }
      
      // 编写一个Benchi类实现Car接口
      class Benchi implements Car {
          @Override
          public void run() {
              System.out.println("奔驰破百需要5秒...");
          }
      }
      
      // 汽车品牌枚举
      enum Brand {
          BENCHI, FALALI, BAOMA, BINLI, AODI;
      }
      
      // 提供一个CarFactory(汽车工厂),用于生产汽车对象
      class CarFactory {
          private CarFactory() {
          }
      
          public static Car getInstance(Brand brand) {
              switch (brand) {
                  case FALALI:
                      return new Falali();
                  case BENCHI:
                      return new Benchi();
                  default:
                      return null;
              }
          }
      }
      
       提供一个CarFactory(汽车工厂),用于生产汽车对象
      //class CarFactory{
      //    private CarFactory(){}
      //
      //    public static Car getInstance(String brand) {
      //        if(brand.equals("Falali")){
      //            return new Falali(10);
      //        }else if(brand.equals("Benchi")) {
      //            return new Benchi();
      //        }else {
      //            return null;
      //        }
      //    }
      //}
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81

      知识小结

      • 工厂模式的存在可以改变创建对象的方式,降低类与类之间的耦合问题.
    • 相关阅读:
      Vue3全局Api应用实例
      mac配置环境变量总结
      在Android开发中如何使用OpenSL ES库播放解码后的pcm音频文件?
      (二)Java网络编程之爆肝HTTP、HTTPS、TLS协议及对称与非对称加密原理!
      swig是怎么通过python调用dll的
      Mac开发指南
      【华为OD题库-030】阿里巴巴找黄金宝箱(V)-java
      Vue3+Elementplus引入面包屑功能
      传统微服务框架如何无缝过渡到服务网格 ASM
      【c++&leetcode】1382. Balance a Binary Search Tree
    • 原文地址:https://blog.csdn.net/wanghaoyingand/article/details/126132025