继承也称为派生,是指一个新类可以继承其他已有类的所有成员,包括成员属性和成员方法。新类不但保留被继承过来的类的属性和方法,而且可以根据自身需要对类进行修改,添加新的成员属性和成员方法。
被继承的类称为超类(父类)。
从超类派生出来(继承超类)的新类称子类。
Java只支持单重继承,不支持多重继承。
继承的基本语法
[修饰符] class 子类名 extends 父类名{
……
}
①在Java中严格规定子类只能拥有一个直接父类;
②子类可以继承父类的所有属性和方法;
③子类可以修改从父类继承来的成员;
④子类在继承父类成员的同时还可以创建自己的新成员。
⑤在Java中,一个类的定义中即使没有extends声明,但这个类仍然是一个类的子类,这个特殊的类是java.lang.Object。
示例代码:
class Father{
int x=8;
private int y=9;
public double z=10.0;
void printS(String s){
System.out.println(s);
}
}
class Son extends Father{
String s="Hello,world!";
}
public class Test {
public static void main(String args[ ]) {
Son a=new Son();
System.out.println(a.z);
System.out.println(a.x);
a.printS(a.s);
//System.out.println(a.y);
}
}
输出:
10.0
8
Hello,world!
子类只继承父类的非私有成员。
继承:特殊类的对象拥有其一般类的全部属性与服务,称作特殊类对一般类的继承。
——李代平. 软件工程. 北京: 冶金工业出版社, 2002.8, 146
一个子类对象将总是包含一个完整的父类对象,即拥有它的全部数据成员和方法。
——Ivor Horton著, 潘晓雷, 于浚泊等译. Java 2入门经典. 北京, 机械工业出版社, 2006.9, 210
代码:
class A {
private int id;
void setId(int i) {
id = i;
}
int getId() {
return id;
}
}
class B extends A {
}
public class Test {
public static void main(String[] args) {
B b = new B();
b.setId(8);
System.out.println(b.getId());
}
}
实际上,public、protected、default、private和继承没有关系,他们对类成员的限制只是在成员的可见性上:
public允许来自任何类的访问;
protected允许来自同一包中的任何类以及该类的任何地方的任何子类的方法访问;
default允许来自同一个包中的类的访问;
private只允许来自该类内部的方法访问,不允许任何来自该类外部的访问。
关于成员变量的继承,父类的任何成员变量都是会被子类继承下去的。这些继承下来的私有成员虽对子类来说不可见,但子类仍然可以用父类的方法操作他们。这样的设计有何意义呢?
我们可以用这个方法将我们的成员保护得更好,让子类的设计者也只能通过父类指定的方法修改父类的私有成员,这样将能把类保护得更好,这对一个完整的继承体系是尤为可贵的。
如果子类中定义了一个与从父类那里继承来的成员变量完全相同的变量,则父类那里继承来的成员变量将被隐藏。——域的隐藏
代码:
class Father{
int x=8;
}
class Son extends Father{
int x=9;
void printS(){
System.out.println(x);
}
}
public class Test{
public static void main(String args[]){
Son a=new Son();
a.printS();
}
}
子类隐藏父类的变量只是使之在子类中不可见,父类的同名变量在子类中仍然占有自己独立的内存空间。
如要访问被隐藏的父类变量,可通过两条途径实现:
①使用super.变量名;
②调用从父类继承的方法操作的是从父类继承的变量。
代码:
class Father{
int x=8;
void printF(){
System.out.println(x);
}
}
class Son extends Father{
int x=9;
void printS(){
System.out.println(x);
System.out.println(super.x);
}
}
public class Test{
public static void main(String args[]){
Son a=new Son();
a.printS();
a.printF();
}
}
super关键字
super表示对某个类的超类的引用。
如子类和超类有同名的域或方法,则:
super.变量名
super.方法名()
表示引用超类的成员(如无super则表示子类中的同名成员)
就近原则
在子类中访问属性和方法时将优先查找自己定义的属性和方法。如果该成员在本类存在,则使用本类的,否则,按照继承层次的顺序到其祖先类查找。
this关键字特指本类的对象引用,使用this访问成员则首先在本类中查找,如果没有,则到父类逐层向上找。
super特指访问父类的成员,使用super首先到直接父类查找匹配成员,如果未找到,再逐层向上到祖先类查找。
终止继承
出于安全性方面的考虑,要避免子类继承超类的某个方法,可以使用“final”关键字来使继承终止。 这样使此方法不会在子类中被覆盖(即子类中不能有和此方法同名的方法)。
不能被继承的类称为最终类。
如:final class Last;
用final说明的成员方法为最终方法。
如:public final void printsuper( )
构造方法的”继承”
Java保证当创建一个类的实例时,一定会调用该类的构造方法,同时,它还保证当创建子类的实例时,父类的构造方法也同样要被调用。为了保证第二点,Java必须确保每个构造方法都会调用它父类的构造方法。
父类的构造方法构造对象的父类部分,而子类的构造方法构造子类部分。
所以,如果构造方法的第一条可执行语句没有通过this()或super()显式地调用其它构造方法,那么Java会默认插入一条方法调用super(),它调用父类中不带参数的构造方法。如果父类不包括不带参数的构造方法,那么这个默认的调用就会导致编译错误。
代码:
class Father{
int a;
public Father(int e){
a=e;
}
}
class Son extends Father{
int b;
int c;
public Son(){
super(1);
}
public Son(int m,int n,int h){
super(m);
b=n;
c=h;
}
public void printSon(){
System.out.println(a+","+b+","+c);
}
}
public class Test{
public static void main(String args[]){
Son A=new Son();
Son B=new Son(3,2,5);
A.printSon();
B.printSon();
}
}
构造方法的调用顺序
⑴按继承顺序依次调用父类的构造方法,直到到达本子类。
⑵依次执行本子类成员对象的构造方法。
⑶最后,执行本子类的构造方法。
代码:
class Person {
public Person() {
System.out.print("A");
}
public Person(String s) {
this();
System.out.print("B");
}
}
class Student extends Person {
public Student() {
System.out.print("C");
}
}
public class Who extends Person {
Student student=new Student();
public Who() {
super("ok");
System.out.print("E");
}
public static void main(String args[]) {
Who w = new Who();
}
}