多态就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。
例如不同的动物吃的食物不一样。

不同的交通工具速度不一样

要实现多态,必须要满足如下几个条件:
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
class Animals {
public String name;
public int age;
public Animals(String name, int age ) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println(name + "吃饭");
}
}
class Cat extends Animals{
public Cat(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(name + "吃饭");
}
}
class Dog extends Animals{
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
//与父类的eat方法构成重写
System.out.println(name + "吃饭");
}
}
public class Test {
public static void eat(Animals animals) {
animals.eat();
}
public static void main(String[] args) {
Dog dog = new Dog("大黄", 6);
Cat cat = new Cat("小黑", 6);
eat(dog);
eat(cat);
}
}

在编写 eat 这个方法的时候, 参数类型为 Animal (父类), 此时在该方法内部并不知道, 也不关注当前的 animals 引用指向的是哪个类型(哪个子类)的实例。此时 animals 这个引用调用 eat方法可能会有多种不同的表现(和 animals 引用的实例相关), 这种行为就称为多态。

重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
【方法重写的规则】
【重写与重载的区别】

重写方法名和参数都一样。
class Animals {
public String name;
public int age;
public void eat() {
System.out.println(name + "吃饭");
}
public void sleep() {
System.out.println(name + "睡觉");
}
public void play() {
System.out.println(name + "做游戏");
}
}
class Cat extends Animals{
public Boolean LoveClean;//爱干净
public void catchMouse() {
System.out.println(name + "抓老鼠");
}
public void eat() {
System.out.println(name + "吃饭");//与父类的eat方法构成重写
}
}
重载方法名相同,参数不同。
class Cat{
public String name = "小黑";
public void catchMouse() {
System.out.println("抓老鼠");
}
public void catchMouse(String name) {//构成重载
}
}
静态绑定: 也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。
动态绑定: 也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型()
Aniamls animals = new Dog("大黄", 7);
向上转型的使用场景:
class Animal {
public String name;
public int age;
public Animal(String name, int age ) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println(name + "吃食物");
}
}
class Cat extends Animals {
public Cat(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(name + "吃食物");
}
}
class Dog extends Animals {
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
//与父类的eat方法构成重写
System.out.println(name + "吃食物");
}
}
public class TestAniamls {
// 方法传参:形参为父类型引用,可以接收任意子类的对象
public static void eat(Animals animals) {
animals.eat();
}
//作返回值:返回任意子类对象
public static Animals byAnimals(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) {
//直接赋值:子类对象赋值给父类对象
Animals cat = new Cat("小黑", 7);
Dog dog = new Dog("大黄", 6);
eat(cat);
eat(dog);
}
}
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
将父类引用再还原为子类对象,即向下转型。
向下转型不安全。

因为猫和狗是动物,向上转型安全;但是动物不一定是猫或者狗,所以不安全。
class Animal {
public String name;
public 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 catchMouse() {
System.out.println(name + "抓老鼠");
}
@Override
public void eat() {
System.out.println(name + "吃食物");
}
}
class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
public void brak() {
System.out.println(name + "汪汪叫");
}
@Override
public void eat() {
//与父类的eat方法构成重写
System.out.println(name + "吃食物");
}
}
public class TestAnimals {
public static void main(String[] args) {
Animal animal = new Cat("小黑", 7);
Cat cat = (Cat)animal;//向下转型
cat.catchMouse();
}
}

Dog类当中没有抓老鼠这一方法,引用的话会报错。
//部分代码
public class TestAnimals {
public static void main(String[] args) {
Animal animal = new Dog("大黄", 7);
Cat cat = (Cat)animal;//向下转型
cat.catchMouse();
}
}

如果想要避免运行后报错,可以使用 instanceof关键字。
public class TestAnimals {
public static void main(String[] args) {
Animal animal = new Dog("大黄", 7);
if (animal instanceof Cat) {
Cat cat = (Cat)animal;//向下转型
cat.catchMouse();
}
}
}
如果反生错误的话会返回一个 flase。
先来看一段代码:
一个用来画三种图形的代码
package demo1;
class Graph {
public void draw() {
System.out.println("画图形");
}
}
class Rectangle extends Graph{
@Override
public void draw() {
System.out.println("画矩形!");
}
}
class Cycle extends Graph{
@Override
public void draw() {
System.out.println("画圆!");
}
}
class Triangle extends Graph{
@Override
public void draw() {
System.out.println("画三角形!");
}
}
public class Test {
public static void main(String[] args) {
Graph graph1 = new Rectangle();
graph1.draw();
Graph graph2 = new Cycle();
graph2.draw();
Graph graph3 = new Triangle();
graph3.draw();
}
}

利用多态来进行优化:
package demo1;
class Graph {
public void draw() {
System.out.println("画图形");
}
}
class Rectangle extends Graph{
@Override
public void draw() {
System.out.println("画矩形!");
}
}
class Cycle extends Graph{
@Override
public void draw() {
System.out.println("画圆!");
}
}
class Triangle extends Graph{
@Override
public void draw() {
System.out.println("画三角形!");
}
}
public class Test {
public static void drawMap(Graph graph) {
graph.draw();
}
public static void main(String[] args) {
drawMap(new Rectangle());
drawMap(new Cycle());
drawMap(new Triangle());
}
}
同样的drawMap方法,在做着不同的事情,说明这是一个多态。
多态使代码变得简洁了。
【使用多态的好处】
在上面的代码中如果先要实现新的类直接添加就可以了。
//部分代码
class Apple extends Graph{
public void draw() {
System.out.println("画苹果!");
}
}
public class Test {
public static void drawMap(Graph graph) {
graph.draw();
}
public static void main(String[] args) {
drawMap(new Rectangle());
drawMap(new Cycle());
drawMap(new Triangle());
drawMap(new Apple());
}
}

这里可以说明使用多态后,改动也比较方便。
先来看一段代码
package demo1;
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 TestDemo {
public static void main(String[] args) {
D d = new D();
}
}
这段代码也该是会输出 D.func() 1。
但是结果却是:

说明一开始num就没有被初始化为1。
结论:
“用尽量简单的方式使对象进入可工作状态”, 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题