接口一、介绍以及使用1.接口接口是更加彻底的抽象它定义了一种规范/协议。2.定义// interface 关键字定义接口 public interface USB { // 抽象方法public abstract 可以省略 void connect(); void disconnect(); }3.实现// implements 关键字实现接口 public class Mouse implements USB { Override public void connect() { System.out.println(鼠标连接了); } Override public void disconnect() { System.out.println(鼠标断开了); } } public class Keyboard implements USB { Override public void connect() { System.out.println(键盘连接了); } Override public void disconnect() { System.out.println(键盘断开了); } }使用// 接口作为类型多态 USB device1 new Mouse(); device1.connect(); // 鼠标连接了 USB device2 new Keyboard(); device2.connect(); // 键盘连接了 // 可以用数组统一管理 USB[] devices {new Mouse(), new Keyboard()}; for (USB d : devices) { d.connect(); }ps:接口用 interface 定义实现类用 implements 实现接口接口中的方法默认是 public abstract接口 规范实现类 具体实现二、接口中的抽象方法1.接口中抽象方法的特点接口中的方法默认都是 public abstract实现类中重写的方法必须是 publicpublic interface MyInterface { // 以下四种写法完全等价接口中 public abstract void method1(); // 完整版 abstract void method2(); // 省略 public public void method3(); // 省略 abstract void method4(); // 都省略最常用 }2.实现类必须重写public class MyClass implements MyInterface { Override public void method1() {} // 必须 public Override public void method2() {} Override public void method3() {} Override public void method4() {} }三、接口中的默认方法和静态方法1.默认方法public interface USB { // 抽象方法 void connect(); // 默认方法有方法体用 default 修饰 public default void checkStatus() { System.out.println(设备状态正常); } } // 实现类可以不重写默认方法 public class Mouse implements USB { Override public void connect() { System.out.println(鼠标连接); } // checkStatus() 可以不重写有默认实现 } // 实现类也可以重写默认方法 public class Keyboard implements USB { Override public void connect() { System.out.println(键盘连接); } Override public void checkStatus() { System.out.println(键盘状态检查中...); } }好处接口升级如果接口需要加新方法原来已经实现该接口的类不用必须修改提供默认实现减少实现类的重复代码2. 静态方法public interface USB { // 静态方法属于接口本身 public static void printInfo() { System.out.println(USB 3.0 标准); } } // 调用接口名直接调用 USB.printInfo(); // USB 3.0 标准 // 实现类不继承接口静态方法 // new Mouse().printInfo(); // 错误ps:默认方法用 default有方法体可重写可不重写静态方法用 static接口名调用实现类不继承四、接口中的成员变量1.特点public interface MyInterface { // 以下四种写法完全等价 public static final int NUM1 10; // 完整写法 static final int NUM2 20; // 省略public public static int NUM3 30; // 省略final int NUM4 40; // 都省略最常用 }接口中的变量默认 public static final实际上是常量必须初始化不能修改通过接口名访问例System.out.println(MyInterface.NUM1); // 10接口名.常量名访问 // MyInterface.NUM1 100; // 错误final 不能修改五、接口特点以及和抽象类区别1. 接口支持多实现// 一个类可以实现多个接口 public class SmartPhone implements Phone, Camera, MP3 { Override public void call() { } Override public void takePhoto() { } Override public void playMusic() { } }2. 接口可以多继承接口public interface A { void a(); } public interface B { void b(); } public interface C extends A, B { // 接口可以多继承 void c(); }3. 类和接口的关系类与类单继承 extends类与接口多实现 implements接口与接口多继承 extends接口和抽象类区别ps:如果多个类有共同的属性和行为 → 抽象类如果定义一种行为规范/能力 → 接口多态一、多态的介绍和基本使用多态是面向对象三大特性之一指同一行为在不同对象上表现出不同的形态。多态的前提条件1.必须有继承或实现关系2.必须有方法重写3.父类引用指向子类对象格式父类类型 变量名 new 子类类型(); 变量名.方法名();例子// 父类 public class Animal { public void eat() { System.out.println(动物吃东西); } } // 子类 public class Dog extends Animal { Override public void eat() { System.out.println(狗吃骨头); } public void watchHouse() { System.out.println(狗看家); } } public class Cat extends Animal { Override public void eat() { System.out.println(猫吃鱼); } public void catchMouse() { System.out.println(猫抓老鼠); } }多态写法// 普通写法子类引用指向子类对象 Dog d new Dog(); d.eat(); // 狗吃骨头 // 多态写法父类引用指向子类对象 Animal a1 new Dog(); // 狗是动物OK Animal a2 new Cat(); // 猫是动物OK a1.eat(); // 狗吃骨头调用的是Dog重写后的方法 a2.eat(); // 猫吃鱼调用的是Cat重写后的方法核心Animal a new Dog(); a.eat(); // 编译看左边AnimalAnimal 中有没有 eat 方法 // 运行看右边Dog实际调用的是 Dog 重写后的 eat 方法ps:多态三条件继承 重写 父类引用指向子类对象格式父类 变量 new 子类();编译看左运行看右二、成员访问特点例子public class Father { int num 10; public void method() { System.out.println(父类方法); } } public class Son extends Father { int num 20; // 和父类变量同名 Override public void method() { System.out.println(子类方法); } }测试Father f new Son(); // 多态写法 // 成员变量看左边Father System.out.println(f.num); // 10父类的num不是子类的20 // 成员方法编译看左运行看右Son f.method(); // 子类方法调用重写后的方法为什么成员变量和成员方法的访问规则不同方法有重写所以运行时用子类的版本变量没有重写只有隐藏所以访问父类的ps:变量看左边父类方法编译看左运行看右子类重写版变量没有重写只有隐藏三、多态的好处好处一提高代码的扩展性和灵活性没有多态时public class AnimalKeeper { public void feedDog(Dog dog) { dog.eat(); } public void feedCat(Cat cat) { cat.eat(); } public void feedPig(Pig pig) { // 每增加一种动物就要加一个方法 pig.eat(); } } // 使用 AnimalKeeper keeper new AnimalKeeper(); keeper.feedDog(new Dog()); keeper.feedCat(new Cat()); // 不同动物要调用不同方法很麻烦用后public class AnimalKeeper { // 参数是父类类型可以接收所有子类对象 public void feed(Animal animal) { animal.eat(); // 自动调用对应子类的方法 } } // 使用统一调用 AnimalKeeper keeper new AnimalKeeper(); keeper.feed(new Dog()); // 狗吃骨头 keeper.feed(new Cat()); // 猫吃鱼 keeper.feed(new Pig()); // 猪吃饲料 // 不管什么动物都用同一个 feed() 方法好处二简化代码数组统一管理// 有多态用父类数组统一管理所有子类对象 Animal[] animals {new Dog(), new Cat(), new Pig(), new Bird()}; for (Animal a : animals) { a.eat(); // 同一行代码不同对象表现不同行为 } // 没有多态每种动物一个数组代码冗长 Dog[] dogs {new Dog(), new Dog()}; Cat[] cats {new Cat(), new Cat()}; // ... 每增加一种就多一个数组好处三降低耦合// 调用方只依赖 Animal 父类不依赖具体的 Dog 或 Cat public class Zoo { public void showAnimal(Animal a) { a.eat(); // 不关心具体是哪种动物 } } // 以后新增 Animal 的子类Zoo 的代码完全不用改 public class Panda extends Animal { Override public void eat() { System.out.println(熊猫吃竹子); } } // 直接使用 zoo.showAnimal(new Panda()); // 完全兼容经典应用// List 是接口ArrayList 和 LinkedList 是实现类 ListString list new ArrayList(); // 多态写法 list.add(hello); list.add(world); // 以后想换实现类只需改一处 // ListString list new LinkedList(); // 其他代码完全不用变 // 方法参数也用接口类型 public void printList(ListString list) { for (String s : list) { System.out.println(s); } }四、向上转型和向下转型1.向上转型子类对象 → 父类类型自动完成安全。// 向上转型自动发生安全 Animal a new Dog(); // Dog → Animal自动转型 a.eat(); // 只能调用 Animal 中定义的方法 // 本质把狗当成普通动物看待 // 优点可以统一处理 // 缺点丢失子类特有方法 // a.watchHouse(); // 编译错误Animal 中没有此方法2.向下转型父类类型 → 子类类型需要强制转换有风险。Animal a new Dog(); // 先向上转型 // 向下转型强制转换 Dog d (Dog) a; // OK因为 a 本质是 Dog d.watchHouse(); // 现在可以调用 Dog 特有的方法了为什么需要向下转型多态写法只能调用父类中定义的方法要调用子类特有的方法必须向下转型ps:向上转型子→父自动安全丢失子类特有功能向下转型父→子强制有风险本质必须是该子类向下转型是为了调用子类特有方法五、向下转型问题说明类型判断风险Animal a new Cat(); // a 本质是 Cat Dog d (Dog) a; // 运行时错误 // ClassCastException: Cat cannot be cast to Dog // 类型转换异常猫不能强制转换成狗错误原因a 的本质是 Cat强制转成 Dog 类型不兼容。如何避免——instanceof 类型判断// 语法对象 instanceof 类型 → 返回 boolean Animal a new Dog(); if (a instanceof Dog) { Dog d (Dog) a; d.watchHouse(); } else if (a instanceof Cat) { Cat c (Cat) a; c.catchMouse(); }例子public static void handleAnimal(Animal a) { // 通用的操作 a.eat(); // 针对不同类型做不同处理 if (a instanceof Dog) { ((Dog) a).watchHouse(); } else if (a instanceof Cat) { ((Cat) a).catchMouse(); } } // 调用 handleAnimal(new Dog()); // 吃骨头 看家 handleAnimal(new Cat()); // 吃鱼 抓老鼠注意Dog d new Dog(); System.out.println(d instanceof Dog); // true System.out.println(d instanceof Animal); // trueDog是Animal的子类 System.out.println(d instanceof Object); // true所有类都是Object的子类ps:向下转型前必须用 instanceof 判断instanceof判断对象的本质类型不用instanceof直接强转可能出ClassCastException