继承的概念
定义
面向对象的继承,指在由一般类和特殊类形成的“一般-特殊”之间的类结构中,把一般类和所有特殊类都共同具有的属性和操作一次性地在一般类中进行定义,特殊类不再重复定义一般类已经定义的属性和操作,特殊类自动拥有一般类(以及所有更上层的一般类)定义的属性和操作。
** 特殊类的对象拥有一般类的对象的全部属性与操作(除非进行限制),称为特殊类对一般类的继承。**
如果类B继承类A,类B的对象具有类A对象的全部或部分属性和操作,则称被继承的类A为基类、父类或超类,而类B为类A的派生类或子类(见图1)。
一个类是子类还是父类,与该类所在的类层次有关,一个类既可以是父类也可以是子类。
继承避免了重复描述一般类和特殊类之间的共同特征,同时,继承能清晰表达每个共同特征所适应的概念范围——在一般类中定义的属性和操作适应于这个类本身以及它以下的每层特殊类的全部对象。
特征
(1)继承关系具有传递性。若类C继承类B,类B继承类A,则类C拥有从类B继承的属性与方法,也拥有从类A继承的属性和方法,还能定义新的属性和方法。
(2)继承简化了人们对客观事物的认识和描述,继承树清晰体现类之间的层次结构。
(3)继承提供了软件复用功能。若类B继承类A,那么建立类B时只需要描述与父类(类A)不同的特征(数据成员和结构方法)。这种方式减少代码量和数据的冗余度,大大增加代码的实用性。
(4)继承降低了模块间的接口和界面,大大增强了软件的可维护性。
(5)具有多种继承形式。①多重继承,即理论上一个类可以是多个一般类的特殊类,它可以从多个一般类中继承属性和方法。②单继承,即一个子类只能有唯一的一个父类。Java出于安全性和可靠性考虑,类之间仅支持单继承,通过接口机制实现多重继承。
创建子类
Java语言通过extends关键字实现继承。
【语法格式】继承
class 子类名 extends 父类名{
//类体
}
【程序案例1】Student类与Person类的继承关系。
package chapter07;
class Person{
//1.1数据成员
private String name;
private int age;
//1.2构造方法
public Person(){ }
public Person(String name, int age){
this.name = name;
this.age = age;
}
//1.3省略setter 和getter方法
//1.4功能方法,获取人的信息
public String getPerson(){
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
class Student extends Person{ //2.Person类的子类Student
//2.1子类的数据成员
private String className;
//2.2子类的构造方法
public Student(){ }
public Student(String name, int age , String className){
super(name, age);
this.className = className;
}
//2.3省略子类的setter和getter方法
//2.4子类的功能方法
public String getStudent(){
return this.getPerson() + ", 班级:" + this.className;
}
}
public class Demo0701{
public static void main(String[] args) {
Student shk = new Student("沈括", 56 ,"物理实验班");
System.out.println(shk.getStudent());
System.out.println(shk.getPerson()); //子类可以直接访问从父类继承的
//public方法
//子类访问从Person类继承的getName()方法
System.out.println("姓名:" + shk.getName());
}
}
子类从父类继承所有非private的数据成员和成员方法作为自己的成员,可以直接访问它们;但子类不能直接访问父类的私有成员,如需访问父类私有数据成员,一般通过setter和getter方法实现。如果定义类时没有使用extends继承父类,默认该类是系统类Object的子类。
方法覆写与属性覆盖
继承机制表示父类与子类之间的关系,父类和子类都包含若干数据成员和成员方法,子类继承父类数据成员和成员方法,子类也能定义特殊的数据成员和成员方法。
方法覆写
继承关系中,如果子类的某个方法与父类的某个方法同名,并且两个同名方法的形参列表相同,则称子类方法覆写父类方法(简称方法覆写)。方法覆写需要满足被子类覆写的方法不能拥有比父类对应方法更严格的访问权限(访问权限关系private<default<public)。如下面代码段,子类B第5行show()方法的访问权限default比父类A第2行show()方法的访问权限严格(小),因此第5行不能覆写第2行的方法,出现编译错误(Cannot reduce the visibility of the inherited method from A)。
class A{
public void show(int x) {}
}
class B extends A{
void show( int x ) {}
}
方法覆写之后会有什么后果呢?顾名思义,方法覆写就是子类方法覆盖了父类方法。方法覆写之后,子类对象调用的覆写方法就是子类的方法。
【程序案例2】方法覆写。
package chapter07;
class Person{ //父类Person
public void talk(String msg){
System.out.println("父类说:" + msg);
}
}
class Student extends Person{ //Person类的子类Student
public void talk(String msg){ //覆写父类Person的成员方法talk()
System.out.println("子类说:" +msg);
}
public void show(String info) { //子类的成员方法
talk(info); //调用子类本身的成员方法
//this.talk(info);
super.talk(info); //调用被子类覆写的父类中的成员方法
}
}
public class Demo0703{
public static void main (String args[]){
Student std = new Student(); //实例化Student对象
std.talk("勤奋出真知!"); //调用Student对象的方法
std.show("王选");
}
}
程序案例2演示了子类Student覆写父类Person的成员方法talk()。第8行覆写了第3行父类Person的成员方法talk(),第12行调用子类本身的成员方法talk(),第14行利用super关键字调用了父类被覆写的talk()方法。程序运行结果如图所示。
** 方法覆写与方法重载不同:①方法覆写指子类的方法与父类的方法在方法名和形参列表两方面完全一致;方法重载指方法同名,但形参列表不同。②方法覆写发生在继承关系,方法重载发生在类体或者继承关系。**
属性覆盖
继承机制的成员方法之间可能存在覆写关系,而数据成员之间可能存在覆盖关系。属性覆盖指继承关系中子类的数据成员与父类的数据成员同名,且父类的同名数据成员在子类可见(即父类的同名数据成员的访问控制权限符不是private)。
【程序案例3】属性覆盖。
package chapter07;
class A{ //父类
private String msg = "父类A的成员msg";
public int x =11;
public void printA(){
System.out.println("父类A的成员方法!");
}
}
class B extends A{ //子类
private String msg = "子类B的成员msg"; //与父类成员同名,private不能覆盖
public int x =22; //与父类成员同名,覆盖A的数据成员x
public void show(){
System.out.println(msg);
System.out.println("子类B的数据成员 x= "+this.x);
System.out.println("父类B的数据成员 x= "+super.x);
//调用父类的覆盖的数据成员x
}
}
public class Demo0704{
public static void main(String[] args){
B b = new B();
b.show(); //调用子类B中的方法
b.printA(); //调用从父类A继承的方法
}
}
程序案例3演示了子类B覆盖父类A的数据成员。第4、11行定义了同名数据成员,并且访问权限都是public(子类可见),因此子类数据成员x覆盖了父类数据成员x。第3、10行定义了同名数据成员msg,但该成员的访问权限为private(子类不可见),因此这两个数据成员之间没有覆盖关系。第14行this.x调用子类数据成员x,第15行使用super.x调用父类数据成员x。程序运行结果如图所示。
方法覆写与属性覆盖的区别
习题
编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
编写PC子类,继承Computer类,添加特有属性【品牌brand】
编写NotePad子类,继承Computer类,添加特有属性【color】
编写Test类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用方法并打印输出信息。
作答:
public class Computer {
private String cpu;
private int memory;
private int disk;
public Computer(String cpu, int memory, int disk){
this.cpu = cpu;
this.memory = memory;
this.disk = disk;
}
public String getDetails(){
return "cpu=" + cpu +"memory=" + memory + "disk=" +disk;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public int getMemory() {
return memory;
}
public void setMemory(int memory) {
this.memory = memory;
}
public int getDisk() {
return disk;
}
public void setDisk(int disk) {
this.disk = disk;
}
}
public class PC extends Computer {
private String brand;
public PC(String cpu, int memory, int disk, String brand) {
super(cpu, memory, disk);
this.brand =brand;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void printInfo(){
System.out.println("PC信息=");
System.out.println(getDetails() + "brand=" + brand);
}
}
public class NotePad extends Computer{
private String color;
public NotePad(String cpu, int memory, int disk, String color) {
super(cpu, memory, disk);
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void printInfo(){
System.out.println("NotePad信息=");
System.out.println(getDetails() +"color" + color);
}
}
public class Test {
public static void main(String[] args) {
PC pc = new PC("intel", 16 ,500 ,"IBM");
pc.printInfo();
NotePad notePad = new NotePad("AMD",8 ,256, "blue");
notePad.printInfo();
}
}
代码结果为