01 接口
在Java中,接口是对希望符合这个接口的类的一组需求,注意接口不是类,虽然在功能上说,接口可以被看作为没有实例字段的抽象类。由此也可见,接口是不会有实例字段的,接口中也绝不会实现方法。对于具体的方法以及实例字段的实现应该由实现接口的那个类来实现。注意接口中的方法都是public。
为什么要使用接口提供方法?:由于Java是一个强类型语言。在我们调用方法的时候,编译器要能检查这个方法确实存在。但同时我们也应该注意到,同一个方法(例如sort方法),可能会有多个类都可以提供,此时若我们直接使用方法,就必须非常注意,否则就会抛出异常。此外我们也知道,Java中的类是单继承,多实现的,这也是使用接口的一个好处。
接口的属性:接口中可以声明变量,但这个接口变量必须引用实现了这和接口的类对象。与建立类的继承层次一样,我们可以扩展接口,允许有多条接口链。
接口与抽象类:上文提到接口与抽象的类似,可以表示一类的通用属性。但由于抽象类表示通用属性存在一个严重的问题。每个类只能扩展一个类。Java不支持多种继承。
静态与私有方法:接口中允许有静态方法,同时接口中的方法可以是私有的(JDK9中)。这个私有方法可以是静态也可以是实例。
默认方法:用default修饰可以为接口中提供一个默认方法。这可以帮助我们在接口中新增一个方法时,过去实现这个接口的类可以正常编译(默认方法会被接口的每一个具体实现覆盖)。
解决默认方法冲突:
1.超类优先
2.接口冲突,必须覆盖方法解决冲突
对象克隆:我们在copy一个对象时,之前是建立一个副本,不过这个副本与原对象的引用指向同一个对象。而使用clone方法,则会复制出一个新对象,其状态与原对象相同。不过由于clone方法时受保护的,只有同一类可以克隆这个类的对象。
浅克隆:克隆对象是不可变的,这是相对安全的。
深拷贝:子对象可变,此时我们需要确定:
1.默认克隆是否满足要求
2.是否可以在可变的子对象上调用克隆来修补默认的克隆方法
3.或许不该使用克隆
注意:子类只能调用受保护的克隆方法(clone方法来自object)来克隆他自己的对象,需要重新定义为public才能允许所有方法克隆。
02lambda表达式
引入lambda表达式的原因:由于lambda表达式是一个可以传递的代码块,而Java作为一个面向对象的语言,我们是需要去构造一个对象的,这个对象的类需要一个方法去包含所需的代码。这时lambda表达式提供了途径。
语法:参数,箭头(->)。以及使用{},在这之中放入我们需要执行的代码。
函数式接口:对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式,这种接口称之为函数式接口。在这里我们把lambda表达式看作一个函数,例如实现一个比较字符串长短方法:Arrays.sort(words,(first,second) -> first.length()-second.length());在这里该方法会接受类对象,并调用方法执行这个lambda体。此外,lambda表达式可以转换为接口(例如直接new 一个对象传递参数,以及lambda体,并实现方法)。不过在Java中lambda表达式也只能转换为函数式接口。
方法引用:类似System.out::println就是一个方法引用,其指示编译器生成一个函数式接口的实例,覆盖这个接口的抽象方法来调用给定方法。其格式主要有三种:
object::instanceMethod
Class::instanceMethod
Class::staticMethod
在方法中使用this参数,使用super也是合法的
super::instanceMethod变量作用域:由于lambda表达式包含:一个代码块,参数,自由变量值三个部分。如果在lambda表达式中更改变量,并执行多个动作时就会不安全。故lambda表达式中捕获的变量必须实际为事实最终变量。注意:lambda表达式的体与嵌套块有相同的作用域。lambda表达式中也不能有同名局部变量。
处理lambda表达式:
延迟执行,原因为:
在一个单独线程中运行代码
多次运行代码
在算法的适当位置运行代码
发生某种情况时执行代码
只在必要时才运行代码
03内部类
内部类(inner Class)是定义在另一个类中的类。我们使用内部类的主要原因是:
1.内部类可以对同一个包中的其他类隐藏。
2.内部类方法可以访问定义这个类的作用域中的数据,包括原本私有的数据。
使用内部类访问对象的状态:一个内部类方法可以访问自身的数据字段,也可以访问创建它的外围类对象的数据字段,为此,内部类的对象总有一个隐式引用,指向创建它的外部类对象。
内部类的特殊语法规则:
编写内部类的构造器:
outerObject. new InnerClass(construction parameter)
在外围作用域之外引用内部类:
OutClass.InnerClass
由于内部类天生有更大的访问权限,其天生比常规类功能更加强大。
局部内部类:在局部方法中定义类。声明局部类时不能有访问说明符。局部类的一个重要优势,即其对外部世界完全隐蔽。
匿名内部类:若我们只想创建一个对象,可以不指定类的名字,这被称为匿名内部类。其一般语法如下:
new SuperType(construction parameter){
inner class method and data
}
由于构造器必须与类名相同,故匿名内部类不能有构造器(不过可以提供一个对象初始化块)。
静态内部类:将内部类声明为静态static,可以通过此不生成对外围对象的引用。注意,只要内部类不需要访问外围对象,其就应该被声明为静态的。与常规类不同,静态内部类可以有静态字段与方法。并且,在接口中声明的内部类自动是public和static的。
04代理(proxy)
使用代理可以在运行时创建实现一组给定的新类。只有在编译器无法确定需要实现哪个接口时我们才必须使用代理。
代理类包含以下方法:
1.指定接口的全部方法
2.Object类中的全部方法
创建代理对象:使用newProxyInstance方法,传递三个参数:
1一个类加载器(class loader
2一个class对象数组
3.一个调用处理器
代理类的特性:由于代理类是在程序运行过程中创建的,一旦创建,它们就变成了常规类。
所有代理类都扩展proxy类
所有代理类都覆盖object类的toString,equals,hashcode方法
若代理类没有名字,虚拟机会生成一个$Proxy开头的类名。
对于一个特定的类加载器和预设的一组接口来说,只能有一个代理类。
代理类总是public和final的