一、接口的概念

接口:用来描述类具有什么功能,而并不给出每个功能的具体实现。
内部类:内部类定义在另外一个类的内部,其中的方法可以访问包含它们的外部类的域,主要用于设计具有相互协作关系的集合。

在Java中,接口不是类,而是对类的一组需求描述。如果类遵循某个特定接口,那么就履行这项服务。接口中的所有方法自动地属于public。因此,在接口中声明方法时,不必提供关键字public。在接口中还可以定义常量。接口绝不能含有实例域。

在JavaSE8之前,也不能在接口中实现方法。现在已经可以在接口中提供简单方法了。当然,这些方法不能引用实例域。提供实例域和方法实现的任务应该由实现接口的那个类来完成。因此,可以将接口看成是没有实例域的抽象类。在实现接口时,必须把方法声明为public;否则,编译器将认为这个方法的访问属性是包可见性的。

二、接口的特性

接口不是类,尤其不能使用new运算符实例化一个接口:x=new Comparable(. . .);//ERROR
然而,尽管不能构造接口的对象,却能声明接口的变量:Comparable x;
接口变量必须引用实现了接口的类对象:x=new Employee(. . .);

接下来,如同使用instanceof检查一个对象是否属于某个特定类一样,也可以使用instance检查一个对象是否实现了某个特定的接口:

if(anObject instanceof Comparable){. . .}

与可以建立类的继承关系一样,接口也可以被扩展。这里允许存在多条从具有较高通用性的接口到较高专用性的接口的链。
例如,假设有一个称为Moveable的接口:

public interface Moveable
{
 void move(double x,double y);
}

然后,可以以它为基础扩展一个叫做Powered的接口:

public interface Powered extends Moveable
{
double milePerGallon();
}

三、接口与抽象类

使用抽象类表示通用属性存在这样一个问题:每个类只能扩展于一个类。假如Employee类已经扩展于一个类,例如Person,它就不能再像下面这样扩展这样第二个类:
class Employee extends Person,Comparable//Error
但每个类可以像下面这样实现多个接口:
class Employee extends Person implements Comparable//OK

在Java SE8中,允许在接口中增加静态方法。目前为止,通常的做法都是将静态方法放在伴随类中。可以为接口方法提供一个默认实现。必须用default修饰符标记这样一个方法。

public interface Comparable<T>
{
  default int compreTo(T other){ return 0;}
}

四、接口与回调

回调是一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采取的动作。例如,可以指出在按下鼠标或选择某个菜单项时应该采取什么行动。对一个对象数组排序,前提是这些对象是实现了Comparable接口的类的实例。例如,可以对一个字符串数组排序,因为String类实现了Comparable,而且String.compareTo方法可以按字典顺序比较字符串。

如果我们希望按长度递增的顺序对字符串进行排序,肯定不能让String类用两种不同的方式实现compareTo方法,更何况,String类也不应由我们来修改。

比较器是实现了Comparator接口的类的实例。

public interface Comparator<T>
{
 int compare(T first,T second);
}

要按长度比较字符串,可以定义一个实现Comparator的类:

class LengthComparator implenments Comparator<String>
{
 public int compare(String first,String second){
     return first.length()-second.length();
}
}

原变量和副本都是同一个对象的引用。任何一个变量改变都会影响另一个副本。如果希望copy是一个新对象,它的初始状态与origin相同,但是之后它们各自会有自己不同的状态,这种情况下就可以使用clone方法。

默认的克隆操作是"浅拷贝",并没有克隆对象中引用的其他对象。如果原对象和浅克隆对象共享的子对象是不可变的,那么这种共享就是安全的。如果子对象属于一个不可变的,如String。
不过,通常子对象都是可变的,必须重新定义clone方法来建立一个深拷贝,同时克隆所有子对象。

五、lambda表达式

lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。
lambda表达式形式:参数,箭头(->)以及一个表达式。
如果代码要完成的计算无法放在一个表达式中,就可以想写方法一样,把这些代码放在{}中,并包含显示的return语句。例如:

(String first,Second second)->
{ 
 if(first.length()<second.length()) return -1;
 else if(first.length()>second.length()) return 1;
 else return 0;
}

即使lambda表达式没有参数,仍然提供空括号,就像无参数方法一样:

()->{for(int i=100;i>=0;i--)System.out.println(i);}

如果可以推导出first和second必然是字符串,因为这个lambda表达式将赋给一个字符串比较器。如果方法只有一个参数,而且这个参数的类型可以推导得出,那么甚至还可以省略小括号。无需指定lambda表达式的返回类型。lambda表达式的返回类型总是会由上下文推导得出。在Java中,lambda表达式就是闭包。

使用lambda表达式的重点是延迟执行。之所以希望以后再执行代码,还有很多原因,如:

  • 在一个单独的线程中运行代码
  • 多次运行代码
  • 在算法的适当位置运行代码
  • 发生某种情况时执行代码
  • 只在必要时才运行代码

六、内部类

内部类是定义在另一个类中的类。
使用内部类的三个原因:

  • 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
  • 内部类可以对同一个包中的其他类隐藏起来。
  • 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。

内部类即可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。假如只创建这个类的一个对象,就不必命名了。这种类就被称为匿名内部类。由于构造器的名字必须与类名相同,而匿名类没有类名,所以,匿名类不能有构造器。取而代之的是,将构造器参数传递给超类构造器。尤其是在内部类实现接口的时候,不能有任何构造参数。程序员习惯的做法是用匿名内部类实现事件监听器和其他回调。如今最好使用lamdba表达式。