什么是 OOP

面向对象编程(Object-Orien Programming,OOP)

  • 面向过程:编写对数据执行的过程或方法
  • 面向对象:创建同时包含数据和方法的对象

类和对象:

  1. 类是对象的模板,而对象是类的实例
  2. 创建各个对象时,它们会从类中继承所有变量和方法

类和对象

创建:

  1. 创建类:class (大写开头,java 文件的名称应与类名匹配)

    1
    2
    3
    4
    class Person {
    public String name;
    public int age;
    }
  2. 创建实例:new

    1
    Person ming = new Person();

**属性:**访问、修改:.

方法

定义

1
2
3
4
修饰符 方法返回类型 方法名(方法参数列表) {
若干方法语句;
return 方法返回值;
}

修饰符:

  • private:只能在类内部调用
  • static:静态方法,属于类本身,不需要创建对象也能调用
  • public:任何类可访问

**this:**指向当前实例

可变参数:类型...

1
2
3
4
5
6
class Group {
private String[] names;
public void setNames(String... names) {
this.names = names;
}
}

按值传递 vs 引用传递:

类型 传递 更改后输出是否变化
基本数据类型 按值传递 不变
引用类型(数组、对象) 引用传递(可在原有地址改变)
字符串、不可变类型 引用传递(修改会创建新对象) 不变

构造方法

要求:

  1. 方法的名称是类名
  2. 没有返回值(没有 void
  3. 调用:new
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
// 构造方法
public class Main {
public static void main(String[] args) {
Person p1 = new Person("Xiao Ming", 15); // 既可以调用带参数的构造方法
Person p2 = new Person(); // 也可以调用无参数构造方法
}
}
class Person {
private String name;
private int age;

public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}

重载

重载 overload:

  1. 方法名相同
  2. 参数列表不同:数量、类型、顺序
  3. 返回值可不同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Hello {
public void hello() {
System.out.println("Hello, world!");
}
public void hello(String name) {
System.out.println("Hello, " + name + "!");
}
public void hello(String name, int age) {
if (age < 18) {
System.out.println("Hi, " + name + "!");
} else {
System.out.println("Hello, " + name + "!");
}
}
}

方法链

要求:

  1. 前一个方法返回类本身 this
  2. 或者是构造函数

修饰符

  • 访问控制修饰符 - 控制访问级别
  • 非访问控制修饰符 - 不控制访问级别,但提供其他功能

访问修饰符

  • 对于

    修饰符 作用
    public 所有类都能访问
    默认不写 同包内可访问
  • 对于属性、方法和构造函数:、

    修饰符 作用
    public 所有类都能访问
    private 仅限当前类可访问
    默认不写 同包内可访问
    protected 同包子类可访问

非访问修饰符

  • 对于

    修饰符 作用
    final 表示类不可继承
    abstract 表示类是抽象类,不能实例化
  • 对于属性和方法

    修饰符 作用
    final 属性和方法不能被重写、修改
    static 属性方法属于类,而不是实例
    abstract 只能在抽象类的抽象方法中使用,方法没有主体
    transient 属性和方法不会被序列化
    synchronized 方法一次只能由一个线程访问
    volatile 属性的值不在线程本地缓存,而是始终从“主内存”读取

导入类:import java.util.Scanner;

导入整个包:import java.util.*;(不包括子包的 class

**命名:**倒置域名

作用域

**类作用域:**在整个类中可访问

**方法作用域:**只能在该方法中访问

**块作用域:**变量只能在块内访问

**循环作用域:**变量只能在循环体内访问

**包作用域:**同包内可访问(没用任何修饰符 publicprivateprotected

封装

封装:确保对用户敏感的数据进行隐藏

  • private:声明变量/属性(只能在同一个类中访问,外部类无权访问)
  • GetSet 方法:访问、更新 private 变量

Get Set

get 方法返回变量值,set 方法设置值

两者的语法是它们都以 getset 开头,后跟变量的名称,第一个字母为大写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Person {
private String name; // private = restricted access

// Getter
public String getName() {
return this.name;
}

// Setter
public void setName(String newName) {
this.name = newName;
}
}
public class Main {
public static void main(String[] args) {
Person myObj = new Person();
myObj.name = "John"; // error
System.out.println(myObj.name); // error
myObj.setName("John"); // Set the value of the name variable to "John"
System.out.println(myObj.getName());
// Outputs "John"
}
}

意义

  • 更好地控制类属性和方法
  • 类属性可以是只读的(如果只使用 get 方法),也可以是只写的(如果只使用 set 方法)
  • 灵活:程序员可以更改代码的一部分,而不会影响其他部分
  • 提高数据安全性

继承

extends:

  • 子类:从另一个类继承的类
  • 父类:被继承的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Vehicle {
protected String brand = "Ford"; // Vehicle attribute
public void honk() { // Vehicle method
System.out.println("Tuut, tuut!");
}
}

class Car extends Vehicle {
private String modelName = "Mustang"; // Car attribute
public static void main(String[] args) {

// Create a myCar object
Car myCar = new Car();

// Call the honk() method (from the Vehicle class) on the myCar object
myCar.honk();

// Display the value of the brand attribute (from the Vehicle class) and the value of the modelName from the Car class
System.out.println(myCar.brand + " " + myCar.modelName);
}
}

修饰符:

  • protected:子类无法访问父类的字段、方法
  • protected:子类可访问
  • final:其他类无法继承

super:

  1. 子类引用父类的字段

  2. 父类没有默认构造方法,子类必须显式调用 super 并给出参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Person {
    protected String name;
    protected int age;

    public Person(String name, int age) {
    this.name = name;
    this.age = age;
    }
    }

    class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
    super(name, age); // 调用父类的构造方法Person(String, int)
    this.score = score;
    }
    }

向上转型:Person p = new Student();

  1. pPerson 类型的引用变量,指向 Student 对象(实例)
  2. 编译时是 Person,运行时是 Student

向下转型:

1
2
3
4
Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!
  • Person 类型 p1 实际指向 Student 实例:成功

  • Person 类型变量 p2 实际指向 Person 实例:失败,子类功能比父类多

**instanceof:**判断变量指向的实例是否是指定类型,或者这个类型的子类,若是可直接转型

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
Object obj = "hello";
if (obj instanceof String s) {
// 可以直接使用变量s:
System.out.println(s.toUpperCase());
}
}
}

多态

重写 @override:

  1. 方法名相同
  2. 参数列表相同
  3. 返回类型相同

**多态:**真正执行的方法取决于运行时期实际类型的方法

重写 vs 重载:

重写:

1
2
3
4
5
6
7
public class Processor {
public void process(int i, int j) { /* ... */ }
}
class MathProcessor extends Processor {
@Override
public void process(int i, int j) { /* ... */ }
}

重载:

1
2
3
4
5
6
public class Processor {
public void process(int i, int j) { /* ... */ }
public void process(int[] ints) { /* ... */ }
public void process(Object[] objs) { /* ... */ }

}

抽象

abstract:

  • **抽象类:**不能创建对象,只能继承(可同时拥有抽象,普通方法、字段)
  • **抽象方法:**只能在抽象类中使用,子类必须全部重写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// abstract class
public class Main {
public static void main(String[] args) {
Person p = new Student();
p.run();
}
}
abstract class Person {
public abstract void run();
}
class Student extends Person {
@Override
public void run() {
System.out.println("Student.run");
}
}

接口

**接口:**没有字段,所有方法都是抽象方法的抽象类(默认 public abstract 可不写)

具体实例实现接口:implements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(this.name + " run");
}
@Override
public String getName() {
return this.name;
}
}

接口继承:

1
2
3
4
5
6
7
8
interface Hello {
void hello();
}

interface Person extends Hello {
void run();
String getName();
}

**default 方法:**具体实现类可以不用重写 default 方法

内部类

内部类

要实例化内部类,需要先创建外部类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Main {
public static void main(String[] args) {
Outer outer = new Outer("Nested"); // 实例化一个Outer
Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
inner.hello();
}
}
class Outer {
private String name;

Outer(String name) {
this.name = name;
}

class Inner {
void hello() {
System.out.println("Hello, " + Outer.this.name);
}
}
}

内部类可以访问外部类的 private 字段

匿名类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Main {
public static void main(String[] args) {
Outer outer = new Outer("Nested");
outer.asyncHello();
}
}
class Outer {
private String name;

Outer(String name) {
this.name = name;
}

void asyncHello() {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello, " + Outer.this.name);
}
};
new Thread(r).start();
}
}

静态内部类

static:可以直接创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public static void main(String[] args) {
Outer.StaticNested sn = new Outer.StaticNested();
sn.hello();
}
}

class Outer {
private static String NAME = "OUTER";

private String name;

Outer(String name) {
this.name = name;
}

static class StaticNested {
void hello() {
System.out.println("Hello, " + Outer.NAME);
}
}
}

核心类

字符串

特点:

  1. 引用类型,本身是 class,但可以 "..." 定义
  2. 不可变:更改是在新空间创建新字符串

比较:.equals()

包含:.contains()

去除首尾空白字符:

  • .trim()\t\r\n

  • .strip()

是否为空:isEmpty()

替换:s.replace('l', 'w');

分割:split()

拼接:join("...", arr);

格式化:

  • formatted()
  • format()

类型转换:

  • 转换为字符串:String.valueOf()
  • 转换为其他类型:Integer.parseIntBoolean.parseBoolean
  • 转换为 char[].toCharArray()

StringBuilder

创建:StringBuilder sb = new StringBuilder();

添加:.append()

转换为字符串:.toString()

插入:.insert(index, "...")

可进行链式操作

StringJoiner

拼接: var sj = new StringJoiner(", ", "Hello ", "!");(中间,头,尾)

包装类

  • 基本类型 -> 引用类型

  • 不变类

基本类型 引用类型
boolean java.lang.Boolean
byte java.lang.Byte
short java.lang.Short
int java.lang.Integer
long java.lang.Long
float java.lang.Float
double java.lang.Double
char java.lang.Character

比较:.equals()

JavaBean

**JavaBean:**如果读写方法符合以下命名规范,那么这种 class 被称为 JavaBean (封装)

1
2
3
4
// 读方法:
public Type getXyz()
// 写方法:
public void setXyz(Type value)

枚举 JavaBean 的属性:

1
2
3
4
5
6
7
8
9
10
11
import java.beans.*;
public class Main {
public static void main(String[] args) throws Exception {
BeanInfo info = Introspector.getBeanInfo(Person.class);
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
System.out.println(pd.getName());
System.out.println(" " + pd.getReadMethod());
System.out.println(" " + pd.getWriteMethod());
}
}
}

枚举类

enum:enum 类型就是 class (引用类型)

  • 继承自java.lang.Enum,且无法被继承
  • 只能定义 enum 的实例,无法通过 new 创建
  • 定义的每个实例都是引用类型的唯一实例
  • 可用于 switch 语句

创建:

1
2
3
public enum Color {
RED, GREEN, BLUE;
}

编译出的 class 文件:

1
2
3
4
5
6
7
8
public final class Color extends Enum { // 继承自Enum,标记为final class
// 每个实例均为全局唯一:
public static final Color RED = new Color();
public static final Color GREEN = new Color();
public static final Color BLUE = new Color();
// private构造方法,确保外部无法调用new操作符:
private Color() {}
}

比较:== (不像其他引用类型只能用 equals()

常量名:String s = Weekday.SUN.name(); // "SUN"

常量顺序:int n = Weekday.MON.ordinal(); // 1 (从0开始)

添加字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enum Weekday {
MON(1, "星期一"), TUE(2, "星期二"), WED(3, "星期三"), THU(4, "星期四"), FRI(5, "星期五"), SAT(6, "星期六"), SUN(0, "星期日");

public final int dayValue;
private final String chinese;

private Weekday(int dayValue, String chinese) {
this.dayValue = dayValue;
this.chinese = chinese;
}

@Override
public String toString() {
return this.chinese;
}
}

记录类

record:

  • final 修饰class以及每个字段(实现不变类)
  • 自动创建构造方法,重写 toString()equals()hashCode()
  • 不能直接从 Record 派生,只能通过关键词由编译器实现继承
1
record Point(int x, int y) {...}

相当于:

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
final class Point extends Record {
private final int x;
private final int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

public int x() {
return this.x;
}

public int y() {
return this.y;
}

public String toString() {
return String.format("Point[x=%s, y=%s]", x, y);
}

public boolean equals(Object o) {
...
}
public int hashCode() {
...
}
}

BigInteger

创建:BigInteger bi = new BigInteger("1234567890");

**运算:**只能用实例

1
2
3
BigInteger i1 = new BigInteger("1234567890");
BigInteger i2 = new BigInteger("12345678901234567890");
BigInteger sum = i1.add(i2); // 12345678902469135780

转换为基本类型:

  • 转换为bytebyteValue()
  • 转换为shortshortValue()
  • 转换为intintValue()
  • 转换为longlongValue()
  • 转换为floatfloatValue()
  • 转换为doubledoubleValue()

BigDecimal

创建:BigDecimal bd = new BigDecimal("123.4567");

小数位数:.scale()

去掉末尾0:stripTrailingZeros()

比较:

  • compareTo():比较大小
  • equals():比较大小和 scale()