继承是面向对象中的第二大特点,其核心的本质在于可以将父类的功能一直沿用下去。
为什么需要有继承?
为了更好的区分出之前学习的概念与现在程序的区别,下面通过两个具体的代码来进行研究,例如现在给两个类:Person、Student,按照原始方式,程序代码如下:
学生是人,出现重复代码的核心问题是两个类之间没有联系。
使用继承解决问题
在Java中可以利用继承的概念实现父类代码的重用问题,程序中可以使用extends关键字实现继承操作定义,其使用的语法如下:
范例:使用继承来解决问题
class Person{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age=age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
class Student extends Person{//Student是Person的子类
//此时并没有在Student类中定于任何操作
}
public class Hello {
public static void main(String args[]){
Student stu = new Student();//实例化子类对象
stu.setName("张三");//通过Person类继承而来
stu.setAge(12);//通过Person类继承而来
System.out.println("姓名:"+stu.getName()+",年龄:"+stu.getAge());
}
}
可以发现,子类完整的继承了父类的属性及方法,但是子类可以对父类的属性和方法进行扩充。
范例:扩充父类
class Student extends Person{//Student是Person的子类
//此时并没有在Student类中定于任何操作
private String school;//子类自己扩充属性
public void setSchool(String school){
this.school=school;
}
public String getSchool(){
return this.school;
}
}
public class Hello {
public static void main(String args[]){
Student stu = new Student();//实例化子类对象
stu.setName("张三");//通过Person类继承而来
stu.setAge(12);//通过Person类继承而来
stu.setSchool("清华大学");//Student类自己扩充的方法
System.out.println("姓名:"+stu.getName()+",年龄:"+stu.getAge()+",学校:"+stu.getSchool());
}
}
通过继承实现发现:
- 父类的功能可以延续到子类继续使用,这样在某些父类不能修改的情况下可以利用子类进行扩充;
- 子类至少会拥有父类的方法和属性。
继承的限制
虽然继承的核心目的在于扩充类中已有的功能,但是在实际开发中,使用Java仍有若干限制。
限制一: Java不允许多重继承,也就是说一个子类只能继承一个父类
- 因为C++支持多继承,也就是说一个子类可以同时继承多个父类。
范例:错误的继承操作
class A{}
class B{}
class C extends A,B{}
为什么会出现这样的代码,其实本质上就是希望C类能够同是继承A类和B类的方法,但是由于这样的做法与现实生活有冲突,所以从Java的角度就屏蔽了,但是有解决方案。虽然Java不允许多重继承,但是却允许多层继承。以下代码是正确的。
class A{}
class B extends A{}
class C extends B{}
此时C类就具备A类和B类的全部定义,但是从实际出发,这样的继承不要超过三层。
限制二:子类在继承父类之后会将父类的全部结构继承下来,但对于私有操作属于隐式继承,而所有非私有操作属于显示继承。
范例:观察显示继承
class A {
private String name;
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
}
class B extends A{}
public class Hello{
public static void main(String args[]){
B b = new B();
b.setName("Jane");
System.out.println(b.getName());
}
}
现在对于子类B而言是存在有name属性的,但是这个属性子类并不能直接操作。
范例:子类B直接访问
class B extends A{
public void print(){
System.out.println(name);
}
}
也就是说此时name属于隐式继承,而所有的setter方法属于显示继承。显示继承可以直接访问,但隐式继承只能间接访问,否则不能访问。
限制三:在实例化子类时会默认调用子类的无参构造方法,但是在执行子类构造前会首先自动实例化父类构造,为父类的对象实例化,也就是说父类对象永远早于子类对象的实例化。
范例:观察实例化过程
class A {
public A(){
System.out.println("***************************");
}
}
class B extends A{
public B(){
System.out.println("############################");
}
}
public class Hello{
public static void main(String args[]){
B b = new B();
}
}
现在如果非要为子类加上一个调用父类构造的标记,那么就可以使用“supe()”的形式完成。
class B extends A{
public B(){
super(); //表示调用父类构造
System.out.println("############################");
}
}
证明在子类的构造中一直隐藏了一行“super”语句,但是在进行无参父类构造调用的时候,写上“super()”是没有意义的。往往是在父类没有提供无参构造时使用的。
范例:错误的代码
class A {
public A(String name){
System.out.println("***************************");
}
}
class B extends A{
public B(){
//super(); //表示调用父类构造
System.out.println("############################");
}
}
public class Hello{
public static void main(String args[]){
B b = new B();
}
}
可以发现现在使用隐含的“super()”并不合适,所以应该明确的调用指定参数的构造方法。
class A {
public A(String name){
System.out.println("***************************");
}
}
class B extends A{
public B(String name){
super( name); //表示调用父类构造
System.out.println("############################");
}
}
public class Hello{
public static void main(String args[]){
B b = new B("SMITH");
}
}
一般情况下父类都会提供无参构造方法,这个时候可以在子类构造中不出现“super()”无参构造,但是如果此时父类中没有提供无参构造方法,那么此时子类就必须使用“super()”调用指定参数的构造方法。
分析:关于this()与super()的问题。
“this()”表示调用本类的其他构造方法,“super()”由子类调用父类指定的构造方法,并且这两条语句都必须出现在首行,也就是说这两条语句不能够同时出现。
范例:不让子类调用父类构造
class A {
public A(){
System.out.println("***************************");
}
}
class B extends A{
public B(){}
public B(String name){
this(); //表示调用父类构造
System.out.println("############################");
}
}
public class Hello{
public static void main(String args[]){
B b = new B("Smith");
}
}
在子类的构造方法中出现了“this()”,这样就不会出现“super()”的默认执行,而转为调用本类的构造方法了,那么这样是不是可以不调用父类的构造方法了呢?不
总结
- 继承的唯一好处就是进行功能上的扩充,并且Java只支持单继承
- 子类对象实例化时一定要先实例化父类对象,而后在实例化子类自己的对象。