前言:
前面已经大致的介绍了一下面向对象的四大特性中的“抽象”和“封装”(封装只讲了一点点,有关访问权限的问题我会在后面的内容中补上),接下来就来涉足一点点“深水区”的内容——继承和多态。这篇文章我将介绍一下继承。
正文:
现实生活中的继承:
既然是“面向对象”思想中的继承,那当然要首先提一下我们现实生活中的“对象”继承的实例了。俗话说“子承父业”,即孩子可以继承父母的所有财产等物。古有皇位世袭制度,作为皇子来说,很多的江山都可以不用自己去打,而直接从父皇那继承过来。现有王校长玩累了就可以回家继承自己家中的万贯家产。总结一下现实中的继承一贯做法:XXX可以不用自己做,可以直接拿来用。无数的现实实例都可以体现出继承这一思想和概念。
编程的面向对象思想中的继承:
继承的作用(优点、好处)
与现实生活类似,面向对象中的继承也是贯彻了——
代码可以不用自己写,可以直接拿来用这一思想。
所以面向对象思想中继承的最大优点与好处也就是——*减少代码量,可以直接把别人写好的代码调过来用,从而实现不用自己写(增强代码的复用性)的目的。
继承的基本语法
class 类名 extends 父类(超类名)
{
}
语法很简单,让我们根据后面实际例子来理解它
继承的运用:
先来举个实际应用的例子吧!
以下为我要定的需求:
要定义一个属性包含有姓名和id的Person类,方法包含一个sayHi()方法用于输出属性。
然后再定义一个属性包含有姓名和id的Teacher类,方法包含一个sayHi()方法用于输出属性和一个teach()方法用于说明职业。
最后再定义一个属性包含有姓名和id的Doctor类,方法包含一个sayHi()方法用于输出属性和一个teach()方法用于说明自己的技能。
最后在测试类中分别new 对象进行测试以输出信息和方法。
如果按照我们前面所说的内容来做的话,大致代码是这样的:
不用继承的话
public class TestDemo {
public static void main(String[] args) {
Person p=new Person("张三",10086);
p.sayHi();
System.out.println("---------------");
Teacher t=new Teacher("张三",10086);
t.sayHi();
t.teach();
System.out.println("---------------");
Docter d=new Docter("张三",10086,"开刀");
d.sayHi();
d.cut();
System.out.println("---------------");
}
}
class Person {
String name;
int id;
Person(String name,int id)
{
this.name=name;
this.id=id;
}
void sayHi()
{
System.out.println("我的名字是"+name+"我的id是"+id);
}
}
class Teacher
{
String name;
int id;
Teacher(String name,int id)
{
this.name=name;
this.id=id;
}
void sayHi()
{
System.out.println("我的名字是"+name+"我的id是"+id);
}
void teach()
{
System.out.println("我的职业是老师");
}
}
class Docter
{
String name;
int id;
String skill;
Docter(String name,int id,String skill)
{
this.name=name;
this.id=id;
this.skill=skill;
}
void sayHi()
{
System.out.println("我的名字是"+name+"我的id是"+id);
}
void cut()
{
System.out.println("我会"+skill);
}
}
然后经过编译运行,得到了如下输出结果:
这样看输出结果的话,的确是满足了我们前面说的需求。但是不得不说,这几个类定义的属性和方法中,有很多都是重复定义的。比如说Person类的姓名、id属性和sayHi()方法,这些成员在后面的Teacher类和Doctor类均有定义和出现,而且都是一模一样的出现形式和作用。所以说,按照上面的代码来实现的话,我们做了很多不必要的重复操作。
使用继承后
为了实现代码的重用,减少代码的重复。我们尝试着运用前面所说的继承的思想来实现一下。
把上面的代码修改过后,内容如下:
public class TestDemo {
public static void main(String[] args) {
Person p=new Person("张三",10086);
p.sayHi();
System.out.println("---------------");
Teacher t=new Teacher("张三",10086);
t.sayHi();
t.teach();
System.out.println("---------------");
Docter d=new Docter("张三",10086,"开刀");
d.sayHi();
d.cut();
System.out.println("---------------");
}
}
class Person {
String name;
int id;
Person(String name,int id)
{
this.name=name;
this.id=id;
}
void sayHi()
{
System.out.println("我的名字是"+name+"我的id是"+id);
}
}
class Teacher extends Person
{
Teacher(String name,int id)
{
super(name,id);//1
}
void teach()
{
System.out.println("我的职业是老师");
}
}
class Docter extends Person
{
String skill;
Docter(String name,int id,String skill)
{
super(name,id);//1
this.skill=skill;//2
}
void cut()
{
System.out.println("我会"+skill);
}
}
由此可知,同样的,运行结果还是与上面一样。
运用继承后的代码分析
接下来我们来深入分析一下这段修改过后的代码中的Teacher类。(前面的测试类和Person类因为没有做任何改动,Doctor类与Teacher类同理,所以就不再分析了)
将上面修改前后的代码进行对比,首先是Teacher类的代码:
修改前(没用继承的时候)
class Teacher
{
String name;
int id;
Teacher(String name,int id)
{
this.name=name;
this.id=id;
}
void sayHi()
{
System.out.println("我的名字是"+name+"我的id是"+id);
}
void teach()
{
System.out.println("我的职业是老师");
}
}
修改后(用了继承后)
class Teacher extends Person
{
Teacher(String name,int id)
{
super(name,id);//1
}
void teach()
{
System.out.println("我的职业是老师");
}
}
对比这两段代码,很明显的可以看出来,除了在Teacher的类名后按照前面所说的语法加了个extends Person之外。我还把其中的name和id属性的定义都删了,然后把构造方法里也删减了显式的赋值部分,取代它的是行super(name,id),与此同时,sayHi()方法也没有了。
在这里我们回忆一下最开始介绍的继承的思想——“XXX写的代码,我不用写,我直接拿来用。”
再结合前面前后2次的输出结果都是一模一样的可以看出来,父类Person中定义的姓名,id和sayHi()方法虽然不是我们自己写的,但是我们也是可以直接调用的。这样也就可以解释我为什么没有在Teacher类中定义姓名和id属性,却仍然有确定的姓名和id输出的问题了。
解决了父类的姓名和id都能够被子类正常调用后。那么再来看看修改后我标注为1的super(name,id);//1
部分。首先,super是一个关键字,在这意为父类,超类的意思(与上篇文章所说的this(当前类)的作用差不多,由于上篇文章已经解释过了,这里就不展开了)。这句话就是实现了将子类的从父类继承而来的属性,把这些属性值通过父类已有的构造方法实现构造的意思。这样子类的构造方法也就可以“少干点活,从而专注于构造自己特有的属性和变量的工作了(如上文修改过后的Doctor类中的构造方法:在那里,它把从父类继承过来的属性和变量发送给父类的构造方法,由父类进行构造,除此之外,它只用专门的对自己特有的skill属性进行赋值构造)”。
与类的成员变量与属性同理,对于子类来说,只要在类中特别定义出自己所特有的成员方法就行了,从父类那继承过来的方法,子类也一样可以正常使用(即继承过来了,也就属于子类的方法了)。
如下面这段代码中B类的成员方法其实是有a()、b()两个方法的(从父类继承过来的)。
class A
{
void a(){}
}
class B extends A
{
void b(){}
}
结语:
最后来总结一下继承的相关内容
作用:增强代码的复用性可迁移性,减少不必要的重复
特征:
1)运用extends关键字
2)子类可以把父类中定义的所有成员都全盘接收,使之成为自己的成员并使用。(暂时不考虑private权限的情况)
3)子类构造之前必须前构造父类