文章目录
第十一章抽象类
1.抽象类概述
在继承体系中,由于父类的设计应该保证继承体系中所有子类的共性,子类往往比父类要描述的更加清晰、具体;因此我们有时需要将父类设计的抽象化;即方法只声明方法体,而没有方法具体功能,我们把没有方法主体的方法称为抽象方法。包含抽象方法的类就是抽象类。
1.1举例
1. 厨师 成员变量:工号,姓名,工资 成员方法:工作(炒菜)
2. 保安 成员变量:工号,姓名,工资 成员方法:工作(检查外来人员)
3. 保洁 成员变量:工号,姓名,工资 成员方法:工作(负责公司的清洁)
图解:
1.2定义
- 抽象方法 :没有方法体的方法。
- 抽象类:包含抽象方法的类。
2.抽象语法
使用 abstract
关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
2.1抽象类的定义
语法:
[修饰符列表] abstract class 类名{
类体;
}
代码示例:
public abstract class Animal {
....
}
2.2抽象方法的定义
语法:
修饰符 abstract 返回值类型 方法名 (参数列表);
代码示例:
public abstract void work();
2.3抽象的使用
- 继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。
- 必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。
抽象类是不可以进行实例化的,抽象类本就是包含有无法实例化的抽象方法,或者说这个方法是没有任何意义的,他存在的意义就是让子类去实现它;因此抽象类是不可以实例化的,也就是不能创建对象;
代码示例:
public class Demo01 {
public static void main(String[] args) {
Employee employee=new Cook("123","张三",28.5);
employee.work();
}
}
/**
* 员工类(抽象)
*/
abstract class Employee {
private String id;
private String name;
private Double salary;
public Employee() {
}
public Employee(String id, String name, Double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
//注意这里要提供getter和setter方法
// 抽象方法不能有方法体
// 表示员工本就有工作,但是每个员工的工作内容是不一样的(由具体的子类来实现)
public abstract void work();
}
/**
* 厨师类
*/
class Cook extends Employee {
public Cook() {
super();
}
public Cook(String id, String name, Double salary) {
super(id, name, salary);
}
// 必须重写父类的抽象方法
public void work() {
System.out.println("炒菜");
}
}
/**
* 保洁类
*/
class Cleaner extends Employee {
public Cleaner() {
super();
}
public Cleaner(String id, String name, Double salary) {
super(id, name, salary);
}
// 必须重写父类的抽象方法
public void work() {
System.out.println("检查外来人员");
}
}
/**
* 保安类
*/
class Security extends Employee {
public Security() {
super();
}
public Security(String id, String name, Double salary) {
super(id, name, salary);
}
// 必须重写父类的抽象方法
public void work() {
System.out.println("负责公司清洁");
}
}
此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法。
2.4 模板方法设计模式
模板方法设计模式解决了什么问题?
- 提高了代码的复用性
模板方法已经定义了通用结构,模板方法不能确定的部分定义成抽象方法,交给子类实现,因此,使用者只需要关心自己需要实现的功能即可。
建议模板方法使用final关键字修饰,这样不能被子类重写
定义Student抽象类
public abstract class Student {
// 模板方法,把相同代码放进去
// 建议将模板方法用final修饰,防止子类重写
final public void writeEssay(){
System.out.println("《深入Java虚拟机》");
System.out.println("《介绍双亲委派机制》");
writeMain();
System.out.println("这就是双亲委派机制");
}
// 定义一个抽象方法
public abstract void writeMain();
}
定义MiddleStudent类
public class MiddleStudent extends Student{
@Override
public void writeMain() {
System.out.println("MiddleStudent...........");
}
}
定义LittleStudent类
public class LittleStudent extends Student{
@Override
public void writeMain() {
System.out.println("LittleStudent.........");
}
}
定义测试类
public class Demo {
public static void main(String[] args) {
// 通过小学生和中学生写作文的案例理解模板方法设计模式
// 1. 定义一个抽象方法Student
// 2. 在抽象类Student中 定义两个方法,一个是模板方法,把相同代码放进去
// 一个是抽象方法,具体实现交给子类完成
LittleStudent littleStudent=new LittleStudent();
littleStudent.writeEssay();
System.out.println("--------------------------------");
MiddleStudent middleStudent=new MiddleStudent();
middleStudent.writeEssay();
}
}
3.抽象类注意点
- 抽象类是无法实例化的,无法创建对象的,所以抽象类是用来被子类继承的。
- final和abstract不能联合使用,是对立的
- 抽象类的子类可以是抽象类。也可以是非抽象类。
- 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
- 抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中!!!!
- 一个非抽象的类继承抽象类,必须将抽象类中的抽象方法实现(覆盖)了,这里的覆盖或者重写,就叫做实现(对抽象的实现)
java语言中凡是没有方法体的方法都是抽象方法?
不对!!
object类中就有很多方法都有方法体,public native int hashCode();都是以;结尾的,但他们不是抽象方法
他们是调用C++的
第十二章接口
1.接口概述
1.1接口引入
继承抽取了类的共性,使得其子类都具备了父类的功能,提高了代码的复用性,但是有些情况下,并不是所有的子类都应该具备父类的全部功能,有些功能只是当做与"扩展功能",并不是与生俱备的;
例如:吃饭、睡觉、上课等都是学生具有的共同的功能,我们就可以将其定义在学生类当中。但是,学生之间也是有差异的,比如课外扩展这一方面有的人喜欢学习俄语,有的人喜欢编程等等,如果讲学俄语,打代码都写到学生类当中显然是不符合逻辑的,不是每个学生都有这样的课外扩展。
像这样的独特的功能,不是每个人都具有的。可以作为"扩展功能",并不是所有都会,而是部分人会;在Java中,我们可以将扩展功能定义在接口中;
1.2接口的定义
接口(英文:Interface),在Java编程语言中是一个抽象类型,接口中定义的全都是抽象方法,使用Interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
一个类继承接口则需要实现该接口的所有的抽象方法,除非继承接口的类是抽象类,因此一个类继承接口我们往往称为某个类实现了(implements
)某个接口,关键字从extends
更换为implements
;
1.3接口的特点
Java中定义接口采用interface
关键字,关于接口有如下特点:
-
接口也是一种引用数据类型,编译之后也是.class字节码文件
-
接口是完全抽象的。(抽象类是半抽象的)或者说接口是特殊的抽象类
-
接口支持多继承,一个接口可以继承多个接口
-
接口中只包含两部分内容:一部分是:常量,一部分是:抽象方法,接口中没有其他内容了
接口中的所有成员变量都默认是由public static final修饰的;
接口中的所有方法都默认是由public abstract修饰的;
-
接口没有构造方法(不能实例化对象);
-
类继承(实现)接口时,必须重写接口中的所有抽象方法,否则该类是抽象类;
-
接口中没有静态代码块。
-
接口的静态方法不能被继承下来;
1.4 抽象类和接口的区别
抽象类 | 接口 | |
---|---|---|
成员属性 | 没有限制 | 只能定义常量,默认有public static final修饰 |
构造器 | 有,但是只能通过子类构造器调用。不能使用new关键字直接实例化抽象类对象 | 没有,不能使用new关键字直接实例化接口对象 |
方法 | 没有限制,可以定义抽象方法,也可以定义实例方法 | JDK8之前只能定义抽象方法,不能写方法体 |
使用 | 子类使用extends关键字继承抽象类,且只能单继承 | 子类使用implements关键字实现接口,可以多实现 |
抽象类和接口都可以用来表示对象的通用行为,我到底该选择用哪个呢?
- 抽象类表示了一种强“ is-a”关系;而接口表示的是一种“is-kind-of”关系,或者说是一种弱“ is-a”关系。
- 如果要在方法中使用成员变量,可以用抽象类;接口中只能定义常量。
- 接口比抽象类更灵活。
2.接口语法
public interface 接口名称{
}
示例:
-
接口
public interface InterfaceTest { //public static final String Guo_ji="中国"; 和下面的写法是等价的 String Guo_ji="中国"; //public abstract void method(); 和下面的是等价的 void method(); int sum(int a,int b); static void method2(){ System.out.println("静态方法执行了"); } }
-
实现类
public class ImplementsTest implements InterfaceTest{ public void method() { String guo_ji = InterfaceTest.Guo_ji; System.out.println(guo_ji);//这里可以直接调用常量 System.out.println("实现了接口中method的方法"); } public int sum(int a, int b) { //两数之和的功能 return a+b; } }
-
测试类
public class Test { public static void main(String[] args) { //可以采用多态的形式 InterfaceTest interfaceTest=new ImplementsTest(); interfaceTest.method(); InterfaceTest.method2(); //调用 sum方法 ImplementsTest implementsTest=new ImplementsTest(); int sum = implementsTest.sum(3, 5); System.out.println("求和结果为"+sum); } }
3.JDK1.8新特性
3.1语法
在JDK7及以前,接口中只允许存在抽象方法,在JDK8中,接口允许存在默认方法和静态方法,两种方法可以为接口提供一些功能性的代码,也可以让子类选择性的重写方法,而不是强制性重写接口中的所有方法;
默认方法的出现可以让子类实现接口时选择性的重写方法,而不是强制性重写所有的方法;
- 默认方法:使用
default
修饰,不可省略,供子类调用或者子类重写。 - 静态方法:使用
static
修饰,供接口直接调用(接口中,被static修饰的方法不能被继承到子类)。
tips:接口中默认方法的
default
修饰符不可省略,这点和我们之前学习的权限修饰符不一样;
3.2示例代码
-
接口
public interface Interface { default void method() { System.out.println("默认的方法"); } default int sum(int a,int b){ return a+b; } void a(); }
-
实现类
public class Implements implements Interface{ public void a() { System.out.println("这个就需要重写"); } }
-
测试
public class Demo { public static void main(String[] args) { Interface in=new Implements(); in.method(); int sum = in.sum(1, 2); System.out.println(sum); } }
tips:
默认方法可以重写也可以不重写,不重写默认被继承下来;
在接口中,静态方法不会被继承下来;
4.JDK1.9新特性
4.1语法
在JDK9中,接口除了运行存在默认方法、静态方法之外(JDK8新特性),还允许存在私有方法,让接口本身可以封装某段逻辑但又不想被子类继承的方法;
示例代码:
public interface InterFaceName {
private void method(){
}
private static void method2(){
}
}
4.2示例
-
定义接口:
public interface A { default void method3(){ System.out.println("A 的默认方法~"); // 调用a的私有方法 method(); } private void method() { System.out.println("A 的私有方法~"); } private static void method2() { System.out.println("A 的静态私有方法~"); } }
-
定义实现类
public class B implements A{ }
-
测试:
public class Demo { public static void main(String[] args) { B b=new B(); b.method3(); } }
5. 接口注意事项
-
接口不能创建对象
-
一个类实现多个接口,多个接口的规范不能冲突
interface A21{ void run(); } interface B21{ int run(); } class C21 implements A21,B21{ @Override public void run() { //这里是报错的 该成int也是 } }
-
一个类实现多个接口,多个接口中有同样的静态方法不冲突。
interface A31{ static void run(){ System.out.println("A31"); } } interface B31{ static void run(){ System.out.println("B31"); } } class C31 implements A31,B31{ }
-
一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。
class Animal{ public void run(){ System.out.println("父类中的run方法"); } } interface A41{ default void run(){ System.out.println("接口的run方法"); } } class C41 extends Animal implements A41{}
-
一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
interface A51{ default void run(){ System.out.println("A51的run方法"); } } interface B51{ default void run(){ System.out.println("B51的run方法"); } } class C51 implements A51,B51{ @Override public void run() { //A51.super.run(); 用那个接口的run方法 或者是自己写逻辑 //B51.super.run(); System.out.println("C51......"); } }
-
一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。
interface A61{ void run(); } interface B61{ int run(); } interface C61 extends A61,B61{ // clashes 冲突 }
6.接口案例
案例:现有一家饭店,有两个厨师 一个是中国厨师,另一个是西方厨师。 这两个厨师都会西红柿炒鸡蛋和鱼香肉丝。
图解:
代码:
-
定义员工类
public class Employee { private int empId; private String empName; private double sal; public Employee() { } public Employee(int empId, String empName, double sal) { this.empId = empId; this.empName = empName; this.sal = sal; } public int getEmpId() { return empId; } public void setEmpId(int empId) { this.empId = empId; } public String getEmpName() { return empName; } public void setEmpName(String empName) { this.empName = empName; } public double getSal() { return sal; } public void setSal(double sal) { this.sal = sal; } }
-
定义做菜的接口
//做菜的接口 public interface Menu { void ScrambledEWT();//西红柿炒鸡蛋 void ShreddedPork();//鱼香肉丝 }
-
定义中国厨师
public class ChineseChef extends Chef{ public ChineseChef() { super(); } public ChineseChef(int empId, String empName, double sal) { super(empId, empName, sal); } public void ScrambledEWT(){ System.out.println("员工:"+super.getEmpName()); System.out.println("中国厨师做的西红柿炒鸡蛋真好吃!!!!!!!!!!!!!"); } public void ShreddedPork(){ System.out.println("员工:"+super.getEmpName()); System.out.println("中国厨师做的鱼香肉丝真好吃!!!!!!!!!!!!!"); } }
-
定义外国厨师
public class WesternChef extends Chef{ public WesternChef() { super(); } public WesternChef(int empId, String empName, double sal) { super(empId, empName, sal); } public void ScrambledEWT(){ System.out.println("员工:"+super.getEmpName()); System.out.println("西方厨师做的西红柿炒鸡蛋真好吃"); } public void ShreddedPork(){ System.out.println("员工:"+super.getEmpName()); System.out.println("西方厨师做的鱼香肉丝真好吃"); } }
-
定义顾客类
public class Customer { private Chef chef; public Customer() { } public Customer(Chef chef) { this.chef = chef; } public Chef getEmployee() { return chef; } public void setEmployee(Chef chef) { this.chef = chef; } //提供点餐的功能 public void order(){ chef.ScrambledEWT(); chef.ShreddedPork(); } }
-
定义测试类
public class TestProcedure { public static void main(String[] args) { // 先new 一个客户 Customer customer1=new Customer(); // 点中国厨师做的饭 //先 new 一个中国厨师 ChineseChef chineseChef=new ChineseChef(111,"张三",8000.00); customer1.setEmployee(chineseChef); //点菜 customer1.order(); // 上面代码的合并 (西方厨师) Customer customer2=new Customer(new WesternChef(222,"jack",6000.00)); customer2.order(); } }
-
定义测试类
public class TestProcedure { public static void main(String[] args) { // 先new 一个客户 Customer customer1=new Customer(); // 点中国厨师做的饭 //先 new 一个中国厨师 ChineseChef chineseChef=new ChineseChef(111,"张三",8000.00); customer1.setEmployee(chineseChef); //点菜 customer1.order(); // 上面代码的合并 (西方厨师) Customer customer2=new Customer(new WesternChef(222,"jack",6000.00)); customer2.order(); } }
这里感觉博主,写的有问题的话,麻烦私信指出一下 谢谢各位大佬