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)操作属性”,实现数据安全与代码可控。
实现步骤:
- 用private修饰属性(私有属性,仅类内部可访问);
- 提供public的 getter 方法(获取属性值)与 setter 方法(设置属性值,可添加校验逻辑);
- 类的方法内部实现细节对外隐藏,仅暴露方法名与参数。
示例:
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)。
实现步骤:
- 用extends关键字声明子类继承父类;
- 子类自动继承父类的非 private 属性与方法;
- 子类可通过super关键字调用父类的构造方法、属性与方法;
- 子类可添加特有属性与方法,或重写父类方法(@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)多态:同一行为,不同实现
多态的核心是 “父类引用指向子类对象,调用方法时根据对象的实际类型执行对应的实现”,实现代码的灵活性与扩展性,是面向对象中最强大的特性。
多态的实现条件:
- 存在继承关系(子类继承父类);
- 子类重写父类方法;
- 父类引用指向子类对象(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):
-
-
- 标记:遍历所有对象,标记出死对象;
-
-
-
- 清除:回收死对象占用的内存空间;
-
优点:实现简单;缺点:产生内存碎片(不连续的内存块),后续创建大对象时可能无法找到足够连续内存。
-
- 复制算法(Copying):
-
-
- 将堆内存分为两个大小相等的区域(From 区与 To 区),仅使用 From 区;
-
-
-
- 标记存活对象,复制到 To 区;
-
-
-
- 清空 From 区,交换 From 区与 To 区的角色;
-
优点:无内存碎片,实现高效;缺点:内存利用率低(仅 50%),适合存活对象少的区域(如年轻代 Eden 区)。
-
- 标记 - 整理算法(Mark-Compact):
-
-
- 标记:遍历所有对象,标记出存活对象;
-
-
-
- 整理:将存活对象向内存一端移动,集中存储;
-
-
-
- 清除:回收内存另一端的死对象空间;
-
优点:无内存碎片,内存利用率高;缺点:整理过程耗时,适合存活对象多的区域(如老年代)。
- 常见垃圾收集器:
-
- 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 地址,提供域名解析功能;
- URL:表示统一资源定位符(如http://www.example.com),用于访问网络资源;
- 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);
}
}
}
运行步骤:
- 先启动 TcpServer,服务器监听 8888 端口;
- 启动 TcpClient,客户端连接服务器并发送 “Hello, TCP Server!”;
- 服务器接收数据后,响应 “服务器已收到:Hello, TCP Server!”;
- 客户端接收响应并输出。
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) 收藏 举报 来源
浙公网安备 33010602011771号