1,什么是多态?
是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状
态,比如:都是吃饭,鸟吃鸟粮,狗吃狗粮,人吃饭…,
2,为什么有多态?
从一个初始者的角度去看,就是为了方便,下面会用代码来说明。
3,多态实现条件
1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法
注意:多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
public class Animal {
String name;
int age;
public Animal(String name, int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(name + "吃东西");
}
}
public class Cat extends Animal{
public Cat(String name, int age){
super(name, age);
}
@Override
public void eat(){
System.out.println(name+"吃鱼饵~~~");
}
}
public class Dog extends Animal {
public Dog(String name, int age){
super(name, age);
}
@Override
public void eat(){
System.out.println(name+"吃狗粮~~~");
}
}
public class TestAnimal {
public static void eat(Animal a){
a.eat();
}
public static void main(String[] args) {
Cat cat = new Cat("小猫",2);
Dog dog = new Dog("小狗", 1);
eat(cat);
eat(dog);
}
}
// 对于eat方法,编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法
// 等程序运行起来后,形参a引用的具体对象确定后,才知道调用那个方法
// 注意:此处的形参类型必须时父类类型才可以
如果没有使用多态,按照以前的写法就像下面代码,只能说各有千秋
class Test{
public static void main(String[] args) {
Cat cat1=new Cat("小猫",1);
cat1.eat();
Dog dog1=new Dog("小狗",2);
dog1.eat();
}
}
4,什么是重写?
重写是子类对已经存在的父类的非静态、非private修饰,非final修饰,非构造方法等的实现过程
进行重新编写。也就是说子类能够根据需要通过重写来实现父类的方法。
5,重写规则
1. 方法签名必须完全一致
- 方法名和参数列表(参数数量、类型、顺序)必须与父类方法完全相同。
-
错误示例:父类方法 void show(int a),子类写成 void show(String a)(参数类型不同,属于重载而非重写)。
- 正确示例:父类 int add(int x,int y),子类重写时保持 int add(int x, int y)。
-
2. 访问权限不能更严格
子类重写方法的访问权限必须大于等于父类方法的权限:
- 父类方法为 public → 子类只能是 public(不能是 protected 或 private)。
- 父类方法为 protected → 子类可以是 protected 或 public。
- 错误示例:父类 protected void test(),子类写成 private void test()(权限更严格)。
3. 返回值类型需满足“协变”
- JDK 8及之后:允许返回父类返回值的子类类型(协变返回)。
- 示例:父类方法 Number getValue(),子类可重写为 Integer getValue()(Integer 是 Number 的子类)。
- 在更早的编译器中,强制要求名字相同。
4. 异常声明不能扩大范围
子类重写方法抛出的异常不能比父类方法更宽泛
例如:如果父类方法被public修饰,则子类中重写该方 法就不能声明为 protected
5. 特殊修饰符的限制
- static 方法不能重写
- final 方法不能重写
- private 方法不能重写
6,动态绑定
在编译的时候还不能确定是谁调用了方法,在运行的时候才知道。如下代码,在编译的时候还不知道是猫(cat)还是狗(dog)调用了代码,到运行的时候才知道
public class TestAnimal {
public static void eat(Animal a){
a.eat();
}
public static void main(String[] args) {
Cat cat = new Cat("小猫",2);
Dog dog = new Dog("小狗", 1);
eat(cat);
eat(dog);
}
}
7,向上转型和向下转型
7.1,向上转型
实际就是创建一个子类对象,将其当成父类对象来使用
向上转型的几种方法:
1. 直接赋值
class Animal {
eat();
} // 父类
class Cat extends Animal {
eat();
} // 子类
public class Test {
public static void main(String[] args) {
// 向上转型:子类对象 → 父类引用
Animal animal = new Cat();
anima.eat();
}
}
关键说明:
- animal是 Animal类型的引用,但指向的是Cat 对象(运行时类型为Cat)。
- 此时通过 animal 只能调用父类Animal中定义的方法(除非子类重写了该方法,触发动态绑定)。
2. 方法传参
class Animal {
void eat() { System.out.println("动物吃东西"); }
}
class Dog extends Animal {
@Override
void eat() { System.out.println("小狗啃骨头"); }
}
public class Test {
// 传参:方法参数为父类类型
public static void feed(Animal animal) {
animal.eat(); // 动态绑定:调用子类重写的方法
}
public static void main(String[] args) {
Dog dog = new Dog();
feed(dog); // 传入子类对象,自动向上转型为Animal类型
}
}
3. 方法返回
public class TestAnimal {
// 3. 作返回值:返回任意子类对象
public static Animal buyAnimal(String var){
if("狗".equals(var) ){
return new Dog("狗狗",1);
}else if("猫" .equals(var)){
return new Cat("猫猫", 1);
}else{
return null;
}
}
public static void main(String[] args) {
//通过返回值引用
Animal animal = buyAnimal("狗");
animal.eat();
animal = buyAnimal("猫");
animal.eat();
}
}
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
7.2,向下转型
1.将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的 方法,此时:将父类引用再还原为子类对象即可,即向下转换。
2.向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入 了 instanceof ,如果该表达式为true,则可以安全转换。
public class TestAnimal {
public static void main(String[] args) {
Cat cat = new Cat("元宝",2);
Dog dog = new Dog("小七", 1);
// 向上转型
Animal animal = cat;
animal.eat();
animal = dog;
animal.eat();
if(animal instanceof Cat){
cat = (Cat)animal;
cat.mew();
}
if(animal instanceof Dog){
dog = (Dog)animal;
dog.bark();
}
}
}
8,多态的优点
如 下代码,虽然通过循环能够达到目的,但是如果打印的种类非常多,过程则非常低效
class Shape {
//属性….
public void draw() {
System.out.println("画图形!");
}
}
//如果还要打印其他的图形,就要重复此继承步骤,在通过main方法来注意实例化实现
class Rect extends Shape{
@Override
public void draw() {
System.out.println("♦");
}
}
class Cycle extends Shape{
@Override
public void draw() {
System.out.println("●");
}
}
public static void drawShapes() {
Rect rect = new Rect();
Cycle cycle = new Cycle();
String[] shapes = {"cycle", "rect", "cycle"};
for (String shape : shapes) {
if (shape.equals("cycle")) {
cycle.draw();
} else if (shape.equals("rect")) {
rect.draw();
}
}
}
如果利用多态,不用过多的if循环,则非常高效
public static void drawShapes() {
// 创建了一个 Shape 对象的数组.
Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),
new Rect()};
for (Shape shape : shapes) {
shape.draw();
}
}
9,多态缺陷:代码的运行效率降低。
1. 属性没有多态性
当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性
2. 构造方法没有多态性
class B {
public B() {
// do nothing
func();
}
public void func() {
System.out.println("B.func()");
}
}
class D extends B {
private int num = 1;
@Override
public void func() {
System.out.println("D.func() " + num);
}
}
public class Test {
public static void main(String[] args) {
D d = new D();
}
}
// 执行结果
D.func() 0
执行顺序:
构造 D 对象的同时, 会调用 B 的构造方法.
B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0.
网硕互联帮助中心


![[英语]-动词的时态-网硕互联帮助中心](https://www.wsisp.com/helps/wp-content/uploads/2026/02/20260221221730-699a2efa1eee1-220x150.png)

评论前必须登录!
注册