第三节抽象类和接口

第十一章抽象类

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关键字,关于接口有如下特点:

  1. 接口也是一种引用数据类型,编译之后也是.class字节码文件

  2. 接口是完全抽象的。(抽象类是半抽象的)或者说接口是特殊的抽象类

  3. 接口支持多继承,一个接口可以继承多个接口

  4. 接口中只包含两部分内容:一部分是:常量,一部分是:抽象方法,接口中没有其他内容了

    接口中的所有成员变量都默认是由public static final修饰的;

    接口中的所有方法都默认是由public abstract修饰的;

  5. 接口没有构造方法(不能实例化对象);

  6. 类继承(实现)接口时,必须重写接口中的所有抽象方法,否则该类是抽象类;

  7. 接口中没有静态代码块。

  8. 接口的静态方法不能被继承下来;

1.4 抽象类和接口的区别

抽象类接口
成员属性没有限制只能定义常量,默认有public static final修饰
构造器有,但是只能通过子类构造器调用。不能使用new关键字直接实例化抽象类对象没有,不能使用new关键字直接实例化接口对象
方法没有限制,可以定义抽象方法,也可以定义实例方法JDK8之前只能定义抽象方法,不能写方法体
使用子类使用extends关键字继承抽象类,且只能单继承子类使用implements关键字实现接口,可以多实现

抽象类和接口都可以用来表示对象的通用行为,我到底该选择用哪个呢?

  • 抽象类表示了一种强“ is-a”关系;而接口表示的是一种“is-kind-of”关系,或者说是一种弱“ is-a”关系。
  • 如果要在方法中使用成员变量,可以用抽象类;接口中只能定义常量。
  • 接口比抽象类更灵活。

2.接口语法

public interface 接口名称{        
}

示例:

  1. 接口

    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("静态方法执行了");
        }
    }
    
    
  2. 实现类

    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;
        }
    }
    
    
  3. 测试类

    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示例

  1. 定义接口:

    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 的静态私有方法~");
        }
    }
    
  2. 定义实现类

    public class B implements A{
    
    }
    
  3. 测试:

    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.接口案例

案例:现有一家饭店,有两个厨师 一个是中国厨师,另一个是西方厨师。 这两个厨师都会西红柿炒鸡蛋和鱼香肉丝。

图解:
在这里插入图片描述

代码:

  1. 定义员工类

    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;
        }
    }
    
  2. 定义做菜的接口

    //做菜的接口
    public interface Menu {
        void ScrambledEWT();//西红柿炒鸡蛋
        void ShreddedPork();//鱼香肉丝
    }
    
    
  3. 定义中国厨师

    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("中国厨师做的鱼香肉丝真好吃!!!!!!!!!!!!!");
        }
    }
    
    
  4. 定义外国厨师

    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("西方厨师做的鱼香肉丝真好吃");
        }
    
    }
    
    
  5. 定义顾客类

    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();
        }
    }
    
    
  6. 定义测试类

    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();
    
        }
    }
    
  7. 定义测试类

    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();
    
        }
    }
    

这里感觉博主,写的有问题的话,麻烦私信指出一下 谢谢各位大佬

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值