目录

  • 静态代理模式
  • lambda 表达式


在前面使用Runnable 接口创建线程的时候,使用到了Thread 类的静态代理模式,把这段代码再拿出来看看。

public class Test2 implements  Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程"+i);
        }

    }
    public static void main(String[] args) {
        //创建runnable 接口的实现类对象
        Test2 test2 = new Test2();
        //创建线程对象-- 静态代理  
        Thread t1 = new Thread(test2);
        t1.start();//
        //上面的代码和与下面等效
        new Thread(new Test2()).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("我在写代码"+i);
        }
    }
}

静态代理模式

静态代理模式: 真实对象(目标对象)和 代理对象

其实就是你要干很多事情,就比如你今天要交三篇论文,可是论文排版和装订你没精力去干了,那怎么办,找个人帮你。但他帮你得有一套标准规范,那就是你们两个都要实现同一个接口,重写接口里面的方法,而且人家帮你只能帮你干杂货,真正写论文这件事还得自己来!

这种模式可以实现帮助目标对象完成一些前期的准备工作和后期的善后工作,但是核心的业务逻辑仍然是由目标对象完成。

好处:代理对象可以做很多真实对象做不了的事情,真实对可以专注做自己的事情。

静态代理模式的要求:

  • 目标对象和代理对象都要实现同一个接口
  • 代理对象要代理真实角色,要持有代理角色的引用

多线程里面就用到了静态代理
Thread 实现了静态代理的模式
自定义线程类实现Runable接口,Thread类也实现了Runalbe接口,在创建子线程的时候,传入了自定义线程类的引用,再通过调用start()方法,调用自定义线程对象的run()方法。实现了线程的并发执行。

Java 静态类 使用多线程 java多线程调用静态方法_System


在这里需要说明以下,最后调用的start方法,本质上是去执行run 方法。

也就是代理对象最后执行的方法是代理对象继承接口后重写的方法。

而代理对象重写的方法里面有 前期杂活方法+让真实对象自己去做(为什么可以做到让真实对象自己去做?–因为我拿到了真实对象的引用)+后期收尾工作

静态代理模式设计模板

//共同接口
interface CommentInterface{
    public abstract void todo();
}

//真实角色
class RealityRole implements CommentInterface{
    @Override
    public void todo() {
        System.out.println("真实角色的功能");
    }
}

//代理角色
class ProxyRole implements CommentInterface{
    //持有代理角色的引用
    private CommentInterface realityRole;

    //传入一个真实角色
    public ProxyRole(CommentInterface role){
        this.realityRole = role;
    }

    @Override
    public void todo() {
        doBefore(); //在真实角色功能运行之前,代理角色做准备工作
        realityRole.todo();  //执行真实角色的功能--还是去让真实角色执行功能去了
        doAfter(); //代理角色的收尾工作
    }
    private void doBefore() {
        System.out.println("准备工作");
    }
    private void doAfter() {
        System.out.println("收尾工作");
    }
}

//静态代理
public class StaticProxy {
    public static void main(String[] args) {
        //创建真实角色对象
        CommentInterface realityRole = new RealityRole();
        //创建代理角色对象,并传入真实对象
        ProxyRole proxyRole = new ProxyRole(realityRole);
        //代理角色工作,本质调用的还是真实角色的功能
        proxyRole.todo();
    }
}

lambda 表达式

说到静态代理模式,就得说一下lambda表达式
Thread 类就是实现了静态代理的模式,而人类的精髓就是懒,懒就是科技创新的动力。lambda 表达式就体现懒,懒得给类取名字就匿名内部类,之后懒得连方法名也不想写就使用lambda 表达式。

我们经常会见到下面出现的这些写法

Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我在学多线程1");
            }
        });
        thread.start();


        Thread thread1 = new Thread(
                ()-> { System.out.println("我在学多线程2"); } // lambda 表达式
        );
        thread1.start();


        new Thread(() -> System.out.println("我在学多线程3")).start();

使用lanbda 表达式还是有条件约束的,虽然代码简洁,但理解起来还是需要花费一些功夫的。

  • lambda 的出现的意义:避免匿名内部类定义过多、代码简洁性、去掉无意义代码,留下核心逻辑。
  • 使用lambda 表达式创建接口对象对象,必须要求这个接口是函数式接口

函数式接口(Functional Interface)是学习java 8 lambda 表达式的关键所在。

函数式接口的定义:一个接口里面只包含唯一一个抽象方法。

而恰恰Runnable 接口就是一个函数式接口,所以可以使用lambda表达式创建一个接口对象。

() -> System.out.println("我在学多线程3")

前面的一对小括号即run方法的参数(无),代表不需要任何条件;
中间的一个箭头代表将前面的参数传递给后面的代码;
后面的代码即业务逻辑代码。

一篇关于lambda 表达式的详解,可以参考补充

使用lambda 表达式的要点

  1. 接口必须是函数式接口,这样才能创建一个接口对象。(所谓接口对象,其实new 一个类,该类实现接口,重写方法)
  2. 如果有参数的话,以去掉参数类型 ,参数类型可以不同。
    要去掉都必须都去掉,必须加() 比如(int 啊,double b) 写成(a,b)
    但是如果只有一个参数(int a) 就可以把()和类型都去掉,多参数()不能去掉。
  3. lambda 表达式只能有一行代码的情况下才能简化称为一行去掉{}。如果有多行,就得有代码块包裹{}
iLove = a-> {
            System.out.println("I Love You!"+a);
        }
iLove = a-> System.out.println("I Love You!"+a);

学习lambda 表达式如果看不懂的话,可以尝试把代码还原回去,一步步还原成原来的样子就会清楚一点。

下面代码给出lambda的推导,其实也就是人类式怎么一步步去偷懒的。

  • 先在外面写一个实现类 通过接口引用指向一个对象然后调用
  • 之后偷懒 将这个类变成静态内部类 – 把实现类放到public 类里面
  • 在偷懒把类写到方法里面–变成局部内部类
  • 再偷懒,连类名都懒得写,而且这个类只用一次就写成匿名内部类 :没有类的名称,必须借助接口或者父类实现
    前面的都是new 实现类,这里是new 一个接口
  • new 的接口名也不想写,直接写成lambda 表达式
/**
 * 推导lambda 接口表达式
 */

//1 定义一个函数式接口
interface  ILike{
    void lambda(); //  默认是public abstract
}
//2 实现类
class Like implements  ILike{
    @Override
    public void lambda() {
        System.out.println("我在学lambda");
    }
}

public class TestLambda1 {

    //3 静态内部类 -- 把实现类放到public 类里面
    static class Like2 implements  ILike{
        @Override
        public void lambda() {
            System.out.println("我在学lambda2");
        }
    }

    public static void main(String[] args) {
        ILike iLike = new Like() ; // 接口引用指向一个对象
        iLike.lambda();

        Like2 like2 = new Like2();
        like2.lambda();

        //4 局部内部类
        class Like3 implements  ILike{
            @Override
            public void lambda() {
                System.out.println("我在学lambda3");
            }
        }
        Like3 like3 =  new Like3();
        like3.lambda();

        //5 匿名内部类  没有类的名称,必须借助接口或者父类实现
        //前面的都是new 类,这里是new 一个接口
        ILike like4 = new ILike() {
            @Override
            public void lambda() {
                System.out.println("我在学lambda4");
            }
        };
        like4.lambda(); //写了匿名内部类之后,还需要调用
        
        //6 lambda 简化
        ILike like5 = ()-> {
            System.out.println("我在学lambda4");
        };
        like5.lambda();



    }

}