1、继承
定义
-
继承是面向对象编程的三大特征之一,是一种
基于已有类
来创建新类的机制。由继承而得到的类称为子类(或派生类),被继承的类称为父类(或超类)。 -
Java中每个类只允许有一个父类。
语法如下:class <子类> extends <父类>
-
Object类是所有类的直接父类或间接父类。【类名首字母大写】
注意
- 根据
访问权限修饰符
的不同,子类可以继承父类中某些
成员变量和方法,提高了代码的重用性,子类也可以添加
新的成员变量和方法 :
1、如果类被final
修饰,则该类不能被继承
2、Java中已有的类(诸如Void、String、Class、Scanner、System、8种基本数据类型对应包装类等类)已经被final修饰,所以这些类不能被继承。
父类对子类构造方法的影响
- (1)如果父类拥有无参构造方法(无论隐式的还是显式的),
- (2)且子类中的构造方法又没有明确指定调用父类的哪个构造方法,
- (3)子类中没有调用该子类其它构造方法的构造方法,
-——当上述三种情况同时出现时,使用super()隐式调用
父类的无参构造方法,
package com.extend;
public class Person {
String name;
String certificatedID;
public Person() {
System.out.println("父类中无参构造方法");
}
}
----------
package com.extend;
public class Student extends Person{
String id;
String headmasterID;
public Student(String id,String headmasterID) {
//super();该构造方法隐式调用了父类无参构造方法。
System.out.println("子类构造方法");
this.id= id;
this.headmasterID=headmasterID;
}
public static void main(String[] args) {
Student student = new Student("20180907","20180709");
System.out.println(student);
}
}
//运行结果:
//父类中无参构造方法
//子类构造方法
//com.extend.Student@52e922
package com.extend;
public class Person {
String name;
String certificatedID;
public Person() {
System.out.println("父类中无参构造方法");
}
}
----------
package com.extend;
public class Student extends Person{
String id;
String headmasterID;
public Student(){
//该构造方法隐式调用了父类无参构造方法。
System.out.println("子类无参构造方法");
}
public Student(String id,String headmasterID) {
//this();调用本类无参构造函数
System.out.println("子类有参构造方法");
this.id= id;
this.headmasterID=headmasterID;
}
public static void main(String[] args) {
new Student("20180907","20180709");
System.out.println("**********************");
new Student();
}
}
----------
//运行结果
1、不加this()时
父类中无参构造方法
子类有参构造方法
**********************
父类中无参构造方法
子类无参构造方法
2、加this()时——-调用本类无参构造函数
父类中无参构造方法
子类无参构造方法
子类有参构造方法
**********************
父类中无参构造方法
子类无参构造方法
- 如果父类
没有无参构造方法
(无论隐式的还是显式的),则要求子类构造方法必须直接或间接指定调用父类
哪个构造方法并且放在有效代码第一行
,子类必须调用父类的构造方法。
package com.extend;
public class Person {
String name;
int age;
public Person(int age) {
this.age=age;
}
public Person(int age,String name) {
this.age=age;
this.name=name;
}
}
----------
package com.extend;
public class Student extends Person{
String id;
String headmasterID;
public Student(String id,int age){
super(age);//直接调用父类的第一个构造方法
this.id= id;
}
public Student(String id,String headmasterID,int age) {
this(id,age);//间接调用父类第一个参构造方法:
//先调用本类第一个构造方法,而第一个构造方法直接调用了父类第一个构造方法。
this.headmasterID=headmasterID;
}
}
父类成员变量被覆盖
- 当子类成员变量和父类成员变量
同名
时,对子类对象来讲,父类的成员变量不能被子类继承
(即子类的成员变量覆盖了父类的成员变量),此时称子类的成员变量隐藏
了父类的成员变量。 - 如果要在
子类非static修饰的代码块或方法中
使用被隐藏的父类成员变量可以通过super关键字
实现
package com.extend;
public class Person {
String name = "父类";
}
----------
package com.extend;
public class Student extends Person{
String name = "子类";
public void sayName() {
System.out.println("我的名字是"+name);//输出我的名字是子类
System.out.println("我的名字是"+super.name);//输出我的名字是父类
}
public static void main(String[] args) {
Student student = new Student();
student.sayName();
}
}
2、重写
- 定义:子类可以继承父类方法,但有时
从父类继承的方法在子类中必须进行修改以适应新类的需要,
这种对父类方法进行改写或改造的现象称为方法重写或方法覆盖。
父类方法在子类中重写使继承更加灵活。 - 调用:子类重写了父类的方法,则使用子类创建的对象调用该方法时,
调用的是重写后的方法
,即子类中的方法。
package com.extend;
public class Person {
public void eat() {
System.out.println("用筷子吃饭");
}
}
----------
package com.extend;
public class Student extends Person{
public void eat() {
System.out.println("用刀叉吃饭");
}
public static void main(String[] args) {
Student student = new Student();
student.eat();//用刀叉吃饭
}
}
- 如果要在子类
非static修饰的代码块或方法中
调用父类被重写的方法可以通过super关键字实现:
package com.extend;
public class Student extends Person{
{
super.eat();//类加载时执行一次
}
public void eat() {
System.out.println("用刀叉吃饭");
}
public static void main(String[] args) {
Student student = new Student();
student.eat();
}
}
//运行结果
用筷子吃饭
用刀叉吃饭
子类重写父类方法条件
- 注释说明:
@Override注解
可以判断当前方法是否重写了父类的某个方法,如果在方法上加上该注解没有出错,则说明重写了父类方法,否则没有重写父类方法。
条件
- 方法名和参数列表:子类重写的方法和父类被重写的方法在
方法名
和参数列表【参数个数及其对应的参数类型相同。】
方面相同; - 返回值类型:
1、如果父类被重写的方法没有返回值类型或者返回值类型为基本数据类型,则要求子类重写的方法的返回值类型和父类被重写方法的返回值类型相同;
2、如果父类被重写的方法返回值类型为引用数据类型,则要求子类重写的方法的返回值类型和父类被重写方法的返回值类型相同或是其子类。
注意
- 子类重写的方法
不能缩小
父类被重写方法的访问权限,子类重写方法的访问权限必须大于等于
父类被重写方法的访问权限; - 父类中
静态方法
可以被子类继承,但却不能被子类重写
; - 父类中
被final关键字
修饰的方法可以被子类继承,但却不能被子类重写; - 重写父类非静态方法时,重写后的方法
不能添加static修饰
;
关键字final
- final关键字可以用来修饰类、方法和变量:
- final修饰的类
不能被继承
。 - final修饰的方法
不能被重写
。 - final修饰的变量是
常量
,不允许二次赋值。
关键字super
super使用原则:
- super关键字可以
调用父类的成员变量
( super.属性)和方法(super.父类方法([参数列表]))。 - 子类
构造方法
中可以使用super关键字调用父类的构造方法:super([参数列表]); - super
不能
用于静态方法或静态代码块中。
3、多态
引入
出现多态的前提:子类重写和上转型对象
public class Mammal {
public void move() {
System.out.println("哺乳动物可以移动……");
}
}
----------
public class Whale extends Mammal{
public void move() {//重写父类中的move()方法
System.out.println("鲸鱼靠鳍移动");
}
}
----------
public class Bat extends Mammal{
public void move() {//重写父类中的move()方法
System.out.println("蝙蝠靠翼移动");
}
----------
public class Test {
public static void main(String[] args) {
Mammal mammal1 = new Whale();
mammal1.move();//鲸鱼靠鳍移动
Mammal mammal2 = new Bat();
mammal2.move();//蝙蝠靠翼移动
}
}
-
父类类型(比如Mammal)的变量(比如mammal1)指向子类创建的对象,使用该变量调用父类中一个被子类重写的方法(比如move方法),则
父类中的方法呈现出不同的行为特征,这就是多态。
-
Java引用变量有两种类型,分别是
编译时类型和运行时类型
:编译时类型由声明该变量时使用的类型决定;运行时类型由实际赋给该变量的对象。如果编译时类型和运行时类型不一致,就可能出现所谓多态。
1、运行时类型决定了编译时类型修饰的变量名所调用的方法
在程序执行过程中最终调用调用的方法。
2、编译时类型决定了其所修饰的变量名
只能调用编译时类型中定义的或其继承过来方法
-
上例分析:当把子类创建的对象直接赋给父类引用类型时,例如上例Test main方法中“Mammal mammal1 = new Whale();”, mammal1引用变量的编译时类型是Mammal,运行时类型是Whale,当程序运行时,该引用变量mammal1调用父类中被子类重写的方法时,
其方法行为表现的是子类重写该方法后的行为特征
,而不是父类方法的行为特征。 -
如果编译时类型和运行时类型不一致时未必会出现所谓多态,当父类类型的变量mammal指向子类创建的对象,如果Whale类中没有重写该变量所调用的move方法,就不会出现多态。
上转型对象
- 定义:子类实例化的对象赋值给父类声明变量,则该对象称为上转型对象,这个过程称为对象上转型,对应于数据类型转换中的自动类型转换:
public class Mammal {
public void move() {
System.out.println("哺乳动物可以移动……");
}
}
----------
public class Whale extends Mammal{
String weight = "8T";
public void breath() {
System.out.println("鲸鱼正在呼吸");
}
public void move() {
System.out.println("鲸鱼靠鳍移动");
}
public static void main(String[] args) {
Mammal mammal = new Whale();
mammal.move();//输出鲸鱼靠鳍移动
//mammal.breath();
//System.out.println(mammal.weight);
//上面两个出现错误,上转型对象`不能`操作子类新增的成员变量;不能调用子类新增的方法:
}
}
----------
Mammal mammal = new Whale();
//自动类型转换 父类类型变量(mammal)=子类创建的对象——new Whale()
等价于
Whale whale= new Whale();
Mammal mammal = whale;
// new Bat()——上转型对象——运行时类型,mammal—编译时类型
- 上转型对象
不能
操作子类新增的成员变量;不能调用子类新增的方法: - 上转对象调用父类方法,如果该方法已被子类重写,则表现子类重写后的行为特征,否则表现父类的行为特征。
- 使用上转型对象调用成员变量,无论该成员变量是否已经被子类覆盖,使用的都是父类中的成员变量:
public class Mammal {
String weight = "80T";
public void move() {
System.out.println("哺乳动物可以移动……");
}
}
----------
public class Whale extends Mammal{
String weight = "8T";
public void move() {
System.out.println("鲸鱼靠鳍移动");
}
public static void main(String[] args) {
Mammal mammal = new Whale();
mammal.move();
System.out.println(mammal.weight);//输出80T
}
}
下转型对象
- 可以将上转型对象再强制转换为创建该对象的子类类型的对象,即将上转型对象还原为子类对象,对应于数据类型转换中的强制类型转换。
Mammal mammal = new Whale();
//mammal = new Mammal();错误,不可以将【父类创建的对象】通过【强制类型转换】赋值给【子类声明的变量。】
Whale whale = (Whale)mammal;//下转型对象
- 还原后的对象又具备了子类所有属性和功能,即可以操作子类中继承或新增的成员变量,可以调用子类中继承或新增的方法。
注意
:不可以将父类创建的对象通过强制类型转换赋值给子类声明的变量。