• 继承和多态


    一、继承

      定义:继承(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("欢迎您下次使用答答出租系统,再见!");
            }
        }
    
    }
  • 相关阅读:
    luogu P5488 差分与前缀和 FFT
    luogu P4173 残缺的字符串 FFT
    《数据结构与算法分析(C++语言描述)》
    《C语言—从入门到项目实践》Issue分析及总结
    操作系统学习笔记——第六章 文件管理
    操作系统学习笔记——第五章 I/O设备管理
    操作系统学习笔记——第四章 存储管理
    操作系统学习笔记——第二章 进程管理 和 第三章 死锁
    操作系统学习笔记——第一章 操作系统概述
    操作系统学习笔记——全部知识点流程图
  • 原文地址:https://www.cnblogs.com/luts/p/5002650.html
Copyright © 2020-2023  润新知