目录
封装,继承,多态是面向对象的三大特性,今天我们学习继承和多态~
1. 继承
继承: 继承指一个类(子类)可以继承另一个类(父类)的属性和者方法,子类也能在保持原有的特性的基础上进行拓展.继承是面向对象编程中使代码可以复用的一种方式,继承完成的工作是:共性的抽取和代码的复用.
举个例子: 猫猫和狗狗都是动物,猫猫有年龄,毛发颜色等属性,有吃饭,睡觉等行为;这些属性和行为狗狗也有,因此可以将这些共性抽取出来,实现代码复用,减少代码的冗余
1.1 继承语法
继承使用extends关键字,语法如下:
修饰符 class 子类 extends 父类 {
//......
}
例:将姓名,年龄,毛发,吃饭,睡觉抽取成一个Animal类,让Dog和Cat继承Animal类(在继承父类的基础上,子类也有自己特有的行为和属性)
父类Animal:
class Animal {
public String name;//年龄
public int age;//年龄
public String color;//毛发
public void eat() {
System.out.println(this.name + "正在吃饭");
}
public void sleep() {
System.out.println(this.name + "正在睡觉");
}
}
子类Dog
class Dog extends Animal {
public void bark() {
System.out.println(this.name + "正在汪汪叫");
}
}
子类Cat
class Cat extends Animal {
public void meow() {
System.out.println(this.name + "正在喵喵叫");
}
}
1.2 成员访问的规则
子类访问成员时,不管是成员变量还是成员方法,都遵循以下规则:
- 如果子类当中有,优先访问子类中的
- 如果子类中没有,再去访问父类中的
- 如果子类中没有,父类中也没有,则会报错
例:
class A {
int a = 10;
int b = 20;
public void Func() {
System.out.println("AFunc");
}
}
class B extends A {
int a = 1;
public void printNum() {
System.out.println(a);
}
public void Func() {
System.out.println("BFunc");
}
}
public class Main {
public static void main(String[] args) {
B b = new B();
b.printNum();
b.Func();
}
}
运行结果:
问题: 如果子类和父类中有重名的变量,但是我就是想要得到父类中的那个变量,该怎么办?
答: 使用super关键字
1.3 super关键字
使用super关键字可以实现在子类中访问父类成员
class A {
int a = 10;
int b = 20;
}
class B extends A {
int a = 1;
public void printNum() {
System.out.println(super.a);
}
}
public class Main {
public static void main(String[] args) {
B b = new B();
b.printNum();
}
}
此时输出的是父类中的a(10)
注意事项:
super只能在非静态的方法中使用
1.4 再谈构造方法
我们再复习一下构造方法的特点:
构造方法的名字与类名相同,没有返回值,可以构成重载,创建对象时由编译器调用,可以根据自己的需求提供参数不同的构造方法
先有父再有子,所以当发生继承时,实例化一个子类对象时,先要调用父类的构造方法,帮助父类的成员的初始化,再调用子类自己的构造方法,将子类新的成员初始化
注意事项:
- 如果父类中没有自定义构造方法,子类中第一行会有隐藏的super();这是编译器自动提供的,super()表示调用父类构造方法
- 子类的构造方法中,super()只能出现在第一行
- super()不能和this()同时出现
1.5 super和this的区别和联系
不说废话,直接上结论!!!
相同点:
- this和super都不能在静态方法中使用
- 如果在在构造方法中使用,都必须在第一行
不同点:
- this是当前对象的引用,而super是从父类继承下来的那部分的引用
- 在非静态成员方法中使用时:this用来访问本类的方法和属性;super用来访问父类继承的方法和属性
- 在构造方法中使用时:this(…)用于调用本类中的其他构造方法(构成重载的);super(…)用于调用父类的构造方法
- 发生继承时,子类构造方法中一定会存在super(…)的调用,不管用户是否手动添加构造方法,而this(…)
如果用户不写则没有
1.6 关于代码块的执行
在发生继承的情况下,实例化一个子类对象,代码执行顺序是怎么样的?看下面代码
class A {
public A() {
System.out.println("父类构造方法");
}
{
System.out.println("父类实例代码块");
}
static {
System.out.println("父类静态代码块");
}
class B extends A {
public B() {
System.out.println("子类构造方法");
}
{
System.out.println("子类实例代码块");
}
static {
System.out.println("子类静态代码块");
}
}
public class Main {
public static void main(String[] args) {
B b1 = new B();
System.out.println("===========");
B b2 =new B();
}
}
运行结果:
结论:
- 1.执行顺序:父类静态>>子类静态>>父类实例>>父类构造>>子类实例>>子类构造
- 2.静态代码块只执行一次,实例代码块和构造方法中的代码每实例一个对象就执行一次
- 3.父类总是比子类先执行
1.7 protected关键字
protected关键字修饰的成员,在不同包中的类也能使用,前提是发生继承
1.8 继承方式
在java中,继承方式有3种:
- 单继承:一个类继承另一个类
- 多层继承:A类被B继承,B类又被C继承
- 不同类继承同一个类
java中不支持多继承(一个类继承多个类)
1.9 final关键字
final关键字课用来修饰变量,方法,类作用如下:
- final修饰变量:表示该变量是常量
- final修饰方法:表示该方法不能被重写
- final修饰类:表示该类不能被继承
2. 多态
多态:当完成同一种行为,不同对象会实现出不同的状态,这就是多态
2.1多态实现的条件
- 发生多态的前提是发生了继承
- 子类必须要对父类中的方法进行重写
- 通过父类的引用来调用重写的方法
2.2 重写
重写(override),重写是指子类对父类的方法重新编写.重写可以根据需求实现父类的方法
方法重写的规则
- 重写的方法,方法名,参数列表,返回值要和父类的完全一样
- 被重写的方法的返回值可以不同,但是必须构成父子关系
- 重写方法时,子类的方法访问权限必须大于等于父类(访问权限:public>protected>默认>private)
- 被static,private,final修饰的方法不能被重写
- 构造方法不能被重写
- 使用@override注解,能帮助我们一些错误(比如方法名不同,会提提示报错)
重载和重写的区别:
2.3 向上转型和向下转型
2.3.1 向上转型
向上转型就是用父类引用来接收创建的子类对象
Father f1 = new Son();
向上转型可以通过1.直接赋值2.参数传递3.返回值传递三种方法实现
注意事项:
- 不能调用子类特有的方法,如上述代码中f1不能调用Son类特有的方法
例:
class Animal {
}
class Dog extends Animal {
}
定义父类Animal和子类Dog,
public static void main(String[] args) {
//向上转型:
Animal animal1 = new Dog();
}
2.3.2 向下转型
和向上转型相反,向下转型是用子类来接受创建的父类对象
例:
public static void main(String[] args) {
Dog dog = new Dog();
Animal animal = new Animal();
dog = (Dog) animal;//此时需要将animal强制转换为Dog类型
}
2.3.3 instanceof
向下转型使用较少,而且不安全,如果转换失败会抛出异常,为了提高安全性,java引入了instanceof,表达式如果为true表示安全转换
使用案例:
public static void main(String[] args) {
Animal animal = new Dog();
Dog dog = new Dog();
if (animal instanceof Dog) {
//animal instanceof Dog表示:如果animal属于Dog类
dog = (Dog) animal;
}
}
2.4 静态绑定和动态绑定
静态绑定: 也叫早绑定,在编译时,根据传递的参数就能确定调用了哪个方法.例如调用重载的方法时,根据参数的个数/类型就知道调用的是哪个重载的方法
动态绑定: 也叫晚绑定,动态绑定是在运行时才能确定调用了哪个方法
2.5 理解多态
看代码:
class Animal {
public void eat() {
System.out.println("吃饭");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("吃狗粮");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("吃猫粮");
}
}
public class Main {
public static void eat(Animal animal) {
animal.eat();
}
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
eat(dog);
eat(cat);
}
}
运行结果:
可以发现,调用同一个eat方法,Dog类型的对象调用和Cat类型的对象调用,输出的结果不同,这就是多态!!!你get到了吗?
2.6 多态的优点和缺点
多态的优点:
- 多态能够减少代码量
- 可扩展能力强
多态的缺点:
- 属性没有多态性:当父类和子类有同名的成员属性时,父类只能引用父类自己的
- 构造方法没有多态性
- 尽量不要在构造方法中调用重写的方法!!!