文章目录
前言(转载请说明作者!)3.25~3.29编写
在学习日志V中,我们编写的电子宠物Alpha中的Pig和Cat类中,部分属性和方法是重复的,对于这样,无论是对于计算机与编写代码人员,毫无疑问是冗余的,所以为了 避免代码重复与实现代码的复用 ,引出本博客的内容——继承。
知识最怕照本宣科,学好一门语言必定是自我的理解与实践的积累!博主尽可能的以白话来叙述知识点,以便自己与大家更好的理解。
自我问答
- 什么是继承?如何实现?
- 如何实现变量隐藏和方法重写?
- super关键字的意义是什么?
- final关键字如何使用?有什么特点?
- abstract关键字有什么用?用来修饰什么?
- Object类是什么?存在哪里?有什么作用?
- toString是什么?如何使用?
- 怎样实现访问控制?
以上内容可以观看完毕后再自我问答,目录结构大致与此相符。
任务导引:电子宠物Beta
在本博客,带领大家一起学会与使用继承,为宠物猫类(Cat)和猪类(Pig) 抽出共同父类 宠物类(Pet) ,把它们共同的属性和行为放到父类中,子类可以继承父类的属性和行为。
任务UML图
通过UML图可以看出,Cat 和 Pig 具有一些共同的特点(包括自身的属性和方法/行为),可以把这些共同的特征放到共同的父类Pet 中,子类Cat Pig 来继承父类的属性和方法,不用在各自的类体中重写这些多余的代码了,省时省力。
聪明的你可以想象到:当你使用Cat或Pig创建了一个新的实例,那么新的实例不仅有你所创建对象所对应类的属性和方法,还有它的父类Pet的所有属性和方法。
如何去实现UML图的想法,就是本博客所要涉及的内容。
继承
什么是继承?
继承是一种基于已有类来创建新类的机制,是指在已有类的基础上增加新的属性和功能。
继承分为 子类和父类 , 父类又叫超类,子类又叫派生类 。
父类可以是Java类库中的类,也可以是自己编写的类。
如何实现继承?
Java SE中,实现继承需要在类的声明中使用 extends 来声明一个类继承另一个类。
格式
[修饰符] class 子类名 extends 父类名{
……
}
e.g.(学生类继承了人类)
class Student extends Person{
……
}
类中有两种重要的成员:成员变量和方法。
子类继承父类的成员变量和方法,就像自己声明的一样使用,可以被子类对象直接调用或者子类中声明的实例方法调用。
下面以学生类( Student )和教师类( Teacher )为例。
案例
学生类( Student )和教师类( Teacher )都属于人类( Person ),都具有人类共同的特征,比如都有属性姓名、年龄及相应getter/setter方法,都可以打印自身信息等,那么我们就可以把共同属性和方法抽到父类Person中,子类可以继承Person的属性和行为,同时可以新增自己的属性,比如学生特有属性系别( department )和相应getter/setter方法,教师特有属性专长( major )和相应getter/setter方法。
EX7_1.java(main)
public class EX7_1 {
public static void main(String[] args) {
Student stu = new Student();
stu.setName("小明");
stu.setAge(20);
stu.printInfo();
stu.setDepartment("信工学院");
stu.haveClass("Java");
Teacher teacher = new Teacher();
teacher.setName("刘老师");
teacher.setAge(35);
teacher.setMajor("Java");
teacher.printInfo();
teacher.giveLesson();
}
}
Person类
public class Person {
// 共有属性
private String name;
private int age;
// 共有行为
public void printInfo() {
System.out.println("姓名:" + name + "\n年龄:" + age);
}
// get/setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Student类
public class Student extends Person {
// 新增属性
private String department;
// 新增行为
public void haveClass(String course) {
System.out.println("我正在上" + course + "课。");
// getter/setter
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
Teacher类
public class Teacher extends Person {
// 新增属性
private String major;
// 新增方法
public void giveLesson() {
System.out.println("我正在教授" + major + "课。");
}
// getter/setter
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
}
子类可以继承父类所有的成员和方法?
子类的继承性取决于子类和父类是否在同一包中以及成员的访问修饰符级别。
主要分为以下几种情况
-
如果子类和父类在同一包中,子类可以继承父类非private修饰的成员变量和成员方法。
-
如果子类和父类不在同一包中,子类只能继承父类public、protected修饰的成员变量和成员方法。
-
子类无法继承父类的构造方法。
继承的特点
-
Java只支持单重继承,不支持多重继承。
即:一个子类只能有一个直接父类。
-
多个类可以继承一个父类。
即:一个父类可以有多个子类。
-
Java支持多层继承。
即:继承有传递性,子类还可以有子类。
如何实现变量隐藏和方法重写?
变量隐藏
当子类中定义的成员变量和父类中定义的成员 变量同名时(类型可不同) , 称子类隐藏了继承的成员变量,当子类对象调用这个成员变量时, 一定是调用在子类中声明定义的那个成员变量,而不是从父类继承的成员变量。
PS:当一个文件有多个类的话,public修饰的类名只能有一个。
变量隐藏的案例
class Father {
public int y = 100;
public void printOfFather() {
System.out.println("Father的y = " + y);
}
}
class Son extends Father {
public double y = 0.5;
public void printOfSon() {
System.out.println("Son的y = " + y);
}
}
public class EX7_2 {
public static void main(String[] args) {
Son son = new Son();
son.y = 10.5;
son.printOfFather();
son.printOfSon();
}
}
输出结果:
Father的y = 100
Son的y = 10.5
方法重写
当子类定义一个方法,这个 方法返回类型、方法名、参数列表与从父类继承的方法完全相同时 ,我们称之为方法重写(OverRide) 。
子类一旦重写了父类的某个方法,则子类对象再调用该方法时,一定是调用的重写后的方法。
PS:子类重写父类方法时,访问权限不能降低!
方法重写案例
EX7_3.java(main)
public class EX7_3 {
public static void main(String[] args) {
Student stu = new Student();
stu.setName("小明");
stu.setAge(20);
stu.setDepartment("信工学院");
stu.printInfo();
stu.haveClass("Java");
Teacher teacher = new Teacher();
teacher.setName("刘老师");
teacher.setAge(35);
teacher.setMajor("Java");
teacher.printInfo();
teacher.giveLesson();
}
}
Person类
public class Person {
// 共有属性
private String name;
private int age;
// 共有行为
public void printInfo() {
System.out.println("姓名:" + name + "\n年龄:" + age);
}
// get/setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Teacher类
public class Teacher extends Person {
// 新增属性
private String major;
// 新增方法
public void giveLesson() {
System.out.println("我正在教授" + major + "课。");
}
// 重写printInfo方法
public void printInfo() {
System.out.println(
"姓名:" + getName() + "\n年龄:" + getAge() + "\n专长:" + major);
}
// getter/setter
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
}
Student类
package com.s7.p2;
public class Student extends Person {
// 新增属性
private String department;
// 新增行为
public void haveClass(String course) {
System.out.println("我正在上" + course + "课。");
}
// 重写printInfo方法
public void printInfo() {
System.out.println(
"姓名:" + getName() + "\n年龄:" + getAge() + "\n院系:" + department);
}
// getter/setter
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
方法重载和方法重写有什么区别?
方法的重写是在继承中出现的,是指子类定义跟父类完全相同的方法,修改方法的实现,达到改变继承来的行为的目的。例如Student对象调用继承来的printInfo()方法只能打印name、age ,通过方法重写实现了打印继承属性和新增属性目的。
方法重载是出现在同一个类中不同方法,只要求方法名字相同,方法参数列表不同,即同一类中同名方法通过传递不同的参数列表执行得到不同结果。
super关键字
super关键字出现在子类中,表示父类的对象。
super可以出现在子类的实例方法和构造方法中,不能出现在 子类的类方法中(即静态方法)。
在子类的实例方法中使用super
子类中一旦隐藏了父类的某个变量,子类的实例方法中不能再使用该变量,这时,如果子类实例方法想调用被隐藏的变量,那么就要用super关键字。
格式为: super.被隐藏变量
子类一旦重写了父类的某个方法,则子类的实例方法中想调用该方法,只能调用重写后的方法,父类的方法被隐藏,这时,如果子类实例方法想调用重写前父类中的该方法,那么也要用super关键字,
格式为: super.被重写方法
案例
案例1
EX7_4.java 实现
class Father {
public int y = 100;
public void print() {
System.out.println("Father的print()方法,y = " + y);
}
}
class Son extends Father {
public double y = 0.5;
public void print() {
super.print();
System.out
.println("Son的print()方法,y = " + y + "\n父类中Father的y = " + super.y);
}
}
public class EX7_4 {
public static void main(String[] args) {
Son son = new Son();
son.print();
}
}
输出:
Father的print()方法,y = 100
Son的print()方法,y = 0.5
父类中Father的y = 100
案例2:程序优化
EX7_5.java(main)
public class EX7_5 {
public static void main(String[] args) {
Student stu = new Student();
stu.setName("小明");
stu.setAge(20);
stu.setDepartment("信工学院");
stu.printInfo();
stu.haveClass("Java");
Teacher teacher = new Teacher();
teacher.setName("刘老师");
teacher.setAge(35);
teacher.setMajor("Java");
teacher.printInfo();
teacher.giveLesson();
}
}
Person类
public class Person {
// 共有属性
private String name;
private int age;
// 共有行为
public void printInfo() {
System.out.println("姓名:" + name + "\n年龄:" + age);
}
// get/setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Student类
public class Student extends Person {
// 新增属性
private String department;
// 新增行为
public void haveClass(String course) {
System.out.println("我正在上" + course + "课。");
}
// 重写printInfo方法
public void printInfo() {
super.printInfo();// 调用父类被重写方法
System.out.println("院系:" + department);
}
// getter/setter
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
Teacher类
public class Teacher extends Person {
// 新增属性
private String major;
// 新增方法
public void giveLesson() {
System.out.println("我正在教授" + major + "课。");
}
// 重写printInfo方法
public void printInfo() {
super.printInfo();// 调用父类被重写方法
System.out.println("专长:" + major);
}
// getter/setter
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
}
在子类的构造方法中使用super
聪明的你可能已经意识到:当你使用子类创建对象时,肯定存在父类中的一些属性或者方法,这些方法创建对象之后都是属于对象自己的。
也就是说,在子类里,肯定也存在以父类为类 实例化父类继承的属性和方法所对应的代码。
而super就可以调用父类的属性方法,那么:
当用子类构造方法创建对象时,子类的构造方法总是使用super关键字先调用父类的某个构造方法。
小例子
在Student中添加构造方法,通过构造方法初始化属性,观察子类如何使用super调用父类构造方法。
EX7_6(main)
public class EX7_6 {
public static void main(String[] args) {
Student stu1 = new Student();
stu1.printInfo();
System.out.println();
Student stu2 = new Student("小红", 20, "外语学院");
stu2.printInfo();
}
}
Person类
public class Person {
private String name;
private int age;
public Person() {
System.out.println("无参构造方法");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person的有参构造方法");
}
public void printInfo() {
System.out.println("姓名:" + name + "\n年龄:" + age);
}
}
Student类
public class Student extends Person {
private String department;
public Student() {
// super();// 可省略
super("小明", 18);
System.out.println("Student无参构造方法");
}
public Student(String name, int age, String department) {
super(name, age);
this.department = department;
}
public void printInfo() {
super.printInfo();
System.out.println("院系:" + department);
}
}
final关键字
大家如果对英语有点基础的话,肯定对finally很熟悉。
finally,…… 等同于:最后,……
所以final的意思就是最终的,不可更改的
它可以修饰类、成员变量、方法、方法的参数,一旦被final修饰,代表不可改变 。
final修饰的变量(成员变量和局部变量)是常量,只能赋值一次。也就是说final修饰的变量一旦被赋值,其值不能改变。如果再次对该变量进行赋值,则程序会在编译时报错。
abstract关键字
abstract指抽象,可以修饰类,也可以修饰方法 。
e.g.(类A是个抽象类)
abstract class A{
……
}
抽象类不能被实例化,不允许用抽象类创建对象,必须先产生其子类,再由子类创建对象。
abstract类是用来继承的,反映了一种 一般/特殊化的关系。
PS:抽象类不能使用final修饰 ,因为final修饰的类不能被继承。