欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  Java

Java 抽象类与接口

程序员文章站 2022-05-03 17:02:53
...
Java 抽象类

我们父类设定了一些方法,设定的主要目的是让子类继承父类去覆写那些方法,来展示不同的结果。换句话说,我们不关心父类方法里的具体实现,反正会被子类的方法覆写,那么我们就可以让父类更抽象一下,抽象到只有方法的声明,而没有方法体。我们管这种方法叫做抽象方法,管包含抽象方法的类叫做抽象类。

抽象类的特点

抽象类里只要含有一个或者一个以上的抽象方法就是抽象类,但如果一个抽象方法都没有,那这个抽象类也没有建立的意义。抽象方法是具有如下格式的方法

[public] abstract 返回类型 方法名(参数列表);
抽象方法是没有方法体的 ,所以方法名写完就会加;表示方法声明结束,抽象法方法体用abstract修饰,表示这是一个抽象的方法,访问修饰符只能用public或者是protected,或者是默认访问权限,就是不能用private,因为根本无法去继承
同样,抽象类的格式如下:

[public] abstract class 类名{
    [public] abstract 返回类型 方法名(参数列表);
}

抽象类的是前面加abstract修饰,表示这个是一个抽象类,访问修饰符只能用public或者是protected,或者是默认访问权限,不能用private的原因和上面一样。
抽象类创造的意义是将方法的声明与方法的实现分隔开,从而实现多态。那么他就具有如下的特点:

抽象类不能被实例化,也就是说不能直接创建一个抽象类的对象,但抽象类是可以有构造函数的,如果是有参构造函数,则子类要去显示调用它。
抽象类是用来被继承的,方法是要被覆写的,如果子类继承了抽象的父类,则需要覆写父类的抽象方法,如果没有覆写,则子类必须也要定义为抽象类。
abstract是不能与private static,final 修饰符一起使用来修饰方法。

这里我们解释一下第三点,abstract不能与private一起用,因为private修饰的方法和类,都是无法再类之外被访问到。也就没有继承的可能性。abstract不能和static一起用,是因为abstract的作用是实现多态,而实现多态则依赖于继承和覆写。static修饰的方法虽能被子类所继承,但是我们修改了继承后的方法时,这个就不能算作是覆写,而是父类的方法被隐藏掉了,只有通过父类名.方法名的形式显示调用它,这个实现不了多态。从另一个角度来看,静态的方法是编译的时候就确定了,无法实现后期绑定,也就不存在运行时在决定方法调用的可能。所以static修饰的方法是可以被继承,但无法实现多态,自然也就不能和abstract一起使用。
abstract不能和final一起使用的原因和上面一样,final修饰的方法无法被继承,自然也谈不上多态,所以abstract无法和final一起用。

抽象类举例

我们把上文多态的例子,继续修改,抽象化。我们把Animal的代码改成如下样子。

public abstract class Animal {    
  abstract void run();
}

我们Dog,Cat类的代码不需要改变。

public class Dog extends Animal{
    @Override
    public void run() {
        System.out.println("狗在奔跑");
    }
}public class Cat extends Animal{
    @Override
    public void run() {
        System.out.println("猫在奔跑");
    }
}

其他Print类,和Test类的代码也保持不变,代码如下:

public class Print {    
    public void print(Animal animal) {
        animal.run();
    }
}
public class Test {    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();        
        new Print().print(dog);        
        new Print().print(cat);
    }
}

我们可以看出抽象类和之前普通类,在其他地方的改动基本是没有的,只是将方法变得抽象,意义上更加明确。

抽象总结

创建抽象类和抽象方法是很有意义的,他能让我们设计类的时候使类的含义更加明确,通过抽象的思想,让类变得更加通用,声明一系列的方法来告诉用户打算怎么去使用它。

Java 接口

接口可以看出一种协议,一种要求,我们继承他来知道我们可以做什么,具体怎么做则取决与我们。比如KFC就是一个接口,我们一看他就知道里面有买汉堡,炸鸡,可乐,但是具体的味道和服务又会根据不同的店,有不同的样子,这就是接口的意义。所以我们这里也可以看出接口里的方法也应该是抽象的。

接口的特点

Java中接口的写法如下:

[public] interface InterfaceName {
         成员变量
         方法声明
}

接口区别于类,他不是用class来修饰,他用特定的interface关键字,访问修饰符与class一致,可以用public或者缺省。里面只有抽象方法和成员变量两种内容。成员变量会默认添加public static final意为成员变量归类所有,访问权限是最大的,但是不能别继承和修改。这也看出接口是因为不能被实例化,才会这样约定的,接口的成员变量不允许为空,在定义的时候就要赋值给他。 而接口的方法则默认为抽象方法,默认添加public abstract和抽象方法一样,只能写方法声明。
不同于我们用extends去继承一个类,我们用implements 来表示实现这个接口。

class ClassName implements Interface1{
}

接口作为一个特殊的存在,是有一些他的独特的地方的。

一个类是可以实现多个接口的,这在一定程度上实现了Java的多继承。
接口是不能被实例化,不同于抽象类,接口的内部只能由成员变量和抽象方法,是不可以存在静态变量块,以及构造器的。
我们是可以声明一个接口类型的变量,但是只能引用一个实现了这个接口的类。
同抽象方法,实现了接口的类必须实现接口的所有方法,否则就会变成抽象类。
接口举例

看过抽象的例子,我们可能想,我们把Animal从抽象换成接口,不就实现了一个接口的例子嘛,其他地方基本也不用去改动。但这显然是错的。我们并不能去说Animal是一个接口,我们上面说了,接口是一种协议,规定我们能做什么,而不是一个事物的抽象。从这里我们也能看出接口和抽象的不同,抽象更多的是一种重构而产生的东西,我们先有dog,cat类,然后才会把他们共性的东西提取出来,放到一个更通用,更抽象的父类Animal中,而我们发现Animal不需要管run方法是怎么实现的,所以我们将run方法设定为抽象的方法,从而将Animal类设为抽象类,等待去=继承者来实现run。这是一种从下而上的设计思想。但是接口不是,接口一开始就是设定好的,我们根据设定好的接口从上往下去写。接口先规定好了有什么方法,然后我们再去具体实现他。这是一种从上而下的设计思想。
所以,我们不能将Animal设为接口,但是我们可以将Print设为接口,他规定我们有一个print()方法,代码如下:

public interface Print {    
  void print(Object obj);
}

那我们就可以写一个新的类去实现这个Print接口。代码如下:

public class SimplePrint implements Print{
    @Override
    public void print(Object obj) {
        ((Animal)obj).run();
    }
}

除了Test类以外,其他地方都不需要改变。我们Test类代码如下:

public class Test {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();        
        Print print = new SimplePrint();        
        print.print(dog);        
        print.print(cat);
    }
}

接口总结

接口和抽象虽然都是通过抽象的方法来提供我们实现多态的方式,但是他们却是两个不同的设计思想。这里关于接口的讲解比较简单,关于接口自身的继承,接口内部包含其他接口,以及利用接口来实现回调等等留在以后的文章专门来说。这里主要是通过对比来了解抽象和接口。