zoukankan      html  css  js  c++  java
  • 一文打尽Java继承的相关问题

    相关文章:

    1. 《面向对象再探究》:介绍了面向对象的基本概念
    2. 《详解Java的对象创建》:介绍了对象的创建、构造器的使用

    《面向对象再探究》这篇文章中已经笼统的介绍过继承的概念了,下面就来具体介绍继承的使用等相关问题。

    1. 引入例子

    Animal类和Dog类例子,下文都会围绕该例展开。

    public class Animal {
        private String name;
        private int age;
    
        public Animal(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public Animal() {
        }
    
        public void say() {
            System.out.println("我是" + name + ",今年" + age + "岁了");
        }
    
    	//getters and setters ...
    }
    
    public class Dog extends Animal {
    
        private String address;
    
        public Dog(String name, int age, String address) {
            super(name, age);
            this.address = address;
        }
    
        public Dog() {
        }
    
        public void say() {
            System.out.println("我叫" + super.getName() + ",今年" + super.getAge() + "岁了,家住" + address + ",汪汪汪...");
        }
    
        public void watchDoor() {
            System.out.println("我在" + address +"看门...");
        }
    
    	//getters and setters ...
    }
    

    Dog类继承Animal类,Animal类有nameage属性和say()方法,Dog类增加了address属性和watchDoor()方法。

    2. 概念介绍

    在现实生活中有许多继承的例子,比如“子承父业”、“继承父母财产”、“继承某人意志”等,这些都是在强调一个体得到另一个体的东西,他们之间存在“继承关系”。

    在Java中,继承是类和类之间的关系。在《面向对象再探究》这篇文章中举了动物Animal类和狗Dog类、猫Cat类、兔子Rabbit类的例子并写了代码,这里直接拿来用不再列出,请移步此文章。

    我们可以说狗、猫、兔子是动物,但不可以说动物是狗、猫、兔子。动物普遍有眼睛、耳朵等属性,有跑、吃、叫等行为。狗显然也具有这些属性和行为,但是狗也具有一些它特有的属性和行为,比如灵敏的鼻子、看门等。

    在这个例子中,Animal类是父类,Dog类、Cat类、Rabbit类是子类。子类和父类存在继承关系,可以说:子类“是”父类,该关系是继承的一个明显特征。

    下面是关于继承的几个Q&A:

    Q1: 什么时候需要用到继承?

    A1: 个人认为可从以下两点出发:

    1. 面向对象语言是对现实世界的抽象,所谓“万物皆对象”,那么我们在实际开发中的类肯定都是与现实世界的实物相对应的,所以看实物之间的关系有没有继承关系。
    2. 当有若干类有大量重复的属性和方法,那么就可以考虑使用继承,将这些重复的属性和方法抽成父类。

    Q2: 怎样使用继承?

    A2: 子类使用extends关键字继承父类,也只有使用了该关键字后,才存在继承关系及“父子类”:

    public class Animal {//父类
        //属性和方法
    }
    
    public class Dog extends Animal {//子类继承父类
        //属性和方法    
    }
    

    Q3: 在具体开发中,是先编写父类,然后扩展出子类,还是先编写子类,然后抽象出父类?

    A3: 个人认为不能一概而论。如果你的系统设计非常好,可以先写好父类,然后扩展子类。如果你没设计好,或者考虑不周,那么后期可能会出现许多类有重复代码,这时候可以考虑抽取父类。

    Q4: 父类和子类在内容上有什么区别?

    A4: 通常,我们将更通用的一些属性和方法放到父类中,比如狗、猫、兔子都具有的属性和方法会方法动物类中。子类中是更加特别的属性和方法,比如狗的看门行为、猫的抓老鼠行为。由于子类继承了父类,所以子类中只需要编写和父类的不同之处。

    所以,子类往往会比父类拥有更加丰富的属性和方法。

    Q5: 子类继承的父类的哪些东西?

    A5: 这里先给出结论,下文再详细介绍。子类继承了父类的非私有private成员变量、方法、嵌套类,子类不继承父类的构造器,但是子类可以调用父类的构造器。

    Q6: Java中可以继承多个类吗?

    A6: Java中的继承是单继承,只能继承一个类。

    3. 具体使用

    3.1. 子类重写父类的方法

    有时子类从父类继承得到的方法不一定适用,这时子类可以重写父类的方法。如Dog类重写了其父类的say()方法。

    注意要和重载加以区分。

    简单来说,重写涉及到的是两个类的同名方法,重载涉及到的是一个类的同名方法。关于重载介绍请移步这里

    3.2. 子类新增属性和方法

    前面提到:子类是对父类的扩展,子类内容比父类更加丰富。所以通常子类都会有自己的属性和方法,比如Dog类的address属性和watchDoor()方法。

    3.3. 子类如何使用父类的私有成员?

    子类不继承父类的私有成员,但是如果父类有能访问其私有成员变量的publicprotected的方法(比如getter方法),那么子类可以通过继承这些方法来使用父类的私有方法(Java官方教程)。

    下面用上面的代码例子解释:

    Animal类有私有成员变量、和公有方法say()say()直接访问本类的私有成员变量,没毛病!

    public class Animal {
        private String name;
        private int age;
        
        public void say() {
            System.out.println("我是" + name + ",今年" + age + "岁了");
        }
        //其他代码...
    }
    

    现在Dog类继承Anima

    public class Dog extends Animal {
    
        private String address;
        
        public void say() {//name和age报红
            System.out.println("我叫" + name + ",今年" + age + "岁了,家住" + address + ",汪汪汪...");
        }
        
        //其他代码...
    }
    

    请注意Dog类的say()方法,直接访问了nameage属性,如果子类继承了父类的私有成员,那么这样写是没问题的,但事实是nameage报红了,说明子类没有继承父类的私有成员。

    虽然子类没有继承父类的私有成员,但是我们可以通过父类的公有方法使用其私有成员变量,代码改动如下:

    public class Dog extends Animal {
    
        private String address;
        
    	public void say() {
            System.out.println("我叫" + super.getName() + ",今年" + super.getAge() + "岁了,家住" + address + ",汪汪汪...");
        }
        
        //其他代码...
    }
    

    使用super.xxx()可以调用父类的方法,由于getName()是父类公有方法,且能访问父类的私有成员变量name,所以子类调用getName()方法可以使用name变量。

    3.4. 子类如何对父类的私有成员赋值?

    子类没有继承父类的私有成员变量,当然不能直接赋值。通常有两种方法:调用父类的公有setter方法或调用父类的构造器。本质上还是子类通过使用父类提供的公有方法(setter或构造器)使用其私有成员变量。

    3.4.1. 使用setter方法:

    public class Dog extends Animal {
    
        private String address;
    
        public void setName(String name) {
            super.setName(name); //注意super
        }
    
        public void setAge(int age) {
            super.setAge(age);
        }
    
        public void say() {
            System.out.println("我叫" + super.getName() + ",今年" + super.getAge() + "岁了,家住" + address + ",汪汪汪...");
        }
    
        //其他代码...
    }
    

    Dog类的两个setter方法调用了Animal类的两个setter方法,由于子类的setter方法名和父类的setter方法名取得一样(重写了),所以一定要使用super关键字加以区分,否则就成递归了。

    3.4.2. 调用父类的构造器:

    public class Dog extends Animal {
    
        private String address;
    
        public Dog(String name, int age, String address) {
            super(name, age); //调用父类构造器
            this.address = address;
        }
    
        public Dog() {
        }
    
    
        public void say() {
            System.out.println("我叫" + super.getName() + ",今年" + super.getAge() + "岁了,家住" + address + ",汪汪汪...");
        }
        
        //其他代码...
    }
    
    public class Animal {
        private String name;
        private int age;
    
        public Animal(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        //其他代码...
    } 
    

    子类使用super()语句调用父类构造器,该语句必须是子类构造器的第一行代码。

    如果子类构造器没有显式调用父类构造器,则会默认调用父类的无参构造器。所以如果父类没有无参构造器,而子类又没有显式调用父类的其他构造器,则会报错。

    4. 关于我

    如有错误,还请指正。

  • 相关阅读:
    26 转义符 re模块 方法 random模块 collection模块的Counter方法
    25 正则表达式
    24 from 模块 import 名字
    24 from 模块 import 名字
    24 from 模块 import 名字
    23 析构方法 items系列 hash方法 eq方法
    21 isinstance issubclass 反射 _str_ _new_ _len_ _call_
    20 属性, 类方法, 静态方法. python2与python3的区别.
    python(1)
    python之字符串格式化
  • 原文地址:https://www.cnblogs.com/xingrenguanxue/p/13427661.html
Copyright © 2011-2022 走看看