6.1 接口(interface)
接口:用来描述类具有某种(些)功能,实现(implement)了接口的类必须要给出接口方法的实现。
接口中所有的方法都属于public,故在接口的定义中忽略不写,但在类中实现时需要写出。
接口中包含三种方法:1.抽象方法,必须要实现了该接口的类中给出实现;2.静态方法(static),在接口内实现方法;3.默认(default)方法,在接口内为此方法给出一个默认实现,必须用default修饰符来标记。这种方法可以让开发者只在类中实现(覆盖)自己感兴趣的方法即可,不必要将接口所有的方法都实现。而且在接口中添加新方法时,若将新方法设置为default,则可以实现“源代码兼用”。
接口与抽象类的区别
1.接口的实现使用implements+接口名,抽象类的继承使用extends+抽象类名
2.接口中不能包括实例域,只能包括上述的三种方法和常量。抽象类中可以包括抽象方法、具体方法实现、实例域、常量。
3.每个类只能由一个超类扩展而来,而能实现一个或多个接口(这就是为什么出现抽象类不能替代接口)
课外知识:强类型、弱类型、静态类型、动态类型语言
1.强/弱类型:弱/强类型指的是语言类型系统的类型检查的严格程度。比如弱类型偏向允许隐式类型转换。
2.静态/动态类型:静态类型指的是编译器在compile time执行类型检查,动态类型指的是编译器(虚拟机)在runtime执行类型检查。简单地说,在声明了一个变量之后,不能改变它的类型的语言,是静态语言;能够随时改变它的类型的语言,是动态语言。因为动态语言的特性,一般需要运行时虚拟机支持
Java是强类型静态语言。
让类实现一个接口:1.让类声明为实现给定的接口(Implement);2.对接口中的所有方法进行定义
接口不能实例化,但是能够创建接口变量,该变量必须引用实现了接口的类对象,可用instanceof来检测某对象是否实现了某接口(anObject instanceof 接口名),返回boolean值。接口也可以被扩展(继承),同抽象类(extends)
解决默认方法冲突:1.类优先;2.若是两个接口的方法产生了冲突:a)如果至少有一个接口提供了实现,则编译器则报告错误,让程序员解决这个二义性(在类中使用接口方法时在其之前加上“接口名.”);b)如果两个都没提供实现,则不存在冲突,可选择实现或不实现。
6.2接口示例
1.Comparator接口和Comparable接口
public int Comparable
{
int compareTo(Type b);
}
public int Comparator
{
int compare(Type a,Type b)
}
Comparable和Comparator都是用来实现集合中的排序的,Comparator位于包java.util下,而Comparable位于包java.lang下。Comparable 是一个对象本身就已经支持自比较所需要实现的接口(如 String、Integer 自己就可以完成比较大小操作,已经实现了Comparable接口), Comparator 是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。如String类默认是字典排序,而要想根据长度排序则要使用比较器。而用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。
import java.util.*;
import java.util.Comparator;
public class TimerTest
{
public static void main(String[] args)
{
Person[] p = new Person[4];
p[0] = new Person("ZXC",19);
p[1] = new Person("AFD", 109);
p[2] = new Person("AFD", 16);
p[3] = new Person("ERT",100);
Arrays.sort(p);//升序排列
//Comparator<Person> cmp = new Cmp(); 建立一个接口对象,引用实现了接口的类的对象,用此对象传递接口的方法(因为Java是面向对象的语言)
//Arrays.sort(p,new Cmp()); 降序排列
System.out.println(Arrays.toString(p));
}
}
class Person implements Comparable<Person>
{
private String name;
private int age;
public Person(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{return name;}
public int getAge()
{return age;}
public String toString()
{return "Name:"+this.name+" Age:"+this.age;}
public int compareTo(Person other)
{
if(this.getName().compareTo(other.getName()) != 0)
return this.getName().compareTo(other.getName());
else{
if(this.getAge() < other.getAge())
return -1;
else if(this.getAge() > other.getAge())
return 1;
else return 0;
}
}
}
class Cmp implements Comparator<Person>//Arrays.sort()会采用compare()返回的整数来排序,默认是升序排列,如果想要换成倒序则将大于返回-1
{
public int compare(Person a,Person b)
{
if(a.getName().compareTo(b.getName()) != 0)
return a.getName().compareTo(b.getName());
else{
if(a.getAge() > b.getAge())
return -1;
else if(a.getAge() < b.getAge())
return 1;
else
return 0;
}
}
}
Comparator:负数排前,正数在后。
2.Cloneable接口
用于复制一个对象的状态,且复制后两者的状态不会相互影响。
所有的数组类型包含一个public的clone()方法,可用这个方法建立一个包含有所有元素的新副本
import java.util.Arrays;
public class CloneTest {
public static void main(String[] args)
{
int[] nums = {1,2,3,4,5};
int[] clones = nums.clone();
nums[1] = 3;
System.out.println(Arrays.toString(nums));
System.out.println();
System.out.println(Arrays.toString(clones));
}
}
输出为:
[1, 3, 3, 4, 5]
[1, 2, 3, 4, 5]
Cloneable接口实际上是标记接口(tagging interface),它没有抽象方法,唯一作用是使用instanceof来表示一个类是否实现了该接口。clone()是从Object类继承而来,但是使用clone()的类必须要表明实现Cloneable接口(implements Cloneable)。
Cloneable是一个protected的方法,如果要让某类实现该接口,就需要在类中的clone()实现注明为public。
clone()可以分为浅复制和深复制:浅复制是指复制了对象中的原始数据类型,对于复制的对象类型,依旧是共享的,如果对象类型是不可变的(如String),则没有影响,如果是可变的,则需要进行深复制。
class Employee implements Cloneable
{
public Employee clone() throw CloneNotSupportedException
{
//call object.clone()
Employee clones = (Employee) super.clone();
//clone mutable fields
clones.hireDay = (Date) hireDay.clone()
return clones;
}
}
6.3 lambda表达式
函数式接口(functional interface):只有一个抽象方法的接口。需要这种接口的对象时,可以提供Lambda表达式。
Java是一种面向对象的语言,如果要像方法传递代码块,则需要构造一个对象,而对象的类中需要有一个方法来包含需要传递的所有代码。
Lambda表达式是一个可传递的代码块,以及必须传入代码的变量规范(参数类型)。在以后可执行一次或者多次,用来取代匿名类(见后)。其形式为:参数,箭头(->),表达式/代码块,自由变量的值(非参数而且不在代码中定义的变量)。
不使用lambda表达式:
class lengthCompare implements Comparator<String>
{
public int compare(String a,String b)
{
return a.length()-b/length();
}
}
Comparator<String> cmp = new lengthCompare();
//Arrays.sort(T[] a,Comparator<T> c)
Arrays.sort(arr,cmp);
使用lambda表达式:(表达式无需制定返回类型)
Arrays.sort(arr,(String a,String b) -> a.length()-b.length());
//Arrays.sort方法会接受实现了Comparator<String>的某个类的对象,而在此对象调用compare方法会执行这个lambda表达式的体
当可以推导出lambda表达式的类型参数时,可省略参数的参数类型:
// Comparator<String> cmp = (a,b)->a.length()-b.length();
Arrays.sort(arr,(a,b)->a.length()-b.length());
当lambda表达式没有参数类型时,括号依然要保留:
()->{for(int i = 0;i < 10;i++) System.out.println(i);}
当lambda表达式只有一个参数且类型可以推导出来时,可省略括号和类型:
event -> System.out.println("The time is "+new Date());
当代码无法用表达式表述,则可以使用代码块,并包含显示的return语句:
注:每个分支都要有return
(String a,String b)->
{
if(a.length() < b.length()) return -1;
else if(a.length() > b.length()) return 1;
else return 0;
}
方法引用:将现有的方法传递给某方法,用::分隔方法名和对象或类
1.object::instanceMethod:
Timer t = new Timer(1000,System.out::println);
//等同于Timer t = new Timer(1000,event->System.out.println(event));
2.Class::StaticMethod
Math::pow;
等同于(x,y)->Math.pow(x,y);
3.Class::instanceMethod
Arrays.sort(Strings,String::compareToIgnoreCase);
等同于Arrays.sort(Strings,(x,y)->x.compareTo(y));
也可以在方法引用中使用this/super
4.构造器引用
类的构造器的引用
ArrayList<String> names = ...;
Stream<Person> stream = names.stream().map(Person::new);
// Person::new是Person构造器的一个引用
数组的构造器引用
int[]::new
等同于x->new int[x]
流库利用构造器引用构造泛型数组
Person[] p = stream.toArray(Person::new);
lambda表达式可以捕获外围方法或类中的变量,但是只能引用值不会改变的变量,而且此变量在外部也不能改变,即初始化后不会再赋予新值。
lambda表达式中的this参数是指创建这个表达式的方法的this参数
public class Application
{
public void init()
{
ActionListener listener = event ->
{
System.out.println(this.toString());//会调用Appliction对象的toString()
}
}
...
}
6.4 内部类
1.即定义在一个类中的另一类,内部类不仅可以访问自身数据域,也可以访问创建它的外围类的数据域
内部类对象有一个隐式引用,指向创建它的外部类
public class TalkingClock//outer class
{
private boolean beep;
....
...
public class TimePrinter implements ActionListener
// an inner class
{
public void actionPerformed(ActionEvent event)
{
System.out.println("Hello");
if(beep) Toolkit.getDefaultToolKit().beep();//outer.beep
}
}
}
构造外部类时编译器给内部类构造了一个默认构造函数,传递参数为外部类,传递给对象变量。
2.内部类的语法规则
a.内部类引用外部类变量:OuterClass.this
public void actionPerformed(ActionEvent event)
{
System.out.println("Hello");
if(TalkingClock.this.deep) Toolkit.getDefaultToolKit().beep();
}
b.外部类中创建内部类:outerObject.new InnerClass(construction parameters)
ActionLister listener = this.new TimePrinter();
c.外部类作用域外引用内部类:OuterClass.InnerClass
TalkingClock j = new TalkingClock(1000,true);
TalkingClock.TimePrinter listener = j.new TimePrinter();
3.局部内部类:当在某方法中只是用某类一次,不能用public/private声明
public void start(int interval,boolean beep)
{
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
System.out.println("Hello");
if(beep) Toolkit.getDefaultToolKit().beep();
}
}
ActionListener listener = new TimePrinter();
...
}
可以访问外部类和局部变量(即包含它的方法的实参),但是参数必须是final型的,不能改变
4.匿名内部类,即没有类名,只需要构建某类的对象一次
public void start(int interval,boolean beep)
{
ActionListener listener = new ActionListener()//创建一个实现AL接口的类的对象
{
public void actionPerformed(ActionEvent event)
{
System.out.println("Hello");
if(beep) Toolkit.getDefaultToolKit().beep();
}
};
...
}
“双括号语法”
invite(new ArrayList<String>{{add("Mary");add("Bill");}};
外括号创建ArrayList的匿名子类,内括号为对象构造块
5.静态内部类:把类隐藏在另一类的内部,内部类不需要访问外围类对象,则将内部类声明为static,可有静态域和方法。
//同时返回数组中的最大值和最小值
public class test {
public static void main(String[] args)
{
double[] a = new double[20];
for(int i = 0;i < 20;i ++)
a[i] = Math.random() * 100;
ArrayAlg.Pair p = ArrayAlg.minmax(a);
System.out.println("Min = "+p.getfirst());
System.out.println("Max = "+p.getsecond());
}
}
class ArrayAlg//outer class
{
public static class Pair//static inner class
{
private double first;
private double second;
public Pair(double first,double second)
{
this.first = first;
this.second = second;
}
public double getfirst()
{return first;}
public double getsecond()
{return second;}
}
public static Pair minmax(double[] values)
{
double max = Double.NEGATIVE_INFINITY;
double min = Double.POSITIVE_INFINITY;
for(double w : values)
{
if(w < min) min = w;
if(w > max) max = w;
}
return new Pair(min,max);
}
}