抽象类和接口–java面对对象编程思维

  • Post author:
  • Post category:java




抽象类的概述

/**
    目标:抽象类的入门概述。

    引入:
        父类知道子类一定要完成某个功能,但是每个子类实现的情况都不一样
        而且子类都会用自己的功能了,父类的该功能就可以定义成抽象的方法。
        拥有抽象方法的类必须定义成抽象类。
    什么是抽象方法?
        没有方法体,只有方法签名,必须用abstract修饰的方法就是抽象方法。
    什么是抽象类?
        拥有抽象方法的类必须定义成抽象类。
        抽象类必须用abstract关键字修饰。
    小结:
        抽象方法:没有方法体,只有方法签名,必须用abstract修饰的方法就是抽象方法。
        抽象类:拥有抽象方法的类必须定义成抽象类,必须用abstract修饰。
 */
public class AbstractDemo {
    public static void main(String[] args) {
        Wolf qpl = new Wolf();
        qpl.run();
    }
}

class Wolf extends Animal{
    @Override
    public void run(){
        System.out.println("🐺跑的贼贼溜~~~");
    }
}

// 抽象类:拥有了抽象方法的类必须定义成抽象类。抽象类必须加上abstract修饰。
abstract class Animal{
    // 抽象方法:没有方法体,只有方法签名,必须加上abstract修饰。
    public abstract void run();
}



抽象类的使用

/**
    目标:抽象类的使用。

    抽象类是为了被继承。

    总结:
        一个类继承了抽象类,必须重写完抽象类的全部抽象方法,否则这个类必须定义成抽象类。
        因为拥有抽象方法的类必须定义成抽象类。
 */
public class AbstractDemo {
    public static void main(String[] args) {
        Techer boZai = new Techer();
        boZai.work();
        boZai.run();

        Manager baoQing = new Manager();
        baoQing.work();
        baoQing.run();
    }
}

class Manager extends Employee{
    @Override
    public void work() {
        System.out.println("班主任需要管理班级~~~~");
    }

     @Override
     public void run() {

     }
 }

class Techer extends Employee{
    @Override
    public void work() {
        System.out.println("老师需要授课~~~~");
    }

    @Override
    public void run() {

    }
}

// 员工(老师,班主任 ) 每个员工都要工作,但是工作内容不一样。
abstract class Employee{
    public abstract void work();
    public abstract void run();
}



抽象类的特征

/**
    目标:抽象类的特征研究和深入。

    抽象类的特征:有得有失。
        有得:抽象类拥有了得到抽象方法的能力。
        失去:抽象类失去了创建对象的能力。

    面试题:抽象类是否有构造器,是否可以创建对象,为什么?
         答:抽象类作为类一定有构造器,而且必须有构造器。
         提供给子类继承后调用父类构造器使用的。

         抽象类虽然有构造器,但是抽象类绝对不能创建对象。
         抽象类中可能存在抽象方法,抽象方法不能执行。
         抽象在学术上本身意味着不能实例化。

    抽象类除了有得有失之外,类的其他成分人家都具备!!

    小结:
        抽象类不能创建对象。
        抽象类除了不能创建对象之外,类的其他成分它都具备!
        抽象类中也可以没有抽象方法!!
 */
public class AbstractDemo {
    public static void main(String[] args) {
        //Animal a = new Animal(); // 抽象类不能创建对象!
        //a.run(); // 抽象方法不能执行,抽象类不能创建对象!!
    }
}

abstract class Animal{
    private String name;
    private int age ;
    public static String schoolName = "黑马";
    public Animal(){

    }

    public abstract void run();

    //普通方法
    public void go(){

    }

    public static void test(){

    }
}



抽象类的意义

/**
    目标:抽象类的意义。

    抽象类存在的意义有两点:
        (1)被继承,抽象类就是为了被子类继承,否则抽象类将毫无意义。(核心意义)
        (2)抽象类体现的是"模板思想":部分实现,部分抽象。(拓展)
            -- 可以使用抽象类设计一个模板模式。
 */
public class ExtendsDemo {
}



抽象类设计模板模式

/**
    目标:使用抽象类设计一个模板模式。

    设计模式:是前人(技术大牛,或者一些技术协会,或者一些大型知名的IT公司)
             已经研发好或者设计好或者在实战开发中发现的的优秀软件设计思想,开源出去
             后来者可以直接使用就能够得到很好的软件模式。

    设计模式的目的:得到优秀的软件架构,从而提升代码的可重用性,扩展性,维护性,可读性。

    模板模式是一种设计模式思想:
    模板模式的作用:部分实现,部分抽象,可以极大的简化功能代码,提高开发效率

    写一个模板模式的案例:作文模板。
        作文模板:
        标题和第一段 以及最后一段是固定的,
        正文部分交给使用模板的人自己来实现。
    小结:
        抽象类是部分实现,部分抽象的含义,所以可以设计模板模式。
        好处:模板可以确定的模板自己实现,模板不能确定的定义成抽象方法交给使用模板的人重写。
        可以设计出优秀的设计模式,提升开发效率,提高代码的重用性!
 */
public class ExtendsDemo {
    public static void main(String[] args) {
        Student xiaoMa = new Student();
        xiaoMa.write();

        Teacher boZai = new Teacher();
        boZai.write();
    }
}

class Teacher extends Template{
    @Override
    public String writeMain() {
        return "\t\t我爸就是好,有多好,做他儿子才能懂~~~";
    }
}

class Student extends Template{
    @Override
    public String writeMain() {
        return "\t\t我爸爸很牛,我爸爸是马云,就是爽,很有钱~~~~";
    }
}
// 1.写一个模板类:代表了作文模板。
abstract class Template{
    private String title = "\t\t\t\t\t\t《我的爸爸》";
    private String one = "\t\t我的爸爸很牛逼,到底有多牛呢,请看如下说明:";
    private String last = "\t\t以上就是我的爸爸,简直太好了,下辈子还要做他儿子!";

    // 2.提供一个写作文方法
    public void write(){
        System.out.println(title);
        System.out.println(one);
        // 正文:正文部分模板是不知道怎么写的!应该把正文部分定义成抽象方法
        // 交给使用模板的子类重写!
        System.out.println(writeMain());
        System.out.println(last);
    }

    // 正文部分定义成抽象方法,交给子类重写!!
    public abstract String writeMain();
}

需求:账户有活期和定期账户两种,计算方式不同。活期是0.35%,定期是 1.75%,结算利息要先进行用户名、密码验证,验证失败直接提示,登录成功进行结算。

1、创建一个抽象的账户类Account作为父类,提供属性(账户卡号,账户余额

2、在父类Account中提供一个模板方法实现登录验证,利息结算、利息输出。

3、具体的利息结算定义成抽象方法,交给子类实现。

4、定义定期账户类,让子类重写实现具体的结算方法

5、创建定期账户对象,完成相关功能

import java.lang.reflect.Field;

public class TestDemo {
    public static void main(String[] args) {
        // ALT + ENTER
        CurrentAccount acc = new CurrentAccount("ICBC-111", 100000);
        acc.handle("itheima" , "123456");

        FixedAccount acc1 = new FixedAccount("ICBC-222"
                , 100000);
        acc1.handle("itheima" , "123456");

    }
}
/**
  模板方法模式
 */
public abstract class Account {
    private String cardId;
    private double money;

    public Account() {

    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    /**
     登录结算利息:模板方法

     final的作用:修饰的方法不能被重写。 好优雅!
     模板方法不是被子类重写的,而是被子类直接使用的。
     */
    public final void handle(String loginName , String passWord ){
        // a.判断登录是否成功
        if("itheima".equals(loginName) && "123456".equals(passWord)){
            System.out.println("登录成功。。");
            // b.正式结算利息:模板不知道如何计算,具体的计算交给子类计算
            double result = calc();
            // c.输出利息详情
            System.out.println("本账户利息是:"+ result);
        }else{
            System.out.println("用户名或者密码错误了");
        }
    }

    /** 子类一定会重写的!! */
    public abstract double calc();


    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}
/**
  定期账户
 */
public class FixedAccount extends Account {
    public FixedAccount(String name, double money) {
        super(name , money);
    }

    @Override
    public double calc() {
        double result = getMoney() * 0.0175;
        if(getMoney() >= 100000){
            result += getMoney() * 0.03;
        }
        return result;
    }
}
/**
  活期账户
 */
public class CurrentAccount extends Account {
    public CurrentAccount(String cardId, double money) {
        super(cardId , money);
    }
    @Override
    public double calc() {
        return getMoney() * 0.0035;
    }
}



抽象类的注意事项

/**
     目标:抽象类的注意事项和总结

     1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
         理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

     2. 抽象类一定有而且是必须有构造器,是供子类创建对象时,初始化父类成员使用的。
         理解:子类的构造器中,有默认的super(),需要访问父类构造器。

     3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

     4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则子类也必须定义成抽象类。

     5. 抽象类存在的意义是为了被子类继承,抽象类体现的是模板思想。
        理解:抽象类中已经实现的是模板中确定的成员,
        抽象类不确定如何实现的定义成抽象方法,交给具体的子类去实现。

 */
public class AbstractDemo {
    public static void main(String[] args) {

    }
}

abstract class Animal{
    public abstract void run();
}



接口的概述

/**
     目标:接口的概述和定义等。(以理解和记住语法为主)

     什么是接口?
          接口是更加彻底的抽象,接口中全部是抽象方法和常量,没有其他成分。(JDK 1.8之前)

     接口有啥用?
          接口体现的是规范思想,实现接口的类必须重写完接口中全部的抽象方法。
          规范 == 约束。
          接口称为被实现,实现接口的类称为实现类。

     定义接口的格式:
          修饰符 interface 接口名称{

          }
          interface:定义接口的关键字。

     接口中的成分研究(JDK 1.8之前):
          1.抽象方法
               a.接口中的抽象方法默认会加上public abstract修饰,所以可以省略不写。
          2.常量
            常量:是指有public static final修饰的成员变量,有且仅能被赋值一次,值不能改变。
            常量的名称规范上要求全部大写,多个单词下划线连接。
            常量修饰的public static final 可以省略不写,默认会加上。
     小结:
          定义接口使用的关键字:interface
          接口中的成分在JDK 1.8之前只能有:常量和抽象方法。
          在接口中常量的修饰符:public static final 可以省略不写,默认会加上。
          在接口中抽象方法的修饰符:public abstract 可以省略不写,默认会加上。

 */
public interface InterfaceDemo {
     // 2.常量
     // 只有一份,在执行的过程中其值必须有,但是不能改变!
     // 常量是public static final修饰
     // 常量的名称建议字母全部大写,多个单词用“_”连接
     // 在接口中常量可以省略public static final不写,默认会加上该三个修饰符!
     //public static final String SCHOOL_NAME = "黑马";
     String SCHOOL_NAME = "黑马";


     // 1.抽象方法
     // public abstract void run();
     // 接口中的抽象方法默认会加上public abstract修饰,所以可以省略不写。
     void run();
     void work();
}



接口的基本实现

/**
    目标:接口的基本实现。

    接口是用来被类实现的。

    引入:
        类与类是继承关系:一个类只能直接继承一个父类。
        类与接口是实现关系:一个类可以实现多个接口。
        实现接口的类称为“实现类”。

        子类   继承   父类
        实现类 实现   接口

    实现类实现接口的格式:
        修饰符 class 实现类名称 implements 接口1,接口2,接口3,....{

        }
        implements:实现的含义。
        接口是可以被多实现的:一个类可以实现多个接口。

    小结:
        接口是用类被实现的,实现接口的类称为实现类。
        实现接口的关键字是:implements。
        接口是可以被类多实现的。
        注意:一个类实现接口必须重写完接口中全部抽象方法,否则这个类必须定义成抽象类!!
 */
public class InterfaceDemo {
    public static void main(String[] args) {
        PingPongMan zjk = new PingPongMan("张继科");
        zjk.run();
        zjk.win();
    }
}

//abstract class BasketBall implements SportMan{
//}

// 实现类 实现 SportMan接口
// 一个类实现接口必须重写完接口中全部抽象方法,否则这个类必须定义成抽象类!!
class PingPongMan implements SportMan{
    private String name;
    public PingPongMan(String name){
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println(name+"必须天天运动。正在🏃训练~~~‍");
    }

    @Override
    public void win() {
        System.out.println(name+"参加比赛中~~~‍");
    }
}

// 定义一个接口:表示运动员的规范
interface SportMan{
    void run(); // 跑步
    void win(); // 比赛得奖
}



接口的多实现

/**
     目标:接口的多实现介绍。

     实现类实现接口的格式:
         修饰符 class 实现类名称 implements 接口1,接口2,接口3,....{

         }

     类与类是单继承。
     类与接口是多实现。

    小结:
        一个类可以实现多个接口。
        一个类如果实现了多个接口,必须重写完全部接口中的全部抽象方法
        否则这个类必须定义抽象类。
 */
public class InterfaceDemo {
    public static void main(String[] args) {
        PingPongMan zjk = new PingPongMan();
        zjk.run();
        zjk.win();
        zjk.rule();
    }
}

class PingPongMan implements SportMan , Law{

    @Override
    public void rule() {

    }

    @Override
    public void run() {

    }

    @Override
    public void win() {

    }
}

interface Law{
    void rule();
    void run();
}

interface SportMan{
    void run();
    void win();
}



接口与接口的多继承

package com.itheima._10接口与接口的多继承;
/**
    目标:接口与接口的多继承。

    引入:
        类与类是单继承关系:一个类只能继承一个直接父类。
        类与接口是多实现关系:一个类可以实现多个接口。
        接口与接口是多继承关系:一个接口可以继承多个接口。
 */
public class InterfaceDemo {
}

class PingPongMan implements SportMan{

    @Override
    public void eat() {

    }

    @Override
    public void rule() {

    }

    @Override
    public void run() {

    }

    @Override
    public void goAbroad() {

    }
}

interface Food{
    void eat();
}

interface Law{
    void rule();
}

// 接口与接口的多继承!
interface SportMan extends Law , Food {
    void run();
    void goAbroad();
}



JDK8之后的接口新增方法

/**
    目标:JDK 1.8开始之后接口新增的三种方法(理解语法,属于Java自己的技术)

    引入:
        JDK 1.8之前接口中只能是抽象方法,常量。
        JDK 1.8开始之后接口不再纯洁了。
        JDK 1.8开始之后接口新增了如下三种方法。

    a.默认方法(就是之前写的普通实例方法)
        -- 必须用default修饰,默认会public修饰
        -- 必须用接口的实现类的对象来调用。

    b.静态方法
        -- 默认会public修饰
        -- 注意:接口的静态方法必须用接口的类名本身来调用。

    c.私有方法(就是私有的实例方法): JDK 1.9才开始有的。
        -- 只能在本类中被其他的默认方法或者私有方法访问。
 */
public class InterfaceDemo {
    public static void main(String[] args) {
        // 1.默认方法调用:必须用接口的实现类对象调用。
        PingPongMan zjk = new PingPongMan();
        zjk.run();
        zjk.work();

        // 2.接口的静态方法必须用接口的类名本身来调用。
        InterfaceJDK8.inAddr();
    }
}
class PingPongMan implements InterfaceJDK8{
    @Override
    public void work() {
        System.out.println("工作中。。。");
    }
}

interface InterfaceJDK8{
    // 之前的抽象方法!!
    void work();

    // a.默认方法(就是之前写的普通实例方法)
    // 必须用接口的实现类的对象来调用。
    default void run(){
        go();
        System.out.println("开始跑步🏃‍~~~~");
    }

    // b.静态方法
    // 注意:接口的静态方法必须用接口的类名本身来调用
    static void inAddr(){
        System.out.println("我们在吉山区~~~~");
    }

    // c.私有方法(就是私有的实例方法): JDK 1.9才开始有的。
    //  -- 只能在本接口中被其他的默认方法或者私有方法访问。
    private void go(){
        System.out.println("开始。。");
    }
}



实现多个接口的使用注意实现

package com.itheima._12实现多个接口的使用注意事项;

/**
    拓展:实现多个接口的使用注意实现。(非常偏的语法,理解和了解即可)

    1.如果实现了多个接口,多个接口中存在同名的静态方法并不会冲突,
        原因是只能通过各自接口名访问静态方法。

    2.当一个类,既继承一个父类,又实现若干个接口时,(重点)
        父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。

    3.当一个类实现多个接口时,多个接口中存在同名的默认方法。
        实现类必须重写这个方法。

    4.接口中,没有构造器,不能创建对象。(重点)
        接口是更彻底的抽象,连构造器都没有,自然不能创建对象!!

 */
public class InterfaceDemo {
    public static void main(String[] args) {
        //C.test(); // 报错 ,接口的静态方法,只能有接口名称调用!
        A.test();
        B.test();

        Cat cat = new Cat();
        cat.run();

        // A2 a = new A2(); // 接口是更彻底的抽象,连构造器都没有,自然不能创建对象!!
    }
}

/**
 3.当一个类实现多个接口时,多个接口中存在同名的默认方法。
 实现类必须重写这个方法。
 */
interface A2{
    default void test(){}
}
interface B2{
    default void test(){}
}
class C2 implements A2 , B2{
    @Override
    public void test() {
    }
}





/**
 2.当一个类,既继承一个父类,又实现若干个接口时,(重点)
 父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。
 */
interface A1{
    default void run(){
        System.out.println("输出干爹的run()方法~~~~");
    }
}
class Animal{
    public void run(){
        System.out.println("输出亲爹Animal的run()方法~~~~");
    }
}
class Cat   extends Animal  implements A1  {
}


/**
 1.如果实现了多个接口,多个接口中存在同名的静态方法并不会冲突,
    原因是只能通过各自接口名访问静态方法。
 */
interface A{
    static void test(){
        System.out.println("A");
    }
}
interface B{
    static void test(){
        System.out.println("B");
    }
}
class C implements A , B{
}



版权声明:本文为weixin_42137874原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。