Java 基础教程
Big Fish Lv1

随机数

在java中有常见的两种生成随机数的方法是Math.random() 和java.util.Random类

基本用法

Math.random() 方法

功能: 生成[0.0,1.0)范围内的double类型的伪随机数,线程安全的

语法:Math.ranndom()

1
2
3
4
5
6
7
8
9
// 0-100
int randomInt = (int)(Math.random() * 100)

// 1-30
int randomInt = (int)(Math.random() * 30) + 1

// 生成 [5, 20] 的随机整数(通用公式)
int min = 5, max = 20;
int randomInt = (int) (Math.random * (max - min + 1)) + min

java.util.Random类

基本用法

  • 功能:生成多种类型的随机数(整数、浮点数、布尔值等)。线程不安全
  • 语法
1
2
Random random = new Random(); // 默认种子为系统时间
Random seededRandom = new Random(123L); // 指定种子

核心方法

方法 返回值 范围
nextInt() int 所有可能的 int
nextInt(int bound) int [0, bound)
nextDouble() double [0.0, 1.0)
nextBoolean() boolean truefalse
nextLong() long 所有可能的 long

函数的可变形参

声明方式: function test(int a, int b, 类型 ….参数名)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.Arrays;

public class Main {

public static void main(String[] args) {
test(1,2,3,4,5);
}

public static void test(int a, int b, int ...c) {

for(int i = 0; i < c.length; i++) {
System.out.print(c[i] + " ");
}
}
}

关键字 package import

package

package,称为包,用于指明该文件中定义的类、接口等结构所在的包。

语法格式:package 顶层包名.子包名 ;

举例:pack1\pack2\PackageTest.java

1
2
3
4
5
6
7
package pack1.pack2;    
//指定类PackageTest 属于包pack1.pack2
public class PackageTest{
public void display(){
System.out.println("in method display()");
}
}

说明:

• 一个源文件只能有一个声明包的package语句

• package语句作为Java源文件的第一条语句出现。若缺省该语句,则指定为无名包。

• 包名,属于标识符,满足标识符命名的规则和规范(全部小写)、见名知意 – – 包通常使用所在公司域名的倒置:com.atguigu.xxx。 大家取包名时不要使用”java.xx”包

• 包对应于文件系统的目录,package语句中用 “.” 来指明包(目录)的层次,每.一次就 表示一层文件目录。

• 同一个包下可以声明多个结构(类、接口),但是不能定义同名的结构(类、接口)。 不同的包下可以定义同名的结构(类、接口) 8.1.2 包的作用

• 包可以包含类和子包,划分项目层次,便于管理

• 帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如:MVC的设计模 式

• 解决类命名冲突的问题 • 控制访问权限

import

为了使用定义在其它包中的Java类,需用import语句来显式引入指定包下所 需要的类。相当于import语句告诉编译器到哪里去寻找这个类。

  • 语法格式 import 包名.类名;

  • 应用举例

1
2
3
4
5
6
7
import pack1.pack2.Test;   //import pack1.pack2.*;表示引入pack1.pack2 包中的所有结构  
public class PackTest{
public static void main(String args[]){
Test t = new Test();
t.display();
}
}

instanceof 关键字

判断某个对象是否属于某个类

1
a对象 instalof A类  如果a对象是A类型的子类 则是true 反之false

getClass() 获取运行时类

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.test.www.utils;

public class MyArrays {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.getClass()); //打印运行时类:class com.test.www.utils.Person
}
}


class Person{

}

interface 接口

接口是什么?怎么使用?

  • 接口就是一种规范,只描述能做什么,不关心怎么做。

  • 接口定义了一组方法,要求实现它的类必须完成这些方法。

  • 接口不能 new 实例,只能由类来实现(implements)。

比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface Animal {
void eat();
void sleep();
}

//然后任何动物类,比如狗(Dog)、猫(Cat),都可以去实现它:

public class Dog implements Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}

@Override
public void sleep() {
System.out.println("狗趴着睡觉");
}
}

接口可以定义什么?

  • 抽象方法

  • 默认方法

  • 静态方法

  • 私有方法(用于默认方法或静态方法内部)

  • 常量

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface ExampleInterface {

// 1. 抽象方法(必须由实现类去实现)
void abstractMethod();

// 2. 默认方法(接口可以直接提供实现,子类可以选择重写)
default void defaultMethod() {
System.out.println("这是一个默认方法。");
helperMethod(); // 调用私有方法
}

// 3. 静态方法(只能通过接口名调用)
static void staticMethod() {
System.out.println("这是一个静态方法。");
}

// 4. 私有方法(只能在接口内部使用,帮助 default 或 static 方法复用代码)
private void helperMethod() {
System.out.println("这是一个私有的辅助方法。");
}
}

什么时候用接口? 什么时候用抽象类?

如果一组类需要保证某种行为的一致,就用接口 就比如 飞机可以射击, 手枪可以射击, 但是它们两个就不属于同一类

再比如:所有支付方式(支付宝、微信、银行卡)都应该有 pay() 方法,不管内部逻辑不同。

抽象类是”是什么”的体现,如果想体现一种继承关系就用抽象类 比如 动物 老虎, 狮子, 狗 都属于动物, 它们是同类关系. 也都可以相同的方法, 比如吃, 走, 叫.玩.

接口冲突

一个类实现了多个接口,而这些接口中定义了相同名字的默认方法,常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.test.www.utils;

interface A{
int a = 10;
default void show(){
System.out.println("接口A的show方法");
}
}
interface B{
int a = 20;
default void show(){
System.out.println("接口B的show方法");
}
}


public class MyArrays {
public static void main(String[] args) {
Person person = new Person();
person.show(); //接口A的show方法
person.printConst(); // 10
}
}


class Person implements A,B{

@Override
public void show() {
A.super.show();
}

public void printConst(){
System.out.println(A.a);
}
}

内部类

什么是内部类?

内部类就定义在类中的另一个类(就像”套娃”一样)

为什么要有内部类?

  • 让两个类之间的关系更紧密
  • 内部类可以直接访问外部类的属性和方法(包括私有的)
  • 有些类只服务器外部类的逻辑,不需要被外界知道
  • 简化代码,增加封装性

内部类分类

  • 成员内部类(类中定义)
  • 局部内部类(方法里定义)
  • 匿名内部类(临时创建,没有名字)
  • 静态内部类(static 修饰 属于外部类本身)

成员内部类

  • 定义在外部类的成员位置,没有 static 关键字。
  • 可以直接访问外部类的所有成员变量和方法(包括private的)必须通过外部类对象才能创建内部类对象!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Outer {
private String name = "外部类";

class Inner {
public void show() {
System.out.println("访问:" + name);
}
}

private void test(){

Inner inner = new Inner(); // 重点写法!
inner.show();
}
}

局部内部类(定义在方法内部)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Outer {
void method() {
class Inner {
void show() {
System.out.println("局部内部类的方法");
}
}
Inner inner = new Inner();
inner.show();
}
}

public class Test {
public static void main(String[] args) {
Outer outer = new Outer();
outer.method();
}
}

特点:

只在方法里局部有效。

访问外部变量时,如果访问的是方法里的局部变量,那么那个局部变量必须是 final(或者事实上的 final)。

(事实上的 final:就是变量一旦赋值后,后面没有再改变)

匿名内部类(超重要,尤其是配合接口)

没有名字的内部类,经常用来临时实现接口或抽象类,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Animal {
void eat();
}

public class Test {
public static void main(String[] args) {
Animal cat = new Animal() {
@Override
public void eat() {
System.out.println("小猫吃鱼");
}
};
cat.eat();
}
}

一边定义类,一边创建对象。

常用于简化代码,比如事件监听器、回调函数等。

静态内部类(加了 static)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Outer {
static class Inner {
void show() {
System.out.println("静态内部类的方法");
}
}
}

public class Test {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner(); // 不需要外部类对象
inner.show();
}
}

枚举类

什么是枚举类?

枚举就是一组固定的常量集合,放在一个类里集中管理

比如一周的七天、四季、订单状态(已下单、已发货、已签收)等等,这些都可以用枚举。

枚举可以有属性、方法、构造器,而且还可以像普通类那样继承接口!

在 Java 里,用 enum 关键字来定义枚举类。

简单示例:

1
2
3
public enum WeekDay {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

枚举类的基础用法

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

使用方式:

1
2
Color c = Color.RED;
System.out.println(c); // 输出:RED

每个枚举都是一个对象(单例模式)

在 Java 中,Color.RED、Color.GREEN 这些都是 Color 类的对象,而且是唯一的(单例)。

枚举遍历(values() 方法)

1
2
3
4
5
6
7
8
for (Color color : Color.values()) {
System.out.println(color);
}

//输出:
//RED
//GREEN
//BLUE

获取枚举名字(name() 方法)

1
System.out.println(Color.RED.name());  // 输出:RED

获取枚举的序号(ordinal() 方法)

1
2
System.out.println(Color.RED.ordinal());  // 输出:0
System.out.println(Color.GREEN.ordinal()); // 输出:1

枚举的高级用法(带属性、方法)

枚举不仅能存常量,还能存数据和行为!

比如订单状态,就可以定义不同的编码和描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public enum OrderStatus {
CREATED(1, "已创建"),
PAID(2, "已支付"),
SHIPPED(3, "已发货"),
RECEIVED(4, "已收货");

private final int code;
private final String description;

// 构造方法(必须是 private)
OrderStatus(int code, String description) {
this.code = code;
this.description = description;
}

// getter方法
public int getCode() {
return code;
}

public String getDescription() {
return description;
}
}


使用方法:

1
2
3
OrderStatus status = OrderStatus.PAID;
System.out.println(status.getCode()); // 输出 2
System.out.println(status.getDescription()); // 输出 已支付

枚举还能实现接口!

枚举可以像普通类一样实现接口,比如:

1
2
3
4
5
6
7
8
9
10
11
12
interface Message {
void printMessage();
}

public enum Mood implements Message {
HAPPY, SAD, ANGRY;

@Override
public void printMessage() {
System.out.println("当前心情是:" + this.name());
}
}

总结:

枚举的构造方法默认是 private,不能写 public。

枚举不能直接继承其他类(因为 Java 里每个枚举类都默认继承自 java.lang.Enum)。

可以实现接口,但不能继承别的类。

可以定义抽象方法,让每个枚举对象单独实现不同的行为!(更高级)


注解

什么是注解?

  • 用生活中的例子解释(如商品标签、书的批注)

  • 技术定义:Java 5引入的元数据形式,用于为代码提供附加信息

  • 与注释(comments)的区别:注解会被编译器处理,能被运行时读取

  • 展示常见内置注解:@Override, @Deprecated, @SuppressWarnings

注解的作用?

  • 编译时信息:如@Override帮助编译器检查

  • 运行时处理:框架如Spring通过运行时注解实现功能

  • 代码生成:如Lombok通过注解生成代码

基本语法

1
2
3
4
5
6
7
8
9
// 定义注解
public @interface MyAnnotation {
String value() default "default";
int count() default 1;
}

// 使用注解
@MyAnnotation(value = "test", count = 5)
public class MyClass { ... }

元注解

  • @Target:指定注解可以应用的位置(类、方法、字段等)
    • TYPE: 代表类, 接口,枚举
    • FIELD: 属性
    • METHOD: 方法
    • PARAMETER: 形参
    • CONSTRUCTOR: 构造器
    • LOCAL_VARIABLE: 局部变量
    • ANNOTATION_TYPE:注解类型(表示只可以用于注解)
    • PACKAGE: 包
    • TYPE_PARAMETER: 参数类型(表示指可以用于参数类型)
  • @Retention:指定注解保留策略(SOURCE, CLASS, RUNTIME)
    • SOURCE: 保留在源码阶段,编译器编译后直接丢弃该注解信息
    • CLASS:保留在字节码阶段,该注解会记录在类文件中,但运行时会丢弃
    • RUNTIME: 保留到运行期间,即在运行期间都可以读取该注释,,程序员自定义注释都使用的这个策略
  • @Documented:是否包含在Javadoc中
  • @Inherited:是否允许子类继承

自定义注解

自定义注解的语法:

1
2
3
4
@元注解
[修饰符] @interface 注解名{
返回值类型 方法名() default 默认返回值
}

在java中注解被看做一种特殊的接口 ,注解中可以没有任何成员, 也可以声明一个或多个抽象方法, 这里的抽象方法不能声明参数列表,返回值类型只能是8大基础类型和String, enum, class,array,泛型,其他注解类型

如果注解只有一个抽象方法, 那么建议抽象方法名为value. value名的抽象方法在使用该注解的时候可以省略value= 而是直接指定返回值

一般注解配合反射才有意义

由于java 没有带注解的检测. 所以一般在开发中常用第三方检测框架

  1. Checker Framework 编译时检测框架
  2. Hiberanate Validator检测框架

@Retention - 指定注解保留策略

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 该注解在运行时可用,可以通过反射读取
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeAnnotation {
}

// 该注解只在源码中保留,编译时会被丢弃
@Retention(RetentionPolicy.SOURCE)
public @interface SourceAnnotation {
}

@Target - 指定注解使用目标

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

// 只能用于方法
@Target(ElementType.METHOD)
public @interface MethodAnnotation {
}

// 可用于类、接口、枚举
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface TypeAndFieldAnnotation {
}

@Documented - 包含在Javadoc中

1
2
3
4
5
6
7
import java.lang.annotation.Documented;

// 该注解会出现在Javadoc中
@Documented
public @interface DocumentedAnnotation {
String value();
}

@Inherited - 允许子类继承

1
2
3
4
5
6
7
import java.lang.annotation.Inherited;

// 父类上的该注解会被子类继承
@Inherited
public @interface InheritedAnnotation {
String value() default "default";
}

@Repeatable - 允许重复使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.lang.annotation.Repeatable;

// 定义容器注解
public @interface Roles {
Role[] value();
}

// 定义可重复注解
@Repeatable(Roles.class)
public @interface Role {
String value();
}

// 使用示例
@Role("admin")
@Role("user")
public class User {
}

自定义注解实战案例

REST API权限控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 定义权限级别枚举
public enum AccessLevel {
PUBLIC, USER, ADMIN, SUPER_ADMIN
}

// 定义权限注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessControl {
AccessLevel requiredLevel() default AccessLevel.USER;
}

// 使用示例
public class UserController {

@AccessControl(requiredLevel = AccessLevel.ADMIN)
public void deleteUser(String userId) {
// 只有管理员可以调用
}

@AccessControl(requiredLevel = AccessLevel.PUBLIC)
public void getUserInfo(String userId) {
// 公开接口
}
}

数据库表映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
String name();
String schema() default "";
}

// 列注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
String name();
String type();
boolean nullable() default true;
int length() default 255;
}

// 使用示例
@Table(name = "employees", schema = "hr")
public class Employee {
@Column(name = "emp_id", type = "BIGINT", nullable = false)
private Long id;

@Column(name = "emp_name", type = "VARCHAR", length = 100)
private String name;

@Column(name = "salary", type = "DECIMAL")
private BigDecimal salary;
}

单元测试标记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 测试类别注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TestCategory {
String[] categories();
}

// 性能测试标记
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PerformanceTest {
int expectedTime() default 100; // 毫秒
}

// 使用示例
public class CalculatorTest {

@Test
@TestCategory(categories = {"fast", "math"})
public void testAdd() {
// 快速数学测试
}

@Test
@PerformanceTest(expectedTime = 500)
@TestCategory(categories = {"slow", "integration"})
public void testComplexCalculation() {
// 性能敏感测试
}
}

日志切面标记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 日志注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Loggable {
Level value() default Level.INFO;
boolean params() default true;
boolean result() default false;

enum Level {
DEBUG, INFO, WARN, ERROR
}
}

// 使用示例
public class OrderService {

@Loggable(Level.INFO, params = true, result = true)
public Order createOrder(OrderRequest request) {
// 方法调用和返回将被记录
}

@Loggable(Level.ERROR)
public void cancelOrder(String orderId) {
// 只在出错时记录
}
}

简单注解处理器示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.Element;
import java.util.Set;

@SupportedAnnotationTypes("com.example.NotNull")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class NotNullProcessor extends AbstractProcessor {

@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(NotNull.class)) {
// 检查字段是否有初始化
// 检查方法参数是否被检查
// 如果不满足条件,生成编译错误
processingEnv.getMessager().printMessage(
Diagnostic.Kind.ERROR,
"@NotNull字段必须被正确初始化",
element);
}
return true;
}
}

String类

字符串一般存储在堆/常量池中. String是不可变的序列,真正因为是不可变的序列所以JVM专门为字符串提供了一个常量池,凡是放在常量池的字符串对象都可以共享

例如

1
2
3
String s1 = "hello world";
String s2 = "hello world";
System.out.println(s1 == s2) //true

当然也不是所有的方式都放在常量池中.

1
2
3
String s1 = new String("hello world");
String s2 = new String("hello world");
System.out.println(s1 == s2);// false

上面的代码返回的是false, 说明他们指向了不同的对象

哪些字符串是放在常量池中?

  • 直接”xxx” 得到的字符串放在常量池
  • 直接 “xxx” + “xxx” 得到的字符串放在常量池
  • 两个指向”xxx”常量的拼接放在常量池
  • 所有字符串对象.intern()方法得到的结果放在常量池中

除了上面4种方式其他都放在堆中

1
2
3
4
5
6
7
8
9
10
11
12
String s1 = "hello world"; //常量池
String s2 = "hello" + "world" //常量池
String s3 = s1 + s2 //堆
final String s4 = "hello ";
final String s5 = "world";
String s6 = s4 + s5; //常量池
String s7 = (s3 + s4).intertn();//常量池
String s8 = s4 + "world"; //堆
String s9 = new String("hello world");//堆
String s10 = String.valueof(new char[]{'h','e','l','l','o'});//堆
String s11 = s1.concat(s2);//堆

虽然有些字符串是存储在堆中,但是值还是指向了常量池

可变字符序列 StringBuffer 和 StringBuilder

String类生成的对象是不可变的,如果涉及频繁的修改和拼接,则效率会很低,所以如果一个字符串需要经常更新,就考虑用可变字符序列StringBuffer和StringBuilder,它们都在java.lang包下

StringBuffer和StringBuilder的区别

StringBuffer是线程安全的, 一般涉及多线程的时候就可以用StringBuffer

StringBuilder是线程不安全的, 一般单线程的时候使用 StringBuilder要比StringBuffer的效率要高

集合

java集合是一个特别有用的工作类,可以存储数量不等到多个对象

ArrayList(最常用的列表)

场景:维护用户列表、购物车、结果列表等

优点:查询快,随机访问快

缺点:中间插入或删除效率低(要移动后面元素)

1
2
3
4
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
System.out.println(list.get(1)); // 输出:李四

LinkedList

场景:双端操作频繁(插入、删除)
优点:插入/删除快(特别是头尾)
缺点:查询慢,内存开销大(链式结构)

1
2
3
4
LinkedList<String> list = new LinkedList<>();
list.addFirst("头部");
list.addLast("尾部");
System.out.println(list.removeFirst()); // 输出:头部

HashSet

场景:需要元素去重、不关心顺序
优点:查找快,去重能力强
缺点:无序、不保证遍历顺序

1
2
3
4
5
Set<String> set = new HashSet<>();
set.add("A");
set.add("B");
set.add("A"); // 无效,A已存在
System.out.println(set); // 输出无序元素

TreeSet

场景:需要元素自动排序 + 去重
优点:元素有序(自然排序或自定义 Comparator)
缺点:插入/查询性能比 HashSet 慢(红黑树结构)

1
2
3
4
5
Set<Integer> set = new TreeSet<>();
set.add(3);
set.add(1);
set.add(2);
System.out.println(set); // 输出:[1, 2, 3]

LinkedHashSet

场景:需要去重并保留插入顺序
优点:保留顺序、无重复元素
缺点:性能不如 HashSet,略占内存

1
2
3
4
5
Set<String> set = new LinkedHashSet<>();
set.add("a");
set.add("b");
set.add("a"); // 不会添加
System.out.println(set); // 输出:[a, b]

PriorityQueue

场景:需要自动排序的队列(最小值或最大值优先)
优点:每次出队都是最小(或最大)元素,适合调度器
缺点:不支持按插入顺序遍历

1
2
3
4
5
6
PriorityQueue<Integer> queue = new PriorityQueue<>();
queue.add(5);
queue.add(1);
queue.add(3);
System.out.println(queue.poll()); // 输出:1(最小)


Queue(以 LinkedList 实现)

场景:标准队列结构(先进先出)
优点:双端操作,支持 offer / poll 等队列方法
缺点:不支持线程安全,不适合并发使用

1
2
3
4
5
Queue<String> queue = new LinkedList<>();
queue.offer("A");
queue.offer("B");
System.out.println(queue.poll()); // 输出:A

HashMap

场景:快速存储和查找键值对
优点:查找、插入、删除快(基于哈希)
缺点:无序,不保证遍历顺序;线程不安全

1
2
3
4
Map<String, String> map = new HashMap<>();
map.put("name", "张三");
map.put("age", "25");
System.out.println(map.get("name")); // 输出:张三

TreeMap

场景:需要按 key 排序访问
优点:自动按 key 排序(自然顺序或自定义)
缺点:性能略低(红黑树实现),不适合频繁写入场景

1
2
3
4
Map<String, Integer> map = new TreeMap<>();
map.put("b", 2);
map.put("a", 1);
System.out.println(map); // 输出:{a=1, b=2}

LinkedHashMap

场景:既要 HashMap 的查找效率,又要保持插入顺序
优点:有序(插入顺序),支持 LRU 缓存(可重写方法)
缺点:内存开销略大,遍历速度比 HashMap 慢些

1
2
3
4
5
Map<String, String> map = new LinkedHashMap<>();
map.put("first", "A");
map.put("second", "B");
System.out.println(map); // 输出:{first=A, second=B}