继承(Extends)、重写(Override)、重载(Overload)
1. 继承(Extends)
1) 继承的概念
继承是 Java 面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的属性和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
类的继承格式:
class Parent {
}
class Sub extends Parent {
}
使用关键字 extends ,子类 Sub 继承了父类 Parent。
2) 继承的特点
(1) 子类继承了父类所有非 private 的属性和方法。
(2) 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
(3) 子类可以用自己的方式重写父类的方法。
(4) Java 的继承是单继承,但是可以多重继承:
a) 单继承就是一个子类只能继承一个父类,即子类只能有一个直接父类,extends 关键字后面只能有一个类名;
b) 多重继承,列如:类A的有自己的子类 B,B可以自己的子类C,C可以 ...
(5) 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系紧密,代码独立性差)。
3) IS-A 关系
IS-A:一个对象是另一个对象的一个分类。
下面是使用关键字 extends 实现继承。
class Human {
}
class Male extends Human {
}
class Boy extends Male {
}
基于上面的例子,以下说法是正确的:
Human 类是 Male 类的父类, Male 类是 Human 类的子类。
Boy 类既是 Male 类的子类又是 Human 类的子类。
分析以上示例中的 IS-A 关系,如下:
Man IS-A Human
Boy IS-A Male
因此: Boy IS-A Human
通过使用 instanceof 关键字,能够确定 Male,Boy 是 Human 类的实例, 也就是 IS-A Human。
1 public static void main(String args[]) { 2 Male m = new Male(); 3 Boy b = new Boy(); 4 System.out.println(m instanceof Human); 5 System.out.println(b instanceof Human); 6 }
输出:
true
true
4)HAS-A 关系
HAS-A 代表类和它的成员之间的从属关系。
class Group {}
public class Boy extends Male {
private Group group;
}
Boy 类和 Group 类是 HAS-A 关系( Boy 有一个 group),这样就不用将 Group 类的全部代码粘贴到 Boy 类中了,并且 Group 类也可以重复使用到其它类中。
5) 关键字
继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。
(1) extends关键字
在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,extends 只能继承一个类。
(2) implements关键字
使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)
(3) instanceof 关键字
使用 instanceof 关键字判断一个对象是否为一个类(或接口、抽象类、父类)的实例,语法格式如下所示。
boolean result = obj instanceof Class
其中,obj 是一个对象,Class 表示一个类或接口。obj 是 class 类(或接口)的实例或者子类实例时,结果 result 返回 true,否则返回 false。
下面 instanceof 关键字的几种用法:
(1) 声明一个 class 类的对象,判断 obj 是否为 class 类的实例对象(很普遍的一种用法);
(2) 声明一个 class 接口实现类的对象 obj,判断 obj 是否为 class 接口实现类的实例对象;
(3) obj 是 class 类的直接或间接子类
(4) super 关键字
子类不能继承父类的构造方法,如果要调用父类的构造方法,可以使用 super 关键字。super 可以用来访问父类的构造方法、普通方法和属性。
super 关键字的功能:
(1) 在子类的构造方法中显式的调用父类构造方法
(2) 访问父类的成员方法和变量。
(5) this 关键字
当一个对象创建后,Java虚拟机(JVM)就会给这个对象分配一个引用自身的指针,这个指针的名字就是 this。
this 的作用:
(1) 使用 this 来区分当前对象,比如在构造函数和对象实例的方法中,实例变量和局部变量名字冲突时,可以给实例变量加上 this. 前缀;
(2) 在构造器中使用 this 来调用对象本身的其他构造器;
(3) 返回类的引用。如在代码中,可以使用return this来返回某个类的引用。此时,这个this关键字就代表类的名称;
不能在 static 方法中使用 this 关键字。
Static 方法是类方法,先于任何的实例(对象)存在。即Static方法在类加载时就已经存在了,但是对象是在创建时才在内存中生成,而 this 指代的是当前对象。
在方法中定义使用的this关键字,它的值是当前对象的引用.也就是说你只能用它来调用属于当前对象的方法或者使用this处理方法中成员变量和局部变量重名的情况.
而且 this 和 super 都无法出现在 static 修饰的方法中,static 修饰的方法是属于类的,该方法的调用者可能是一个类,而不是对象。
(6) final 关键字
final 关键字声明类可以把类定义为不能继承,用于修饰方法,该方法不能被子类重写:
声明类:
final class 类名 {//类体}
声明方法:
修饰符(public/private/default/protected) final 返回值类型 方法名() {//方法体}
注: 实例变量也可以被定义为 final,被定义为 final 的变量不能被修改。被声明为 final 类的方法自动地声明为 final,但是实例变量并不是 final。
6) 构造函数
构造函数(或称构造方法,或称构造器),是一种特殊的方法。主要用来在创建对象时初始化对象,即为对象成员变量赋初始值,总与 new 运算符一起使用在创建对象的语句中。
构造函数的作用和特点:
(1) 构造函数的主要作用是完成对象的初始化工作;
(2) 构造函数的名称必须与类名相同,包括大小写;
(3) 构造函数没有返回值,也不能用 void 修饰;
(4) 一个类可以定义多个构造方法,如果在定义类时没有定义构造方法,则编译系统会自动插入一个无参数的默认构造器,这个构造器不执行任何代码;
(5) 构造方法可以重载,以参数的个数、类型或顺序的不同;
2. 重写(Override)
在子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的实现不同,以实现不同于父类的功能,这种方式被称为方法重写(Override),又称为方法覆盖。
当父类中的方法无法满足子类需求或子类具有特有功能的时候,需要方法重写。
下面程序演示了重写:
1 class Male extends Human { 2 3 public void display() { 4 } 5 6 public static void test() { 7 } 8 } 9 10 class Boy extends Male { 11 12 @Override 13 public void display() { 14 } 15 16 // 父类的 static 方法不能被重写,可以被再次声明和定义 17 //@Override 18 public static void test() { 19 } 20 }
1)@Override
是伪代码,表示方法重写, 使用 @Override 标签的好处:
1) 作为注释,帮助自己检查是否正确的复写了父类中已有的方法;
2) 便于别人理解代码;
3) 编译器可以给你验证 @Override 下面的方法名是否是你父类中所有的,如果没有则报错;
2) 重写的规则
(1) 参数列表与被重写方法的参数列表必须完全相同;
(2) 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同);
(3) 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected;
(4) 父类的成员方法只能被它的子类重写;
(5) 声明为 final 的方法不能被重写;
(6) 声明为 static 的方法不能被重写,但是能够被再次声明和定义,父类和子类有各自同名的 static 方法;
(7) 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法;
(8) 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法;
(9) 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以;
(10) 构造方法不能被重写;
3. 重载(Overload)
重载(Overload) 是在一个类里面,方法名字相同,而参数不同,返回类型可以相同也可以不同。最常用的地方就是构造函数的重载。
下面程序演示了重写:
1 public class Male extends Human { 2 3 @Override 4 public void display() { 5 } 6 7 // 重载本类的 display() 8 public void display(String str) { 9 } 10 }
1) 重载的规则
(1) 被重载的方法必须改变参数列表(参数个数或类型不一样);
(2) 被重载的方法可以改变返回类型;
(3) 被重载的方法可以改变访问修饰符;
(4) 被重载的方法可以声明新的或更广的检查异常;
(5) 方法能够在同一个类中或者在一个子类中被重载;
(6) 无法以返回值类型作为重载函数的区分标准;
2) 重载与重写的区别
项目 重载方法 重写方法
参数列表 必须修改 不能修改
返回类型 可以修改 不能修改
异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
访问 可以修改 不能做更严格的限制(可以降低限制)
实例:
1 public class App { 2 public static void main( String[] args ) { 3 4 Male m = new Male("Male name"); 5 Boy b = new Boy("Boy name"); 6 System.out.println("Male is an instance of Human: " + (m instanceof Human)); 7 System.out.println("Boy is an instance of Human: " +(b instanceof Human)); 8 9 b.display(); 10 b.display("Boy: Overload display(String str)"); 11 b.displayParent(); 12 13 b.test(); // Boy 类的 static test() 14 Male m2 = b; 15 m2.test(); // Male 类的 static test() 16 } 17 } 18 19 // final 类不能继承, 即最终类 20 final class Group { 21 22 } 23 24 class Human { 25 private String name; 26 27 // 构造函数 28 public Human(String name) { 29 // this 指向自己的引用 30 this.name = name; 31 } 32 33 public String getName() { 34 return name; 35 } 36 37 public static void test() { 38 // 不能在 static 方法中使用 this 关键字 39 //System.out.println("Human: test() -> name = " + this.name); 40 } 41 } 42 43 class Male extends Human { 44 45 // 构造函数 46 public Male(String name) { 47 super(name); 48 } 49 50 public String getName() { 51 return super.getName(); 52 } 53 54 // final 方法不能被子类重写(Override) 55 final public void testFinalFunction() { 56 System.out.println("Male: test final function"); 57 } 58 59 public void display() { 60 System.out.println("Male: display()"); 61 } 62 63 // static test() 方法不能被子类重写(Override) 64 public static void test() { 65 System.out.println("Male: static test()"); 66 } 67 } 68 69 class Boy extends Male { 70 // final 类 Group 不能被继承, 71 // 一般作为实例变量或类变量使用 72 private Group group; 73 74 // 构造函数 75 public Boy(String name) { 76 // 使用 super 调用父类构造方法 77 super(name); 78 } 79 80 public String getName() { 81 // 访问父类的成员方法 82 return super.getName(); 83 } 84 85 /* 86 // 不能重写(Override)父类的 final 方法 87 @Override 88 public void testFinalFunction() { 89 System.out.println("Boy: test final function"); 90 } 91 */ 92 93 // 重写(Override) 94 @Override 95 public void display() { 96 System.out.println("Boy: Override display()"); 97 } 98 99 // 重载(Overload) 100 public void display(String str) { 101 System.out.println(str); 102 } 103 104 // 使用 super 关键字可以调用父类的被重写方法 105 public void displayParent() { 106 super.display(); 107 } 108 109 // 父类的 static test() 方法不能被重写(Override),可以被再次声明和定义 110 //@Override 111 public static void test() { 112 System.out.println("Boy: static test()"); 113 } 114 }
输出:
Male is an instance of Human: true
Boy is an instance of Human: true
Boy: Override display()
Boy: Overload display(String str)
Male: display()
Boy: static test()
Male: static test()