JVM
让Java 程序 一次编写,到处运行
功能:
-
运行 Java 字节码
Java 源码(.java)会先被编译成 字节码(.class)
JVM 负责加载、解析并执行这些字节码
不同操作系统有不同的 JVM,但字节码是通用的 -
内存管理(自动)
堆(Heap) :存对象
栈(Stack) :存方法调用、局部变量
方法区 / 元空间:存类信息、常量等
开发者不需要手动malloc / free -
垃圾回收(GC)
JVM 自动回收不再使用的对象
防止内存泄漏和野指针
常见 GC:Serial、Parallel、G1、ZGC 等
JRE
JRE 是 Java 程序 运行环境 ,只能运行,不能开发。
JRE 负责:
1. 启动 JVM
2. 加载 .class / .jar
3. 执行 Java 字节码
JDK
开发 + 运行 Java 程序的一整套工具包
- 开发 Java 程序(JDK 提供了完整的开发工具)
javac:把.java源码编译成.class字节码
java:运行 Java 程序
jar:打包
javadoc:生成文档
jdb:调试 - JDK 内部包含 JVM,可以直接运行 Java 程序
- JDK 自带大量基础类库
java.lang(String、Object)
java.util(集合)
java.io / java.nio(IO)
java.net(网络)
java.concurrent(并发)
Java 数据类型
基础数据类型
整数类型
| 类型 | 位数 | 范围 |
|---|---|---|
byte | 8 位 | -128 ~ 127 |
short | 16 位 | -32768 ~ 32767 |
int | 32 位 | -2³¹ ~ 2³¹-1即 -2147483648 ~ 2147483647 |
long | 64 位 | -2⁶³ ~ 2⁶³-1 |
浮点类型
| 类型 | 总位数 | 符号位 | 指数位 | 尾数位 |
|---|---|---|---|---|
float | 32 | 1 | 8 | 23 |
double | 64 | 1 | 11 | 52 |
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
数据类型转换
- 注意数据类型,不同数据类型不能赋值转换
- 数值范围:byte → short → int → long → float → double
- 范围大的数据无法直接转换为范围小的数据,但可以使用小括号间接强制转换
- 自动类型转换(隐式声明、从小到大、不丢失精度)
int a = 10;
double b = a;
- 强制类型转换(显式声明、从大到小、可能丢失精度)
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
↓
堆:再创建一个新的对象实例
↓
栈:保存新的引用
- 成员变量的默认值
| 类型 | 默认值 |
|---|---|
| byte / short / int / long | 0 |
| float | 0.0f |
| double | 0.0 |
| char | '\u0000'(空字符) |
| boolean | false |
| 引用类型(String、数组、对象) | null |
静态
- static:和对象无关,只和类相关(可以通过
类名.直接访问) - 静态方法中可以访问:静态变量 / 静态方法
- 可以在成员方法内部调用 静态方法/属性,但静态方法不能访问 成员方法/属性,因为对象可能没创建。
class Test {
String name;
static String sex;
void test() {
test1(); ✅ 可以
System.out.println(sex); ✅ 可以
}
static void test1() {
test();❌ 编译错误
System.out.println(name);❌ 编译错误
}
}
- 静态代码块
类的信息加载完成后,会自动调用静态代码块,可以完成静态属性的初始化(按照声明静态代码块顺序依次执行) - 不管 new 多少次,静态代码块 只执行一次
- 实例代码块按代码顺序执行,每 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
包
- 包主要用于分类管理
- import语句只能使用在package后,class前
- package + 包路径(用 . 隔开)
- 为了区分类名,一般都是小写
- Java 中存在不同包的相同名称的类,可以写类的全名加以区分(包名 + 类名)
- JVM 会在使用时自动添加 java.lang 包
- 可以使用 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
}
}
- 构造方法:
- 父类对象是在子类对象创建前创建完成,创建子类对象前,会调用父类的构造方法完成父类的创建
- 默认情况下,子类对象构建时,会默认调用父类的构造方法完成父类对象的创建。使用的是super的方式,只不过JVM自动完成(无参构造方法)
- 如果父类提供构造方法,那么JVM不会提供默认的构造方法,那么子类应该显示调用super方法构建父类对象。
- 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()报错?
因为:Person类中 没有testBoy()方法- 编译器只检查
Person Boy是Person的子类,子类对象“本质上也是”一个父类对象,所以可以向上转型。
- 真正体现多态的运行效果(子类重写父类中的方法)
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 {
}
}
单例模式
- 类的创建过程复杂
- 类的对象消耗资源
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可以修改方法的参数,一旦修饰,参数就无法修改。
抽象
- 如果一个类中含有抽象方法(只有申明,没有实现),那么这个类是抽象类
- 如果一个类是抽象类,它的方法不一定是抽象方法。
abstract class Person {
public abstract void eat();
public void test() {
}
}
- 抽象类无法直接构建对象,但是可以通过子类间接构建对象
- 如果抽象类中含有抽象方法,那么子类继承抽象类,需要重写抽象方法,将方法补充完整
class Chinese extends Person {
@Override
public void eat() {
System.out.println("中国人使用筷子吃饭");
}
}
Chinese c = new Chinese();
c.eat();
- abstract关键字不能和final同时使用,因为 final 之后就不能被重写或继承,不符合 abstract
接口
- 规则的属性必须为固定值,而且不能修改。
- 属性和行为的访问权限必须为公共的,大家都能看到
- 属性是静态的,和某个对象没关系。行为是抽象的,和某一个具体的对象有关。
- 接口和类是两个层面的东西
- 接口可以继承其他接口
- 类的对象需要遵循接口,在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();
枚举类
- 枚举是一个特殊的类,其中包含了一组特定的对象,这些对象不会发生改变, 一般都使用大写的标识符
- 枚举使用enum关键字使用
- 枚举类会将 对象放置在最前面,那么和后面的语法需要使用分号隔开
- 枚举类不能创建对象,它的对象是在内部自行创建
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文件
- 例如实现接口 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();
- 继承抽象类
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 类
- 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());
}
}
- 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进制)
- 重写 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()
}
}
- 用 Object 引用指向子类对象
class Person {
}
public class Test {
public static void main(String[] args) {
Object obj = new Person(); // 完全合法
System.out.println(obj.getClass());
}
}
- equals():方法比较对象,默认比较内存地址
- 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 | ✅ | ✅ | 中 |
包装类
把基本数据类型包装成对象的类
| 基本类型 | 包装类 |
|---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
装箱 & 拆箱
- 装箱(基本 → 对象)
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 == s2 | true |
s1 == s3 | false |
s1.equals(s3) | true |
- 包装类的比较
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
| 比较 | 结果 |
|---|---|
a == b | true(缓存 -128~127) |
c == d | false |
a.equals(b) | true |
异常
异常是程序在运行过程中发生的不正常情况。
Java 的异常体系是一棵树 🌳:
Throwable
├── Error (严重错误,程序无能为力)
│ └── OutOfMemoryError
│ └── StackOverflowError
│
└── Exception (可以处理的异常)
├── RuntimeException (运行时异常)
│ ├── NullPointerException
│ ├── ArithmeticException
│ ├── ClassCastException
│ └── IndexOutOfBoundsException
│
└── CheckedException (编译时异常)
├── IOException
├── SQLException
└── ParseException
| 对比 | Error | Exception |
|---|---|---|
| 严重程度 | 非常严重 | 一般错误 |
| 是否处理 | ❌ 不处理 | ✅ 需要处理 |
| 示例 | OOM | IO 异常 |
| 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 {
...
}
throwvsthrows
| 关键字 | 作用 |
|---|---|
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 | 越界 |
| IOException | IO 操作 |
| SQLException | 数据库 |