外围概述:本篇咱们将学习面向对象中的接口和多态,接口相似咱们之前学习继承时的父类或抽象类,接口不同凡响的时,接口中跟多的定义事物的性能(办法),子类或实现类能够实现或重写接口中的办法。而接口或继承,则是多态的前提。正当地利用多态能够进步咱们程序的可扩展性和灵活性。
第一章:接口
1.1-接口概述(理解)
什么是接口
Java中的接口是一系列办法的申明
,是一些办法特色的汇合
。
一个接口只有办法的特色(只有申明)没有办法的实现(没有办法体),因而这些办法能够在不同的中央被不同的类实现,而这些实现能够具备不同的行为(性能)
如果说类的外部封装了成员变量、构造方法和成员办法,那么接口的外部次要就是封装了办法,蕴含形象办法(JDK 7及以前),默认办法和静态方法(JDK 8)。
总而言之,Java中的接口就是一系列办法申明的汇合。
为什么须要接口
接口的劣势:
- 是多态的根底
- 能够多实现(能够了解为多继承)
接口是一种援用数据类型
接口的定义,它与定义类形式类似,然而应用 interface
关键字。它也会被编译成.class文件,但肯定要明确它并不是类,而是另外一种援用数据类型。
类和接口都是java代码,都会转换为字节码文件
public class 类名.java → 类名.classpublic interface 接口名.java → 接口名.class
1.2-接口的定义格局(记忆)
定义格局:关键字 interface
public interface 接口名称 { // 形象办法 // 默认办法 // 静态方法}
接口中定义形象办法
形象办法:应用abstract
关键字润饰,能够省略,没有办法体。该办法供子类实现应用。
public interface InterFaceName { public abstract void method();}
接口中定义默认办法和静态方法
默认办法:应用 default
润饰,不可省略,供子类调用或者子类重写。
静态方法:应用 static
润饰,供接口间接调用。
public interface InterFaceName { public default void method() { // 执行语句 } public static void method2() { // 执行语句 }}
1.3-接口的应用形式(记忆)
咱们之前学习继承时,父类须要子类继承。而接口和继承中父类类似,也须要一个相似子类的实现类来实现接口。
接口的实现
类与接口的关系为实现关系,即类实现接口,该类能够称为接口的实现类,也能够称为接口的子类。
实现的动作相似继承,格局相仿,只是关键字不同,实现应用 implements
关键字。
非抽象类实现接口注意事项
- 必须重写接口中所有形象办法。
- 继承了接口的默认办法,即能够间接调用,也能够重写。
子类实现接口格局
public class 类名 implements 接口名 { // 重写接口中形象办法【必须】 // 重写接口中默认办法【可选】}
子类实现接口中的形象办法
对于接口中定义的形象办法,子类必须全副实现(重写)。代码如下:
定义一个接口:LiveAble
public interface LiveAble { // 定义形象办法 public abstract void eat(); public abstract void sleep();}
定义一个实现类:Animal
public class Animal implements LiveAble { @Override public void eat() { System.out.println("吃货色"); } @Override public void sleep() { System.out.println("早晨睡"); }}
定义一个测试类:Test
public class Test { public static void main(String[] args) { // 创立子类对象 Animal a = new Animal(); // 调用实现后的办法 a.eat(); a.sleep(); }}/* 输入后果: 吃货色 早晨睡*/
子类应用或重写接口中的默认办法
对于接口中的默认办法,子类能够继承,也能够重写,二选一,然而只能通过实现类的对象来调用。
间接应用默认办法,代码如下:
定义接口:LiveAble
public interface LiveAble { public default void fly(){ System.out.println("天上飞"); }}
定义实现类:Animal
public class Animal implements LiveAble { // 继承,什么都不必写,间接调用}
定义测试类:Test
public class Test { public static void main(String[] args) { // 创立子类对象 Animal a = new Animal(); // 调用默认办法 a.fly(); }}/* 输入后果: 天上飞*/
或者重写默认办法,代码如下:
定义接口:LiveAble 同上
定义实现类:Animal
public class Animal implements LiveAble { @Override public void fly() { System.out.println("无拘无束的飞"); }}
定义测试类:Test
public class Test { public static void main(String[] args) { // 创立子类对象 Animal a = new Animal(); // 调用重写办法 a.fly(); }}/* 输入后果: 无拘无束的飞*/
接口中静态方法
的应用
动态与.class 文件相干,只能应用接口名调用,不能够通过实现类的类名或者实现类的对象调用,代码如下:
定义接口:LiveAble
public interface LiveAble { public static void run(){ System.out.println("跑起来~~~"); }}
定义实现类:Animal
public class Animal implements LiveAble { // 无奈重写静态方法}
定义测试类:Test
public class Test { public static void main(String[] args) { // Animal.run(); // 【谬误】无奈继承办法,也无奈调用 LiveAble.run(); // }}/* 输入后果: 跑起来~~~*/
接口中不能定义成员变量
,能够定义常量
接口中,无奈定义成员变量,然而能够定义常量,其值不能够扭转,默认应用public static final润饰。
定义接口:LiveAble
public interface LiveAble { int NUM0 ; // 谬误,必须赋值 int NUM1 =10; // 正确 , 省去了默认修饰符 public static final public static final int NUM2= 100; // 正确 , 残缺写法}
定义测试类:
public class Test { public static void main(String[] args) { System.out.println(Live.NUM1); System.out.println(Live.NUM2); }}/* 输入后果: 10 100*/
1.4-接口的多实现(记忆)
在继承体系中,一个类只能继承一个父类(单继承)。
而对于接口而言,一个类是能够实现多个接口的,这叫做接口的多实现。
并且,一个类能继承一个父类,同时实现多个接口。
实现格局
public class 类名 [extends 父类名] implements 接口名1,接口名2,接口名3... { // 重写接口中形象办法【必须】 // 重写接口中默认办法【不重名时可选】}
[ ]中的格局: 示意可选操作。
接口多实现的形象办法
接口中,有多个形象办法时,实现类必须重写所有形象办法。如果形象办法有重名的,只须要重写一次。代码如下:
定义多个接口:
interface A { public abstract void showA(); public abstract void show();}interface B { public abstract void showB(); public abstract void show();}
定义实现类:
public class C implements A,B{ @Override public void showA() { System.out.println("showA"); } @Override public void showB() { System.out.println("showB"); } @Override public void show() { System.out.println("show"); }}
接口多实现的默认办法
接口中,有多个默认办法时,实现类都可继承应用。如果默认办法有重名的,必须重写一次。代码如下:
定义多个接口:
interface A { public default void methodA(){} public default void method(){}}interface B { public default void methodB(){} public default void method(){}}
定义实现类:
public class C implements A,B{ @Override public void method() { System.out.println("method"); }}
接口多实现中的静态方法
接口中,存在同名的静态方法并不会抵触,起因是只能通过各自接口名拜访静态方法。
public interface MyInterface{ public static void inter(){ system.out.println("接口静态方法"); }}public class Test{ public static void main(String[] args){ //接口名间接调用 MyInterface.inter(); }}
1.5 接口的多继承 (记忆)
一个接口能继承另一个或者多个接口,这和类之间的继承比拟类似。
接口的继承应用 extends
关键字,子接口继承父接口的办法。如果父接口中的默认办法有重名的,那么子接口须要重写一次。代码如下:
定义父接口:
interface A { public default void method(){ System.out.println("AAAAAAAAAAAAAAAAAAA"); }}interface B { public default void method(){ System.out.println("BBBBBBBBBBBBBBBBBBB"); }}
定义子接口:
interface D extends A,B{ @Override public default void method() { System.out.println("DDDDDDDDDDDDDD"); }}
1.6 抽象类和接口的区别(了解)
通过实例进行剖析和代码演示抽象类和接口的用法。
举例:犬和缉毒犬
犬:
- 行为:呼啸;吃饭;
缉毒犬:
- 行为:呼啸;吃饭;缉毒;
思考
因为犬分为很多品种,他们呼啸和吃饭的形式不一样,在形容的时候不能具体化,也就是呼啸和吃饭的行为不能明确。
当形容行为时,行为的具体动作不能明确,这时,能够将这个行为写为形象行为,那么这个类也就是抽象类。
可是当缉毒犬有其余额定性能时,而这个性能并不在这个事物的体系中。这时能够让缉毒犬具备犬科本身特点的同时也有其余额定性能,能够将这个额定性能定义接口中。
代码
interface 缉毒{ public abstract void 缉毒();}//定义犬科的这个揭示的共性功能abstract class 犬科{ public abstract void 吃饭(); public abstract void 呼啸();}// 缉毒犬属于犬科一种,让其继承犬科,获取的犬科的个性,//因为缉毒犬具备缉毒性能,那么它只有实现缉毒接口即可,这样即保障缉毒犬具备犬科的个性,也领有了缉毒的性能class 缉毒犬 extends 犬科 implements 缉毒{ public void 缉毒() { } void 吃饭() { } void 呼啸() { }}class 缉毒猪 implements 缉毒{ public void 缉毒() { }}
通过示例总结抽象类和接口的区别
相同点:
- 都位于继承的顶端,用于被其余类实现或继承;
- 都不能间接实例化对象;
- 都蕴含形象办法,其子类都必须覆写这些形象办法;
区别:
- 抽象类为局部办法提供实现,防止子类反复实现这些办法,进步代码重用性;接口只能蕴含形象办法;
- 一个类只能继承一个间接父类(可能是抽象类),却能够实现多个接口(接口补救了Java的单继承);
- 抽象类为继承体系中的共性内容,接口为继承体系中的扩大性能;
语法具体区别:
成员区别
抽象类
- 变量,常量;有构造方法;有形象办法,也有非形象办法
接口
- 常量;形象办法
关系区别
类与类
- 继承,单继承
类与接口
- 实现,能够单实现,也能够多实现
接口与接口
- 继承,单继承,多继承
设计理念区别
抽象类
- 对类形象,包含属性、行为
接口
- 对行为形象,次要是行为
第二章:多态
2.1-多态概述(理解)
什么是多态?
首先,多态是继封装、继承之后,面向对象的第三大个性。
多态Polymorphism,按字面意思就是“多种状态”。
生存中,比方跑的动作,小猫、小狗和大象,跑起来是不一样的。再比方飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。可见,同一行为,通过不同的事物,能够体现进去的不同的状态。多态,形容的就是这样的状态。
在面向对象编程中,多态是指同一行为
,具备多个不同表现形式
。
多态的前提
- 继承或者实现【二选一】
- 办法的重写【意义体现:不重写,无意义】
- 父类援用指向子类对象【格局体现】
2.2-多态的语法格局(记忆)
语法格局:
父类类型 变量名 = new 子类();变量名.办法名();
父类类型:是指子类继承的父类类型,或者实现的父接口类型。
示例代码:
当应用多态形式调用办法时,首先查看父类中是否有该办法,如果没有,则编译谬误;如果有,执行的是子类重写后办法。
定义父类:
public abstract class Animal { public abstract void eat(); }
定义子类:
class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } }
定义测试类:
public class Test { public static void main(String[] args) { // 多态模式,创建对象 Animal a1 = new Cat(); // 调用的是 Cat 的 eat a1.eat(); // 多态模式,创建对象 Animal a2 = new Dog(); // 调用的是 Dog 的 eat a2.eat(); } }
2.3-多态的益处(了解)
理论开发的过程中,父类类型作为办法形式参数,传递子类对象给办法,进行办法的调用,更能体现出多态的扩展性与灵活性。
示例代码如下:
定义父类:
public abstract class Animal { public abstract void eat(); }
定义子类:
class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } }
定义测试类:
public class Test { public static void main(String[] args) { // 多态模式,创建对象 Cat c = new Cat(); Dog d = new Dog(); // 调用showCatEat showCatEat(c); // 调用showDogEat showDogEat(d); /* 以上两个办法, 均能够被showAnimalEat(Animal a)办法所代替 而执行成果统一 */ showAnimalEat(c); showAnimalEat(d); } public static void showCatEat (Cat c){ c.eat(); } public static void showDogEat (Dog d){ d.eat(); } public static void showAnimalEat (Animal a){ a.eat(); }}
因为多态个性的反对,showAnimalEat办法的Animal类型,是Cat和Dog的父类类型,父类类型接管子类对象,当然能够把Cat对象和Dog对象,传递给办法。
当eat办法执行时,多态规定,执行的是子类重写的办法,那么成果天然与showCatEat、showDogEat办法统一,所以showAnimalEat齐全能够代替以上两办法。
不仅仅是代替,在扩展性方面,无论之后再多的子类呈现,咱们都不须要编写showXxxEat办法了,间接应用showAnimalEat都能够实现。
所以,多态的益处,体现在,能够使程序编写的更简略,并有良好的扩大。
2.4-多态的转型(了解)
多态的转型分为向上转型与向下转型两种:
向上转型
多态自身是子类类型向父类类型向上转换的过程,这个过程是默认的。
表现形式:当父类援用指向一个子类对象时,便是向上转型。
父类类型 变量名 = new 子类类型();如:Animal a = new Cat();
向下转型
父类类型向子类类型向下转换的过程,这个过程是强制的。
表现形式:一个曾经向上转型的子类对象,将父类援用转为子类援用,能够应用强制类型转换的格局,便是向下转型。
子类类型 变量名 = (子类类型) 父类变量名;如: Animal a = new Cat(); // Cat 向上转型为Animal a示意Cat转型后的Animal类型 Cat c =(Cat) a; // 曾经向上转型的Cat类型a,向下强制转型为Cat
为什么还要向下转型呢?
当应用多态形式调用办法时,首先查看父类中是否有该办法,如果没有,则编译谬误。也就是说,不能调用子类领有,而父类没有的办法。编译都谬误,更别说运行了。这也是多态给咱们带来的一点"小麻烦"。所以,想要调用子类特有的办法,必须做向下转型。示例代码如下:
定义类:
abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } public void watchHouse() { System.out.println("看家"); } }
定义测试类:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 Cat c = (Cat)a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } }
转型异样
转型的过程中,一不小心就会遇到这样的问题,请看如下代码:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 Dog d = (Dog)a; d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】 } }
这段代码能够通过编译,然而运行时,却报出了 ClassCastException
,类型转换异样!这是因为,明明创立了Cat类型对象,运行时,当然不能转换成Dog对象的。
为了防止ClassCastException的产生,Java提供了 instanceof
关键字,给援用变量做类型的校验,格局如下:
变量名 instanceof 数据类型 // 如果变量属于该数据类型,返回true。// 如果变量不属于该数据类型,返回false。
所以,转换前,咱们最好先做一个判断,代码如下:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 if (a instanceof Cat){ Cat c = (Cat)a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } else if (a instanceof Dog){ Dog d = (Dog)a; d.watchHouse(); // 调用的是 Dog 的 watchHouse } } }
第三章:综合案例
需要:
定义笔记本类,具备开机,关机和应用USB设施的性能。
具体是什么USB设施,笔记本并不关怀,只有合乎USB规格的设施都能够。
鼠标和键盘要想能在电脑上应用,那么鼠标和键盘也必须恪守USB标准,不然鼠标和键盘的生产进去无奈应用;
进行形容笔记本类,实现笔记本应用USB鼠标、USB键盘
- USB接口,蕴含开启性能、敞开性能
- 笔记本类,蕴含运行性能、关机性能、应用USB设施性能
- 鼠标类,要合乎USB接口
- 键盘类,要合乎USB接口
剖析:
阶段一:应用笔记本,笔记本有运行性能,须要笔记本对象来运行这个性能
阶段二:想应用一个鼠标,又有一个性能应用鼠标,并多了一个鼠标对象。
阶段三:还想应用一个键盘 ,又要多一个性能和一个对象。
问题:每多一个性能就须要在笔记本对象中定义一个办法,不爽,程序扩展性极差。
解决:应用多态机制,升高鼠标、键盘等外围设备和笔记本电脑的耦合性。
代码:
//定义鼠标、键盘,笔记本三者之间应该恪守的规定 public interface USB { void open();// 开启性能 void close();// 敞开性能}//鼠标实现USB规定public class Mouse implements USB { public void open() { System.out.println("鼠标开启"); } public void close() { System.out.println("鼠标敞开"); }}//键盘实现USB规定public class KeyBoard implements USB { public void open() { System.out.println("键盘开启"); } public void close() { System.out.println("键盘敞开"); }}//定义笔记本public class NoteBook { // 笔记本开启运行性能 public void run() { System.out.println("笔记本运行"); } // 笔记本应用usb设施,这时当笔记本对象调用这个性能时,必须给其传递一个合乎USB规定的USB设施 public void useUSB(USB usb) { // 判断是否有USB设施 if (usb != null) { usb.open(); usb.close(); } } public void shutDown() { System.out.println("笔记本敞开"); }}//测试public class Test { public static void main(String[] args) { // 创立笔记本实体对象 NoteBook nb = new NoteBook(); // 笔记本开启 nb.run(); // 创立鼠标实体对象 Mouse m = new Mouse(); // 笔记本应用鼠标 nb.useUSB(m); // 创立键盘实体对象 KeyBoard kb = new KeyBoard(); // 笔记本应用键盘 nb.useUSB(kb); // 笔记本敞开 nb.shutDown(); }}