《Java核心技术》第六章读书笔记(接口、lambda表达式和内部类)

第六章 接口、lambda表达式和内部类

接口

  • 接口中的方法自动的属于public,因此在接口声明的方法中,不必提供关键字public。

  • 接口绝不能含有实例域

  • 接口不是类,不能使用new运算符实例化一个接口

    x = new Compareble(...)  // Error
    
  • 尽管不能构造接口对象但是可以声明

    Compareble x // ok
    
  • 判断一个对象是否实现了特定接口 instanceof

    public interface Animal {
        void fly();
    }
    
    public class Bird implements Animal{
        @Override
        public void fly() {
            System.out.println("bird can fly");
        }
    }
    
        @Test
        public void test03(){
            Bird bird = new Bird();
            boolean b = bird instanceof Animal;
            System.out.println(b);
        }
    
  • 接口中不能包含实例域,但是可以包含常量,且自动设为public static final

  • 解决默认方法冲突

    • 超类优先,如果超类提供了一个具体方法,同名而且有相同类型的默认方法会被忽略
    • 接口冲突,如果一个超接口提供了一个默认方法,另一个提供了一个完全相同的方法,必须覆盖重写
  • Clone

    需要实现深拷贝的类要实现Cloneable接口,并且要重写clone方法
    数组中有一个public clone方法,可以建立一个新的深拷贝数组
    

lambda表达式

有三个部分:

  1. 一个代码块
  2. 参数
  3. 自由变量的值,这里指非参数而且不在代码中定义的变量

lambda表达式中,只能引用值不会改变的变量

以上就会报错。因为并发执行多个动作时就会不安全。

另外如果在lambda表达式中引用变量,而这个变量可能在外部改变,这也是不合法的。

注意:

  1. lambda表达式不火的变量必须实际上是最终变量,即这个变量初始化之后就不会再为他赋值。
  2. lambda表达式中不能有两个同名的局部变量

内部类

内部类既可以访问自身的数据域,也可以访问创建他的外围类对象的数据域,内部类的对象总有一个隐式引用,指向了外部类。

内部类声明的所有静态域必须是final,我们希望一个静态域只有一个实例,不过对于每个外部对象,会分别有一个单独的内部类实例,如果不是final可能不唯一。

局部内部类

局部类不能用public或private进行生命,作用域被限定在声明这个局部类的块中。

public class DemoTest{

	 public void start() {

        class TimePrinter implements ActionListener {
            public void actionPerformed(ActionEvent event){
                System.out.println("...");
            }
        }
        TimePrinter listener = new TimePrinter();
        Timer t = new Timer(interval,listener);
        t.start()
    }
}

局部内部类有一个优势,即对外部世界可以完全隐藏起来,除了start()方法,没有任何方法知道内部类的存在。

局部类的方法只能饮用定义为final的局部变量,对他进行初始化后不能够在进行修改。因此,就是的局部变量与在局部类内简历的拷贝保持一致。

匿名内部类

匿名内部类既可以是接口,必须要去实现抽象方法,也可以是类,需要去扩展功能。

双括号初始化,利用了内部类的语法,外层括号建立了一个ArrayList匿名子类,内层括号是一个对象的构造块

代理

利用代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定需要实现那个接口时,才有必要使用。

何时使用代理

使用场景:假设有一个表示接口的class对象,有可能只包含一个接口,他的确切类型在编译时无法知道,想要构造一个实现这些接口的类,但是不能实例化一个接口,需要在程序处于运行状态时定义一个新类。为了解决这个问题,代理类可以在运行时创建全新的类,这样的代理类能实现指定的接口。

它具有指定接口所需要的全部方法。

然而不能在运行时定义这些方法,需要提供一个调用处理器(invocation handler)。调用处理器是为了实现InvocationHandler接口的类对象,在这个接口中只有一个方法:

Object invoke(Object proxy, Method method, Object[] args)

无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数。调用处理器必须给出处理调用的方式。

创建代理对象

要想创建一个代理对象,需要使用Proxy类的newProxyInstance方法,这个方法有三个参数

  1. 一个类加载器(class loader),作为java安全模型的一部分,对于系统类和从因特网上下载下来的类,可以使用不同的类加载器。
  2. 一个Class对象数组,每个元素都是需要实现的接口
  3. 一个调用处理器
代理类的特性

代理类是在程序运行过程中创建的,一旦被创建,就变成了常规类,与虚拟机中的任何其他类没有什么区别。

所有的代理类都扩展于Proxy类,一个代理类只有一个实例域——调用处理器,它定义在Proxy超类中,为了履行代理对象的职责,所需要的任何附加数据都必须存储在调用处理器中。

所有的代理类覆盖了Object类中的方法toString,equals,hashCode,如同所有的代理方法一样,这些方法仅仅调用了处理器的invoke,Object类中的其他方法,clone和getClass并没有被重新定义。

对于特定的类加载器和预设的一组接口来说,只能有一个代理类,也就是说,如果同一个类加载器和接口数组调用两次newProxyInstance方法的话,那么只能得到同一个类的两个对象,一可以利用以下方法获取这个类。

Class proxyClass = proxy.getProxyClass(null,interfaces);

代理类一定是public和final,如果代理类实现的所有接口都是public,代理类就不属于某个特定的包,否则,所有非公有的接口都必须属于同一个包,同时,代理类也属于这个包。

posted @ 2020-10-18 15:07  Jimmyhe  阅读(104)  评论(0编辑  收藏  举报