Java 核心知识整理
一、Java 语言概述
1. 诞生背景
- 1995 年 6 月由 Sun Microsystems 公司推出,创始人 James Gosling(Java 之父)。
2. 技术体系架构
(1)核心版本分类
- Java SE(标准版):基础开发平台。
- Java ME(微型版):针对移动设备优化。
- Java EE(企业版):用于构建大型分布式应用。
(2)运行环境组件
- JDK(Java Development Kit):开发工具包,包含 JRE 和编译工具(如
javac)、运行工具(如java)等。 - JRE(Java Runtime Environment):运行环境,包含 JVM 和核心类库。
- JVM(Java Virtual Machine):虚拟机,实现跨平台的字节码执行。
3. 核心开发工具
| 工具名称 | 功能描述 |
|---|---|
javac.exe |
编译器,将 .java 源代码编译为 .class 字节码。 |
java.exe |
运行器,启动 JVM 执行字节码(格式:java 类名)。 |
javadoc.exe |
文档生成器,根据代码注释生成 HTML 格式的 API 文档。 |
jar.exe |
打包工具,将类文件及资源打包为 JAR 文件(支持双击运行)。 |
4. 语言特性
- 面向对象:支持封装、继承、多态,以对象为代码组织单位。
- 平台无关性:通过 JVM 实现“一次编写,到处运行”。
- 自动内存管理:垃圾回收机制(GC)自动处理内存分配与回收。
- 多线程支持:内置线程模型,需注意线程安全。
二、Java 语法基础
1. 标识符与注释
(1)标识符规则
- 由字母、数字、
_、$组成,以字母、_、$开头,区分大小写。 - 命名规范:变量/方法名小写驼峰,类名大写驼峰,常量全大写(如
MAX_VALUE)。
(2)注释类型
- 行注释:
// 单行注释 - 块注释:
/* 多行注释 */ - 文档注释:
/** 文档注释 */
2. 数据类型
(1)基本数据类型
| 类型 | 字节数 | 范围(示例) |
|---|---|---|
byte |
1 | -128 ~ 127 |
short |
2 | -32768 ~ 32767 |
int |
4 | -2³¹ ~ 2³¹-1 |
long |
8 | -2⁶³ ~ 2⁶³-1(后缀 L/l) |
float |
4 | 单精度浮点型(后缀 f/F) |
double |
8 | 双精度浮点型(默认,如 3.14) |
char |
2 | Unicode 字符(如 'A'、'\u0041') |
boolean |
1 | true/false |
说明:
long类型赋值时需加后缀L(如100L),避免整数溢出。float类型赋值时需加后缀f(如3.14f),否则会被视为double类型。char可存储单个 Unicode 字符,支持转义字符(如'\n'表示换行)。
(2)引用数据类型
| 分类 | 描述 |
|---|---|
| 数组 | 存储固定大小的同类型元素,通过索引访问。 |
| 类 | 自定义类型,包含属性和方法。 |
| 接口 | 抽象方法的集合,实现类必须实现所有接口方法。 |
| 枚举 | 特殊类,表示一组固定常量。 |
| 字符串 | 不可变的字符序列,属于 java.lang 包。 |
| 集合框架 | 动态存储对象的容器,包含 List、Set、Map 等接口及实现类。 |
| 包装类 | 基本类型的对象形式,用于泛型、集合等场景。 |
| 其他常见类 | 包括日期时间、文件操作、异常类等。 |
说明:
- 引用类型变量存储的是对象的内存地址,而非对象本身。
- 所有引用类型的默认值为
null,使用前需初始化(通过new或赋值)。 - 集合框架 常用实现类:
ArrayList(动态数组)、LinkedList(双向链表)HashSet(无序唯一)、TreeSet(有序唯一)HashMap(键值对,无序)、TreeMap(键值对,按键排序)
3. 运算符
| 类型 | 运算符列表 | 示例与说明 |
|---|---|---|
| 算术运算符 | +、-、*、/、%(取模)、++(自增)、--(自减)、-(负号) |
5 / 2 → 2(整数除法取整)5 % 2 → 1a++(先使用后自增) vs ++a(先自增后使用) |
| 关系运算符 | ==、!=、<、>、<=、>=、instanceof(类型检查) |
3 > 5 → falseobj instanceof String → true/false |
| 逻辑运算符 | &&(短路与)、` |
|
| 位运算符 | &(按位与)、` |
(按位或)、^(按位异或)、~(取反)、«(左移)、»(带符号右移)、»>`(无符号右移) |
| 赋值运算符 | =、+=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、` |
=、^=` |
| 三元运算符 | 条件表达式 ? 表达式1 : 表达式2 |
int max = (a > b) ? a : b;(若 a > b 则返回 a,否则返回 b) |
优先级与结合性(由高到低):
- 括号:
() - 单目运算符:
++、--、!、~、-(负号) - 算术运算符:
*、/、%→+、- - 移位运算符:
<<、>>、>>> - 关系运算符:
<、>、<=、>=、instanceof→==、!= - 位运算符:
&→^→| - 逻辑运算符:
&&→|| - 三元运算符:
? : - 赋值运算符:
=、+=、-=等
三、流程控制与数组
1. 流程控制语句
(1)条件语句
| 类型 | 语法与特性 |
|---|---|
| if-else | 多分支判断,支持嵌套。条件表达式必须为 boolean 类型。 |
| switch | JDK 7+ 支持 String,JDK 14+ 支持箭头语法和 yield。匹配失败执行 default。 |
示例代码:
// if-else 示例
if (score >= 90) {
System.out.println("A");
} else if (score >= 80) {
System.out.println("B");
} else {
System.out.println("C");
}
// switch 示例(JDK 14+ 语法)
switch (day) {
case "Monday" -> System.out.println("工作日");
case "Sunday" -> System.out.println("休息日");
default -> System.out.println("其他");
}
注意事项:
switch表达式的返回值类型必须与case常量类型一致。- 避免
if-else嵌套过深(超过3层),建议重构为switch或多态。
(2)循环语句
| 类型 | 适用场景 |
|---|---|
| for | 已知循环次数,支持初始化、条件和迭代部分。 |
| while | 未知循环次数,先判断条件再执行。 |
| do-while | 至少执行一次,后判断条件。 |
| for-each | 简化数组或集合遍历,无需索引。 |
示例代码:
// for 循环示例
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
// while 循环示例
int i = 0;
while (i < 10) {
System.out.println(i++);
}
// do-while 循环示例
int j = 0;
do {
System.out.println(j++);
} while (j < 10);
// for-each 循环示例
int[] arr = {1, 2, 3};
for (int num : arr) {
System.out.println(num);
}
控制关键字:
break:终止整个循环。continue:跳过当前循环,进入下一次迭代。return:结束方法,返回结果(若有)。
2. 数组
(1)定义方式
。
// 一维数组
int[] arr1 = new int[5]; // 动态初始化(默认值:0)
int[] arr2 = {10, 20, 30}; // 静态初始化
String[] arr3 = new String[]{"A", "B", "C"}; // 完整语法
// 多维数组
int[][] matrix = new int[3][4]; // 3行4列的二维数组
int[][] triangle = {{1}, {2, 3}, {4, 5, 6}}; // 不规则二维数组
// 动态初始化二维数组
int[][] dynamic = new int[2][]; // 先定义行
dynamic[0] = new int[3]; // 再定义列
dynamic[1] = new int[2];
(2)常用操作
获取长度
// 一维数组长度
int len1 = arr.length;
// 二维数组行数
int rows = matrix.length;
// 二维数组列数(假设matrix至少有一行)
int cols = matrix[0].length;
遍历数组
// for循环(带索引)
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// for-each循环(简化遍历)
for (int num : arr) {
System.out.println(num);
}
数组拷贝
// 浅拷贝(仅复制引用,共享同一对象)
int[] copy1 = arr;
// 深拷贝方式一:使用Arrays.copyOf
int[] copy2 = Arrays.copyOf(arr, arr.length);
// 深拷贝方式二:使用System.arraycopy(效率更高)
int[] copy3 = new int[arr.length];
System.arraycopy(arr, 0, copy3, 0, arr.length);
数组排序
// 升序排序(基本类型数组)
int[] arr = {5, 3, 8};
Arrays.sort(arr); // 结果:[3, 5, 8]
// 降序排序(需使用包装类型)
Integer[] arrObj = {3, 1, 2};
Arrays.sort(arrObj, Collections.reverseOrder()); // 结果:[3, 2, 1]
查找元素
// 线性查找(适用于未排序数组)
int target = 5;
int index = -1;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) {
index = i;
break;
}
}
// 二分查找(仅适用于已排序数组)
Arrays.sort(arr); // 先排序
int pos = Arrays.binarySearch(arr, target);
if (pos >= 0) {
// 找到元素,pos为索引
} else {
// 未找到元素,pos为插入点取反-1
}
注意事项:
- 数组下标从
0开始,访问越界会抛出ArrayIndexOutOfBoundsException。 - 数组一旦创建,长度不可变;若需动态扩容,可使用
ArrayList。
四、面向对象编程
1. 类与对象
| 概念 | 描述 |
|---|---|
| 类 | 对象的抽象模板,包含属性(成员变量)和行为(成员方法),是面向对象编程的基础单元。 |
| 对象 | 类的实例化结果,通过 new 关键字创建,每个对象独立拥有属性值和方法调用能力。 |
| 封装 | 通过访问修饰符(private、public、protected)隐藏内部实现细节,仅暴露必要接口。 |
核心特性说明:
- 访问修饰符优先级:
private(类内可见) <default(包内可见) <protected(子类+包内可见) <public(全局可见)。 - 构造方法:无返回值,名称与类名相同,用于对象初始化;若未显式定义,编译器自动生成无参构造。
示例代码:
// 类的完整定义(含封装)
public class Person {
// 私有属性(封装核心数据)
private String name;
private int age;
private static final String DEFAULT_COUNTRY = "China"; // 静态常量
// 构造方法(初始化对象状态)
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 公共方法(暴露访问接口)
public void sayHello() {
System.out.println("Hello, my name is " + name + ", age " + age);
}
// Getter/Setter(属性访问控制)
public String getName() { return name; }
public void setName(String name) {
if (name != null && !name.isEmpty()) {
this.name = name;
}
}
public int getAge() { return age; }
public void setAge(int age) {
if (age >= 0 && age <= 150) {
this.age = age;
}
}
// 静态方法(属于类而非实例)
public static String getDefaultCountry() {
return DEFAULT_COUNTRY;
}
}
// 对象创建与使用示例
public class ObjectDemo {
public static void main(String[] args) {
// 实例化对象
Person person = new Person("Alice", 25);
// 调用方法
person.sayHello(); // 输出:Hello, my name is Alice, age 25
// 通过Setter修改属性
person.setName("Bob");
person.setAge(30);
person.sayHello(); // 输出:Hello, my name is Bob, age 30
// 访问静态成员
System.out.println(Person.getDefaultCountry()); // 输出:China
}
}
2. 子类与继承
| 概念 | 描述 |
|---|---|
| 继承 | 子类通过 extends 关键字继承父类的属性和方法,实现代码复用,Java 仅支持单继承。 |
| 方法重写(Override) | 子类重新定义父类同名方法,需满足方法签名一致,访问权限不低于父类。 |
| 多态 | 父类引用指向子类对象,运行时根据实际对象类型动态调用方法,是面向对象的核心特性。 |
关键规则:
super关键字:用于访问父类成员(构造方法、属性、方法),子类构造方法必须首行调用父类构造(隐式调用super())。- 重写限制:父类
final方法不可重写,private方法子类无法继承(不存在重写问题)。
示例代码:
// 父类定义
public class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void sayHello() {
System.out.println("Hello, I'm a person.");
}
public void showInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
// 子类继承示例
public class Student extends Person {
private String studentId;
private double score;
// 子类构造方法必须调用父类构造
public Student(String name, int age, String studentId, double score) {
super(name, age); // 显式调用父类构造
this.studentId = studentId;
this.score = score;
}
// 方法重写示例
@Override
public void sayHello() {
super.sayHello(); // 调用父类方法
System.out.println("I'm a student, ID: " + studentId);
}
// 子类特有方法
public void showScore() {
System.out.println("Score: " + score);
}
}
// 多态应用示例
public class PolymorphismDemo {
public static void main(String[] args) {
// 父类引用指向子类对象
Person p1 = new Student("Bob", 20, "S001", 90.5);
Person p2 = new Person("Alice", 25);
// 动态调用方法(根据实际对象类型)
p1.sayHello(); // 输出子类重写的方法
p1.showInfo(); // 输出父类方法(未重写)
p2.sayHello(); // 输出父类方法
// 多态与类型转换
if (p1 instanceof Student) {
Student student = (Student) p1; // 向下转型
student.showScore(); // 调用子类特有方法
}
}
}
3. 接口与实现
| 概念 | 语法与特性 |
|---|---|
| 接口 | 使用 interface 定义抽象方法集合,方法默认 public abstract,变量默认 public static final。 |
| 实现接口 | 类通过 implements 关键字实现接口,需重写所有抽象方法,可实现多个接口。 |
| 默认方法(Java 8+) | 接口中使用 default 修饰的方法,提供默认实现,实现类可选择重写。 |
核心优势:
- 解耦设计:接口定义行为规范,实现类专注具体逻辑,符合“面向接口编程”原则。
- 多实现能力:弥补 Java 单继承局限,一个类可实现多个接口,实现混合特性。
示例代码:
// 接口定义(含抽象方法和默认方法)
public interface Flyable {
// 抽象方法(必须实现)
void fly();
// 默认方法(可选重写)
default void takeOff() {
System.out.println("Preparing to take off...");
}
// 静态方法(接口直接调用)
static void printFlyableInfo() {
System.out.println("This is a flyable object.");
}
}
// 接口定义(继承其他接口)
public interface Swimmable {
void swim();
}
// 类实现多个接口示例
public class Duck implements Flyable, Swimmable {
private String name;
public Duck(String name) {
this.name = name;
}
// 实现Flyable接口方法
@Override
public void fly() {
System.out.println(name + " is flying low over the water.");
}
@Override
public void takeOff() {
System.out.println(name + " is flapping wings to take off.");
}
// 实现Swimmable接口方法
@Override
public void swim() {
System.out.println(name + " is swimming in the water.");
}
}
// 接口使用示例
public class InterfaceDemo {
public static void main(String[] args) {
Duck duck = new Duck("Donald");
duck.fly(); // 输出:Donald is flying low over the water.
duck.takeOff(); // 输出:Donald is flapping wings to take off.
duck.swim(); // 输出:Donald is swimming in the water.
// 调用接口静态方法
Flyable.printFlyableInfo(); // 输出:This is a flyable object.
}
}
4. 内部类与异常类
(1)内部类
| 类型 | 特性 |
|---|---|
| 成员内部类 | 定义在类内部,无 static 修饰,可访问外部类所有成员,依赖外部类实例。 |
| 静态内部类 | 使用 static 修饰,仅能访问外部类静态成员,不依赖外部类实例。 |
| 局部内部类 | 定义在方法或代码块中,作用域仅限于当前区域,可访问所在方法的局部变量(需为 final 或隐式 final)。 |
| 匿名内部类 | 没有类名的内部类,常用于创建接口或抽象类的实例,语法简洁但功能有限。 |
示例代码:
// 成员内部类示例
public class OuterClass {
private int outerField = 10;
private static int staticOuterField = 20;
// 成员内部类(非静态)
public class InnerClass {
private int innerField = 5;
// 访问外部类成员
public void accessOuter() {
System.out.println("Outer field: " + outerField); // 访问外部类实例变量
System.out.println("Static outer field: " + staticOuterField); // 访问静态变量
}
// 外部类方法中创建内部类实例
public void innerMethod() {
System.out.println("Inner method, field: " + innerField);
}
}
// 静态内部类示例
public static class StaticInnerClass {
private int staticInnerField = 100;
// 仅能访问外部类静态成员
public void accessOuter() {
System.out.println("Static outer field: " + staticOuterField);
// 无法访问 outerField(非静态)
}
}
// 局部内部类示例
public void localInnerDemo() {
final int localVar = 50; // 局部变量需为final或隐式final
// 局部内部类定义在方法中
class LocalInner {
public void printLocal() {
System.out.println("Local variable: " + localVar);
}
}
LocalInner local = new LocalInner();
local.printLocal();
}
// 匿名内部类示例
public void anonymousInnerDemo() {
// 创建接口的匿名实现
Flyable flyable = new Flyable() {
@Override
public void fly() {
System.out.println("Anonymous inner class is flying.");
}
};
flyable.fly();
}
}
// 内部类使用示例
public class InnerClassDemo {
public static void main(String[] args) {
// 创建成员内部类实例
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.accessOuter();
inner.innerMethod();
// 创建静态内部类实例
OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();
staticInner.accessOuter();
// 调用局部内部类方法
outer.localInnerDemo();
// 调用匿名内部类方法
outer.anonymousInnerDemo();
}
}
(2)异常类
| 概念 | 特性 |
|---|---|
| 异常体系 | 所有异常继承自 Throwable,分为 Error(系统错误,不可恢复)和 Exception(程序异常)。 |
| 受检异常 | 继承自 Exception 且非 RuntimeException,必须在方法中处理或声明。 |
| 非受检异常 | 继承自 RuntimeException,无需强制处理,通常表示程序逻辑错误。 |
| 自定义异常 | 继承 Exception 或 RuntimeException,用于封装特定业务场景的错误。 |
异常处理核心语法:
try {
// 可能抛出异常的代码
} catch (ExceptionType1 e1) {
// 处理异常类型1
} catch (ExceptionType2 e2) {
// 处理异常类型2
} finally {
// 无论是否异常都会执行(如资源释放)
}
示例代码:
// 自定义受检异常(继承Exception)
class AgeValidationException extends Exception {
public AgeValidationException(String message) {
super(message);
}
}
// 自定义非受检异常(继承RuntimeException)
class NegativeValueException extends RuntimeException {
public NegativeValueException(String message) {
super(message);
}
}
// 使用异常的业务类
public class Person {
private int age;
private double salary;
// 抛出受检异常的方法
public void setAge(int age) throws AgeValidationException {
if (age < 0 || age > 150) {
throw new AgeValidationException("年龄必须在0-150之间,当前输入:" + age);
}
this.age = age;
}
// 抛出非受检异常的方法
public void setSalary(double salary) {
if (salary < 0) {
throw new NegativeValueException("薪资不能为负数,当前输入:" + salary);
}
this.salary = salary;
}
// 异常链示例
public void processData() {
try {
// 调用可能抛出异常的方法
setAge(-5);
} catch (AgeValidationException e) {
// 包装异常并抛出,保留原始异常信息
throw new IllegalStateException("数据处理失败", e);
}
}
}
// 异常处理示例
public class ExceptionDemo {
public static void main(String[] args) {
Person person = new Person();
// 处理受检异常
try {
person.setAge(200);
} catch (AgeValidationException e) {
System.err.println("错误:" + e.getMessage());
e.printStackTrace(); // 打印异常栈轨迹
}
// 处理非受检异常
try {
person.setSalary(-5000);
} catch (NegativeValueException e) {
System.err.println("运行时错误:" + e.getMessage());
}
// 处理异常链
try {
person.processData();
} catch (IllegalStateException e) {
System.err.println("业务异常:" + e.getMessage());
System.err.println("原始异常:" + e.getCause()); // 获取原始异常
}
// try-with-resources示例(自动关闭资源)
try (java.io.FileReader reader = new java.io.FileReader("test.txt")) {
int data = reader.read();
// 处理文件读取
} catch (java.io.IOException e) {
System.err.println("文件读取失败:" + e.getMessage());
}
}
}
五、常用实用类
1. 字符串处理
| 类 | 特性 |
|---|---|
String |
不可变,每次操作生成新对象。 |
StringBuilder |
可变,非线程安全,性能高。 |
示例代码:
// String 示例
String s1 = "hello";
String s2 = s1 + " world"; // 生成新字符串
// StringBuilder 示例
StringBuilder sb = new StringBuilder();
sb.append("a").append("b"); // 高效拼接
2. 随机数生成
示例代码:
// 使用 Math.random() 生成指定范围整数
int randomNum = (int)(Math.random() * 9000 + 1000); // 生成 1000~9999 的整数
// 使用 Random 类生成随机数
Random random = new Random();
int rand = random.nextInt(100); // 生成 [0, 100) 的整数
3. 数组操作技巧
示例代码:
// 二维数组赋值示例
String[] str2 = {"1 2", "3 4"};
String[][] str1 = new String[str2.length][];
for (int j = 0; j < str2.length; j++) {
str1[j] = str2[j].split(" "); // 按空格分割字符串并赋值给二维数组
}
// 数组复制示例
int[] src = {1, 2, 3, 4, 5};
int[] dest = new int[5];
System.arraycopy(src, 1, dest, 0, 3); // 从src[1]复制3个元素到dest[0]
4. 日期与时间(Java 8+)
| 类 | 描述 |
|---|---|
LocalDate |
日期(年、月、日),不含时间。 |
LocalDateTime |
日期时间,包含年、月、日、时、分、秒。 |
示例代码:
// LocalDate 示例
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(2000, 1, 1);
// LocalDateTime 示例
LocalDateTime dt = LocalDateTime.now();
dt.plusDays(1); // 加一天
5. 包装类
| 基本类型 | 包装类 |
|---|---|
int |
Integer |
double |
Double |
示例代码:
// 自动装箱/拆箱示例
Integer num = 10; // 自动装箱(int → Integer)
int value = num; // 自动拆箱(Integer → int)
Double d = 3.14; // 自动装箱(double → Double)
double primitive = d; // 自动拆箱(Double → double)
六、泛型编程
1. 泛型基础概念
定义:泛型是一种参数化类型的编程模式,将类型作为参数传递给类、接口或方法,实现“类型安全的参数化”。
核心优势:
- 类型安全:编译时检查类型,避免类型转换异常(如
ClassCastException)。 - 代码复用:一套逻辑支持多种数据类型,减少重复代码。
- 可读性提升:明确标注数据类型,增强代码可维护性。
2. 泛型类与泛型方法
泛型类语法:
public class 类名<T1, T2, ...> { // 类型参数列表(可多个)
private T1 field1;
private T2 field2;
public T1 getField1() { return field1; }
public void setField1(T1 field1) { this.field1 = field1; }
// ...
}
示例代码:
// 泛型类:通用容器 Box
public class Box<T> {
private T content; // 类型参数 T 作为属性类型
public Box() {}
public Box(T content) {
this.content = content;
}
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return content;
}
// 泛型类中定义泛型方法
public <S> Box<S> createBox(S content) {
return new Box<>(content);
}
}
// 泛型方法:打印任意类型数组
public class GenericMethods {
// 类型参数 T 定义在方法签名中
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
// 多类型参数方法
public static <K, V> void printMap(Map<K, V> map) {
map.forEach((k, v) -> System.out.println(k + " -> " + v));
}
}
// 使用示例
public class GenericDemo {
public static void main(String[] args) {
// 泛型类实例化(类型擦除前)
Box<Integer> intBox = new Box<>(100);
Box<String> strBox = new Box<>("Hello");
intBox.setContent(200);
System.out.println(intBox.getContent()); // 输出:200
// 调用泛型方法
Integer[] nums = {1, 2, 3, 4};
String[] names = {"Alice", "Bob"};
GenericMethods.printArray(nums); // 输出:1 2 3 4
GenericMethods.printArray(names); // 输出:Alice Bob
// 泛型类中的泛型方法
Box<Double> doubleBox = intBox.createBox(3.14);
System.out.println(doubleBox.getContent()); // 输出:3.14
}
}
3. 泛型边界与通配符
(1)类型边界(Type Bounds)
上界语法:T extends 类型(表示 T 是该类型或其子类型)
下界语法:T super 类型(表示 T 是该类型或其父类型)
示例场景:
- 计算数字平均值:需限制类型为
Number及其子类(T extends Number)。 - 插入整数到集合:需限制类型为
Integer及其父类(T super Integer)。
(2)通配符(Wildcards)
| 通配符 | 含义 | 示例场景 |
|---|---|---|
? |
未知类型(等同于 ? extends Object) |
仅读取不修改的场景 |
? extends T |
上界通配符:类型为 T 或其子类型 | 遍历继承体系中的类型 |
? super T |
下界通配符:类型为 T 或其父类型 | 向集合中添加元素 |
示例代码:
// 上界通配符:打印数字集合(? extends Number)
public static void printNumbers(List<? extends Number> list) {
for (Number num : list) {
System.out.println(num);
}
// list.add(1); 报错:无法向? extends Number的集合添加元素
}
// 下界通配符:向集合添加整数(? super Integer)
public static void addIntegers(List<? super Integer> list) {
list.add(10);
list.add(20);
// Integer num = list.get(0); 报错:无法确定具体类型
}
// 无界通配符:读取集合元素(?)
public static void printElements(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
// list.add("abc"); 报错:未知类型无法添加
}
// 多边界泛型:类型同时继承多个接口(T extends A & B & C)
public interface Printable { void print(); }
public interface Comparable<T> { int compareTo(T o); }
public class GenericBounds<T extends Printable & Comparable<T>> {
public void process(T obj) {
obj.print();
// 使用 Comparable 方法
}
}
// 使用示例
public class WildcardDemo {
public static void main(String[] args) {
List<Integer> intList = Arrays.asList(1, 2, 3);
List<Double> doubleList = Arrays.asList(3.14, 2.71);
List<Object> objList = new ArrayList<>();
printNumbers(intList); // 输出整数
printNumbers(doubleList); // 输出浮点数
addIntegers(intList); // 向Integer集合添加元素
addIntegers(objList); // 向Object集合添加元素
// 多边界示例(假设Integer实现了Printable接口)
GenericBounds<Integer> processor = new GenericBounds<>();
processor.process(100);
}
}
4. 类型擦除与限制
类型擦除机制:
- 泛型是编译时特性,运行时会擦除类型参数,替换为其边界类型(默认
Object)。 - 擦除导致无法在运行时获取具体类型(如
new T()会报错)。
泛型限制:
- 不能使用基本类型作为类型参数(如
Box<int>非法,需用Box<Integer>)。 - 无法实例化类型参数(如
T t = new T();非法)。 - 不能使用
static修饰类型参数(如static T field;非法)。 - 泛型数组创建受限(如
T[] arr = new T[10];非法,需强制转换)。
示例代码(类型擦除演示):
public class ErasureDemo {
public static void main(String[] args) {
Box<Integer> intBox = new Box<>(10);
Box<String> strBox = new Box<>("Hello");
// 运行时类型擦除,类型参数被替换为Object
System.out.println(intBox.getClass() == strBox.getClass()); // 输出:true
// 泛型限制示例
// Box<int> errorBox = new Box<int>(5); // 编译错误:不能使用基本类型
// T[] arr = new T[10]; // 编译错误:无法创建泛型数组
}
}
// 泛型限制解决方案
public class GenericRestrictions<T> {
// 方案1:通过反射实例化类型参数
public T createInstance(Class<T> clazz) throws Exception {
return clazz.getDeclaredConstructor().newInstance();
}
// 方案2:使用数组包装类
public static <T> List<T> createList(Class<T> clazz, int size) {
T[] arr = (T[]) new Object[size]; // 强制转换,需确保类型安全
return Arrays.asList(arr);
}
}
七、数据结构(集合框架)
1. 集合接口与实现类
| 接口 | 特点 | 实现类 | 适用场景 |
|---|---|---|---|
List |
有序(索引访问)、可重复,支持动态扩容。 | ArrayList(动态数组)、LinkedList(双向链表)、Vector(线程安全) |
频繁随机访问,优先用 ArrayList频繁插入/删除,优先用 LinkedList |
Set |
无序(除 TreeSet)、元素唯一(通过 equals() 和 hashCode() 保证)。 |
HashSet(基于哈希表)、LinkedHashSet(插入顺序)、TreeSet(自然排序) |
去重场景 需要排序时用 TreeSet |
Map |
键值对(键唯一),键不可重复,值可重复。 | HashMap(无序)、LinkedHashMap(插入顺序)、TreeMap(键排序)、ConcurrentHashMap(线程安全) |
快速键值查找 需要排序时用 TreeMap多线程环境用 ConcurrentHashMap |
关键区别:
-
ArrayListvsLinkedList:ArrayList基于数组,随机访问快(O(1)),插入/删除慢(O(n))。LinkedList基于链表,随机访问慢(O(n)),插入/删除快(O(1),首尾操作)。
-
HashMapvsLinkedHashMapvsTreeMap:HashMap:无序,性能最高(平均 O(1))。LinkedHashMap:维护插入顺序,遍历时按插入顺序返回。TreeMap:按键的自然顺序或自定义比较器排序,插入/删除/查询均为 O(log n)。
2. 常用操作示例
示例代码:
// List 操作示例(ArrayList vs LinkedList)
List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();
// 通用操作
arrayList.add("apple");
arrayList.add("banana");
arrayList.add(1, "cherry"); // 指定位置插入
System.out.println(arrayList.get(1)); // 输出:cherry
System.out.println(arrayList.size()); // 输出:3
arrayList.remove(0); // 按索引删除
// LinkedList 特有操作(双向链表)
linkedList.addFirst("head");
linkedList.addLast("tail");
System.out.println(linkedList.getFirst()); // 输出:head
System.out.println(linkedList.poll()); // 移除并返回头部元素
// Set 操作示例
Set<Integer> hashSet = new HashSet<>();
Set<Integer> treeSet = new TreeSet<>();
// 通用操作
hashSet.add(3);
hashSet.add(1);
hashSet.add(2);
System.out.println(hashSet.contains(2)); // 输出:true
System.out.println(hashSet.size()); // 输出:3
hashSet.remove(1);
// TreeSet 特有操作(有序)
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
System.out.println(treeSet.first()); // 输出:1(最小值)
System.out.println(treeSet.last()); // 输出:3(最大值)
// Map 操作示例
Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
Map<String, Integer> treeMap = new TreeMap<>();
// 通用操作
hashMap.put("one", 1);
hashMap.put("two", 2);
hashMap.put("three", 3);
System.out.println(hashMap.get("two")); // 输出:2
System.out.println(hashMap.containsKey("three")); // 输出:true
hashMap.remove("one");
// LinkedHashMap 维护插入顺序
linkedHashMap.put("b", 2);
linkedHashMap.put("a", 1);
linkedHashMap.put("c", 3);
System.out.println(linkedHashMap.keySet()); // 输出:[b, a, c]
// TreeMap 按键排序(自然顺序)
treeMap.put("b", 2);
treeMap.put("a", 1);
treeMap.put("c", 3);
System.out.println(treeMap.keySet()); // 输出:[a, b, c]