java中的继承

继承是面向对象设计的四大特性之一。从表面上看,继承关系就是“父子”关系。子类可以继承父类非私有的成员变量和成员方法。换句话说,在父类中已经定义的成员变量和成员方法,子类可以直接使用,而不必重新定义。那么,在java中,继承是如何体现的呢?在使用继承关系时,又有哪些要点呢?在这篇文章就简单总结一下。


首先说继承。如果仅仅说继承就是“父子”关系,其实不太全面。准确来说,继承描述的是对象之间的 “is a” 关系。 举例来说:学生是一个人;牡丹是一种花;鹦鹉是一种鸟…… 这样的关系简直是太常见了。

在java中实现继承可以更清楚地展现类之间的关系,易于维护。这也是面向对象设计的优点。同时,继承可以提高代码的复用性,提高项目的可扩展性等等。注意:一定不能为了复用代码而随便继承。代码实现是基于设计的,没有“is a”关系的类,就不要建立继承关系。


java中继承用extends关键字。子类中可以直接使用父类的成员变量和成员方法,同时也可以重新定义成员变量和成员方法(覆盖)。在子类中,关键字this指向当前的类,关键字super指向父类。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person{
String name;
void speak(){
System.out.println("my name:" + name);
}
}
class Student extends Person{
String university;
//覆盖父类的成员方法
void speak(){
super.speak();
System.out.println("my university:"+ this.university);
}
void study(){
System.out.println("I should study hard, because it's my duty");
}
}
class Worker extends Person{
void work(){
System.out.println("I should work hard, because it's my duty");
}
}

成员变量的继承

子类可以继承父类非私有的成员变量,也可以在子类中覆盖父类的成员变量。在子类中,使用this调用子类的成员变量,使用super调用父类的成员变量。

成员方法的继承

子类可以继承父类非私有的成员方法。同时子类可以覆盖父类的成员方法(如上例Student类中是speak方法)。继承中的这个特性让项目更易于扩展。具体来说,就是项目中同样的方法需要有新的实现时(比如:来电显示中以前只显示号码和姓名,现在要添加归属地和头像的显示;播放视频时以前只是单纯地播放,现在要在视频右上角显示当前时间等等。需求变更、添加功能是很常见的)。若项目已经上线,或进入后期阶段,则直接修改原来的父类或子类的代码,将是一个灾难。因为可能很多地方都用到了这个类,要改的话,可能会改一堆……
所以,最好的办法就是使用继承,并在新类中覆盖需要重写的方法。在重写该方法时,可以用super调用父类的方法,在此基础上扩展新功能。以后只需要new新类,就可以调用新的功能。
当然,若要覆盖父类的成员方法,需要以下两个条件:

  1. 子类中,该方法的访问权限要大于或等于父类的方法。我们知道java中访问权限有4种:public、private、protected和default。上个例子中没有任何修饰符,就是default权限。default的访问级别介于public和private之间。若父类中是default权限,子类只能是default、protected或public,不能是private。注意:如果是父类中有个A方法是private修饰的,那么,在子类中就算有一个方法与A方法签名完全相同,也不算方法覆盖,而是子类中的新方法。因为在子类,该方法本身不可见。这里,覆盖的说法没意义。
  2. 静态方法只能覆盖静态方法,不能覆盖非静态方法。因为静态和非静态的加载时机不一样,存储位置也不一样。静态的成员变量和成员方法都是类加载的时候就加载进入内存,而非静态是创建对象时加载。

继承中的构造方法

构造方法和类同名,当然没有继承的说法。但是,在继承中,构造方法的调用时机需要说明一下。
子类的所有构造方法,都会先执行父类的默认构造方法,然后才执行子类构造方法中的其它语句。也就是说,在子类的构造方法中都会隐式地插入这样一句代码:super();(子类构造方法中先用this()调用其它构造方法的除外,因为在this()中已经调用了父类的构造方法)
因为在子类其实包含父类的成员变量,但这些成员变量的初始化是在父类中定义的。因此,初始化子类时一定要先调用父类的构造方法初始化这些成员变量。
当然,子类的构造方法中也可以手动指定调用父类的哪个构造方法,但一定要先调用父类的构造方法。

final关键字

说了这么多,继承的确有很多好处,但是继承却破坏了对象的封装性。在java中,为了解决这个问题,可以使用final关键字。
final关键字可以修饰类、变量、方法等,只要被final修饰,就不能再被修改。具体来说,final修饰的类不能被继承;final修饰的方法不能被覆盖;final修饰的变量就是一个常量了。ps:可以在类中定义全局静态常量,用类名就可以直接访问,如下:public static final double PI = 3.14159;

好了,先记这些吧,以后有新的见解再补上。

若文章中有问题,欢迎指正。