一、Object类 - 万类之祖
1.1 Object类的作用
Object是Java中所有类的根类,每个类都直接或间接继承自Object。当你定义一个类时,如果没有显式地指定父类,Java编译器会自动让它继承Object类。
继承关系示例:
// 等价写法1
class MyClass extends Object {
// ...
}
// 等价写法2(编译器自动添加extends Object)
class MyClass {
// ...
}
1.2 Object类的核心方法
equals()方法
// Object类中的原始实现
public boolean equals(Object obj) {
return (this == obj); // 比较对象的内存地址
}
// 实际应用场景:
Object obj1 = new Object();
Object obj2 = new Object();
Object obj3 = obj1;
System.out.println(obj1.equals(obj2)); // false,内存地址不同
System.out.println(obj1.equals(obj3)); // true,引用同一个对象
为什么需要重写equals()?
-
Object的默认equals()只比较内存地址
-
实际开发中我们需要比较对象的内容是否相等
-
比如:两个学生对象,学号相同就应该是同一个学生
正确重写equals()的步骤:
public class Student {
private String name;
private int age;
@Override
public boolean equals(Object obj) {
// 1. 检查是否同一个对象
if (this == obj) return true;
// 2. 检查是否为null
if (obj == null) return false;
// 3. 检查类型是否相同
if (getClass() != obj.getClass()) return false;
// 4. 类型转换
Student other = (Student) obj;
// 5. 比较关键属性
return age == other.age && Objects.equals(name, other.name);
}
}
hashCode()方法
// Object类中的原始实现
public native int hashCode(); // native表示用C/C++实现
// 哈希值的应用场景:
// 1. HashMap、HashSet等集合的快速查找
// 2. 对象在散列表中的存储位置
哈希冲突演示:
String s1 = "通话";
String s2 = "重地";
System.out.println(s1.hashCode()); // 1179395
System.out.println(s2.hashCode()); // 1179395
System.out.println(s1.equals(s2)); // false
toString()方法
// Object类中的原始实现
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
// 示例输出:com.example.Student@1b6d3586
为什么要重写toString()?
// 重写前
Student student = new Student("张三", 20);
System.out.println(student); // 输出:Student@1b6d3586
// 重写后
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
// 输出:Student{name='张三', age=20}
1.3 hashCode()和equals()的关系(面试重点)
重要规则:
-
equals相等,hashCode必须相等
-
hashCode相等,equals不一定相等
-
重写equals()必须重写hashCode()
违反规则的后果:
public class BadStudent {
private String name;
private int age;
// 只重写equals,不重写hashCode
@Override
public boolean equals(Object obj) {
// ... equals实现
}
public static void main(String[] args) {
BadStudent s1 = new BadStudent("张三", 20);
BadStudent s2 = new BadStudent("张三", 20);
Set<BadStudent> set = new HashSet<>();
set.add(s1);
set.add(s2);
System.out.println(set.size()); // 可能是2!应该为1
}
}
正确实现:
@Override
public int hashCode() {
// 使用31的原因:
// 1. 31是素数,减少哈希冲突
// 2. 31 * i = (i << 5) - i,JVM可以优化
int result = 17;
result = 31 * result + (name == null ? 0 : name.hashCode());
result = 31 * result + age;
return result;
// Java 7+ 简化写法
// return Objects.hash(name, age);
}
1.4 == 和 equals 的对比
|
比较维度 |
== 运算符 |
equals() 方法 |
|---|---|---|
|
基本类型 |
比较值是否相等 |
不可用 |
|
引用类型 |
比较内存地址是否相同 |
比较对象内容是否相等(可重写) |
|
null处理 |
可与null比较 |
需先判空,否则NPE |
|
性能 |
O(1),直接比较 |
取决于实现,可能较慢 |
|
重写性 |
不可重写 |
可重写 |
|
示例 |
|
|
特殊案例:String常量池
String s1 = "hello"; // 常量池
String s2 = "hello"; // 常量池
String s3 = new String("hello"); // 堆内存
String s4 = new String("hello"); // 堆内存
System.out.println(s1 == s2); // true,指向常量池同一对象
System.out.println(s1 == s3); // false,内存地址不同
System.out.println(s3 == s4); // false,不同对象
System.out.println(s1.equals(s3)); // true,内容相同
System.out.println(s3.equals(s4)); // true,内容相同
二、包装类(Wrapper Classes)
2.1 基本类型与包装类的对应关系
|
基本类型 |
包装类 |
字节数 |
取值范围 |
默认值 |
|---|---|---|---|---|
|
byte |
Byte |
1 |
-128 ~ 127 |
0 |
|
short |
Short |
2 |
-32768 ~ 32767 |
0 |
|
int |
Integer |
4 |
-2³¹ ~ 2³¹-1 |
0 |
|
long |
Long |
8 |
-2⁶³ ~ 2⁶³-1 |
0L |
|
float |
Float |
4 |
±3.4E-38 ~ ±3.4E+38 |
0.0f |
|
double |
Double |
8 |
±1.7E-308 ~ ±1.7E+308 |
0.0d |
|
char |
Character |
2 |
'\u0000' ~ '\uffff' |
'\u0000' |
|
boolean |
Boolean |
1 |
true/false |
false |
2.2 Integer类详解
构造方法
// 1. 通过int值创建
Integer i1 = new Integer(100);
// 2. 通过字符串创建(可能抛出NumberFormatException)
Integer i2 = new Integer("100");
// 3. 推荐使用valueOf(有缓存优化)
Integer i3 = Integer.valueOf(100);
Integer i4 = Integer.valueOf("100");
常用方法
// 类型转换
Integer num = Integer.valueOf(100);
byte b = num.byteValue(); // 100
short s = num.shortValue(); // 100
int i = num.intValue(); // 100
long l = num.longValue(); // 100L
float f = num.floatValue(); // 100.0f
double d = num.doubleValue(); // 100.0d
// 字符串转换
String str = num.toString(); // "100"
int parseInt = Integer.parseInt("100"); // 100
Integer valueOf = Integer.valueOf("100"); // Integer(100)
// 进制转换
System.out.println(Integer.toBinaryString(10)); // "1010" 二进制
System.out.println(Integer.toOctalString(10)); // "12" 八进制
System.out.println(Integer.toHexString(10)); // "a" 十六进制
// 比较
Integer a = 100, c = 200;
System.out.println(a.equals(100)); // true
System.out.println(a.compareTo(200)); // -1 (a < c)
System.out.println(Integer.compare(a, c)); // -1
2.3 自动装箱与拆箱(AutoBoxing & Unboxing)
什么是自动装箱拆箱?
// 自动装箱:基本类型 → 包装类型
Integer a = 100; // 编译器转换为:Integer.valueOf(100)
// 自动拆箱:包装类型 → 基本类型
int b = a; // 编译器转换为:a.intValue()
// 运算时的自动拆箱
Integer x = 10;
Integer y = 20;
Integer z = x + y; // 先拆箱:x.intValue() + y.intValue(),再装箱
Integer缓存机制(重要!)
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println(a == b); // true,-128~127之间有缓存
System.out.println(c == d); // false,超出缓存范围
// 源码分析:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
// 默认缓存范围:-128 ~ 127
自动装箱的性能问题
// 不推荐的写法:频繁创建Integer对象
Long start = System.currentTimeMillis();
Integer sum = 0; // 这里发生了自动装箱
for (int i = 0; i < 1000000; i++) {
sum += i; // 这里发生了:拆箱 → 相加 → 装箱
}
Long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");
// 推荐的写法:使用基本类型
long start2 = System.currentTimeMillis();
int sum2 = 0; // 基本类型,在栈中分配
for (int i = 0; i < 1000000; i++) {
sum2 += i; // 直接运算
}
long end2 = System.currentTimeMillis();
System.out.println("耗时:" + (end2 - start2) + "ms");
自动装箱的最佳实践:
-
循环体内避免使用包装类型
-
集合框架必须使用包装类型
-
数据库查询结果可能为null时使用包装类型
-
方法参数允许null值时使用包装类型
三、字符串类
3.1 String类 - 不可变字符串
String的特点
// 1. final类,不可被继承
// 2. 使用final char[]存储,不可修改
// 3. 实现了Serializable、Comparable、CharSequence接口
// 不可变性示例
String str = "hello";
str.concat(" world"); // 创建新对象,原str不变
System.out.println(str); // 输出:hello
String newStr = str.concat(" world");
System.out.println(newStr); // 输出:hello world
字符串的创建方式
// 方式1:字面量(推荐)
String s1 = "hello"; // 在常量池中创建
String s2 = "hello"; // 复用常量池中的"hello"
// 方式2:new关键字
String s3 = new String("hello"); // 在堆中创建新对象
String s4 = new String("hello"); // 在堆中创建另一个新对象
// 方式3:字符数组
char[] chars = {'h', 'e', 'l', 'l', 'o'};
String s5 = new String(chars);
// 方式4:字节数组
byte[] bytes = {104, 101, 108, 108, 111};
String s6 = new String(bytes, StandardCharsets.UTF_8);
内存结构分析:
String s1 = "abc"; // 常量池
String s2 = new String("abc"); // 堆内存
String s3 = s2.intern(); // 返回常量池引用
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // true
System.out.println(s1.equals(s2)); // true
String的常用方法
1. 字符串获取和判断
String str = "Hello World";
// 获取长度
int length = str.length(); // 11
// 获取字符
char ch = str.charAt(1); // 'e'
// 获取索引
int index1 = str.indexOf('o'); // 4
int index2 = str.lastIndexOf('o'); // 7
int index3 = str.indexOf("World"); // 6
// 判断
boolean b1 = str.startsWith("Hello"); // true
boolean b2 = str.endsWith("World"); // true
boolean b3 = str.contains("lo"); // true
boolean b4 = str.isEmpty(); // false
boolean b5 = "".isEmpty(); // true
2. 字符串转换
String str = " Hello Java ";
// 大小写转换
String upper = str.toUpperCase(); // " HELLO JAVA "
String lower = str.toLowerCase(); // " hello java "
// 去除空格
String trim = str.trim(); // "Hello Java"
// 转字符数组
char[] chars = str.toCharArray();
// 字符串分割
String[] parts = "a,b,c,d".split(","); // ["a", "b", "c", "d"]
// 子字符串
String sub1 = str.substring(2); // "Hello Java "
String sub2 = str.substring(2, 7); // "Hello"
3. 字符串替换
String str = "Hello World";
// 替换字符
String s1 = str.replace('l', 'L'); // "HeLLo WorLd"
// 替换字符串
String s2 = str.replace("World", "Java"); // "Hello Java"
// 正则替换
String s3 = "a1b2c3".replaceAll("\\d", "*"); // "a*b*c*"
String s4 = "a1b2c3".replaceFirst("\\d", "*"); // "a*b2c3"
4. 字符串比较
String s1 = "hello";
String s2 = "HELLO";
String s3 = "hello";
String s4 = "hell";
// equals - 区分大小写
System.out.println(s1.equals(s2)); // false
// equalsIgnoreCase - 忽略大小写
System.out.println(s1.equalsIgnoreCase(s2)); // true
// compareTo - 字典序比较
System.out.println(s1.compareTo(s3)); // 0
System.out.println(s1.compareTo(s4)); // 1
System.out.println(s4.compareTo(s1)); // -1
3.2 StringBuffer - 可变字符串(线程安全)
StringBuffer的特点
// 1. 线程安全的可变字符序列
// 2. 内部使用char[]存储,可动态扩容
// 3. 方法使用synchronized修饰,保证线程安全
构造方法
// 默认构造:初始容量16
StringBuffer sb1 = new StringBuffer();
// 指定初始容量
StringBuffer sb2 = new StringBuffer(100);
// 通过字符串创建
StringBuffer sb3 = new StringBuffer("Hello");
常用方法
StringBuffer sb = new StringBuffer("Hello");
// 追加
sb.append(" World"); // "Hello World"
sb.append(123); // "Hello World123"
sb.append(true); // "Hello World123true"
// 插入
sb.insert(5, ","); // "Hello, World123true"
// 删除
sb.delete(5, 6); // "Hello World123true"
sb.deleteCharAt(5); // "Hello orld123true"
// 替换
sb.replace(6, 11, "Java"); // "Hello Java123true"
// 反转
sb.reverse(); // "eurt321avaJ olleH"
// 容量相关
int capacity = sb.capacity(); // 当前容量
sb.ensureCapacity(100); // 确保最小容量
sb.trimToSize(); // 修剪到实际长度
// 转String
String result = sb.toString();
3.3 StringBuilder - 可变字符串(非线程安全)
StringBuilder vs StringBuffer
// 1. StringBuilder非线程安全,性能更高
// 2. StringBuffer线程安全,性能稍低
// 3. API完全兼容
// 性能对比示例
long start = System.currentTimeMillis();
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < 100000; i++) {
buffer.append(i);
}
long bufferTime = System.currentTimeMillis() - start;
start = System.currentTimeMillis();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 100000; i++) {
builder.append(i);
}
long builderTime = System.currentTimeMillis() - start;
System.out.println("StringBuffer: " + bufferTime + "ms");
System.out.println("StringBuilder: " + builderTime + "ms");
3.4 字符串拼接性能对比
// 方式1:+ 拼接(少量时可用)
String s1 = "a" + "b" + "c"; // 编译器优化为"abc"
// 方式2:concat方法
String s2 = "a".concat("b").concat("c");
// 方式3:StringBuilder(单线程推荐)
StringBuilder sb = new StringBuilder();
sb.append("a").append("b").append("c");
String s3 = sb.toString();
// 方式4:StringBuffer(多线程环境)
StringBuffer sbf = new StringBuffer();
sbf.append("a").append("b").append("c");
String s4 = sbf.toString();
// 方式5:String.join(Java 8+)
String s5 = String.join("", "a", "b", "c");
性能测试结果(拼接10000次):
-
使用
+:最慢,每次拼接创建新对象 -
使用concat:较慢
-
使用StringBuilder:最快
-
使用StringBuffer:次快(有同步开销)
四、日期时间类
4.1 System类的时间方法
// 1. 当前时间戳(毫秒)
long timestamp = System.currentTimeMillis();
System.out.println("时间戳:" + timestamp);
// 2. 当前时间戳(纳秒,用于高精度计时)
long nanoTime = System.nanoTime();
System.out.println("纳秒时间:" + nanoTime);
// 3. 计算程序运行时间
long start = System.currentTimeMillis();
// ... 执行代码 ...
long end = System.currentTimeMillis();
System.out.println("执行时间:" + (end - start) + "ms");
// 4. 系统属性
String osName = System.getProperty("os.name");
String javaVersion = System.getProperty("java.version");
System.out.println("操作系统:" + osName);
System.out.println("Java版本:" + javaVersion);
4.2 Date类(已过时,了解即可)
// 创建Date对象
Date now = new Date(); // 当前时间
System.out.println(now); // 输出:Mon Jan 17 10:30:00 CST 2023
// 指定时间创建
Date specificDate = new Date(1234567890000L);
System.out.println(specificDate);
// 获取时间戳
long time = now.getTime(); // 毫秒数
System.out.println(time);
// 设置时间
now.setTime(1234567890000L);
4.3 Calendar类(传统日期处理)
// 获取Calendar实例
Calendar calendar = Calendar.getInstance();
// 获取日期各部分
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1; // 月份从0开始
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
int week = calendar.get(Calendar.DAY_OF_WEEK); // 周日=1,周一=2...
// 设置日期
calendar.set(2023, Calendar.JANUARY, 1, 0, 0, 0);
// 日期计算
calendar.add(Calendar.DAY_OF_MONTH, 10); // 10天后
calendar.add(Calendar.MONTH, -1); // 1月前
// 比较日期
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal2.add(Calendar.DAY_OF_MONTH, 1);
boolean isBefore = cal1.before(cal2); // true
boolean isAfter = cal1.after(cal2); // false
boolean isEqual = cal1.equals(cal2); // false
4.4 SimpleDateFormat(日期格式化)
// 创建格式化对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Date -> String
Date now = new Date();
String formatted = sdf.format(now);
System.out.println(formatted); // 2023-01-17 10:30:00
// String -> Date
try {
String dateStr = "2023-01-17 10:30:00";
Date date = sdf.parse(dateStr);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}
// 常用模式字母
/**
* yyyy - 年
* MM - 月
* dd - 日
* HH - 时(24小时制)
* hh - 时(12小时制)
* mm - 分
* ss - 秒
* SSS - 毫秒
* E - 星期
* a - 上午/下午
*/
注意:SimpleDateFormat线程不安全
// 错误用法:多线程共享同一个SimpleDateFormat
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 正确用法1:每个线程创建自己的实例
// 正确用法2:使用ThreadLocal
// 正确用法3:使用Java 8的DateTimeFormatter
4.5 Java 8新的日期时间API(推荐)
// 1. LocalDate - 日期
LocalDate today = LocalDate.now();
LocalDate specificDate = LocalDate.of(2023, 1, 17);
System.out.println("今天:" + today);
System.out.println("指定日期:" + specificDate);
// 2. LocalTime - 时间
LocalTime nowTime = LocalTime.now();
LocalTime specificTime = LocalTime.of(10, 30, 0);
System.out.println("现在时间:" + nowTime);
System.out.println("指定时间:" + specificTime);
// 3. LocalDateTime - 日期时间
LocalDateTime dateTime = LocalDateTime.now();
System.out.println("当前日期时间:" + dateTime);
// 4. DateTimeFormatter - 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = dateTime.format(formatter);
System.out.println("格式化:" + formatted);
// 5. 日期计算
LocalDate tomorrow = today.plusDays(1);
LocalDate lastMonth = today.minusMonths(1);
System.out.println("明天:" + tomorrow);
System.out.println("上个月:" + lastMonth);
// 6. 日期比较
boolean isBefore = today.isBefore(tomorrow);
boolean isAfter = today.isAfter(lastMonth);
System.out.println("今天在明天之前:" + isBefore);
System.out.println("今天在上个月之后:" + isAfter);
五、Arrays类 - 数组工具类
5.1 数组的创建和初始化
// 声明数组
int[] arr1; // 推荐
int arr2[]; // 不推荐
// 创建数组
int[] arr3 = new int[5]; // 长度为5,默认值0
int[] arr4 = {1, 2, 3, 4, 5}; // 初始化
int[] arr5 = new int[]{1, 2, 3, 4, 5};
5.2 Arrays常用方法
1. 数组转字符串
int[] arr = {1, 2, 3, 4, 5};
String str = Arrays.toString(arr);
System.out.println(str); // [1, 2, 3, 4, 5]
// 深度toString(用于多维数组)
int[][] deepArr = {{1, 2}, {3, 4}};
String deepStr = Arrays.deepToString(deepArr);
System.out.println(deepStr); // [[1, 2], [3, 4]]
2. 数组排序
int[] arr = {5, 3, 1, 4, 2};
// 默认排序(升序)
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5]
// 部分排序
int[] arr2 = {5, 3, 1, 4, 2, 6, 8, 7};
Arrays.sort(arr2, 2, 6); // 排序索引[2,6)的元素
System.out.println(Arrays.toString(arr2)); // [5, 3, 1, 2, 4, 6, 8, 7]
// 自定义排序
Integer[] arr3 = {5, 3, 1, 4, 2};
Arrays.sort(arr3, (a, b) -> b - a); // 降序
System.out.println(Arrays.toString(arr3)); // [5, 4, 3, 2, 1]
3. 数组查找
int[] arr = {1, 3, 5, 7, 9};
// 二分查找(必须先排序)
int index = Arrays.binarySearch(arr, 5);
System.out.println("5的索引:" + index); // 2
// 查找不存在的元素
int index2 = Arrays.binarySearch(arr, 6);
System.out.println("6的索引:" + index2); // 负数,表示插入点
4. 数组比较
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
int[] arr3 = {1, 2, 4};
// equals - 比较一维数组
boolean eq1 = Arrays.equals(arr1, arr2); // true
boolean eq2 = Arrays.equals(arr1, arr3); // false
// deepEquals - 比较多维数组
int[][] deepArr1 = {{1, 2}, {3, 4}};
int[][] deepArr2 = {{1, 2}, {3, 4}};
boolean deepEq = Arrays.deepEquals(deepArr1, deepArr2); // true
5. 数组填充
int[] arr = new int[5];
// 全部填充
Arrays.fill(arr, 1);
System.out.println(Arrays.toString(arr)); // [1, 1, 1, 1, 1]
// 部分填充
Arrays.fill(arr, 1, 4, 2);
System.out.println(Arrays.toString(arr)); // [1, 2, 2, 2, 1]
6. 数组复制
int[] original = {1, 2, 3, 4, 5};
// copyOf - 指定长度
int[] copy1 = Arrays.copyOf(original, 3);
System.out.println(Arrays.toString(copy1)); // [1, 2, 3]
int[] copy2 = Arrays.copyOf(original, 7);
System.out.println(Arrays.toString(copy2)); // [1, 2, 3, 4, 5, 0, 0]
// copyOfRange - 指定范围
int[] copy3 = Arrays.copyOfRange(original, 1, 4);
System.out.println(Arrays.toString(copy3)); // [2, 3, 4]
7. 数组转列表
Integer[] arr = {1, 2, 3, 4, 5};
// Arrays.asList - 返回固定大小的列表
List<Integer> list = Arrays.asList(arr);
System.out.println(list); // [1, 2, 3, 4, 5]
// 注意:这个列表不支持添加/删除操作
// list.add(6); // 抛出UnsupportedOperationException
// 如果要可变列表
List<Integer> mutableList = new ArrayList<>(Arrays.asList(arr));
mutableList.add(6); // 正常
8. 并行排序(大数据量优化)
int[] largeArr = new int[1000000];
Random random = new Random();
for (int i = 0; i < largeArr.length; i++) {
largeArr[i] = random.nextInt(1000000);
}
// 并行排序(多核CPU加速)
long start = System.currentTimeMillis();
Arrays.parallelSort(largeArr);
long time = System.currentTimeMillis() - start;
System.out.println("并行排序耗时:" + time + "ms");
// 对比普通排序
int[] largeArr2 = Arrays.copyOf(largeArr, largeArr.length);
start = System.currentTimeMillis();
Arrays.sort(largeArr2);
time = System.currentTimeMillis() - start;
System.out.println("普通排序耗时:" + time + "ms");
六、Random类 - 随机数生成
6.1 Random类的使用
创建Random对象
// 默认种子(使用当前时间毫秒数)
Random random1 = new Random();
// 指定种子(相同种子产生相同随机序列)
Random random2 = new Random(12345L);
生成随机数
Random random = new Random();
// 1. 生成int随机数
int randomInt = random.nextInt(); // 整个int范围
int boundInt = random.nextInt(100); // [0, 100)
// 2. 生成指定范围的随机数
int min = 10, max = 20;
int rangeInt = random.nextInt(max - min) + min; // [10, 20)
// 3. 生成long随机数
long randomLong = random.nextLong();
// 4. 生成float随机数
float randomFloat = random.nextFloat(); // [0.0, 1.0)
// 5. 生成double随机数
double randomDouble = random.nextDouble(); // [0.0, 1.0)
// 6. 生成boolean随机数
boolean randomBoolean = random.nextBoolean();
// 7. 生成高斯分布随机数
double gaussian = random.nextGaussian(); // 均值0,标准差1
6.2 种子(Seed)的作用
// 相同种子产生相同随机序列
Random r1 = new Random(123);
Random r2 = new Random(123);
System.out.println("r1的第一个随机数:" + r1.nextInt(100));
System.out.println("r2的第一个随机数:" + r2.nextInt(100));
System.out.println("r1的第二个随机数:" + r1.nextInt(100));
System.out.println("r2的第二个随机数:" + r2.nextInt(100));
// 输出:
// r1的第一个随机数:83
// r2的第一个随机数:83
// r1的第二个随机数:87
// r2的第二个随机数:87
6.3 ThreadLocalRandom(多线程推荐)
// 获取ThreadLocalRandom实例
ThreadLocalRandom random = ThreadLocalRandom.current();
// 生成随机数
int num1 = random.nextInt(); // 整个int范围
int num2 = random.nextInt(100); // [0, 100)
int num3 = random.nextInt(10, 100); // [10, 100)
double num4 = random.nextDouble(1.0, 10.0); // [1.0, 10.0)
ThreadLocalRandom的优势:
-
每个线程有自己的随机数生成器
-
避免多线程竞争,提高性能
-
API更简洁,支持范围生成
6.4 SecureRandom(安全随机数)
// 用于加密的安全随机数
SecureRandom secureRandom = new SecureRandom();
// 生成随机字节
byte[] bytes = new byte[16];
secureRandom.nextBytes(bytes);
// 生成int随机数
int secureInt = secureRandom.nextInt();
// 设置种子
secureRandom.setSeed(System.currentTimeMillis());
SecureRandom的特点:
-
加密强度高,不可预测
-
性能比Random低
-
适用于密码学、安全令牌等场景
七、最佳实践总结
7.1 字符串处理
-
少量字符串拼接:使用
+或concat -
循环内字符串拼接:使用
StringBuilder(单线程)或StringBuffer(多线程) -
字符串比较:使用
equals()比较内容,注意null检查 -
常量字符串:使用字面量方式创建
7.2 日期时间处理
-
新项目:使用Java 8的
java.time包 -
旧项目维护:注意
SimpleDateFormat的线程安全问题 -
时间戳:使用
System.currentTimeMillis()获取毫秒时间戳
7.3 数组操作
-
数组转字符串:使用
Arrays.toString() -
数组排序:使用
Arrays.sort() -
大数组排序:考虑使用
Arrays.parallelSort() -
数组复制:使用
Arrays.copyOf()或System.arraycopy()
7.4 随机数生成
-
一般用途:使用
Random类 -
多线程环境:使用
ThreadLocalRandom -
安全相关:使用
SecureRandom -
指定范围:使用
nextInt(max-min)+min公式
7.5 性能优化建议
-
避免在循环中创建对象
-
合理使用包装类的缓存机制
-
字符串操作使用
StringBuilder -
使用
Arrays工具类替代手动操作 -
日期格式化使用线程安全的方式
通过本文的详细解析,你应该对Java常用类有了全面的了解。这些类是Java编程的基础,掌握它们的特性和使用场景,能够帮助你编写出更高效、更健壮的代码。在实际开发中,根据具体需求选择合适的类和方法,是成为Java高手的重要一步。
1694



被折叠的 条评论
为什么被折叠?



