继承

继承实际上是存在于面向对象程序中的两个类之间的一种关系。
当一个类拥有另一个类的所有数据和操作时,就称这两个类之间具有继承关系。
被继承的类称为父类或超类,继承了父类或超类的所有数据和操作的类称为子类。
继承是子类自动共享父类的数据和方法的机制,它由类的派生功能体现。继承发生在父类和子类之间,子类继承父类的属性,同时可以定义自己特有的属性。
继承具有传递性。子类继承父类,及父类的父类,层层继承,直至根类。
继承支持软件的可重用性,且有助于软件的可扩充性。
继承的优点:
• 实现软件可重用性;
• 使程序结构清晰;
• 可降低编码和维护的工作量,提高软件的可维护性。

得到新类的方法

• 直接定义一个类(自定义类)
• 由已有的类派生出新类(继承)

  1. class CPoint {
  2. protected int x,y;
  3. public void setPoint(int a,int b) {
  4. x=a; y=b;
  5. }
  6. public int getX() {return x;}
  7. public int getY() {return y;}
  8. }
  9. class CLine extends CPoint {
  10. protected int endX,endY;
  11. public void setEndPoint(int x1,int y1) {endX=x1; endY=y1; }
  12. public int getEndX() {return endX;}
  13. public int getEndY() {return endY;}
  14. public double length() {
  15. return Math.sqrt( (endX-x)*(endX-x)+(endY-y)*(endY-y) ); }
  16. }
  17. public class LineDemo2 {
  18. public static void main( String args[] ) {
  19. CLine l1=new CLine();
  20. l1.setEndPoint(7,9);
  21. l1.setPoint(2,3);
  22. System.out.println("x="+l1.getX());
  23. System.out.println("y="+l1.getY());
  24. System.out.println("endX="+l1.getEndX());
  25. System.out.println("endY="+l1.getEndY());
  26. System.out.println("length="+l1.length());
  27. }
  28. }

image.png

类的继承的层次结构

类的继承的层次结构如下图所示。
image.png
系统类Object是根类,其它任何类都由Object直接或间接继承而来。
如下的类的声明,没有使用关键字extends,但实际上它等价于第二句

  1. public class MyApp { }
  2. public class MyApp extends Object { }

Object类里常用的方法有:
Class getClass()
Boolean equals(Object obj)
String toString() ……
由于每个类都是由Object类直接或间接派生出来的,所以在每个类中都自动具有这些方法。

对继承的基本理解:
• 子类不能继承父类中的private私有成员,即不能使用父类的私有成员(包括成员变量和成员方法)。
• 对继承的理解应该扩展到整个父类的分支(层层继承)。
• 在Java中,子类只能有一个父类。

成员变量的隐藏

在类的继承关系中,当子类的成员变量与父类的成员变量同名时,子类的成员变量会隐藏父类的成员变量。
隐藏:指子类中定义了父类中的同名变量。
在隐藏的情况下,
• 如果在子类中直接使用变量名,则是使用子类定义的变量。
• 要想使用从父类继承而来的成员变量,就要使用super引用来明确指出。

成员方法的覆盖

当子类的方法与父类的方法具有相同的方法名、返回值、以及相同的参数个数和参数类型时,子类的方法会覆盖父类的方法。
当子类对象调用被覆盖的方法时,会按照该方法在子类中定义的代码执行;该方法在父类中定义的代码被隐藏。
要想调用父类的方法 ,使用super引用。

  1. class Point {// 定义类Point
  2. protected int x,y;
  3. Point(int a, int b) {setPoint(a,b);}
  4. public void setPoint(int a,int b) { x=a; y=b;}
  5. public int getX() {return x;}
  6. public int getY() {return y;}
  7. }
  8. class Line extends Point {// 定义类Line,它是Point类的子类
  9. protected int x,y,endX,endY;
  10. Line(int x1,int y1,int x2,int y2){
  11. super(30,50);setLine(x1,y1,x2,y2);
  12. }
  13. public void setLine(int x1,int y1,int x2,int y2) {
  14. x=x1; y=y1; endX=x2; endY=y2;
  15. }
  16. public int getX() {return x;}
  17. public int getY() {return y;}
  18. public int getEndX() {return endX;}
  19. public int getEndY() {return endY;}
  20. public double length() {
  21. return Math.sqrt( (endX-x)*(endX-x)+(endY-y)*(endY-y) );
  22. }
  23. public void show() {
  24. System.out.println("子类成员变量x="+x);
  25. System.out.println("父类类成员变量x="+super.x);
  26. System.out.println("子类成员变量y="+getY());
  27. System.out.println("父类类成员变量y="+super.getY());
  28. }
  29. }
  30. public class LineDemo{
  31. public static void main( String args[] ){
  32. Line l1=new Line(3,5,8,9);
  33. System.out.println("线段长度="+l1.length());
  34. l1.show();
  35. }
  36. }

image.png

this引用和super引用

this引用:
• this实际代表的是当前类或对象本身。
• 在一个类中,this表示对当前类的引用。
• 在使用类的成员时,隐含着this引用,尽管可以不明确地写出。
• 当一个类被实例化为一个对象时,this就是对象名的另一种标识。
super引用:
• super代表父类。
• 如果子类变量隐藏了父类的变量,使用不加任何引用的变量一定是子类的变量。
• 如果使用父类的变量,就必须加上super引用。
• 同样道理,如果有方法覆盖的发生,调用父类的方法时也必须加上super引用。

继承中的构造方法

构造方法在继承关系中的处理:
-在创建子类对象的时候,
1)Java会先调用父类的构造方法,为从父类继承而来的成员做初始化操作,
2)然后再调用子类自己的构造方法,初始化子类自己定义的成员。
-可以在子类的构造方法中显式地调用父类的构造方法。
-也可以不在子类的构造方法中显式地调用父类的构造方法。这时系统会自动调用父类没有参数的构造方法。
例4-3:this引用和super引用。

  1. class Point {
  2. protected int x,y;
  3. Point(int a, int b) { setPoint(a,b); }
  4. public void setPoint(int a, int b) { x=a; y=b; }
  5. }
  6. class Line extends Point {
  7. protected int x,y;
  8. Line(int a, int b) {
  9. super(a,b); //显式调用父类的构造方法
  10. setLine(a,b);
  11. }
  12. public void setLine(int x, int y) {
  13. this.x=x+x; // 形参x是局部变量,this.x是成员变量
  14. this.y=y+y;
  15. }
  16. public double length() {
  17. // 使用父类的点(x,y)作为起点,子类的点(x,y)作为终点的线段
  18. // 计算该线段的长度
  19. int x1=super.x, y1=super.y, x2=this.x, y2=this.y;
  20. return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
  21. }
  22. public String toString() {
  23. return “直线端点:[”+super.x+“,”+super.y+“][”+x+“,”+y+“]直线长度:”+this.length();
  24. }
  25. }
  26. public class Capp6 {
  27. public static void main(String args[]) {
  28. Line line=new Line(50,50); //先调用父类的构造方法,再调用子类自己的构造方法
  29. System.out.println("\n"+line.toString());
  30. System.out.println("\n"+line.length());
  31. }
  32. }

多态性的概念

类的多态性提供了类中的方法设计的灵活性和执行的多样性。
通过多态,就能“对通用情况进行编程”,而不是“对特定情况进行编程”。
多态的特别之处是使程序能够处理类层次中共享同一父类的对象,就好像它们都是父类的对象一样。
多态指“一种定义,多种实现”。
具体来说,Java中的多态就是指一个程序中同名的不同方法共存的情况。
分为编译时多态和运行时多态两类。
“覆盖”与“重载”的比较:
方法的重载:
• 是在相同类内,定义名称相同,但参数个数或类型不同的方法。
• 编译器会根据实参的个数和类型,确定调用哪个方法。
• 实现Java的编译时多态。
方法的覆盖:
• 是在子类当中,定义名称、参数个数与类型均与父类相同的方法,用以改写父类方法的功能。
• Java运行时系统根据调用该方法的实例的类型,来决定选择哪个方法调用。
• 实现Java的运行时多态。

抽象类

抽象类只作为继承层次中的超类或父类使用。因为抽象类是不完整的,所以不能实例化抽象类的对象,子类必须声明出“缺少的部分”。程序中实例化抽象类会产生编译错误。
抽象类的基本目的是提供合适的父类,其他类可以继承它的公共部分。
抽象方法是只有方法头而没有方法体的方法;抽象方法为所在抽象类的子类定义一个方法的接口标准,方法的具体实现在子类中定义。
抽象类和抽象方法:
• 用 abstract 关键字修饰的方法称为抽象方法。抽象方法不提供具体实现。
• 用 abstract 关键字修饰的类称为抽象类。
• 抽象类不一定包含抽象方法,但是包含抽象方法的类一定要被声明为抽象类。
抽象类的作用:
• 专门当作父类,用来派生出其它子类。
抽象类定义的格式如下:

  1. abstract class 类名称 {
  2. 声明数据成员;
  3. 定义一般的方法;
  4. abstract returnType methodName([paramList]); // 抽象方法
  5. }

注意两点:
• 抽象类不能被实例化,它只能作为父类,用来派生出子类。在子类当中,实现抽象类中定义的抽象方法。
• 抽象类不一定必须包含抽象方法,但是,包含抽象方法的类必须声明为抽象类。

抽象类的定义

使用abstract关键字定义的类被称为抽象类,语法如下:

  1. [权限修饰符] abstract class 类名
  2. {
  3. 类体
  4. }

抽象类不能产生对象实例。

抽象方法的定义

• 使用abstract关键字定义的方法被称为抽象方法,语法如下:

  1. [权限修饰符] abstract 方法值返回类型 方法名(参数列表);

• 抽象方法以分号结尾;
• 抽象方法没有方法体;
• 承载抽象方法的抽象类必须被继承,实际上,抽象方法除了被继承外没有任何意义;
• 但是,构造方法不能定义为抽象方法。

如何利用抽象方法

系统架构师使用抽象的方法设计出一个程序的整体架构,然后再交给程序员来实现这些方法。

抽象类与抽象方法的使用原则

  1. 在抽象类中,可以包含抽象方法,也可以不包含抽象方法,但是包含了抽象方法的类必须被定义为抽象类。
    2. 抽象类不能直接被实例化,即使抽象类中没有声明抽象方法,也不能被实例化。
    3. 抽象类被继承后,子类需要重写抽象类中所有的抽象方法。
    4. 如果继承抽象类的子类也被声明为抽象类,则可以不用重写父类中所有的抽象方法。

    什么是接口?

    接口(interface)是抽象类的延伸,可以将它看成纯粹的抽象类。
    使用接口继承关系
    image.png
    image.png

    声明接口

    使用Interface关键字声明一个接口。 ```java [修饰符] interface 接口名 [extends 父接口名列表] { [public] [static] [final] 变量; // 声明变量 [public] [abstract] 方法 ;// 抽象方法 }
  1. 修饰符:可选,用于指定接口的访问权限,可选值为public,如果省略则使用默认的访问权限。<br />• 接口名:必选参数,用于指定接口的名称,接口名必须是合法的Java标识符。一般,首字母大写。<br />• extends 父接口名列表:可选参数,用于指定要定义的接口继承于哪个父接口。当使用extends关键字时,父接口名为必选参数。<br />• 方法:接口中的方法都是抽象方法,没有方法体。<br />• 变量:接口中定义的任何变量都是staticfinal的,定义时就要初始化,实现接口的子类不能对变量重新赋值。
  2. <a name="XFQY0"></a>
  3. ## 实现接口
  4. 一个类实现一个接口使用implements关键字声明。
  5. ```java
  6. [修饰符] class 类名 implements 接口名 { …… }

多重继承

Java中类不允许多重继承,但是使用接口可以实现多重继承,因为一个类可以有多个接口。
可以将所有需要实现的接口放在implements关键字后,并使用英文逗号“,”隔开。

  1. [修饰符] class 类名 implements 接口名1,接口2,接口3,……接口n { …… }

使用多重继承时,可能出现变量或方法名冲突。如果出现变量冲突,需要明确指定变量的接口,即通过“接口名.变量”实现。如果出现方法冲突,则只要实现一个方法即可。
接口继承接口

  1. interface intf1 {}
  2. interface intf2 extends intf1 {}
  3. 类实现接口
  4. class 类名 implements 接口1,接口2,…,接口n{}

区分抽象类与接口

抽象类是对根源的抽象,例如女人和男人可以抽象为人类。
接口是对动作的抽象,例如狗和猫都可以吃东西。

抽象类与接口的相同点

  1. 都包含抽象方法。
    2. 都不能被实例化。
    3. 都是引用数据类型。

    抽象类与接口的区别

  2. 子类只能继承一个抽象类,但可以实现任意多个接口。
    2. 接口中的方法都是抽象方法,抽象类可以有非抽象方法。
    3. 抽象类中的成员变量可以是各种类型,接口中的成员变量只能是静态常量。
    4. 抽象类中可以有静态方法和静态代码块等,接口中不可以。
    5. 接口没有构造方法,抽象类可以有构造方法。
比较项 抽象类 接口
方法 可以有非抽象方法 所有方法都是抽象方法
属性 属性中可以有非静态常量 所有的属性都是静态常量
构造方法 有构造方法 没有构造方法
继承 一个类只能继承一个父类 一个类可以同时实现多个接口
被继承 一个类只能继承一个父类 一个接口可以同时继承多个接口