Base64j加密解密、动态代理、正则表达式、单例多例设计模式、枚举、工厂设计模式

Base64的加密和解密(jdk8新特性)(重点)

/*
    java.util.Base64
        该类仅由用于获得Base64编码方案的编码器和解码器的静态方法组成。
    作用:
        使用Base64里边的编码器对数据进行编码(加密)
        使用Base64里边的解码器对数据进行解码(解密)
    静态成员方法:
        static Base64.Encoder getEncoder​() 获取加密器
        static Base64.Decoder getDecoder​() 获取解密器
    java.util.Base64.Encoder:是Base64的内部类,用于对数据进行加密
        成员方法:
            String encodeToString​(byte[] src) 使用Base64编码方案将指定的字节数组编码为字符串。
    java.util.Base64.Decoder:是Base64的内部类,用于对数据进行解密
        成员方法:
            byte[] decode​(String src) 使用Base64编码方案将Base64编码的字符串解码为新分配的字节数组。
 */
public class Demo01Base64 {
    public static void main(String[] args) {
        String s1 = "国家的绝密文件!";
        System.out.println("原字符串:"+s1);//原字符串:国家的绝密文件!

        //使用加密器Encoder中的方法encodeToString​把字符串进行加密
        String s2 = Base64.getEncoder().encodeToString(s1.getBytes());
        System.out.println("加密后的字符串:"+s2);//加密后的字符串:5Zu95a6255qE57ud5a+G5paH5Lu2IQ==

        //使用解密器Decoder中的方法decode把加密后的字符串进行解密
        byte[] bytes = Base64.getDecoder().decode(s2);
        String s3 = new String(bytes);
        System.out.println("解密后的字符串:"+s3);
    }
}
动态代理(原理)
动态代理概述

在这里插入图片描述

动态代理代码实现

Star接口

//培养明星的接口
public interface Star {
    //培养唱歌的方法
    public abstract void changge();

    //培养跳舞的方法
    public abstract void tiaowu();

    //培养演电影的方法
    public abstract String yandianying(int money);

    //培养吃饭的方法
    public abstract void chifan();

    //培养玩游戏的方法
    public abstract void wanyouxi();
}

Caixukun类

public class CaiXuKun implements Star {
    @Override
    public void changge() {
        System.out.println("蔡徐坤在唱歌");
    }

    @Override
    public void tiaowu() {
        System.out.println("蔡徐坤在跳舞+打篮球");
    }

    @Override
    public String yandianying(int money) {
        System.out.println("蔡徐坤在演电影"+money);
        return "电影演完了";
    }

    @Override
    public void chifan() {
        System.out.println("和蔡徐坤一起吃饭");
    }

    @Override
    public void wanyouxi() {
        System.out.println("和蔡徐坤一起玩游戏");
    }
}

WuQian类

/*
    ctrl+r:查找并替换
 */
public class WuQian implements Star {
    @Override
    public void changge() {
        System.out.println("吴Q在唱歌");
    }

    @Override
    public void tiaowu() {
        System.out.println("吴Q在跳舞");
    }

    @Override
    public String yandianying(int money) {
        System.out.println("吴Q在演电影"+money);
        return "电影演完了";
    }

    @Override
    public void chifan() {
        System.out.println("吴Q吃大锅饭");
    }

    @Override
    public void wanyouxi() {
        System.out.println("吴Q踩缝纫机");
    }
}

InvocationHandler接口的实现类

/*
    java.lang.reflect.InvocationHandler接口
        InvocationHandler 是代理实例的调用处理程序 实现的接口。
        用来生产代理人对象的动态代理接口
        可以把被代理人(明星)传递到InvocationHandler接口的实现类中,让InvocationHandler接口的实现类生产明星的代理人
    InvocationHandler接口的方法
        Object invoke(Object proxy, Method method, Object[] args) 在代理实例上处理方法调用并返回结果。
        作用:
            使用invoke方法对被代理人(明星)的方法进行拦截,部分方法可以运行,部分方法不能运行
        参数:
             Object proxy:内部产生的代理人对象
             Method method:invoke会对明星的方法(changge,tiaowu..)进行拦截,使用反射技术获取到这些方法,把方法赋值给method
             Object[] args:拦截到明星方法的参数
        返回值:
             Object:就是拦截到方法的返回值
   注意:
        在实现类中定义一个变量为Star类型
        使用带参数构造方法,传递Star接口的实现类对象(明星),在类的内部生产明星的代理人对象
 */
public class InvocationHandlerImpl implements InvocationHandler {
    //在实现类中定义一个变量为Star类型
    private Star star;

    public InvocationHandlerImpl(Star star) {
        this.star = star;
    }

    /*
        invoke方法对被代理人(明星)的方法进行拦截,部分方法可以运行,部分方法不能运行
            调用明星changge,tiaowu,yandianying方法,可以运行
            调用明星chifan,wanyouxi的方法,拦截不运行
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取到方法名称
        String methodName = method.getName();
        //对方法名称进行判断
        if("chifan".equals(methodName)){
            throw new RuntimeException("不和你吃饭!");
        }
        if("wanyouxi".equals(methodName)){
            throw new RuntimeException("不和玩游戏!");
        }

        //其他的方法,使用Method类的方法invoke让方法运行
        Object v = method.invoke(star, args);
        return v;
    }
}

测试类:

/*
    动态代理:
        创建代理人对象,对明星进行代理,对明星的方法进行拦截
            调用明星changge,tiaowu,yandianying方法,可以运行
            调用明星chifan,wanyouxi的方法,拦截不运行
   想要实现动态代理:使用Proxy类生产代理人对象
        java.lang.reflect.Proxy:
            Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
        Proxy类中的静态方法:可以生产代理人对象
            static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
                返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
            参数:
                ClassLoader loader:传递类加载器
                Class<?>[] interfaces:传递被代理人实现的所有接口
                InvocationHandler h:生产代理人的接口,传递InvocationHandler接口的实现类对象
            返回值:
                Object:返回的就是代理人对象
 */
public class Demo01Proxy {
    public static void main(String[] args) {
        //没有使用动态代理
        CaiXuKun cxk = new CaiXuKun();
        //cxk.changge();
        //cxk.tiaowu();

        //获取蔡徐坤的代理人对象
        Star cxkProxy = (Star) Proxy.newProxyInstance(CaiXuKun.class.getClassLoader(),
                CaiXuKun.class.getInterfaces(),new InvocationHandlerImpl(cxk));
        cxkProxy.changge();
        cxkProxy.tiaowu();
        String s = cxkProxy.yandianying(100);
        System.out.println(s);

        //cxkProxy.chifan();
        //cxkProxy.wanyouxi();

        //获取吴Q的代理人对象
        Star wqProxy = (Star)Proxy.newProxyInstance(WuQian.class.getClassLoader(),
                WuQian.class.getInterfaces(),new InvocationHandlerImpl(new WuQian()));
        wqProxy.changge();
        wqProxy.tiaowu();
        wqProxy.chifan();
    }
}
动态代理综合案例

需求:
模拟Collections的unmodifiableList方法,对List接口进行代理
调用List接口的方法会被拦截
如果使用的size,get方法,没有对集合进行修改,则允许执行
如果使用的add,remove,set方法,对集合进行了修改,则抛出运行时异常

分析:
1.定义一个代理方法proxyList
参数:传递List集合
返回值:被代理之后的List集合
2.方法内部可以使用Proxy类中的方法实现动态代理

代码实现:

/*
    需求:
        模拟unmodifiableList方法,对List接口进行代理
            调用List接口的方法会被拦截
            如果使用的size,get方法,没有对集合进行修改,则允许执行
            如果使用的add,remove,set方法,对集合进行了修改,则抛出运行时异常

    分析:
        1.定义一个代理方法proxyList
            参数:传递List集合
            返回值:被代理之后的List集合
        2.方法内部可以使用Proxy类中的方法实现动态代理

 */
public class Demo02Proxy {
    public static void main(String[] args) {
        //创建List对象
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        //调用proxyList方法,获取List接口的代理人对象
        List<String> listProxy = proxyList(list);
        System.out.println(listProxy.size());//3
        System.out.println(listProxy.get(1));//b

        listProxy.remove(0);//UnsupportedOperationException: remove no run
    }

    //1.定义一个代理方法proxyList
    public static List<String> proxyList(List<String> list){
        //2.方法内部可以使用Proxy类中的方法实现动态代理
        List<String> listProxy = (List<String>)Proxy.newProxyInstance(Demo02Proxy.class.getClassLoader(),
                list.getClass().getInterfaces(),new InvocationHandlerImpl(list));
        return listProxy;
    }
}
public class InvocationHandlerImpl implements InvocationHandler {
    //定义一个List集合的变量
    private List<String> list;

    public InvocationHandlerImpl(List<String> list) {
        this.list = list;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //invoke方法会获取list集合中的方法,在invoke方法内部对list集合的方法进行拦截
        //获取方法的名称
        String methodName = method.getName();
        //如果使用的add,remove,set方法,对集合进行了修改,则抛出运行时异常
        if("add".equals(methodName)){
            throw new UnsupportedOperationException("add no run");
        }
        if("remove".equals(methodName)){
            throw new UnsupportedOperationException("remove no run");
        }
        if("set".equals(methodName)){
            throw new UnsupportedOperationException("set no run");
        }

        //如果使用的size,get方法,没有对集合进行修改,则允许执行
        Object v = method.invoke(list, args);
        return v;
    }
}

动态代理案例流程图
在这里插入图片描述

总结

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

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

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

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

缺点:只能针对接口的实现类做代理对象,普通类是不能做代理对象的。

正则表达式(重点)

正则表达式的概念及演示
/*
    正则表达式:
        就是一个包含某些规则的字符串
        用来对其他的字符串进行校验,校验其他的字符串是否满足正则表达式的规则
    需求:
        对QQ号进行校验
        1.长度5-15位
        2.全是数字
        3.第一位不是0
   校验QQ号的正则表达式
        "[1-9][0-9]{4,14}"
    学习目标:
        能看懂一般的正则表达式即可
 */
public class Demo01Regex {
    public static void main(String[] args) {
        //获取一个用户输入的QQ号
        System.out.println("请输入一个QQ号:");
        String qq = new Scanner(System.in).nextLine();
        boolean b1 = checkQQ(qq);
        System.out.println("b1:"+b1);
        boolean b2 = checkQQRegex(qq);
        System.out.println("b2:"+b2);
    }

    //定义校验QQ号的方法,不使用正则表达式
    public static boolean checkQQ(String qq){
        //1.长度5-15位
        if(qq.length()<5 || qq.length()>15){
            return false;
        }
        //2.全是数字
        char[] chars = qq.toCharArray();
        for (char c : chars) {
            //判断字符是否在'0'到'9'之间
            if(c<'0' || c>'9'){
                return false;
            }
        }

        //3.第一位不是是0
        if("0".equals(qq.charAt(0)+"")){
            return false;
        }

        //满足所有的规则
        return true;
    }

    /*
        定义校验QQ号的方法,使用正则表达式
        String类中的方法:
            boolean matches(String regex) 告知此字符串是否匹配给定的正则表达式。
                判断字符串是否满足正则表达式的规则
                    满足:返回true
                    不满足:返回false
     */
    public static boolean checkQQRegex(String qq){
        return qq.matches("[1-9][0-9]{4,14}");
    }
}
正则表达式-字符类
/*
    java.util.regex.Pattern:正则表达式的编译表示形式。
    正则表达式-字符类:[]表示一个区间,范围可以自己定义
        语法示例:
        1. [abc]:代表a或者b,或者c字符中的一个。
        2. [^abc]:代表除a,b,c以外的任何字符。
        3. [a-z]:代表a-z的所有小写字符中的一个。
        4. [A-Z]:代表A-Z的所有大写字符中的一个。
        5. [0-9]:代表0-9之间的某一个数字字符。
        6. [a-zA-Z0-9]:代表a-z或者A-Z或者0-9之间的任意一个字符。
        7. [a-dm-p]:a 到 d 或 m 到 p之间的任意一个字符
 */
public class Demo02Regex {
    public static void main(String[] args) {
        String str = "ead";//false
        //1.验证str是否以h开头,以d结尾,中间是a,e,i,o,u中某个字符
        String regex = "h[aeiou]d";
        str = "hid";//true
        str = "Hud";//false
        boolean b1 = str.matches(regex);
        System.out.println("b1:"+b1);

        //2.验证str是否以h开头,以d结尾,中间不是a,e,i,o,u中的某个字符
        regex = "h[^aeiou]d";
        str = "h1d";//true
        str = "had";//false
        boolean b2 = str.matches(regex);
        System.out.println("b2:"+b2);

        //3.验证str是否a-z的任何一个小写字符开头,后跟ad
        regex = "[a-z]ad";
        str = "wad";//true
        str = "1ad";//false
        boolean b3 = str.matches(regex);
        System.out.println("b3:"+b3);

        //4.验证str是否以a-d或者m-p之间某个字符开头,后跟ad
        regex = "[a-dm-p]ad";
        str = "cad";//true
        str = "ead";//false

        boolean b4 = str.matches(regex);
        System.out.println("b4:"+b4);
    }
}
正则表达式-逻辑运算符
/*
    正则表达式-逻辑运算符
        语法示例:
        1. &&:并且
        2. | :或者
 */
public class Demo03Regex {
    public static void main(String[] args) {
        String str = "had";//true
        str = "ead";//false
        str = "Had";//false

        //1.要求字符串是小写[a-z]和福音字符[^aeiou]开头,后跟ad
        String regex = "[[a-z]&&[^aeiou]]ad";
        boolean b1 = str.matches(regex);
        System.out.println("b1:"+b1);

        //2.要求字符串是aeiou中的某个字符开头,后跟ad
        regex = "[a||e||i||o||u]ad";//"[a|e|i|o|u]ad"就相当于"[aeiou]" 或运算符是可以省略不写的
        str = "aad";//true
        str = "1ad";//false
        boolean b2 = str.matches(regex);
        System.out.println("b2:"+ b2);
    }
}
正则表达式-预定义字符
/*
    正则表达式-预定义字符
    语法示例:
    1. "." : 匹配任何字符。(重点)
    2. "\\d":任何数字[0-9]的简写;(重点)
    3. "\\D":任何非数字[^0-9]的简写;
    4. "\\s": 空白字符:[ \t\n\x0B\f\r] 的简写
    5. "\\S": 非空白字符:[^\s] 的简写
    6. "\\w":单词字符:[a-zA-Z_0-9]的简写(重点)
    7. "\\W":非单词字符:[^\w]
    注意:
        \本身就是转义字符,写正则表达式语法的时候,必须写\\,把有特殊含义的\转义为一个普通的\使用
 */
public class Demo04Regex {
    public static void main(String[] args) {
        //1.验证str是否3位数字
        String str = "250";//true
        str = "25a";//false
        String regex = "[0-9][0-9][0-9]";
        regex = "\\d\\d\\d";
        boolean b1 = str.matches(regex);
        System.out.println("b1:"+b1);

        //2.验证手机号:1开头,第二位:3/5/8,剩下9位都是0-9的数字
        regex = "1[358]\\d\\d\\d\\d\\d\\d\\d\\d\\d";
        str = "13800138000";//true
        str = "23800138000";//false
        str = "138001380001";//false
        str = "13800138a00";//false
        boolean b2 = str.matches(regex);
        System.out.println("b2:"+b2);

        //3.验证字符串是否以h开头,以d结尾,中间是任何字符
        regex = "h.d";
        str = "h&d";//true
        str = "h中d";//true
        str = "1Ad";//false
        boolean b3 = str.matches(regex);
        System.out.println("b3:"+b3);

        //4.验证str是否是:had.
        //注意: .不是任意字符,就是一个普通的.需要使用转义字符把有特殊含义的.转换为普通的.
        regex = "had\\.";
        str = "hadA";//false
        str = "had.";//true
        boolean b4 = str.matches(regex);
        System.out.println("b4:"+b4);
    }
}
正则表达式-数量词
/*
    正则表达式-数量词
        语法示例:
        1. X? : 0次或1次
        2. X* : 0次到多次 任意次
        3. X+ : 1次或多次 X>=1次
        4. X{n} : 恰好n次 X=n次
        5. X{n,} : 至少n次 X>=n次
        6. X{n,m}: n到m次(n和m都是包含的)   n=<X<=m
 */
public class Demo05Regex {
    public static void main(String[] args) {
        String str = "";//false
        str = "123";//true
        str = "1234";//false

        //1.验证str是否是三位数字
        String regex = "\\d{3}";
        boolean b1 = str.matches(regex);
        System.out.println("b1:"+b1);

        //2.验证str是否是多位数字:1次以上的数字
        regex = "\\d+";
        str = "12312413221312";//true
        str = "1";//true
        str = "";//false
        str = "111a";//false
        boolean b2 = str.matches(regex);
        System.out.println("b2:"+b2);

        //3.验证str是否是手机号:1开头,第二位:3/5/8,剩下9位都是0-9的数字
        regex = "1[358]\\d{9}";
        str = "13800138000";//true
        str = "1380013800a0";//false
        boolean b3 = str.matches(regex);
        System.out.println("b3:"+b3);

        //4.验证小数:必须出现小数点,但是只能出现1次
        double d =1.1;
        d = 0.1;
        d = .1;
        d = 1.;
        //d = .;
        System.out.println(d);
        regex = "\\d*\\.\\d+";
        str = "1.1";//true
        str = ".1";//true
        str = "1.";//true
        str = "aa.1";
        str = "1.1.1";

        boolean b4 = str.matches(regex);
        System.out.println("b4:"+b4);

        //5.验证小数:小数点可以不出现,也可以出现1次
        regex = "\\d*\\.?\\d*";
        str = "10000";//true
        str = "10000.13213123";//true
        str = ".1212312";//true
        str = "";//true
        str = "a";//false
        str = "a";//false
        str = "11..11";//false
        boolean b5 = str.matches(regex);
        System.out.println("b5:"+b5);

        //6.验证小数:要求匹配:3、3.、3.14、+3.14、-3.
        regex = "[+-]?\\d+\\.?\\d*";
        str = "3";//true
        str = "3.";//true
        str = "3.14";//true
        str = "+3.14";//true
        str = "-3.";//true
        boolean b6 = str.matches(regex);
        System.out.println("b6:"+b6);

        //7.验证qq号码:1).5--15位;2).全部是数字;3).第一位不是0
        regex = "[1-9]\\d{4,14}";
        str = "11111";//true
        str = "111";//false
        str = "111111a";//false
        boolean b7 = str.matches(regex);
        System.out.println("b7:"+b7);
    }
}
正则表达式-分组括号()
/*
    正则表达式-分组括号( )
 */
public class Demo06Regex {
    public static void main(String[] args) {
        String str = "abc";//true
        str = "";//true
        str = "abcabc";//true
        str = "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc";//true
        str = "abcaaabbbccc";//false
        //校验字符串"abc"可以出现任意次
        String regex = "(abc)*";
        boolean b1 = str.matches(regex);
        System.out.println("b1:"+b1);

        str = "DG8FV-B9TKY-FRT9J-99899-XPQ4G";//b2:true
        str = "DG8FV-B9TKY-FRT9J-99899-XPQ4G-";//b2:false
        //验证这个序列号:分为5组,每组之间使用-隔开,每组由5位A-Z或者0-9的字符组成
        regex = "([A-Z0-9]{5}-){4}[A-Z0-9]{5}";
        boolean b2 = str.matches(regex);
        System.out.println("b2:"+b2);
    }
}
String类中和正则表达式相关的方法
/*
    String类中和正则表达式相关的方法
        boolean matches(String regex) 判断字符串是否匹配给定的正则表达式。
        String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串。
        String replaceAll(String regex, String replacement)把满足正则表达式的字符串,替换为新的字符串
 */
public class Demo07Regex {
    public static void main(String[] args) {
        show04();
    }

    /*
        String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串。
     */
    private static void show01() {
        String s = "11-22-33-44-55";
        String[] arr = s.split("-");
        for (String str : arr) {
            System.out.println(str);
        }
    }

    //正则表达式中.代表任意字符串
    private static void show02() {
        String s = "192.168.1.100";
        String[] arr = s.split("\\.");
        System.out.println(arr.length);
        for (String str : arr) {
            System.out.println(str);
        }
    }

    private static void show03() {
        String s = "192 168 1 100";
        s = "192       168       1         100";
        String[] arr = s.split(" +");//根据一个空格或者多个连续的空格切割字符串
        System.out.println(arr.length);
        for (String str : arr) {
            System.out.println(str);
        }
    }

    /*
        String replaceAll(String regex, String replacement)把满足正则表达式的字符串,替换为新的字符串
     */
    public static void show04(){
        String s = "12312asfdsa12123asa11c1fzvs1v*&&3242fsfa11";
        System.out.println("原字符串:"+s);
        //需求:使用replaceAll方法把字符串中每一个数字替换为@_@
        String reg = "\\d";
        String s1 = s.replaceAll(reg, "@_@");
        System.out.println("替换后的字符串:"+s1);
        //需求:使用replaceAll方法把字符串中的连续的数字替换为@_@
        reg = "\\d+";
        String s2 = s.replaceAll(reg,"@_@");
        System.out.println("替换后的字符串:"+s2);
    }
}
单例设计模式(重点)

饿汉式

/*
    单例:只能创建一个本类的对象
    单例设计模式:饿汉式
        人非常饥饿,直接把对象吃了
        无论是否有人使用类,都先创建好本类的对象,供用户使用
    实现步骤:
        1.私有空参数构造方法,不让用户直接创建对象
        2.定义一个私有的静态的Person变量,并进行初始化赋值
        3.定义一个公共的静态方法,返回Person对象
 */
public class Person {
    //1.私有空参数构造方法,不让用户直接创建对象
    private Person(){}
    //2.定义一个私有的静态的Person变量,并进行初始化赋值
    private static Person p = new Person();
    //3.定义一个公共的静态方法,返回Person对象
    public static Person getInstance(){
        return p;
    }
}

public class Demo01Singleton {
    public static void main(String[] args) {
        //正常情况下,可以根据类创建多个对象
        //Person p1 = new Person();
        //System.out.println(p1);
        //Person p2 = new Person();
        //System.out.println(p2);
        //Person p3 = new Person();
        //Person p4 = new Person();
        //Person p5 = new Person();

        //测试单例设计模式,无论调用多少次静态的方法,获取的对象都是同一个
        for (int i = 0; i < 20; i++) {
            Person p = Person.getInstance();
            System.out.println(p);
        }
    }
}

懒汉式

/*
    单例设计模式:懒汉式
        人非常懒,抽一鞭子动一下
        当我们要使用对象的时候,再创建对象
    实现步骤:
        1.私有空参数构造方法,不让用户直接创建对象
        2.在类中定义一个私有的静态的Person变量,不进行初始化赋值
        3.定义一个公共的静态的成员方法,返回Person对象,保证无论调用多少次方法,只返回一个对象
 */
public class Person {
    //1.私有空参数构造方法,不让用户直接创建对象
    private Person(){}
    //2.在类中定义一个私有的静态的Person变量,不进行初始化赋值
    private static Person p;
    //3.定义一个公共的静态的成员方法,返回Person对象,保证无论调用多少次方法,只返回一个对象
    public static Person getInstance(){
        //增加一个判断,判断变量p的值是否为null(第一次调用方法),创建对象
        if(p == null){
            p = new Person();
        }
        //变量p不是null直接返回
        return p;
    }
}
解决懒汉式多线程问题
public class Demo01Sigleton {
    public static void main(String[] args) {
        //创建两个线程,每个线程获取20次对象
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                Person p = Person.getInstance();
                System.out.println(p);
            }
        }).start();

        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                Person p = Person.getInstance();
                System.out.println(p);
            }
        }).start();
    }
}

执行结果

com.llz.demo07Sigleton.Person@2f404fd1
com.llz.demo07Sigleton.Person@67c084e1
com.llz.demo07Sigleton.Person@67c084e1
com.llz.demo07Sigleton.Person@67c084e1

在这里插入图片描述
解决方法如下

/*
    单例设计模式:懒汉式
        人非常懒,抽一鞭子动一下
        当我们要会使用对象的时候,在创建对象
    实现步骤:
        1.私有空参数构造方法,不让用户直接创建对象
        2.在类中定义一个私有的静态的Person变量,不进行初始化赋值
        3.定义一个公共的静态的成员方法,返回Person对象,保证无论调用多少次方法,只返回一个对象
 */
public class Person {
    //1.私有空参数构造方法,不让用户直接创建对象
    private Person(){}
    //2.在类中定义一个私有的静态的Person变量,不进行初始化赋值
    private static Person p;
    //3.定义一个公共的静态的成员方法,返回Person对象,保证无论调用多少次方法,只返回一个对象
    public static Person getInstance(){
        //在外层在增加一个判断,可以提高程序的效率
        if(p==null){
            synchronized (Person.class){
                //增加一个判断,判断变量p的值是否为null(第一次调用方法),创建对象
                if(p == null){
                    p = new Person();
                }
            }
        }
        //变量p不是null直接返回
        return p;
    }
}

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

实现单例模式的步骤:

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

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

例如:

​ 扑克牌程序,一个“扑克类”会创建固定的54个对象,不能多、也不能少。

​ 麻将程序,一个“骰子类”会创建固定的2个对象,不能多、也不能少。

​ 程序中需要用到“颜色的表示”,只能有三种颜色“红、绿、蓝”,一个“颜色类(Color)”应该只创建三个对象,来代 表这三个颜色。

多例模式的作用:使某个类,在程序运行期间,只能产生固定的几个对象,不能多、也不能少。
获取多个通用的对象

/*
    多例设计模式:获取多个通用的对象
    需求:
        只让程序产生3个Student对象,不能多,也不能少
    实现步骤:
        1.私有空参数构造方法,不让用户直接创建对象
        2.定义一个私有的,静态的,最终的变量,值定义对象的总个数
        3.定义一个自由的,静态的集合,存储多个对象
        4.定义一个静态代码块,产生3个对象,存储到集合中
        5.定义一个公共的静态的方法,给用户随机在集合中取出一个对象返回
 */
public class Student {
    //1.私有空参数构造方法,不让用户直接创建对象
    private Student(){}
    //2.定义一个私有的,静态的,最终的变量,值定义对象的总个数
    private static final int MAX = 3;
    //3.定义一个自由的,静态的集合,存储多个对象
    private static ArrayList<Student> list = new ArrayList<>();
    //4.定义一个静态代码块,产生3个对象,存储到集合中
    static {
        for (int i = 0; i < MAX; i++) {
            list.add(new Student());
        }
    }
    //5.定义一个公共的静态的方法,给用户随机在集合中取出一个对象返回
    public static Student getInstance(){
        //定义一个Random对象
        Random r = new Random();
        //在集合索引的范围内,随机产生一个随机数
        int index = r.nextInt(list.size());//[0,1,2]
        //通过产生的随机索引,在集合中获取Student对象返回
        return list.get(index);
    }
}

小结
多例模式可以保证系统中一个类有固定个数的实例, 在实现需求的基础上, 能够提高实例的复用性.

实现多例模式的步骤:

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

枚举(重点)

/*
    枚举:它就是"多例设计模式:获取多个特定对象"的一种简单写法
    需求:
        获取两个Sex对象,一个代表男,一个代表女
    使用多例实现:
        public static final Sex MAN = new Sex("男");
        public static final Sex WOMAN = new Sex("女");
    使用枚举实现:
        MAN==>public static final Sex MAN = new Sex();
        WOMAN==> public static final Sex WOMAN = new Sex();
     枚举中也可以包含成员变量,成员方法,构造方法(私有),这3个必须写在枚举常量的下边
 */
public enum Sex {
    MAN("男"),WOMAN("女");

    private String s;

    private Sex(String s) {
        this.s = s;
    }

    //重写toString方法,返回对象中的字符串
    @Override
    public String toString() {
        return s;
    }
}
public class Demo01Enum {
    public static void main(String[] args) {
        //创建Student对象
        Student s = new Student();
        s.setName("柳岩");
        s.setAge(18);
        s.setSex(Sex.WOMAN);
        System.out.println(s);
    }
}

枚举的应用

枚举的作用:枚举通常可以用于做信息的分类,如性别,方向,季度等。

枚举表示性别:

public enum Sex {
    MAIL, FEMAIL;
}

枚举表示方向:

public enum Orientation {
    UP, RIGHT, DOWN, LEFT;
}

枚举表示季度

public enum Season {
    SPRING, SUMMER, AUTUMN, WINTER;
}

小结*

  • 枚举类在第一行罗列若干个枚举对象。(多例)
  • 第一行都是常量,存储的是枚举类的对象。
  • 枚举是不能在外部创建对象的,枚举的构造器默认是私有的。
  • 枚举通常用于做信息的标志和分类。

工厂设计模式(重点)

简单工厂设计模式
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃肉!");
    }
}

/*
    定义一个生产动物的工厂
    只生产动物,不生产其他对象
    定义一个静态方法,根据用户传递的动物名称,创建指定的动物返回
 */
public class AnimalFactory {
    //定义一个静态方法,参数传递动物的名称,根据名称创建指定的动物返回
    public static Animal getInstance(String name){
        if("cat".equals(name)){
            return new Cat();
        }else if("dog".equals(name)){
            return new Dog();
        }else{
            //不是动物
            return null;
        }
    }
}

/*
    简单工厂设计模式:
        创建一个工厂类,在工厂类中定义一个生产对象的方法
        我们要使用对象,不在自己创建对象了,使用工厂类的方法获取
    好处:
        可以传递不同的动物名称,产生不同的动物对象;解除耦合性,增强扩展性
    弊端:
        胡乱指示一个动物的名称,不存在,会抛出空指针异常
    解决:
        可以使用工厂方法设计模式来解决,产生多个工厂,每个工厂生产特定的动物对象
 */
public class Demo01SimpleFactory {
    public static void main(String[] args) {
        //使用工厂类的方法,获取指定的动物对象
        //获取一个Cat对象
        Animal cat = AnimalFactory.getInstance("cat");
        cat.eat();
        //获取一个Dog对象
        Animal dog = AnimalFactory.getInstance("dog");
        dog.eat();
        //胡乱指示一个动物的名称
        Animal car = AnimalFactory.getInstance("car");
        car.eat();// null.eat(); NullPointerException:抛出了空指针异常
    }
}
工厂方法设计模式
/*
    工厂方法设计模式:
        可以设计多个工厂,每个工厂生产不同的对象
        猫工厂只生产猫,狗工厂只生产狗
    好处:
        可以解决简单工厂设计模式的弊端,不让用户传递参数
    弊端:
        如果动物过多,产生多个工厂
 */
public class Demo01FactoryMethod {
    public static void main(String[] args) {
        //创建生产猫的工厂==>生产猫
        CatFactory catFactory = new CatFactory();
        Animal cat = catFactory.getInstance();
        cat.eat();
        
        //创建生产狗的工厂 ==>生产狗
        DogFactory dogFactory = new DogFactory();
        Animal dog = dogFactory.getInstance();
        dog.eat();
    }
}

/*
    定义一个工厂的接口:所有的工厂都得实现这个接口
 */
public interface Factory {
    //定义一个抽象的生产动物的方法
    public abstract Animal getInstance();
}

/*
    生产猫的工厂,实现工厂接口
    只生产猫对象
 */
public class CatFactory implements Factory{
    @Override
    public Animal getInstance() {
        return new Cat();
    }
}

/*
    定义生产狗的工厂,实现工厂接口
    只生产狗对象
 */
public class DogFactory implements Factory{
    @Override
    public Animal getInstance() {
        return new Dog();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值