多态存在的三个条件
1、有继承关系
2、子类重写父类方法
3、父类引用指向子类对象
补充一下第二点,既然多态存在必须要有“子类重写父类方法”这一条件,那么以下三种类型的方法是没有办法表现出多态特性的(因为不能被重写):
1、static方法,因为被static修饰的方法是属于类的,而不是属于实例的
2、final方法,因为被final修饰的方法无法被子类重写
3、private方法和protected方法,前者是因为被private修饰的方法对子类不可见,后者是因为尽管被protected修饰的方法可以被子类见到,也可以被子类重写,但是它是无法被外部所引用的,一个不能被外部引用的方法,怎么能谈多态呢
代码当中体现多态性,其实就是一句话:父类引用指向子类对象。
格式: 父类名称 对象名 = new 子类名称(); 或者: 接口名称 对象名 = new 实现类名称();
示例:
public class Fu { public void method() { System.out.println("父类方法"); } public void methodFu() { System.out.println("父类特有方法"); } } =================== ublic class Zi extends Fu { @Override public void method() { System.out.println("子类方法"); } } ===================== public class Demo01Multi { public static void main(String[] args) { // 使用多态的写法 // 左侧父类的引用,指向了右侧子类的对象 Fu obj = new Zi(); obj.method(); obj.methodFu(); } }
多态中成员变量和成员方法的访问规则:
成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。
public class Fu { int num = 10; public void showNum() { System.out.println(num); } public void method() { System.out.println("父类方法"); } public void methodFu() { System.out.println("父类特有方法"); } } =============================== public class Zi extends Fu { int num = 20; int age = 16; @Override public void showNum() { System.out.println(num); } @Override public void method() { System.out.println("子类方法"); } public void methodZi() { System.out.println("子类特有方法"); } } ============================= public class Demo01MultiField { public static void main(String[] args) { // 使用多态的写法,父类引用指向子类对象 Fu obj = new Zi(); System.out.println(obj.num); // 父:10 //System.out.println(obj.age); // 错误写法!成员变量运行和编译看左面 System.out.println("============="); // 子类没有覆盖重写,就是父:10 // 子类如果覆盖重写,就是子:20 obj.showNum(); System.out.println("============="); obj.method(); // 父子都有,优先用子 obj.methodFu(); // 子类没有,父类有,向上找到父类 } }
向上转型和向下转型
1、向上转型是自动的。即Father f = new Children()是自动的,不需要强转
2、向下转型要强转。即Children c = new Father()是无法编译通过的,必须要Children c = (Children)new Father(),让父类知道它要转成具体哪个子类
3、父类引用指向子类对象,子类重写了父类的方法,调用父类的方法,实际调用的是子类重写了的父类的该方法。即Father f = new Children(),f.toString()实际上调用的是Children中的toString()方法
4、向上转型一定是安全的,没有问题的,正确的。但是也有一个弊端:对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。解决方案:用对象的向下转型【还原】。
instanceof方法
如何才能知道一个父类引用的对象,本来是什么子类?
格式: 对象 instanceof 类名称
使用接口或者父类作为方法参数,既可以多态写法,也可以用实现类对象。两者都会向上转型。
public interface USB { public abstract void open(); // 打开设备 public abstract void close(); // 关闭设备 } =========================== // 鼠标就是一个USB设备 public class Mouse implements USB { @Override public void open() { System.out.println("打开鼠标"); } @Override public void close() { System.out.println("关闭鼠标"); } public void click() { System.out.println("鼠标点击"); } } =========================== // 键盘就是一个USB设备 public class Keyboard implements USB { @Override public void open() { System.out.println("打开键盘"); } @Override public void close() { System.out.println("关闭键盘"); } public void type() { System.out.println("键盘输入"); } } ============================ public class Computer { public void powerOn() { System.out.println("笔记本电脑开机"); } public void powerOff() { System.out.println("笔记本电脑关机"); } // 使用USB设备的方法,使用接口作为方法的参数 public void useDevice(USB usb) { usb.open(); // 打开设备 if (usb instanceof Mouse) { // 一定要先判断 Mouse mouse = (Mouse) usb; // 向下转型 mouse.click(); } else if (usb instanceof Keyboard) { // 先判断 Keyboard keyboard = (Keyboard) usb; // 向下转型 keyboard.type(); } usb.close(); // 关闭设备 } } ============================== public class DemoMain { public static void main(String[] args) { // 首先创建一个笔记本电脑 Computer computer = new Computer(); computer.powerOn(); USB usbMouse = new Mouse(); // 首先进行向上转型,多态写法 // 参数是USB类型,我正好传递进去的就是USB鼠标 computer.useDevice(usbMouse); // 创建一个USB键盘 Keyboard keyboard = new Keyboard(); // 没有使用多态写法 // 方法参数是USB类型,传递进去的是实现类对象 computer.useDevice(keyboard); // 正确写法!也发生了向上转型 // 使用子类对象,匿名对象,也可以 // computer.useDevice(new Keyboard()); // 也是正确写法,也发生了向上转型 computer.powerOff(); System.out.println("=================="); } }