zoukankan      html  css  js  c++  java
  • 031:Java继承(extends)简明教程

    Java 继承(extends)简明教程

    (转自:http://c.biancheng.net/view/940.html

    1.类的封装

           继承是面向对象的三大特征之一。继承和现实生活中的“继承”的相似之处是保留一些父辈的特性,从而减少代码冗余,提高程序运行效率。
    Java 中的继承就是在已经存在类的基础上进行扩展,从而产生新的类。已经存在的类称为父类、基类或超类,而新产生的类称为子类或派生类。在子类中,不仅包含父类的属性和方法,还可以增加新的属性和方法
    Java 中子类继承父类的语法格式如下:

    1. 修饰符 class class_name extends extend_class {
    2.     // 类的主体
    3. }

    其中,class_name 表示子类(派生类)的名称;extend_class 表示父类(基类)的名称;extends 关键字直接跟在子类名之后,其后面是该类要继承的父类名称。例如:

    1. // Student是子类,extends后面是要继承的父类Person
    2. public class Student extends Person{}

    Java 的继承通过 extends 关键字来实现,extends 的英文意思是扩展,而不是继承。extends 很好的体现了子类和父类的关系,即子类是对父类的扩展,子类是一种特殊的父类。从这个角度看,使用继承来描述子类和父类的关系是错误的,用扩展更恰当。
    那么为什么国内把 extends 翻译为“继承”呢?子类扩展父类之后就可以获得父类的属性和方法,这与汉语中的继承(子类从父类获得一笔财富称为继承)具有相似性。

    Java 与 C++ 定义继承类的方式十分相似。Java 用关键字 extends 代替了 C++ 中的冒号(:)。在 Java 中,所有的继承都是公有继承, 而没有 C++ 中的私有继承和保护继承。

    类的继承不改变类成员的访问权限,也就是说,如果父类的成员是公有的、被保护的或默认的,它的子类仍具有相应的这些特性,并且子类不能获得父类的构造方法。

    例 1

    教师和学生都属于人,他们具有共同的属性:姓名、年龄、性别和身份证号,而学生还具有学号和所学专业两个属性,教师还具有教龄和所教专业两个属性。下面编写 Java 程序代码,使教师(Teacher)类和学生(Student)类都继承于人(People)类,具体的实现步骤如下。
    1)创建人类 People,并定义 name、age、sex、sn 属性,代码如下:

    1. //创建People 类
    2. public class People {
    3.     public String name; // 姓名
    4.     public int age; // 年龄
    5.     public String sex; // 性别
    6.     public String sn; // 身份证号
    7. //添加类的属性,构造方法
    8.     public People(String name, int age, String sex, String sn) {
    9.           //this用于任何实例方法内指向当前对象
    10. 10.         this.name = name;
    11. 11.         this.age = age;
    12. 12.         this.sex = sex;
    13. 13.         this.sn = sn;
    14. 14.     }

    15. //添加方法

    1. 16.     public String toString() {
    2. 17.         return "姓名:" + name + "\n年龄:" + age + "\n性别:" + sex + "\n身份证号:" + sn;
    3. 18.     }

    19. }

    如上述代码,在 People 类中包含 4 个公有属性、一个构造方法和一个 toString() 方法。
    2)创建 People 类的子类 Student 类,并定义 stuNo 和 department 属性,代码如下:

    1. //添加People的子类Student,Student继承了People
    2. public class Student extends People {
    3.     private String stuNo; // 学号
    4.     private String department; // 所学专业
    5.  
    6.     public Student(String name, int age, String sex, String sn, String stuno, String department) {
    7.           // 调用父类中的构造方法:super()
    8.         super(name, age, sex, sn);
    9.         this.stuNo = stuno;
    10. 10.         this.department = department;
    11. 11.     }
    12. 12.  
    13. 13.     public String toString() {
    14. 14.         return "姓名:" + name + "\n年龄:" + age + "\n性别:" + sex + "\n身份证号:" + sn + "\n学号:" + stuNo + "\n所学专业:" + department;
    15. 15.     }

    16. }

    由于 Student 类继承自 People 类,因此,在 Student 类中同样具有 People 类的属性和方法,这里重写了父类中的 toString() 方法。
    注意:如果在父类中存在有参的构造方法而并没有重载无参的构造方法,那么在子类中必须含有有参的构造方法,因为如果在子类中不含有构造方法,默认会调用父类中无参的构造方法,而在父类中并没有无参的构造方法,因此会出错。
    3)创建 People 类的另一个子类 Teacher,并定义 tYear 和 tDept 属性,代码如下:

    1. //添加子类
    2. public class Teacher extends People {
    3.     private int tYear; // 教龄
    4.     private String tDept; // 所教专业
    5.  
    6.     public Teacher(String name, int age, String sex, String sn, int tYear, String tDept) {
    7.         super(name, age, sex, sn); // 调用父类中的构造方法
    8.         this.tYear = tYear;
    9.         this.tDept = tDept;
    10. 10.     }
    11. 11.  
    12. 12.     public String toString() {
    13. 13.         return "姓名:" + name + "\n年龄:" + age + "\n性别:" + sex + "\n身份证号:" + sn + "\n教龄:" + tYear + "\n所教专业:" + tDept;
    14. 14.     }

    15. }

    Teacher 类与 Student 类相似,同样重写了父类中的 toString() 方法。
    4)编写测试类 PeopleTest,在该类中创建 People 类的不同对象,分别调用它们的 toString() 方法,输出不同的信息。具体的代码如下:

    1. //创建子类PeopleTest
    2. public class PeopleTest {
    3.     public static void main(String[] args) {
    4.         // 创建Student类对象
    5.         People stuPeople = new Student("王丽丽", 23, "女", "410521198902145589", "00001", "计算机应用与技术");
    6.         System.out.println("----------------学生信息---------------------");
    7.         System.out.println(stuPeople);
    8.  
    9.         // 创建Teacher类对象
    10. 10.         People teaPeople = new Teacher("张文", 30, "男", "410521198203128847", 5, "计算机应用与技术");
    11. 11.         System.out.println("----------------教师信息----------------------");
    12. 12.         System.out.println(teaPeople);
    13. 13.     }

    14. }

    运行程序,输出的结果如下:

    ----------------学生信息---------------------

    姓名:王丽丽

    年龄:23

    性别:女

    身份证号:410521198902145589

    学号:00001

    所学专业:计算机应用与技术

    ----------------教师信息----------------------

    姓名:张文

    年龄:30

    性别:男

    身份证号:410521198203128847

    教龄:5

    所教专业:计算机应用与技术

    1      单继承

    Java 语言摒弃了 C++ 中难以理解的多继承特征,即 Java 不支持多继承,只允许一个类直接继承另一个类,即一个子类只能有一个直接父类,extends 关键字后面只能有一个类名。例如,如下代码会导致编译错误:

    1. class Student extends Person,Person1,Person2{…}
    2. class Student extends Person,extends Person1,extends Person2{…}
      很多地方在介绍 Java 的单继承时,可能会说 Java 类只能有一个父类,严格来讲,这种说法是错误的,应该是一个类只能有一个直接父类,但是它可以有多个间接的父类。例如,Student 类继承 Person 类,Person 类继承 Person1 类,Person1 类继承 Person2 类,那么 Person1 和 Person2 类是 Student 类的间接父类。图 1 展示了单继承的关系。


    图 1  图形类之间的关系
    从图 1 中可以看出,三角形、四边形和五边形的直接父类是多边形类,它们的间接父类是图形类。图形类、多边形类和三角形、四边形、五边形类形成了一个继承的分支。在这个分支上,位于下层的子类会继承上层所有直接或间接父类的属性和方法。如果两个类不在同一个继承树分支上,就不会存在继承关系,例如多边形类和直线。
    如果定义一个 Java 类时并未显式指定这个类的直接父类,则这个类默认继承 java.lang.Object 类。因此,java.lang.Object 类是所有类的父类,要么是其直接父类,要么是其间接父类。因此所有的 Java 对象都可调用 java.lang.Object 类所定义的实例方法。
    使用继承的注意点:

    1. 子类一般比父类包含更多的属性和方法。
    2. 父类中的 private 成员在子类中是不可见的,因此在子类中不能直接使用它们。
    3. 父类和其子类间必须存在“是一个”即“is-a”的关系,否则不能用继承。但也并不是所有符合“is-a”关系的都应该用继承。例如,正方形是一个矩形,但不能让正方形类来继承矩形类,因为正方形不能从矩形扩展得到任何东西。正确的继承关系是正方形类继承图形类。
    4. Java 只允许单一继承(即一个子类只能有一个直接父类),C++ 可以多重继承(即一个子类有多个直接父类)。

    2        继承的优缺点

    在面向对象语言中,继承是必不可少的、非常优秀的语言机制,它有如下优点:

    1. 实现代码共享,减少创建类的工作量,使子类可以拥有父类的方法和属性。
    2. 提高代码维护性和可重用性。
    3. 提高代码的可扩展性,更好的实现父类的方法。


    自然界的所有事物都是优点和缺点并存的,继承的缺点如下:

    1. 继承是侵入性的。只要继承,就必须拥有父类的属性和方法。
    2. 降低代码灵活性。子类拥有父类的属性和方法后多了些约束。
    3. 增强代码耦合性(开发项目的原则为高内聚低耦合)。当父类的常量、变量和方法被修改时,需要考虑子类的修改,有可能会导致大段的代码需要重构。

    例 1

    下面以一个员工类的封装为例介绍封装过程。一个员工的主要属性有姓名、年龄、联系电话和家庭住址。假设员工类为 Employee,示例如下:

    1. public class Employee {
    2.     private String name; // 姓名
    3.     private int age; // 年龄
    4.     private String phone; // 联系电话
    5.     private String address; // 家庭住址
    6.     public String getName () {
    7.         return name;
    8.     }
    9. // setXxx() 方法来对其进行赋值
    10. 10.     public void setName(String name) {
    11. 11.         this.name = name;
    12. 12.     }

    13. //通过 getXxx() 方法来访问这些属性

    1. 14.     public int getAge() {
    2. 15.         return age;
    3. 16.     }
    4. 17.     public void setAge(int age) {
    5. 18.         // 对年龄进行限制
    6. 19.         if (age < 18 || age > 40) {
    7. 20.             System.out.println("年龄必须在18到40之间!");
    8. 21.             this.age = 20; // 默认年龄
    9. 22.         } else {
    10. 23.             this.age = age;
    11. 24.         }
    12. 25.     }
    13. 26.     public String getPhone() {
    14. 27.         return phone;
    15. 28.     }
    16. 29.  
    17. 30.     public void setPhone(String phone) {
    18. 31.         this.phone = phone;
    19. 32.     }
    20. 33.  
    21. 34.     public String getAddress() {
    22. 35.         return address;
    23. 36.     }
    24. 37.     public void setAddress(String address) {
    25. 38.         this.address = address;
    26. 39.     }

    40. }

    如上述代码所示,使用 private 关键字修饰属性,这就意味着除了 Employee 类本身外,其他任何类都不可以访问这些属性。但是,可以通过这些属性的 setXxx() 方法来对其进行赋值,通过 getXxx() 方法来访问这些属性。
    在 age 属性的 setAge() 方法中,首先对用户传递过来的参数 age 进行判断,如果 age 的值不在18 到 40 之间,则将 Employee 类的 age 属性值设置为20,否则为传递过来的参数值。
    编写测试类 EmployeeTest,在该类的 main() 方法中调用 Employee 属性的 setXxx() 方法对其相应的属性进行赋值,并调用 getXxx() 方法访问属性,代码如下:

    1. //新建测试类
    2. public class EmployeeTest {
    3.     public static void main(String[] args) {
    4.           //创建一个实例
    5.         Employee people = new Employee();
    6.           //使用setXxx()对属性进行赋值
    7.         people.setName("王丽丽");
    8.         people.setAge(35);
    9.         people.setPhone("13653835964");
    10. 10.         people.setAddress("河北省石家庄市");
    11. 11.           //使用getXxx()对属性进行访问
    12. 12.         System.out.println("姓名:" + people.getName());
    13. 13.         System.out.println("年龄:" + people.getAge());
    14. 14.         System.out.println("电话:" + people.getPhone());
    15. 15.         System.out.println("家庭住址:" + people.getAddress());
    16. 16.     }

    17. }

    运行该示例,输出结果如下:

    姓名:王丽丽

    年龄:35

    电话:13653835964

    家庭住址:河北省石家庄市

           通过封装,实现了对属性的数据访问限制,满足了年龄的条件。在属性的赋值方法中可以对属性进行限制操作,从而给类中的属性赋予合理的值,并通过取值方法获取类中属性的值(也可以直接调用类中的属性名称来获取属性值)。

    2.封装图书信息类

           了解有关封装的知识后,通过完整的例子再次实现封装。要求编写表示图书的 Book 类,实现以下需求:

    • 基本信息包括图书名称(bookName)、总页数(pagelbtalNum),其中页数不能少于 200 页,否则输出错误信息,并赋予默认值 200。
    • 为各个属性设置赋值和取值方法。
    • 具有 details() 方法,该方法在控制台输出每本图书的名称和总页数。
      编写 BookTest 测试类,为 Book 对象的属性赋予初始值,并调用 details() 方法输出详细信息。根据上面的描述添加代码,步骤如下。
      1)创建 Book 类,首先向该类添加 bookName 变量,并封装该变量。代码如下:
    1. //创建类
    2. public class Book {
    3. //添加 bookName 变量
    4.     private String bookName; // 图书名称
    5. //封装变量,使用了getXxx()访问
    6.     public String getBookName() {
    7.         return bookName;
    8.     }
    9. //封装变量,使用了setXxx()赋值
    10. 10.     public void setBookName(String bookName) {
    11. 11.         this.bookName = bookName;
    12. 12.     }

    13. }

    2)在 Book 类中添加 bookTotalNum 变量,并封装该变量,在封装的 setter 方法中判断页数的值是否小于 200。代码如下:

    1. private int bookTotalNum; // 图书总页数
    2. //封装变量
    3. public int getBookTotaiNum() {
    4.     return bookTotalNum;
    5. }
    6. //在封装的 setXxx()方法中判断页数的值是否小于 200
    7. public void setBookTotalNum(int bookTotalNum) {
    8.     if (bookTotalNum < 200) {
    9.         System.out.println(this.bookName + "这本书的页数不能少于 200 页");
    10. 10.         this.bookTotalNum = 200;
    11. 11.     } else {
    12. 12.         this.bookTotalNum = bookTotalNum;
    13. 13.     }

    14. }

    3)在 Book 类中添加公有的 details() 方法,输出图书的名称和总页数。代码如下:

    1. //创建 details()方法,输出图书信息
    2. public void details() {
    3.     System.out.println(this.bookName + "这本书的总页数是:" + this.bookTotalNum);
    4. }

    4)创建 BookTest 测试类,在该类的 main() 方法中创建 Book 类的两个实例对象,然后分别为类中的两个属性赋值,最后调用 details() 方法输出信息。代码如下:

    1. //创建类
    2. public class BookTest {
    3.     public static void main(String[] args) {
    4.           //创建Book的实例对象
    5.         Book book1 = new Book();
    6.          
    7.         book1.setBookName("《红与黑》");
    8.         book1.setBookTotalNum(190);
    9.         book1.details();
    10. 10.         System.out.println("************************************");
    11. 11.         Book book2 = new Book();
    12. 12.         book2.setBookName("《格林童话》");
    13. 13.         book2.setBookTotalNum(520);
    14. 14.         book2.details();
    15. 15.     }

    16. }

    5)执行上述代码,输出结果如下:

    《红与黑》这本书的页数不能少于 200 页

    《红与黑》这本书的总页数是:200

    ************************************

    《格林童话》这本书的总页数是:520

     

    本文来自博客园,作者:Jaoany,转载请注明原文链接:https://www.cnblogs.com/fanglijiao/p/15553904.html

  • 相关阅读:
    循序渐进学习XHTML
    一些常用正则表达式
    输入框限制
    Oracle 取随机数
    安装部署中的数据库打包和快捷方式启动浏览器
    游标小例
    查询列数
    临时表简介
    Update动态更新
    sql 多列转一列
  • 原文地址:https://www.cnblogs.com/fanglijiao/p/15553904.html
Copyright © 2011-2022 走看看