接口是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。设计目的是解决多继承的问题
接口中所有方法时 public 不用现实声明
java.lang.Comparable
int compareTo(T other) 当前对象与 other 比较,对象小于 other 则返回负值;相等则返回 0 ;否则返回正值。
java.util.Arrays 有方法
static void sort(Object[] a) 使用 mergesort 算法对数组中的元素进行排序。要求数组中的元素属于实现了 Compareable 接口的类,并且元素之间是可比较的。
语言标准规定:对于任意的 x 和 y ,实现必须能够保证 sgn(x.compareTo(y)) = -sgn(y.compareTo(x))。(也就是说,如果 y.compareTo(x) 抛出一个异常,那么x.compareTo(y) 也应该抛出一个异常。)这里的“sgn”是一个数值的符号:如果 n 是负值,sgn(n) 等于-1;如果 n 是0,sgn(n) 等于0;如果 n 是正值,sgn(n)等于1简单地讲,如果调换compareTo的参数,结果的符号也应该调换(而不是实际值)
与 equals 方法一样,在继承过程中有可能会出现问题,因为涉及到子类与父类比较,子类之间的比较。可以在比较对象前,检查是否属于同一类。或者在父类中将比较方法定义为 final,不允许子类覆盖。
接口不能实例化,但可以声明变量引用实体类的对象。接口中不能包含实例域,可以包含常量。 接口中的域自带 public static final。 可以使用静态方法,用来替代伴随工具类。
default 方法可以在接口内添加方法的具体实现。默认方法的一个重要用途是接口演化,给已经被使用的接口添加方法时,如果没有提供默认实现,则实现此接口的类必须去实现新的方法,破坏了兼容性。
默认方法与基类的方法冲突时,同名且有相同类型参数的默认方法会被忽略。
如果一个超接口提供了一个默认方法,另一个接口提供了一个同名而且参数类型 (不论是否是默认参数)相同的方法, 必须覆盖这个方法来解决冲突。
两个接口如何冲突并不重要。 如果至少有一个接口提供了一个实现, 编译器就会报告错误, 而程序员就必须解决这个二义性。
一种程序设计模式,可以指出某个特定事件发生时应该采取的动作。
public class TimePrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone, the time is "+new Date());
Toolkit.getDefaultToolkit().beep();
}
}
TimePrinter 实现了接口 ActionListener 实现了 actionPerformed 方法,即告诉程序发生了某件事调用了这个方法时,进行如何操作。
有的时候需要自定义一些比较规则用于数组排序 Arrays.sort(T[] a, Comparator super T> c)
则可以定义一个类实现 Comparator 接口,实现 compara 方法
Object 实现 clone 方法,只是一个个域进行拷贝。如果对象中所有的域都是基本类型,拷贝没有问题。但如果对象包含子对象的引用,拷贝域则会得到相同对象的另一个引用。这样一来,克隆与原对象仍然会共享一些信息。 如果共享的子对象是不可改变的,这种共享是安全的。或者在对象生命周期中,子对象所包含的都是不变的常量。
但是通常情况下,子对象都是可变的,必须重新定义 clone 方法来建立一个深拷贝,同时克隆所有子对象。如果需要使用 clone 方法,必须实现 Cloneable 接口,重新定义 clone 方法,并指定访问权限为 public。
即使 clone 的默认实现能够满足要求,还是需要实现 Cloneable 接口,将 clone 重新定义为 public ,再调用 super.clone()。
将一个代码块传递到某个对象,代码块将在未来某个时候调用。Java 中传递一段代码并不容易,不能直接传递代码段,必须构造一个对象,这个对象的类需要有一个方法能包含所需的代码。
lambda 表达式就是一个代码块,以及必须传入代码的变量规范。
参数,箭头(->)以及一个表达式。如果代码要完成的计算无法放到一个表达式中,可以像写方法一样,把代码放到{}中并包含显式的 return 语句。
即使没有参数仍然要提供空括号,如果能推导出一个 lambda 表达式的参数类型,则可以忽略其类型。如果方法只有一个参数,且参数类型可以推导出来,小括号也可以省略。无需指定 lambda 表达式的返回类型,lambda 表达式的返回类型总是会由上下文推导得出。
对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个 lambda 表达式。这种接口称为函数式接口(functional interface)
通常遇到方法需要一个标记接口为参数时,会创建一个类去实现接口,再用接口类型的变量引用类的对象作为参数。更直接一点的办法是,使用一个匿名类实现接口,并实现方法。而使用 lambda表达式则直接传入方法,对应的就是函数式接口的那个方法的实现。
例如下面代码的 lambda 表达式是等价的,System.out::println 是方法引用。
Timer t = new Timer(1000,event -> System.out.println(event));
Timer t1 = new Timer(1000,System.out::println);
要用 :: 操作符分隔方法名与对象或类名。主要有 3 种情况:
object::instanceMethod
Class::staticMethod
Class::instanceMethod
前两种情况中,方法引用等价于提供参数的 lambda 表达式。例如
System.out::println //等价于 x -> System.outprintln(x)
Math::pow //等价于(x,y) ->Math.pow(x,y)
第三种情况,第 1 个参数会成为方法的目标。例如
String::compareToIngoreCase //等价于(x,y) ->x.compareToIgnoreCase(y)
可以在方法引用中使用 this 例如
this::equal //等价于 x ->this.equal(x)。
使用 super 也是合法的。
与方法引用类似,不过方法名为new
int[]::new //等价于 x-> new int[x]
lambda 表达式可以捕获外围作用域中变量的值。在 Java 中,要确保所捕获的值是明确定义的,有一个限制,即 lambda 表达式中只能引用不会改变的变量。lambda 表达式中捕获的变量必须实际上是最终变量,即变量初始化后不会再为它赋值。
使用 lambda表达式的重点是延迟执行。方法在被当做函数时接口调用时才会执行。可能有下列原因需要延迟执行
在一个单独的线程中运行代码
多次运行代码
在算法的适当位置运行代码
发生某种情况时执行代码
只在必要时才运行代码
下表是常用的函数式接口
函数式接口
参数类型
返回类型
抽象方法名
描述
其他方法
Runnable
无
void
run
作为无参数或返回值的动作运行
Supplier
无
T
get
提供一个 T 类型的值
Consumer
T
void
accept
提供一个 T 类型的值
andThen
BiConsumer
T,U
void
accept
处理 T 和 U 类型的值
andThen
Function
T
R
apply
有一个 T 类型参数的函数
compose, andThen, identity
BiFunction
T,U
R
apply
有 T 和 U 类型参数的函数
andThen
UnaryOperation
T
T
apply
类型 T 上的一元操作符
compose, andThen, identity
BinaryOperatior
T,T
T
apply
类型 T 上的二元操作符
andThen, maxBy, minBy
Predicate
T
boolean
test
布尔值函数
and, or, negate, isEqual
BiPredicate
T,U
boolean
test
有两个参数的布尔值函数
and, or, negate
手机扫一扫
移动阅读更方便
你可能感兴趣的文章