Java的多态(Polymorphism)是面向对象编程中的一种特性,它允许不同的对象能够以统一的方式进行访问和操作。
它允许一个类的实例在运行时表现出多种形态。
Java多态的实现主要依赖于两个基本概念:继承和方法重写。
在Java中,一个子类可以继承父类的方法,并且可以通过重写这些方法来实现自己的特定行为。当我们创建一个对象时,它可以指向自身的类类型,也可以指向任何父类或者实现的接口类型。这就是Java多态性的核心思想:一个对象具有多种外观和行为,这取决于使用它的上下文。
Java多态的好处是可以提高代码的重用性和可维护性,使得代码更加灵活和可扩展。例如,一个方法可以接受一个基类(如Object)作为参数,然后在运行时传入一个子类的实例,这样就可以处理各种不同的对象类型,而无需重写这个方法。
文章目录
- 在Java中,多态性的实现方式有两种:
- 表现出多态度的过程:
- 简要说明:`父类类型` `类型名称` `=` `子类对象`
- 详细:
- Java中使用多态的时机通常是在面向对象编程的场景中,特别是在以下情况下比较合适:
- Java要实现多态性,需要以下几个要点:
- 0.其他:
- 1. **继承关系(创建父类和子类):
- 2. **方法重写(Override):**
- 3. **父类引用和子类对象:**
- 4. **运行时绑定(Runtime Polymorphism):**
- 5. **使用抽象类或接口(可选):**
- 多态调用成员的特点
- instanceof 匹配判断成功后将对象强制转换为指定的类
- 方式壹
- 代码1:仅仅判断是不是某类
- 代码2: 匹配后强转
- 方拾贰:条件判断中同时进行类型检查和类型转换
- 举例代码:
父类引用引用一个具有子类身份信息的对象时,就创建了一个多态关系的实现效果。
在Java中,多态性的实现方式有两种:
Parent obj = new Child();
obj.show(); // 调用子类的方法
-
编译时多态:也称为静态多态,通过方法的重载实现。方法重载是指在一个类中定义多个同名的方法,但参数列表不同,编译器会根据传入的参数类型来选择调用哪个方法。
-
运行时多态:也称为动态多态,通过方法的重写实现。方法重写是指子类重新定义了父类中具有相同名称和参数列表的方法,子类对象在运行时会根据具体的对象类型来调用对应的方法。
运行时多态性的实现需要满足以下条件:
- 存在继承关系:子类继承自父类。
- 存在方法重写:子类重写了父类的方法。
- 父类引用指向子类对象:通过父类的引用指向子类的对象,在运行时会根据实际对象的类型调用相应的方法。
表现出多态度的过程:
多态性的优点在于它增加了代码的灵活性和可扩展性,使得代码更易于维护和重用。
简要说明:父类类型
类型名称
=
子类对象
详细:
-
父类和子类关系的建立:
class Parent { void show() { System.out.println("Parent's show method"); } } class Child extends Parent { void show() { System.out.println("Child's show method"); } }
-
使用多态的情况:
Parent obj1 = new Parent(); Parent obj2 = new Child(); obj1.show(); // 调用父类的方法 obj2.show(); // 调用子类的方法
-
方法重写(Override):
@Override void show() { System.out.println("Child's overridden show method"); }
-
父类引用调用子类方法:
Parent obj = new Child(); obj.show(); // 调用子类的方法
Java中使用多态的时机通常是在面向对象编程的场景中,特别是在以下情况下比较合适:
-
继承关系:当有多个类存在继承关系时,可以通过多态来处理它们的对象,从而实现更灵活的代码结构和简化代码逻辑。
-
方法的参数:如果一个方法需要接收不同类型的对象,并对它们进行相似的操作,使用多态可以使代码更加通用和易于扩展。
-
方法的返回值:当方法返回类型是基类或接口类型,但实际返回的是派生类的实例,可以使用多态来实现。
-
集合的使用:在使用集合(如List、Set、Map)时,可以使用多态来存储不同类型的对象,并且通过统一的接口来进行操作。
总的来说,多态使代码更加灵活、可维护性更高,能够提高代码的重用性和扩展性。
Java要实现多态性,需要以下几个要点:
0.其他:
- 向上转型:将子类对象赋值给父类引用。这样做可以实现多态性,但只能调用父类中定义的方法。
- 向下转型:将父类引用转换为子类对象。需要使用强制类型转换符进行转换。但需要注意的是,向下转型可能会引发ClassCastException异常,因此在转型之前需要进行类型检查(使用instanceof)。
1. **继承关系(创建父类和子类):
** 首先,你需要设计一个父类(基类),其中包含一些通用属性和方法。然后,创建一个或多个子类(派生类),它们继承了父类的属性和方法,并可以添加自己的特定属性和方法。
class Shape {
void draw() {
System.out.println("Drawing a shape");
}
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Drawing a circle");
}
}
class Rectangle extends Shape {
@Override
void draw() {
System.out.println("Drawing a rectangle");
}
}
2. 方法重写(Override):
在子类中,你可以使用@Override
注解来重写父类的方法。确保重写的方法具有相同的方法签名(名称、参数列表和返回类型),以便在多态性中起作用。
3. 父类引用和子类对象:
’使用父类的引用变量来引用子类的对象,这是实现多态性的关键。这允许你在编码时保持灵活性,可以在运行时决定调用哪个类的方法。
Shape myShape1 = new Circle();//`Shape` 是父类引用,而 `new Circle();` 是子类对象。
Shape myShape2 = new Rectangle();
myShape1.draw(); // 输出:Drawing a circle
myShape2.draw(); // 输出:Drawing a rectangle
多态性允许我们使用父类的引用来引用子类的对象,这是因为子类对象是可以赋值给父类引用的。这有助于实现代码的灵活性和可扩展性。
在Shape myShape1 = new Circle();
这行代码中,我们创建了一个 Circle
类的对象,并将其赋值给 Shape
类型的引用变量 myShape1
。由于 Circle
是 Shape
的子类,这种赋值是合法的。这意味着虽然myShape1
的类型是 Shape
,但它指向的实际上是一个 Circle
类型的对象。
通过这种方式,我们可以使用通用的 Shape 引用来引用不同的具体形状对象,从而实现了多态性和继承的概念。这种设计模式允许我们在代码中以一种更抽象的方式操作对象,同时保留了具体对象的特定功能。
4. 运行时绑定(Runtime Polymorphism):
当通过父类引用调用方法时,Java会在运行时动态地确定实际调用的方法,这称为运行时绑定。这意味着你可以在不同的上下文中使用相同的方法调用,但实际执行的是不同子类的方法。
5. 使用抽象类或接口(可选):
如果你希望定义一组共享的方法签名,可以使用抽象类或接口。抽象类可以提供部分实现,而接口则强制实现所有方法。这使得多态性更加灵活,可以在不同类之间共享更多的行为。
interface Sound {
void makeSound();
}
class Dog implements Sound {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
class Cat implements Sound {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
综上所述,实现多态性需要创建继承关系、重写方法、使用父类引用和子类对象、运行时绑定以及可能的抽象类或接口。这些概念共同使多态性成为面向对象编程中的强大特性,提高了代码的灵活性和可维护性。
多态调用成员的特点
变量调用:编译看左边,运行也看左边
方法调用:编译看左边,运行看右边
- 变量调用成员时,编译时会根据变量的声明类型(左边)来确定可访问的成员,运行时也会使用变量的实际类型(左边)来确定实际执行的代码。
编译时看左边: 在编译阶段,编译器会检查父类中是否存在被调用的成员变量。如果存在,编译通过;如果不存在,编译失败。
运行时也看左边: 在运行阶段,程序实际获取的是左边父类中的成员变量的值,而不考虑实际对象的类型。
- 方法调用时,编译时会根据变量的声明类型(左边)来确定可调用的方法,但在运行时会根据实际对象的类型(右边)来决定实际执行的方法。
编译时看左边: 在编译阶段,编译器会检查变量的声明类型(左边)来确定可调用的方法。如果左边的类型没有声明被调用的方法,编译会报错,即使实际对象具有相应的方法。
运行时看右边: 在运行阶段,方法调用会根据实际对象的类型(右边)来决定实际执行的方法。即使使用父类的引用,程序也会根据实际对象的类型来调用相应的方法,这被称为动态方法分派。
这 种行为允许你在运行时通过替换对象实例来实现不同的行为,这是面向对象编程中多态性的一个关键概念。
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal1 = new Dog(); // 编译看左边,运行也看左边
Animal myAnimal2 = new Cat(); // 编译看左边,运行也看左边
myAnimal1.makeSound(); // 编译看左边,运行看右边 ("Dog barks")
myAnimal2.makeSound(); // 编译看左边,运行看右边 ("Cat meows")
}
}
instanceof 匹配判断成功后将对象强制转换为指定的类
使用 instanceof 进行类型检查和强制类型转换时,应该确保类型转换是安全的,即要确保对象的实际类型与你尝试转换的类型是相符的。如果类型不匹配,会在运行时抛出 ClassCastException 异常。
方式壹
if (object instanceof MyClass) {
MyClass myObject = (MyClass) object;
// 此时可以使用 myObject 来访问 MyClass 特有的方法和属性
}
在这里,object 是要检查的对象,MyClass 是要检查的类名。如果 object 是 MyClass 类的实例或者其派生类的实例,条件就会为真,代码块会被执行。`
代码1:仅仅判断是不是某类
class ParentClass {
// Contents of the parent class
}
class ChildClass1 extends ParentClass {
// Contents of the first child class
}
class ChildClass2 extends ParentClass {
// Contents of the second child class
}
public class Main {
public static void main(String[] args) {
ParentClass obj = new ParentClass(); // This can be an instance of any class
if (obj instanceof ChildClass1) {
System.out.println("obj is an instance of ChildClass1");
} else if (obj instanceof ChildClass2) {
System.out.println("obj is an instance of ChildClass2");
} else {
System.out.println("obj is not an instance of ChildClass1 or ChildClass2");
}
}
}
在这个示例中,我们首先创建了一个 ParentClass 的实例,然后使用 instanceof 运算符检查它是否是 ChildClass1 或 ChildClass2 的实例。如果都不是,就输出相应的提示信息。
请注意,这个示例中的类和实例是一种简化,实际情况可能会更加复杂。不过,基本的 instanceof 使用方式和逻辑与这个示例类似。
代码2: 匹配后强转
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
public void makeSound() {
System.out.println("Dog barks");
}
public void fetch() {
System.out.println("Dog fetches the ball");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Dog();
if (myAnimal instanceof Dog) {
Dog myDog = (Dog) myAnimal;
myDog.fetch(); // 可以调用 Dog 特有的方法
}
myAnimal.makeSound(); // 输出:Dog barks
}
}
方拾贰:条件判断中同时进行类型检查和类型转换
if (object instanceof MyClass myObject) {
// 此时可以使用 myObject 来访问 MyClass 特有的方法和属性
}
举例代码:
class MyClass {
public void myMethod() {
System.out.println("MyClass method");
}
}
public class Main {
public static void main(String[] args) {
Object object = new MyClass();
if (object instanceof MyClass myObject) {
myObject.myMethod(); // 可以直接使用 myObject 调用 MyClass 的方法
}
}
}