面向对象高级(1)

news2025/4/12 23:43:36

文章目录

  • final
    • 认识final关键字
      • 修饰类:
      • 修饰方法:
      • 修饰变量
      • final修饰变量的注意事项
    • 常量
  • 单例类
    • 什么是设计模式?
    • 单例怎么写?
    • 饿汉式单例的特点是什么?
    • 单例有啥应用场景,有啥好处?
    • 懒汉式单例类。
  • 枚举类
    • 认识枚举类
    • 枚举类的常见应用场景
  • 抽象类
    • 认识抽象类
    • 使用抽象类的好处
    • 模板方法设计模式
  • 接口
    • 什么是接口
    • 接口语法
    • 接口如何实现
    • 接口的好处
    • 接口的综合小案例
      • 需求
      • 分析
      • 代码
    • JDK8新增的三种方法
    • 接口的注意事项
      • 1
      • 2
      • 3
      • 4
  • 抽象类和接口的区别
  • 案例

final、单例类、枚举类、抽象类、接口

final

认识final关键字

final 关键字是最终的意思,可以修饰:类、方法、变量。
修饰类:该类被称为最终类,特点是不能被继承了。应用场景:工具类,不需要被继承。
修饰方法:该方法被称为最终方法,特点是不能被重写了。应用场景:在抽象类会讲。
修饰变量:该变量有且仅能被赋值一次。应用场景:局部变量修饰一些固定的值,比如圆周率。
成员变量,比如作为系统的配置信息。给实例变量加无意义。

修饰类:

public class FinalDemo1 {
    public static void main(String[] args) { }
}
// 1、final修饰类,类不能被继承。
final class A{
}
// class B extends A{} // 报错
// 应用场景 : 工具类不需要继承,所以加final
// 在比如java的内置math方法是个工具类。查看Ctrl+点击math 查看源码
// 你会发现math类使用了final:  public final class Math{...}
public final class XxxxUtil {
  	public static void xxx(){   ......  }
  	public static boolean xxxx(String email){  ... }  
 	public static String xxxxx(int n){  ... }
    ...
}

修饰方法:

public class FinalDemo1 {
    public static void main(String[] args) { }
}

//  final修饰方法,方法不能被重写。应用场景会在抽象类中讲。
class C{
    public final void show(){
        System.out.println("show方法被执行");
    }
}
class D extends C{
//     @Override
//     public void show(){
//         System.out.println("show方法被执行");
//     }
}

修饰变量

我们知道变量分为很多种

  1. 成员变量:
    • 静态成员变量
    • 实例成员变量
  2. 局部变量
    下面对每种变量都加一个final修饰。
public class FinalDemo1 {
    public static void main(String[] args) {
        // final修饰变量:变量有且仅能被赋值一次。
        // 修饰局部变量
        final double rate = 3.14;
        // rate = 3.16; // 第二次赋值,报错。
		// 局部变量应用
		// 比如这里打八折
        buy(0.8);
    }
    public static void buy(final double z){
        // z = 0.1;  // 第二次赋值,报错。
        System.out.println(z);
    }
}

public class FinalDemo1 {
    // final修饰静态成员变量
    // final修饰静态变量,这个变量今后被称为常量,
    // 常量可以记住一个固定值,并且程序中不能修改了,
    // 应用场景: 通常这个值作为系统的配置信息。
    // 常量的名称,建议全部大写,多个单词用下划线链接
    public static final String SCHOOL_NAME = "黑马程序员";
    public static void main(String[] args) { }
}


public class FinalDemo1 {
    // final修饰实例变量(一般没有意义)
    private final String name = "猪刚鬣";
    
    public static void main(String[] args) {
        FinalDemo1 f = new FinalDemo1();
        System.out.println(f.name);
        // f.name = "高翠兰"; // 第二次赋值,报错。
    }
}

final修饰变量的注意事项

final修饰基本类型的变量,变量存储的数据不能被改变。
final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的。

public class FinalDemo1 {
    public static void main(String[] args) {
        final int a = 20;
        // a = 30; // 第二次赋值,报错。

        final int[] arr = {11, 22, 33, 44};
        // arr = new int[]{33, 55, 44}; // 第二次赋值,报错。修改地址
        arr[2] = 90;// 不报错没有修改地址
    }
}

常量

使用了 static final 修饰的成员变量就被称为常量(上节已经讲过).
作用:常用于记录系统的配置信息。

public class Constant {
   public static final String SCHOOL_NAME  = “传智教育";
} 

注意!常量名的命名规范:建议使用大写英文单词,多个单词使用下划线连接起来。

使用常量记录系统配置信息的优势、执行原理

代码可读性更好,可维护性也更好。
程序编译后,常量会被“宏替换”:出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。
运行下面代码,在out文件夹下查看编译后的代码。

public class Constant {
    public static final String SYSTEM_NAME = "黑马程序员智能家居AI系统";
}
public class FinalDemo2 {
    public static void main(String[] args) {
        // 目标:详解常量。
        System.out.println(Constant.SYSTEM_NAME);
        System.out.println(Constant.SYSTEM_NAME);
        System.out.println(Constant.SYSTEM_NAME);
        System.out.println(Constant.SYSTEM_NAME);
        System.out.println(Constant.SYSTEM_NAME);
        System.out.println(Constant.SYSTEM_NAME);
    }
}

out文件夹下查看编译后的代码如下:
常量会被“宏替换”

package com.itheima.finaldemo;

public class FinalDemo2 {
    public FinalDemo2() {
    }
    public static void main(String[] args) {
        System.out.println("黑马程序员智能家居AI系统");
        System.out.println("黑马程序员智能家居AI系统");
        System.out.println("黑马程序员智能家居AI系统");
        System.out.println("黑马程序员智能家居AI系统");
        System.out.println("黑马程序员智能家居AI系统");
        System.out.println("黑马程序员智能家居AI系统");
    }
}

单例类

单例类属于设计模式这门课,设计模式架构师学的技术。
我们面试或者看底层源码可能会有一些知识涉及到设计模式。

什么是设计模式?

一个问题通常有n种解法,其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。简而言之:设计模式是具体问题的最优解决方案。
设计模式有20多种,对应20多种软件开发中会遇到的问题。

关于设计模式,主要学什么?

  1. 解决什么问题。
  2. 怎么写。

单例模式解决了什么问题 ?或者说他是干什么的。
确保某个类只能创建一个对象。

单例怎么写?

  1. 把类的构造器私有;
  2. 定义一个静态变量存储类的一个对象;
  3. 提供一个静态方法返回对象。
// 设计成单例设计模式
public class A {
    // 2、定义一个静态变量,用于存储本类的一个唯一对象。
    // 第三步可以用下面的这个代替。
    // public static final A a = new A();
    private static A a = new A();

    // 1、私有化构造器: 确保单例类对外不能创建太多对象,单例才有可能性。
    private A() {
    }

    // 3、提供一个公开的静态方法,返回这个类的唯一对象。
    public static A getInstance() {
        return a;
    }
}
package com.itheima.singleinstance;

public class Test {
    public static void main(String[] args) {
        // 目标:设计单例类。
        A a1 = A.getInstance();
        A a2 = A.getInstance();
        System.out.println(a1); // com.itheima.singleinstance.A@404b9385
        System.out.println(a2); // com.itheima.singleinstance.A@404b9385
        System.out.println(a1 == a2);// true

        System.out.println("============================");

        B b1 = B.getInstance();
        B b2 = B.getInstance();
        System.out.println(b1);
        System.out.println(b2);
        System.out.println(b1 == b2);
    }
}

饿汉式单例的特点是什么?

在获取类的对象时,对象已经创建好了。

单例有啥应用场景,有啥好处?

  • 任务管理器对象、获取运行时对象。
  • 在这些业务场景下,使用单例模式,可以避免浪费内存。

懒汉式单例类。

用对象时,才开始创建对象。

  1. 懒汉单例模式怎么写?
    1. 把构造器私有。
    2. 定义一个类变量用于存储对象。
    3. 提供一个类方法,保证返回的是同一个对象。
// 懒汉式单例类。
public class B {
    // 2、私有化静态变量
    private static B b;
    // 1、私有化构造器
    private B() { }
    // 3、提供静态方法返回对象: 真正需要对象的时候才开始创建对象。
    public static B getInstance() {
        if (b == null) {
            // 第一次拿对象时,会创建对象,给静态变量b记住。
            b = new B();
        }
        return b;
    }
}

public class Test {
    public static void main(String[] args) {
        B b1 = B.getInstance();
        B b2 = B.getInstance();
        System.out.println(b1);// com.itheima.singleinstance.B@682a0b20
        System.out.println(b2);// com.itheima.singleinstance.B@682a0b20
        System.out.println(b1 == b2);//true
    }
}

枚举类

认识枚举类

枚举是一种特殊类。
枚举类的写法

修饰符 enum 枚举类名{  
	名称1 ,  名称2, ... ; 
    其他成员…
}

特点:
枚举类中的第一行,只能写枚举类的对象名称,且要用逗号隔开。
这些名称,本质是常量,每个常量都记住了枚举类的一个对象。

// 枚举类
public enum A {
    // 枚举类的第一行:只能罗列枚举对象的名称,这些名称本质是常量。
    X, Y, Z;
}
// 这是
public class Test {
    public static void main(String[] args) {
        // 目标:认识枚举类,搞清楚其本质特点。
        A a1 = A.X;
        System.out.println(a1);

        A a2 = A.Y;
        System.out.println(a2);

		// 编译器为枚举类新增了几个方法
        System.out.println(a1.name()); // X
        System.out.println(a2.name()); // Y
        System.out.println(a1.ordinal()); // 索引  0
        System.out.println(a2.ordinal()); // 索引  1
    }
}

我们来看一下public enum A{ }的编译后的代码

Compiled from “A.java"
public final class A extends java.lang.Enum<A> {
    public static final A X = new A();
    public static final A Y = new A();
    public static final A Z = new A();
	public static A[] values();
	public static A valueOf(java.lang.String);
}

枚举都是最终类,不可以被继承,枚举类都是继承java.lang.Enum类的。
枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量会记住枚举类的一个对象。
枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象。
编译器为枚举类新增了几个方法。

枚举类的常见应用场景

先使用变量写:

public class Constant {
    public static final int UP = 0; // 上
    public static final int DOWN = 1; // 下
    public static final int LEFT = 2; // 左
    public static final int RIGHT = 3; // 右
}

public class Test2 {
    public static void main(String[] args) {
        // 需求:模拟上下左右移动图片。
        // 第一种是常量做信息标志和分类: 但参数值不受约束。
        // 参数值可以传入0,1,110。
        move(Constant.UP);
    }
    public static void move(int direction){
        // 根据这个方向做移动:上下左右。
        switch (direction){
            case Constant.UP :
                System.out.println("向上移动");
                break;
            case Constant.DOWN :
                System.out.println("向下移动");
                break;
            case Constant.LEFT :
                System.out.println("向左移动");
                break;
            case Constant.RIGHT :
                System.out.println("向右移动");
                break;
            default:
                System.out.println("输入有误");
        }
    }
}

使用枚举类写。

public enum Direction {
    UP, DOWN, LEFT, RIGHT;
}
public class Test2 {
    public static void main(String[] args) {
        // 第二种是枚举做信息标志和分类: 参数值受枚举类约束。
        move2(Direction.DOWN);
    }
    public static void move2(Direction direction){
        // 根据这个方向做移动:上下左右。
        switch (direction){
            case UP:
                System.out.println("向上移动");
                break;
            case DOWN :
                System.out.println("向下移动");
                break;
            case LEFT :
                System.out.println("向左移动");
                break;
            case RIGHT :
                System.out.println("向右移动");
                break;
        }
    }
}

抽象类

认识抽象类

在Java中有一个关键字叫:abstract,它就是抽象的意思,可以用它修饰类、成员方法。

  • abstract修饰类,这个类就是抽象类。
  • abstract修饰方法,这个方法就是抽象方法。

语法

修饰符 abstract class 类名{ 
	// 抽象方法:必须abstract修饰,只有方法签名,不能有方法体
	修饰符 abstract 返回值类型 方法名称(形参列表)}

抽象类的注意事项、特点

  • 抽象类中不一定要有抽象方法,有抽象方法的类必须是抽象类。
  • 类有的成员:成员变量、方法、构造器,抽象类都可以有。
  • 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
  • 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
// 抽象类
public abstract class A {
	// 抽象类中不一定要有抽象方法,有抽象方法的类必须是抽象类。
	// 抽象类中:成员变量、方法、构造器,抽象方法都可以有
    private String name;
    private int age;

    public A() { System.out.println("A的无参构造器"); }

    public A(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 抽象方法:必须用abstract修饰,没有方法体,只有方法声明。
    public abstract void show();
    public void show1() { System.out.println("show1方法"); }
    //省略getter()和setter()
}

public class B extends A{
// 一个类继承抽象类,必须重写完继承的全部抽象方法,否则这个类也必须定义成抽象类
    @Override
    public void show() {
        System.out.println("B类重写show方法");
    }
}
public class AbstractDemo1 {
    public static void main(String[] args) {
        // 目标:认识抽象类,抽象方法,搞清楚他们的特点。
        // 抽象类的核心特点:有得有失(得到了抽象方法的能力,失去了创建对象的能力)
        // 抽象类不能创建对象(重点)。
        // A a = new A();  // 报错了
        // 抽象类的使命就是被子类继承:就是为了生孩子。
        B b = new B(); // A的无参构造器
        b.setName("张三");
        b.setAge(18);
        System.out.println(b.getName() + "---" + b.getAge());// 张三---18
        b.show(); // B类重写show方法
        b.show1();// show1方法
    }
}

使用抽象类的好处

父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,我们设计这样的抽象类,就是为了更好的支持多态

public abstract class Animal {
    // 每个动物的叫声。
    // 如果这里不写抽象方法cry(),无法使用父类调用子类的方法。
    //  Animal a = new Dog();右侧不能解耦。
    public abstract void cry();
}
public class Dog extends Animal{
    @Override
    public void cry() {
        System.out.println("🐕是汪汪汪的叫~~~");
    }
}
public class Cat extends Animal{
    @Override
    public void cry() {
        System.out.println("🐱是喵喵喵的叫~~~");
    }
}
public class Test {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.cry();// 🐕是汪汪汪的叫~~~
    }
}

模板方法设计模式

  • 提供一个方法作为完成某类功能的模板,模板方法封装了每个实现步骤,但允许子类提供特定步骤的实现。
  • 模板方法设计模式可以:提高代码的复用、并简化子类设计。
    在这里插入图片描述
    写法
    1、定义一个抽象类。
    2、在里面定义2个方法
    • 一个是模板方法:把共同的实现步骤放里面去。
    • 一个是抽象方法:不确定的实现步骤,交给具体的子类来完成。

建议使用final关键字修饰模板方法,为什么?

  • 模板方法是给子类直接使用的,不能被子类重写。
  • 一旦子类重写了模板方法,模板方法就失效了。
public abstract class People {
   // 1、模板方法设计模式。
    public final void write(){
        System.out.println("\t\t\t《我的爸爸》");
        System.out.println("\t我爸爸是一个好人,我特别喜欢他,他对我很好,我来介绍一下:");
        // 2、模板方法知道子类一定要写这个正文,但是每个子类写的信息是不同的,父类定义一个抽象方法
        // 具体的实现交给子类来重写正文。
        writeMain();
        System.out.println("\t我爸爸真好,你有这样的爸爸吗?");
    }
    public abstract void writeMain();
}
public class Student extends People{
    @Override
    public void writeMain() {
        System.out.println("\t我爸爸很牛逼,是个管理者,我开车不用看红绿灯的,有这样的爸爸真好,下辈子还要做他儿子!");
    }
}
public class Teacher extends People {
    @Override
    public void writeMain() {
        System.out.println("\t我爸爸经常让我站在这里别动,他要去买几斤橘子~~柚子我也吃,毕竟是亲生的!");
    }
}
public class Test {
    public static void main(String[] args) {
        // 抽象类的使用场景之二:模板方法设计模式。
        // 学生和老师都要写一篇作文:《我的爸爸》
        //         第一段是一样的:我爸爸是一个好人,我特别喜欢他,他对我很好,我来介绍一下:
        //         第二段是不一样:老师和学生各自写各自的。
        //         第三段是一样的:我爸爸真好,你有这样的爸爸吗?
        // 解决:抽出一个父类。父类中还抽取一个模板方法给子类直接用。
        Student s = new Student();
        s.write();
//        			《我的爸爸》
//	我爸爸是一个好人,我特别喜欢他,他对我很好,我来介绍一下:
//	我爸爸很牛逼,是个管理者,我开车不用看红绿灯的,有这样的爸爸真好,下辈子还要做他儿子!
// 我爸爸真好,你有这样的爸爸吗?

        s.writeMain();
//我爸爸很牛逼,是个管理者,我开车不用看红绿灯的,有这样的爸爸真好,下辈子还要做他儿子!
    }
}

接口

什么是接口

Java提供了一个关键字interface定义出接口。

接口语法

public interface 接口名 {
	// 成员变量(常量)
	// 成员方法(抽象方法)
} 

注意:接口不能创建对象。

接口如何实现

接口是用来被类实现(implements)的,实现接口的类称为实现类,一个类可以同时实现多个接口。

修饰符 class 实现类类名 implements 接口1, 接口2, 接口3 , ... {
        // 实现类实现多个接口,必须重写完全部接口的全部抽象方法,
        // 否则实现类需要定义成抽象类。
        // JDK 8之前,接口中只能定义常量和抽象方法。
} 
// 接口:使用interface关键字定义的
public interface A {
    // JDK 8之前,接口中只能定义常量和抽象方法。
    // 1、常量:接口中定义常量可以省略public static final不写,默认会加上去。
    String SCHOOL_NAME = "黑马程序员";
    // public static final String SCHOOL_NAME2 = "黑马程序员";

    // 2、抽象方法: 接口中定义抽象方法可以省略public abstract不写,默认会加上去。
    // public abstract void run();
    void run();
    String go();
}
public interface B {
    void play(); // 玩
}
public class Test {
    public static void main(String[] args) {
        System.out.println(A.SCHOOL_NAME);
        // 注意:接口不能创建对象。
        C c = new C();
        c.run();
        System.out.println(c.go());
        c.play();
    }
}
// C被称为实现类。同时实现了多个接口。
// 实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则这个类必须定义成抽象类
class C implements  B , A{
    @Override
    public void run() {
        System.out.println("C类重写了run方法");
    }
    @Override
    public String go() {
        return "黑马找磊哥!";
    }
    @Override
    public void play() {
        System.out.println("C类重写了play方法");
    }
}

接口的好处

+ 弥补了类单继承的不足,一个类同时可以实现多个接口,使类的角色更多,功能更强大。
+ 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现(更利于程序的解耦合)。
public class Test {
    public static void main(String[] args) {
        // 接口弥补了类单继承的不足,可以让类拥有更多角色,类的功能更强大。
        // 学生是个人,但是学生可能兼职代代驾。所以亲爹是人,干爹是代驾等等。
        People p = new Student();
        Driver d = new Student(); // 多态
        BoyFriend bf = new Student();

        // 接口可以实现面向接口编程,更利于解耦合。
        // 比如说A公司要做一个对学生增删改查的功能,A公司设计一个接口,
        // 这个接口要求删接口 查接口 统计作业接口 
        // 给学生建议接口 集成ai接口。
        // 现在有B,C两个公司去实现这5个接口,我们用哪个呢?
        // A公司 a = new B公司 
        // A公司 a = new C公司 
        // 如上A公司接口不变,只要换个公司就能接着实现功能
        Driver a = new Student();
        Driver a2 = new Teacher();

        BoyFriend b1 = new Student();
        BoyFriend b2 = new Teacher();
    }
}
interface Driver{}
interface BoyFriend{}
class People{}
class Student extends People implements Driver, BoyFriend{}

class Teacher implements Driver, BoyFriend{}

接口的综合小案例

需求

请设计一个班级学生的信息管理模块:学生的数据有:姓名、性别、成绩
功能1:要求打印出全班学生的信息;
功能2:要求打印出全班学生的平均成绩。

注意!以上功能的业务实现是有多套方案的,比如:

  • 第1套方案:能打印出班级全部学生的信息;能打印班级全部学生的平均分。
  • 第2套方案:能打印出班级全部学生的信息(包含男女人数);能打印班级全部学生的平均分(要求是去掉最高分、最低分)。
    要求:系统可以支持灵活的切换这些实现方案。

分析

提供两套业务实现方案,支持灵活切换(解耦合): 面向接口编程。
– 定义一个接口(规范思想):必须完成打印全班学生信息,打印平均分。(ClassDataInter)

  • 定义第一套实现类,实现接口:实现打印学生信息,实现打印平均分。
  • 定义第二套实现类,实现接口:实现打印学生信息(男女人数),实现打印平均分(去掉最高分和最低分)。

在这里插入图片描述

代码

在这里插入图片描述

// Student.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private String name;
    private char sex;
    private double score;
}
public interface ClassDataInter {
    void printAllStudentInfos();
    void printAverageScore();
}
public class ClassDataInterImpl1 implements ClassDataInter{
    private Student[] students; // 记住送来的全班学生对象信息。
    public ClassDataInterImpl1(Student[] students) {
        this.students = students;
    }
    @Override
    public void printAllStudentInfos() {
        System.out.println("全班学生信息如下:");
        for (int i = 0; i < students.length; i++) {
            Student s = students[i];
            System.out.println(s.getName() + " " + s.getSex() + " " + s.getScore());
        }
    }

    @Override
    public void printAverageScore() {
        double sum = 0;
        for (int i = 0; i < students.length; i++) {
            Student s = students[i];
            sum += s.getScore();
        }
        System.out.println("全班平均成绩为:" + sum / students.length);
    }
}
//    -- 定义第二套实现类,实现接口:实现打印学生信息(男女人数),实现打印平均分(去掉最高分和最低分)。
public class ClassDataInterImpl2 implements ClassDataInter{
    private Student[] students; // 记住送来的全班学生对象信息。
    public ClassDataInterImpl2(Student[] students) {
        this.students = students;
    }
    @Override
    public void printAllStudentInfos() {
        System.out.println("学生信息如下:");
        int maleCount = 0; // 男生人数
        for (int i = 0; i < students.length; i++) {
            Student s = students[i];
            System.out.println(s.getName() + " " + s.getSex() + " " + s.getScore());
            if (s.getSex() == '男') {
                maleCount++;
            }
        }
        System.out.println("男学生人数:" + maleCount);
        System.out.println("女学生人数:" + (students.length - maleCount));
    }

    // 实现打印平均分(去掉最高分和最低分)
    @Override
    public void printAverageScore() {
        System.out.println("平均分如下:");
        Student s1 = students[0];
        double sum = s1.getScore();
        double max = s1.getScore();
        double min = s1.getScore();
        for (int i = 1; i < students.length; i++) {
            Student s = students[i];
            sum += s.getScore();

            if(s.getScore() > max) {
                max = s.getScore();
            }

            if(s.getScore() < min) {
                min = s.getScore();
            }
        }
        System.out.println("最高分:" + max);
        System.out.println("最低分:" + min);
        System.out.println("平均分:" + (sum - max - min) / (students.length - 2));
    }
}
public class Test {
    public static void main(String[] args) {
        // 目标:完成接口的小案例。
        // 1、定义学生类,创建学生对象,封装学生数据,才能交给别人处理学生的数据。
        // 2、准备学生数据,目前我们自己造一些测试数据
        Student[] allStudents = new Student[10];
        allStudents[0] = new Student("张三", '男', 100);
        allStudents[1] = new Student("李四", '女', 99);
        allStudents[2] = new Student("王五", '男', 98);
        allStudents[3] = new Student("赵六", '女', 97);
        allStudents[4] = new Student("孙七", '男', 96);
        allStudents[5] = new Student("周八", '女', 95);
        allStudents[6] = new Student("吴九", '男', 94);
        allStudents[7] = new Student("郑十", '女', 93);
        allStudents[8] = new Student("赵敏", '女', 100);
        allStudents[9] = new Student("周芷若", '女', 90);

        // 3、提供两套业务实现方案,支持灵活切换(解耦合): 面向接口编程。
        //    -- 定义一个接口(规范思想):必须完成打印全班学生信息,打印平均分。(ClassDataInter)
        //    -- 定义第一套实现类,实现接口:实现打印学生信息,实现打印平均分。
        //    -- 定义第二套实现类,实现接口:实现打印学生信息(男女人数),实现打印平均分(去掉最高分和最低分)。
        ClassDataInter cdi = new ClassDataInterImpl2(allStudents);
        cdi.printAllStudentInfos();
        cdi.printAverageScore();
    }
}

JDK8新增的三种方法

一般开发不用,这是sun公司为了开发其他功能而设计的。

public interface A {
    // 1、默认方法(普通实例方法):必须加default修饰,
    // 默认会用public修饰。
    // 如何调用? 使用接口的实现类的对象来调用。
    default void go(){
        System.out.println("==go方法执行了===");
        run();
    }

    // 2、私有方法(JDK 9开始才支持的)
    // 私有的实例方法。
    // 如何调用? 使用接口中的其他实例方法来调用它
    private void run(){
        System.out.println("==run方法执行了===");
    }

    // 3、静态方法
    // 默认会用public修饰。
    // 如何调用? 只能使用当前接口名来调用。
    static void show(){
        System.out.println("==show方法执行了===");
    }
}
class AImpl implements A{

}
public class Test {
    public static void main(String[] args) {
        // 目标:搞清楚接口JDK 8新增的三种方法,并理解其好处。
        // 我们知道当我们需要添加一个新接口去实现某个东西,
        // 我们需要修改几十个接口的实现类,
        // 当我们有了这三种就可以直接实现不用修改接口的实现类。
        AImpl a = new AImpl();
        a.go();
        A.show(); // 静态方法'
    }
}

接口的注意事项

1、接口与接口可以多继承:一个接口可以同时继承多个接口[重点]。
2、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现。[了解]
3、一个类继承了父类,又同时实现了接口,如果父类中和接口中有同名的默认方法,实现类会优先用父类的。
4、一个类实现了多个接口,如果多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。

1

public class Test {
    public static void main(String[] args) {  }
}
// 1、接口与接口可以多继承:一个接口可以同时继承多个接口[重点]。
// 类与类: 单继承 一个类只能继承一个直接父类
// 类与接口:多实现,一个类可以同时实现多个接口。
// 接口与接口: 多继承,一个接口可以同时继承多个接口。
interface A {
    void show1();
}
interface B {
    void show2();
}
interface C extends B , A{
    void show3();
}
class D implements C{
    @Override
    public void show3() {
    }
    @Override
    public void show1() {
    }
    @Override
    public void show2() {
    }
}

2

public class Test {
    public static void main(String[] args) {  }
}

// 2、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现。[了解]
interface A1 {
    void show();
}
interface B1 {
    String show();
}
// 下面报错
//interface C1 extends A1, B1 {
//}
//class D1 implements A1, B1{
//}

3

public class Test {
    public static void main(String[] args) {
        Dog d = new Dog();
        d.go();
         // 父类中的 show 方法
        // 父类中的 show 方法
        // 接口中的 A2 show 方法
    }
}

// 3、一个类继承了父类,又同时实现了接口,如果父类中和接口中有同名的方法,实现类会优先用父类的。[了解]
interface A2 {
    default void show(){
        System.out.println("接口中的 A2 show 方法");
    }
}

class Animal{
    public void show(){
        System.out.println("父类中的 show 方法");
    }
}

class Dog extends Animal implements A2 {
    public void go(){
        show(); // 父类的
        super.show(); // 父类的
        A2.super.show(); // A2接口的方法
    }
}

4

public class Test {
    public static void main(String[] args) {
        Dog d = new Dog();
        d.go();
    }
}

// 4、一个类实现了多个接口,如果多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
interface A3 {
    default void show(){
        System.out.println("接口中的 A3 show 方法");
    }
}

interface B3 {
    default void show(){
        System.out.println("接口中的 B3 show 方法");
    }
}

class Dog2 implements A3, B3 {
    @Override
    public void show() {
        A3.super.show(); // A3干爹的
        B3.super.show(); // B3干爹的
    }
}

抽象类和接口的区别

相同点:

  1. 都是抽象形式,都可以有抽象方法,都不能创建对象。
  2. 都是派生子类形式:抽象类是被子类继承使用,接口是被实现类实现。
  3. 一个类继承抽象类,或者实现接口,都必须重写完他们的抽象方法,否则自己要成为抽象类或者报错!
  4. 都能支持的多态,都能够实现解耦合。

不同点:

  1. 抽象类中可以定义类的全部普通成员,接口只能定义常量,抽象方法(JDK8新增的三种方式)
  2. 抽象类只能被类单继承,接口可以被类多实现。
  3. 一个类继承抽象类就不能再继承其他类,一个类实现了接口(还可以继承其他类或者实现其他接口)。
  4. 抽象类体现模板思想:更利于做父类,实现代码的复用性。 最佳实践
  5. 接口更适合做功能的解耦合:解耦合性更强更灵活。 最佳实践

案例

某智能家居系统,可以让用户选择要控制的家用设备(吊灯,电视机,洗衣机,落地窗),并可以对它们进行打开或者关闭操作。
在这里插入图片描述

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        // 目标:面向对象编程实现智能家居控制系统。
        // 角色:设备(吊灯,电视机,洗衣机,落地窗,....)
        // 具备的功能:开和关。
        // 谁控制他们:智能控制系统(单例对象),控制调用设备的开和关。
        // 1、定义设备类:创建设备对象代表家里的设备。
        // 2、准备这些设备对象,放到数组中,代表整个家庭的设备。
        JD[] jds = new JD[4];
        jds[0] = new TV("小米电视", true);
        jds[1] = new WashMachine("美的洗衣机", false);
        jds[2] = new Lamp("欧灯", true);
        jds[3] = new Air("美的空调", false);

        // 3、为每个设备制定一个开个关的功能。定义一个接口,让JD实现开关功能。
        // 4、创建智能控制系统对象,控制设备开和关。
        SmartHomeControl smartHomeControl = SmartHomeControl.getSmartHomeControl();
        // 5、控制电视机。
        // smartHomeControl.control(jds[0]);

        // 6 、提示用户操作,a、展示全部设备的当前情况。b、让用户选择哪一个操作。
        // 打印全部设备的开和关的现状。
        while (true) {
            smartHomeControl.printAllStatus(jds);
            System.out.println("请您选择要控制的设备:");
            Scanner sc = new Scanner(System.in);
            String command = sc.next();
            switch (command){
                case "1":
                    smartHomeControl.control(jds[0]);
                    break;
                case "2":
                    smartHomeControl.control(jds[1]);
                    break;
                case "3":
                    smartHomeControl.control(jds[2]);
                    break;
                case "4":
                    smartHomeControl.control(jds[3]);
                    break;
                case "exit":
                    System.out.println("退出App!");
                    return;
                default:
                    System.out.println("输入有误,请重新输入");
            }
        }
    }
}
public class Air extends JD{
    public Air(String name, boolean status) {
        super(name, status);
    }
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

// 家电
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JD implements Switch{
    private String name;
    // 状态:开或者关。
    private boolean status; // false 默认是关闭。

    @Override
    public void press() {
        // 控制当前设备开和关
        status = !status;
    }
}
// 灯
public class Lamp extends JD{
    public Lamp(String name, boolean status) {
        super(name, status);
    }
}
// 智能控制系统类。
public class SmartHomeControl {
	private static SmartHomeControl smartHomeControl= new SmartHomeControl();

    private SmartHomeControl() {

    }
    public static SmartHomeControl getSmartHomeControl() {
        return smartHomeControl;
    }
    // 多态。
    public void control(JD jd) {
        System.out.println(jd.getName() + "状态目前是:" + (jd.isStatus() ? "开着" : "关闭!"));
        System.out.println("开始您的操作。。。。。");
        jd.press(); // 按下开关。
        System.out.println(jd.getName() + "状态已经是:" + (jd.isStatus() ? "开着" : "关闭!"));
    }

    public void printAllStatus(JD[] jds) {
        // 使用for循环,根据索引遍历每个设备。
        for (int i = 0; i < jds.length; i++) {
            JD jd = jds[i];
            System.out.println((i + 1) + "," + jd.getName() + "状态目前是:" + (jd.isStatus() ? "开着" : "关闭!"));
        }
    }
}

public interface Switch {
	// 这个开关设计到 JD类中是因为方便以后得解耦合。
    void press(); // 按压
}
public class TV extends JD{
    public TV(String name, boolean status) {
        super(name, status);
    }
}
// 洗衣机
public class WashMachine extends JD{
    public WashMachine(String name, boolean status) {
        super(name, status);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2333439.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTTP 压力测试工具autocannon(AI)

简介 autocannon 是一款基于 Node.js 的高性能 HTTP 压力测试工具&#xff0c;适用于评估 Web 服务的并发处理能力和性能瓶颈。 一、工具特点 高性能‌&#xff1a;利用 Node.js 异步非阻塞机制模拟高并发请求‌。‌实时监控‌&#xff1a;测试过程中动态展示请求统计和性能…

my2sql工具恢复误删数据

一、下载my2sql my2sql下载地址https://github.com/liuhr/my2sql/blob/master/releases/centOS_release_7.x/my2sql 二、my2sql工具注意事项 1. binlog格式必须为row&#xff0c;且binlog_row_imagefull 原因&#xff1a;binlog_row_image 参数决定了 binlog 中是否记录完整的…

【AGI-Eval行业动态】OpenAI 语音模型三连发,AI 语音进入“声优”时代

前言&#xff1a;OpenAI又双叒叕搞事情了&#xff01;这次他们带着三款全新语音模型强势来袭&#xff0c;直接让 AI 语音界卷出新高度&#xff01;无论是语音识别的精准度、还是根据文字生成音频的脑洞&#xff0c;这三款模型都堪称“神仙打架”。 如果你还在用老掉牙的语音助手…

蓝桥杯嵌入式十四届模拟一(eeprom)

一.LED 先配置LED的八个引脚为GPIO_OutPut&#xff0c;锁存器PD2也是&#xff0c;然后都设置为起始高电平&#xff0c;生成代码时还要去解决引脚冲突问题 二.按键 按键配置&#xff0c;由原理图按键所对引脚要GPIO_Input 生成代码&#xff0c;在文件夹中添加code文件夹&#…

DevOps与功能安全:Perforce ALM通过ISO 26262合规认证,简化安全关键系统开发流程

本文来源perforce.com&#xff0c;由Perforce中国授权合作伙伴、DevSecOps解决方案提供商-龙智翻译整理。 近日&#xff0c;Perforce ALM&#xff08;原Helix ALM&#xff09;通过了国际权威认证机构 TV SD的ISO 26262功能安全流程认证&#xff01;该认证涵盖Perforce ALM解决方…

【图片识别改名工具】如何识别图片中文字内容,并根据文字对图片批量重命名批量改名,基于WPF和腾讯OCR的完整实现

​​办公场景​​ ​​批量处理图片文件​​:用户有一批图片文件,图片中包含文字信息(如编号、日期、名称等),需要根据图片中的文字内容对图片进行重命名。​​自动化办公​​:在办公场景中,用户需要将图片文件按内容分类或归档,手动重命名效率低下,自动化工具可以大幅…

Dify+DeepSeek能做出什么来?快速构建可扩展的 AI 应用

将 Dify&#xff08;开源 LLM 应用开发平台&#xff09;与 DeepSeek&#xff08;深度求索公司的高性能大模型&#xff0c;如 DeepSeek-R1 或 DeepSeek-Lite&#xff09;结合使用&#xff0c;可以充分发挥两者的优势&#xff0c;快速构建高效、灵活且可扩展的 AI 应用。以下是具…

【深度学习】Ubuntu 服务器配置开源项目FIGRET(PyTorch、torch-scatter、torch-sparse、Gurobi 安装)

开源项目网址&#xff1a;https://github.com/FIGRET/figret 该项目在SIGCOMM2024发表&#xff0c;用深度学习方法处理流量工程中的突发问题 1. 创建新的 Conda 环境 使用国内镜像源创建环境​ conda create -n figret python3.8.0 --override-channels -c https://mirrors.…

浅析Centos7安装Oracle12数据库

Linux下的Oracle数据库实在是太难安装了&#xff0c;事贼多&#xff0c;我都怀疑能安装成功是不是运气的成分更高一些。这里虚拟机是VMware Workstation 15.5&#xff0c;操作系统是Centos7&#xff0c;Oracle版本是Oracle Database 12c Enterprise Edition Release 12.1.0.2.0…

代码随想录算法训练营Day27 | Leetcode 56. 合并区间、738.单调递增的数字、968.监控二叉树

代码随想录算法训练营Day27 | Leetcode 56.合并区间、738.单调递增的数字、968.监控二叉树 一、合并区间 相关题目&#xff1a;Leetcode56 文档讲解&#xff1a;Leetcode56 视频讲解&#xff1a;Leetcode56 1. Leetcode56. 合并区间 以数组 intervals 表示若干个区间的集合&am…

ESP32S3 链接到 WiFi

以下是关于如何让 ESP32S3 连接到 WiFi 的完整流程和代码示例&#xff1a; ESP32S3 链接到 WiFi 1. 设置工作模式 ESP32 可以工作在两种模式下&#xff1a; Station (STA) 模式&#xff1a;作为无线终端连接到无线接入点&#xff08;AP&#xff09;&#xff0c;类似于手机或…

2024年博客之星的省域空间分布展示-以全网Top300为例

目录 前言 一、2024博客之星 1、所有排名数据 2、空间属性管理 二、数据抓取与处理 1、相关业务表的设计 2、数据抓取处理 3、空间查询分析实践 三、数据成果挖掘 1、省域分布解读 2、技术开发活跃 四、总结 前言 2024年博客之星的评选活动已经过去了一个月&#xf…

蓝桥赛前复习2:一维差分二维差分

一维差分 问题描述 给定一个长度为 nn 的序列 aa。 再给定 mm 组操作&#xff0c;每次操作给定 33 个正整数 l,r,dl,r,d&#xff0c;表示对 al∼ral∼r​ 中的所有数增加 dd。 最终输出操作结束后的序列 aa。 Update&#xff1a;由于评测机过快&#xff0c;n,mn,m 于 2024…

算法---子序列[动态规划解决](最长递增子序列)

最长递增子序列 子序列包含子数组&#xff01; 说白了&#xff0c;要用到双层循环&#xff01; 用双层循环中的dp[i]和dp[j]把所有子序列情况考虑到位 class Solution { public:int lengthOfLIS(vector<int>& nums) {vector<int> dp(nums.size(),1);for(int i …

100道C#高频经典面试题带解析答案——全面C#知识点总结

100道C#高频经典面试题带解析答案 以下是100道C#高频经典面试题及其详细解析&#xff0c;涵盖基础语法、面向对象编程、集合、异步编程、LINQ等多个方面&#xff0c;旨在帮助初学者和有经验的开发者全面准备C#相关面试。 &#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSD…

MQTT的构成、使用场景、工作原理介绍

一、MQTT内容简介 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级、基于发布-订阅模式的消息传输协议【适用于资源受限的设备和低带宽、高延迟或不稳定的网络环境】它在物联网应用中广受欢迎&#xff0c;能够实现传感器、执行器和其它设备之间的…

Vanna + qwq32b 实现 text2SQL

Vanna 是一个开源的 Text-2-SQL 框架&#xff0c;主要用于通过自然语言生成 SQL 查询&#xff0c;它基于 RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09;技术。Vanna 的核心功能是通过训练一个模型&#xff08;基于数据库的元数据和用户提…

电脑知识 | TCP通俗易懂详解 <一>

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f68d;什么是TCP/TCP协议 三、&#x1f9cd;‍♂为什么TCP可靠 1.&#x1f970;关于可靠 2.&#x1f920;哪里可靠 3.&#x1f393;️图片的三次握手&#xff0c;四次挥手 4.&#x1f4da;️知识点总结 四、&…

精品推荐-最新大模型MCP核心架构及最佳实践资料合集(18份).zip

精品推荐-最新大模型MCP核心架构及最佳实践资料合集&#xff0c;共18份。 1、2025年程序员必学技能&#xff1a;大模型MCP核心技术.pdf 2、MCP 架构设计剖析&#xff1a;从 Service Mesh 演进到 Agentic Mesh.pdf 3、MCP 架构设计深度剖析&#xff1a;使用 Spring AI MCP 四步…

Linux 线程:从零构建多线程应用:系统化解析线程API与底层设计逻辑

线程 线程的概述 在之前&#xff0c;我们常把进程定义为 程序执行的实例&#xff0c;实际不然&#xff0c;进程实际上只是维护应用程序的各种资源&#xff0c;并不执行什么。真正执行具体任务的是线程。 那为什么之前直接执行a.out的时候&#xff0c;没有这种感受呢&#xf…