public interface Comparable
{
int compareTo(Object other);
}
Comparable x=new Comparable()//错误
Comparable x=new Employee(...);//正确
class Employee implements Comparable
{
public int compareTo(Object otherObject)
{
Employee other=(Employee) otherObject;
return Double.compare(salary,other.salary);
}
}
class Employee implements Cloneable,Comparable
Employee同时实现Cloneable接口与Comparable接口。
public interface Path
{
public static Path of(URI uri){...}
public static Path of(String first,String... more){...}
...
}
public interface Comparable<T>
{
default int compareTo(T other)
{
return 0;
}
}
默认方法带来的好处,如同下面的代码而言,不需要操心remove()或者isEmpty()的实现问题。
public interface Iterator<E>
{
boolean hasNext();
E next();
deafult void remove()
{
...;
}
}
默认方法可以调用其他方法
public interface Collection
{
int size();// 一个抽象方法
default boolean isEmpty()
{
return size()==0;
}
}
默认方法可以实现接口演化:已经存在一个接口j,并且有一个类x实现了这个接口j,现在由于某些原因,在接口中又声明一个方法,但是不是一个默认方法,因为类x中没有这个方法,所以对类x的编辑会出错。
默认方法带来的问题
两个接口中同名方法带来的冲突
一个类既继承于一个接口,又继承于一个超类,且超类中有与接口中相同的方法,那么超类中的方法会覆盖掉接口的方法
public interface Person
{
default String getName()
{
return "";
}
}
public interface Named
{
default String getName()
{
return getClass().getName()+"_"+hashCode();
}
class Student implements Person,Named
{
public String getName()
{
//为什么这里不用this,用super,Person只是一个接口
return Person.super.getName();
}
}
String类已经实现了Comparable,但是String.compareTo方法按照字典顺序比较字符串,但现在我希望通过字符串长度去比较。
需要重新定义一个实现Comparator的类(一开始学的时候,总认为comparable与comparator是一个,原来是两个不同的接口,而comparator接口的功能更为强大)
在我现在的认识里,以及结合博客来看,Comparable接口与Comprator接口的最大差别在于Comparable是内部接口,Comparator是外部接口
//在真正写代码的时候,下面的接口定义是不用写的
//因为Comparator已经定义在java.util包中了
//我们只需要导入java。util.Comparator
public interface Comparator<T>
{
int compare(T first,T second);
}
class LengthComparator implements Comparator<String>
{
public int compare(String first,String second)
{
return first.length()-second.length();
}
}
//具体使用时
String[] friends={"Peter","Paul","Mary"};
Arrays.sort(friends,new LengthComparator());
综上所述,排序接口的实现方式有两种
方法一、使用Comparable接口:
让待排序对象所在的类实现Comparable接口,并重写Comparable接口中的compareTo() 。方法缺点是只能按照一种规则排序。
方法二、使用Comparator接口 (推荐使用)
如果一个类要实现java.util.Comparator接口:它一定要实现
int compare(T o1, T o2) 函数,而另一个可以不实现(boolean equals(Object obj)。
使用编写排序方式类实现Comparator接口,并重写新Comparator接口中的compare()方法。优点:想用什么方式排就用什么方式排。
讨论Cloneable接口,我们先来看这一段代码
var original=new Employee("John Public",50000);
Employee copy=original;
copy.raiseSalary(10);
上面的的copy是original变量的一个副本,两者引用的是一个对象,任何对copy的修改都会造成original对象的改变。如果想要copy是一个与original初始状态相同的新的对象,就可以使用clone方法
Employee copy=original.clone();
copy.raiseSalary();
这样copy改变了,但是original却没有改变。但是这样的克隆只是“浅拷贝“,Object类在实现clone方法的时候,只能逐个字段的进行拷贝,但是如果遇到了对象包含子对象的引用,拷贝字段仍然得到的是相同子对象的一个引用。
如果想要实现clone,这个类必须实现Cloneable接口,重新定义clone方法,并指定public访问修饰符(因为所有类的父类Object中的clone方法虽然为protected的,但是子类只能调用受保护的clone方法克隆其自己的对象,如果想在类中随意的使用,必须将其声明为public)
对于一个类的实现者而言,需要有以下的思考:
浅拷贝的例子
class Employee implements Cloneable
{
public Employee clone() throws CloneNotSupportedException
{
return (Employee) super.clone();
}
....
}
深拷贝的例子
class Employee implements Cloneable
{
public Employee clone() throws CloneNotSupportedException
{
Employee cloned=(Employee) super.clone();
cloned.hireDay=(Date)hireDay.clone();
return cloned;
}
....
}
纯粹是为了编写代码时的简单高效。
lambda表达式允许你通过表达式来代替功能接口。lambda表达式就和方法一样,提供正常的参数列表和使用这些参数的主体(body,可以是一个表达式或代码块。
函数的方法的要素是:返回值、方法名、参数列表、方法体。
lambda表达式的要素:参数列表 方法体
定义:lambda表达式是一个可以传递的代码块,可以在以后执行一次或多次。
特点:将代码传递到某个对象。这个代码块会在将来的某个时间调用
lambda表达式的基本结构:(parameters) -> expression或者(parameters)->{statements;}
组成部分:
1.parameters:类似方法中的形参列表,这里的参数是函数式接口里的参数,这里的参数类型可以明确的声明 也可不声明而由JVM隐含的推断。另外当只有一个参数时可以省略掉圆括号
语法要点:
* 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
* 参数的小括号里面只有一个参数,那么小括号可以省略
* 如果方法体当中只有一句代码,那么大括号可以省略
* 如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字。
(String first,String second)
->first.length()-second.length()
x->2*x
Comparator<String> comp=(first,second) ->first.length()-second.length()
写成方法形式(代码块之外要加大括号)
(String first,String second)->
{
if(first.length()<second.length()) return -1;
else if(first.length()>second.length()) return 1;
else return 0;
}
无参数形式
() ->{for(int i=100;i>=0;i--) System.out.println(i);}
()->2
忽略参数类型形式
java中有很多封装代码块的接口,并不是所有的接口都是函数式接口,只有当一个接口有且只有一个抽象方法(什么叫抽象方法啊:只定义方法声明,而不定义方法体的方法)时才叫做函数式接口。
定义方式
@FunctionalInterface
public interface Comparator{
int compare(Object o1,Object o2)
}
@FunctionalInterface
public interface test{
void test();
default void test2(){
System.out.println("我是一个默认方法");
}
}
lambda表达式与这些接口是兼容的,对于只有一个抽象方法的接口,需要这种接口的对象(往往就是=的右边,需要我们new 一个对象的时候)时,就可以提供一个lambda表达式,这种接口称为函数式接口。
Arrays.sort方法在底层会接受实现了Comparator的某个类的对象。
Arrays.sort(words,(first,second)->first.length()-second.length());
//做了两个操作,算长度,作比较
//所以不能使用方法引用
s->s.length()==0
方法引用与普通的lambda表达式可以相互转换
//下面的s其实如果在不知道具体的参数类型的时候,我们也没必要知道,但是这个情况可以从前面的推断得出
Consumer<String> printer = s -> System.out.println(s);
Consumer<String> printer=System.out::println;
//下面这种情况,我们几乎不得而知s是什么类型的
walker.walk(x->
{
x.forEach(s->{System.out.println(s);});
return null;
}
);
walker.forEach(s->{System.out.println(s);});
walker.forEach(System.out::println);
有几种方式:
对象引用::实例方法名
类名::静态方法名
类名::实例方法名
类名::new
类型[]::new
构造器引用
在方法引用中还区分绑定的方法引用与未绑定的方法引用。
变量作用域
text
就是自由变量,这个变量并不在lambda表达式中,而是在外围的方法中。public static void repeatMessage(String text,int delay)
{
ActionListener listener=event->
{
System.out.println(text);
Toolkit.getDefaultToolKit().beep();
};
new Timer(delay,listener).start();
}
int e1=0;
Arrays.sort(envelopes,(e1,e2)->
{
if(e1[0]!=e2[0])
{
return e1[0]-e2[0];
}
else{
return e2[1]-e1[1];
}
});
this
关键词指的是外围class的this对象。Comparator与lambda表达式
为什么内部类能直接使用外部类的实例字段呢?因为内部类的对象总有一个指向外部类对象的隐式引用。
外围类引用:OuterClass.this
表示外围类引用。
内部类对象的构造函数outerObject.new InnerClass(construction parameters)
在外部类的作用域之外,可以这样引用内部类:
OuterClass.InnerClass
编译器会将内部类转换为常规的类文件,用$区分外部类名与内部类名。
new SuperType(construction parameters)
{
inner class methods and data
}
匿名内部类不允许有构造器,匿名内部类常常用于实现继承与接口实现过程中方法的重写。
看一段Comparator接口重写的代码
Arrays.sort(envelopes,new Comparator<int[]>()
{
public int compare(int []e1,int []e2){
if(e1[0]!=e2[0])
{
return e1[0]-e2[0];
}
else{
return e2[1]-e1[1];
}
}
});
继承Thread类并实现其中的run方法
new Thread(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}.start();