我们书接上回:这一章,我们进入"继承"。
先来了解题目有关继承的需求:(本题是为知识服务,也可用于练手)
题目:
已有一个类Person类,代码如下:
Person类定义:
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
//做自我介绍
public void doSelfIntroduction(){
System.out.println("My name is "+name);
}
}
现要求定义一个类Student继承于Person类,Student类的要求如下:
- 新增一个成员变量 :stuID 表示学生的学号;
- 一个带参数构造器,初始化学生的姓名和学号;
- 重写父类的void doSelfIntroduction() 方法,改输出为:
"My name is xxxx and my stuID is xxxx"
- 新增一个方法,void study(),该方法输出:"I am studying!"
Main类代码:
public class Main{
public static void main(String[] args) {
Person person = new Person("Tom");
Scanner scanner = new Scanner(System.in);
String name = scanner.next();
String stuID = scanner.next();
Person stu1 = new Student(name,stuID);
person.doSelfIntroduction();
stu1.doSelfIntroduction();
Student stu2 = new Student(name,stuID);
stu2.study();
}
}
输入样例:
在这里给出一组输入。例如:
Jerry 121001
输出样例:
在这里给出相应的输出。例如:
My name is Tom
My name is Jerry and my stuID is 121001
I am studying!
继承:
继承这个词,一般在语言学上:子承父业,继往开来;在生物学上也异曲同工:遗传与变异
其实,好巧不巧,java世界里面的继承也是如此道理。还记得java的核心是什么吗?类。那类在继承场景中自然而然扮演主人公——继承(java),是子类对父类属性和行为的继承
父类(superclass),子类(subclass)
继承的语法
class 子类名 extends 父类名
{
//可以不再重复定义相同成员变量
}
子类继承父类时,需要在类定义时,使用extends+ 父类名,表示继承关系
继承的表现
既然继承是子类对父类属性和行为的继承——那么子类一旦继承父类,就可以拥有父类的属性和方法。这个说法确实是对的。
我们开始对这句话进行各种假设,父类是单一的吗?在继承语法上,只能extends一个直接父类。但子类的间接父类可以有很多个。就好比,人只有一个亲生父亲,却也有父亲的父亲,父亲的父亲的父亲,爷爷的爷爷的爷爷...这些间接的父类的属性和行为,子类同样也可以继承。
俩个验证
我们用代码验证
子类只允许单一继承父类:
上面代码,我们子类单一直接继承一个父类——假如我们尝试一次性直接继承两个父类
这样:
又或者是这样:
发现结果都报错了
:Class cannot extend multiple classes,不能同时继承多个父类
故:确实子类只能单一继承一个父类
验证:子类可以继承直接父类、间接父类的属性和方法:
我们写一个主类,创建三个类,Son类继承Father类,Father类继承Grandpa类
可知,在这三类的继承关系上,Grandpa类位于最高地位。
public class Grandpa {
String name;//属性1:姓名
int workyear;//属性2:工作年限
void work()//行为1:工作
{
System.out.println(name + "写唐诗");
}
void hobby()//行为2:爱好
{
System.out.println(name + "君子六艺");
}
}
public class Father extends Grandpa{
}//直接继承,定义类里代码为空
我们在main里验证,倘若符合子类只要extends一个父类,就可以拥有父类的属性和方法,何谓"拥有",能在主方法能对属性进行调用、赋值,对方法进行调用。
public class Main {
public static void main(String[] args) {
Father father = new Father();
father.name = "钱";//调用对象的属性并对其进行赋值
father.work();//调用对象的实例方法:工作
father.hobby();//调用对象的实例方法:爱好
}
}
代码并没有报错,说明子类只要继承父类,即可拥有父类的属性和方法
那子类可否继承间接父类的属性和方法呢?
public class Son extends Father {
}//Son类只是继承Father类,并没有定义其他属性和方法
public class Father extends Grandpa{
}//直接继承,定义类里代码为空
主类里面:
public class Main {
public static void main(String[] args) {
Son son = new Son();//对类实例化
son.name = "孙";//同样对姓名属性赋值
son.work();//调用方法
son.hobby();//调用方法
}
}
看看效果:
Son类除了继承Father类没写一行,Father类如上也只继承了Grandpa类,按理说Son继承Father类,应该没有一个属性和方法。但是如上面的代码,可见,Son也继承了Grandpa的属性和两个方法——"work" 和"hobby".
我们来看看运行结果:
public class Main {
public static void main(String[] args) {
Grandpa grandpa = new Grandpa();
grandpa.name = "赵";
grandpa.work();
grandpa.hobby();//创建一个grandpa的对象
Father father = new Father();
father.name = "钱";
father.work();
father.hobby();//创建一个father的对象
Son son = new Son();
son.name = "孙";
son.work();
son.hobby();//创建一个son的对象
}
}
三代可谓一脉相承,都会写唐诗和善君子六艺 。可是,这并不符合实际发展,子类为适应不断发展变化的环境和外界需求,对自身的行为和属性必须作出相应的变化和发展。
JAVA里面对这一子类进化需求,也作出回应:
1.子类可以添加父类所不具有的属性和方法
2.子类也可对继承过来的方法进行修改,但不能对属性进行修改。
再来俩个验证
1.子类可以添加父类所不具有的属性和方法
我们让Son适应时代需求,新添加一个行为和一个属性
public class Son extends Father {
String[] career;//添加属性:所谓技多不压身
void work2()//添加方法
{
System.out.println(name + "武功高强");
}
}
public class Main {
public static void main(String[] args) {
Son son = new Son();
son.name = "孙";
son.career = new String[]{"诗人","侠客"};//意外发现对于属性是字符串数组初始化赋值: new String[]{};
son.work();
son.work2();
son.hobby();
}
}
我们在Son类添加一个新的属性和方法,在主方法里对Son 进行实例化,并对属性进行赋值和方法调用。代码没报错,可见验证假设成功
看看结果:
2.子类也可对继承过来的方法进行修改,但不能对属性进行修改。
前半句,对继承过来的方法进行修改,我们又称——方法覆盖(Method Override)
在进行Override的时候,我们最好提前在上方加个注释:意为告诉JVM我们要对继承过来的方法进行覆盖重写了。
方法覆盖:
方法包括:
返回类型 方法名(参数列表)
{
方法体
}//如果原方法还有public ,覆盖的方法也得原封不动写下来
进行方法覆盖时,为了让JVM明确我们覆盖的到底是哪个方法,返回类型和方法名(参数列表)即除了方法体可做改变,其余保持原样。(返回类型可以返回原方法的类型的子类型,该知识点涉及向上转型,咱们后面再聊)
方法覆盖必须满足返回类型,方法名和参数列表与原方法一样。(now)
子类为了适应发展,创造出更多的属性和行为,还应该对继承下来的方法进行进化,发展出一套适合自身和外界需求的方法。——这也是传承的意义,不光为子类自身,更为整个类的发展
我们重写Father类和Son类方法work()
public class Father extends Grandpa{
@Override
void work()//继承并覆盖Grandpa的方法
{
System.out.println(name + "满腹经纶,学富五车");
}
}
public class Son extends Father {
@Override//继承间接父类的方法并进行覆盖
void work()
{
System.out.println(name + "文能提笔安天下,武能上马定乾坤");
}
}
主类代码进行调用:
public class Main {
public static void main(String[] args) {
Grandpa grandpa = new Grandpa();
grandpa.name = "赵";
grandpa.work();
Father father = new Father();
father.name = "钱";
father.work();
Son son = new Son();
son.name = "孙";
son.work();
}
}
对父类属性的修改:
JAVA本身并不提供子类“覆盖”父类成员变量的方法,子类仍然继承,但是可以不用表现,就如遗传时,基因都给子类,至于是否显现这个性状,就看子类是否愿意在主方法里面调用了。
小总结:
子类通过extends关键字,单一继承父类
子类继承直接父类和所有间接父类的属性和方法
子类可以添加新的属性和方法
子类可以改变父类的方法体
回到题目
先继承;
class Student extends Person
{}
在子类里面添加属性:
class Student extends Person
{
String stuID;
}
在子类写一个带参数构造器:
class Student extends Person
{
String stuID;
}
public Student(String name,String stuID)
{
this.name = name;//真的是这样赋值吗
this.stuID = stuID;
}//带参数构造器
我们发现报错了
父类里面的name成员是private,子类不能公开访问,那子类拥有这个属性吗?答案是有的。但是不能公开访问,需要其他方式进行访问。
我们再看父类代码,发现父类的构造器是公开的:父类是这个属性的创始者,对于这个属性虽然是私有的,子类蛮干是无法访问的。但是父类也为我们公开了访问和使用的方法,扑面而来的哲学气息:授人以鱼不如授人以渔。
鱼,父类已经暗暗给你,不过需要你学会如何渔。
super关键字
还记得父类的英文名吗?superclass,super关键字就是调用父类的方法,实现当前方法的需求。
方便理解,我们可以把super替换成父类的名字。
那么在子类里面要初始化从父类继承过来的私有属性,需要调用父类的构造器,怎么用super来写呢?答案已经呼之欲出了对不对
super()是调用父类的无参构造器,super(参数1,参数...)是调用父类的有参构造器,super.方法名是调用父类的方法。特殊说明,调用父类的构造器我们一般选用super关键字且在第一行,而其他公开实例方法,this和super都可以:his是因为子类直接继承过来了,super也可以:在子类里面使用父类的方法完成当前对象的需求
同样的,这里的父类可以是间接父类。
class Student extends Person
{
String stuID;
public Student(String name,String stuID)
{
super(name);//调用父类的构造器一定是在子类的第一行
this.stuID = stuID;
}
}
对父类方法的重写:
class Student extends Person
{
//...
@Override
public void doSelfIntroduction()
{
System.out.println("My name is " + getName() + " and my stuID is " + stuID);
}//getName()前面默认省掉this,也可用super,结果是一样的
}
在子类里面新增一个方法:
class Student extends Person
{
//...
public void study()
{
System.out.println("I am studying!");
}//新增方法
}
完整代码参考:
class Student extends Person
{
String stuID;
public Student(String name,String stuID)
{
super(name);
this.stuID = stuID;
}
@Override
public void doSelfIntroduction()
{
System.out.println("My name is " + getName() + " and my stuID is " + stuID);
}
public void study()
{
System.out.println("I am studying!");
}
}