目录
继承
Java中使用类对于现实世界中的实体进行描述,类经过实例化后产生的对象,就可以用来表示现实中的实体。在定义类的时候遇到两个类有相同属性或者相同功能的部分,就可以考虑将这两个类中相同的部分抽取出来,这就是面向对象思想中的继承,利用继承来进行共性抽取,实现代码复用。
假设定义了如下两个类,这两个类有着很多的共同属性功能:name、age、sex、eat()、sleep()
继承的概念
继承机制:是面向对象对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增添新的功能,这样产生的新的类称为派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
根据继承机制,将Cat类和Dog类中相同的部分抽取出来,实现一个新类—“Animal类”,此时Cat类和Dog类都继承了Animal类,这即为继承思想。
其中Animal类称为: 父类/基类/超类 ,Dog类和Cat类称为Animal的子类/派生类,继承之后,子类可以复用父类中的成员,子类在实现时只需要关注自己所需要添加的新成员即可。
继承的语法
Java中表示类的继承关系,需要借助extends关键字来实现,语法格式如下:
修饰符 class 子类 extends 父类{
//...
}
例如 :
//Dog类是Animal类的子类/派生类
class Dog extends Animal{
public void bark(){
System.out.println(this.name + "汪汪~");
}
}
//Cat类是Animal类的子类/派生类
class Cat extends Animal{
public void mew(){
System.out.println(this.name + "喵喵~");
}
}
//Animal类是Cat类和Dog类的父类/基类/超类
public class Animal {
public String name;
public int age;
public String sex;
public void eat(){
System.out.println(this.name + "在吃饭");
}
public void sleep(){
System.out.println(this.name + "在睡觉");
}
}
父类成员的访问
在子类方法中访问父类的成员变量
当子类中不存在与父类相同的成员变量时,可以直接在子类中访问到父类的成员变量
当所访问的成员变量子类和父类中都存在时,优先访问子类的成员变量,即子类的成员变量将父类的成员变量掩盖了。成员变量的访问遵循就近原则,自己有则优先访问自己的,自己没有则在父类中寻找。
在子类中访问父类的成员方法
通过子类对象访问父类与子类不同名的方法时,优先在子类中寻找,找到则访问,否则在父类中寻找,找到则访问,否则就会报错;
通过子类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同,根据调用方法的传递选择合适的方法访问,如果未找到合适的方法,则会报错;如果父类和子类同名方法的原型一致,则只能访问到子类的,不能通过派生类对象直接访问到父类中的方法。
例如如下代码
//Derived.java
public class Derived extends Base{
int c = 10;
int a = 5;
public void method(){
System.out.println("a = " + a + " b = " + b + " c = " + c);
}
}
//Base.java
public class Base {
int a = 10;
int b = 5;
}
//Test.java
public class Test {
public static void main(String[] args) {
Derived test = new Derived();
test.method();
}
}
输出结果
a = 5 b = 5 c = 10
根据输出结果 ,可以看出在执行method方法时,实际上输出的是子类中的a,那么我们如何可以在子类方法中访问到父类的成员变量呢?利用super关键字
super关键字
super关键字的作用是:可以在子类方法中访问到父类成员
例如:
//Derived.java
public class Derived extends Base{
int c = 10;
int a = 5;
public void method(){
//通过super访问父类成员变量
System.out.println("a = " + super.a + " b = " + b + " c = " + c);
print();
//通过super访问父类成员方法
super.print();
}
public void print(){
System.out.println("Derived类");
}
}
//Base.java
public class Base {
int a = 10;
int b = 5;
public void print(){
System.out.println("Base类");
}
}
//Test.java
public class Test {
public static void main(String[] args) {
Derived test = new Derived();
test.method();
}
}
输出结果
a = 10 b = 5 c = 10
Derived类
Base类
上述示例体现了super关键字的用法:在子类方法中,访问父类的成员方法和变量.
注意事项
1.super关键字只能在非静态方法中使用;
2.在子类方法中,访问父类的成员变量和方法.
super关键字的用法
super.data:访问父类成员变量;
super.func():访问父类成员方法;
super():调用父类的构造方法,必须放在第一行.
super和this的区别
关于this关键字的描述可以浏览这个链接:this关键字的了解
super和this关键字都可以在成员方法中用来访问成员变量和调用其他的成员函数,那它们之间有什么区别呢?
相同点:
1.只能在非静态方法中使用,用来访问非静态成员方法和字段;
2.在构造方法中调用时,必须是构造方法中的第一条语句,且this和super不能同时存在.
不同点:
1.this是对于当前对象的引用,super是对子类对象从父类继承下来部分成员的引用;
2.在非静态成员方法中,this用来访问本类的方法和属性,super用来访问从父类继承下来的方法和属性;
3.this是非静态成员方法的隐藏参数,super不是隐藏参数;
4.在构造方法中,this(...)用于调用本类构造方法,super(...)用于构造父类构造方法,两种调用不能同时在构造方法中出现;
5.构造方法中一定会存在super(...)的调用(自己不写编译器也会自动生成),但不一定会存在this(...)的调用(除非自己写this调用)
6.成员方法中直接访问本类的成员时,编译之后会将this还原,本类中所有的非静态成员都是通过this来访问的;在子类中通过super访问父类成员,编译之后在字节码层面super实际上是不存在的.
继承关系的执行顺序与继承方式
继承关系上的执行顺序
1.父类静态代码块的执行顺序优先于子类静态代码块,且是最早执行的;
2.之后执行父类实例代码块和构造方法;
3.之后执行子类的实例代码块和构造方法;
4.第二次实例化子类对象时,父类和子类的静态代码块不会再次执行,它只会执行一次.
Java中所支持的继承方式
Java中只支持单继承、多层继承、不同类继承同一个类,不支持多继承.
多态
通过一个引用,调用方法之后,可以有多种表现形式,这种思想就称为多态。
多态的实现条件
1.必须在继承体系下使用;
2.子类必须要对父类的方法进行重写;
3. 通过父类的引用调用重写的方法.
多态的体现:当传递不同的对象时,会调用对应类中的方法.
重写
重写(Override):重写是子类对父类非静态、非private修饰、非final修饰、非构造方法等的实现过程进行重新编写,返回值和形参都不能改变.
重写的注意事项:
1.被final修饰的方法叫做密封方法,代表当前方法是不能被重写的;
2.被static修饰的方法,不可以被重写;
3.被private修饰的方法,不能被重写;
4.子类的访问修饰权限要大于等于父类的访问修饰权限.
重写和重载的区别
方法的重载是一个类的多态表现,方法的重写是子类与父类的一种多态性表现.
静态绑定和动态绑定
静态绑定:编译时,根据用户所传递的实参类型来确定具体调用哪个方法,代表类型:函数重载;
动态绑定:在编译时,不能确定具体调用哪个方法,需要等到程序运行时才能够确定.
向上转型和向下转型
向上转型
创建一个子类对象,将其当成父类对象来使用.
语法格式:
父类对象 对象名 = new 子类类型();
Animal dog = new Dog();
向上转型发生的时机:直接赋值、方法传参、方法返回.
向上转型的优点:让代码实现更加简单灵活;
向上转型的缺点:不能调用到子类特有的方法.
向下转型
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转型。
多态的优缺点
优点:
1.能够降低代码的“圈复杂度”,避免使用大量的if-else语句;
2.可扩展能力强.
缺点:
代码的运行效率降低.
多态的代码示例如下:
class Car {
public void carOfName(){}
}
class Bmw extends Car {
@Override
public void carOfName(){
System.out.println("BMW: 宝马");
}
}
class Audi extends Car {
@Override
public void carOfName() {
super.carOfName();
System.out.println("Audi: 奥迪");
}
}
class Benz extends Car{
@Override
public void carOfName() {
super.carOfName();
System.out.println("Benz: 奔驰");
}
}
public class TestDemo {
public static void printCar(Car car){
car.carOfName();
}
public static void main(String[] args) {
printCar(new Bmw());
printCar(new Audi());
printCar(new Benz());
}
}
运行结果:
BMW: 宝马
Audi: 奥迪
Benz: 奔驰