什么是抽象类
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。抽象类也属于引用数据类型可以包含普通方法和抽象方法。Java中规定如下语法:
- 用abstract关键字来修饰一个类,这个类叫做抽象类。
- 用abstract来修饰一个方法,该方法叫做抽象方法。
- 抽象方法:只有方法的声明,没有方法的实现。以分号结束:
public abstract void talk();//抽象方法
抽象类怎么定义?
代码示例
package demo01; // 定义一个抽象类 public abstract class Animal { }
特点
- 抽象类是无法实例化的,无法创建对象的,所以抽象类是用来被子类继承的。
- 抽象类虽然无法实例化,但是抽象类有构造方法,这个构造方法是供子类使用的。
- 不能用abstract修饰变量、代码块、构造器;
- 不能用abstract修饰私有方法、静态方法、final的方法、final的类。
抽象方法
抽象方法表示没有实现的方法,没有方法体的方法。
定义格式:
例如:
public abstract void doSome();
抽象方法特点:
- 没有方法体,以分号结尾。
- 前面修饰符列表中有abstract关键字。
抽象的使用
继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。package demo01; public class Test { public static void main(String[] args) { // 能不能使用多态? // 父类型引用指向子类型对象。 Animal a = new Bird(); // 向上转型。(自动类型转换) // 这就是面向抽象编程。 // 以后你都是调用的a.XXXX // a的类型是Animal,Animal是抽象的 // 面向抽象编程,不要面向具体编程,降低程序的耦合度,提高程序的扩展力。 // 这种编程思想符合OCP原则。 /* 分析以下: 编译的时候这个move()方法是谁的?父类 运行的时候这个move()方法又是谁的?子类 */ a.move();//鸟儿在飞翔! // 多态(当对多态不是很理解的时候,以后写代码能用多态就用多态。慢慢就理解了。) Animal x = new Cat(); x.move();//猫在走猫步! } } // 动物类(抽象类) abstract class Animal { // 抽象方法 public abstract void move(); //成员方法 public void message() { System.out.println("我是一个动物"); } } class Bird extends Animal { // 需要将从父类中继承过来的抽象方法进行覆盖/重写,或者也可以叫做“实现”。 @Override public void move() { System.out.println("鸟儿在飞翔!"); } } class Cat extends Animal { @Override public void move() { System.out.println("猫在走猫步!"); } }
注意事项
抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
- 理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
- 理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
- 理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
接口
接口的由来
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。
接口的概述
接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了抽象方法。接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。接口(interface)是抽象方法和常量值定义的集合。
接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则必须能...”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
定义格式
代码示例
package demo02; // 定义一个接口 public interface Runner { int ID = 1; void start(); public void run(); void stop(); }
接口的特点
- 在 java 中接口采用 interface 声明,所有的元素都是public修饰的
- 接口中的方法默认都是 public abstract 的,不能更改。 public abstract 可以省略不写。
- 接口中的变量默认都是 public static final 类型的,不能更改,所以必须显示的初始化。public static final可以省略不写。
- 接口不能被实例化,接口中没有构造函数的概念
- 接口之间可以继承,但接口之间不能实现
上面的代码等价于下面的
package demo02; // 定义一个接口 public interface Runner { //常量 public static final int ID = 1; //抽象方法 public abstract void start(); public abstract void run(); public abstract void stop(); }
接口的基本的实现
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements 关键字。非抽象子类实现接口: 必须重写接口中所有抽象方法。
我来实现下上面定义的接口package demo02; //非抽象RunnerTest实现了Runner接口 public class RunnerTest implements Runner { @Override public void start() { System.out.println("实现了start"); } @Override public void run() { System.out.println("实现了run"); } @Override public void stop() { System.out.println("实现了stop"); } }
定义测试类
package demo02; public class Test { public static void main(String[] args) { Runner runnerTest = new RunnerTest(); //调用实现类中的方法 runnerTest.run(); //访问接口中的常量 int id = Runner.ID; System.out.println(id); } }
总结
- 如果Java实现类即继承了类又实现了接口的语法格式先写extends,后写implements
package demo04; /* 继承和实现都存在的话,代码应该怎么写? extends 关键字在前。 implements 关键字在后。 */ public class Test { public static void main(String[] args) { // 创建对象(表面看Animal类没起作用!) Flyable f = new Cat(); //多态。 f.fly();//飞猫起飞,翱翔太空的一只猫,很神奇,我想做一只猫!! //向下转型调用继承下来的方法 if (f instanceof Cat) { Cat cat = (Cat) f; cat.message();//我是动物 // 同一个接口 Flyable f2 = new Pig(); // 调用同一个fly()方法,最后的执行效果不同。 f2.fly();//我是一只会飞的猪!!! Flyable f3 = new Fish(); f3.fly();//我是六眼飞鱼(流言蜚语)!!! } } } // 动物类:父类 class Animal { public void message() { System.out.println("我是动物"); } } // 可飞翔的接口(是一对翅膀) // 接口通常提取的是行为动作。 interface Flyable { void fly(); } // 动物类子类:猫类 // Flyable是一个接口,是一对翅膀的接口,通过接口插到猫身上,让猫变的可以飞翔。 class Cat extends Animal implements Flyable { public void fly() { System.out.println("飞猫起飞,翱翔太空的一只猫,很神奇,我想做一只猫!!"); } } // 蛇类,如果你不想让它飞,可以不实现Flyable接口 // 没有实现这个接口表示你没有翅膀,没有给你插翅膀,你肯定不能飞。 class Snake extends Animal { } // 想飞就插翅膀这个接口。 class Pig extends Animal implements Flyable { public void fly() { System.out.println("我是一只会飞的猪!!!"); } } // 鱼(默认实际上是存在继承的,默认继承Object。) /* class Fish extends Object implements Flyable{ } */ class Fish implements Flyable { //没写extends,也是有的,默认继承Object。 public void fly() { System.out.println("我是六眼飞鱼(流言蜚语)!!!"); } }
- 接口和接口之间支持多继承
interface X{ } interface Y{ } interface Z extends X,Y{ //接口和接口支持多继承。 }
- 一个类可以同时实现多个接口。
- 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
package deno03; /* 接口和接口之间支持多继承,那么一个类可以同时实现多个接口吗? 对于计算机来说,一个机箱上有多个接口,一个接口是接键盘的, 一个接口是接鼠标的,一个接口是接电源的,一个接口是接显示器的..... 重点(五颗星*****):一个类可以同时实现多个接口。 这种机制弥补了java中的哪个缺陷? java中类和类只支持单继承。实际上单继承是为了简单而出现的,现实世界中 存在多继承,java中的接口弥补了单继承带来的缺陷。 接口A和接口B虽然没有继承关系,但是写代码的时候,可以互转。 编译器没意见。但是运行时可能出现:ClassCastException 之前有一个结论: 无论向上转型还是向下转型,两种类型之间必须要有继承关系, 没有继承关系编译器会报错。(这句话不适用在接口方面。) 最终实际上和之前还是一样,需要加:instanceof运算符进行判断。 向下转型养成好习惯。转型之前先if+instanceof进行判断。 */ public class Test { public static void main(String[] args) { // 都是父类型引用指向子类型对象 A a = new D(); //a.m2(); // 编译报错。A接口中没有m2()方法。 B b = new D(); C c = new D(); // 调用其他接口中的方法,你需要转型(接口转型。) // 接口和接口之间在进行强制类型转换的时候,没有继承关系,也可以强转。 // 但是一定要注意,运行时可能会出现ClassCastException异常。编译没问题,运行有问题。 //向下转型养成好习惯。转型之前先if+instanceof进行判断。直接转为子类,就可以调用子类中实现的方法 if (a instanceof D) { D b1 = (D) a; b1.m2();//m2 .... } } } interface A { void m1(); } interface B { void m2(); } interface C { void m3(); } // 实现多个接口,其实就类似于多继承。 class D implements A, B, C { // 实现A接口的m1() public void m1() { } // 实现B接口中的m2() public void m2() { System.out.println("m2 ...."); } // 实现接口C中的m3() public void m3() { } }
- 接口的主要用途就是被实现类实现。(面向接口编程)
- 与继承关系类似,接口与实现类之间存在多态性
- 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前),而没有变量和方法的实现。
其他成员特点
- 接口中,没有构造方法,不能创建对象。
- 接口中,没有静态代码块。
接口的进一步应用
在 java 中接口其实描述了类需要做的事情,类要遵循接口的定义来做事,使用接口到底有什么本质的好处?可以归纳为两点:- 采用接口明确的声明了它所能提供的服务
- 解决了 Java 单继承的问题
- 实现了可接插性(重要)
代码示例
我们去餐厅吃饭,菜单就是一个接口,这个饭馆的“菜单”,让“顾客”和“后厨”解耦合了。顾客不用找后厨,后厨不用找顾客。他们之间完全依靠这个抽象的菜单
- 谁面向接口调用。(顾客面向菜单点菜,调用接口。)
- 谁负责实现这个接口。(后台的厨师负责把西红柿鸡蛋做好!是接口的实现者。)
定义菜单接口
package demo03; public interface FoodMenu { // 西红柿炒蛋 void shiZiChaoJiDan(); // 鱼香肉丝 void yuXiangRouSi(); }
厨师是接口的实现者。
package demo03; //中餐厨师 public class ChinaCooker implements FoodMenu { @Override public void shiZiChaoJiDan() { System.out.println("中国口味炒鸡蛋"); } @Override public void yuXiangRouSi() { System.out.println("中国口味鱼香肉丝"); } }
package demo03; //西餐厨师 public class AmericCooker implements FoodMenu { @Override public void shiZiChaoJiDan() { System.out.println("西餐师傅做的西红柿炒鸡蛋!"); } @Override public void yuXiangRouSi() { System.out.println("西餐师傅做的鱼香肉丝!"); } }
定义顾客类,
package demo03; //顾客类 public class Customer { // 顾客手里有一个菜单 // Customer has a FoodMenu!(这句话什么意思:顾客有一个菜单) // 记住:以后凡是能够使用 has a 来描述的,统一以属性的方式存在。 // 实例变量,属性 // 面向抽象编程,面向接口编程。降低程序的耦合度,提高程序的扩展力。 private FoodMenu foodMenu; //构造方法 public Customer() { } public Customer(FoodMenu foodMenu) { this.foodMenu = foodMenu; } // setter and getter public FoodMenu getFoodMenu() { return foodMenu; } public void setFoodMenu(FoodMenu foodMenu) { this.foodMenu = foodMenu; } // 提供一个点菜的方法 public void order(){ // 先拿到菜单才能点菜 // 调用get方法拿菜单。 //FoodMenu fm = this.getFoodMenu(); // 也可以不调用get方法,因为在本类中私有的属性是可以访问 foodMenu.shiZiChaoJiDan(); foodMenu.yuXiangRouSi(); } }
定义测试类
package demo03; public class Test { public static void main(String[] args) { // 创建厨师对象 //FoodMenu cooker1 = new ChinaCooker(); FoodMenu cooker1 = new AmericCooker(); //创建顾客类 Customer customer = new Customer(cooker1); // 顾客点菜 customer.order(); } }
执行结果
接口可以解耦合,解开的是谁和谁的耦合!!!
任何一个接口都有调用者和实现者。接口可以将调用者和实现者解耦合。调用者面向接口调用。实现者面向接口编写实现。
接口和抽象类的区别
- 在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,要么实现接口。
- 接口描述了方法的特征,不给出实现,一方面解决 java 的单继承问题,实现了强大的可接插性
- 抽象类提供了部分实现,抽象类是不能实例化的,抽象类的存在主要是可以把公共的代码移植到抽象类中
- 面向接口编程,而不要面向具体编程(面向抽象编程,而不要面向具体编程)
- 优先选择接口(因为继承抽象类后,此类将无法再继承,所以会丧失此类的灵活性)
常见的类之间的关系
- is a(继承):但凡满足is a的表示都可以设置为继承。例如:Cat is a Animal,
- has a(关联):但凡是满足has a的表示都以属性的形式存在。例如:I has a Pen(我有一支笔)
- like a(实现):凡是能够满足like a关系的表示“实现关系”实现关系通常是:类实现接口。例如:Cooker like a FoodMenu(厨师像一个菜单一样)