6 核心类2
约 2586 个字 463 行代码 预计阅读时间 19 分钟
包括:枚举类、记录类、BigInteger类、BigDecimal类、Math类、HexFormat类、Random类、SecureRandom类、常见方法类型。
输出结果都写在了注释中
1. 枚举类enum¶
1.1 static final的劣势¶
在Java中,我们可以通过static final
来定义常量
- 定义一周七天
public class Weekday {
public static final int SUN = 0;
public static final int MON = 1;
public static final int TUE = 2;
public static final int WED = 3;
public static final int THU = 4;
public static final int FRI = 5;
public static final int SAT = 6;
}
- 定义颜色
public class Color {
public static final String RED = "r";
public static final String GREEN = "g";
public static final String BLUE = "b";
}
使用常量的时候,可以这么引用:
或:
问题 | 描述 |
---|---|
无法检查范围合法性 | 比如if (day == 7) 不报错,但7 不在定义中 |
不同用途常量可混用 | 可将Color.RED 赋给Weekday ,编译不报错 |
类型信息缺失 | 无法明确表示这是一种“星期类型” |
1.2 enum的使用¶
使用 enum
定义枚举类型:
引用:
Weekday day = Weekday.SUN;
if (day == Weekday.SAT || day == Weekday.SUN) {
System.out.println("Work at home!");
// Work at home!
}
优势 | 描述 |
---|---|
类型安全 | Weekday 是强类型,不能与其他类型混用 |
唯一实例 | 每个枚举常量在 JVM 中只有一个 |
编译器校验 | 不可能引用到非枚举的值,比如if (day == 7) 会直接报错;不同类型的枚举也不能比较或者赋值,比如不能给 Weekday 枚举类型的变量赋值为Color 枚举类型的值 |
支持switch | 直接在switch 中使用 enum |
类型混用不可能编译通过:
类型不符不可能编译通过:
虽然 enum
看似特别,其本质还是 Java 类,只是具有以下特点:
特性 | 描述 |
---|---|
自动继承java.lang.Enum | 编译器自动继承 |
不能被继承 | 所有 enum 都是final |
每个枚举值是类的实例 | 不能用new 创建 |
可添加字段、方法、构造函数 | 像普通类一样扩展功能 |
编译器编译出的class大概就像这样:
public final class Color extends Enum { // 继承自Enum,标记为final class
// 每个实例均为全局唯一:
public static final Color RED = new Color();
public static final Color GREEN = new Color();
public static final Color BLUE = new Color();
// private构造方法,确保外部无法调用new操作符:
private Color() {}
}
编译后的enum
类和普通class
并没有任何区别,但不能按定义普通class
那样来定义enum
,必须使用enum
关键字,这是Java语法规定的。
public class demo30 {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
if(day == Weekday.SAT || day == Weekday.SUN){
System.out.println("Work at home!");
}else{
System.out.println("Work at office!");
}
// Work at home!
}
}
enum Weekday{
SUN,MON,TUE,WED,THU,FRI,SAT;
}
1.3 enum添加字段与构造方法¶
package Exp03;
public class demo31 {
public static void main(String[] args) {
Weekday day = Weekday.FRI;
//name():返回常量名,不可被覆写
System.out.println(day.name());
// FRI
// ordinal():返回定义的常量的顺序,从0开始计数(不建议使用)
System.out.println(day.ordinal());
// 4
// dayValue:建议使用自定义字段来做逻辑判断
if (day.dayValue == 6 || day.dayValue == 0) {
System.out.println("Today is " + day + ". Work at home!");
}else{
System.out.println("Today is " + day + ". Work at office!");
}
// Today is 星期五. Work at office!
// 返回“星期五”而不是“5”或者“FRI”或者别的,是因为自动调用了day.toString()方法
// 覆写的toString()
System.out.println(day.toString());
// 星期五
}
}
enum Weekday{
MON(1, "星期一"), TUE(2, "星期二"), WED(3, "星期三"),
THU(4, "星期四"), FRI(5, "星期五"), SAT(6, "星期六"), SUN(7, "星期日");
public final int dayValue;
private final String chinese;
private Weekday(int dayValue,String chinese){
this.dayValue = dayValue;
this.chinese = chinese;
}
@Override
public String toString(){ //覆写toString()
return this.chinese;
}
}
1.4 enum支持switch语句¶
public class Main {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
switch(day) {
case MON:
case TUE:
case WED:
case THU:
case FRI:
System.out.println("Today is " + day + ". Work at office!");
break;
case SAT:
case SUN:
System.out.println("Today is " + day + ". Work at home!");
break;
default:
throw new RuntimeException("cannot process " + day);
}
// Today is 星期五. Work at office!
}
}
enum Weekday {
MON, TUE, WED, THU, FRI, SAT, SUN;
}
总结
比较项 | static final 常量 | enum 枚举类型 |
---|---|---|
类型安全 | ❌ 否 | ✅ 是 |
范围检查 | ❌ 否 | ✅ 是 |
使用限制 | 可随意使用 | 仅限自身定义的常量 |
支持switch | ✅ 支持int /String | ✅ 更适合enum 使用 |
编译期错误检查 | ❌ 运行时可能出错 | ✅ 编译期报错 |
可读性 | 一般 | 高(可覆盖toString() ) |
2. 记录类record¶
自Java 14起,引入了新的record
类型,用于简洁地定义不变类。
2.1 传统的不变类¶
传统的不可变类(如 String
, Integer
)具有以下特点:
特点 | 说明 |
---|---|
final 修饰类 | 防止被继承 |
所有字段为final | 创建后字段值不可变 |
提供只读方法 | 无 setter,仅有 getter |
正确重写equals() 、hashCode() | 用于集合或比较操作 |
public final class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int x() {
return this.x;
}
public int y() {
return this.y;
}
// 还需覆写 equals(), hashCode(), toString()
}
2.2 使用record类简化不变类¶
只需写:
即相当于:
final class Point extends Record {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int x() {
return this.x;
}
public int y() {
return this.y;
}
public String toString() {
return String.format("Point[x=%s, y=%s]", x, y);
}
public boolean equals(Object o) {
...
}
public int hashCode() {
...
}
}
使用示例:
public class demo32 {
public static void main(String[] args) {
Point p = new Point(10,20);
System.out.println(p.x());
// 10
System.out.println(p);
// Point[x=10, y=20]
}
}
record Point(int x,int y){
}
record与传统类对比:
项目 | 传统不变类 | record 写法 |
---|---|---|
类定义 | 需手动添加final | 自动为final ,不可继承 |
字段 | 手动定义private final | 自动为private final |
构造方法 | 手写 | 自动生成 |
访问方法 | 手写 | 自动生成x() ,y() 等 |
toString() | 手写 | 自动生成 |
equals() 和hashCode() | 手写 | 自动生成 |
2.3 构造方法¶
Compact Constructor(紧凑构造方法)用于参数验证:
record Point(int x,int y){
public Point{
if(x<0 || y<0){
throw new IllegalArgumentException("坐标不能为负数");
}
}
}
可以定义静态方法帮助构造对象,如 of()
方法:
record Point(int x, int y) {
public static Point of() {
return new Point(0, 0);
}
public static Point of(int x, int y) {
return new Point(x, y);
}
}
这样我们可以写出更简洁的代码:
什么是静态方法和工厂方法?¶
静态方法:
是使用 static
关键字修饰的方法,属于类本身,而不是类的实例。
特性 | 说明 |
---|---|
无需创建对象 | 可以直接通过类名调用,例如:Math.abs(-1) |
不能访问实例变量 | 不能访问this ,因为它不属于任何一个实例 |
常用于工具类、辅助方法 | 如Math.max() 、Collections.sort() 等 |
示例:
public class Utils {
public static int square(int x) {
return x * x;
}
}
// 调用方式
int result = Utils.square(5);
// 25
工厂方法:
是一个专门用于创建对象的静态方法。它可以根据参数或条件返回不同的对象,是构造方法的替代方式。
优点 | 描述 |
---|---|
可控制对象创建 | 比如可以添加参数校验、缓存重复对象、返回子类等 |
代码更清晰,语义更明确 | 如Point.of(1, 2) 比new Point(1, 2) 可读性更强 |
支持重载返回不同类型 | 可根据不同参数决定返回何种实现 |
小结:
特性 | record 是否支持 | 说明 |
---|---|---|
不变类支持 | ✅ | 所有字段为final ,自动生成访问器 |
自动构造方法 | ✅ | 按声明字段顺序生成 |
参数验证 | ✅ | 通过 Compact Constructor 实现 |
添加静态方法 | ✅ | 如of() 工厂方法 |
自定义方法 | ✅ | 可以添加普通方法 |
继承其他类 | ❌ | 继承自java.lang.Record ,不可再继承其他类 |
3. BigInteger类¶
在 Java 中,基本整型的最大范围由 long
类型决定,其范围是:
long
类型的数据可以由 CPU 原生支持,计算速度快。但如果整数超出这个范围,就必须使用软件模拟的大整数 —— 即 BigInteger
类。
定义所在包: java.math.BigInteger
作用: 表示任意大小的整数(不限于64位)
内部实现: 使用 int[]
数组来存储大整数的每一部分
特性: 不可变类(immutable),继承自 Number
3.1 运算方法¶
运算类型 | 方法 | 示例代码 |
---|---|---|
加法 | add(BigInteger val) | i1.add(i2) |
减法 | subtract(BigInteger val) | i1.subtract(i2) |
乘法 | multiply(BigInteger val) | i1.multiply(i2) |
除法 | divide(BigInteger val) | i1.divide(i2) |
幂运算 | pow(int exponent) | i1.pow(5) |
import java.math.BigInteger;
public class demo33 {
public static void main(String[] args) {
BigInteger bi = new BigInteger("1234567890");
BigInteger bi2 = new BigInteger("9876543210");
System.out.println(bi.add(bi2));
// 11111111100
System.out.println(bi2.subtract(bi));
// 8641975320
System.out.println(bi.multiply(bi2));
// 12193263111263526900
System.out.println(bi2.divide(bi));
// 8
System.out.println(bi.pow(5));
// 2867971860299718107233761438093672048294900000
}
}
3.2 基本类型转换¶
方法名 | 说明 | 是否精确转换 | 超范围行为 |
---|---|---|---|
byteValue() | 转换为 byte | 否 | 丢失高位 |
shortValue() | 转换为 short | 否 | 丢失高位 |
intValue() | 转换为 int | 否 | 丢失高位 |
longValue() | 转换为 long | 否 | 丢失高位 |
floatValue() | 转换为 float | 否 | 超范围返回 Infinity |
doubleValue() | 转换为 double | 否 | 超范围返回 Infinity |
intValueExact() | 精确转换为 int | 是 | 超范围抛出异常 |
longValueExact() | 精确转换为 long | 是 | 超范围抛出异常 |
建议使用 xxxValueExact()
防止溢出。
4. BigDecimal类¶
和BigInteger
类似,BigDecimal
表示一个任意大小且精度完全准确的浮点数。
特性 | 说明 |
---|---|
精度高 | 可以表示任意大小且精度完全准确的浮点数 |
常用于场景 | 财务、科学等对小数精度要求高的场合 |
不可变对象 | 所有修改操作返回新对象 |
内部结构 | 由一个BigInteger 表示整数部分 + 一个int scale 表示小数位数 |
4.1 运算方法¶
加、减、乘、幂运算方法与BigInteger
类同理。
1.获取小数位(scale()
) :
BigDecimal d1 = new BigDecimal("123.45");
BigDecimal d2 = new BigDecimal("123.4500");
BigDecimal d3 = new BigDecimal("1234500");
System.out.println(d1.scale());
// 2
System.out.println(d2.scale());
// 4
System.out.println(d3.scale());
// 0
2.去除末尾0(stripTrailingZeros()
):
BigDecimal d1 = new BigDecimal("123.4500");
BigDecimal d2 = d1.stripTrailingZeros();
System.out.println(d1.scale());
// 4
System.out.println(d2.scale());
// 2
BigDecimal d3 = new BigDecimal("1234500");
BigDecimal d4 = d3.stripTrailingZeros();
System.out.println(d3.scale());
// 0
System.out.println(d4.scale());
// -2 (表示整数末尾有两个0)
3.可以对一个BigDecimal
设置它的scale
,如果精度比原始值低,那么按照指定的方法进行四舍五入或者直接截断:
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) {
BigDecimal d1 = new BigDecimal("123.456789");
// 四舍五入
BigDecimal d2 = d1.setScale(4, RoundingMode.HALF_UP);
// 直接截断
BigDecimal d3 = d1.setScale(4, RoundingMode.DOWN);
System.out.println(d2);
// 123.4568
System.out.println(d3);
// 123.4567
}
}
4.除法与异常处理(divide()
):
存在无法除尽的情况时,必须指定精度以及如何进行截断
BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("23.456789");
// 保留10位小数并四舍五入
BigDecimal d3 = d1.divide(d2, 10, RoundingMode.HALF_UP);
// 报错:ArithmeticException,因为除不尽
BigDecimal d4 = d1.divide(d2);
5.同时获取商和余数(divideAndRemainder()
):
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BigDecimal n = new BigDecimal("12.345");
BigDecimal m = new BigDecimal("0.12");
BigDecimal[] dr = n.divideAndRemainder(m);
// 商:12.345/0.12 = 102.875
System.out.println(dr[0]);
// 102
// 余数:12.345/0.12 ... 0.105
System.out.println(dr[1]);
// 0.105
}
}
判断是否是整数倍数:
BigDecimal n = new BigDecimal("12.75");
BigDecimal m = new BigDecimal("0.15");
BigDecimal[] dr = n.divideAndRemainder(m);
if (dr[1].signum() == 0) {
System.out.println("n是m的整数倍数");
// n是m的整数倍数
}
4.2 比较值¶
方法 | 是否比较数值 | 是否比较scale() | 推荐使用 |
---|---|---|---|
equals() | ✅ | ✅(必须一致) | ❌ 不推荐 |
compareTo() | ✅ | ❌ 仅比较数值大小 | ✅ 推荐使用 |
BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("123.45600");
System.out.println(d1.equals(d2));
// false
System.out.println(d1.equals(d2.stripTrailingZeros()));
// true
System.out.println(d1.compareTo(d2));
// true
如果查看BigDecimal
的源码,可以发现,实际上一个BigDecimal
是通过一个BigInteger
和一个scale
来表示的,即BigInteger
表示一个完整的整数,而scale
表示小数位数:
public class BigDecimal extends Number implements Comparable<BigDecimal> {
private final BigInteger intVal;
private final int scale;
}
5. 常用工具类¶
Java 的核心库中提供了大量实用的工具类(Utils
)
Math
:数学计算工具类HexFormat
:十六进制字符串与 byte[] 转换工具Random
:伪随机数生成器SecureRandom
:安全随机数生成器
5.1 Math类:数学计算¶
方法 | 示例代码 | 说明 |
---|---|---|
绝对值 | Math.abs(-100) →100 | 适用于 int、long、float、double |
最大/最小值 | Math.max(10, 20) →20 | 比较两个数的大小 |
幂运算 | Math.pow(2, 10) →1024 | x^y |
平方根 | Math.sqrt(2) →1.414... | |
自然指数 | Math.exp(2) →7.389... | e^x |
自然对数 | Math.log(4) →1.386... | 以 e 为底的对数 |
常用对数 | Math.log10(100) →2 | 以 10 为底的对数 |
三角函数 | Math.sin(π/6) →0.5 | 弧度制 |
常量 | Math.PI ,Math.E | 圆周率与自然常数 |
随机数 | Math.random() →[0,1) | 每次结果不同 |
生成指定范围的随机数:
public class demo35 {
public static void main(String[] args) {
double min = 5;
double max = 15;
double x = Math.random(); // [0,1)
double y = x * (max - min) + min; // [min,max)
System.out.println(y);
// 7.01306198489437
long n = (long) y; // 取整后的随机整数
System.out.println(n);
// 7
}
}
5.2 HexFormat类:十六进制格式化¶
HexFormat
用于在 byte[]
与十六进制字符串之间转换
方法 | 示例 | 说明 |
---|---|---|
formatHex(byte[]) | "Hello" →"48656c6c6f" | 转换为无分隔小写十六进制 |
parseHex(String) | "48656c6c6f" →byte[] | 解析为原始字节数组 |
自定义格式 | HexFormat.ofDelimiter(" ").withPrefix("0x").withUpperCase() | 添加分隔符、前缀、大小写 |
将byte[]
数组转换为十六进制字符串(formatHex()
)
定制转换格式(HexFormat
)
从十六进制字符串到byte[]数组转换(parseHex()
)
十六进制 | 十进制 | 字符 |
---|---|---|
48 | 72 | H |
65 | 101 | e |
6c | 108 | l |
6c | 108 | l |
6f | 111 | o |
import java.util.HexFormat;
public class Main {
public static void main(String[] args) throws InterruptedException {
// 将字符串 "Hello" 转换为对应的 byte 数组
byte[] data = "Hello".getBytes();
// byte[] {72, 101, 108, 108, 111}
// 创建一个默认配置的HexFormat实例
HexFormat hf = HexFormat.of();
// 自定义格式:分隔符为空格,添加前缀0x,大写字母:
HexFormat hf2 = HexFormat.ofDelimiter(" ").withPrefix("0x").withUpperCase();
// 将byte[]数组格式化为十六进制字符串
String hexData = hf.formatHex(data);
String hexData2 = hf2.formatHex(data);
System.out.println(hexData);
// 48656c6c6f
System.out.println(hexData2);
// 0x48 0x65 0x6C 0x6C 0x6F
// 将十六进制字符串转化为byte[]
byte[] bs = hf.parseHex(hexData);
String str = new String(bs);
System.out.println(str);
// Hello
System.out.println(Arrays.toString(bs));
//[72, 101, 108, 108, 111]
}
}
5.3 Random类:伪随机数生成器¶
Random
生成伪随机数,可指定种子得到确定性序列,所谓伪随机数,是指只要给定一个初始的种子,产生的随机数序列是完全一样的。
方法 | 示例 | 说明 |
---|---|---|
nextInt() | r.nextInt() | 任意 int |
nextInt(n) | r.nextInt(10) →[0,10) | 限定范围内的 int |
nextLong() | r.nextLong() | 任意 long |
nextFloat() | r.nextFloat() →[0,1) | 浮点随机数 |
nextDouble() | r.nextDouble() →[0,1) | 双精度随机数 |
固定种子:
import java.util.Random;
public class demo37 {
public static void main(String[] args) {
//种子为:12345
Random r = new Random(12345);
for (int i = 0; i < 5; i++) {
// 每次输出都获得相同的随机数
System.out.println(r.nextInt(100));
// 51
// 80
// 41
// 28
// 55
}
}
}
5.4 SecureRandom类:安全随机数生成器¶
SecureRandom
用于生成安全的、不可预测的随机数,常用于加密和安全场景。
SecureRandom
无法指定种子,它使用RNG(random number generator)算法。
JDK的SecureRandom
实际上有多种不同的底层实现,有的使用安全随机种子加上伪随机数算法来产生安全的随机数,有的使用真正的随机数生成器。
实际使用的时候,可以优先获取高强度的安全随机数生成器,如果没有提供,再使用普通等级的安全随机数生成器。
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
public class demo37 {
public static void main(String[] args) {
SecureRandom sr = null;
try {
// 获取高强度安全随机数生成器
sr = SecureRandom.getInstanceStrong();
} catch (NoSuchAlgorithmException e) {
// 获取普通的安全随机数生成器
sr = new SecureRandom();
}
byte[] buffer = new byte[5];
// 用安全随机数填充buffer
sr.nextBytes(buffer);
System.out.println(Arrays.toString(buffer));
}
}
6. 附:Java方法类型¶
6.1 常见方法类型对比¶
方法类型 | 是否 static | 是否依赖对象 | 是否可访问this | 说明 |
---|---|---|---|---|
实例方法 | ❌ | ✅ | ✅ | 操作对象的行为和属性 |
静态方法 | ✅ | ❌ | ❌ | 属于类本身,用于工具函数等 |
工厂方法 | ✅ | ❌ | ❌ | 静态方法,用来构造对象 |
构造方法 | ❌(特殊) | ✅(创建中) | ✅ | 用new 时调用,初始化对象 |
默认方法 | ❌ | ✅ | ✅ | 接口中带实现的方法(Java 8+) |
抽象方法 | ❌ | ✅(需重写) | ❌(无方法体) | 必须由子类实现 |
私有方法 | ✅ / ❌ | ❌ | ❌ | 接口中用于内部辅助(Java 9+) |
6.2 方法声明¶
// 实例方法
public void greet() {}
// 静态方法
public static int sum(int a, int b) {
return a + b;
}
// 工厂方法(静态构造)
public static Dog of(String name) {
return new Dog(name);
}
// 构造方法(无返回值)
public Dog(String name) {
this.name = name;
}
// 默认方法(接口中)
default void log() {
System.out.println("Logging...");
}
// 抽象方法(必须被实现)
abstract void draw();
6.3 方法调用¶
方法类型 | 调用方式示例 |
---|---|
实例方法 | object.method() |
静态方法 | ClassName.method() |
工厂方法 | ClassName.of(...) |
构造方法 | new ClassName(...) |
6.4 示例¶
public class Dog {
private String name;
// 构造方法
public Dog(String name) {
this.name = name;
}
// 实例方法
public void bark() {
System.out.println(name + " says woof!");
}
// 静态方法
public static void info() {
System.out.println("Dogs are great pets.");
}
// 工厂方法
public static Dog of(String name) {
return new Dog(name);
}
}
// 调用方式
Dog.info(); // 静态方法
Dog d = Dog.of("Buddy"); // 工厂方法
d.bark(); // 实例方法