一、java.lang.System – 代表 JVM 所在操作平台。
如果要获取操作平台相关的特性,用 System 。
1、标准输入、标准输出、标准错误输出
- in :标准输入。通常就是键盘。
- out :标准输出。通常就是屏幕。
- err :标准错误输出。通常就是屏幕。
// 输出到控制台
System.out.println("Hello, World!"); // 标准输出
System.err.println("Error occurred!"); // 标准错误输出
// 从控制台读取输入
Scanner scanner = new Scanner(System.in);
System.out.print("请输入内容:");
String input = scanner.nextLine();
System.out.println("输入内容:" + input);
2、垃圾回收 或 程序终止
- exit(int status):结束(终止)虚拟机,状态码非零,表示异常终止。
- 无论是否还有方法、线程在运行,都会结束(终止)。
- 可能导致资源未释放(如:文件句柄、数据库连接)。
- gc():建议 JVM 执行垃圾回收(不保证立即执行)。
// 建议执行垃圾回收(实际执行时间不确定)
System.gc();
// 终止程序(状态码 0 表示正常退出)
System.exit(0);
3、高效数组复制
- arraycopy():高效复制数组 。
public static void main(String[] args) {
int[] src = {1, 2, 3, 4, 5};
int[] dest = new int[5];
// 复制整个数组
System.arraycopy(src, 0, dest, 0, src.length);
// [1, 2, 3, 4, 5]
System.out.println(Arrays.toString(dest));
dest = new int[5];
// 部分复制(从索引 2 开始,复制 2 个元素到目标索引 1 )
System.arraycopy(src, 2, dest, 1, 2);
// [0, 3, 4, 0, 0]
System.out.println(Arrays.toString(dest));
}
4、时间测量
- currentTimeMillis(), nanoTime():获取当前时间戳(毫秒或纳秒),用于性能测量。
public static void main(String[] args) throws InterruptedException {
long startTime = System.currentTimeMillis();
// 模拟耗时操作
Thread.sleep(1000);
long endTime = System.currentTimeMillis();
// 耗时(毫秒): 1004
System.out.println("耗时(毫秒): " + (endTime - startTime));
// 高精度计时(纳秒)
long nanoStart = System.nanoTime();
// ... 执行代码 ...
long nanoEnd = System.nanoTime();
// 耗时(纳秒): 157
System.out.println("耗时(纳秒): " + (nanoEnd - nanoStart));
}
5、环境变量(由操作系统管理)访问
- getenv():获取操作系统所有的环境变量。
- getenv(String name):获取操作系统指定变量的环境变量。
public static void main(String[] args) throws InterruptedException {
// 获取 操作系统 单个环境变量
String path = System.getenv("PATH");
System.out.println("PATH环境变量: " + path);
System.out.println("======================================================");
// 获取 操作系统 所有环境变量
Map<String, String> env = System.getenv();
env.forEach((k, v) -> System.out.println(k + " = " + v));
}
系统属性 vs 环境变量
- 系统属性通过 -D 参数设置(如:java -Dapp.mode=prod MyApp),属于 JVM 层面。
- 环境变量由操作系统管理(如:PATH 或 JAVA_HOME)。
6、系统属性(由 JVM 层面的 -D 参数设置)管理
- Static Properties getProperties():获取 JVM 所有系统属性的值。
- Static Properties getProperties(Sring key):获取 JVM 指定系统属性的值。
public static void main(String[] args) throws InterruptedException {
// 获取 JVM 单个属性
String javaVersion = System.getProperty("java.version");
String userDir = System.getProperty("user.dir");
System.out.println("Java版本: " + javaVersion);
System.out.println("用户目录: " + userDir);
System.out.println("==============================");
// 获取 JVM 所有属性
Properties props = System.getProperties();
// 输出 JVM 中所有的 系统属性
props.list(System.out);
System.out.println("==============================");
// 设置 JVM 自定义属性
System.setProperty("app.mode", "debug");
String mode = System.getProperty("app.mode");
// 输出 "debug"
System.out.println(mode);
}
二、java.lang.Runtime – 代表 JVM 所在的 JRE
- JRE(Java 运行时环境) = JVM + 核心类库
- 通俗地说,如果要获取 JVM 相关的特性,用 Runtime 。
- 典型的 “单例类” Runtime 的构造器被隐藏了。
- 因此,只能通过 getRuntime() 方法来获取 Runtime 实例。
- 核心作用:
- 系统交互:执行外部进程、获取系统资源信息(如:CPU、内存)、注册 JVM 关闭钩子。
- 资源管理:监控内存 的使用,触发垃圾回收,终止 JVM。
1、执行外部命令:exec(String command)
- 功能:执行操作系统命令,返回 Process 对象。
- 示例:执行 ping 命令。
public static void main(String[] args) {
// 1、获取 Runtime 实例。
Runtime runtime = Runtime.getRuntime();
// 2、执行 操作系统 命令,返回 Process 对象(以 ping 为例)
try {
Process process;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
// Windows
process = runtime.exec("ping -n 4 baidu.com");
} else {
// Unix/Linux/Mac
process = runtime.exec("ping -c 4 baidu.com");
}
// 读取命令输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 等待命令执行完成
int exitCode = process.waitFor();
// 命令退出码: 0
System.out.println("命令退出码: " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
2、获取系统资源信息
- 操作系统信息、cpu 核心数、内存管理。
- totalMemory():JVM 当前分配的总内存(字节)。
- freeMemory():JVM 当前空闲内存(字节)。
- maxMemory():JVM 最大可分配内存(字节)。
- gc():建议 JVM 执行垃圾回收(不保证立即执行)。
public static void main(String[] args) {
// 1、获取 Runtime 实例。
Runtime runtime = Runtime.getRuntime();
// 2、输出系统信息
// 操作系统: Mac OS X
System.out.println("操作系统: " + System.getProperty("os.name"));
// 3、返回 JVM 可用的处理器数量。
// 处理器核心数: 12
System.out.println("处理器核心数: " + runtime.availableProcessors());
// 4、内存管理
// JVM 当前分配的总内存(字节)。
// 总内存: 258 MB
System.out.println("总内存: " + runtime.totalMemory() / 1024 / 1024 + " MB");
// JVM 当前空闲内存(字节)。
// 空闲内存: 252 MB
System.out.println("空闲内存: " + runtime.freeMemory() / 1024 / 1024 + " MB");
// maxMemory():JVM 最大可分配内存(字节)。
// 最大内存: 4096 MB
System.out.println("最大内存: " + runtime.maxMemory() / 1024 / 1024 + " MB");
// 5、手动触发垃圾回收。建议 JVM 执行垃圾回收(不保证立即执行)。
runtime.gc();
Thread.sleep(2000);
// GC 后空闲内存: 26 MB
System.out.println("GC 后空闲内存: " + runtime.freeMemory() / 1024 / 1024 + " MB");
}
3、终止 JVM:exit(int status)
- 功能:终止当前 JVM,状态码 0 表示正常退出,非零表示异常。
- 示例:条件性终止程序。
if (criticalErrorOccurred) {
// 终止当前 JVM,状态码 0 表示正常退出,非零表示异常。
// 强制终止,状态码 1
Runtime.getRuntime().exit(1);
}
4、注册关闭钩子:addShutdownHook(Thread hook)
- 功能:在 JVM 关闭前执行清理任务(如:释放资源)。
- 示例:保存日志文件。
public static void main(String[] args) {
// 1、获取 Runtime 实例。
Runtime runtime = Runtime.getRuntime();
// 2、注册关闭钩子
// 在 JVM 关闭前执行清理任务(如释放资源)。
runtime.addShutdownHook(new Thread(() -> {
System.out.println("JVM 关闭中...");
System.out.println("正在执行清理操作...");
System.out.println("正在保存日志...");
}));
// 终止当前 JVM,状态码 0 表示正常退出,非零表示异常。
runtime.exit(0);
}
三、Java 生成随机数
1、Math.random() 静态方法
- 作用:大于或等于 0.0 且小于 1.0 的伪随机 double 精度值。
- 本质:用的就是 new java.util.Random()。
- 将 Random 对象 赋值给 Math 的静态内部类 RandomNumberGeneratorHolder 的静态常量 randomNumberGenerator 。
- 使用 Math.random() 时,就不用每次就 new 一个 Random 对象了。
public static double random() {
return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
private static final class RandomNumberGeneratorHolder {
static final Random randomNumberGenerator = new Random();
}
2、java.util.Random 工具类 – 生成 伪随机数
- nextInt(int bound):得到一个 [0, bound) 范围内的随机数。
- 因此,bound 参数必须大于 0。
- 否则,会抛出 java.lang.IllegalArgumentException 异常。
- 示例:
- 生成 [5, 15] 的整数。
int min = 5, max = 15;
int value = random.nextInt(max - min + 1) + min;
System.out.println("范围随机数: " + value);
- 使用了 AtomicLong、自旋 + CAS ,所以, Random 生成随机数是线程安全的。
- 在高并发场景中,造成了很大的性能开销。
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
public Random(long seed) {
if (getClass() == Random.class)
this.seed = new AtomicLong(initialScramble(seed));
else {
// subclass might have overriden setSeed
this.seed = new AtomicLong();
setSeed(seed);
}
}
public int nextInt() {
// 调用 nextInt 默认会生成 32 个比特位的数字。
return next(32);
}
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
int r = next(31);
int m = bound - 1;
if ((bound & m) == 0) {
// i.e., bound is a power of 2
r = (int)((bound * (long)r) >> 31);
} else {
for (int u = r; u - (r = u % bound) + m < 0; u = next(31)){
;
}
}
return r;
}
// 计算出 nextseed 会使 CAS,将 nextseed 替换现有 oldseed 。
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
- 只要两个 Random 对象的种子相同,而且方法的调用顺序也相同,它们就会产生相同的数字序列。
- Random 产生的数字并不是真正随机的,而是一种伪随机。
public static void main(String[] args) {
long seed = 12345L;
Random random1 = new Random(seed);
Random random2 = new Random(seed);
// Random1: 51
System.out.println("Random1: " + random1.nextInt(100));
// Random2: 51 与 Random1 相同
System.out.println("Random2: " + random2.nextInt(100));
}
3、java.util.concurrent.ThreadLocalRandom 工具类(Java7)
- 每个线程必须通过 ThreadLocalRandom.current() 获取自己的实例,禁止跨线程传递。
- 即:不能在多线程之间共享 ThreadLocalRandom 。
import java.util.concurrent.ThreadLocalRandom;
public class ParallelStreamExample {
public static void main(String[] args) {
// 生成10个 [100, 200) 的随机数(并行流)
ThreadLocalRandom.current().ints(10, 100, 200)
.parallel()
.forEach(System.out::println);
}
}
- 虽然 Random 线程安全,但是,由于 CAS 乐观锁在多线程下消耗性能。
- 所以,多线性推荐使用 ThreadLocalRandom;
- ThreadLocalRandom 都是通过 UNSAFE 调用本地方法拿到线程本身的一些变量作为外部因子。
- 所有的参数绑定在线程本身,和其他线程没有竞争。
- 所以,可以不加锁就保证线程安全。
public static ThreadLocalRandom current() {
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0){
localInit();
}
return instance;
}
static final void localInit() {
int p = probeGenerator.addAndGet(PROBE_INCREMENT);
int probe = (p == 0) ? 1 : p; // skip 0
long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
Thread t = Thread.currentThread();
UNSAFE.putLong(t, SEED, seed);
UNSAFE.putInt(t, PROBE, probe);
}
public int nextInt() {
return mix32(nextSeed());
}
private static int mix32(long z) {
z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32);
}
final long nextSeed() {
Thread t; long r;
// read and update per-thread seed
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}
4、java.Security.SecureRandom
- Java 提供的加密安全随机数生成器(CSPRNG),专为 高安全性 场景设计。
- 如:密钥生成、令牌验证、加密盐值等。
- SecureRandom 通过强随机性算法生成不可预测的随机数,确保安全敏感操作的安全性。
- SecureRandom 可以理解为 Random 升级,它的种子选取比较多。
- 主要有:时间、CPU 使用情况、点击事件等一些种子,安全性高。
- 因为 SecureRandom 采用了很多外部参数,会产生熵源不足时阻塞问题。
- 曾经项目中,因为,有的业务凌晨没有流量,这个问题实际发生过。
- 建议有明显低谷的业务,低谷时低于 1TPS 时不要用。
- 在熵不足的系统(如:某些虚拟化环境)中,/dev/random 可能阻塞。
- 建议用 /dev/urandom(通过配置 -Djava.security.egd=file:/dev/urandom)。
- 算法选择:
- SecureRandom.getInstanceStrong():
- 返回平台最强的随机源(可能阻塞直到熵足够)。
- new SecureRandom():
- 默认使用 NativePRNG 或混合模式,优先非阻塞源(如:/dev/urandom)。
0、指定算法和提供者
- 代码:
// 使用 SHA1PRNG 算法(Sun 提供者)
SecureRandom sr1 = SecureRandom.getInstance("SHA1PRNG", "SUN");
// 使用 NativePRNG(依赖 操作系统)
SecureRandom sr2 = SecureRandom.getInstance("NativePRNG");
1、生成随机字节数组(加密盐)
- 代码:
public static void main(String[] args) {
// 1、生成随机字节数组(加密盐)
try {
// 创建 SecureRandom 实例(默认算法)
// 或 new SecureRandom()
SecureRandom secureRandom = SecureRandom.getInstanceStrong();
// 生成 16 字节的随机盐
byte[] salt = new byte[16];
secureRandom.nextBytes(salt);
// 随机盐(Hex): b91d220a3a63698ff89e72b53c4a6cba
System.out.println("随机盐(Hex): " + bytesToHex(salt));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
// 字节数组转十六进制字符串
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
2、生成指定范围的随机整数(如 OTP 验证码)
- 代码:
public static void main(String[] args) {
// 2、生成指定范围的随机整数(如 OTP 验证码)
SecureRandom secureRandom = new SecureRandom();
// 生成 100000-999999 的6位数
int otp = secureRandom.nextInt(900000) + 100000;
// OTP 验证码: 105075
System.out.println("OTP 验证码: " + otp);
}
3、生成加密密钥(AES 密钥)
- 代码:
public static void main(String[] args) throws NoSuchAlgorithmException {
// 3、生成加密密钥(AES 密钥)
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
// 256位密钥
keyGen.init(256, secureRandom);
SecretKey secretKey = keyGen.generateKey();
// AES 密钥(Base64): TxU+QzxntpYgQtyFvUCgQgDLHdHYOddfTn4yRJ8qRgU=
System.out.println("AES 密钥(Base64): " + java.util.Base64.getEncoder().encodeToString(secretKey.getEncoded()));
}
5、随机字符串 生成
- 随机字符串,可以使用 Apache Commons-Lang 包中的 RandomStringUtils 类。
四、java.lang.Math 类
- Java 标准库中提供数学运算的工具类,包含大量静态方法,可直接调用。
- Math 类方法通常由底层硬件优化,适合高性能计算。
1、取整
- 向上取整
// 4.0
double ceil = Math.ceil(3.2);
- 向下取整
// 3.0
double floor = Math.floor(3.8);
2、四舍五入
- Math.round() 函数用于将一个数字四舍五入到最接近的整数。其工作原理如下:
- 1、正数四舍五入规则:
- 如果小数部分小于 0.5,则向下舍入(即向下取整)。
- 如果小数部分大于或等于 0.5,则向上舍入(即向上取整)12 。
- 2、负数四舍五入规则:
- 如果小数部分大于 -0.5,则向上舍入(即向零方向舍入)。
- 如果小数部分小于或等于 -0.5,则向下舍入(即远离零方向舍入)。
// 4
long round = Math.round(3.5);
// -3(注意中间值向正无穷舍入)
long roundNeg = Math.round(-3.5);
五、java.util.UUID
- Java 中用于生成和操作 通用唯一标识符 (Universally Unique Identifier,UUID) 的工具类。
- UUID 是一个 128 位的全局唯一标识符,常用于分布式系统中唯一标识数据或资源。
public static void main(String[] args) {
// 生成随机 UUID
UUID uuid = UUID.randomUUID();
// UUID: 8af85994-1506-4bf3-9205-9bbaaa99c086
System.out.println("UUID: " + uuid);
// 解析字符串为 UUID
UUID parsed = UUID.fromString(uuid.toString());
// Parsed UUID: 8af85994-1506-4bf3-9205-9bbaaa99c086
System.out.println("Parsed UUID: " + parsed);
// 比较 UUID
// true .
System.out.println("Equals: " + uuid.equals(parsed));
}
1、版本演化
- 版本1 - 基于时间戳、单调计数器和 MAC 地址生成。
- 全局唯一且部分有序。
- 版本2 - 保留用于没有已知详细信息的安全 ID。(
基本不再使用)- 版本3 - 根据提供的某些数据的 MD5 哈希值生成的。
* RFC 在候选数据中建议了 DNS 和 URL。- 版本4 - 根据完全随机的数据生成的。
- 高随机性,无顺序性,重复概率极低。
- 适合无需排序的场景(如:会话 ID)。
- 版本5 - 根据提供的一些数据的 SHA1 哈希值生成的。比版本3更安全
* 与 v3 一样,RFC 建议使用 DNS 或 URL 作为候选。- 版本6 - 基于时间戳、单调计数器和 MAC 地址生成。
* 这些数据与版本 1 相同,但更改了顺序,以便对它们进行排序时将按创建时间排序。
- 时间戳部分可自然排序,适合数据库主键。
- 版本7 - 由时间戳和随机数据生成。
- Unix时间戳(毫秒,48位) + 随机数(74位)。
- 跨系统时间兼容,无需依赖节点ID。
- 时间戳高位保证有序,适合分布式系统。
- 版本8 - 完全是自定义的(除了所有版本都包含的必需版本/变体字段)。
2、版本选择建议
- 需要 时间有序:
- 优先选择 v6/v7(若环境支持),其次 v1(注意隐私)。
- 需要 完全随机:
- 使用 v4(默认选择)。
- 需要 确定性生成:
- 使用 v3/v5(如资源唯一标识)。
- 需要 自定义结构:
- 使用 v8 或自行实现。
六、Comparable 和 Comparator
1、实现自定义排序的两个接口
- 对象实现 Comparable 接口
- 定义比较规则的类实现 Comparator 接口
2、代码
public class AboutSort {
public static void main(String[] args) {
User user1 = new User(0);
User user2 = new User(0);
System.out.println(user1.compareTo(user2));
Shoper shoper1 = new Shoper(1);
Shoper shoper2 = new Shoper(2);
Shoper.ShoperComparator comparator = new Shoper(0).new ShoperComparator();
System.out.println(comparator.compare(shoper1, shoper2));
}
}
class User implements Comparable<User>{
int age;
public User(int age) {
this.age = age;
}
@Override
public int compareTo(User user) {
if(null == user){
throw new RuntimeException("被比较对象不能为 null");
}
int ageGap = this.age - user.age;
return ageGap < 0 ? -1 : ageGap > 0 ? 1 : 0;
}
}
class Shoper {
int age;
public Shoper(int age) {
this.age = age;
}
class ShoperComparator implements Comparator<Shoper>{
@Override
public int compare(Shoper o1, Shoper o2) {
int ageGap = o1.age - o2.age;
return ageGap < 0 ? -1 : ageGap > 0 ? 1 : 0;
}
}
}