美文网首页
Java中的类,抽象类和接口

Java中的类,抽象类和接口

作者: lulu_6c14 | 来源:发表于2019-02-19 14:43 被阅读0次

    想必大家对Java中的实体类都比较熟悉。实体类通过属性和方法去描述一系列相似对象的特征。例如我们要写一个程序去计算图形的面积,需要定义一个长方形的类:

    public class Rect {
    
        private double width;
        private double height;
    
        public Rect(double width, double height, String color) {
            this.width = width;
            this.height = height;
            this.color = color;
        }
    
        public double area() {
            return width * height;
        }
    }
    

    Rect类中有着实例字段去描述长方形的宽高以及图形的颜色,area()方法用来计算长方形的面积。我们再定义一个圆形的类:

    public class Circle {
        
        private double radius;
        private String color;
    
        public Circle(double radius, String color) {
            this.radius = radius;
            this.color = color;
        }
        
        public void area() {
            return Math.PI * radius * radius
        }
    } 
    

    我们可以发现,Rect类和Circle类有一部分代码是相似的,例如color字段和area()方法。然而,由于计算面积的公式不同,area()的具体实现在这两个类中是不同的。这时,我们就可以创建一个新的类将相似的代码抽象出来。

    public abstract class Shape {
    
        private String color;
    
        public Shape(String color) {
            this.color = color;
        }
    
        public abstract double area();
    }
    

    可以看到Shape类和Rect类以及Circle类的一个明显不同是,class前面多了一个abstract修饰符,这代表着Shape是一个抽象类而非实体类。抽象类和实体类的一个明显的区别就是,抽象类中包含有抽象方法(没有具体实现的方法)。换句话说,包含有抽象方法的类被称为抽象类。在Shape类中,area()就是一个抽象方法。它比普通方法多了一个abstract修饰符,并且没有由花括号包裹起来的方法体。

    抽象类不能被实例化,但可以被继承。抽象类的子类必须实现父类的抽象方法(重写并使其有着具体的实现),否则子类也应该被定义为抽象类(继承了父类的抽象方法而未实现)。在这个例子中,Shape类的子类必须实现area()方法。

    可能有人会问,抽象方法的存在只是为了简化代码吗?其实并不是的。抽象方法实际上定义了子类必须实现的“规范”。上层代码只定义规范,具体的业务逻辑由不同的子类实现,调用者并不关心子类的具体实现。同时,由于抽象方法的存在,在编写代码的时候不需要子类就可以实现业务逻辑(正常编译不报错)。例如我们只有一个抽象类Shape,要写一个ShapeUtil类,里面有计算图形面积之和的方法。

    public class ShapeUtil {
    
        public double sum(Shape[] shapes) {
            double s = 0;
            for (Shape shape : shapes) {
                s += shape.area();
            }
            return s;
        }
    }
    

    可以看到,在计算面积和时,我们并不关心每一个图形的面积是如何计算的,我们只负责处理面积求和的业务逻辑,而把计算每个图形面积的具体实现交给了子类处理。也就是说,抽象方法定义了规范(area()方法需要返回图形的面积),而具体的实现交给了子类。

    接下来我们使用抽象类来实现Rect类和Circle类:

    public class Rect extends Shape {
    
        private double width;
        private double height;
    
        public Rect(double width, double height, String color) {
            super(color);
            this.width = width;
            this.height = height;
        }
    
        @Override
        public double area() {
            return width * height;
        }
    }
    public class Circle extends Shape{
    
        private double radius;
    
        public Circle(double radius, String color) {
            super(color);
            this.radius = radius;
        }
    
        @Override
        public double area() {
            return Math.PI * radius * radius;
        }
    }
    

    可以看出,除了拥有抽象方法外,抽象类和普通类一样,同样可以拥有成员变量和普通的成员方法。抽象类和普通类在用法上也是相似的,在继承抽象类时使用extends关键字,一个类只能继承一个抽象类

    如果一个抽象类没有字段,所有方法全部是抽象方法,就可以把该抽象类改写为接口。接口是一种声明的集合,是一种声明的规范,里面包含了很多抽象方法。需要明确的一点是,接口不是类,类描述对象的属性和方法,接口则包含类要实现的方法。

    接口用interface来声明,它无法实例化。它可以被类实现(implements),无法被继承。当接口被类实现时,除非该类为抽象类,否则该类需要实现接口中所规定的所有抽象方法。我们将以上代码改写,使用interface来实现:

    public interface Shape {
    
        double area();
        
    }
    
    public class Rect implements Shape {
    
        private double width;
        private double height;
    
        public Rect(double width, double height) {
            this.width = width;
            this.height = height;
        }
    
        @Override
        public double area() {
            return width * height;
        }
    }
    public class Circle implements Shape {
    
        private double radius;
    
        public Circle(double radius) {
            this.radius = radius;
        }
    
        @Override
        public double area() {
            return Math.PI * radius * radius;
        }
    }
    

    接口和抽象类是有相似之处的。接口可以继承自另一个接口(使用extends关键字),相当于扩展类接口的方法。接口定义的方法默认是public abstract(不需要写)。接口与抽象类的一些区别如下:

    抽象类 接口
    继承 一个子类只能继承一个抽象类 一个子类可以实现多个接口
    成员变量类型 各种类型均可 只能是 public static final
    抽象方法 可以定义抽象方法 可以定义抽象方法
    非抽象方法 可以定义非抽象方法 可以定义default方法*
    static方法 可以有static方法/代码块 不能含有static修饰的方法

    *:default方法是一个非抽象方法,所有实现接口的类都不需要实现这个方法,任何子类都可以重写这个方法来实现它自己的逻辑。

    接口的作用主要是作为一个上层的规范。比如我们要开发一个无人驾驶汽车的软件。开发过程中,我们需要GPS服务提供商提供GPS相关的服务(例如,定位、导航),我们还需要汽车制造厂商提供操控汽车的服务(例如,加速、刹车、转弯等)。这两种服务是不相关的,GPS公司和汽车厂商不需要了解对方公司是如何提供服务的(具体的代码实现)。在这种情况下,我们可以使用接口。我们写出GPS服务的接口和汽车操控的接口,根据接口来处理业务逻辑。与此同时,GPS提供商和汽车厂商可以根据我们提供的接口规范去实现具体功能。

    如何判断该使用抽象类还是接口呢?

    当以下情况适用于你时,可以考虑使用抽象类:

    • 你希望在一些紧密相关的类中共享代码
    • 你需要被继承的类拥有一些共同的方法、字段,或者需要除了public以外的访问修饰符(例如protected/private)
    • 你希望声明一些非静态/非常量的字段(你可以定义一些方法去获取/修改对象的属性)

    当以下情况适用于你时,可以考虑使用抽象类:

    • 你希望让许多不相关的类来实现你的接口。例如,Java中ComparableCloneable接口被许多不相关的类所实现。
    • 你希望具体指明某一特定数据类型的某个具体行为,但并不关心是谁实现了这个行为
    • 你想利用对于类型的多重继承

    相关文章

      网友评论

          本文标题:Java中的类,抽象类和接口

          本文链接:https://www.haomeiwen.com/subject/kryxyqtx.html