Java 新手入门与实战开发指南

Java 新手入门与实战开发指南

面向零基础读者,手把手带你从安装环境到写出第一个可运行程序,再到理解面向对象、集合、多线程,最后用一个综合案例把知识串起来。所有代码均可复制运行。


目录

① [JDK 环境搭建与第一个程序运行](#JDK 环境搭建与第一个程序运行)

核心语法基础与变量数据类型

流程控制语句与逻辑判断实战

面向对象编程思想与类的设计

常用集合框架的使用与遍历

异常处理机制与调试技巧

⑦ [文件读写操作与 IO 流应用](#文件读写操作与 IO 流应用)

多线程基础与并发安全初探

综合案例:简易管理系统实现

常见编译报错分析与解决策略


一、JDK 环境搭建与第一个程序运行

1.1 什么是 JDK / JRE / JVM

初学者先搞清楚三个缩写,避免后面绕晕:

  • JVM(Java 虚拟机):真正"运行"Java 程序的地方。Java 之所以能"一次编写,到处运行",靠的就是不同平台有不同的 JVM,但执行的是同一份字节码。
  • JRE(Java 运行环境):JVM + 运行 Java 程序所需的基础类库。只运行程序、不开发,装 JRE 就够了。
  • JDK(Java 开发工具包) :JRE + 编译器(javac)+ 调试等开发工具。我们要开发,必须装 JDK。

一句话:开发装 JDK,JDK 已经包含了 JRE。

1.2 下载与安装

  1. 打开浏览器访问 Oracle 官网(https://www.oracle.com/java/technologies/downloads/)或 Adoptium(https://adoptium.net/,开源免费,推荐新手用)。
  2. 选择 LTS 长期支持版本 (目前主流是 Java 17 或 Java 21),下载对应操作系统的安装包:
    • Windows:选 .msi 安装包,双击按提示安装。
    • macOS:选 .pkg
    • Linux:可用 apt install openjdk-17-jdk 等命令。
  3. 记住安装路径(Windows 默认类似 C:\Program Files\Java\jdk-17)。

建议:新手优先用 Adoptium 的 Temurin 发行版,无需注册、无商业授权顾虑。

1.3 配置环境变量(Windows)

配置环境变量是为了在任意目录下都能使用 javajavac 命令。

  1. 右键"此电脑" → 属性 → 高级系统设置 → 环境变量。

  2. 在"系统变量"中新建 JAVA_HOME,值为 JDK 安装路径,例如:

    复制代码
    C:\Program Files\Java\jdk-17
  3. 找到已有的 Path 变量,编辑 → 新建,添加两行:

    复制代码
    %JAVA_HOME%\bin
  4. 一路确定保存。

1.4 验证安装

打开命令行(Win + R → 输入 cmd),执行:

bash 复制代码
java -version
javac -version

能正确显示版本号(如 java version "17.0.x")即表示安装成功。

1.5 写出第一个程序:Hello World

新建一个文件 HelloWorld.java,注意文件名必须和类名完全一致

java 复制代码
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, Java 世界!");
    }
}

逐行解释:

  • public class HelloWorld:定义一个公开的"类",类名是 HelloWorld。Java 要求 public 类的文件名必须等于类名。
  • public static void main(String[] args):程序的入口方法(主方法)。JVM 启动时会自动找它并执行。
  • System.out.println(...):向控制台打印一行文字。

1.6 编译与运行

HelloWorld.java 所在目录执行:

bash 复制代码
javac HelloWorld.java   # 编译:生成 HelloWorld.class 字节码文件
java HelloWorld         # 运行:JVM 执行字节码(注意不需要写 .class)

看到输出 Hello, Java 世界! 就成功了!🎉

新手易错:java 命令后面跟的是类名 HelloWorld,不是文件名 HelloWorld.class,也不带 .java


二、核心语法基础与变量数据类型

2.1 程序的基本结构

Java 是强类型、面向对象 语言,所有代码都写在类 {} 里,语句以分号 ; 结尾。

2.2 变量的声明与使用

变量就是"装数据的盒子",先声明类型再使用:

java 复制代码
int age = 18;              // 整数
double price = 9.9;        // 小数
String name = "小明";       // 字符串(引用类型)
boolean isStudent = true;  // 布尔值:true / false

age = 19;                  // 可以重新赋值
System.out.println(name + " 今年 " + age + " 岁");

2.3 八大基本数据类型

类型 占用空间 取值范围 示例
byte 1 字节 -128 ~ 127 byte b = 10;
short 2 字节 -32768 ~ 32767 short s = 1000;
int 4 字节 约 ±21 亿 int i = 100000;
long 8 字节 非常大 long l = 100L;
float 4 字节 单精度小数 float f = 3.14f;
double 8 字节 双精度小数 double d = 3.14;
char 2 字节 单个字符 char c = 'A';
boolean 1 位 true / false boolean flag = true;

注意:long 类型数字末尾要加 Lfloat 末尾要加 f,否则编译报错(默认整数是 int,小数是 double)。

2.4 引用类型(Reference Type)

除了基本类型,其他都是引用类型,常见有:

  • String:字符串,用双引号包裹。
  • 数组、类、接口、集合等(后面章节会讲)。

引用类型存的是"对象在内存中的地址",不是数据本身。

2.5 类型转换

java 复制代码
// 自动转换(小 → 大,安全)
int a = 10;
double b = a;   // int 自动转 double

// 强制转换(大 → 小,可能丢精度,需手动)
double d = 9.99;
int i = (int) d;  // 结果为 9,小数部分被截断

// String 与数字的互转
String s = "123";
int n = Integer.parseInt(s);   // 字符串 → int
String s2 = String.valueOf(n); // int → 字符串

2.6 常量(final)

不希望被修改的值用 final 修饰,相当于常量:

java 复制代码
final double PI = 3.14159;
// PI = 3.14;  // 编译报错:不能修改 final 变量

2.7 命名规范(养成好习惯)

  • 类名:大驼峰(首字母大写),如 StudentManager
  • 变量/方法名:小驼峰(首字母小写),如 studentNamegetAge()
  • 常量:全大写加下划线,如 MAX_COUNT

三、流程控制语句与逻辑判断实战

3.1 条件判断 if / else

java 复制代码
int score = 85;
if (score >= 90) {
    System.out.println("优秀");
} else if (score >= 60) {
    System.out.println("及格");
} else {
    System.out.println("不及格");
}

3.2 switch 多分支(JDK 12+ 新写法更简洁)

java 复制代码
String day = "周一";
switch (day) {
    case "周一":
    case "周二":
    case "周三":
    case "周四":
    case "周五":
        System.out.println("工作日");
        break;           // 不加 break 会"穿透"执行下一个 case
    case "周六":
    case "周日":
        System.out.println("休息日");
        break;
    default:
        System.out.println("未知");
}

JDK 14+ 还支持 switch 表达式(返回值),写法更现代,学有余力可了解。

3.3 循环:for

java 复制代码
// 打印 1~5
for (int i = 1; i <= 5; i++) {
    System.out.println("第 " + i + " 次");
}

// 增强 for(遍历数组/集合时用,后面常用)
int[] nums = {10, 20, 30};
for (int n : nums) {
    System.out.println(n);
}

3.4 循环:while 与 do-while

java 复制代码
int i = 1;
while (i <= 3) {        // 先判断,再执行
    System.out.println(i);
    i++;
}

int j = 1;
do {
    System.out.println(j);  // 先执行一次,再判断
    j++;
} while (j <= 3);

3.5 break 与 continue

  • break:跳出整个循环。
  • continue:跳过本次循环,进入下一轮。
java 复制代码
for (int i = 1; i <= 10; i++) {
    if (i % 2 == 0) continue;  // 偶数跳过
    if (i > 7) break;          // 超过 7 停止
    System.out.println("奇数:" + i);  // 输出 1 3 5 7
}

3.6 实战:简单计算器

java 复制代码
import java.util.Scanner;   // 读取键盘输入

public class Calculator {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("输入第一个数:");
        double a = sc.nextDouble();
        System.out.print("输入运算符(+ - * /):");
        String op = sc.next();
        System.out.print("输入第二个数:");
        double b = sc.nextDouble();

        double result;
        switch (op) {
            case "+": result = a + b; break;
            case "-": result = a - b; break;
            case "*": result = a * b; break;
            case "/": result = (b != 0) ? a / b : 0; break;
            default:  result = 0; System.out.println("不支持的运算符");
        }
        System.out.println("结果 = " + result);
        sc.close();
    }
}

四、面向对象编程思想与类的设计

4.1 什么是面向对象(OOP)

面向过程关注"怎么做步骤",面向对象关注"谁来做"。现实世界由对象 组成(一个学生、一本书),对象有属性(数据)行为(方法) 。把对象抽象成类(Class),用类创建具体对象,就是面向对象编程。

4.2 定义一个类

java 复制代码
public class Student {
    // 属性(字段)
    String name;
    int age;
    double score;

    // 行为(方法)
    public void study() {
        System.out.println(name + " 正在学习");
    }

    public void showInfo() {
        System.out.println("姓名:" + name + ",年龄:" + age + ",成绩:" + score);
    }
}

4.3 创建对象(实例化)

java 复制代码
public class Test {
    public static void main(String[] args) {
        Student stu = new Student();  // 用 new 创建对象
        stu.name = "小红";
        stu.age = 20;
        stu.score = 95.5;
        stu.study();
        stu.showInfo();
    }
}

4.4 构造方法

构造方法在 new 对象时自动执行,用来初始化属性。名字必须与类名相同,没有返回值。

java 复制代码
public class Student {
    String name;
    int age;

    // 无参构造
    public Student() {}

    // 有参构造
    public Student(String name, int age) {
        this.name = name;   // this 指代"当前对象"
        this.age = age;
    }
}

使用:Student stu = new Student("小明", 18);

4.5 封装(Encapsulation)------ 三大特性之一

封装 = 把属性藏起来,只通过公开的方法访问,保护数据安全。

java 复制代码
public class Student {
    private String name;     // 私有,外部不能直接访问
    private int age;

    public void setAge(int age) {
        if (age >= 0 && age <= 150) {  // 校验合法性
            this.age = age;
        } else {
            System.out.println("年龄不合法");
        }
    }

    public int getAge() {
        return age;
    }

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

这是 JavaBean 规范的雏形:private 字段 + getter/setter 方法。

4.6 继承(Inheritance)------ 三大特性之二

子类继承父类的属性和方法,实现代码复用。

java 复制代码
class Person {
    String name;
    public void eat() { System.out.println(name + " 吃饭"); }
}

class Teacher extends Person {   // Teacher 继承 Person
    public void teach() { System.out.println(name + " 讲课"); }
}

Teacher 自动拥有 nameeat(),并新增自己的 teach()

4.7 多态(Polymorphism)------ 三大特性之三

父类引用指向子类对象,运行时才决定调用哪个方法。

java 复制代码
class Animal {
    public void sound() { System.out.println("动物叫"); }
}
class Cat extends Animal {
    public void sound() { System.out.println("喵喵"); }
}
class Dog extends Animal {
    public void sound() { System.out.println("汪汪"); }
}

// 多态:统一用 Animal 类型,调用同一个方法,表现不同
Animal a1 = new Cat();
Animal a2 = new Dog();
a1.sound();  // 喵喵
a2.sound();  // 汪汪

五、常用集合框架的使用与遍历

数组长度固定,不够灵活。集合(Collection) 能动态增删元素,是日常开发的主力。

5.1 List(有序、可重复)

常用实现:ArrayList(查询快)、LinkedList(增删快)。

java 复制代码
import java.util.*;

public class ListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("苹果");
        list.add("香蕉");
        list.add("苹果");   // 允许重复

        // 遍历方式一:for 循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

        // 遍历方式二:增强 for(最常用)
        for (String s : list) {
            System.out.println(s);
        }

        // 遍历方式三:forEach + Lambda(JDK 8+)
        list.forEach(s -> System.out.println(s));

        System.out.println("长度:" + list.size());
        list.remove("香蕉");
    }
}

5.2 Set(无序、不可重复)

常用实现:HashSet。适合去重。

java 复制代码
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(1);   // 重复,不会加入
System.out.println(set.size());  // 2

for (Integer n : set) {
    System.out.println(n);
}

5.3 Map(键值对)

常用实现:HashMap。适合"通过 key 找 value",如字典、配置表。

java 复制代码
Map<String, Integer> map = new HashMap<>();
map.put("语文", 90);
map.put("数学", 95);
map.put("英语", 88);

// 遍历方式一:遍历 key
for (String key : map.keySet()) {
    System.out.println(key + ":" + map.get(key));
}

// 遍历方式二:遍历 entry(推荐,效率高)
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

System.out.println("数学分数:" + map.get("数学"));

5.4 如何选择

需求 用哪个
有序、可重复、按下标取 ArrayList
去重、不关心顺序 HashSet
键值对应(如学号→学生) HashMap

六、异常处理机制与调试技巧

6.1 什么是异常

程序运行中出现的错误叫异常,比如除以 0、读取不存在的文件。Java 用 try-catch 优雅处理,避免程序崩溃。

6.2 try-catch-finally

java 复制代码
try {
    int a = 10 / 0;          // 可能出错的代码
} catch (ArithmeticException e) {
    System.out.println("出错了:" + e.getMessage());
} finally {
    System.out.println("无论是否出错,这里都会执行");  // 常用于关闭资源
}

6.3 多重捕获与异常层级

异常有继承关系:Exception 是父类,下面是各种具体异常。可用多个 catch 分别处理,或用一个 catch (Exception e) 兜底。

java 复制代码
try {
    int[] arr = {1, 2};
    System.out.println(arr[5]);     // 数组越界
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("数组越界");
} catch (Exception e) {
    System.out.println("其他异常:" + e);
}

6.4 throw 与 throws

  • throw:在方法内主动抛出一个异常。
  • throws:在方法签名上声明该方法可能抛出异常,交给调用者处理。
java 复制代码
public static int divide(int a, int b) throws Exception {
    if (b == 0) {
        throw new Exception("除数不能为 0");
    }
    return a / b;
}

// 调用处必须处理(try-catch 或继续 throws)

6.5 自定义异常

java 复制代码
class AgeException extends Exception {
    public AgeException(String msg) { super(msg); }
}

public void setAge(int age) throws AgeException {
    if (age < 0) throw new AgeException("年龄不能为负数");
}

6.6 调试技巧(IDE 中)

以 IntelliJ IDEA / Eclipse 为例:

  1. 在代码行号左侧单击打红点(断点)。
  2. 右键 → Debug 运行。
  3. 程序在断点处暂停,可查看变量值、单步执行(Step Over / Step Into)。
  4. Watch 窗口监控表达式。
  5. 控制台查看完整异常堆栈,从最上面的"自己写的类"行定位问题。

新手排错黄金法则:认真读异常信息里的 类名 + 行号,90% 的问题都在那里。


七、文件读写操作与 IO 流应用

7.1 IO 流概念

"流(Stream)"就是数据流动的通道。按方向分:

  • 输入流 :读数据进程序(如 InputStreamReader)。
  • 输出流 :写数据出程序(如 OutputStreamWriter)。

按单位分:字节流(图片、视频等任意文件)和字符流(文本文件,带编码)。

7.2 写文本文件(字符流)

java 复制代码
import java.io.*;

public class WriteFile {
    public static void main(String[] args) {
        try (FileWriter fw = new FileWriter("note.txt");
             BufferedWriter bw = new BufferedWriter(fw)) {
            bw.write("第一行内容");
            bw.newLine();          // 换行
            bw.write("第二行内容");
        } catch (IOException e) {
            e.printStackTrace();   // 打印异常
        }
    }
}

try (...)try-with-resources 语法,写完自动关闭流,避免资源泄漏,强烈推荐。

7.3 读文本文件(字符流)

java 复制代码
import java.io.*;

public class ReadFile {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("note.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {  // 逐行读取
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

7.4 复制二进制文件(字节流)

java 复制代码
try (FileInputStream fis = new FileInputStream("a.jpg");
     FileOutputStream fos = new FileOutputStream("b.jpg")) {
    byte[] buffer = new byte[1024];
    int len;
    while ((len = fis.read(buffer)) != -1) {
        fos.write(buffer, 0, len);
    }
    System.out.println("复制完成");
} catch (IOException e) {
    e.printStackTrace();
}

7.5 路径说明

  • 相对路径 :相对当前项目目录,如 "note.txt"
  • 绝对路径 :从盘符开始,如 "D:/data/note.txt"(Windows 建议用 /\\)。

八、多线程基础与并发安全初探

8.1 为什么需要多线程

单线程一次只做一件事。多线程让程序"同时"处理多个任务,比如下载文件时界面不卡顿。Java 天然支持多线程。

8.2 创建线程的两种方式

方式一:继承 Thread

java 复制代码
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(getName() + " 执行:" + i);
        }
    }
}
// 启动:new MyThread().start();

方式二:实现 Runnable(推荐,更灵活)

java 复制代码
class MyTask implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(Thread.currentThread().getName() + " 执行:" + i);
        }
    }
}
// 启动:new Thread(new MyTask()).start();

注意:启动线程用 start(),不是直接调用 run()start() 才会真正开新线程。

8.3 线程不安全示例

多个线程同时修改同一个变量,结果可能错乱:

java 复制代码
class Counter {
    static int count = 0;
}

// 两个线程各加 1000 次,理论结果 2000,实际可能 < 2000
Runnable task = () -> {
    for (int i = 0; i < 1000; i++) Counter.count++;
};
new Thread(task).start();
new Thread(task).start();

8.4 并发安全初探:synchronized

synchronized 加锁,保证同一时间只有一个线程能执行被保护的代码:

java 复制代码
class SafeCounter {
    private int count = 0;

    public synchronized void add() {  // 加锁
        count++;
    }

    public synchronized int get() {
        return count;
    }
}

进阶可了解 ReentrantLockvolatileAtomicInteger 等并发工具(JUC 包)。

8.5 线程的状态

新建(New)→ 就绪(Runnable)→ 运行(Running)→ 阻塞(Blocked/Waiting)→ 死亡(Terminated)。理解状态有助于排查卡死问题。


九、综合案例:简易管理系统实现

把前面知识串起来,做一个学生信息管理系统 (控制台版),支持增、删、查、遍历,并用 ArrayList 存储、Scanner 交互。

java 复制代码
import java.util.*;

// 学生类(封装属性)
class Student {
    private String id;
    private String name;
    private int age;

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

    public String getId() { return id; }
    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public String toString() {
        return "学号:" + id + ",姓名:" + name + ",年龄:" + age;
    }
}

// 管理类
class StudentManager {
    private List<Student> list = new ArrayList<>();

    public void add(Student s) { list.add(s); }

    public void remove(String id) {
        list.removeIf(s -> s.getId().equals(id));
    }

    public Student find(String id) {
        for (Student s : list) {
            if (s.getId().equals(id)) return s;
        }
        return null;
    }

    public void showAll() {
        if (list.isEmpty()) {
            System.out.println("暂无学生");
            return;
        }
        for (Student s : list) System.out.println(s);
    }
}

// 主程序
public class StudentSystem {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        StudentManager mgr = new StudentManager();

        while (true) {
            System.out.println("\n===== 学生管理系统 =====");
            System.out.println("1.添加  2.删除  3.查询  4.显示全部  5.退出");
            System.out.print("请选择:");
            int choice = sc.nextInt();
            sc.nextLine();   // 吸收换行符

            switch (choice) {
                case 1:
                    System.out.print("学号:"); String id = sc.nextLine();
                    System.out.print("姓名:"); String name = sc.nextLine();
                    System.out.print("年龄:"); int age = sc.nextInt(); sc.nextLine();
                    mgr.add(new Student(id, name, age));
                    System.out.println("添加成功");
                    break;
                case 2:
                    System.out.print("输入要删除的学号:");
                    mgr.remove(sc.nextLine());
                    System.out.println("删除完成");
                    break;
                case 3:
                    System.out.print("输入要查询的学号:");
                    Student s = mgr.find(sc.nextLine());
                    System.out.println(s != null ? s : "未找到");
                    break;
                case 4:
                    mgr.showAll();
                    break;
                case 5:
                    System.out.println("再见");
                    sc.close();
                    return;
                default:
                    System.out.println("无效选择");
            }
        }
    }
}

这个案例综合运用了:类与对象(第④章)、封装与构造方法、集合 ArrayList(第⑤章)、Scanner 与循环/分支(第③章)、switchtoString 重写。你可以把它作为第一个"完整小项目"反复练习、逐步加功能(如存文件、改信息、按成绩排序)。


十、常见编译报错分析与解决策略

报错信息 原因 解决办法
error: class X is public, should be declared in a file named X.java 文件名与 public 类名不一致 让文件名=类名,或去掉 public
error: cannot find symbol 变量/方法名拼错,或作用域不对 检查拼写、是否已声明、是否导包
error: incompatible types 类型不匹配(如把 String 赋给 int) 加类型转换,或改正确类型
error: ';' expected 少了分号 在报错行末尾补 ;
error: reached end of file while parsing 大括号 {} 没配对 检查并补齐 }
Exception in thread "main" java.lang.NullPointerException 调用了 null 对象的方法 使用前判空 if (obj != null)
ArrayIndexOutOfBoundsException 数组下标越界 检查循环条件和下标范围
NumberFormatException Integer.parseInt("abc") 转非数字 确保字符串是合法数字
java: 找不到或无法加载主类 没先 javac 编译,或 java 后跟了 .class 先编译,再 java 类名(不带后缀)
编码 GBK 的不可映射字符 源码含中文但用 GBK 编译 编译时加 -encoding UTF-8javac -encoding UTF-8 X.java

排错三步法

  1. 看报错行号 :异常堆栈里 at 你的类.方法(文件名.java:行号) 直接定位到代码。
  2. 搜索关键词:把英文报错粘到搜索引擎,多半有人遇到过。
  3. 最小化复现:把出问题的代码抽成几行小程序单独跑,快速隔离原因。

学习路线建议

  • 第 1~2 周:吃透 ①~③(环境、语法、流程控制),每天写小程序。
  • 第 3~4 周:攻下 ④~⑦(面向对象、集合、异常、IO),完成第⑨章案例。
  • 第 5 周起:了解 ⑧(多线程),并尝试接入真实项目或框架(如 Spring Boot)。
  • 贯穿全程:遇到报错就回到 ⑩,把每个错误变成经验。

写在最后:编程是"做会"的,不是"看懂"的。复制本文代码运行一遍,改改参数,再自己从头敲一遍,你就已经超过了大多数只看不练的人。加油!💪

相关推荐
摩西蒙1 小时前
计算机网络
开发语言·计算机网络·php
山峰哥1 小时前
数据库性能救星:Explain执行计划深度拆解
服务器·开发语言·数据库·sql·启发式算法
执笔画流年呀1 小时前
Linux搭建Java项目部署环境
java·linux·运维
程序员天天困1 小时前
Arthas ognl 表达式从入门到实战:掌握在线调试最强的表达式引擎
java·jvm·后端
IT_Octopus2 小时前
Spring Boot 中 ThreadLocal 请求上下文的完整生命周期
java·spring boot·spring
用户40966601317512 小时前
Lombok 你用对了吗?@Data 之外的 6 个隐藏神器
java·后端·代码规范
梅头脑3 小时前
交易跨10个库、日均千万订单——选错一次分布式事务方案,加班三个月重写
java·分布式
久久学姐3 小时前
基础转码学 AI:Java+Python 双语言入门,3 个月可落地实战项目
java·python·ai·转码·实战项目
花生了什么事o3 小时前
synchronized 与 ReentrantLock:Java 锁机制原理与实现对比
java·开发语言