java学习记录二十五:动态代理、JDK8新特性、base64编码

一、动态代理

一、解释

为什么要有“代理”?生活中就有很多代理的例子,例如,我现在需要出国,但是我不愿意自己去办签证、预定机票和酒店(觉得麻烦 ,那么就可以找旅行社去帮我办,这时候旅行社就是代理,而我自己就是被代理了。

代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者, 所以代理模式包含了3个角色: 被代理角色 代理角色 抽象角色(协议)。
和装饰模式类型,增强 了某个方法。

二、静态代理

public interface Happy {
// 协议,被代理者需要代理的方法,就定义在这里,然后让代理者和被代理者去实现
    // 被代理者实现: 为了确保和代理者实现的方法一致
    // 代理者实现: 为了增强被代理者的这些方法
    public abstract void happy();
}


public class JinLian implements Happy {

    public void happy(){
        System.out.println("金莲在happy...");
    }

}


public class WangPo implements Happy{
    // 成员变量
    JinLian jl;

    // 构造方法
    public WangPo(JinLian jl) {
        this.jl = jl;
    }

    // 成员方法
    @Override
    public void happy() {
        System.out.println("王婆以做衣服的名义开好房间,并把2人约到房间里...");
        // 金莲happy
        jl.happy();
        System.out.println("王婆打扫战场...");
    }
}


public class XiMen {
    public static void main(String[] args) {
        /*
            案例: 金莲要找西门happy
            代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者,
            所以代理模式包含了3个角色: 被代理角色     代理角色    抽象角色(协议)

         */
        // 不请代理: 金莲直接找西门happy
        // 创建金莲对象
        JinLian jl = new JinLian();
        // happy
        // jl.happy();

        // 请代理: 静态代理,代理类真实存在
        Happy wp = new WangPo(jl);// wp:代理对象   WangPo类: 代理类   Happy接口: 协议  JinLian: 被代理类
        wp.happy();
    }
}

三、动态代理

概述 : 动态代理就是直接通过反射生成一个代理对象,代理对象所属的类是不需要存在的
上面的静态代理,是已经把代理对象写死了。
现在是运行的时候创建一个代理对象来增强方法。

  • 动态代理的获取:

    ​ jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象

四、动态代理相关api

Java.lang.reflect.Proxy类可以直接生成一个代理对象
常用方法:

  • Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
    • 参数1:ClassLoader loader 被代理对象的类加载器
    • 参数2:Class<?>[] interfaces 被代理对象的要实现的接口
    • 参数3:InvocationHandler h (接口)执行处理类
    • 返回值: 代理对象
    • 前2个参数是为了帮助在jvm内部生成被代理对象的代理对象,第3个参数,用来监听代理对象调用方法,帮助我们调用方法
  • InvocationHandler中的Object invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会执行
    • 参数1:代理对象(慎用)
    • 参数2:当前执行的方法
    • 参数3:当前执行的方法运行时传递过来的参数
    • 返回值:当前方法执行的返回值

五、案例

public interface Happy {
// 协议,被代理者需要代理的方法,就定义在这里,然后让代理者和被代理者去实现
    // 被代理者实现: 为了确保和代理者实现的方法一致
    // 代理者实现: 为了增强被代理者的这些方法
    public abstract void happy();
}


public class JinLian implements Happy {

    public void happy(){
        System.out.println("金莲在happy...");
    }

}


public class XiMen {
    public static void main(String[] args) {

        // 使用动态代理直接产生金莲的代理对象
        // 动态代理: 代理类是不真实存在的,但代理类是一定实现了被代理类的接口的
        // p:动态代理产生的代理对象 代理类是不真实存在的
        Happy p = (Happy) Proxy.newProxyInstance(JinLian.class.getClassLoader(), JinLian.class.getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 回调方法: 当代理对象调用了方法,就会来执行该invoke方法, 在该方法中就可以增强被代理类的方法
                // 参数1: 生成的代理对象 这里就是p这个代理对象 (慎用)
                // 参数2: 当前代理对象执行的方法 这里method就是happy()方法对象
                // 参数3: 当前代理对象执行的方法,传入的实际参数
                // 返回值:当前代理对象执行的方法的返回值
                // System.out.println("invoke");
                if (method.getName().equals("happy")){
                    System.out.println("王婆以做头发的名义把金莲和西门约到房间...");
                    // 通过反射来调用被代理对象的方法
                    method.invoke(jl);
                    System.out.println("王婆打扫战场...");
                }
                return null;
            }
        });

        // 代理happy
        p.happy();// 无参数
    }
}

六、动态代理增强方法

/*
            对Collection接口进行代理,以前的remove(Object obj)方法是删除集合中第一次出现的元素
            (比如集合中有多个“abc”,调用remove(“abc”)后只会删除一个元素)。
            代理后,要求在调用remove(Object obj)方法后,能够删除集合中所有匹配的元素。【动态代理】
         */
        // 创建ArrayList集合
        Collection<String> col = new ArrayList<>();
        // 添加元素
        col.add("abc");
        col.add("abc");
        col.add("bac");
        col.add("abc");
        col.add("abc");
        col.add("abc");
        System.out.println("删除前:" + col);// 删除前:[abc, abc, bac, abc, abc, abc]
        / 动态代理增强remove方法
        Collection<String> proxy = (Collection<String>) Proxy.newProxyInstance(col.getClass().getClassLoader(), col.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /*
                    invoke方法:
                        参数1proxy:表示生成的代理对象,一般不用
                        参数2method:表示代理对象调用的方法
                        参数3args:表示代理对象调用方法传入的实际参数
                        返回值:表示代理对象调用方法的返回值
                 */
                // 代理对象调用方法就会来到这里,所以增强方法的代码就写在这,就可以了
                // 被代理对象执行一次代理对象调用的方法,来确定返回值  删除一个

                Object res = method.invoke(col, args);// col.remove("abc")  col.toArray();


                if (method.getName().equals("remove")) {
                    // 删除剩余的
                    // 获取col集合对象的迭代器
                    Iterator<String> it = col.iterator();
                    // 使用迭代器进行遍历
                    while (it.hasNext()) {
                        // 在循环中,判断遍历出来的元素是否是要删除的元素
                        String e = it.next();
                        if (e.equals(args[0])) {
                            // 如果是,就删除
                            it.remove();
                        }
                    }
                }

                if (method.getName().equals("toArray")) {
                    System.out.println("增强toArray方法...");

                }

                return res;

            }
        });

        // 代理对象删除元素
        boolean res = proxy.remove("abc");
        System.out.println(res);//true
        System.out.println("删除后:" + col);// 删除后:[bac]


        Object[] arr = proxy.toArray();
        System.out.println(arr);
        System.out.println(Arrays.toString(arr));


       /*
        // 集合对象删除元素
        boolean res = col.remove("abc");
        System.out.println(res);// true
        System.out.println("删除后:"+col);// 删除后:[abc, bac, abc, abc, abc]*/

二、JDK8新特性

一、方法引用

方法引用使用一对冒号 :: , 方法引用就是用来在一定的情况下,替换Lambda表达式

二、方法引用基本使用

使用场景:

  • 如果一个Lambda表达式大括号中的代码和另一个方法中的代码一模一样,那么就可以使用方法引用把该方法引过来,从而替换Lambda表达式
  • 如果一个Lambda表达式大括号中的代码就是调用另一方法,那么就可以使用方法引用把该方法引过来,从而替换Lambda表达式
    使用步骤
    1.分析lambda表达式大括号是不是只调用了某个方法
    2.是的话,就使用方法引用替换lambda表达式,如果不是就不能替代
    3.分析引用方法的类型
    4,确定类型后,根据该方法引用格式来引用即可

三、写法

public class Test {

    public static void show(){
        System.out.println("线程执行了");
    }

    public static void main(String[] args) {

        // 创建并启动线程
        new Thread(()->{
            System.out.println("线程执行了");
        }).start();

        // 发现上述的Lambda表达式大括号中的内容和Test类的show方法的方法体一模一样,符合方法引用替换Lambda表达式的场景
        new Thread(Test::show).start();

        System.out.println("=========================================");

        new Thread(()->{
            Test.show();
        }).start();

        // 发现上述的Lambda表达式大括号中的内容就是调用Test类的show方法,符合方法引用替换Lambda表达式的场景
        new Thread(Test::show).start();
    }
}

四、方法引用的分类

每个方法的引用格式不一样
1.构造方法引用

public class Test2 {
    public static void main(String[] args) {
        //创建集合
        ArrayList<String> list = new ArrayList<>();
        list.add("杨");
        list.add("迪");
        list.add("陈");

        // 需求: 把集合中的元素转换为Person对象,打印输出
        list.stream().map(s-> new Person(s)).forEach(s-> System.out.println(s));

        System.out.println("======================");
       
        list.stream().map(Person::new).forEach(s-> System.out.println(s));
	
    }
}

2.静态方法引用

public class Test2 {
    public static void main(String[] args) {
        //创建集合
        ArrayList<String> list = new ArrayList<>();
        list.add("110");
        list.add("111");
        list.add("112");

        // 需求:把集合中的元素转换为int类型,打印输出
        list.stream().map(s-> Integer.parseInt(s)).forEach(s-> System.out.println(s));

        System.out.println("======================");

        list.stream().map(Integer::parseInt).forEach(s-> System.out.println(s));

    }
}

3.对象成员方法引用: 带参数

public class Test2 {
    public static void main(String[] args) {
        //创建集合
        ArrayList<String> list = new ArrayList<>();
        list.add("杨紫");
        list.add("迪丽热巴");
        list.add("陈钰琪");

        // 需求:把集合中所有元素打印输出
        list.stream().forEach(s-> System.out.println(s));

        System.out.println("=================================");
        
        list.stream().forEach(System.out::println);

    }
}

4.类的成员方法\成员方法(无参数)

ublic class Test2 {
    public static void main(String[] args) {
        //创建集合
        ArrayList<String> list = new ArrayList<>();
        list.add("杨紫");
        list.add("迪丽热巴");
        list.add("陈钰琪");

        // 需求: 把集合中的元素转换为该元素对应的字符长度,打印输出
        list.stream().map(s->s.length()).forEach(System.out::println);

        System.out.println("=================================");
		//会默认的用参数s去调用String类中的length()方法
        list.stream().map(String::length).forEach(System.out::println);

    }
}

5.总结使用方法引用的步骤
1.分析要写的Lambda表达式的大括号中是否就是调用另一个方法
2.如果是,就可以使用方法引用替换,如果不是,就不能使用方法引用
3.确定引用的方法类型(构造方法,成员方法,静态方法,类的成员方法)
4.按照对应的格式去引用:
构造方法: 类名::new
成员方法(有参数): 对象名::方法名
静态方法: 类名::方法名
类的成员方法\成员方法(无参数): 类名::方法名

三、base64

一、解释

Base64是jdk8提出的一个新特性,可以用来进行按照一定规则编码和解码

二、Base64编码和解码的相关方法

  • 编码的步骤:

    • 获取编码器
    • 调用方法进行编码
  • 解码步骤:

    • 获取解码器
    • 调用方法进行解码
  • Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:

    • 基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
    • URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
    • MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用’\r’并跟随’\n’作为分割。编码输出最后没有行分割。
      • 获取编码器和解码器的方法
    static Base64.Decoder getDecoder() 基本型 base64 解码器。
    static Base64.Encoder getEncoder() 基本型 base64 编码器。
    
    static Base64.Decoder getMimeDecoder() Mime型 base64 解码器。
    static Base64.Encoder getMimeEncoder() Mime型 base64 编码器。
    
    static Base64.Decoder getUrlDecoder() Url型 base64 解码器。
    static Base64.Encoder getUrlEncoder() Url型 base64 编码器。
    
  • 编码和解码的方法:

    Encoder编码器:  encodeToString(byte[] bys)编码
    Decoder解码器:  decode(String str) 解码
    

三、案例

基本

public class Test1 {
    public static void main(String[] args) {
        // 使用基本型的编码器和解码器对数据进行编码和解码:
        // 1.获取编码器
        Base64.Encoder encoder = Base64.getEncoder();

        // 2.对字符串进行编码
        String str = "name=中国?password=123456";
        String str1 = encoder.encodeToString(str.getBytes());

        // 3.打印输出编码后的字符串
        System.out.println("编码后的字符串:"+str1);

        // 4.获取解码器
        Base64.Decoder decoder = Base64.getDecoder();

        // 5.对编码后的字符串进行解码
        byte[] bys = decoder.decode(str1);
        String str2 = new String(bys);

        // 6.打印输出解码后的字符串
        System.out.println("解码后的字符串:"+str2);
    }
}

URL

public class Test2 {
    public static void main(String[] args) {

        // 使用URL型的编码器和解码器对数据进行编码和解码:
        // 1.获取编码器
        Base64.Encoder encoder = Base64.getUrlEncoder();

        // 2.对字符串进行编码
        String str = "name=中国?password=123456";
        String str1 = encoder.encodeToString(str.getBytes());

        // 3.打印输出编码后的字符串
        System.out.println("编码后的字符串:"+str1);

        // 4.获取解码器
        Base64.Decoder decoder = Base64.getUrlDecoder();

        // 5.对编码后的字符串进行解码
        byte[] bys = decoder.decode(str1);
        String str2 = new String(bys);

        // 6.打印输出解码后的字符串
        System.out.println("解码后的字符串:"+str2);
    }
}

MIME

public class Test3 {
    public static void main(String[] args) {
        // 使用MIME型的编码器和解码器对数据进行编码和解码:
        // 1.获取编码器
        Base64.Encoder encoder = Base64.getMimeEncoder();

        // 2.对字符串进行编码
        String str = "";
        for (int i = 0; i < 100; i++) {
            str += i;
        }
        System.out.println("编码前的字符串:"+str);

        String str1 = encoder.encodeToString(str.getBytes());

        // 3.打印输出编码后的字符串
        System.out.println("编码后的字符串:"+str1);

        // 4.获取解码器
        Base64.Decoder decoder = Base64.getMimeDecoder();

        // 5.对编码后的字符串进行解码
        byte[] bys = decoder.decode(str1);
        String str2 = new String(bys);

        // 6.打印输出解码后的字符串
        System.out.println("解码后的字符串:"+str2);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值