## I'm on my way...

# 面向对象高级

EaborH
2025-03-17 18:11:51 2025-03-17 18:11:51 Created 2025-03-22 16:01:21 2025-03-22 16:01:21 Updated

- 面向对象

- final
- | 单例类
- | 枚举类
- | 抽象类
- | 接口

# Final关键字

### 认识final

final 关键字是最终的意思，可以修饰：类，方法，变量

- 修饰类：该类被称为最终类，特点是不能被继承了
- 修饰方法：该方法被成为最终方法，特点是不能被重写了
- 修饰变量：该变量有且只能被赋值一次

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40

    package life.eabor.finaldemo;

    public class FinalDemo1 {

        // final修饰静态成员变量
        // final修饰的成员变量，这个变量今后被成为常量，记录一个固定值，不能被修改，作为系统配置信息
        public static final String NAME = "eabor";

        public static void main(String[] args) {
            // 认识final关键字的作用
            // 3、final修饰的变量，不能被修改
            /*
                变量有哪些
                a、成员变量：
                    静态成员变量，实例成员变量
                b、局部变量
             */        final int NUMBER = 666;
            // NUMBER = 888;
        }
    }

    // 1、final修饰的类，不能被继承

    final class A{}
    //class B extends A{}

    // 2、final修饰的方法，不能被重写
    class C{
        public void m1(){
            System.out.println("m1");
        }
    }

    class D extends C{
        // @Override
        // void m1(){}
    }

final 修饰变量的注意

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

    1
    2
    3
    4
    5
    6
    7

    final int a = 20;
    // a = 30 !errow

    final int[] arr = {};
    // arr = new int[]{} !errow
    arr[index] = num;

### 常量

- 使用 static final 修饰的成员变量就被称为常量
- 作用：常用于记录系统的配置信息
- 常量命名规范：建议大写英文单词，多个单词使用下划线连接

    1
    2

    public class Constant
    	public static final USER_NAME = "Eabor";

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

- 代码的可读性更好，可维护性更好
- 程序编译后，常量会被”宏替换“：出现常量的地方全部会被替换成其记住的字面量，这样可以保证使用常量和直接使用字面量的性能是一样的

# 单例类（设计模式）

### 什么是设计模式

- 一个问题通常有
  种解法，其中肯定有一种解法是最优的，这种最优的解法被总结出来了，称为设计模式
- 设计模式有
  多种，对应
  多种软件开发中会遇到的问题

### 单例设计模式

- 作用：确保某个类只能创建一个对象

写法，实现步骤

- 把类的构造器私有化
- 定义一个类变量记住类的一个对象
- 定义一个类方法，返回对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    // 单例类
    public class A {
    	// 2、定义一个类变量记住类的一个唯一对象
    	private static A a = new A();

    	// 1、私有构造器
    	private A(){
    	}

    	// 3、定义一个类方法返回对象
    	public static A getObject(){
    		return a;
    	}
    }

懒汉式单例类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    public class B {
    	// 2、定义一个类变量记住类的一个唯一对象
    	private static B b;

    	// 1、私有构造器
    	private b(){
    	}

    	// 3、定义一个类方法返回对象
    	public static B getObject(){
    		if(b == null){
    			b = new B();
    		}
    		return b;
    	}
    }

### 枚举类

枚举类式一种特殊类

枚举类的写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    修饰符 enum 枚举类名{
    	名称1,名称2,...; // 只能是枚举对象的名称
    	其他成员...
    }

    public enum A{
    	X,Y,Z;
    }

    public class Text {
    	public static void main(String[] args) {
    		A a1 = new X;
    		A a2 = new 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
    	}
    }

### 枚举类的常见应用场景

- 枚举类很适合做信息分类和标志

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

    public class Text {
    	public static void main(String[] args) {
    		move(Direction.Dowm);
    	}
    	public static void move(Direction direction){
    		switch(direction){
    			case UP :
    				...
    				break;
    			case DOWN :
    				...
    				break;
    			case LEFT :
    				...
    				break;
    			case ROGHT :
    				...
    				break;
    		}
    	}

    public enum Direction {
    	UP,DOWN,LEFT,RIGHT;
    }

### 抽象类

- 在
  中有一个关键字：abstract，他就是抽象类的意思，可以用来修饰类，成员方法
- abstract修饰类，这个类就是抽象类
- abstract修饰方法，这个方法就是抽象方法

    1
    2
    3

    修饰符 abstract class 类名{
    	修饰符 abstract 返回值类型 方法名称(形参列表)；
    }

    1
    2
    3
    4

    public abstract class A{
    	// 抽象方法：必须abstract修饰，只有方法签名，不能有方法体
    	public abstract void test();
    }

抽象类的注意事项、特点：

- 抽象类中不一定要有抽象方法，有抽象方法的类必须是抽象类
- 类有的成员：成员变量，方法，构造器，抽象类都可以有
- 抽象类最主要的特点：抽象类不能创建对象，仅作为一种特殊的父类，让子类继承并实现
- 一个类继承抽象类，必须重写完抽象类的全部抽象方法，否则这个类也必须定义成抽象类

### 抽象类的好处

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

### 模板方法设计模式

提供一个方法作为完成某类功能的模板，模板方法封装了每个实现步骤，但允许子类提供特定步骤实现

写法

1. 定义一个抽象类
2. 在里面定义两个方法
   - 一个是模板方法(建议使用
     修饰)：把共同的实现步骤放里面去
   - 一个是抽象方法：不确定的实现步骤，交给具体的子类来完成

### 接口

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

    1
    2
    3
    4

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

1. 常量： 接口中定义的常量可以省略
   不写，默认会加上去
2. 抽象方法： 接口中定义的抽象方法可以省略
   不写，默认会加上去

注意抽象类不能创建对象

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

    1
    2
    3

    修饰符 class 实现类类名 implements 接口1, 接口2, 接口3 ,... {
    	// 实现类实现多个接口，必须重写完全部接口的全部抽象方法，否则实现类需要定义成抽象类
    }

### 接口的好处

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

### JDK 8开始，接口新增了三种形式的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    public interface A{
    	/**
    	 * 1.默认方法(实例方法)：使用 default修饰，默认会被加上public修饰
    	 * 注意：只能使用接口的实现类对象调用
    	 */

    	 default void text1(){
    		 ...
    	 }
    	 /**
    	 * 2.私有方法：必须用private修饰(JDK 9才开始支持)
    	 */
    	 private void text2(){
    		 ...
    	 }

    	 /**
    	 * 3.类方法(静态方法)：使用static修饰，默认会被加上public修饰
    	 * 注意：只能用接口名来调用
    	 */
    	 static void text3(){
    		 ...
    	 }
    }

### 接口的注意事项

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

抽象类、接口的区别对比
相同点：

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

不同点

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

## 代码块

代码块是类的五大成分之一(成员变量、构造器、方法、代码块、内部类)

代码块分两种：

- 静态代码块：属于类
  - 格式：static{}
  - 特点：类加载时自动执行，由于类只会加载一次，所以静态代码块也会执行一次
  - 作用：完成类的初始化，例如：对静态变量的初始化赋值
- 实例代码块：
  - 格式：{}
  - 特点：每次创建对象时，执行实例代码块，并在构造器前执行
  - 作用：和构造器一样，都是用来完成对象初始化的，例如：对实例变量进行初始化赋值

## 内部类

如果一个类定义在另一个类的内部，这个类就是内部类
场景： 当一个类的内部，包含了一个完整的事物，且这个事物没有必要单独设计时，就可以把这个事物设计成内部类

    1
    2
    3
    4
    5

    public class A{
    	// 内部类
    	public class B{
    	}
    }

### 成员内部类

就是类中的一个普通成员，类似前面我们学过的成员变量，成员方法

    1
    2
    3
    4
    5
    6
    7
    8

    main{
    	// 成员内部类创建对象的格式
    	// 外部类的名称.内部类的名称 对象名 = new 外部类的名称().new内部类的名称();
    	Outer.Inner oi = new Outer().new Inner();

    	// 1.成员内部类中可以直接访问外部类中的静态成员，也可以直接访问外部类的实例成员
    	// 2.成员内部类的实例方法中，可以直接拿到当前寄生的外部类对象：外部类名.this
    }

    1
    2
    3
    4
    5
    6

    public class Outer {
    	// 成员内部类：无static修饰，属于外部类的对象持有
    	public class Inner {

    	}
    }

### 静态内部类

有static修饰的内部类，属于外部类自己持有

    1
    2
    3
    4
    5
    6

    public class Outer {
    	// 静态内部类
    	public static class Inner {

    	}
    }

    1
    2
    3
    4
    5
    6

    main{
    	外部类名.内部类名 对象名 = new 外部类.内部类();
    	Outer.Inner in = new Outer.Inner();
    	// 1.静态内部中可以直接访问外部类的静态成员
    	// 2.静态内部中不能直接访问外部类的实例成员
    }

### 局部内部类

局部内部类是定义在方法中、代码块中、构造器等执行体中

### 匿名内部类

是一种特殊的局部内部类
所谓匿名：指的是程序员不需要为这个类声明名字，默认有一个隐藏的名字

    1
    2
    3

    new 类或接口(参数值...){
    	类体(一般是方法重写)；
    };

    1
    2
    3
    4
    5

    new A(){
    	@Override
    	public void cry() {
    	}
    };

特点： 匿名内部类本质就是一个子类，并会立即创建出一个子类对象
作用： 用于更方便的创建一个子类对象

常见使用形式

- 通常作为一个对象参数传输给方法
- 需求：学生老师要参加游泳比赛

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33

    public class Text {
        public static void main(String[] args) {
            // 目标：搞清楚匿名内部类的使用形式：通常可以作为一个对象参数传输给方法使用
            // 学生和老师都要参加游泳比赛

            start(new Swim() {
                @Override
                public void swim() {
                    System.out.println("老师开始游泳");
                }
            });

            start(new Swim() {
                @Override
                public void swim() {
                    System.out.println("学生开始游泳");
                }
            });
        }

        // 设计一个方法，可以接收老师和学生开始比赛
        public static void start(Swim s){
            System.out.println("game start");
            s.swim();
            System.out.println("game end");
        }

        interface Swim{
            void swim();
        }
    }

使用场景
调用别人提供的方法实现需求时，这个方法正好可以让我们传输一个匿名内部类对象给其使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34

    import javax.swing.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;

    public class Test2 {
        public static void main(String[] args) {
            // 目标：搞清楚几个匿名内部类的使用形式
            // 需求：创建一个登录窗口，窗口上有一个登录按钮

            JFrame win = new JFrame("登录窗口");
            win.setSize(300, 200);
            win.setLocationRelativeTo(null);
            win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            JPanel panel = new JPanel();
            win.add(panel);

            JButton btn = new JButton("登录");
            win.add(btn);

            // java需求必须给这个按钮添加一个点击事件监听器对，这样就可以监听用户的点击操作，就可以做出反应

            btn.addActionListener(e -> System.out.println("用户点击了登录按钮"));

            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    System.out.println("用户点击了登录按钮");
                }
            });

            win.setVisible(true);
        }
    }

eg. 使用comparetor接口的匿名内部类实现对数组进行排序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36

    package life.eabor.innerclass;

    import java.util.Arrays;
    import java.util.Comparator;

    public class Text3 {
        public static void main(String[] args) {
            Student[] s = new Student[6];
            s[0] = new Student("李long", 19, '女' );
            s[1] = new Student("张三", 18, '男' );
            s[2] = new Student("李四", 19, '女' );
            s[3] = new Student("王五", 20, '男' );
            s[4] = new Student("赵六", 21, '女' );
            s[5] = new Student("钱七", 22, '男' );

            Arrays.sort(s, new Comparator<Student>() {
                @Override
                public int compare(Student o1, Student o2) {

                    if (o1.getAge() > o2.getAge()){
                        return 1;
                    }else if (o1.getAge() < o2.getAge()){
                        return -1;
                    }
                    return 0;

                    return o1.getAge() - o2.getAge();
                }
            });
            for (Student student : s){
                if (student != null){
                    System.out.println(student);
                }
            }
        }
    }

## 函数式编程

此“函数”类似于数学中的函数（强调做什么），只要输入的数据一直返回的结果也是一致
Java中的函数(Lambda表达式)：(x) -> 2x+1

函数式编程解决了：
使用Lambda函数替代某些匿名内部类对象，从而让代码更简洁，可读性更好

### Lambda

- JDK 8 开始新增的一种语法，他表示函数
- 可以用于替代某些匿名内部类对象，让程序更简洁，可读性更好

    1
    2
    3

    (被重写方法的形参列表)->{
    	被重写方法的方法体代码
    }

注意：Lambda表达式只能替代函数式接口的匿名内部类

什么是函数式接口：有且只有一个抽象方法接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    package life.eabor.lambda;
    public class Text {
        public static void main(String[] args) {
            // 认识lambda表达式
            // Lambda表达式是函数式接口的实例
            Swimmer swimmer = () -> {
                System.out.println("I am swimming");
            };
            swimmer.swim();
        }
    }

    @FunctionalInterface
    // 函数式接口，只有一个抽象方法接口
    interface Swimmer {
        void swim();
    }

Lambda表达式的省略规则
作用：用于进一步简化Lambda表达式的写法

具体规则：

- 参数类型全部可以省略不写
- 如果只有一个参数的同时”()”也可以省略，但多个参数不能省略
- 如果Lambda表达式中只有一行代码，大括号可以不写，同时要省略”;”，如果这行代码是return语句，也必须去掉return

    1

    Arrays.sort(s, (o1, o2)->o1.getAge() - o2.getAge());

### 方法引用

静态方法引用：
类名::静态方法
使用场景
如果某个Lambda表达式里只是调用一个静态方法，并且”->”前后参数的形式一致，就可以使用静态方法引用

    1
    2
    3
    4
    5
    6
    7
    8

    Arrays.sort(s, Student::compareByAge);

    Student{
    	.....
    	public static int compareByAge(Student s1, Student s2) {
        return s1.getAge() - s2.getAge();
    	}
    }

实例方法引用：
类名::实例方法
使用场景
如果某个Lambda表达式里只是调用一个实例方法，并且”->”前后参数的形式一致，就可以使用实例方法引用

特定类型的方法引用
特定类的名称::方法
使用场景
如果某个Lambda表达式里只是调用一个特定类型的实例方法，并且前面参数列表中第一个参数是作为方法的主调，后面的所有参数都是作为该实例方法的入参是，则此时就可以使用特定类型的方法引用

    1

    Arrays.sort(names, String::compareToIgmoreCase)

构造器引用
类名::new
使用场景
如果某个Lambda表达式里只是在创建对象，并且”->”前后参数的形式一致，就可以使用构造器引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41

    package life.eabor.method1reference;

    public class Demo2 {
        public static void main(String[] args) {
            // 理解构造器引用
            CarFactory cf = Car::new;
            Car c = cf.getCar("保时捷");
            System.out.println(c);

        }
    }
    @FunctionalInterface
    interface CarFactory {
        Car getCar(String name);
    }

    class Car {
        private String name;

        public Car() {
        }

        public Car(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

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

        @Override
        public String toString() {
            return "Car{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

## 常用API

String
String代表字符串，它的对象可以封装字符串数据，并提供了很多方法完成对字符串的处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    package life.eabor.stringdemo;

    public class StringDemo1 {
        public static void main(String[] args) {
            // 掌握创建字符串对象，封装要处理的字符串数据，调用Sting提供的方法处理字符串

            //1. ""创建字符串对象
            String s1 = "Hello World";
            System.out.println(s1);
            System.out.println(s1.length());

            // 2.构造器创建字符串对象
            String s2 = new String();
            System.out.println(s2);

            String s3 = new String("Hello World");
            System.out.println(s3);

            char [] chars = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
            String s4 = new String(chars);
            System.out.println(s4);

            byte [] bytes = {72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100};
            String s5 = new String(bytes);
            System.out.println(s5);

        }
    }

只要是以“…”方式写出的字符串对象，会存储到字符串常量池且相同内容的字符串只存储一份
通过new方式创建字符串对象，每new一次都会产生一个新的对象放在堆内存中

ArrayList
集合是一种容器，用来装数据的，类似于数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

    package life.eabor.arraylist;

    import java.util.ArrayList;

    public class ArrayListDemo1 {
        public static void main(String[] args) {
            // 掌握ArrayList的使用
            ArrayList<String> list = new ArrayList<>();
            list.add("Hello");
            list.add("World");
            list.add("Java");

            // 查看数据
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }

            // 删除数据
            list.remove(1);
            list.remove("Java");

            // 修改数据
            list.set(0, "Hello World");

        }
    }

- Title: 面向对象高级
- Author: EaborH
- Created at : 2025-03-17 18:11:51
- Updated at : 2025-03-22 16:01:21
- Link: https://eabor.xyz/2025/03/17/面向对象高级/
- License: This work is licensed under CC BY-NC-SA 4.0.

- #final
- #单例类
- #枚举类
- #抽象类
- #接口

面向对象 Next posts