继承
继承实际上是存在于面向对象程序中的两个类之间的一种关系。
当一个类拥有另一个类的所有数据和操作时,就称这两个类之间具有继承关系。
被继承的类称为父类或超类,继承了父类或超类的所有数据和操作的类称为子类。
继承是子类自动共享父类的数据和方法的机制,它由类的派生功能体现。继承发生在父类和子类之间,子类继承父类的属性,同时可以定义自己特有的属性。
继承具有传递性。子类继承父类,及父类的父类,层层继承,直至根类。
继承支持软件的可重用性,且有助于软件的可扩充性。
继承的优点:
• 实现软件可重用性;
• 使程序结构清晰;
• 可降低编码和维护的工作量,提高软件的可维护性。
得到新类的方法
• 直接定义一个类(自定义类)
• 由已有的类派生出新类(继承)
class CPoint {
protected int x,y;
public void setPoint(int a,int b) {
x=a; y=b;
}
public int getX() {return x;}
public int getY() {return y;}
}
class CLine extends CPoint {
protected int endX,endY;
public void setEndPoint(int x1,int y1) {endX=x1; endY=y1; }
public int getEndX() {return endX;}
public int getEndY() {return endY;}
public double length() {
return Math.sqrt( (endX-x)*(endX-x)+(endY-y)*(endY-y) ); }
}
public class LineDemo2 {
public static void main( String args[] ) {
CLine l1=new CLine();
l1.setEndPoint(7,9);
l1.setPoint(2,3);
System.out.println("x="+l1.getX());
System.out.println("y="+l1.getY());
System.out.println("endX="+l1.getEndX());
System.out.println("endY="+l1.getEndY());
System.out.println("length="+l1.length());
}
}
类的继承的层次结构
类的继承的层次结构如下图所示。
系统类Object是根类,其它任何类都由Object直接或间接继承而来。
如下的类的声明,没有使用关键字extends,但实际上它等价于第二句
public class MyApp { … }
public class MyApp extends Object { … }
Object类里常用的方法有:
Class getClass()
Boolean equals(Object obj)
String toString() ……
由于每个类都是由Object类直接或间接派生出来的,所以在每个类中都自动具有这些方法。
对继承的基本理解:
• 子类不能继承父类中的private私有成员,即不能使用父类的私有成员(包括成员变量和成员方法)。
• 对继承的理解应该扩展到整个父类的分支(层层继承)。
• 在Java中,子类只能有一个父类。
成员变量的隐藏
在类的继承关系中,当子类的成员变量与父类的成员变量同名时,子类的成员变量会隐藏父类的成员变量。
隐藏:指子类中定义了父类中的同名变量。
在隐藏的情况下,
• 如果在子类中直接使用变量名,则是使用子类定义的变量。
• 要想使用从父类继承而来的成员变量,就要使用super引用来明确指出。
成员方法的覆盖
当子类的方法与父类的方法具有相同的方法名、返回值、以及相同的参数个数和参数类型时,子类的方法会覆盖父类的方法。
当子类对象调用被覆盖的方法时,会按照该方法在子类中定义的代码执行;该方法在父类中定义的代码被隐藏。
要想调用父类的方法 ,使用super引用。
class Point {// 定义类Point
protected int x,y;
Point(int a, int b) {setPoint(a,b);}
public void setPoint(int a,int b) { x=a; y=b;}
public int getX() {return x;}
public int getY() {return y;}
}
class Line extends Point {// 定义类Line,它是Point类的子类
protected int x,y,endX,endY;
Line(int x1,int y1,int x2,int y2){
super(30,50);setLine(x1,y1,x2,y2);
}
public void setLine(int x1,int y1,int x2,int y2) {
x=x1; y=y1; endX=x2; endY=y2;
}
public int getX() {return x;}
public int getY() {return y;}
public int getEndX() {return endX;}
public int getEndY() {return endY;}
public double length() {
return Math.sqrt( (endX-x)*(endX-x)+(endY-y)*(endY-y) );
}
public void show() {
System.out.println("子类成员变量x="+x);
System.out.println("父类类成员变量x="+super.x);
System.out.println("子类成员变量y="+getY());
System.out.println("父类类成员变量y="+super.getY());
}
}
public class LineDemo{
public static void main( String args[] ){
Line l1=new Line(3,5,8,9);
System.out.println("线段长度="+l1.length());
l1.show();
}
}
this引用和super引用
this引用:
• this实际代表的是当前类或对象本身。
• 在一个类中,this表示对当前类的引用。
• 在使用类的成员时,隐含着this引用,尽管可以不明确地写出。
• 当一个类被实例化为一个对象时,this就是对象名的另一种标识。
super引用:
• super代表父类。
• 如果子类变量隐藏了父类的变量,使用不加任何引用的变量一定是子类的变量。
• 如果使用父类的变量,就必须加上super引用。
• 同样道理,如果有方法覆盖的发生,调用父类的方法时也必须加上super引用。
继承中的构造方法
构造方法在继承关系中的处理:
-在创建子类对象的时候,
1)Java会先调用父类的构造方法,为从父类继承而来的成员做初始化操作,
2)然后再调用子类自己的构造方法,初始化子类自己定义的成员。
-可以在子类的构造方法中显式地调用父类的构造方法。
-也可以不在子类的构造方法中显式地调用父类的构造方法。这时系统会自动调用父类没有参数的构造方法。
例4-3:this引用和super引用。
class Point {
protected int x,y;
Point(int a, int b) { setPoint(a,b); }
public void setPoint(int a, int b) { x=a; y=b; }
}
class Line extends Point {
protected int x,y;
Line(int a, int b) {
super(a,b); //显式调用父类的构造方法
setLine(a,b);
}
public void setLine(int x, int y) {
this.x=x+x; // 形参x是局部变量,this.x是成员变量
this.y=y+y;
}
public double length() {
// 使用父类的点(x,y)作为起点,子类的点(x,y)作为终点的线段
// 计算该线段的长度
int x1=super.x, y1=super.y, x2=this.x, y2=this.y;
return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
public String toString() {
return “直线端点:[”+super.x+“,”+super.y+“][”+x+“,”+y+“]直线长度:”+this.length();
}
}
public class Capp6 {
public static void main(String args[]) {
Line line=new Line(50,50); //先调用父类的构造方法,再调用子类自己的构造方法
System.out.println("\n"+line.toString());
System.out.println("\n"+line.length());
}
}
多态性的概念
类的多态性提供了类中的方法设计的灵活性和执行的多样性。
通过多态,就能“对通用情况进行编程”,而不是“对特定情况进行编程”。
多态的特别之处是使程序能够处理类层次中共享同一父类的对象,就好像它们都是父类的对象一样。
多态指“一种定义,多种实现”。
具体来说,Java中的多态就是指一个程序中同名的不同方法共存的情况。
分为编译时多态和运行时多态两类。
“覆盖”与“重载”的比较:
方法的重载:
• 是在相同类内,定义名称相同,但参数个数或类型不同的方法。
• 编译器会根据实参的个数和类型,确定调用哪个方法。
• 实现Java的编译时多态。
方法的覆盖:
• 是在子类当中,定义名称、参数个数与类型均与父类相同的方法,用以改写父类方法的功能。
• Java运行时系统根据调用该方法的实例的类型,来决定选择哪个方法调用。
• 实现Java的运行时多态。
抽象类
抽象类只作为继承层次中的超类或父类使用。因为抽象类是不完整的,所以不能实例化抽象类的对象,子类必须声明出“缺少的部分”。程序中实例化抽象类会产生编译错误。
抽象类的基本目的是提供合适的父类,其他类可以继承它的公共部分。
抽象方法是只有方法头而没有方法体的方法;抽象方法为所在抽象类的子类定义一个方法的接口标准,方法的具体实现在子类中定义。
抽象类和抽象方法:
• 用 abstract 关键字修饰的方法称为抽象方法。抽象方法不提供具体实现。
• 用 abstract 关键字修饰的类称为抽象类。
• 抽象类不一定包含抽象方法,但是包含抽象方法的类一定要被声明为抽象类。
抽象类的作用:
• 专门当作父类,用来派生出其它子类。
抽象类定义的格式如下:
abstract class 类名称 {
声明数据成员;
定义一般的方法;
abstract returnType methodName([paramList]); // 抽象方法
}
注意两点:
• 抽象类不能被实例化,它只能作为父类,用来派生出子类。在子类当中,实现抽象类中定义的抽象方法。
• 抽象类不一定必须包含抽象方法,但是,包含抽象方法的类必须声明为抽象类。
抽象类的定义
使用abstract关键字定义的类被称为抽象类,语法如下:
[权限修饰符] abstract class 类名
{
类体
}
抽象方法的定义
• 使用abstract关键字定义的方法被称为抽象方法,语法如下:
[权限修饰符] abstract 方法值返回类型 方法名(参数列表);
• 抽象方法以分号结尾;
• 抽象方法没有方法体;
• 承载抽象方法的抽象类必须被继承,实际上,抽象方法除了被继承外没有任何意义;
• 但是,构造方法不能定义为抽象方法。
如何利用抽象方法
系统架构师使用抽象的方法设计出一个程序的整体架构,然后再交给程序员来实现这些方法。
抽象类与抽象方法的使用原则
- 在抽象类中,可以包含抽象方法,也可以不包含抽象方法,但是包含了抽象方法的类必须被定义为抽象类。
2. 抽象类不能直接被实例化,即使抽象类中没有声明抽象方法,也不能被实例化。
3. 抽象类被继承后,子类需要重写抽象类中所有的抽象方法。
4. 如果继承抽象类的子类也被声明为抽象类,则可以不用重写父类中所有的抽象方法。什么是接口?
接口(interface)是抽象类的延伸,可以将它看成纯粹的抽象类。
使用接口继承关系
声明接口
使用Interface关键字声明一个接口。 ```java [修饰符] interface 接口名 [extends 父接口名列表] { [public] [static] [final] 变量; // 声明变量 [public] [abstract] 方法 ;// 抽象方法 }
• 修饰符:可选,用于指定接口的访问权限,可选值为public,如果省略则使用默认的访问权限。<br />• 接口名:必选参数,用于指定接口的名称,接口名必须是合法的Java标识符。一般,首字母大写。<br />• extends 父接口名列表:可选参数,用于指定要定义的接口继承于哪个父接口。当使用extends关键字时,父接口名为必选参数。<br />• 方法:接口中的方法都是抽象方法,没有方法体。<br />• 变量:接口中定义的任何变量都是static和final的,定义时就要初始化,实现接口的子类不能对变量重新赋值。
<a name="XFQY0"></a>
## 实现接口
一个类实现一个接口使用implements关键字声明。
```java
[修饰符] class 类名 implements 接口名 { …… }
多重继承
Java中类不允许多重继承,但是使用接口可以实现多重继承,因为一个类可以有多个接口。
可以将所有需要实现的接口放在implements关键字后,并使用英文逗号“,”隔开。
[修饰符] class 类名 implements 接口名1,接口2,接口3,……接口n { …… }
使用多重继承时,可能出现变量或方法名冲突。如果出现变量冲突,需要明确指定变量的接口,即通过“接口名.变量”实现。如果出现方法冲突,则只要实现一个方法即可。
接口继承接口
interface intf1 {}
interface intf2 extends intf1 {}
类实现接口
class 类名 implements 接口1,接口2,…,接口n{}
区分抽象类与接口
抽象类是对根源的抽象,例如女人和男人可以抽象为人类。
接口是对动作的抽象,例如狗和猫都可以吃东西。
抽象类与接口的相同点
- 都包含抽象方法。
2. 都不能被实例化。
3. 都是引用数据类型。抽象类与接口的区别
- 子类只能继承一个抽象类,但可以实现任意多个接口。
2. 接口中的方法都是抽象方法,抽象类可以有非抽象方法。
3. 抽象类中的成员变量可以是各种类型,接口中的成员变量只能是静态常量。
4. 抽象类中可以有静态方法和静态代码块等,接口中不可以。
5. 接口没有构造方法,抽象类可以有构造方法。
比较项 | 抽象类 | 接口 |
---|---|---|
方法 | 可以有非抽象方法 | 所有方法都是抽象方法 |
属性 | 属性中可以有非静态常量 | 所有的属性都是静态常量 |
构造方法 | 有构造方法 | 没有构造方法 |
继承 | 一个类只能继承一个父类 | 一个类可以同时实现多个接口 |
被继承 | 一个类只能继承一个父类 | 一个接口可以同时继承多个接口 |