面向对象高级

EaborH

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");


}
}

GUI

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

import javax.swing.*;

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

JFrame jf = new JFrame("Log in"); // 创建一个窗口

JPanel jp = new JPanel(); // 创建一个面板
jf.add(jp); // 将面板添加到窗口中

jf.setSize(400, 300); // 设置窗口大小
jf.setLocationRelativeTo(null); // 居中显示
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 点击关闭按钮,退出程序

JButton JB = new JButton("登录"); // 创建一个按钮
jp.add(JB); // 将按钮添加到面板中



jf.setVisible(true); // 显示窗口
}
}

[[加强]]

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