deeperthinker

Java 全面详解:从基础语法到生态实践

Java 全面详解:从基础语法到生态实践

一、Java 概述:定位、特点与发展历程

Java 是由 Sun Microsystems(后被 Oracle 收购)于 1995 年推出的面向对象编程语言,其核心设计理念是 “Write Once, Run Anywhere”(一次编写,到处运行),凭借跨平台性、安全性与稳定性,成为企业级应用、Android 开发、大数据处理等领域的主流技术。

1. Java 的核心定位与特点

Java 的成功源于其独特的技术特性,这些特性使其在不同场景下均能保持竞争力:

  • 跨平台性:Java 程序通过 “源代码编译为字节码(.class 文件),字节码由 Java 虚拟机(JVM)解释执行” 的流程,实现 “一次编译,多平台运行”—— 无需针对 Windows、Linux、macOS 等不同系统修改代码,只需安装对应平台的 JVM 即可运行,解决了传统 C/C++ 需针对不同系统重新编译的痛点。
  • 面向对象(OOP):Java 完全基于面向对象思想设计,将现实世界的事物抽象为 “类与对象”,通过封装、继承、多态三大特性,实现代码的模块化、可复用与可维护,是大型复杂系统开发的核心思想支撑。
  • 安全性:Java 通过多层安全机制保障程序运行,例如:字节码验证(防止恶意字节码执行)、沙箱机制(限制程序对系统资源的访问)、垃圾自动回收(避免内存泄漏与野指针),尤其适合 Web 应用与分布式系统。
  • 稳定性与可扩展性:Java 的强类型检查(编译期语法与类型校验)、异常处理机制(统一的错误处理流程)、丰富的类库(提供成熟的工具类),降低了程序崩溃风险;同时,其模块化设计(Java 9 引入 Module System)与多线程支持,让系统可轻松应对高并发与业务扩展。
  • 丰富的生态:经过近 30 年发展,Java 拥有完善的开发工具、框架(如 Spring、MyBatis)、中间件(如 Redis、RabbitMQ)与社区支持,覆盖从开发、测试到部署的全流程,降低开发成本。

2. Java 的发展历程:关键版本与特性演进

Java 的版本迭代始终围绕 “提升性能、简化开发、扩展场景” 展开,以下是影响深远的关键版本:

  • JDK 1.0(1996 年):首个正式版本,奠定 Java 核心基础,包含:面向对象语法、JVM、基础类库(java.lang、java.io)、Applet(早期 Web 客户端技术,现已淘汰),标志着 Java 正式进入商用领域。
  • JDK 1.2(1998 年):引入 “Java 2 平台” 概念,分为 J2SE(标准版,面向桌面应用)、J2EE(企业版,面向 Web 与分布式应用)、J2ME(微型版,面向嵌入式设备),同时新增集合框架(java.util.Collection)、 Swing GUI 组件,奠定 Java 多领域应用的基础。
  • JDK 5(2004 年):Java 发展的里程碑版本,引入众多革命性特性:泛型(解决类型安全与强制转换问题)、注解(简化代码配置与框架扩展)、枚举(统一常量定义)、foreach 循环(简化集合遍历)、自动装箱 / 拆箱(基本类型与包装类自动转换),大幅提升开发效率。
  • JDK 8(2014 年):至今最流行的 LTS(长期支持)版本之一,核心特性包括:Lambda 表达式(支持函数式编程,简化匿名内部类代码)、Stream API(高效处理集合数据,支持链式操作)、Optional 类(优雅处理空指针问题)、接口默认方法(接口可定义默认实现,兼容旧代码)、新日期时间 API(java.time 包,替代线程不安全的 SimpleDateFormat),这些特性让 Java 在函数式编程与大数据处理领域更具竞争力。
  • JDK 9(2017 年):引入 Module System(模块化),将 JDK 拆分为多个模块(如 java.base、java.sql),解决 “JAR 地狱” 问题,减少内存占用;同时新增 JShell(交互式编程工具,支持即时执行代码)、增强 Stream API 与集合操作。
  • JDK 11(2018 年):重要 LTS 版本,Oracle 宣布对非商业用途免费,核心更新包括:HttpClient API(替代旧的 HttpURLConnection,支持 HTTP/2 与 WebSocket)、本地变量类型推断(var 关键字,简化变量声明)、String 类新增方法(isBlank ()、lines ()、strip ())、ZGC 垃圾收集器(低延迟 GC,适合大内存应用)。
  • JDK 17(2021 年):最新 LTS 版本,整合多个重要特性:密封类(sealed class,限制类的继承范围,增强代码安全性)、switch 表达式(支持箭头语法与返回值,替代传统 switch 语句)、增强型伪随机数生成器、移除废弃 API(如 Applet 相关类),进一步提升 Java 的现代性与性能。

二、Java 核心语法:从基础到面向对象

Java 语法严谨且具有规律性,掌握基础语法是后续学习高级特性的前提,以下从 “基础语法” 与 “面向对象核心特性” 两部分展开。

1. 基础语法:数据类型、变量与控制流

(1)数据类型:基本类型与引用类型

Java 数据类型分为 “基本类型”(存储原始值)与 “引用类型”(存储对象地址),两者在内存存储与使用方式上有本质区别:

  • 基本类型(8 种)

示例:

 

// 基本类型声明与赋值

byte age = 25; // 字节型

int score = 95; // 整型(默认)

long population = 1000000000L; // 长整型(加L)

float weight = 65.5F; // 单精度浮点(加F)

double height = 1.85; // 双精度浮点(默认)

char gender = '男'; // 字符型

boolean isStudent = true; // 布尔型

    • 整数类型:byte(1 字节,范围 - 128~127)、short(2 字节)、int(4 字节,默认整数类型)、long(8 字节,声明时需加 L 后缀,如long num = 100L);
    • 浮点类型:float(4 字节,声明时需加 F 后缀,如float pi = 3.14F)、double(8 字节,默认浮点类型,如double price = 99.9);
    • 字符类型:char(2 字节,存储 Unicode 字符,用单引号包裹,如char c = 'A'、char ch = '中');
    • 布尔类型:boolean(1 字节,仅取值 true/false,用于条件判断)。
  • 引用类型:包括类(如 String、自定义类)、接口、数组、枚举等,变量存储的是对象在堆内存中的地址,而非实际值。未赋值时默认值为null(表示无引用对象)。

示例:

 

// 引用类型声明与赋值

String name = "Java"; // String类(字符串,特殊引用类型,可直接用双引号赋值)

Integer num = 100; // 基本类型包装类(引用类型)

int[] arr = {1, 2, 3}; // 数组(引用类型,存储int值的地址)

List<String> list = new ArrayList<>(); // 集合类(引用类型,需通过new创建对象)

  • 包装类与自动装箱 / 拆箱:为解决基本类型无法参与面向对象操作(如作为集合元素)的问题,Java 为每种基本类型提供对应的 “包装类”(如 int→Integer、boolean→Boolean)。Java 5 引入 “自动装箱”(基本类型自动转为包装类)与 “自动拆箱”(包装类自动转为基本类型),无需手动转换:
 

// 自动装箱:int → Integer

Integer a = 10; // 等价于 Integer a = Integer.valueOf(10);

// 自动拆箱:Integer → int

int b = a; // 等价于 int b = a.intValue();

// 包装类参与集合操作(集合仅支持引用类型)

List<Integer> numList = new ArrayList<>();

numList.add(5); // 自动装箱:5 → Integer

int sum = numList.get(0) + 3; // 自动拆箱:Integer → int,再计算

(2)变量、常量与作用域
  • 变量:存储可变数据的容器,声明格式为 “数据类型 变量名 = 初始值;”,需遵循命名规范(首字母小写,驼峰命名,如userName):
 

// 局部变量:定义在方法/代码块中,需手动初始化,作用域限于当前方法/代码块

public void printInfo() {

String message = "Hello"; // 局部变量

System.out.println(message);

}

// 成员变量:定义在类中、方法外,默认初始化(基本类型为0/false,引用类型为null),作用域限于当前类

public class User {

String name; // 成员变量(默认值null)

int age; // 成员变量(默认值0)

}

// 静态变量:用static修饰,属于类而非对象,所有对象共享,作用域限于当前类

public class MathUtil {

public static final double PI = 3.1415926; // 静态常量(final+static,值不可变)

public static int add(int a, int b) {

return a + b;

}

}

// 调用静态变量/方法:直接通过类名调用,无需创建对象

double circleArea = MathUtil.PI * 5 * 5;

  • 常量:用final修饰的变量,值一旦赋值不可修改,通常与static联用(静态常量),命名规范为全大写,下划线分隔(如MAX_SIZE):
 

// 局部常量:值不可修改

public void calculate() {

final int MAX_NUM = 100;

// MAX_NUM = 200; // 编译错误:final变量不可重新赋值

}

// 静态常量:类级别的常量,常用于定义配置参数

public class Config {

public static final String DB_URL = "jdbc:mysql://localhost:3306/test";

public static final int DB_PORT = 3306;

}

(3)运算符与表达式

Java 支持多种运算符,用于实现数据计算与逻辑判断:

  • 算术运算符:+(加)、-(减)、*(乘)、/(除,整数除法取整)、%(取余)、++(自增)、--(自减):
 

int a = 10, b = 3;

System.out.println(a + b); // 13(加)

System.out.println(a / b); // 3(整数除法,舍去小数)

System.out.println(a % b); // 1(取余)

System.out.println(++a); // 11(先自增,后取值)

System.out.println(b--); // 3(先取值,后自减)

  • 关系运算符:==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于),返回值为 boolean:
 

int x = 5, y = 8;

System.out.println(x == y); // false

System.out.println(x < y); // true

// 注意:引用类型==比较地址,equals()比较内容(需重写)

String s1 = "Java";

String s2 = new String("Java");

System.out.println(s1 == s2); // false(地址不同)

System.out.println(s1.equals(s2)); // true(内容相同,String重写了equals())

  • 逻辑运算符:&&(短路与,两边均为 true 才为 true)、||(短路或,一边为 true 即 true)、!(非,取反),用于组合布尔表达式:
 

boolean isVip = true;

int score = 90;

// 短路与:score>80为true,才判断isVip

if (score > 80 && isVip) {

System.out.println("可享受VIP折扣");

}

// 短路或:isVip为true,不再判断score<60

if (isVip || score < 60) {

System.out.println("符合条件");

}

  • 赋值运算符:=(基本赋值)、+=(加后赋值)、-=(减后赋值)等,简化赋值操作:
 

int num = 5;

num += 3; // 等价于 num = num + 3 → 8

num *= 2; // 等价于 num = num * 2 → 16

(4)控制流语句:分支与循环

控制流语句用于控制程序执行顺序,包括分支(if-else、switch)与循环(for、while、do-while)。

  • if-else 语句:根据条件执行不同代码块,支持嵌套:
 

int score = 85;

if (score >= 90) {

System.out.println("优秀");

} else if (score >= 80) {

System.out.println("良好");

} else if (score >= 60) {

System.out.println("及格");

} else {

System.out.println("不及格");

}

  • switch 语句(Java 14 + 增强):根据变量值匹配分支,支持整数、字符、字符串、枚举类型,Java 14 引入 “switch 表达式”(支持箭头语法与返回值),替代传统 break:
 

// 传统switch语句(需break防止穿透)

String day = "Monday";

switch (day) {

case "Monday":

case "Tuesday":

case "Wednesday":

case "Thursday":

case "Friday":

System.out.println("工作日");

break;

case "Saturday":

case "Sunday":

System.out.println("休息日");

break;

default:

System.out.println("无效日期");

}

// Java 14+ switch表达式(箭头语法,无需break,支持返回值)

String result = switch (day) {

case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> "工作日";

case "Saturday", "Sunday" -> "休息日";

default -> "无效日期";

};

System.out.println(result); // 输出"工作日"

  • 循环语句:重复执行代码块,包括 for、while、do-while:
 

// 1. for循环:适合已知循环次数

for (int i = 0; i < 5; i++) {

System.out.println("循环次数:" + (i+1)); // 输出1~5

}

// 2. 增强for循环(foreach):简化集合/数组遍历,无需索引

List<String> fruits = Arrays.asList("苹果", "香蕉", "橙子");

for (String fruit : fruits) {

System.out.println(fruit); // 依次输出苹果、香蕉、橙子

}

// 3. while循环:适合未知循环次数,先判断条件再执行

int count = 0;

while (count < 3) {

System.out.println("count:" + count); // 输出0、1、2

count++;

}

// 4. do-while循环:先执行一次,再判断条件(至少执行一次)

int num = 5;

do {

System.out.println("num:" + num); // 输出5

num--;

} while (num < 5);

2. 面向对象核心特性:封装、继承、多态

面向对象是 Java 的核心思想,通过 “封装、继承、多态” 三大特性实现代码的模块化与可复用,以下是详细解析:

(1)封装:隐藏实现细节,暴露安全接口

封装的核心是 “将对象的属性与方法结合为一个整体,限制外部对属性的直接访问,仅通过指定方法(getter/setter)操作属性”,实现数据安全与代码可控。

实现步骤:

  1. 用private修饰属性(私有属性,仅类内部可访问);
  1. 提供public的 getter 方法(获取属性值)与 setter 方法(设置属性值,可添加校验逻辑);
  1. 类的方法内部实现细节对外隐藏,仅暴露方法名与参数。

示例:

 

public class Student {

// 1. 私有属性(外部不可直接访问)

private String name;

private int age;

private double score;

// 2. getter方法:获取属性值

public String getName() {

return name;

}

// 3. setter方法:设置属性值,添加校验逻辑

public void setName(String name) {

// 校验:姓名不能为空

if (name == null || name.trim().isEmpty()) {

throw new IllegalArgumentException("姓名不能为空");

}

this.name = name; // this表示当前对象,区分成员变量与局部变量

}

public int getAge() {

return age;

}

public void setAge(int age) {

// 校验:年龄需在0~150之间

if (age < 0 || age > 150) {

throw new IllegalArgumentException("年龄需在0~150之间");

}

this.age = age;

}

// 4. 业务方法:内部逻辑对外隐藏,仅暴露接口

public String getGrade() {

// 根据分数计算等级(逻辑隐藏)

if (score >= 90) {

return "优秀";

} else if (score >= 80) {

return "良好";

} else if (score >= 60) {

return "及格";

} else {

return "不及格";

}

}

// 省略score的getter/setter...

}

// 使用封装类:通过getter/setter操作属性,无需关注内部校验逻辑

public class Main {

public static void main(String[] args) {

Student student = new Student();

student.setName("张三"); // 调用setter设置姓名(触发校验)

student.setAge(20); // 调用setter设置年龄(触发校验)

student.setScore(85); // 调用setter设置分数

System.out.println(student.getName()); // 调用getter获取姓名:张三

System.out.println(student.getGrade()); // 调用业务方法获取等级:良好

// student.name = "李四"; // 编译错误:private属性不可直接访问

// student.setAge(200); // 运行错误:抛出IllegalArgumentException

}

}

封装的优势:

  • 数据安全:通过 setter 校验避免非法值(如年龄为负数);
  • 代码可控:若需修改属性逻辑(如调整分数等级标准),只需修改类内部方法,外部调用无需改动;
  • 降低耦合:外部仅依赖方法接口,不依赖内部实现。
(2)继承:复用父类代码,扩展功能

继承的核心是 “子类(Subclass)继承父类(Superclass)的属性与方法,同时可添加子类特有属性与方法,或重写父类方法”,实现代码复用与功能扩展。Java 支持单继承(一个子类仅能继承一个父类),但支持多层继承(如 A→B→C)。

实现步骤:

  1. 用extends关键字声明子类继承父类;
  1. 子类自动继承父类的非 private 属性与方法;
  1. 子类可通过super关键字调用父类的构造方法、属性与方法;
  1. 子类可添加特有属性与方法,或重写父类方法(@Override 注解标识)。

示例:

 

// 1. 父类:定义通用属性与方法

public class Person {

protected String name; // protected:子类可访问,外部不可访问

protected int age;

// 父类构造方法

public Person(String name, int age) {

this.name = name;

this.age = age;

}

// 父类通用方法

public void introduce() {

System.out.println("我叫" + name + ",今年" + age + "岁");

}

}

// 2. 子类:继承Person,添加特有属性与方法,重写父类方法

public class Teacher extends Person {

// 子类特有属性

private String subject; // 教授科目

// 子类构造方法:需通过super()调用父类构造方法(必须位于第一行)

public Teacher(String name, int age, String subject) {

super(name, age); // 调用父类构造方法,初始化name与age

this.subject = subject; // 初始化子类特有属性

}

// 子类特有方法

public void teach() {

System.out.println("我教授" + subject + "课程");

}

// 重写父类方法:修改父类方法的实现(需与父类方法签名一致)

@Override

public void introduce() {

// 调用父类的introduce()方法

super.introduce();

// 添加子类特有逻辑

System.out.println("我是一名教师,教授" + subject);

}

}

// 3. 使用子类:子类可调用父类与自身的方法

public class Main {

public static void main(String[] args) {

Teacher teacher = new Teacher("李老师", 35, "数学");

teacher.introduce(); // 调用重写后的方法:输出"我叫李老师,今年35岁;我是一名教师,教授数学"

teacher.teach(); // 调用子类特有方法:输出"我教授数学课程"

System.out.println(teacher.name); // 访问父类protected属性:李老师

}

}

继承的注意事项:

  • 父类的 private 属性与方法:子类不可直接访问,需通过父类的 public getter/setter 或 protected 方法访问;
  • 构造方法:子类构造方法必须调用父类构造方法(默认调用父类无参构造,若父类无无参构造,需显式用 super () 调用父类有参构造);
  • 方法重写规则:子类方法的返回值类型、方法名、参数列表必须与父类一致;子类方法的访问修饰符不能严于父类(如父类为 public,子类不能为 private)。
(3)多态:同一行为,不同实现

多态的核心是 “父类引用指向子类对象,调用方法时根据对象的实际类型执行对应的实现”,实现代码的灵活性与扩展性,是面向对象中最强大的特性。

多态的实现条件:

  1. 存在继承关系(子类继承父类);
  1. 子类重写父类方法;
  1. 父类引用指向子类对象(Parent p = new Child();)。

示例:

 

// 1. 父类:定义抽象方法(无实现,子类必须重写)

public abstract class Animal {

// 抽象方法:用abstract修饰,无方法体,仅定义接口

public abstract void makeSound();

}

// 2. 子类1:重写抽象方法

public class Dog extends Animal {

@Override

public void makeSound() {

System.out.println("汪汪汪");

}

}

// 3. 子类2:重写抽象方法

public class Cat extends Animal {

@Override

public void makeSound() {

System.out.println("喵喵喵");

}

}

// 4. 多态应用:父类引用指向子类对象,统一调用

public class Main {

public static void main(String[] args) {

// 父类引用指向Dog对象

Animal animal1 = new Dog();

// 父类引用指向Cat对象

Animal animal2 = new Cat();

// 统一调用makeSound(),根据对象实际类型执行不同实现

animal1.makeSound(); // 输出"汪汪汪"(Dog的实现)

animal2.makeSound(); // 输出"喵喵喵"(Cat的实现)

// 多态参数:方法参数为父类类型,可接收所有子类对象

makeAnimalSound(animal1);

makeAnimalSound(animal2);

}

// 多态参数:接收Animal类型,支持所有子类

public static void makeAnimalSound(Animal animal) {

animal.makeSound();

}

}

多态的优势:

  • 代码解耦:调用方只需依赖父类接口,无需关注子类具体实现,新增子类时无需修改调用代码;
  • 扩展性强:例如新增Bird子类(重写makeSound()输出 “叽叽叽”),直接传入makeAnimalSound()方法即可,无需修改该方法;
  • 简化代码:统一用父类引用管理子类对象,减少重复代码。
(4)接口与抽象类:多态的重要载体

接口与抽象类是实现多态的重要工具,两者均不能实例化,需通过子类实现 / 继承后使用,但设计目的不同:

  • 抽象类(abstract class)

示例:

 

public abstract class Vehicle {

// 非抽象方法:通用实现(所有交通工具都需启动)

public void start() {

System.out.println("交通工具启动");

}

// 抽象方法:子类需自定义实现(不同交通工具行驶方式不同)

public abstract void run();

}

// 子类继承抽象类,重写抽象方法

public class Car extends Vehicle {

@Override

public void run() {

System.out.println("汽车在公路上行驶");

}

}

public class Plane extends Vehicle {

@Override

public void run() {

System.out.println("飞机在天空中飞行");

}

}

    • 用abstract修饰,可包含抽象方法(无实现)与非抽象方法(有实现);
    • 子类用extends继承抽象类,必须重写所有抽象方法(除非子类也是抽象类);
    • 适合定义 “具有共性的类的模板”,包含部分通用实现(非抽象方法),例如Animal抽象类包含eat()非抽象方法(所有动物都会吃)与makeSound()抽象方法(不同动物叫声不同)。
  • 接口(interface)

示例:

 

// 接口1:定义“可飞翔”行为

public interface Flyable {

// 抽象方法:无实现

void fly();

// 默认方法:有实现(Java 8+),子类可重写

default void land() {

System.out.println("降落");

}

}

// 接口2:定义“可游泳”行为

public interface Swimmable {

void swim();

}

// 类实现多个接口(多实现),重写所有抽象方法

public class Duck implements Flyable, Swimmable {

@Override

public void fly() {

System.out.println("鸭子低空飞行");

}

@Override

public void swim() {

System.out.println("鸭子在水中游");

}

// 可选:重写默认方法

@Override

public void land() {

System.out.println("鸭子在岸边降落");

}

}

// 多态应用:接口引用指向实现类对象

public class Main {

public static void main(String[] args) {

Flyable flyable = new Duck();

flyable.fly(); // 输出"鸭子低空飞行"

flyable.land(); // 输出"鸭子在岸边降落"

Swimmable swimmable = new Duck();

swimmable.swim(); // 输出"鸭子在水中游"

}

}

    • 用interface修饰,Java 8 前仅包含抽象方法(默认public abstract)与常量(默认public static final);Java 8 后支持默认方法(default修饰,有实现)与静态方法(static修饰,有实现);
    • 类用implements实现接口,必须重写所有抽象方法(除非类是抽象类);
    • 适合定义 “行为规范”,不包含具体实现(默认方法除外),例如Runnable接口定义 “可运行” 行为,Comparable接口定义 “可比较” 行为;Java 支持多实现(一个类可实现多个接口),弥补单继承的不足。
  • 接口与抽象类的区别

特性

抽象类(abstract class)

接口(interface)

继承 / 实现方式

子类用 extends 继承(单继承)

类用 implements 实现(多实现)

方法类型

可包含抽象方法与非抽象方法

Java 8 前仅抽象方法,后支持默认 / 静态方法

属性类型

可包含任意类型属性(public/private 等)

仅支持 public static final 常量

设计目的

定义类的模板,包含通用实现

定义行为规范,不依赖具体实现

实例化

不可实例化

不可实例化

三、Java 核心组件:JVM、类库与集合框架

Java 的运行与开发依赖三大核心组件:Java 虚拟机(JVM)、Java 类库(JDK API)、集合框架,理解这些组件是掌握 Java 底层原理与高效开发的关键。

1. Java 虚拟机(JVM):Java 跨平台的核心

JVM 是 “Java 虚拟机” 的缩写,是运行 Java 字节码的虚拟计算机,负责将字节码翻译为具体平台的机器码并执行,是 Java“一次编写,到处运行” 的核心保障。

(1)JVM 的核心作用
  • 字节码解释与执行:Java 源代码编译为字节码(.class 文件)后,JVM 通过 “解释器” 逐行翻译字节码为机器码,或通过 “即时编译器(JIT)” 将热点代码(频繁执行的代码)编译为机器码缓存,提升执行效率;
  • 内存管理:JVM 自动管理内存分配与回收(垃圾回收 GC),开发者无需手动申请 / 释放内存,避免内存泄漏与野指针问题;
  • 跨平台适配:不同平台(Windows、Linux)的 JVM 会将字节码翻译为对应平台的机器码,实现 “字节码与平台无关”;
  • 安全保障:JVM 包含字节码验证器(检查字节码合法性)、沙箱机制(限制程序对系统资源的访问),保障程序安全运行。
(2)JVM 内存结构

JVM 运行时将内存划分为多个区域,各区域承担不同职责,以下是 Java 8 及以后的内存结构(Java 8 移除了永久代,改用元空间):

  • 程序计数器(Program Counter Register)
    • 线程私有,存储当前线程执行的字节码指令地址(行号);
    • 若线程执行的是 Java 方法,存储字节码地址;若执行的是本地方法(native 方法),值为 undefined;
    • 唯一不会发生 OutOfMemoryError 的区域。
  • Java 虚拟机栈(Java Virtual Machine Stack)

示例(递归导致 StackOverflowError):

 

public class StackOverflowExample {

public static void main(String[] args) {

recursiveCall(); // 递归调用,栈帧不断压入栈,最终超出栈深度

}

private static void recursiveCall() {

recursiveCall(); // 无终止条件的递归

}

}

// 运行结果:Exception in thread "main" java.lang.StackOverflowError

    • 线程私有,每个线程创建时分配一个栈,存储 “栈帧”(每个方法调用对应一个栈帧);
    • 栈帧包含:局部变量表(存储方法内局部变量)、操作数栈(方法执行时的临时数据栈)、动态链接(指向常量池的方法引用)、方法返回地址(方法执行完后返回的位置);
    • 栈深度超过 JVM 限制时,抛出 StackOverflowError(如递归调用无终止条件);栈内存不足时,抛出 OutOfMemoryError。
  • 本地方法栈(Native Method Stack)
    • 线程私有,与 Java 虚拟机栈类似,但用于执行本地方法(native 方法,用 C/C++ 实现);
    • 抛出异常与 Java 虚拟机栈一致(StackOverflowError/OutOfMemoryError)。
  • Java 堆(Java Heap)

示例(堆内存溢出):

 

public class OutOfMemoryExample {

public static void main(String[] args) {

List<Object> list = new ArrayList<>();

while (true) {

list.add(new Object()); // 不断创建对象,堆内存无法回收,最终溢出

}

}

}

// 运行结果:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

    • 线程共享,JVM 启动时创建,是 JVM 内存中最大的区域,存储所有对象实例与数组;
    • 堆内存分为 “年轻代”(Young Generation,存储新创建的对象,分为 Eden 区、From Survivor 区、To Survivor 区)与 “老年代”(Old Generation,存储长期存活的对象);
    • 堆内存不足时,抛出 OutOfMemoryError(如创建大量对象且无法回收)。
  • 方法区(Method Area)
    • 线程共享,存储类信息(类名、父类、接口、方法信息)、常量、静态变量、即时编译器编译后的代码;
    • Java 8 前用 “永久代(PermGen)” 实现,Java 8 后改用 “元空间(Metaspace)”,元空间物理内存位于本地内存(而非 JVM 内存),默认无大小限制(可通过参数配置);
    • 方法区内存不足时,Java 8 前抛出 OutOfMemoryError: PermGen space,Java 8 后抛出 OutOfMemoryError: Metaspace。
  • 运行时常量池(Runtime Constant Pool)
    • 方法区的一部分,存储类编译后的常量(字符串常量、数字常量)、符号引用(类名、方法名的引用);
    • Java 7 后,字符串常量池从方法区移至堆内存,例如String s = "Java"的 “Java” 存储在堆的字符串常量池中。
(3)垃圾回收(GC):JVM 的内存自动回收机制

GC 是 JVM 的核心功能,负责回收堆内存中 “不再被引用的对象”(即死对象),释放内存空间,避免内存泄漏。

  • 垃圾判定算法
    • 可达性分析算法:JVM 通过 “GC Roots”(如虚拟机栈中的局部变量、静态变量、本地方法栈的引用)作为起点,遍历对象引用链,若对象无法通过 GC Roots 到达(引用链断裂),则判定为死对象,可被回收;
    • 常见 GC Roots:虚拟机栈中引用的对象、方法区中静态变量引用的对象、方法区中常量引用的对象、本地方法栈中 JNI(Java Native Interface)引用的对象。
  • 垃圾收集算法
    • 标记 - 清除算法(Mark-Sweep)
      1. 标记:遍历所有对象,标记出死对象;
      1. 清除:回收死对象占用的内存空间;

优点:实现简单;缺点:产生内存碎片(不连续的内存块),后续创建大对象时可能无法找到足够连续内存。

    • 复制算法(Copying)
      1. 将堆内存分为两个大小相等的区域(From 区与 To 区),仅使用 From 区;
      1. 标记存活对象,复制到 To 区;
      1. 清空 From 区,交换 From 区与 To 区的角色;

优点:无内存碎片,实现高效;缺点:内存利用率低(仅 50%),适合存活对象少的区域(如年轻代 Eden 区)。

    • 标记 - 整理算法(Mark-Compact)
      1. 标记:遍历所有对象,标记出存活对象;
      1. 整理:将存活对象向内存一端移动,集中存储;
      1. 清除:回收内存另一端的死对象空间;

优点:无内存碎片,内存利用率高;缺点:整理过程耗时,适合存活对象多的区域(如老年代)。

  • 常见垃圾收集器
    • Serial 收集器:单线程收集器,收集时暂停所有用户线程(STW,Stop The World),适合客户端应用(内存小,STW 时间短);
    • Parallel 收集器:多线程收集器,注重吞吐量(吞吐量 = 用户代码执行时间 /(用户代码执行时间 + GC 时间)),适合服务端应用(如后台计算);
    • CMS(Concurrent Mark-Sweep)收集器:并发收集器,GC 与用户线程并发执行,减少 STW 时间,适合对响应时间敏感的应用(如 Web 应用);缺点:产生内存碎片,占用 CPU 资源;
    • G1(Garbage-First)收集器:Java 9 + 默认收集器,将堆内存划分为多个大小相等的 Region,优先回收垃圾多的 Region,兼顾吞吐量与响应时间,适合大内存应用(如 10GB + 内存);
    • ZGC(Z Garbage Collector):Java 11 引入的低延迟收集器,STW 时间控制在毫秒级以下,适合大内存、低延迟需求的应用(如金融交易系统)。

2. Java 类库(JDK API):开发的基础工具集

JDK(Java Development Kit)内置丰富的类库(API),涵盖字符串处理、IO 流、网络编程、日期时间等常用功能,开发者无需重复造轮子,直接调用即可。以下是核心类库的介绍:

(1)java.lang 包:Java 的核心基础类

java.lang 是 Java 最基础的包,无需 import 即可直接使用,包含以下核心类:

  • Object 类:所有 Java 类的父类(默认继承),包含 equals ()(比较对象内容)、hashCode ()(返回对象哈希码)、toString ()(返回对象字符串表示)、wait ()/notify ()(线程通信)等方法,子类可重写这些方法;
 

public class User {

private String id;

private String name;

// 重写toString():自定义对象的字符串表示

@Override

public String toString() {

return "User{id='" + id + "', name='" + name + "'}";

}

// 重写equals()与hashCode():基于id比较对象相等性

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

User user = (User) o;

return Objects.equals(id, user.id);

}

@Override

public int hashCode() {

return Objects.hash(id);

}

}

// 使用Object类方法

User user1 = new User("1", "张三");

User user2 = new User("1", "张三");

System.out.println(user1.toString()); // 输出"User{id='1', name='张三'}"

System.out.println(user1.equals(user2)); // 输出true(id相同)

  • String 类:用于处理字符串,字符串是不可变的(值创建后无法修改),常用方法包括:

示例:

 

String s = "Hello, Java!";

System.out.println(s.length()); // 11(字符串长度)

System.out.println(s.charAt(7)); // 'J'(索引7的字符)

System.out.println(s.substring(0, 5)); // "Hello"(截取0~4索引的子串)

System.out.println(s.contains("Java")); // true(包含"Java")

System.out.println(s.replace("Java", "World")); // "Hello, World!"(替换子串)

String[] parts = s.split(", "); // 按", "分割

System.out.println(Arrays.toString(parts)); // ["Hello", "Java!"]

    • length():获取字符串长度;
    • charAt(int index):获取指定索引的字符;
    • substring(int beginIndex, int endIndex):截取子字符串;
    • equals(Object obj):比较字符串内容;
    • contains(CharSequence s):判断是否包含子串;
    • replace(CharSequence old, CharSequence new):替换子串;
    • split(String regex):按正则表达式分割字符串。
  • 包装类:如 Integer、Double、Boolean 等,提供基本类型与字符串的转换、常量定义等功能:
 

// 基本类型与字符串转换

int num = Integer.parseInt("123"); // 字符串→int

String str = Integer.toString(456); // int→字符串

// 常量定义

System.out.println(Integer.MAX_VALUE); // 2147483647(int最大值)

System.out.println(Double.NaN); // NaN(非数字)

  • Math 类:提供数学计算功能,如绝对值、三角函数、随机数等,所有方法均为静态方法:
 

System.out.println(Math.abs(-5)); // 5(绝对值)

System.out.println(Math.max(3, 7)); // 7(最大值)

System.out.println(Math.sqrt(16)); // 4.0(平方根)

System.out.println(Math.random()); // 0.0~1.0的随机数

System.out.println(Math.sin(Math.PI / 2)); // 1.0(正弦值,PI为Math类常量)

  • Thread 类与 Runnable 接口:用于多线程编程,Thread 类封装线程操作,Runnable 接口定义线程执行逻辑(详见 “并发编程” 章节)。
(2)java.util 包:工具类与集合框架

java.util 包包含常用工具类(如日期时间、随机数)与集合框架,是 Java 开发中使用最频繁的包之一。

  • 日期时间类(Java 8+ java.time 包)

Java 8 前的日期时间类(如 Date、SimpleDateFormat)线程不安全,Java 8 引入 java.time 包,提供线程安全、易用的日期时间类:

示例:

 

import java.time.*;

import java.time.format.DateTimeFormatter;

public class DateTimeExample {

public static void main(String[] args) {

// 1. 获取当前日期时间

LocalDate nowDate = LocalDate.now(); // 当前日期:2025-10-04

LocalTime nowTime = LocalTime.now(); // 当前时间:15:30:45

LocalDateTime nowDateTime = LocalDateTime.now(); // 当前日期时间:2025-10-04T15:30:45

// 2. 构造指定日期时间

LocalDate birthday = LocalDate.of(2000, 1, 1); // 2000-01-01

LocalDateTime meetingTime = LocalDateTime.of(2025, 10, 10, 14, 0); // 2025-10-10T14:00

// 3. 日期时间格式化(线程安全)

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

String formattedDateTime = nowDateTime.format(formatter); // "2025-10-04 15:30:45"

// 4. 解析字符串为日期时间

LocalDateTime parsedDateTime = LocalDateTime.parse("2025-10-01 10:00:00", formatter);

// 5. 计算时间间隔

Duration duration = Duration.between(LocalTime.of(10, 0), LocalTime.of(12, 30));

System.out.println(duration.toMinutes()); // 150(间隔150分钟)

Period period = Period.between(birthday, nowDate);

System.out.println(period.getYears()); // 25(间隔25年)

}

}

    • LocalDate:表示日期(年 / 月 / 日),无时间;
    • LocalTime:表示时间(时 / 分 / 秒),无日期;
    • LocalDateTime:表示日期时间(年 / 月 / 日 时 / 分 / 秒);
    • DateTimeFormatter:格式化日期时间(线程安全);
    • Duration:计算两个时间的间隔;
    • Period:计算两个日期的间隔。
  • 集合框架:详见下一节,是 java.util 包的核心,用于存储与操作多个对象。
(3)java.io 包与 java.nio 包:IO 流与非阻塞 IO

IO(输入 / 输出)是 Java 与外部设备(文件、网络、键盘)交互的核心,java.io 包提供传统的 “流 IO”(阻塞 IO),java.nio 包(Java 4 引入)提供 “非阻塞 IO”(NIO),支持更高效的 IO 操作。

  • java.io 包:流 IO

流 IO 按数据类型分为 “字节流”(处理二进制数据,如图片、视频)与 “字符流”(处理文本数据,如.txt 文件),按流向分为 “输入流”(从外部设备读入内存)与 “输出流”(从内存写入外部设备)。

核心字节流类:

核心字符流类:

示例(用 BufferedReader 读取文件内容):

 

import java.io.BufferedReader;

import java.io.FileReader;

import java.io.IOException;

public class FileReadExample {

public static void main(String[] args) {

// try-with-resources:自动关闭资源(Java 7+),无需手动调用close()

try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))) {

String line;

// 按行读取文件内容,直至末尾(line为null)

while ((line = reader.readLine()) != null) {

System.out.println(line);

}

} catch (IOException e) {

// 处理IO异常(如文件不存在、权限不足)

e.printStackTrace();

}

}

}

示例(用 BufferedWriter 写入文件内容):

 

import java.io.BufferedWriter;

import java.io.FileWriter;

import java.io.IOException;

public class FileWriteExample {

public static void main(String[] args) {

try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {

writer.write("Hello, Java IO!"); // 写入字符串

writer.newLine(); // 写入换行符

writer.write("这是第二行内容");

} catch (IOException e) {

e.printStackTrace();

}

}

}

    • InputStream:字节输入流抽象类,常用子类:FileInputStream(文件字节输入流)、BufferedInputStream(缓冲字节输入流,提升效率);
    • OutputStream:字节输出流抽象类,常用子类:FileOutputStream(文件字节输出流)、BufferedOutputStream(缓冲字节输出流)。
    • Reader:字符输入流抽象类,常用子类:FileReader(文件字符输入流)、BufferedReader(缓冲字符输入流,支持按行读取);
    • Writer:字符输出流抽象类,常用子类:FileWriter(文件字符输出流)、BufferedWriter(缓冲字符输出流)。
  • java.nio 包:非阻塞 IO

NIO 基于 “通道(Channel)” 与 “缓冲区(Buffer)” 实现,支持非阻塞 IO 操作(IO 操作时线程可执行其他任务),适合高并发场景(如服务器端网络编程)。

核心组件:

NIO 的优势:

    • Buffer:缓冲区,存储数据的容器(如 ByteBuffer、CharBuffer),所有 IO 操作均通过 Buffer 进行;
    • Channel:通道,连接数据源与程序的管道(如 FileChannel、SocketChannel),支持双向 IO(读写);
    • Selector:选择器,监听多个 Channel 的 IO 事件(如读就绪、写就绪),实现单线程管理多 Channel,提升并发效率。
    • 非阻塞 IO:线程无需等待 IO 操作完成,可处理其他 Channel 的事件;
    • 缓冲区操作:IO 数据先写入 Buffer,再由程序读取,减少 IO 次数;
    • Selector 管理多 Channel:单线程可处理大量并发连接,适合高并发服务器(如 Netty 框架基于 NIO 实现)。
(4)java.net包:网络编程

java.net包提供网络编程相关类,支持 TCP/IP 协议的网络通信,实现客户端与服务器的交互。

核心类:

  • Socket:客户端 Socket,用于与服务器建立连接并通信;
  • ServerSocket:服务器端 Socket,用于监听客户端连接请求;
  • InetAddress:表示 IP 地址,提供域名解析功能;
  • HttpURLConnection:用于发送 HTTP 请求(Java 11 后被 HttpClient 替代)。

示例(TCP 客户端与服务器通信):

 

// 1. TCP服务器端:监听端口,接收客户端请求并响应

import java.io.*;

import java.net.ServerSocket;

import java.net.Socket;

public class TcpServer {

public static void main(String[] args) throws IOException {

// 监听8888端口

try (ServerSocket serverSocket = new ServerSocket(8888)) {

System.out.println("服务器已启动,等待客户端连接...");

// 阻塞等待客户端连接(accept()方法)

Socket clientSocket = serverSocket.accept();

System.out.println("客户端已连接:" + clientSocket.getInetAddress());

// 获取输入流(读取客户端发送的数据)

try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

// 获取输出流(向客户端发送响应)

BufferedWriter out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))) {

// 读取客户端数据

String clientData = in.readLine();

System.out.println("收到客户端数据:" + clientData);

// 向客户端发送响应

String response = "服务器已收到:" + clientData;

out.write(response);

out.newLine(); // 换行符表示数据结束

out.flush(); // 刷新缓冲区,确保数据发送

}

}

}

}

// 2. TCP客户端:连接服务器,发送数据并接收响应

import java.io.*;

import java.net.Socket;

public class TcpClient {

public static void main(String[] args) throws IOException {

// 连接服务器(IP为localhost,端口8888)

try (Socket socket = new Socket("localhost", 8888);

// 获取输出流(向服务器发送数据)

BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

// 获取输入流(读取服务器响应)

BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {

// 向服务器发送数据

String sendData = "Hello, TCP Server!";

out.write(sendData);

out.newLine();

out.flush();

// 读取服务器响应

String serverResponse = in.readLine();

System.out.println("收到服务器响应:" + serverResponse);

}

}

}

运行步骤:

  1. 先启动 TcpServer,服务器监听 8888 端口;
  1. 启动 TcpClient,客户端连接服务器并发送 “Hello, TCP Server!”;
  1. 服务器接收数据后,响应 “服务器已收到:Hello, TCP Server!”;
  1. 客户端接收响应并输出。

3. 集合框架:高效管理与操作数据集合

集合框架是 java.util 包的核心,用于存储、管理、操作多个对象(集合仅支持引用类型),替代传统数组(数组长度固定,功能单一),提供动态扩容、排序、查找等功能。

(1)集合框架的体系结构

Java 集合框架分为两大体系:

  • Collection 接口:存储单个元素的集合,子接口包括:
    • List:有序、可重复的集合(元素有索引,可通过索引访问),常用实现类:ArrayList、LinkedList、Vector;
    • Set:无序、不可重复的集合(元素无索引,基于 equals () 与 hashCode () 判断唯一性),常用实现类:HashSet、TreeSet、LinkedHashSet;
    • Queue:队列(先进先出 FIFO),常用实现类:LinkedList(实现 Queue 接口)、PriorityQueue(优先级队列)。
  • Map 接口:存储键值对(key-value)的集合(key 不可重复,value 可重复),常用实现类:HashMap、TreeMap、LinkedHashMap、Hashtable、ConcurrentHashMap。
(2)Collection 接口核心实现类
① List 接口:有序可重复集合
  • ArrayList

示例:

 

import java.util.ArrayList;

import java.util.List;

public class ArrayListExample {

public static void main(String[] args) {

// 创建ArrayList(存储String类型)

List<String> list = new ArrayList<>();

// 添加元素

list.add("Java");

list.add("Python");

list.add("C++");

list.add("Java"); // 允许重复元素

// 按索引访问元素

System.out.println(list.get(0)); // "Java"(索引0)

// 修改元素

list.set(1, "JavaScript"); // 将索引1的元素改为"JavaScript"

// 遍历元素(三种方式)

// 1. 增强for循环

for (String lang : list) {

System.out.print(lang + " "); // 输出"Java JavaScript C++ Java "

}

// 2. 普通for循环(利用索引)

for (int i = 0; i < list.size(); i++) {

System.out.print(list.get(i) + " ");

}

// 3. 迭代器(Iterator)

java.util.Iterator<String> iterator = list.iterator();

while (iterator.hasNext()) {

System.out.print(iterator.next() + " ");

}

// 删除元素(按索引/元素)

list.remove(2); // 删除索引2的元素("C++")

list.remove("Java"); // 删除第一个"Java"

// 判断元素是否存在

System.out.println(list.contains("JavaScript")); // true

// 获取集合大小

System.out.println(list.size()); // 2(剩余"JavaScript"与"Java")

}

}

    • 底层基于动态数组实现(数组容量不足时自动扩容,默认初始容量 10,扩容为原容量的 1.5 倍);
    • 优点:随机访问效率高(通过索引直接访问,时间复杂度 O (1));
    • 缺点:插入 / 删除元素效率低(需移动数组元素,时间复杂度 O (n));
    • 适用场景:频繁读取,少量插入 / 删除。
  • LinkedList

示例(作为队列使用):

 

import java.util.LinkedList;

import java.util.Queue;

public class LinkedListQueueExample {

public static void main(String[] args) {

// 创建Queue(LinkedList实现)

Queue<String> queue = new LinkedList<>();

// 入队(添加元素到队尾)

queue.offer("A");

queue.offer("B");

queue.offer("C");

// 查看队头元素(不删除)

System.out.println(queue.peek()); // "A"

// 出队(删除并返回队头元素)

while (!queue.isEmpty()) {

System.out.print(queue.poll() + " "); // 输出"A B C "(FIFO)

}

}

}

    • 底层基于双向链表实现(每个元素存储前驱与后继节点地址);
    • 优点:插入 / 删除元素效率高(只需修改链表节点指针,时间复杂度 O (1));
    • 缺点:随机访问效率低(需遍历链表,时间复杂度 O (n));
    • 适用场景:频繁插入 / 删除,少量读取;同时实现 Queue 接口,可作为队列使用。
  • Vector
    • 底层基于动态数组实现,与 ArrayList 类似,但线程安全(所有方法用 synchronized 修饰);
    • 缺点:线程安全导致性能较低,现代开发中多使用 ArrayList + 手动同步(如 Collections.synchronizedList ())或并发集合(如 CopyOnWriteArrayList)替代;
    • 适用场景:多线程环境下的 List 操作(但不推荐,优先用并发集合)。
② Set 接口:无序不可重复集合
  • HashSet

示例:

 

import java.util.HashSet;

import java.util.Set;

public class HashSetExample {

public static void main(String[] args) {

Set<String> set = new HashSet<>();

// 添加元素(重复元素会被过滤)

set.add("Apple");

set.add("Banana");

set.add("Apple"); // 重复元素,添加失败

set.add("Orange");

// 遍历元素(无序,无索引,不可用普通for循环)

// 1. 增强for循环

for (String fruit : set) {

System.out.print(fruit + " "); // 输出顺序不确定(如"Apple Banana Orange ")

}

// 2. 迭代器

java.util.Iterator<String> iterator = set.iterator();

while (iterator.hasNext()) {

System.out.print(iterator.next() + " ");

}

// 判断元素是否存在

System.out.println(set.contains("Banana")); // true

// 删除元素

set.remove("Orange");

// 获取集合大小

System.out.println(set.size()); // 2(剩余"Apple"与"Banana")

}

}

    • 底层基于 HashMap 实现(存储元素作为 HashMap 的 key,value 为固定对象);
    • 特点:无序(元素存储顺序与插入顺序无关)、不可重复(基于 equals () 与 hashCode () 判断,若两个元素 equals () 为 true 且 hashCode () 相等,则视为同一元素);
    • 优点:添加 / 删除 / 查找效率高(时间复杂度 O (1));
    • 适用场景:无需排序,仅需去重的场景。
  • TreeSet

示例(自定义排序):

 

import java.util.Comparator;

import java.util.TreeSet;

public class TreeSetExample {

public static void main(String[] args) {

// 自定义排序:按字符串长度降序排列

TreeSet<String> set = new TreeSet<>(new Comparator<String>() {

@Override

public int compare(String s1, String s2) {

// 长度降序:s2.length() - s1.length()

int lenDiff = s2.length() - s1.length();

if (lenDiff != 0) {

return lenDiff;

}

// 长度相同则按字典序升序

return s1.compareTo(s2);

}

});

set.add("Apple"); // 5个字符

set.add("Banana"); // 6个字符

set.add("Orange"); // 6个字符

set.add("Grape"); // 5个字符

// 遍历元素(按自定义排序)

for (String fruit : set) {

System.out.print(fruit + " "); // 输出"Banana Orange Apple Grape "

}

}

}

    • 底层基于 TreeMap 实现(红黑树结构);
    • 特点:有序(默认按元素自然排序,如 String 按字典序、Integer 按数值序)、不可重复;
    • 优点:支持排序与范围查询;
    • 缺点:添加 / 删除 / 查找效率低于 HashSet(时间复杂度 O (log n));
    • 适用场景:需要排序的去重场景,可通过 Comparator 自定义排序规则。
  • LinkedHashSet
    • 底层基于 LinkedHashMap 实现,在 HashSet 基础上维护双向链表,记录元素插入顺序;
    • 特点:有序(按插入顺序)、不可重复;
    • 优点:兼顾 HashSet 的高效性与插入顺序的保留;
    • 适用场景:需要去重且保留插入顺序的场景。
③ Queue 接口:队列集合

Queue 是 “先进先出(FIFO)” 的集合,常用实现类包括 LinkedList(普通队列)与 PriorityQueue(优先级队列,按优先级排序,非 FIFO)。

  • PriorityQueue

示例:

 

import java.util.PriorityQueue;

import java.util.Queue;

public class PriorityQueueExample {

public static void main(String[] args) {

// 优先级队列:默认按Integer自然排序(升序,最小元素在队头)

Queue<Integer> queue = new PriorityQueue<>();

// 添加元素

queue.offer(5);

queue.offer(2);

queue.offer(8);

queue.offer(1);

// 出队(按优先级,依次输出1、2、5、8)

while (!queue.isEmpty()) {

System.out.print(queue.poll() + " ");

}

// 自定义优先级:按Integer降序排序

Queue<Integer> descQueue = new PriorityQueue<>((a, b) -> b - a);

descQueue.offer(5);

descQueue.offer(2);

descQueue.offer(8);

descQueue.offer(1);

// 出队(依次输出8、5、2、1)

while (!descQueue.isEmpty()) {

System.out.print(descQueue.poll() + " ");

}

}

}

    • 底层基于二叉堆实现,元素按优先级排序(默认自然排序,可自定义 Comparator);
    • 特点:队头元素为优先级最高的元素(如 Integer 默认最小的元素在队头);
    • 适用场景:需要按优先级处理任务的场景(如任务调度)。
(3)Map 接口核心实现类

Map 存储键值对(key-value),key 不可重复(重复添加会覆盖原 value),value 可重复,常用实现类如下:

  • HashMap

示例:

 

import java.util.HashMap;

import java.util.Map;

import java.util.Set;

public class HashMapExample {

public static void main(String[] args) {

// 创建HashMap(存储<String, Integer>键值对)

Map<String,</doubaocanvas>

    • 底层基于 “数组 + 链表 / 红黑树” 实现(Java 8 前为数组 + 链表,Java 8 后当链表长度超过 8 时转为红黑树,提升查询效率);
    • 特点:无序(key 存储顺序与插入顺序无关)、key 不可重复、value 可重复;
    • 优点:添加 / 删除 / 查找效率高(时间复杂度 O (1),红黑树为 O (log n));
    • 缺点:线程不安全(多线程并发修改可能导致死循环或数据异常);
    • 适用场景:单线程环境下的键值对存储,是开发中最常用的 Map 实现。

                

posted on 2025-10-04 17:05  gamethinker  阅读(30)  评论(0)    收藏  举报  来源

导航