继承和多态

一、继承

  定义:继承(inheritance):从已有的类派生出新的类。

  继承可以让你定义一个通用的类,该通用类包含一些不同类的公共特征和动作;以该通用类扩展别的特定的类。

1.1 父类和子类

  如果一个类c1继承于类c2,那么c1称为次类(subclass)或者子类(child class)扩展类(extended class)派生类(derived class);C2称为超类(superclass)或者父类(parent class)基类(base class)

  子类可以从其父类中继承可以访问的数据域和方法,还可以添加新的数据域和新方法。

继承的实例:先创建一个通用的几何类,由几何类派生出圆、和矩形类:

几何类(基类):

package demo;

/**
 * GeometricObject
* Created by luts on 2015/11/28.
*/ public class GeometricObject { private String color = "white"; private boolean filled; private java.util.Date dateCreadted; public GeometricObject(){ dateCreadted = new java.util.Date(); } public GeometricObject(String newColor, boolean newFilled){ dateCreadted = new java.util.Date(); this.color = newColor; this.filled = newFilled; } public String getColor(){ return color; } public void setColor(String newColor){ this.color = newColor; } public boolean isFilled(){ return filled; } public void setFilled(boolean newFilled){ this.filled = newFilled; } public java.util.Date getDateCreadted(){ return dateCreadted; } public String toString(){ return "creadted on " + dateCreadted + "\ncolor: " + color + " and filled: " + filled; } }

圆类(子类):

package demo;

/**
 * cricle 
 * Created by luts on 2015/11/28.
 */
public class Circle extends GeometricObject {
    private double radius;

    public Circle(){

    }

    public Circle(double newRadius){
        this.radius = newRadius;
    }

    public Circle(double newRadius, String newColor, boolean newFilled){
        this.radius = newRadius;
        setColor(newColor);
        setFilled(newFilled);
    }

    public double getRadius(){
        return radius;
    }

    public double getArea(){
        return radius * radius * Math.PI;
    }

    public double getDiameter(){
        return 2 * radius;
    }

    public double getPerimeter(){
        return 2* radius * Math.PI;
    }

    public void printCircle(){
        System.out.println("The circle is creadted " + getDateCreadted() + " and the raius is " + radius);
    }
}

矩形类(子类):

package demo;

/**
 * Rectangle  extends from GeometricObject
 * Created by luts on 2015/11/28.
 */
public class Rectangle extends GeometricObject{
    private double width;
    private double height;

    public Rectangle(){

    }

    public Rectangle(double newWidth, double newHeight){
        this.width = newWidth;
        this.height = newHeight;
    }

    public  Rectangle(double newWidth, double newHeight, String newColor, boolean newFilled){
        this.width = newWidth;
        this.height = newHeight;
        setColor(newColor);
        setFilled(newFilled);
    }

    public double getWidth(){
        return width;
    }

    public void setWidth(double newWidth){
        this.width = newWidth;
    }

    public double getHeight(){
        return height;
    }

    public void setHeight(double newHeight){
        this.height = newHeight;
    }

    public double getArea(){
        return width * height;
    }

    public double getPerimeter(){
        return 2 * (width + height);
    }
}

测试类:

package demo;

/**
 * test for circle and rectangle
 * Created by luts on 2015/11/28.
 */
public class TestCircleRectangle {
    public static void main(String[] args){

        //create a circle
        Circle circle = new Circle(1);
        System.out.println("A circle "+ circle.toString());
        System.out.println("The radius is: " + circle.getRadius() + "\nThe area is: " + circle.getArea());
        System.out.println("The diameter is: "+ circle.getPerimeter());


        //create a rectangle
        Rectangle rectangle = new Rectangle(2,4);
        System.out.println("\nA rectangle "+ rectangle.toString());
        System.out.println("The area is: " + circle.getArea());
        System.out.println("The diameter is: "+ rectangle.getPerimeter());
    }
}

输出结果:

A circle creadted on Sat Nov 28 10:27:04 CST 2015
color: white and filled: false
The radius is: 1.0
The area is: 3.141592653589793
The diameter is: 6.283185307179586

A rectangle creadted on Sat Nov 28 10:27:04 CST 2015
color: white and filled: false
The area is: 3.141592653589793
The diameter is: 12.0

   Circle和Rectangle类继承GeometricObject类中所有可以访问的数据域和方法,另外两个类都重新定义了属于自己的求面积和周长的方法。GeometricObject类中的color和filled数据域是私有的,只能在GeometricObject类中访问,所有在子类中,如果要修改这两个属性,需要通过set和get方法。如:子类中重载的构造函数。

小结:

    • 1)子类并不是父类的一个子集,子类包含的信息通常比父类还多
    • 2)父类中的私有数据域在父类之外是不可访问的。如果子类中要访问父类的私有数据域,需要在父类中定义其get、set方法,通过访问器和构造器来访问和修改。
    • 3)继承关系是is-a关系建模。一个父类和子类之间必须存在是关系。
    • 4)java中不允许多继承,一个java类只能继承一个父类 ,即单一继续。多继承可以通过接口实现。

1.2. 使用super关键字

  super关键字的作用:

  ·1)调用父类的构造方法

super();  //调用父类的无参构造方法
super(parameter); //调用父类的有参构造方法

 语句super()或super(参数)必须出现在子类构造方法的第一行,这是显示调用父类构造方法的唯一方法。

例如上例中的circle类中的一个含参构造方法可以替换为:

//原来的定义
public Circle(double newRadius, String newColor, boolean newFilled){
        this.radius = newRadius;
        setColor(newColor);
        setFilled(newFilled);
    }


//调用父类的构造方法
    public Circle(double newRadius, String newColor, boolean newFilled){
        super(newColor, newFilled);
        this.radius = newRadius;
    }

注意:  1. 要调用父类的构造方法必须使用super关键字,而且调用super语句必须是子类构造方法的第一条语句。在子类中调用父类的构造方法的名字会引起语法错误。

     2. 构造方法可以用来构造一个类的实例。父类的构造方法不能被子类继承,只能通过super关键字调用。

     3. 构造方法可以调用重载的构造方法或者其父类的构造方法。如果没有显示的调用,编译器会自动将super()作为构造方法的第一条语句:

public ClassName() {
}

等价于:

public ClassName() {
    super();      
}


public ClassName(double a) {
}

等价于:

public ClassName(double a) {
    super();      
}

    4. 构造一个类的实例的时候,将会调用沿着继承链的所有父类的构造方法。——构造方法链

  ·2)调用父类的方法

  super关键字不仅可以调用父类的构造方法,还可以调用父类的方法:

  super.方法名 (参数);

例如上例中的Circle类的一个方法:
public void printCircle(){
        System.out.println("The circle is creadted " + getDateCreadted() + " and the raius is " + radius);
    }


可以写出:
    public void printCircle(){
        System.out.println("The circle is creadted " + super.getDateCreadted() + " and the raius is " + radius);
    }

1.3. 覆盖方法

  方法覆盖(method overriding):当子类需要修改父类中的方法实现。

  例如上例中的GeometricObject中的toString方法返回的是几何对象的字符串,这个方法在Circle中可以被覆盖:

public String  toString(){
        return super.toString() + "\nradius is " + radius;
    }

  注意:· 1)仅当实例方法是可被访问的时候,它才能被覆盖。父类中的私有方法不能被子类覆盖。

     · 2)虽然静态方法也能被子类继承,但是不能被覆盖。如果父类中的静态方法被子类重新定义,那么父类中的静态方法会被隐藏。可以使用:父类名.静态方法名(SuperClassName.staticMethodName)来调用静态方法。

1.4 覆盖和重载

  重载(overload):对于类的方法(包括从父类中继承的方法),方法名相同参数列表不同的方法之间就构成了重载关系。

----参数列表又叫参数签名,包括:参数的类型参数的个数参数的顺序。这三者只要有一个不同就叫做参数列表不同。

  覆盖 (override):也叫重写,就是在当父类中的某些方法不能满足要求时,子类中改写父类的方法。当父类中的方法被覆盖了后,除非用super关键字,否则就无法再调用父类中的方法了。

  

发生覆盖的条件:

  · 1、子类和父类的方法名称参数列表返回类型必须完全相同,而且子类方法的访问修饰符的权限不能比父类

  · 2、子类方法不能抛出比父类方法更多的异常。即子类方法所抛出的异常必须和父类方法所抛出的异常一致,或者是其子类,或者什么也不抛出

  · 3、被覆盖的方法不能是final类型的。因为final修饰的方法是无法覆盖的。

  · 4、被覆盖的方法不能为private。否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。

  · 5、被覆盖的方法不能为static。所以如果父类中的方法为静态的,而子类中的方法不是静态的,但是两个方法除了这一点外其他都满足覆盖条件,那么会发生编译错误。反之亦然。即使父类和子类中的方法都是静态的,并且满足覆盖条件,但是仍然不会发生覆盖,因为静态方法是在编译的时候把静态方法和类的引用类型进行匹配。

  方法的覆盖和重载具有以下相同点:

   · 都要求方法同名

     · 都可以用于抽象方法和非抽象方法之间

  方法的覆盖和重载具有以下不同点:

    -- 方法覆盖要求参数列表(参数签名)必须一致,而方法重载要求参数列表必须不一致

    -- 方法覆盖要求返回类型必须一致,方法重载对此没有要求。

    -- 方法覆盖只能用于子类覆盖父类的方法,方法重载用于同一个类中的所有方法(包括从父类中继承而来的方法)

    -- 方法覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。

    -- 父类的一个方法只能被子类覆盖一次,而一个方法可以在所有的类中可以被重载多次。

覆盖:

public class Test{
    
    public static void main(String[] args){
        A.a = new A();
        a.p(10);
        a.p(10.0);
    }
    
    class B{
        public void p(double i){
            System.out.println(i * 2);
        }
    }
    
    class A extends B(){
        public void p(double i){
            System.out.println(i);
        }
    }
}

上述程序运行输出都是10.0。因为调用的都是A中定义的p (double i)方法。

重载:

public class Test{
    
    public static void main(String[] args){
        A.a = new A();
        a.p(10);
        a.p(10.0);
    }
    
    class B{
        public void p(double i){
            System.out.println(i * 2);
        }
    }
    
    class A extends B(){
        public void p(int i){
            System.out.println(i);
        }
    }
}

输出:10    //调用A类中的p(int i)方法

   20.0    //调用B类中的p(double i)方法

1.5. 对象类Object和它的toString()方法

  java中的每个类都起源于java.lang.Object类。如果一个类没有指定继承性,那么其父类就是Object类。

  Object的toString()方法返回描述一个对象的字符串。

  上例中的GeometricObject类重新了toString()方法:

 public String toString(){
        return "creadted on " + dateCreadted + "\ncolor: " + color + " and filled: " + filled;
    }

二、多态(dynamic)

  多态:父类型的变量可以引用子类型的对象。

   先看两个概念:声明类型和实际类型。声明类型是变量被声明的某种类型,实例可以用声明类型或者其子类型的构造方法创建。实际类型是被变量引用的对象的实际类。

Object a = new GeometricObject();
System.out.println(a.toString());

  这里a声明的类型是Object,实际类型是GeometricObject,a指向使用new GeometricObject()创建的对象。a调用的是实际类型中的toString()方法。

  多态存在的三个必要条件
    1、要有继承;
    2、要有重写;
    3、父类引用指向子类对象。

package demo;

/**
 * Created by luts on 2015/11/28.
 */
public class DynamicBindingDemo {
    public static void main(String[] args) {
        m(new GraDuateStudent());
        m(new Student());
        m(new Person());
        m(new Object());
    }

    public static void m(Object x) {
        System.out.println(x.toString());
    }
}

    class GraDuateStudent extends Student{
    }

    class Student extends Person{
        public String toString(){
            return "Student";
        }
    }

    class Person extends Object{
        public String toString(){
            return "Person";
        }
    }

输出:

Student
Student
Person
java.lang.Object@1540e19d

三、对象转换与instanceof运算符

  语句 Object a = new Student() 是合法的,称为隐式转换。---(1)

     Student b = (Student) a; 称为显式转换。------(2)

  (1)将一个子类的实例转换为一个父类的变量,称为向上转换,是可行的,因为子类的实例也是父类的实例。

  (2)当把一个父类的实例转换成它的子类变量的时候(向下转换),必须使用“(子类名)”进行显示转换。

  进行类型转换是因为在编译时声明类型决定了匹配的方法。通常将变量定义为父类型,这样就可以接收任何子类型的值。

  instanceof运算符用于判断两个变量是不是同一类型。

  object 的equals方法:测试两个对象是否相等。

object中的equals方法的签名:
public boolean equals(Object obj){
       return (this == obj)
}

调用语法: object1.equals (object2)

这里的 == 比较运算符用来比较两个基本数据类型是否相等,或者判断两个对象是否具有相同的引用。

练习:答答出租车系统:(这里将数据域都设置为public了)

交通工具类:

package project1;

public abstract class TransTool {
    
    public String name;
    public int price;
    public int rentDay;
    //public int capacity;
    //public int weight;
    public abstract void runPlay();
    public  int RentBill(){
        return this.price * this.rentDay;
    }
}

货车类:

package project1;

public class GoodsCar extends TransTool {
    int weight;
    public GoodsCar(String newName, int newPrice, int newRentday, int newWeight){
        this.name = newName;
        this.price = newPrice;
        this.rentDay = newRentday;
        this.weight = newWeight;
    }
    @Override
    public void runPlay() {
        // TODO Auto-generated method stub
        System.out.println(name+"  租金:"+ price + "元每天,载货:"+weight+"吨");
    }

}

载人的车类:

package project1;

public class PassengerCar extends TransTool {
    public int capacity;
    public PassengerCar(String str, int newPrice, int newRentday, int newCapacity){
        this.name = str;
        this.price = newPrice;
        this.rentDay = newRentday;
        this.capacity = newCapacity;
    }

    @Override
    public void runPlay() {
        // TODO Auto-generated method stub
        System.out.println(name+"  租金:"+ price + "元每天,载人:"+capacity+"人");
    }

}

皮卡车:

package project1;

public class Pika extends TransTool {
    public int capacity;
    public int weight;
    public Pika(String newName, int newPrice, int newrentDay, int newCapacity, int newWeight){
        this.name = newName;
        this.price = newPrice;
        this.rentDay = newrentDay;
        this.capacity = newCapacity;
        this.weight = newWeight;
    }
    

    public void runPlay() {
        // TODO Auto-generated method stub
        System.out.println(name + "租金"+price+"元每天,载人"+capacity+"人,载货"+weight+"吨");
    }
    
}

测试类:

package project1;
import java.util.*;

public class Initial {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String[] names = {"AodiA4","Mazida6","Jinlong","Pika","Songhuojiang","Yiweike"};
        String []carName = {"奥迪A4","马自达6","金龙","皮卡雪6","松花江","依维柯"};
        int[] carCapacity={4,4,20,4,2,4,20};
        int[] prices={500, 400, 800, 450, 400, 1000};
        Scanner input = new Scanner(System.in);
        System.out.println("欢迎使用答答租车系统:");
        System.out.println("您是否需要租车:1 是  0否");
        int choice = input.nextInt();
        if(choice == 1){
            System.out.println("您可租车的类型及其价格表:");
            System.out.println("序号      汽车名称  租金   容量");
            System.out.println("1.  奥迪A4  500元/天   载人:4人 ");
            System.out.println("2.  马自达6  400元/天   载人:4人 ");
            System.out.println("3.  金龙           800元/天   载人:20人");
            System.out.println("4.  皮卡雪6 450元/天   载人:4人  载货:2吨 ");
            System.out.println("5.  松花江      400元/天   载货:4吨 ");
            System.out.println("6.  依维柯      1000元/天   载货:20吨 ");
            System.out.println("请输入您要租汽车的数量: ");
            
            
            int sumCapacity = 0;
            int sumWeight = 0;
            int sumBill = 0;
            int day;
            int i ;
            
            int num = input.nextInt();
            int[] carArray = new int[num];
            TransTool []car = new TransTool[num];
            for( i = 1; i <= num; i++){
                System.out.println("请选择第"+i+"辆汽车类型");
                carArray[i-1] = input.nextInt();
            }
            System.out.println("请输入租车天数:");
            day = input.nextInt();
            
            for(i = 0; i < carArray.length;i++){
                int Index = carArray[i];
                if(Index < 4){
                    PassengerCar carTemp = new PassengerCar(names[Index - 1],prices[Index - 1],day,carCapacity[Index - 1]);
                    sumCapacity +=carTemp.capacity;
                    car[i] = carTemp;

                }
                else if(Index == 4){
                    Pika carTemp  = new Pika("Pika",450,day,4,2);
                    sumCapacity += carTemp.capacity;
                    sumWeight += carTemp.weight;
                    car[i] = carTemp;
                }
                else{
                    GoodsCar carTemp = new GoodsCar(names[Index - 1], prices[Index - 1], day, carCapacity[Index]);
                    sumWeight += carTemp.weight;
                    car[i] = carTemp;
                }
                sumBill += car[i].RentBill();
            }
            
            input.close();
            System.out.println("您的账单:");
            System.out.println("***可载人的车有***");
            for(i = 0; i < carArray.length;i++){
                if(carArray[i] <= 4)
                    System.out.print(carName[carArray[i] - 1]+" ");
            }
            System.out.println(" 共载人:"+sumCapacity);
            System.out.println("***可载货的有***");
            for(i = 0; i < carArray.length;i++){
                if(carArray[i] >= 4)
                    System.out.print(carName[carArray[i] - 1]+" ");
            }
            System.out.println(" 共载货:"+sumWeight);
            System.out.println("***租车总价格:"+ sumBill);
            
        }
        else{
            System.out.println("欢迎您下次使用答答出租系统,再见!");
        }
    }

}
原文地址:https://www.cnblogs.com/luts/p/5002650.html