Java--接口与多态

其实,我们在Java中,提倡用的是接口而不是继承,很多书上都说“别滥用继承”,那么什么是接口呢,他和继承,多态有什么关系?


何谓接口

Java是一门面向对象的语言,它尝试着把所有问题都抽象为一个实例,也就是一个对象。Java中的类就是一种”类”,可以看做是一个种类。既然是对象,我们可以把它抽象为一个人,而我们在前面说的继承就是子类继承父类,可以看做我们继承了父母的基因,而它完全继承了父类的方法。而接口相当于“行为”,我们可以设计一个类名为people的类,然后给它定义一个接口(行为),我们称这个类拥有这个行为,或这个类操作这个接口,这样的话,在逻辑上面会很好理解,当我们写一些程序时,是非常方便我们理解的。


接口定义行为

我们在Java中使用interface关键字来进行定义接口,假设我们现在有一个类people,现在我们定义一个接口,表示人拥有游泳这个行为:

public interface Swimmer{
    public abstract void swim();
}
  • 接口可以定义行为但不定义操作,我们用abstract来标示,而且一定是public。对象如果想拥有Swimmer定义的行为,就必须操作Swimmer接口。
  • 在接口中,所有的方法都必须是public abstract的,这个一般是不用写的,编译程序的时候会自动加上。

现在我们让人拥有Swimmer行为:

public abstract class People implements Swimmer{
    protected String name;

    public People(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }

    @Override
    public abstract void swim();
}

我们可以看到,类要操作接口时,需要implements关键字。操作某接口时,有两种处理方式,一种是操作接口中定义的方法,二是再度将该方法标示为abstract。


行为的多态

我们要对行为使用多态语法,和继承时使用多态语法是一样的,都是通过“扮演”来判断这个行为是否是合法的多态语法。例如:

Swimmer swimmer = new Shark();

当我们判断的时候,是从右边向左边读的,右边是不是有左边的行为,或者是右边对象是不是操作了左边的接口。
那好,我们再来看一个问题:

Swimmer swimmer = new Shark();
Shark shark = swimmer;

这个会不会编译正确呢,答案是不会的,第一句话表示鲨鱼拥有Swimmer这个行为是正确的,在第二行由于swimmer是Swimmer类型,编译程序就会想有Swimmer行为的对象是不是Shark呢,答案当然是不一定了,还有可能是People实例。我们可以做如下的修改:

Swimmer swimmer = new Shark();
Shark shark = (Shark) swimmer;

对于第二行的语义就是我现在告诉编译程序,swimmer这个行为就是Shark拥有的,你不要再报错了,所以它就会让你编译通过,但是由这个可能引发的后果就要自己承担了。
我们再来看一个:

Swimmer swimmer = new Shark();
Fish fish = (Fish) swimmer;

如果在第二行没有让swimmer扮演Fish的话,这个写法就是错误的,它参考的是鲨鱼,鲨鱼继承自鱼,你现在让swimmer强行扮演Fish也是可以通过的,不会报错,但是如果你这样做的话那么就GG了:

Swimmer swimmer = new Submarine();
Fish fish = (Fish) swimmer;

这个程序在执行时期是会出错的,因为我们让swimmer参考的是Submarine,现在在第二行又让它扮演鱼类,这肯定是不正确。


解决需求变化
  • 在Java中,类可以操作两个以上的类,也就是拥有两种以上的行为,类也可以同时继承某个类,并操作某些接口。
  • 在Java中,接口可以继承自另一个接口,也就是继承父接口的行为,再在子接口中额外设计行为,我们可以用这个方法来扩展我们的程序功能。

接口语法细节

接口的默认
  • 在刚才已经说过了,我们在接口中定义的没有操作的行为都是public abstract,这是系统默认的。
  • 在接口中我们还可以定义枚举常数,对于枚举常数我会在以后说明,现在就了解一下这个概念就可以了。
  • 在接口中我们也只能定义public static final 的枚举常数,如果我们在接口中只写了int STOP = 0;这样的语句,编译程序会自动展开为public static final。

匿名内部类

在Java中,我们有时会有临时继承某个类或操作某个接口并建立实例的需求,由于这些类或接口只使用一次,并不需要为这些类命名,所以我们可以使用匿名内部类来解决这个需求。它的基本语法:

new 父类() | 接口(){
    //类本体操作
};

比如:

Object o = new Object(){
    @Override
    public String toString(){
        return "语法示范";
    }
};

在JDK8出现之前,如果要在匿名内部类中存取局部变量,该局部变量必须是final,要了解为什么,就需要涉及一些底层知识,我们知道,在Java中局部变量的生命周期是比对象要短的,当一个方法调用完之后就会返回对象,局部变量的生命周期就结束了。要解决这个问题,在Java中使用传值,也就是在匿名内部类的实例中,建立新的变量参考原来的对象。例如:

int ai[] = {10, 20};

Object obj = new Object(ai){
    public String toString(){
        return ...;            //使用局部变量,方法之后生命周期结束
    }

    final int x[];
    {
        x = ai;                //传值
    }
}

所以我们也不能改变x的参考,为此程序才强制你要在局部变量加上final。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值