Java EE

61 阅读19分钟

JVM

让Java 程序 一次编写,到处运行
功能:

  1. 运行 Java 字节码
    Java 源码(.java)会先被编译成 字节码(.class
    JVM 负责加载、解析并执行这些字节码
    不同操作系统有不同的 JVM,但字节码是通用的

  2. 内存管理(自动)
    堆(Heap) :存对象
    栈(Stack) :存方法调用、局部变量
    方法区 / 元空间:存类信息、常量等
    开发者不需要手动 malloc / free

  3. 垃圾回收(GC)
    JVM 自动回收不再使用的对象
    防止内存泄漏和野指针
    常见 GC:Serial、Parallel、G1、ZGC 等

JRE

JRE 是 Java 程序 运行环境 ,只能运行,不能开发。 JRE 负责:
1. 启动 JVM
2. 加载 .class / .jar
3. 执行 Java 字节码

JDK

开发 + 运行 Java 程序的一整套工具包

  1. 开发 Java 程序(JDK 提供了完整的开发工具
    javac:把 .java 源码编译成 .class 字节码
    java:运行 Java 程序
    jar:打包
    javadoc:生成文档
    jdb:调试
  2. JDK 内部包含 JVM,可以直接运行 Java 程序
  3. JDK 自带大量基础类库 java.lang(String、Object)
    java.util(集合)
    java.io / java.nio(IO)
    java.net(网络)
    java.concurrent(并发)

image.png

Java 数据类型

image.png

基础数据类型

整数类型

类型位数范围
byte8 位-128 ~ 127
short16 位-32768 ~ 32767
int32 位-2³¹ ~ 2³¹-1-2147483648 ~ 2147483647
long64 位-2⁶³ ~ 2⁶³-1

浮点类型

类型总位数符号位指数位尾数位
float321823
double6411152
  • float:约 6~7 位十进制有效数字
  • double:约 15~16 位十进制有效数字

默认情况下,小数点数据会被识别为精度更高的双精度 double 类型,日常开发 优先用 double
如果想要设置单精度float,数据需要使用 F / f 结尾

public class Demo {
    public static void main(String[] args) {
        float price = 12.5f;
        System.out.println(price);
    }
}
  • double 转换为 float 可能会 丢精度
public class Demo {
    public static void main(String[] args) {
        float price = (float)12.5;
    }
}
  • 浮点字面量默认是 double
float f = 1.23;   // ❌ 编译错误
float f = 1.23f;  // ✅
  • 金额、精度敏感计算 不要用 float / double
BigDecimal

字符类型

  • char 必须用单引号 '' ,只能放一个字符,占2字节
char c1 = 'a';
char c2 = '中';
char c3 = '1';
char c4 = '?';
  • char 的本质是数字
char c = '中';
System.out.println((int) c); // 20013

布尔类型

  • boolean

数据类型转换

  1. 注意数据类型,不同数据类型不能赋值转换
  2. 数值范围:byte → short → int → long → float → double
  3. 范围大的数据无法直接转换为范围小的数据,但可以使用小括号间接强制转换
  4. 自动类型转换(隐式声明、从小到大、不丢失精度)
int a = 10;
double b = a;
  1. 强制类型转换(显式声明、从大到小、可能丢失精度)
double d = 3.14;
int i = (int) d; // 3

面向对象

class Cooking {
    // 特征(属性)
    // 名字
    String name;
    // 菜的类型
    String type = "红烧";
    // 食材
    String food;
    // 佐料
    String relish = "大料";
    // 方法
    void execute() {
        System.out.println("准备食材:" + food);
        System.out.println("准备佐料:" + relish);
        System.out.println("开始烹饪");
        System.out.println(name + "烹饪完成");
    }
}

:存储方法、变量 User user
:存储对象 new User()
元空间 存储类的信息包括:类名、包名、修饰符、父类、接口信息等等。
元空间在 “类被加载”时 占用内存。元空间只在“第一次用到这个类”时参与,后续再 new 同一个类,不会再动元空间。

  • 第一次 new 某个类:

类第一次被使用

类加载(类信息进入 元空间 Metaspace)

new

堆:创建对象实例

栈:保存对象引用

  • 再次 new 同一个类时

类已加载(元空间已有类信息)

new

堆:再创建一个新的对象实例

栈:保存新的引用
image.png

  • 成员变量的默认值
类型默认值
byte / short / int / long0
float0.0f
double0.0
char'\u0000'(空字符)
booleanfalse
引用类型(String、数组、对象)null

静态

  1. static:和对象无关,只和类相关(可以通过 类名. 直接访问)
  2. 静态方法中可以访问:静态变量 / 静态方法
  3. 可以在成员方法内部调用 静态方法/属性,但静态方法不能访问 成员方法/属性,因为对象可能没创建。
class Test {
    String name;
    static String sex;
    
    void test() {
        test1(); ✅ 可以
        System.out.println(sex); ✅ 可以
    }
    static void test1() {
        test();❌ 编译错误
        System.out.println(name);❌ 编译错误
    }
}
  1. 静态代码块
    类的信息加载完成后,会自动调用静态代码块,可以完成静态属性的初始化(按照声明静态代码块顺序依次执行)
  2. 不管 new 多少次,静态代码块 只执行一次
  3. 实例代码块按代码顺序执行,每 new 一次执行一次
public static void main(String[] args) {
    new User();
}

class User {
    // 静态代码块
    static {
        System.out.println("静态代码块执行1");
    }
    static {
        System.out.println("静态代码块执行2");
    }
    static void test() {
        System.out.println("test...");
    }
    {
        // 实例代码块
        System.out.println("代码块执行1");
    }
    static {
        // 静态代码块
        System.out.println("静态代码块执行3");
    }
    {
        // 实例代码块
        System.out.println("代码块执行2");
    }
}

执行结果:

// 类加载时输出
静态代码块执行1
静态代码块执行2
静态代码块执行3
// 类加载完成后,开始创建对象
代码块执行1
代码块执行2

  1. 包主要用于分类管理
  2. import语句只能使用在package后,class前
  3. package + 包路径(用 . 隔开)
  4. 为了区分类名,一般都是小写
  5. Java 中存在不同包的相同名称的类,可以写类的全名加以区分(包名 + 类名
  6. JVM 会在使用时自动添加 java.lang 包
  7. 可以使用 import 在使用类前导入包(位于 package 后,class 前),可以多次使用
import java.util.*;
import java.sql.Date;

public class Java_Object {
    public static void main(String[] args) {        
        java.util.Date d = new java.util.Date();
    }
}

构造方法

  • 用于构建对象,在 new 对象的时候调用执行
  • 如果一个类中没有任何构造方法,JVM 会自动添加一个 公共的、无参的 构造方法,方便对象调用,如果类中有构造方法,那么JVM不会提供默认的构造方法
  • 构造方法也是方法,但是不能有返回值
  • 代码块 比构造方法先执行
  • 方法名和类名完全 相同
public class Java11_Object_Instance {
    public static void main(String[] args) {
        System.out.println("before...");
        User11 user = new User11("zhangsan");
        System.out.println("after...");
        
        user.test();
        System.out.println(user.username);
    }
}
class User11 {
    String username;
    {
        System.out.println("代码块1");
    }
    User11(String name) {
        username = name;
        System.out.println("user...");
    }
    {
        System.out.println("代码块2");
    }
    void test() {
        System.out.println("test...");
    }
}

执行结果:

before...
代码块1
代码块2
user...
after...
test...
zhangsan

继承

  • 类的继承只能是单继承,一个类只能有一个父类,但一个父类可以有多个子类
public class Java12_Object_Extends {
    public static void main(String[] args) {
        Child c = new Child();
        System.out.println(c.name); // zhangsan
        c.test(); // test
    }
}
class Parent {
    String name = "zhangsan";
    void test() {
        System.out.println("test");
    }
}
class Child extends Parent {}
  • 如果父类和子类含有相同的属性,那么可以采用特殊的关键字进行区分:super、this,都表示对象
public class Java12_Object_Extends_1 {
    public static void main(String[] args) {
        Child1 c = new Child1();
        c.test();
    }
}
class Parent1 {
    String name = "zhangsan";
}
class Child1 extends Parent1 {
    String name = "lisi";
    void test() {
        System.out.println(super.name); // zhangsan
        System.out.println(this.name); // lisi
        System.out.println(name); // lisi
    }
}
  • 构造方法:
    1. 父类对象是在子类对象创建前创建完成,创建子类对象前,会调用父类的构造方法完成父类的创建
    2. 默认情况下,子类对象构建时,会默认调用父类的构造方法完成父类对象的创建。使用的是super的方式,只不过JVM自动完成(无参构造方法)
    3. 如果父类提供构造方法,那么JVM不会提供默认的构造方法,那么子类应该显示调用super方法构建父类对象。
    4. new 只会构建一个对象,在内存中只会开辟一块空间存储创建的对象,不会再新建一个父类对象
public class Java_Object_Extend {
    public static void main(String[] args) {
        Child2 c1 = new Child2(); // parent... child...
    }
}
class Parent2 {
    String username;
    Parent2 (String name) {
        username = name;
        System.out.println("parent...");
    }
}
class Child2 extends Parent2 {
    Child2() {
        super("zhangsan");
        System.out.println("child...");
    }
}

多态

一个对象在不同场景下表现出来的不同状态和形态,对象的使用场景进行了约束,一个对象可以使用的功能取决于引用变量的引用类型

public class Java_Object {
    public static void main(String[] args) {
        // 引用类型:Person, 对象类型:`Boy`
        Person p1 = new Boy();
        p1.testPerson(); // 父类有
        p1.testBoy();    // Person 中没有,编译报错
    }
}
class Person {
    void testPerson() {
        System.out.println("test person...");
    }
}
class Boy extends Person {
    void testBoy() {
        System.out.println("test boy...");
    }
}
  • 为什么 p1.testBoy() 报错?
    因为:
    1. Person 类中 没有 testBoy() 方法
    2. 编译器只检查 Person
    3. BoyPerson 的子类,子类对象“本质上也是”一个父类对象,所以可以向上转型。
  • 真正体现多态的运行效果(子类重写父类中的方法)
class Boy extends Person {
    @Override
    void testPerson() {
        System.out.println("boy test person...");
    }
}
Person p1 = new Boy();
p1.testPerson(); // boy test person...
阶段看什么
编译期引用类型(能不能调用方法)
运行期对象类型(如果方法被重写,调用谁的)

编译看左边(引用类型)
运行看右边(对象类型,前提是方法被重写)

方法重载

  • 方法名相同,但参数列表(个数,顺序,类型)不相同,会认为是不同的方法。
public class Java14_Object {
    public static void main(String[] args) {
        User14 user = new User14("zhangsan");
        user.login(1111);
        user.login("123123");
        user.login("zhangsan", "123123");
    }
}
class User14 {
    // 构造函数重载
    User14() {
        System.out.println("user...");
    }
    User14(String name) {
        System.out.println("user..." + name);
    }
    
    // 方法重载
    void login( String account, String password ) {
        System.out.println("账号,密码登录");
    }
    void login(int tel) {
        System.out.println("手机验证码登录");
    }
    void login(String wx) {
        System.out.println("微信,支付宝登录");
    }
}
  • 如果在一个构造方法中,想要调用其他的构造方法,那么需要使用特殊的关键字:this
public class Java_Object {
    public static void main(String[] args) {
        User141 user1 = new User141();
        User141 user2 = new User141("zhangsan");
        User141 user3 = new User141("zhangsan", "男");
    }
}
class User {
    User() {
        this("zhangsan");
    }
    User( String name ) {
        this(name, "男");
    }
    User( String name, String sex ) {
        System.out.println(name + "," + sex);
    }
}
  • 基本数据类型在匹配方法时,可以在数值不变的情况下扩大数据精度,不允许自动缩小
// 多个重载,优先选择“提升幅度最小”的
static void test(int x) {
    System.out.println("int");
}
static void test(long x) {
    System.out.println("long");
}
test(10); // int


static void test(float f) {}
static void test(long l) {}
test(10);// `int` → `long` ✔   `int` → `float` ✔ 两个都合法,无法判断谁更近


// 不会自动缩小
static void test(byte b) {}
test(10); // ❌ 编译错误


// 完全匹配优先于提升匹配
static void test(int x) {
    System.out.println("int");
}
static void test(char x) {
    System.out.println("char");
}
test('a'); // char
  • byte类型无法和char类型做转换,char没有负数,但byte有负数

方法重写

  • 父类对象的方法主要体现通用性,无法在特殊场合下使用,如果子类对象需要在特殊场合下使用,就需要重写方法的逻辑
  • 重写并不意味着父类的方法被覆盖,只是当前场合不适用,使用 super 还是可以访问
  • 构造方法不能重写
public class Java_Object {
    public static void main(String[] args) {
        Child child = new Child();
        child.test();
    }
}
class Parent {
    String name = "zhangsan";
    void test() {
        System.out.println("parent test...");
    }
}
class Child extends Parent {
    String name = "lisi";

    @Override
    void test() {
        System.out.println(this.name);  // lisi
        System.out.println(super.name); // zhangsan
        super.test();                   // parent test...
        System.out.println("child test..."); // child test...
    }
}
  • 重写要求:子类方法和父类方法方法名相同、返回值类型相同、参数列表相同

访问权限

  • (default) : 同类,同包(路径)可访问
  • protected : 同类,同包(路径),子类
  • public : 公共的
  • private : 同类

内部类

  • 内部类可以当成外部类的属性
  • 因为内部类可以看作外部类的属性,所以需要构建外部类对象才可以使用
public class Java_Object {
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        OuterClass.InnerClass innerClass = outer.new InnerClass();
    }
}
class OuterClass {
    public class InnerClass {
    
    }
}

单例模式

  1. 类的创建过程复杂
  2. 类的对象消耗资源
public class Java_Object {
    public static void main(String[] args) {
        User instance1 = User.getInstance();
        User instance2 = User.getInstance();
        User instance3 = User.getInstance();
        System.out.println(instance1.equals(instance3));
    }
}
class User {
    private static User user = null;
    // 不允许外部直接通过 new 的方式创建对象
    private User() {}
    public static User getInstance() {
        if ( user == null ) {
            user = new User();
        }
        return user;
    }
}

final

  • final修饰的变量称之为常量,变量的值一旦初始化后无法修改
  • final可以修饰属性:那么JVM无法自动进行初始化,需要自己进行初始化, 属性值不能发生变化
  • final可以修饰方法,这个方法不能被子类重写
  • final可以修饰类,这样类就没有子类了
  • final不可以修饰构造方法
  • final可以修改方法的参数,一旦修饰,参数就无法修改。

抽象

  1. 如果一个类中含有抽象方法(只有申明,没有实现),那么这个类是抽象类
  2. 如果一个类是抽象类,它的方法不一定是抽象方法。
abstract class Person { 
    public abstract void eat(); 
    public void test() { 
    
    } 
}
  1. 抽象类无法直接构建对象,但是可以通过子类间接构建对象
  2. 如果抽象类中含有抽象方法,那么子类继承抽象类,需要重写抽象方法,将方法补充完整
class Chinese extends Person {
    @Override
    public void eat() {
        System.out.println("中国人使用筷子吃饭");
    }
}
Chinese c = new Chinese(); 
c.eat();
  1. abstract关键字不能和final同时使用,因为 final 之后就不能被重写或继承,不符合 abstract

接口

  1. 规则的属性必须为固定值,而且不能修改。
  2. 属性和行为的访问权限必须为公共的,大家都能看到
  3. 属性是静态的,和某个对象没关系。行为是抽象的,和某一个具体的对象有关。
  4. 接口和类是两个层面的东西
  5. 接口可以继承其他接口
  6. 类的对象需要遵循接口,在java中,这个遵循,称之为实现(implements),类需要实现接口,而且可以实现多个接口
interface USBInterface { 

}
interface USBSupply extends USBInterface { 
    public void powerSupply(); 
}
interface USBReceive extends USBInterface { 
    public void powerReceive(); 
}

class Computer implements USBSupply {
    public USBReceive usb1;
    public USBReceive usb2;
    @Override
    public void powerSupply() {
        System.out.println("电脑提供能源");
        usb1.powerReceive();
        usb2.powerReceive();
    }
}
class Light implements USBReceive {
    @Override
    public void powerReceive() {
        System.out.println("电灯接收能源");
    }
}

Computer c = new Computer();
Light light = new Light();
c.usb1 = light;
Light light1 = new Light();
c.usb2 = light1;
c.powerSupply();

枚举类

  1. 枚举是一个特殊的类,其中包含了一组特定的对象,这些对象不会发生改变, 一般都使用大写的标识符
  2. 枚举使用enum关键字使用
  3. 枚举类会将 对象放置在最前面,那么和后面的语法需要使用分号隔开
  4. 枚举类不能创建对象,它的对象是在内部自行创建
public class Java_Object_Enum {
    public static void main(String[] args) {
        System.out.println(City.BEIJING.name);
        System.out.println(City.SHANGHAI.code);
        System.out.println(MyCity.SHANGHAI.name);
        System.out.println(MyCity.BEIJING.code);
    }
}
enum City {
    BEIJING("北京", 1001), SHANGHAI("上海", 1002);
    City( String name, int code ) {
        this.code = code;
        this.name = name;
    }
    public String name;
    public int code;
}

仿写:

// 仿写
public class Java_Object_Enum {
    public static void main(String[] args) {
        System.out.println(MyCity.SHANGHAI.name);
        System.out.println(MyCity.BEIJING.code);
    }
}
class MyCity {
    public String name;
    public int code;

    private MyCity(String name, int code) {
        this.code = code;
        this.name = name;
    }

    public static final MyCity BEIJING = new MyCity("北京", 1001);
    public static final MyCity SHANGHAI = new MyCity("上海", 1002);
}

匿名类

  • 只想用一次
  • 只想改一两个方法
  • 不想专门新建一个 .java 文件
  1. 例如实现接口 Runnable
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("线程在运行");
    }
};

// JVM 悄悄做了件事
class Xxx$1 implements Runnable {
    @Override
    public void run() {
        System.out.println("线程在运行");
    }
}
Runnable r = new Xxx$1();
  1. 继承抽象类
abstract class Animal {
    abstract void speak();
}

// 必须实现所有抽象方法
// 只能 new 一次,用完就丢
Animal dog = new Animal() {
    @Override
    void speak() {
        System.out.println("汪汪");
    }
};
  • 只能继承一个类 / 实现一个接口,不能同时 extends + implements

Bean规范

Bean类设计规范:
1.类要求必须含有无参,公共的构造方法
2.属性必须私有化,然后提供公共得,set,get方法

public class Java_Object {
    public static void main(String[] args) {
        User25 user = new User25();
        user.setAccount("admin");
        user.setPassword("admin") ;
        System.out.println(login(user));
    }
    public static boolean login( User25 user ) {
        if (user.getAccount().equals("admin") && user.getPassword().equals("admin")) {
            return true;
        } else {
            return false;
        }
    }
}
class User25 {
    private String account;
    private String password;

    public void setAccount( String account ) {
        this.account = account;
    }
    public void setPassword(String password) {
        this.password = password;
    }

    public String getAccount() {
        return account;
    }
    public String getPassword() {
        return account;
    }
}

作用域

如果属性和(局部)变量名称相同,访问时不加修饰符,则优先访问局部变量

public class Java_Object {
    public static void main(String[] args) {
        User26 user = new User26();
        User26.test();
    }
}
class Person {
    public static String name = "zhangsan";
}
class User extends Person {
      public static String name = "lisi";
      public void test() {
          String name = "wangwu";
          System.out.println(super.name); // zhangsan
          System.out.println(name); // wangwu
    }
}

Object 类

  1. Java 中所有类,若没有显式继承其他类,都会默认继承 java.lang.Object,因此所有对象都具备 Object 的基本行为。
// 什么都没继承,但能用 Object 的方法
class Person {}

public class Test {
    public static void main(String[] args) {
        Person p = new Person();

        // 下面这些方法,Person 并没有定义
        System.out.println(p.toString());
        System.out.println(p.hashCode());
        System.out.println(p.equals(p));
        System.out.println(p.getClass());
    }
}
  1. toString():将对象转换成字符串,toString() 默认打印对象的内存地址
class Person {
    String name;
}
public class Test {
    public static void main(String[] args) {
        Person p = new Person();
        p.name = "Tom";

        // 直接打印对象
        System.out.println(p);  // Person@5e91993f
        // 等价写法
        System.out.println(p.toString()); // Person@5e91993f
    }
}
// 类的全限定名 @ 对象的哈希码(16进制)
  1. 重写 Object 的方法
class Person {
    String name;
    Person(String name) {
        this.name = name;
    }
    // 重写 Object 的 toString 方法
    @Override
    public String toString() {
        return "Person{name='" + name + "'}";
    }
}

public class Test {
    public static void main(String[] args) {
        Person p = new Person("Tom");
        System.out.println(p);  // 自动调用 toString()
    }
}
  1. 用 Object 引用指向子类对象
class Person {
}

public class Test {
    public static void main(String[] args) {
        Object obj = new Person(); // 完全合法
        System.out.println(obj.getClass());
    }
}
  1. equals():方法比较对象,默认比较内存地址
  2. getClass():获取对象的类型信息

数组

  • 一维数组
int[] arr = {1, 2, 3, 4}; // 静态初始化(已知元素)
int[] arr = new int[4]; // 动态初始化(先给长度)

// 默认值:
`int` → `0`
`double` → `0.0`
`boolean` → `false`
引用类型 → `null`

// 访问和遍历
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}
for (int x : arr) {
    System.out.println(x);
}
  • 二维数组
// 定义方式
int[][] arr;

int[][] arr = {
    {1, 2, 3},
    {4, 5, 6}
}; // 静态初始化

int[][] arr = new int[2][3]; // 动态初始化(规则矩阵)

// 不规则数组
int[][] arr = new int[2][];
arr[0] = new int[3];
arr[1] = new int[5];
arr
 ├── [0] → [][][]
 └── [1] → [][][][][]

// 访问和遍历
for (int i = 0; i < arr.length; i++) {
    for (int j = 0; j < arr[i].length; j++) {
        System.out.print(arr[i][j] + " ");
    }
    System.out.println();
}
for (int[] row : arr) {
    for (int x : row) {
        System.out.print(x + " ");
    }
    System.out.println();
}

字符串

  • 创建方式
// 创建方式(字符串字面量),存在字符串常量池
String s1 = "abc";
String s2 = "abc";
`s1 == s2` → `true`

// 创建方式(new 方式)
String s3 = new String("abc");
`s3 == s1` → `false`
  • String 不可变
String s = "hello";
s = s + " world";

System.out.println(s);

`"hello"` 在常量池
`" world"` 在常量池
`"hello world"` **新对象**
原来的 `"hello"` **没变**
String 拼接 = 创建新对象
  • 字符串内容比较,永远用 equals()

  • 常用 String 方法:

方法作用
length()长度
charAt(0)取字符
substring(0, 5)截取
contains("Hello")是否包含
indexOf("o")查找
toUpperCase()转大写
trim()去首尾空格
replace("l", "x")替换
split(" ")分割
  • StringBuilder 因为每次拼接都会创建新对象,性能极差
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
    sb.append(i);
}
String result = sb.toString();
可变线程安全性能
String
StringBuilder
StringBuffer

包装类

把基本数据类型包装成对象的类

基本类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

装箱 & 拆箱

  • 装箱(基本 → 对象)
int a = 10;

// 自动装箱
Integer i2 = a;
  • 拆箱(对象 → 基本)
Integer i = 20;
// 手动拆箱
int b = i.intValue();
// 自动拆箱
int c = i;

本质

Integer i = 10;   // 自动装箱
int a = i;        // 自动拆箱

// 编译后等价于:
Integer i = Integer.valueOf(10);
int a = i.intValue();

Integer 缓存机制

  • Integer 缓存范围-128 ~ 127
  • 范围内:复用同一个对象
  • 范围外:新建对象
Integer a = 100;
Integer b = 100;

Integer c = 200;
Integer d = 200;

System.out.println(a == b); // true
System.out.println(c == d); // false

== vs equals()

  • == 比较 地址
  • equals() 比较
Integer a = 100;
Integer b = 100;
Integer c = new Integer(100);

System.out.println(a == b);        // true
System.out.println(a == c);        // false
System.out.println(a.equals(c));   // true

日期

含义
LocalDate日期(年-月-日)
LocalTime时间(时-分-秒)
LocalDateTime日期 + 时间
Instant时间戳(UTC)
ZonedDateTime带时区的日期时间
Duration时间间隔(秒、毫秒)
Period日期间隔(年、月、日)
DateTimeFormatter格式化
LocalDate today = LocalDate.now();
System.out.println(today);   // 2026-01-26

int year  = today.getYear();
int month = today.getMonthValue();
int day   = today.getDayOfMonth();

LocalTime now = LocalTime.now();
System.out.println(now); // 17:45:30.123

LocalDateTime now = LocalDateTime.now();
System.out.println(now); // 2026-01-26T17:45:30

工具类

只提供通用功能方法、不保存状态、通过静态方法使用的类

  • Math
int a = Math.abs(-10);     // 10
double b = Math.sqrt(9);  // 3.0
double c = Math.random(); // [0,1)
  • Arrays
int[] arr = {3, 1, 2};

Arrays.sort(arr);                 // 排序
System.out.println(Arrays.toString(arr));
  • UUID
String id = UUID.randomUUID().toString();
System.out.println(id);
  • 自己写一个标准工具类
public final class StringUtil {
    // 私有构造,防止 new
    private StringUtil() {
        throw new AssertionError("不能实例化工具类");
    }
    public static boolean isEmpty(String s) {
        return s == null || s.isEmpty();
    }
    public static boolean isBlank(String s) {
        return s == null || s.trim().isEmpty();
    }
}

比较

  • 基本数据类型的比较
// `==`(最直接)
int a = 10;
int b = 10;
System.out.println(a == b); // true

// 浮点数比较
double x = 0.1 + 0.2;
System.out.println(x == 0.3); // false
正确比较:Math.abs(x - 0.3) < 1e-9
  • 引用类型的比较
// `==`(比较地址)
Person p1 = new Person("Tom");
Person p2 = new Person("Tom");
System.out.println(p1 == p2); // false

// `equals()`(比较内容)
System.out.println(p1.equals(p2)); // true(前提是重写)
默认:`Object.equals()` → 地址比较
  • String 的比较
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
比较结果
s1 == s2true
s1 == s3false
s1.equals(s3)true
  • 包装类的比较
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
比较结果
a == btrue(缓存 -128~127)
c == dfalse
a.equals(b)true

异常

异常是程序在运行过程中发生的不正常情况。
Java 的异常体系是一棵树 🌳:

Throwable
 ├── Error            (严重错误,程序无能为力)
 │    └── OutOfMemoryError
 │    └── StackOverflowError
 │
 └── Exception        (可以处理的异常)
      ├── RuntimeException   (运行时异常)
      │    ├── NullPointerException
      │    ├── ArithmeticException
      │    ├── ClassCastException
      │    └── IndexOutOfBoundsException
      │
      └── CheckedException   (编译时异常)
           ├── IOException
           ├── SQLException
           └── ParseException
对比ErrorException
严重程度非常严重一般错误
是否处理❌ 不处理✅ 需要处理
示例OOMIO 异常
Error 不写 try-catch

运行时异常

  • 编译不报错
  • 多数是 代码问题
int a = 10 / 0;          // ArithmeticException
String s = null;
s.length();             // NullPointerException

编译时异常

  • 必须处理
  • 不处理就 编译失败
FileInputStream fis =
    new FileInputStream("a.txt"); // 编译报错

异常处理方式

  • try-catch-finally
try {
    int a = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("除数不能为 0");
} finally {
    System.out.println("一定会执行");
}
  • 多 catch(顺序很重要),子类在前,父类在后
try {
    ...
} catch (NullPointerException e) {
    ...
} catch (Exception e) {
    ...
}
  • throws(向上抛)当前方法不处理,交给调用者处理
public void read() throws IOException {
    ...
}
  • throw vs throws
关键字作用
throw手动抛异常
throws声明可能抛异常
throw new IllegalArgumentException("参数非法");
  • 自定义异常
// 自定义运行时异常
class BizException extends RuntimeException {
    public BizException(String msg) {
        super(msg);
    }
}

if (age < 0) {
    throw new BizException("年龄不能为负数");
}
// 自定义编译时异常
class MyException extends Exception { }
异常场景
NullPointerException空指针
ArithmeticException除 0
ClassCastException类型转换
IndexOutOfBoundsException越界
IOExceptionIO 操作
SQLException数据库