Java SE多态
CAMELLIA!!! note 目录
多态
一、类型转换
1.1、基本数据类型转换
在Java中,基本数据类型之间可以进行自动类型转换和强制类型转换。
自动类型转换(隐式类型转换):当一个表达式中包含不同类型的数据时,系统会自动将其中的低精度数据类型转换为高精度数据类型,以保证精度不丢失。
自动类型转换的规则如下:
- byte、short、char类型会自动提升为int类型。
- 如果表达式中包含了不同类型的数据,系统会自动将低精度的类型转换为高精度的类型。
示例:
1 2
| int x = 10; double y = x;
|
强制类型转换(显式类型转换):在某些情况下,需要将一个数据类型转换为另一个数据类型,这时就需要使用强制类型转换。强制类型转换可以通过将目标类型的数据类型放在被转换的数据类型前面的括号中实现。
强制类型转换的规则如下:
- 数据类型范围大的可以强制转换为数据类型范围小的,但可能会导致精度丢失或溢出。
- 强制类型转换可能会造成数据丢失或溢出,因此需要谨慎使用。
示例:
1 2
| double a = 10.5; int b = (int) a;
|
1.2、Java中的向上转型和向下转型
在Java中,向上转型(Upcasting)和向下转型(Downcasting)是面向对象编程中常用的概念,用于处理类之间的继承关系。
向上转型(Upcasting):向上转型是指将子类对象赋值给父类引用变量的过程。这样做是安全的,因为子类对象拥有父类的所有属性和方法。向上转型可以实现多态性,使得代码更加灵活。例如:
1 2 3 4
| class Animal { } class Dog extends Animal { }
Animal animal = new Dog();
|
向下转型(Downcasting):向下转型是指将父类引用变量转换为子类对象的过程。这种转型可能会导致异常,因为编译器只知道变量的编译时类型,而不知道实际的运行时类型。因此,在进行向下转型时,需要使用强制类型转换,并且需要确保转换是安全的,即实际对象是子类的实例。否则,会抛出 ClassCastException
异常。例如:
1 2
| Animal animal = new Dog(); Dog dog = (Dog) animal;
|
需要注意的是,向上转型是自动的,不需要显式地指定类型转换,而向下转型需要显式地使用强制类型转换,并且可能会导致异常,因此需要谨慎使用。
二、多态
- 父类型引用指向子类型对象。
Animal a=new Cat(); a.move();
- 程序分为编译阶段和运行阶段
- 编译阶段:编译器只知道a是Animal类型,因此去Animal类找move()方法,找到后,绑定成功,编译通过。这个过程通常被称为静态绑定。
- 运行阶段:运行时和JVM堆内存中真实的Java对象有关,所以运行时会自动调用真实对象move()方法。这个过程通常被称为动态绑定。
- 多态是指:多种形态,编译阶段一种形态,运行阶段另一种形态。
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.camellia.oop19;
public class Animal {
public void move(){ System.out.println("动物在移动"); }
public void eat(){ System.out.println("正在吃东西"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.camellia.oop19;
public class Bird extends Animal{
@Override public void move() { System.out.println("鸟儿在飞翔"); }
public void sing(){ System.out.println("鸟儿在歌唱!"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.camellia.oop19;
public class Cat extends Animal{
@Override public void move() { System.out.println("猫在走猫步"); }
public void catchMouse(){ System.out.println("猫在抓老鼠"); } }
|
2.1、发生在向上转型时的多态
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
| package com.camellia.oop19;
public class Test01 { public static void main(String[] args) {
Animal a2 = new Cat();
a2.move();
a2.catchMouse(); } }
|
2.2、发生在向下转型时的多态
注意: 向下转型使用不当容易发生类型转换异常:ClassCastExcetion。
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
| package com.camellia.oop19;
public class Test01 { public static void main(String[] args) {
Animal a2 = new Cat();
Cat c2 = (Cat) a2; c2.catchMouse();
Animal x = new Cat();
Bird y = (Bird) x;
} }
|
2.3、用instanceof运算符避免向下转型时的风险
instanceof
运算符用于检查对象是否是特定类的实例,或者是否是特定类的子类的实例。它的语法如下:
1
| object instanceof ClassName
|
其中 object
是要检查的对象,ClassName
是要检查的类名。
instanceof
运算符的返回结果是一个布尔值,如果 object
是 ClassName
的一个实例或子类的实例,则返回 true
;否则返回 false
。
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
| package com.camellia.oop19;
public class Test01 { public static void main(String[] args) {
Animal x = new Cat();
Bird y = (Bird) x;
System.out.println(x instanceof Bird);
if (x instanceof Bird) { System.out.println("========================="); Bird y = (Bird) x; } } }
|
2.3、多态有什么作用?
通过 instanceof 运算符可以在程序运行时动态确定对象的类型,根据不同的情况做出相应的处理。
这使得程序具有更强的适应性和灵活性,可以根据实际情况采取不同的行动,而不需要在编码时就确定对象的具体类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.camellia.oop19;
public class Test01 { public static void main(String[] args) { Animal a = new Bird(); a.eat();
if (a instanceof Cat) { Cat cat = (Cat) a; cat.catchMouse(); } else if (a instanceof Bird) { Bird bird = (Bird) a; bird.sing(); } } }
|
三、开闭原则(OCP)————使用多态实现OCP原则
开放-封闭原则(Open-Closed Principle,OCP):
软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着当需要改变系统的行为时,应该尽量通过扩展而不是修改现有的代码来实现。
3.1、 未使用多态
1 2 3 4 5 6 7 8 9 10
| package com.camellia.oop20;
public class Cat { public void eat(){ System.out.println("猫吃鱼"); } }
|
1 2 3 4 5 6 7
| package com.camellia.oop20;
public class Dog { public void eat(){ System.out.println("狗狗啃骨头!"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.camellia.oop20;
public class Master { public void feed(Cat c) { c.eat(); }
public void feed(Dog d){ d.eat(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.camellia.oop20;
public class Test { public static void main(String[] args) { Cat c = new Cat(); Dog d = new Dog(); Master master = new Master(); master.feed(c); master.feed(d); } }
|
3.2、使用多态
1 2 3 4 5 6 7 8 9
| package com.camellia.oop21;
public abstract class Pet {
public abstract void eat();
}
|
1 2 3 4 5 6 7 8 9
| package com.camellia.oop21;
public class Cat extends Pet{
@Override public void eat(){ System.out.println("猫吃鱼"); } }
|
1 2 3 4 5 6 7 8 9
| package com.camellia.oop21;
public class Dog extends Pet{
@Override public void eat(){ System.out.println("狗狗在啃骨头"); } }
|
1 2 3 4 5 6 7 8
| package com.camellia.oop21;
public class Master {
public void feed(Pet p){ p.eat(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.camellia.oop21;
public class Test { public static void main(String[] args) { Cat c = new Cat(); Dog d = new Dog();
Master master = new Master(); master.feed(c); master.feed(d); } }
|
3.3、静态方法不存在方法覆盖
1 2 3 4 5 6 7 8 9
| package com.camellia.oop22;
public class Animal {
public static void test(){ System.out.println("Animal's test method invoke"); }
}
|
1 2 3 4 5 6 7 8 9
| package com.camellia.oop22;
public class Cat extends Animal{
public static void test(){ System.out.println("Cat's test method invoke"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.camellia.oop22;
public class Test { public static void main(String[] args) { Animal.test(); Cat.test(); Animal a = new Cat(); a.test(); } }
|
3.4、实例变量没有多态
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
| package com.camellia.oop22;
public class Test2 { public static void main(String[] args) { A a = new B(); System.out.println(a.name);
B b = new B(); System.out.println(b.name); } }
class A { String name = "张三"; }
class B extends A { String name = "李四"; }
|