面向对象【四】(final关键字、多态)

final关键字

  • 为什么会有final:

    ​ 由于继承中有一个方法重写的现象,而有时候我们不想让子类去重写父类的方法.这对这种情况java就给我们提供了一个关键字: final

  • final概述:

    final关键字是最终的意思,可以修饰变量成员方法

final关键字修饰类,方法以及变量的特点

final修饰特点:

  • 修饰类: 被修饰类不能被继承
  • 修饰方法: 被修饰的方法不能被重写
  • 修饰变量: 被修饰的变量不能被重新赋值,因为这个量其实是一个常量

代码示例:

​ final修饰成员变量:

1
2
3
4
5
6
7
8
9
10
public class MyTest {
public static final int A=10; //公共的静态常量
public static void main(String[] args) {
//final 最终的,可以修饰类,变量,可以修饰成员方法
final int NUM=100; //自定义常量 常量的命名字母全部大写
System.out.println(A);
System.out.println(MyTest.A);
System.out.println(NUM);
}
}

上述代码输出结果为:

1
2
3
10
10
100

final修饰类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MyTest{
public static void main(String[] args) {
//final 修饰类,此类不能被继承
B b = new B();
b.show1();
}
}

final class A{

public void show1(){
System.out.println("这是父类的成员方法");
}
}

class B extends A{
public void show(){
System.out.println("这是子类的成员方法");
}
}

上述代码对类进行了final修饰,运行上述代码,会报错。

B类无法成功继承A类

final修饰成员方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MyTest {
public static void main(String[] args) {
//final 修饰方法,此方法不能被重写,子类可以原封不动的继承下去用,就是不能重写

new Zi().test();
}
}

class Fu{

public void show(){
System.out.println("这是父类的show方法");
}
public final void test(){
System.out.println("这是父类的final");
}
}

class Zi extends Fu{
@Override
public void test() {
super.show();
}
}

通过上述代码可以看出,通过final关键字修饰了成员方法,执行上述代码会报错,test()无法覆盖。

可知,被final修饰过的成员方法不能被子类重写。

final关键字修饰局部变量

  1. 基本类型,是值不能被改变
  2. 引用类型,是地址值不能被改变

多态的概述及其代码体现

概述

某一个事物,在不同时刻表现出来的不同状态。

举例:

1
2
Cat c=new Cat();
Animal a=new Cat();

猫可以是猫的类型。猫 m = new 猫();
同时猫也是动物的一种,也可以把猫称为动物。动物 d = new 猫();

多态前提

  • 要有继承关系。
  • 要有方法重写。 其实没有也是可以的,但是如果没有这个就没有意义。
  • 要有父类引用指向子类对象。父 f = new 子();

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MyTest {
public static void main(String[] args) {
Animal an=new Cat();
an.sleep();
an.eat();
an.show();
}
}

class Animal{
public void eat(){
System.out.println("吃饭");
}

public void sleep() {
System.out.println("睡觉");
}

public void show() {
System.out.println("父类中的show方法");
}
}

class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫爱吃鱼");
}

@Override
public void sleep() {
System.out.println("猫爱白天睡觉");
}
}

上述代码就是对多态的举例,运行结果为:

1
2
3
猫爱白天睡觉
猫爱吃鱼
父类中的show方法

多态中的成员访问特点

多态中的成员访问特点

  • 成员变量,编译看左边,运行看左边。
  • 构造方法,创建子类对象的时候,会访问父类的构造方法,对父类的数据进行初始化。
  • 成员方法,编译看左边,运行看右边。
  • 静态方法,编译看左边,运行看左边(静态和类相关,算不上重写,所以,访问还是左边的)。

多态的好处

多态的好处

  • 提高了代码的维护性(继承保证)
  • 提高了代码的扩展性(由多态保证)

多态的弊端

通过多态的弊端引出问题:

不能使用子类特有的功能

  • 解决方法:

    把父类的引用强制转换为子类的引用。(向下转型)

向上转型和向下转型

1.向上转型(upcasting)

​ 子—->父 自动类型提升

2.向下转型(downcasting)

​ 父—->子 强制类型转换

  • 注意:无论是向上转型还是向下转型,两个类之间必须要有继承关系

代码示例:

父类: 动物类

1
2
3
4
5
public class Animal {
public void eat(){
System.out.println("动物在吃!");
}
}

子类1:猫类

1
2
3
4
5
6
7
8
public class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼!");
}
public void move(){
System.out.println("猫走猫步");
}
}

子类2:狗类

1
2
3
4
5
public class Dog extends Animal {
public void eat(){
System.out.println("狗啃骨头");
}
}

测试类1:没转型,正常调用

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String[] args) {
Animal a1 = new Animal();
a1.eat();

Cat c1 = new Cat();
c1.eat();

Dog d1 = new Dog();
d1.eat();
}
}

测试类2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Test2 {

public static void main(String[] args) {

//向上转型又被称作:自动类型转换

//向上转型,子类转为父类
//父类型的引用指向子类型对象
//程序分两个阶段:编译阶段,运行阶段
//程序编译阶段只知道a1是一个Animal类
//程序运行的时候堆中的实际对象是Cat类型
Animal a1 = new Cat();

//程序在编译阶段a1被看做Animal类型
//所以程序在编一阶段a1引用绑定的是Animal类中的eat方法(静态绑定)
a1.eat(); //猫吃鱼!

===========================================================
//向下转型,强制类型转换
Animal a2 = new Cat();
//要执行猫类的特有方法,怎么做?
Cat c1 = (Cat) a2;
c1.move();
===========================================================
//抛出异常
//做强制类型转换是有风险的
Animal a3 = new Dog(); //向上转型,底层是Dog类
Cat c2 = (Cat) a3; //所以就不能强转成Cat类,因为之间没继承
//报错:ClassCastException

//为了避免ClassCastException,引入了instanceof
/*
* 用法:
1.instanceof作为运算符返回的结果是boolean类型
2.(引用 instanceof 类型)----->true/false

例如:(a instanceof Cat) 如果是true,则a指向堆中的对象是Cat类型
============================================================
Animal a3 = new Dog();
if(a3 instanceof Cat){
Cat c2 =(Cat) a3;
}
}
}

静态绑定和动态绑定

静态绑定:编译阶段a1引用绑定的是Animal类中的eat()方法。

动态绑定:但是运行阶段,堆中的对象实际上是Cat类型,而Cat已经重写了eat方法,所以程序在运行阶段对象的绑定的方法时Cat中的eat方法

instance可以判断类型

  • 用法:

    1.instanceof作为运算符返回的结果是boolean类型
    2.(引用 instanceof 类型)----->true/false
    
    例如:(a instanceof Cat) 如果是true,则a指向堆中的对象是Cat类型
    
-------------本文结束感谢您的阅读-------------
0%