Java(4-1)

(一)本章概述:
1. 接口的概念:接口不是类,而是对类的一组需求描述,描述类具体有什么功能,而并不给出每个功能的具体实现。 一个类可以实现一个或多个接口,并在需要接口的地方,随时使用实现了相应接口的对象。
2. lambda表达式的概念:这是一种表示可以在将来某个时间点执行的代码块的简介方法。 使用lambda表达式可以用一种精巧而简洁的方式表示毁掉或变量行为的代码 。
3. 内部类机制:内部类主要定义在另外一个类的内部,其中的方法可以访问包含他们(内部类)的外部类的域。 这种技术主要用于设计具有相互协作关系的类集合 。

(二) 谈谈接口:
接口不是类,而是对类的一组需求描述
举个例子: 我们经常听见服务提供商这样说:“如果类遵从某个特定接口,那么就履行这项服务”,比如,Arrays类中的sort方法承诺可以对对象数组进行排序,但这是有一个前提的:对象所属的类必须实现了Comparable接口。
下面是Comparable接口的代码:

public interface Comparable{
    int compareTo(Object other);
}

这就是说,任何实现了Comparable接口的类,都需要包含compareTo方法,并且这个方法的参数是一个Object对象,返回一个整型数值。

Tip:从java5开始,Comparable接口已经改进为了泛型类型:

public interface Comparable<T>{
    int compareTo(T other);  //泛型,ok了吧
}

接口中的所有方法都自动的属于public。所以,在接口中声明方法时,不必提供关键字public。 在这个例子中还有一个问题没说到,就是上面的接口有一个没有说明的附加要求,即,在调用x.compareTo(y)的时候,这个compareTo方法必须确实比较两个对象的内容,并返回比较的结果,当x小于y的时候返回个负数;等于,返回0;大于,返回正数。

在接口中可以定义常量(final那种!!!注意是常量啊!!!),接口绝对不能含有实例域,在java8之前,也不能在接口中实现方法。提供实例域和具体的实现方法应该有实现接口的那个类完成。

现在,假设希望Arrays类的sort方法对Employee对象数组进行排序,Employee类就必须实现Comparable接口。

要将类声明为实现了一个接口,需要使用关键字impements:
class Employee imlements Comparable<Employee>
当然,这里的Employee类需要提供compareTo方法。假设希望根据雇员的薪水进行比较,以下是compareTo方法的实现:

public int compareTo<Employee other>{
    return Double.compare(salary,other.salary);
}

现在我们已经看到,让一个类实现排序服务时,必须让他实现compareTo方法。这是由于要想sort方法提供比较的方式。

(三)接口的特性:
接口不是类,尤其不能使用new运算符实例化一个接口:

x = new Comparable(...)//错误

有意思的是,接口不能实例化,但是可以声明接口变量:Comparable x; x = new Employee();
这是可以的。这样需要提及的内容是,我们可以使用instanceof,检查一个对象是否属于某个特定类一样,也可以使用intanceof方法来检查一个对象是否实现了某个特定接口
例如:if(anObject instanceof Comparable){...}

还需要说的就是,接口也可以被扩展 嗯…这里再举个例子:

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

//可以吧 Moveable 作为基础 扩展一个叫做powered的接口:
public interface Powered extends Moveable{
    double milesPerGallon();//新加的
}

接口中可以包含常量 , 例如 double SPEED = 95; 这些常量将会自动设置为public static final。

尽管每个类只能有一个超类,但是可以有多个接口,像这样:

class Employee implements Cloneable,Comparable

(四)静态方法与默认方法:
Fist,在java SE 8中,java才允许向接口中增加静态方法。理论上讲,没有理由认为这不合法,只是有违于将接口作为抽象规范的初衷。

现在假设,你的jdk不是java8,那该如何实现上面的功能呢?目前为止,通常的做法都是将静态方法放在伴随类中。比如在java库中,你会看见成对出现的接口和实用工具类,例如:Collection/Collections 或者 Path/Paths 。

我们还可以为接口方法提供一个默认实现。必须用default修饰标记这样的一个方法

public interface CompareTo<T>
{
    default int compareTo(T other){return 0;}
}

当然,在上面的那个例子中,默认方法并没有太大用处,因为Comparable的每一个实际实现都要覆盖这个方法。不过有些情况下默认方法可能很有用!(回忆下以前学过设计模式的选择模式) 。 在选择模式中,我们可以只为我们真正要用的东西覆盖相应的方法。默认方法可以调用任何方法。

对于默认方法,用户有三种选择:
①完全无视默认方法
直接继承了上级接口的默认方法
②重新声明默认方法
重新把默认方法声明为抽象方法(无实现,具体子类必需再次实现该方法)
③重新实现默认方法
重写了默认方法的实现。

现在假设一种冲突:即如果先在一个接口中将一个方法定义为默认方法,然后又在超类中或另外一个接口中定义了同样的方法,会发生什么?

Java对这种情况的规则如下:
1)超类优先。如果一个超类提供了一个具体方法,同名而且有相同参数类型的默认方法会被忽略。

一个类扩展了一个超类,同时实现了一个接口,并从超类和接口中继承了相同的方法。即,class Student extends Person implements Named{...};。这种情况下,只会考虑超类方法,接口的所有默认方法都被忽略。

2)接口冲突。如果一个超接口提供了一饿默认方法,另一个接口提供了一个同名且参数类型相同的方法,必须覆盖这个方法解决冲突。(编译器会报错,程序员来解决。)

(五)接口示例:
①定时器接口示例:
介绍一种设计模式:回调。 在这种设计模式中,可以指出某个特定时间发生时,应该采取的动作。

在java.swing包中有一个Timer类,可以使用它在到达给定的时间间隔发出通告。例如,假如程序有一个时钟,就可以请求每秒钟获得一个通告,以便更新适中的表盘。 构造定时器时候,需要设置一个时间间隔,并告诉定时器,到达时间间隔需要做什么操作。

在Java中如何向定时器传递这个操作呢?Java是通过把某个类的对象传递给定时器,然后定时器调用这个对象的方法。当然,定时器需要知道调用哪一个方法,并要求传递的对象所属的类实现了java.awt.event包中的ActionLister接口。下面就是这个接口:

public interface ActionListener{
    void actionPerformed(ActionEvent event);
}

加入希望每间隔十秒就打印一条消息“At the tone,the time is … ”,然后响一声,就应该定义一个ActionListener接口的类,然后将需要执行的语句放在actionPerformed方法中。如下:

class TimePrinter implements ActionListener{
    public void actionPerformed(ActionEvent event){
        System.out.println("At the tone,the time is " + new Date());
        Toolkit.getDefaultToolkit().beep();
    }
}

接下来,构造这个类的一个对象,然后吧它传递给Timer构造器:

ActionListener listener = new TimePrinter();
Timer t = new Timer(10000,listener);
//最后,启动定时器
t.start();

②Comparator接口示例:
在之前,我们了解了如何对一个对象数组排序,前提是这些对象是实现了Comparable接口的类的实例。

现在我们举一个例子:例如现在有两个字符串,字符串本身是有compareTo方法地,这个方法是安扎字典顺序比较字符串地,但是!假设我们现在希望按长度递增的顺序对字符串进行排序,而不是按照字典顺序对字符串进行排序。肯定不能让String类用两种不同的方式实现compareTo方法—–更何况String类也不应由我们来修改。

对付这种情况,Arrays.sort方法还有第二个版本,有一个数组和一个比较器(comparator)u哦为参数,比较器是实现了Comparator接口的类的实例。

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

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

class LengthComparator implements Compartor<String>{
    pulic int compare(String first,String second){
        return first.length() - second.length();
    }
}

具体完成比较时,需要建立一个实例:

Comparator<String>comp = new LengthComparator();
if(comp.compare(wrods[i],words[j])>0). . . 

把这个调用和之前写的 words[i].compareTo(words[j])的调用作比较,这个还覆盖了interface,而且这个实在比较器对象上调用,而不是在字符串本身上调用,而且这个调用的是compare,那个是compareTo!

③对象克隆:
介绍下,Cloneable接口,这个接口只是了一个类体哦概念股了一个安全的clone方法。

为了解释克隆,下面我们举一个例子:
先来回忆一下,一个包含对象引用的变量建立副本时会发生什么。原变量和副本都是同一个对象的引用。这说明任何一个变量改变,都会影响另一个变量。

如果i希望copy的是一个新的对象,它的初始状态和 原本的对象相同,但是之后他们会有各自不同的状态,这种情况下就可以使用clone方法。

但是!clone方法是Object的一个protected方法,这说明你的代码不能直接调用这个方法。只有Employee类可以克隆Employee对象。这个限制是有原因地,想一下,Object类如何实现克隆,它对这个对象一无所知,所以智能逐个域的进行拷贝。如果对象中的所有数据域都是数值或其他基本类型,拷贝这些域没有任何问题。但是如果对象中包含对子对象的引用呢???这样拷贝域就会得到相同子对象的一个引用,这样一来,原对象和子对象仍然会共享一些信息!

默认的克隆操作是“浅拷贝”,并没有克隆对象中引用的其他对象。
完全克隆就是深拷贝。

对于每一个类,需要确定:
1)默认的clone方法是否满足需求;
2)是否可以在可变的子对象上调用clone来修补默认的clone方法;

如果选择1)或者2 ),那么那个类必须:
1. 实现了Cloneable接口;
2. 重新定义了clone方法,并指定Public修饰符。

对象对于克隆很“偏执”,如果一个对象请求克隆,但没有实现这个接口,就生成一个受查异常。

所以,即使clone的默认(浅拷贝)实现能够满足需求,我们还是要实现Cloneable接口,将clone重新定义为Public,再调用super.clone()。下面给出一个例子:

class Employee implements Cloneable{
    public Employee clone() throws CloneNotSupportExcption
    {
        return (Employee)super.clone // clone是Object下的方法
    }
}

和Object.clone提供的浅拷贝相比,深拷贝还需要做更多的动作:克隆对象中可变的实例域。

class Employee implements Clonable{
...
    public Employee clone() throws CloneNotSupportException
    {
        Employee cloned = (Employee)super.clone();
        cloned.可变实例域(可能是对另一个对象的引用) = 可变实例域.clone();

        return cloned;
    }
}

如果在一个对象上调用clone()方法,但是没有实现Cloneable接口,Object类的clone方法就会抛出一个CloneNotSupportedException的异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值