day07-面向对象高级一

Java面向对象高级一

—————————————————————————————————————————————————————————————————————

1.final

  • final关键字:final是最终的意思,可以修饰类、方法、变量;
    修饰类:该类被称为最终类,特点是不能被继承;
    修饰方法:该方法被成为最终方法,特点是不能被重写了;
    修饰变量:该变量有且仅能被赋值一次。

  • final修饰静态变量,这个变量今后被称为常量,可以记住一个固定值,
    并且程序中不能修改了,通常这个值作为系统的配置信息;
    常量的名称,建议全部大写,多个单词用下划线链接。

  • 变量有哪些?
    1、成员变量:
    ① 静态成员变量
    ② 实例成员变量
    2、③ 局部变量

  • 定义和赋值的问题?
    ① final修饰静态成员变量,必须要先赋值。
    ② final修饰实例变量(一般没有意义),一定要先赋值。可以在定义时赋值,也可以在构造器或实例初始化块中赋值,但必须确保在构造过程结束之前(即创建对象之前)完成赋值,一旦赋值后不可再修改。
    ③ final修饰局部变量,可以先只定义,后面再赋值,但是只能赋值一次;必须在定义时或者同一个方法内的后续代码中明确赋值,只能赋值一次。
    不能先定义后赋值,除非是在定义的同时进行初始化,或者确保在同一作用域内有确定的赋值操作。

  • final修饰变量注意:
    final修饰基本类型的变量,变量存储的数据不能被改变;
    final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的。(数组,类,接口,String类型……)

public class FinalDemo1 {
    // ① final修饰静态成员变量,必须要先赋值。
    public static final String SCHOOL_NAME = "XXX大学";

    // ② final修饰实例变量(一般没有意义),一定要先赋值。
    private final String name = "林青霞";

    public static void main(String[] args) {
        // ③ final修饰局部变量
        final double rate ;
        rate = 3.14;
//      rate = 3.1415;   // 第二次赋值,报错
        pay(8);

        FinalDemo1 fd = new FinalDemo1();
        System.out.println(fd.name);  // 林青霞
//      f.name = "林志玲";  // 第二次赋值,报错

        System.out.println("-------------------");
        final int [] arr = new int []{11,22,33,44};
//      arr = new int []{22,33,44,55}; // 第二次赋值,报错
        // final修饰引用类型的变量,变量存储的地址不能被改变,
        // 但地址所指向对象的内容是可以被改变的
        arr[2]= 55;
    }

    // 传入的形参变量值不能被修改,以防止恶意修改
    public static void pay(final double z){
//        z = 9;    // 报错
        System.out.println(z);
    }
}


// 1. final修饰类:表示该类不能被继承
final class A{
}
//class B extends A{ } // 报错

// 2. final修饰方法:表示该方法不能被重写
class C{
    final void show(){
        System.out.println("C.m1()");
    }
}

class D extends C{
//    @Override // 报错,不能重写final方法
    //void show(){
    //    System.out.println("D.m1()");
    // }
}

2.单例类

  • 单例类:(是Java的一种软件设计模式)

  • 什么是设计模式?
    一个问题通常有n种解法,其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。

  • 关于设计模式,主要学什么?
    1、解决什么问题?
    2、怎么写?

  • 单例设计模式:
    作用:确保每个类只能创建一个对象。
    例如computer上的任务管理器,在Java中是一个JFrame对象(窗口对象),这个任务管理器在电脑上应该只有一个对象,这种就可以设计成单例类,对外就只能创建一个对象。
    还有Java的虚拟机对象,也只创建一个。

  • 写法、实现步骤:
    ① 把类的构造器私有;
    ② 定义一个静态变量(类变量),用于存储对象;
    ③ 定义一个静态方法(类方法),保证返回的是同一个对象。

  • 饿汉式单例:拿对象时,对象早就创建好了。

public class A {
    private static final A a = new A(); 
    private A(){
    }
    public static A getInstance(){
        return a;
    }
}
  • 懒汉式单例:拿对象时,才开始创建对象。
public class B {
    private static B b; 
    private B(){
    }
    public static B getInstance(){
        if(b == null){
            b = new B();
        }
        return b;
    }
}
public class Test {
    public static void main(String[] args) {
        // 目标:设计单例类
        A a1 = A.getInstance(); // 获取唯一对象
        A a2 = A.getInstance();
        System.out.println(a1);  // 地址:sun.superstring.singleinstance.A@2f4d3709
        System.out.println(a2);  // 地址:sun.superstring.singleinstance.A@2f4d3709
        System.out.println(a1 == a2);  // true

        // 这样写不专业,可以随时将A的唯一对象干掉,所以需要写一个get方法,用于返回A类的唯一对象
//        A.a = null;
//        System.out.println(A.a);

        System.out.println("-------------------------");
        B b1 = B.getInstance();
        B b2 = B.getInstance();
        System.out.println(b1);  // 地址:sun.superstring.singleinstance.B@1d81eb93
        System.out.println(b2);  // 地址:sun.superstring.singleinstance.B@1d81eb93
        System.out.println(b1 == b2);  // true
    }
}

// 设计成单例设计模式:饿汉式单例
public class A {
    // 2.定义一个静态变量,用于记住本类的一个唯一对象
    // public static A a = new A();  容易被外部修改
    // private static A a = new A();  只能内部改,不暴露出去
    private static final A a = new A();  // 推荐
    // 加上private,防止外部修改;加上final,防止在内部修改

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

    // 3.定义一个静态方法,用于获取本类的唯一对象
    public static A getInstance(){
        return a;
    }
}

// 设计成单例设计模式:懒汉式单例
public class B {
    // 2.定义一个静态变量,用于存储对象
    private static B b;   // null

    // 1.私有化构造方法:确保单例类对外不能创建太多对象,单例才有可能性
    private B(){
    }

    // 3.提供一个静态方法返回对象,真正需要对象的时候才开始创建对象
    public static B getInstance(){
        if(b == null){
            b = new B();
        }
        return b;
    }
}

3.枚举类

  • 枚举类:是一种特殊类

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

修饰符 enum 枚举类名{
    名称1,名称2,……;
    其他成员……;
}
  • 枚举类的特点:
    枚举类都是最终类,不可以被继承,枚举类都是继承java.lang.Enum类的;
    枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量都会记住枚举类的一个对象;
    枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象;
    所以枚举类是多例模式;
    编译器为枚举对象新增了几个方法。枚举类对象名.ordinal():拿到枚举类对象的索引(指代第几个枚举类对象,从0开始)

  • 多例模式:(对外不能创建对象,又不能被别人继承,里面只有X、Y、Z三个对象,单例是只有一个对象,多例是只能有多个对象)
    单例类也可以理解为枚举对象只有一个;

// 枚举类
public enum A {
    // 枚举类的第一行:只能罗列枚举对象的名称,这些对象本质是常量
    X, Y, Z;
}

public class Test {
    public static void main(String[] args) {
        // 目标:认识枚举类,搞清楚其本质特点。
        A a1 = A.X;
        System.out.println(a1);   // X

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

        System.out.println(a1.name());   // X
        System.out.println(a2.name());   // Y
        System.out.println(a1.ordinal());  // 可以拿枚举对象的索引 0
        System.out.println(a2.ordinal()); // 索引 1
    }
}
  • 枚举类的常见应用场景:
    枚举类很适合做信息分类和标志。
    数字迷阵的游戏,控制某个图块上下左右的移动。这些方向的信号可以定义成枚举类;
    或者是某两个数相除,最终结果保留两位小数,其他位数是:直接舍弃down,进一位up,还是四舍五入half_up
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 enum Direction {
    UP, DOWN, LEFT, RIGHT;
}

public class Test2 {
    public static void main(String[] args) {
        // 目标:掌握枚举类的应用场景:做信息的分类和标志
        // 需求:模拟上下左右移动图片
        // 第一种是常量做信息标志和分类:但参数值不受约束
        move(Constant.UP);  // direction = 0     向上移动

        // 第二种是枚举做信息标志和分类:参数值受约束
        move(Direction.DOWN);  // direction = DOWN    向下移动
    }

    // 第二种匹配:使用枚举型变量作为信号标志
    public static void move(Direction direction){
        System.out.println("direction = " + direction);
        // 根据这个方向做移动:上下左右
        switch (direction){  // switch支持省略枚举对象的前缀:Direction,可以自动去找对应的枚举对象
            case UP:
                System.out.println("向上移动");
                break;
            case DOWN:
                System.out.println("向下移动");
                break;
            case Direction.LEFT:
                System.out.println("向左移动");
                break;
            case Direction.RIGHT:
                System.out.println("向右移动");
        }
    }

    // 第一种匹配:使用int型变量作为信号标志
    public static void move(int direction){
        System.out.println("direction = " + direction);
        // 根据这个方向做移动:上下左右
        switch (direction){
            case 0:
                System.out.println("向上移动");
                break;
            case 1:
                System.out.println("向下移动");
                break;
            case 2:
                System.out.println("向左移动");
                break;
            case 3:
                System.out.println("向右移动");
                break;
            default:
                System.out.println("输入有误:");
        }
    }
}

4.抽象类

  • 抽象类:
    在Java中有一个关键字叫:abstract,就是抽象的意思,用它可以修饰类、成员方法。
    abstract修饰类,就是抽象类;
    abstract修饰方法,就是抽象方法;
修饰符 abstract class 类名{
    修饰符 abstract 返回值类型  方法名称(形参列表);
}

public abstract class A{
    // 抽象方法:必须abstract修饰,只有方法签名,不能有方法体
    public abstract void test(int a);
}
  • 抽象类的注意事项,特点:
    抽象类中不一定要有抽象方法,但是有抽象方法的类必须是抽象类;
    类有的成员(成员变量,成员方法,构造器),抽象类都可以有;
    抽象类最主要的特点是——抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现;
    一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
public class AbstractDemo1 {
    public static void main(String[] args) {
//        A a = new A();  // 错误,抽象类不能创建对象

        // 抽象类的使命就是被子类继承:就是为了生孩子
        B b = new B();
        b.setName("小明");
        b.setAge(18);
        System.out.println(b.getName() + ", " + b.getAge());  // 小明 18
        b.show();  // B类重写了run方法
        b.show1();  // show1方法
    }
}


// 抽象类,可以不写抽象方法
public abstract class A {
    private String name;  // 实例变量
    private int age;   // 实例变量

    // 无参构造器
    public 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方法");
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

// 一个类继承抽象类,必须重写抽象类的所有抽象方法,否则这个类也必须定义成抽象类
public class B extends A{
    // 重写抽象类的run方法,B就成为了一个具体的类;否则B必须定义成抽象类
    @Override
    public void show() {
        System.out.println("B类重写了run方法");
    }
}
  • 抽象类不能创建对象的原因:
    假设抽象类可以创建对象,那里面的抽象方法怎么执行,抽象方法连方法体都没有(不能抱着抽象类里面不写抽象方法的掉以轻心的心态)

  • 抽象的本质:不应该有具体的对象
    一个类继承抽象类,必须重写抽象类的所有抽象方法,否则这个类也必须定义成抽象类;
    抽象类的使命就是被子类继承:就是为了生孩子

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

  • 父类定义的方法,子类不会用,而是用子类重写的方法,所以父类的方法体定义出来是没有用的,所以这些代码是多余的。
    抽象方法,子类是一定要重写的。但是不写成抽象方法也可以实现多态,但是写成抽象方法是一种最佳的实践。
    (虽然abstract不是必须的技术,但是这是一种最优雅的写法,是最佳实践)

public class Test {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.shout();  // 汪汪汪~~~
    }
}

public abstract class Animal {
    // 每个动物的叫声
    public abstract void shout();
}

public class Cat extends Animal{
    @Override
    public void shout() {
        System.out.println("猫是喵喵喵~~~");
    }
}

public class Dog extends Animal{
    @Override
    public void shout() {
        System.out.println("汪汪汪~~~");
    }
}
  • 抽象类模板方法设计模式:
    解决了什么问题?
    提供了一个方法作为完成某类功能的模板,模板方法封装了每个实现步骤,但允许子类提供特定步骤的实现。
    模板方法设计模式可以提高代码的复用,并简化子类设计。

  • 怎么写?
    1、定义一个抽象类
    2、在里面定义2个方法
    一个是模板方法:把共同的实现步骤放进去
    一个是抽象方法:不确定的实现步骤,交给具体的子类来完成。

  • 多学一招:
    建议使用final关键字修饰模板方法:这样子类就不能重写模板方法了,因为模板方法一旦重写,就失效了。
    它是给子类使用的,不能被子类重写;
    一旦子类重写了模板方法,模板方法就失效了。

public class Test {
    public static void main(String[] args) {
        // 目标:理解抽象类的使用场景之二:模板方法设计模式
        // 学生和老师都要写一篇作文:《我的爸爸》
        //           第一段是一样的:我爸爸是一个好人,我特别喜欢他,他对我很好,我来介绍一下:
        //           第二段是不一样的:老师和学生各自写各自的
        //           第三段是一样的:我爸爸真好,你有这样的爸爸吗?
        //   解决:抽出一个父类。父类中还抽取一个模板方法给子类直接用。

        Teacher teacher = new Teacher();  
        // People teacher = new Teacher(); 这两个是一个意思

        teacher.write();
        Student student = new Student();
        student.write();
    }
}

// 抽象类:老师和学生都是People类
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我爸爸真好,你有这样的爸爸吗?");
    }

    // 抽象方法 —— writeMain(); 
    public abstract void writeMain();
}

public class Teacher extends People{

    @Override
    public void writeMain(){
        System.out.println("\t我爸爸经常让我站在这里别动,他要去买几斤橘子~~柚子我也吃,毕竟是我爸爸买的,是亲生的!!!");
    }

//    public void write(){
//        System.out.println("\t\t\t《我的爸爸》");
//        System.out.println("\t我爸爸是一个好人,我特别喜欢他,他对我很好,我来介绍一下:");
//        System.out.println("\t我爸爸经常让我站在这里别动,他要去买几斤橘子~~柚子我也吃,毕竟是我爸爸买的,是亲生的!!!");
//        System.out.println("\t我爸爸真好,你有这样的爸爸吗?");
//    }
}

public class Student extends People{
    @Override
    public void writeMain(){
        System.out.println("\t我爸爸很厉害,熟知君子六艺,是一个言行合一的君子,我非常喜欢他,他就是我以后找男朋友的标准!!");
    }

//    public void write() {
//        System.out.println("\t\t\t《我的爸爸》");
//        System.out.println("\t我爸爸是一个好人,我特别喜欢他,他对我很好,我来介绍一下:");
//        System.out.println("\t我爸爸很厉害,熟知君子六艺,是一个言行合一的君子,我非常喜欢他,他就是我以后找男朋友的标准!!");
//        System.out.println("\t我爸爸真好,你有这样的爸爸吗?");
//    }
}

5.接口

  • 接口:Java提供了一个关键字interface定义接口;
    JDK 8之前,接口中只能写常量和抽象方法——传统接口(本课学习内容就是传统接口)
public interface 接口名{
           // 成员变量(常量)
           // 成员方法(抽象方法)
}
  • 接口的特点:
    接口中的抽象方法都是公开的;(写不写public都是公开的)
    接口不能创建对象;
    接口是用来被类实现(implements)的,实现接口的类称为实现类,一个类可以实现多个接口。(接口可以理解为“干爹”,亲爸继承只能有一个,但是干爹可以有多个。)

  • 接口的好处:
    弥补了类单继承的不足,一个类可以同时实现多个接口,使得类的角色更多,功能更强大;
    让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现(更利于程序的解耦合)。

// 接口:使用interface关键字定义的
public interface A {
    // JDK 8之前,接口中只能写常量和抽象方法——传统接口
    // 1、常量:接口中定义常量可以省略 public static final 不写,默认会加上。
    String SCHOOL_NAME = "黑马程序员";
    public static final String SCHOOL_NAME2 = "黑马";  
    // 前面的 public static final 可以省略不写

    // 2、抽象方法:接口中定义的方法可以省略 public abstract 不写,默认会加上。
    //    public abstract void study();
    void study();
    String go();
}

public interface B {
    void play();  // 玩
}

// C 被称为实现类。同时实现了多个接口
// 要么把C定义成一个抽象类 abstract修饰
// 要么C 全部重写A和B的全部抽象方法
class C implements A, B {
    @Override
    public void study() {
        System.out.println("C类重写了study方法");
    }

    @Override
    public String go() {
        return "黑马找磊哥";
    }

    @Override
    public void play() {
        System.out.println("C类重写了play方法");
    }
}

public class Test {
    public static void main(String[] args) {
        // 目标:认识接口,搞清楚几口的特点,基本使用
        System.out.println(A.SCHOOL_NAME);   // 黑马程序员

        // 注意:接口不能创建对象
        // 接口是用来被类实现的
        C c = new C();
        c.study();   // C类重写了study方法
        System.out.println(c.go());   // 黑马找磊哥
        c.play();  // C类重写了play方法
    }
}
  • 案例:
    A公司设计一个A接口(多个方法名,给出规范)
    让B、C公司分别实现A接口(写一套实现类),看看B、C公司的实现类怎么样,然后哪个实现类好用直接用哪个公司的实现类。

  • 案例收获:
    1.接口定义的抽象方法不能添加形参
    2.传入参数是用在接口的实现类中定义私有的数组,然后定义实现类对象获取数组信息;
    3.接口实现类的命名规则是在接口名后加Impl,作为实现类的类名。

public class Test {
    public static void main(String[] args) {
        // 目标:完成接口的小案例。
        // 题目:
        /* 1.请设计一个班级学生的信息管理模块:学生的数据有:姓名、性别、成绩
           2.功能一:要求打印出全班学生的信息;
             功能二:要求打印出全班学生的平均成绩
           注意:
           第一套方案:能打印出班级全部学生的信息;能打印班级全部学生的平均分
           第二套方案:能打印出班级全部学生的信息(包含男女人数)
                     能打印班级全部学生的平均分(要求是去掉最高分、最低分)
           要求:系统可以支持灵活的切换这些实现方案 */

        // 1.定义学生类,创建学生对象,封装学生信息,才能交给别人处理
        // 2.准备学生数据,自己造一些测试数据

        Student[] students = new Student[10];
        students[0] = new Student("张三", '男', 100);
        students[1] = new Student("李四", '男', 80);
        students[2] = new Student("王五", '男', 80);
        students[3] = new Student("赵六", '女', 30);
        students[4] = new Student("孙七", '女', 90);
        students[5] = new Student("钱八", '女', 80);
        students[6] = new Student("钱九", '女', 77);
        students[7] = new Student("钱十", '女', 90);
        students[8] = new Student("钱十一", '女', 20);
        students[9] = new Student("钱十二", '女', 100);

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

        ClassDateInter cdi = new ClassDataInterImpl2(students);
        cdi.printAllStudentsInfos();
        cdi.printAverageScore();
    }
}

public class Student {
    private String name;
    private char sex;
    private double score;

    public Student() {}

    public Student(String name, char sex, double score) {
        this.name = name;
        this.sex = sex;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
}

public interface ClassDateInter {
    //功能一:要求打印出全班学生的信息
    void printAllStudentsInfos();

    //功能二:要求打印出全班学生的平均成绩
    void printAverageScore();
}

public class ClassDataInterImpl1 implements ClassDateInter {
    private Student [] students; // 用来记住送来的全班学生信息数组。

    // 用有参构造器来接数据
    public ClassDataInterImpl1(Student[] students) {
        this.students = students;
    }

    @Override
    public void printAllStudentsInfos() {
        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 ClassDateInter {
    private Student [] students; // 用来记住送来的全班学生信息数组。

    // 用有参构造器来接数据
    public ClassDataInterImpl2(Student[] students) {
        this.students = students;
    }

    @Override
    public void printAllStudentsInfos() {
        System.out.println("学生信息如下:");
        int menNum = 0;
        for(int i=0; i<students.length; i++){
            Student s = students[i];
            if(s.getSex() == '男'){
                menNum++;
            }
            System.out.println(s.getName() + " " + s.getSex() + " " + s.getScore());
        }
        System.out.println("男同学人数是:" + menNum + " ,女同学的人数是:" + (students.length - menNum));
    }

    @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 + ",去掉一个最低分:" + min + "\n全班同学平均成绩是:" + (sum - max - min)/ (students.length - 2));
    }
}

  • JDK8开始,接口新增了三种形式的方法:默认方法,私有方法,类方法(静态方法)
    ① 默认方法(普通实例方法):必须加default修饰
    默认会用public修饰
    如何调用? 使用接口的实现类的对象来调用。
    ② 私有方法(JDK9开始才支持的)
    私有的实例方法。
    如何调用? 只能在接口中,使用接口中的其他实例方法来调用
    ③ 静态方法
    默认会用public修饰
    如何调用? 只能使用当前接口名来调用。
    不允许用实现类来调用当前接口的静态方法。

  • java的接口为什么新增三种形式的方法?
    我们自己写的可能性几乎为0,sun公司自己用的比较多。
    增强了接口的能力,更便于项目的扩展和维护

  • 其实这个在项目中基本用不到,现在三层架构,直接controller自动注入接口的bean,然后在impl实现接口的功能,不会用到这些新增的方法。——可以跳过了

public class Test {
    public static void main(String[] args) {
        // 目标:搞清楚接口新增的三种方式,并理解其好处。
        AImpl a = new AImpl();
        a.go();   // ===go方法执行了===
        A.show();  // ===show方法执行了===
    }
}

class AImpl implements A {
}

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

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

    // 3、静态方法
    //  默认会用public修饰
    // 如何调用? 只能使用当前接口名来调用。
    // 不允许用实现类来调用当前接口的静态方法。
    static void show(){
        System.out.println("===show方法执行了===");
    }
}
  • 接口的注意事项:
    1、接口与接口可以多继承:一个接口可以同时继承多个接口;
    2、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现;(了解)
    3、一个类继承了父类,又同时实现了接口,如果父类中和接口中有同名的默认方法,实现类会优先用父类的;
    怎么在实现类对象中调用接口的同名方法呢?
    只能中转:在实现类写一个方法,用接口名.super.方法名();调用。// 指定调接口爸爸的方法
    4、一个类实现了多个接口,如果多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。

  • 抽象类和接口的区别对比:
    相同点:
    1、都是抽象形式,都可以有抽象方法,都不能创建对象
    2、都是派生子类形式:抽象类是被子类来使用,几口是被实现类实现。
    3、一个类继承抽象类或者实现接口,都必须重写完他们的全部抽象方法,否则自己要成为抽象类
    4、都能支持多态,都能够实现解耦合
    不同点:
    1、抽象类中可以定义类的全部普通成员,接口只能定义常量、抽象方法(JDK8新增的三种方法)
    2、抽象类只能被类单继承,接口可以被类多实现
    3、一个类继承抽象类就不能再继承其他类,一个类实现了接口(还可以继承其他类,或者实现其他接口)
    4、抽象类体现模板思想,更利于做父类,实现代码的复用性;
    5、接口更适合做功能的解耦合,解耦合性更强更灵活。

  • 最佳实践:
    集合都是功能——都是用接口做的解耦合
    IO流是抽象类做父类

public class Test {
    public static void main(String[] args) {
        // 目标:理解接口的几点注意事项
        Dog d = new Dog();
        d.show();

        Dog2 d2 = new Dog2();
        d2.show();
    }
}

// 1、接口与接口可以多继承:一个接口可以同时继承多个接口;
interface A {
    void show1();
}
interface B {
    void show2();
}
interface C extends A,B {
    void show3();
}

class D implements C {
    @Override
    public void show1() {
    }
    @Override
    public void show2() {
    }
    @Override
    public void show3() {
    }
}

// 2、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现;
interface A1 {
    void show();
}
interface B1 {
//    String show(); // B1用这个,那么D1实现A1和B1的方法会因签名冲突而报错
    void show();
}
interface C1 extends A1,B1 {
}
class D1 implements C1 {
    @Override
    public void 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(){
        super.show(); // 还是找父类的show方法
        A2.super.show(); // 找A2接口的show方法
    }
}

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

6.其他

  • 常量:
    使用了static、final修饰的成员变量就称为常量
    public static final String SCHOO_NAME = "黑马";
    作用:常用于记录系统的配置信息

  • 使用常量记录系统配置信息的优势、执行原理:
    代码可读性好,可维护性也更好;
    程序编译后,常量会被宏替换(就是全部替换):出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和字面量的性能是一样的。

  • 设计模式:
    设计模式有20多种,对应20多种软件开发过程中会遇到的问题(总体来说有23种)

  • 关于设计模式,主要学什么?
    1、解决什么问题?
    2、怎么写?

  • 枚举类:
    枚举类都是最终类,不可以被继承,所有的枚举类都是继承java.lang.Enum类的;
    枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象;
    所以枚举类是多例模式;
    编译器为枚举对象新增了几个方法:枚举类对象名.ordinal():拿到枚举类对象的索引(指代第几个枚举类对象,从0开始)

  • 比较强大的反编译工具:Xjad
    或者用命令行进行反编译:

D:\Desktop>javap .\A.class
Compiled from "A.java"
public final class sun.superstring.enumdemo.A extends java.lang.Enum<sun.superstring.enumdemo.A> {
  public static final sun.superstring.enumdemo.A X;
  public static final sun.superstring.enumdemo.A Y;
  public static final sun.superstring.enumdemo.A Z;
  public static sun.superstring.enumdemo.A[] values();
  public static sun.superstring.enumdemo.A valueOf(java.lang.String);
  static {};
}

D:\Desktop>
  • mybatis把JDBC封装成一个框架
    JDBC(Java database Connectivity,Java数据库连接)是Java编程语言中用于执行SQL语句的一套API。它为Java应用程序提供了与各种关系型数据库进行交互的标准方法,使得开发者可以使用统一的接口来访问不同的数据库系统,而无需关心底层数据库的具体实现细节。

  • java的接口为什么新增三种形式的方法?
    我们自己写的可能性几乎为0,sun公司自己用的比较多。
    增强了接口的能力,更便于项目的扩展和维护
    其实这个在项目中基本用不到,现在三层架构,直接controller自动注入接口的bean,然后在impl实现接口的功能,不会用到这些新增的方法。——可以跳过了

posted @   桃花仙(泽玉)  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~
点击右上角即可分享
微信分享提示