1. 项目概述
最近在整理Java基础知识点时,发现很多常用API虽然天天在用,但对其底层实现和最佳实践却了解不够深入。特别是String、集合框架和日期时间这些高频使用的API,很多细节值得重新梳理。这次我们就来重点探讨Java基础中那些"熟悉又陌生"的核心API。
2. 字符串操作精要
2.1 String的不可变性本质
String的不可变性是Java设计中一个精妙的特性。每次看似"修改"字符串的操作,实际上都是创建了新对象。比如:
java复制String str = "hello";
str += " world"; // 实际上新建了String对象
这种设计带来三个关键优势:
- 线程安全:无需额外同步
- 缓存哈希值:提升性能
- 字符串常量池优化:减少内存占用
注意:在循环中拼接字符串务必使用StringBuilder,否则会产生大量临时对象
2.2 StringBuilder与StringBuffer对比
两者都继承自AbstractStringBuilder,主要区别在于:
- StringBuilder:非线程安全,性能更高(推荐)
- StringBuffer:线程安全,方法带synchronized
实测在单线程环境下,StringBuilder的append操作比StringBuffer快约15-20%。典型使用场景:
java复制// 正确的高效拼接方式
StringBuilder sb = new StringBuilder(128); // 预分配容量
for(int i=0; i<100; i++){
sb.append(i);
}
String result = sb.toString();
3. 集合框架深度解析
3.1 ArrayList扩容机制
ArrayList的自动扩容是个经典面试点。默认初始容量10,扩容公式:
java复制int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍
扩容过程涉及数组拷贝,非常耗时。优化建议:
- 预知大小时指定初始容量
- 使用Arrays.asList转换固定集合
3.2 HashMap实现原理
JDK8的HashMap引入了红黑树优化:
- 链表长度>8时转红黑树
- 红黑树节点<6时转回链表
哈希冲突处理流程:
- 计算key的hashCode()
- (n-1)&hash确定桶位置
- 遍历链表/红黑树查找
警示:自定义对象作为key时,必须正确重写hashCode()和equals()
4. 日期时间API演进
4.1 Date与Calendar的缺陷
传统API的主要问题:
- Date的月份从0开始
- Calendar修改会改变原对象
- 非线程安全
- 时区处理复杂
4.2 Java8时间API优势
LocalDateTime等新特性:
java复制// 创建指定日期
LocalDate date = LocalDate.of(2023, Month.JUNE, 15);
// 日期计算
LocalDate nextWeek = date.plusDays(7);
// 格式化输出
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String text = date.format(formatter);
关键改进:
- 不可变对象(线程安全)
- 链式调用
- 明确区分时间类型(Instant/LocalDate/ZonedDateTime)
5. 异常处理最佳实践
5.1 异常分类体系
Java异常分类:
code复制Throwable
├── Error(系统级错误)
└── Exception
├── RuntimeException(未检异常)
└── 其他Exception(已检异常)
处理原则:
- 不要捕获Error
- 未检异常应预防而非捕获
- 已检异常必须处理
5.2 异常处理模式
推荐做法:
java复制try {
// 业务代码
} catch(SpecificException e) {
log.error("上下文信息", e);
throw new BusinessException("友好提示");
} finally {
// 资源清理
}
反模式警示:
- 捕获Exception基类
- 吞掉异常(空catch块)
- finally块中return
6. IO流操作要点
6.1 字节流与字符流选择
选择依据:
- 文本文件:使用字符流(Reader/Writer)
- 二进制文件:使用字节流(InputStream/OutputStream)
缓冲流使用示例:
java复制// 高效读取文本文件
try(BufferedReader reader = new BufferedReader(new FileReader("file.txt"))){
String line;
while((line = reader.readLine()) != null){
// 处理每行内容
}
}
6.2 try-with-resources优化
相比传统try-catch-finally的优势:
- 自动关闭资源(实现AutoCloseable)
- 代码更简洁
- 避免资源泄漏
JDK7+推荐写法:
java复制try(InputStream in = new FileInputStream("data.bin");
OutputStream out = new FileOutputStream("copy.bin")){
byte[] buffer = new byte[8192];
int bytesRead;
while((bytesRead = in.read(buffer)) != -1){
out.write(buffer, 0, bytesRead);
}
}
7. 反射机制核心应用
7.1 反射性能考量
反射调用比直接调用慢约50-100倍。优化方案:
- 缓存Class对象
- 缓存Method/Field实例
- 必要时使用MethodHandle
性能对比测试:
java复制// 直接调用
method.invoke(obj); // 约100ns
// 反射调用
method.setAccessible(true);
method.invoke(obj); // 约5000ns
7.2 动态代理实现
JDK动态代理示例:
java复制interface Service {
void serve();
}
class RealService implements Service {
public void serve() {
System.out.println("实际服务");
}
}
class LogHandler implements InvocationHandler {
private Object target;
public LogHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用前日志");
Object result = method.invoke(target, args);
System.out.println("调用后日志");
return result;
}
}
// 使用方式
Service proxy = (Service)Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class[]{Service.class},
new LogHandler(new RealService())
);
8. 并发工具类实战
8.1 CountDownLatch应用
典型场景:多线程任务同步
java复制CountDownLatch latch = new CountDownLatch(3);
// 工作线程
new Thread(() -> {
doWork();
latch.countDown();
}).start();
// 主线程等待
latch.await(); // 阻塞直到计数器归零
System.out.println("所有任务完成");
8.2 ThreadLocal原理
每个线程独立存储的机制:
java复制public class ThreadLocal<T> {
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
map.set(this, value);
}
// 其他方法...
}
使用注意事项:
- 必须及时remove()避免内存泄漏
- 不要用static修饰(除非确实需要全局共享)
- 线程池环境要特别小心
9. 注解与元编程
9.1 自定义注解实现
定义注解示例:
java复制@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Benchmark {
String value() default "";
int iterations() default 1;
}
处理注解的代码:
java复制Method[] methods = clazz.getDeclaredMethods();
for(Method method : methods) {
if(method.isAnnotationPresent(Benchmark.class)) {
Benchmark bench = method.getAnnotation(Benchmark.class);
long start = System.nanoTime();
for(int i=0; i<bench.iterations(); i++) {
method.invoke(target);
}
long duration = System.nanoTime() - start;
System.out.printf("%s耗时:%.2fms%n",
bench.value(), duration/1_000_000.0);
}
}
9.2 元注解应用
常用元注解:
- @Target:限定注解使用位置
- @Retention:控制注解生命周期
- @Documented:包含在Javadoc中
- @Inherited:允许子类继承
10. 函数式编程实践
10.1 Lambda表达式优化
从匿名类到Lambda的演进:
java复制// 旧方式
Collections.sort(list, new Comparator<String>() {
public int compare(String a, String b) {
return a.length() - b.length();
}
});
// Lambda方式
Collections.sort(list, (a, b) -> a.length() - b.length());
// 方法引用终极版
Collections.sort(list, Comparator.comparingInt(String::length));
10.2 Stream API实战
常用操作链:
java复制List<String> result = data.stream()
.filter(s -> s.startsWith("A"))
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
性能提示:
- 大数据集使用parallelStream
- 避免在stream中修改外部状态
- 简单操作优先使用循环
11. 枚举高级用法
11.1 枚举实现接口
灵活的设计模式:
java复制public interface Operation {
double apply(double x, double y);
}
public enum BasicOperation implements Operation {
PLUS("+") { public double apply(double x, double y) { return x + y; } },
MINUS("-") { public double apply(double x, double y) { return x - y; } };
private final String symbol;
BasicOperation(String symbol) {
this.symbol = symbol;
}
}
11.2 枚举单例模式
线程安全的单例实现:
java复制public enum Singleton {
INSTANCE;
public void doSomething() {
// 业务方法
}
}
// 使用方式
Singleton.INSTANCE.doSomething();
12. 泛型编程技巧
12.1 类型擦除应对
运行时类型信息获取技巧:
java复制public class GenericType<T> {
private final Class<T> type;
public GenericType(Class<T> type) {
this.type = type;
}
public boolean isInstance(Object obj) {
return type.isInstance(obj);
}
}
12.2 通配符应用
PECS原则(Producer Extends, Consumer Super):
java复制// 生产者使用extends
void processList(List<? extends Number> list) {
for(Number n : list) { /*...*/ }
}
// 消费者使用super
void addNumbers(List<? super Integer> list) {
list.add(42);
}
13. 正则表达式高效用法
13.1 预编译模式优化
避免重复编译的开销:
java复制private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);
boolean isValidEmail(String email) {
return EMAIL_PATTERN.matcher(email).matches();
}
13.2 分组与捕获技巧
提取匹配内容:
java复制Pattern p = Pattern.compile("(\\d{3})-(\\d{4})");
Matcher m = p.matcher("123-4567");
if(m.matches()) {
String area = m.group(1); // "123"
String number = m.group(2); // "4567"
}
14. 对象拷贝机制
14.1 浅拷贝与深拷贝
实现深拷贝的几种方式:
- 手动逐字段复制
- 序列化/反序列化
- 使用克隆工具库(如Apache Commons)
序列化实现示例:
java复制public static <T> T deepCopy(T obj) throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (T) ois.readObject();
}
14.2 Cloneable接口问题
Object.clone()的缺陷:
- 浅拷贝默认实现
- 需要处理CloneNotSupportedException
- 破坏封装性(需调用super.clone())
更优的替代方案:
- 拷贝构造器
- 静态工厂方法
15. 资源管理进阶
15.1 关闭钩子应用
确保资源释放的保险措施:
java复制Runtime.getRuntime().addShutdownHook(new Thread(() -> {
// 清理工作
closeResources();
System.out.println("资源已释放");
}));
15.2 文件操作陷阱
常见文件操作错误:
- 未检查文件是否存在
- 未处理文件权限
- 未考虑符号链接
- 未关闭文件流
健壮的文件拷贝:
java复制Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");
try(InputStream in = Files.newInputStream(source);
OutputStream out = Files.newOutputStream(target)) {
byte[] buffer = new byte[8192];
int bytesRead;
while((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
} catch(IOException e) {
throw new UncheckedIOException("文件操作失败", e);
}
16. 编码与字符集处理
16.1 乱码问题排查
常见编码问题场景:
- 文件读写编码不一致
- HTTP传输未指定编码
- 数据库连接字符集不匹配
正确设置编码的方式:
java复制// 读取UTF-8文件
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("data.txt"),
StandardCharsets.UTF_8
)
);
// 写入GBK文件
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("output.txt"),
"GBK"
)
);
16.2 字符集转换工具
常用转换方法:
java复制String original = "中文";
byte[] utf8Bytes = original.getBytes(StandardCharsets.UTF_8);
String recovered = new String(utf8Bytes, StandardCharsets.UTF_8);
17. 数学运算精度控制
17.1 BigDecimal使用要点
浮点数精确计算方案:
java复制// 错误方式:使用double构造
BigDecimal d1 = new BigDecimal(0.1); // 精度已丢失
// 正确方式:使用String构造
BigDecimal d2 = new BigDecimal("0.1");
// 运算设置
BigDecimal result = d2.add(new BigDecimal("0.2"))
.setScale(2, RoundingMode.HALF_UP);
17.2 数值溢出处理
安全运算方法:
java复制// 加法溢出检查
public static int addExact(int x, int y) {
int r = x + y;
if (((x ^ r) & (y ^ r)) < 0) {
throw new ArithmeticException("整数溢出");
}
return r;
}
18. 随机数生成策略
18.1 安全随机数生成
密码学安全随机数:
java复制SecureRandom random = SecureRandom.getInstanceStrong();
byte[] key = new byte[16];
random.nextBytes(key); // 适合生成密钥
18.2 随机数性能优化
ThreadLocalRandom用法:
java复制// 多线程环境下的高效随机数
int randomNum = ThreadLocalRandom.current().nextInt(1, 100);
19. 系统属性与环境变量
19.1 运行时信息获取
系统属性读取:
java复制String javaVersion = System.getProperty("java.version");
String userHome = System.getProperty("user.home");
19.2 环境变量访问
跨平台环境变量处理:
java复制Map<String, String> env = System.getenv();
String path = env.get("PATH"); // Linux/Windows兼容
20. 对象生命周期管理
20.1 对象池模式实现
连接池简化示例:
java复制public class ObjectPool<T> {
private Queue<T> pool = new ConcurrentLinkedQueue<>();
private Supplier<T> supplier;
public ObjectPool(int size, Supplier<T> supplier) {
this.supplier = supplier;
for(int i=0; i<size; i++) {
pool.add(supplier.get());
}
}
public T borrow() {
T obj = pool.poll();
return obj != null ? obj : supplier.get();
}
public void release(T obj) {
pool.offer(obj);
}
}
20.2 软引用与弱引用
内存敏感缓存实现:
java复制Map<String, SoftReference<BigData>> cache = new HashMap<>();
public BigData getData(String key) {
SoftReference<BigData> ref = cache.get(key);
BigData data = ref != null ? ref.get() : null;
if(data == null) {
data = loadFromDB(key);
cache.put(key, new SoftReference<>(data));
}
return data;
}