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. 关于我

    如有错误,还请指正。

  • 相关阅读:
    Firefox常用web开发插件
    引用MFC指针的获取(转载)
    J2EE的13种核心技术(转载)
    用Visio画ER图的解决方案(转载)
    [导入]六一
    [导入]独自等待
    [导入]随想
    [导入]小聚
    [导入]网站需求分析
    [导入]如何做好网站开发项目需求分析
  • 原文地址:https://www.cnblogs.com/xingrenguanxue/p/13427661.html
Copyright © 2011-2022 走看看