通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。 总的来说:同一件事情,发生在不同对象身上,就会产生不同的结果。
在java
中要实现多态,必须要满足如下几个条件,缺一不可:
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。(动态绑定)
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型()
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
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 + "吃饭");
}
}
class Cat extends Animal {
public Cat(String name, int age) {
super(name, age);
}
public void action(){
System.out.println(name + "小猫抓老鼠");
}
}
public class TestAnimal1 {
public static void main(String[] args) {
//向上转型 父类引用animal 引用了子类的对象
Cat cat1 = new Cat("小猫1", 2);
Animal animal = cat1;
//简洁写法(推荐)
Animal animal2 = new Cat("小猫2", 2);
animal2.eat();
//不可以调用action方法,因为此时通过父类引用只能调用父类的属性和方法了
animal2.action();//(报错)
//解决办法: 再向下转型到具体要调用的子类.
Cat cat = (Cat) animal2;
cat.action();
}
}
重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写, **返回值和形参都不能改变。即外壳不变,核心重写!**重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
重写满足重写的条件
1.方法名相同
2.方法的参数列表相同(参数个数, 参数类型, 参数顺序)
3.方法的返回值相同.(父类的返回值和子类的返回值是父子类关系也可以.这种情况叫协变类型)
4.static
的方法不能被重写
5.private
修饰的方法不能被重写
6.子类的修饰访问符,需要大于等于父类的访问修饰符
重写和重载的区别
即:方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。典型代表方法重写
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 + "吃饭");
}
}
class Cat extends Animal {
public Cat(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(name + "吃鱼~~~");
}
}
class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(name + "吃骨头~~~");
}
}
public class TestAnimal {
// 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法
// 等程序运行起来后,形参a引用的具体对象确定后,才知道调用那个方法
// 注意:此处的形参类型必须时父类类型才可以
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);
}
}
运行结果
小猫吃鱼~~~
小狗吃骨头~~~
注意: 避免在构造方法中调用重写的方法
class B {
public B() {
//避免在构造方法中调用重写的方法,会发生不易察觉的错误
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
ntln("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.