Java 使用技巧与最佳实践
一、Java 8+ 新特性使用技巧
1. Lambda 表达式与函数式接口
java
// 使用Lambda表达式简化代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 传统方式
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
// Lambda方式
Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
// 方法引用更简洁
Collections.sort(names, String::compareTo);
2. Stream API 高效数据处理
java
// 基本流操作
List<User> users = Arrays.asList(
new User(1, "Alice", 25),
new User(2, "Bob", 30),
new User(3, "Charlie", 35)
);
// 过滤、映射、收集
List<String> names = users.stream()
.filter(user -> user.getAge() > 28)
.map(User::getName)
.collect(Collectors.toList());
// 并行流处理大量数据
List<UserDTO> userDTOs = users.parallelStream()
.map(this::convertToDTO)
.collect(Collectors.toList());
// 分组和汇总
Map<Integer, Long> ageCount = users.stream()
.collect(Collectors.groupingBy(User::getAge, Collectors.counting()));
3. Optional 优雅处理空值
java
// 避免空指针异常
Optional<User> userOptional = userRepository.findById(userId);
// ifPresent 处理存在的值
userOptional.ifPresent(user -> System.out.println("找到用户: " + user.getName()));
// orElse 提供默认值
User user = userOptional.orElse(new User(0, "Unknown", 0));
// orElseGet 延迟计算默认值
User user = userOptional.orElseGet(() -> new User(0, "Unknown", 0));
// orElseThrow 抛出异常
User user = userOptional.orElseThrow(() -> new UserNotFoundException("用户不存在"));
// map/flatMap 链式调用
String userName = userOptional
.map(User::getName)
.orElse("Unknown");
4. 日期时间API (Java 8)
java
// 创建日期时间
LocalDate today = LocalDate.now();
LocalTime now = LocalTime.now();
LocalDateTime dateTime = LocalDateTime.now();
// 日期操作
LocalDate tomorrow = today.plusDays(1);
LocalDate nextMonth = today.plusMonths(1);
boolean isBefore = today.isBefore(LocalDate.of(2024, 12, 31));
// 格式化和解析
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = dateTime.format(formatter);
LocalDateTime parsed = LocalDateTime.parse("2024-01-01 12:00:00", formatter);
// 计算日期差异
long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);
二、编码规范与最佳实践
1. 命名规范
- 类名 :使用大驼峰命名法,如
UserService
- 方法名 :使用小驼峰命名法,如
getUserById()
- 变量名 :使用小驼峰命名法,如
userName
- 常量名 :使用全大写加下划线,如
MAX_CONNECTIONS
- 包名 :使用小写字母,如
com.example.service
2. 代码格式规范
java
// 缩进:使用2个空格(Google规范)或4个空格
public class Example {
// 2个空格缩进
void method() {
if (condition) {
// 继续缩进
}
}
}
// 行宽限制:80或100个字符
// 长行自动换行,在更高语法级别断开
public void longMethod(ParamType param1, ParamType param2,
ParamType param3, ParamType param4) {
// 方法体
}
// 空块简洁表示
void doNothing() {}
// 但在多块语句中,即使为空也要换行
if (condition) {
// 空块
}
else {
// 空块也要换行
}
3. 注释规范
java
/**
* 类级文档注释
* 描述类的功能、用途
* @since 1.0.0
*/
public class UserService {
/**
* 方法级文档注释
* @param userId 用户ID
* @return 用户对象
* @throws UserNotFoundException 用户不存在时抛出
*/
public User getUserById(Long userId) throws UserNotFoundException {
// 方法实现
}
// 行内注释,说明复杂逻辑
public void complexMethod() {
// 这是一个特殊处理,因为...
specialHandling();
}
}
三、性能优化技巧
1. 算法与数据结构选择
java
// 集合选择
// 频繁随机访问用ArrayList
List<String> arrayList = new ArrayList<>(); // O(1)访问,O(n)插入删除
// 频繁插入删除用LinkedList
List<String> linkedList = new LinkedList<>(); // O(1)插入删除,O(n)访问
// 频繁查找用HashSet/HashMap
Set<String> hashSet = new HashSet<>(); // O(1)查找
Map<String, User> userMap = new HashMap<>(); // O(1)查找
// 需要排序用TreeSet/TreeMap
Set<String> sortedSet = new TreeSet<>(); // 自动排序
Map<String, User> sortedMap = new TreeMap<>(); // 按键排序
// 并发环境用ConcurrentHashMap
ConcurrentMap<String, User> concurrentMap = new ConcurrentHashMap<>();
2. 内存优化
java
// 复用对象
StringBuilder sb = new StringBuilder(); // 比String拼接更高效
for (int i = 0; i < 1000; i++) {
sb.append(i); // 避免创建大量临时String对象
}
String result = sb.toString();
// 合理设置初始容量
List<User> users = new ArrayList<>(100); // 已知大小,避免多次扩容
Map<String, Object> map = new HashMap<>(16, 0.75f); // 合理的初始容量和负载因子
// 及时释放资源
public void readFile(String path) {
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
// 使用reader
} catch (IOException e) {
// 异常处理
}
// try-with-resources自动关闭资源
}
3. 并发优化
java
// 使用并发集合
List<String> threadSafeList = Collections.synchronizedList(new ArrayList<>());
List<String> copyOnWriteList = new CopyOnWriteArrayList<>(); // 读多写少场景
// 线程池使用
ExecutorService executor = Executors.newFixedThreadPool(10);
// 更好的做法:使用ThreadPoolExecutor自定义配置
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
5, // 核心线程数
20, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(100), // 工作队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 减少锁竞争,缩小同步范围
public synchronized void badMethod() { // 方法级同步,范围太大
// 很多不涉及共享资源的代码
synchronized (lockObject) { // 更好的做法:只同步必要的代码块
// 访问共享资源的代码
}
}
4. JVM 调优参数
bash
# 堆内存设置
java -Xms2g -Xmx2g -jar application.jar
# GC策略选择
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar application.jar
# 新生代/老年代比例
java -XX:NewRatio=2 -jar application.jar
# 元空间设置
java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -jar application.jar
# GC日志
java -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar application.jar
四、代码质量提升技巧
1. 异常处理最佳实践
java
// 具体异常优先于通用异常
public void processFile(String path) {
try {
// 文件操作
} catch (FileNotFoundException e) { // 具体异常
logger.error("文件未找到: {}", path, e);
throw new BusinessException("无法读取指定文件", e);
} catch (IOException e) { // 更通用的异常
logger.error("IO错误处理文件: {}", path, e);
throw new BusinessException("处理文件时发生错误", e);
}
}
// 不要在循环中捕获异常
public void processItems(List<Item> items) {
try {
for (Item item : items) {
processItem(item); // 循环外捕获异常
}
} catch (Exception e) {
logger.error("处理项目时出错", e);
}
}
// 资源关闭使用try-with-resources
public void useResources() {
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
// 使用资源
} catch (SQLException e) {
logger.error("数据库操作错误", e);
}
}
2. 防御性编程
java
// 参数校验
public User getUserById(Long userId) {
if (userId == null || userId <= 0) {
throw new IllegalArgumentException("无效的用户ID");
}
return userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException("用户不存在: " + userId));
}
// 返回空集合而非null
public List<User> getUsersByName(String name) {
if (name == null) {
return Collections.emptyList(); // 避免返回null
}
List<User> users = userRepository.findByName(name);
return users != null ? users : Collections.emptyList();
}
// 使用断言进行内部校验
public void criticalOperation(int value) {
assert value > 0 : "值必须为正数";
// 操作逻辑
}
3. 代码重构技巧
java
// 提取方法减少重复
public void processOrder(Order order) {
validateOrder(order); // 提取验证逻辑
calculateTotal(order); // 提取计算逻辑
saveOrder(order); // 提取保存逻辑
}
// 拆分复杂方法
// 重构前
public void complexMethod() {
// 100行复杂逻辑
}
// 重构后
public void refactoredMethod() {
stepOne();
stepTwo();
stepThree();
}
private void stepOne() { /* 第一步逻辑 */ }
private void stepTwo() { /* 第二步逻辑 */ }
private void stepThree() { /* 第三步逻辑 */ }
// 使用策略模式替代条件语句
interface PaymentStrategy {
void pay(double amount);
}
class CreditCardPayment implements PaymentStrategy { /* 实现 */ }
class PayPalPayment implements PaymentStrategy { /* 实现 */ }
// 客户端代码
public void processPayment(double amount, PaymentStrategy strategy) {
strategy.pay(amount); // 无需条件语句
}
五、Java 高级特性使用
1. 反射与动态代理
java
// 使用反射创建对象
public <T> T createInstance(Class<T> clazz) {
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("创建对象失败", e);
}
}
// 动态代理实现AOP
public <T> T createProxy(T target, InvocationHandler handler) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
}
// 使用示例
UserService proxy = createProxy(
realUserService,
(proxy, method, args) -> {
logger.info("调用方法: {}", method.getName());
Object result = method.invoke(realUserService, args);
logger.info("方法调用完成");
return result;
}
);
2. 泛型高级用法
java
// 泛型方法
public <T, R> List<R> transform(List<T> input, Function<T, R> transformer) {
return input.stream()
.map(transformer)
.collect(Collectors.toList());
}
// 泛型边界
public <T extends Number & Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
// 通配符使用
public void printList(List<?> list) { // 无界通配符
list.forEach(System.out::println);
}
public void addNumbers(List<? super Integer> list) { // 下界通配符
list.add(1);
list.add(2);
}
public double sum(List<? extends Number> list) { // 上界通配符
return list.stream()
.mapToDouble(Number::doubleValue)
.sum();
}
3. 模块化系统 (Java 9+)
java
// module-info.java
module com.example.app {
// 导出包
exports com.example.app.api;
exports com.example.app.model to com.example.client;
// requires 其他模块
requires java.sql;
requires spring.context;
// 使用服务
uses com.example.app.spi.PaymentProvider;
// 提供服务实现
provides com.example.app.spi.PaymentProvider
with com.example.app.impl.CreditCardPaymentProvider;
}
六、工具类和库的高效使用
1. 常用工具类
java
// Apache Commons Lang
StringUtils.isBlank(text); // 比String.isEmpty()更安全
StringUtils.join(elements, ","); // 安全的字符串拼接
// Google Guava
List<String> list = Lists.newArrayList();
Map<String, Integer> map = Maps.newHashMap();
Preconditions.checkNotNull(obj, "对象不能为空");
// Lombok 减少样板代码
@Data // 生成getter/setter/equals/hashCode/toString
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private int age;
}
// StreamEx 增强的Stream API
List<User> users = StreamEx.of(list)
.filter(User::isActive)
.sortedBy(User::getAge)
.toList();
2. 测试框架使用
java
// JUnit 5
@Test
void testUserService() {
User user = userService.getUserById(1L);
assertNotNull(user);
assertEquals("Alice", user.getName());
}
// Mockito 模拟对象
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void testFindUser() {
// 配置模拟行为
when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, "Alice", 25)));
// 测试
User user = userService.getUserById(1L);
// 验证
assertEquals("Alice", user.getName());
verify(userRepository).findById(1L);
}
七、常见问题解决方案
1. 空指针异常处理
java
// 使用Optional避免NPE
public String getUserName(User user) {
return Optional.ofNullable(user)
.map(User::getName)
.orElse("Unknown");
}
// 使用Objects.requireNonNull
public void processUser(User user) {
Objects.requireNonNull(user, "User cannot be null");
// 处理user
}
// 链式调用中的空检查
public String getDepartmentName(User user) {
if (user == null || user.getDepartment() == null) {
return "Unknown Department";
}
return user.getDepartment().getName();
}
2. 内存泄漏检测与避免
java
// 避免静态集合持有对象引用
private static final List<User> cache = new ArrayList<>(); // 可能导致内存泄漏
// 改用WeakHashMap或定期清理
private static final Map<Long, WeakReference<User>> cache = new WeakHashMap<>();
// 关闭资源避免泄漏
public void useResources() {
Connection conn = null;
Statement stmt = null;
try {
conn = dataSource.getConnection();
stmt = conn.createStatement();
// 使用资源
} catch (SQLException e) {
logger.error("数据库错误", e);
} finally {
// 确保关闭所有资源
if (stmt != null) try { stmt.close(); } catch (SQLException e) {}
if (conn != null) try { conn.close(); } catch (SQLException e) {}
}
// 更好的方式是使用try-with-resources
}
// 监听器移除
public void registerListener() {
listener = event -> { /* 处理 */ };
eventSource.addListener(listener);
}
public void cleanup() {
// 移除监听器避免内存泄漏
eventSource.removeListener(listener);
}
3. 线程安全问题
java
// 使用线程安全集合
private final List<String> threadSafeList = Collections.synchronizedList(new ArrayList<>());
private final Set<String> concurrentSet = ConcurrentHashMap.newKeySet();
// 原子操作
private final AtomicInteger counter = new AtomicInteger(0);
public int increment() {
return counter.incrementAndGet(); // 线程安全的自增
}
// 避免使用StringBuilder在多线程环境
public String buildString() {
StringBuilder sb = new StringBuilder(); // 单线程安全
// 构建字符串
return sb.toString();
}
public String buildStringConcurrent() {
StringBuffer sb = new StringBuffer(); // 线程安全,但性能较差
// 构建字符串
return sb.toString();
// 更好的方式是使用StringBuilder并同步
StringBuilder sb = new StringBuilder();
synchronized (lock) {
// 构建字符串
}
return sb.toString();
}
八、持续学习与技能提升
- 关注Java版本更新:Java 11、17、21等长期支持版本的新特性
- 学习Java性能调优工具:JFR、JMC、VisualVM等
- 深入理解JVM:内存模型、垃圾回收、类加载机制
- 掌握设计模式:在Java中的应用和最佳实践
- 学习Java并发编程:CompletableFuture、Reactive Programming
- 关注开源项目:Spring、Apache等主流Java项目的源码和设计理念
通过掌握这些Java使用技巧和最佳实践,可以编写出更高效、更健壮、更易维护的Java应用程序,提升开发效率和代码质量。