如果你也是从
public static void main(String[] args) 和
System.out.println() 开始Java生涯的,那也是Java老油条了。在日常的业务开发中,我们每天都在写着增删改查的逻辑,有时候会觉得Java有点笨重,语法有点啰嗦。
但其实,Java在不断进化。从我们熟悉的Java 8到现在的Java 25,它多了很多实用的新特性和一些不为人知的老技巧。用好它们,不仅能让代码更简洁,还能在同事面前小小地秀一下。
下面就聊几个我自己在工作中觉得特别好用的技巧,看看你用过几个。
很多人对枚举的印象还停留在定义一组常量,比如
MALE,
FEMALE。但其实,枚举远比这强大,它甚至可以拥有自己的方法和实现,非常适合用来替代一堆
if-else 或者
switch 的策略逻辑。
举个例子,假设我们有不同的会员等级,需要计算折扣后的价格:
// 利用枚举实现不同会员的折扣策略
public
enum
MemberType {
REGULAR {
@Override
public
double
applyDiscount
(
double price) {
return price *
0.98;
// 普通会员98折
}
},
VIP {
@Override
public
double
applyDiscount
(
double price) {
return price *
0.9;
// VIP会员9折
}
},
PREMIUM {
@Override
public
double
applyDiscount
(
double price) {
return price *
0.8;
// 高级会员8折
}
};
public
abstract
double
applyDiscount
(
double price);
}
// 使用起来非常清晰
public
class
PriceCalculator {
public
static
void
main
(String[] args) {
double
originalPrice
=
100.0;
double
vipPrice
= MemberType.VIP.applyDiscount(originalPrice);
System.out.println(
"VIP会员价:" + vipPrice);
// 输出:VIP会员价:90.0
}
}
这样写,每种会员的折扣逻辑都封装在自己的枚举实例里,代码清晰,扩展起来也方便。以后要加新的会员等级,只需要添加一个新的枚举实例就行,完全符合开闭原则。
自从Java 16引入了
Record 类型,我写DTO(数据传输对象)的幸福感直线上升。以前为了定义一个简单的数据载体,得手动写一堆的
getter,
setter,
equals(),
hashCode() 和
toString(),或者依赖Lombok。
现在,一行代码就够了。
// 以前的写法
// public class User {
// private final String username;
// private final String email;
// // ... 一大堆 getter, equals, hashCode, toString ...
// }
// 现在用Record
public
record
UserProfile
(String username, String email) {}
// 使用
public
class
Main {
public
static
void
main
(String[] args) {
UserProfile
user
=
new
UserProfile(
"dev_user",
"user@example.com");
System.out.println(user.username());
// 直接调用,像方法一样
System.out.println(user);
// 自带了很好的toString()实现
}
}
编译器会自动帮你生成所有必需的方法,代码瞬间清爽了很多。
在业务代码里,经常会用
Long 或者
String 来表示各种ID,比如
userId,
orderId,
productId。这样做的风险是,方法的参数很容易传混。
// 很容易写错的调用
public
void
processOrder
(Long userId, Long orderId) {
// ...
}
// 调用时可能不小心把两个ID搞反
processOrder(orderId, userId);
// 编译器不会报错,但逻辑全错了
那就可以利用
Record (或者普通类) 来给ID一层包装,增加类型安全性,让编译器在编码阶段就帮我们发现错误。
public
record
UserId
(
long value) {}
public
record
OrderId
(
long value) {}
public
class
OrderService {
public
void
processOrder
(UserId userId, OrderId orderId) {
System.out.println(
"处理用户 " + userId.value() +
" 的订单 " + orderId.value());
}
public
static
void
main
(String[] args) {
OrderService
service
=
new
OrderService();
UserId
userId
=
new
UserId(
1001L);
OrderId
orderId
=
new
OrderId(
9527L);
service.processOrder(userId, orderId);
// 正确
// service.processOrder(orderId, userId); // 这行代码会直接编译失败,安全!
}
}
这个小改动能避免很多难以排查的线上问题。
从Java 8开始,Stream API 就已经是处理集合的标配了。如果还在用
for 循环和一堆
if 来做筛选和转换,那代码读起来会很费劲。Stream API可以用一种更声明式、更流畅的方式来操作数据。
比如,我们要从一堆产品里,筛选出价格大于500的,然后取出它们的名字:
import java.util.List;
import java.util.stream.Collectors;
public
class
StreamDemo {
record
Product
(String name,
double price) {}
public
static
void
main
(String[] args) {
List<Product> products = List.of(
new
Product(
"笔记本电脑",
5999.0),
new
Product(
"鼠标",
299.0),
new
Product(
"机械键盘",
799.0)
);
// 使用Stream API
List<String> expensiveProductNames = products.stream()
.filter(p -> p.price() >
500.0)
// 筛选价格
.map(Product::name)
// 提取名称
.collect(Collectors.toList());
// 收集成列表
System.out.println(expensiveProductNames);
// 输出: [笔记本电脑, 机械键盘]
}
}
链式调用,一气呵成,代码的意图一目了然。
以前在Java代码里拼接SQL或者JSON字符串,简直是一场灾难,充满了
+ 号和
\n 转义符,可读性极差。Java 15引入的文本块(Text Blocks)彻底解决了这个问题。
看看对比:
public
class
TextBlockDemo {
public
static
void
main
(String[] args) {
// 以前的方式,又丑又容易出错
String
oldJson
=
"{\n" +
" "name
": "Alice
",\n" +
" "age
": 30\n" +
"}";
// 现在用文本块,所见即所得
String
newJson
=
"""
{
"name": "Alice",
"age": 30
}
""";
System.out.println(oldJson.equals(newJson));
// 输出: true
}
}
用三个双引号
""" 包起来,字符串的格式就能完全保留,代码干净多了。
!= null?试试
Optional空指针异常(NullPointerException)应该不少 Java 开发者都很熟了。为了避免它,我们代码里堆满了
if (obj != null) 的判断。
Optional 的出现就是为了更优雅地处理可能为空的情况。
import java.util.Optional;
public
class
UserRepository {
// 模拟从数据库查找用户
public Optional<String>
findUserNameById
(
long id) {
if (id ==
1L) {
return Optional.of(
"Alice");
}
return Optional.empty();
// 表示没找到
}
public
static
void
main
(String[] args) {
UserRepository
repo
=
new
UserRepository();
// 优雅地处理
repo.findUserNameById(
1L)
.ifPresentOrElse(
name -> System.out.println(
"找到用户:" + name),
() -> System.out.println(
"用户不存在")
);
// 获取值,如果不存在则提供一个默认值
String
userName
= repo.findUserNameById(
2L).orElse(
"默认用户");
System.out.println(
"查询ID为2的用户:" + userName);
}
}
用
Optional 不仅能让代码意图更明确(这个方法的返回值可能为空),还能通过链式调用写出更流畅的代码。
看到这里,你可能已经注意到,上面提到的不少特性都和Java版本有关。比如
Record 是Java 16的,文本块是Java 15的,
ifPresentOrElse 是Java 9的。在实际工作中,我们经常会遇到这样的情况:
老项目A还在用稳定的Java 8。
新项目B想尝试Java 17的LTS版本。
自己想学习一下最新的Java 25。
在电脑上同时管理这么多Java版本,配置环境变量,为不同项目切换JDK,挺让人破防的。每次切换环境,都要敲一堆命令,或者在IDE里改来改去,很影响开发效率。
所以,这时候就需要ServBay。
它本来是一个集成了PHP、Node.js等环境的Web开发工具,但我发现它对Java的支持也做得特别好。最让我喜欢的一点是,它可以一键安装和管理多个Java版本。我可以同时安装Java 8、11、17、21等多个版本,它们之间完全隔离,互不干扰。
而且,我还可以为不同的项目指定使用不同的Java版本。比如,可以设置项目A的运行环境就是Java 8,项目B就是Java 23。这样一来,在切换项目时,根本不用关心JDK版本的问题,ServBay都帮你自动处理好了。
对于我们Java开发者来说,这意味着可以把更多精力放在代码和技术本身,而不是被环境配置这些琐事消耗。
Java依然是一门生命力旺盛的语言。学习和使用这些技巧,可以让我们的代码质量更高,开发体验也更好。而借助像ServBay这样的工具,又能帮我们轻松搞定复杂的环境管理问题,让我们能更专注于写出优秀的代码。
你还有什么私藏的Java技巧吗?欢迎在评论区分享交流。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70048945/viewspace-3108668/ ,如需转载,请注明出处,否则将追究法律责任。