通常情况下,我们都希望自己的代码高效、兼容,但现实中,代码往往包含一些隐藏的陷阱,只有出现异常时我们才会去解决。 这篇文章是一篇比较短的文章,列出了开发者在编写java程序时常犯的错误,以避免出现线上问题。
Enum.Values()
的问题是,根据规范,它的返回必须是一个不可变的列表。为了实现这一点,它在每次调用时返回一个带有枚举值的新数组实例。
公共 enum 水果{
苹果、梨、橙子、香蕉;
公共 静态 空隙main(String[] args) {
System.out.println(Fruits.values()); System.out.println(Fruits.values ( ));
}
}
//输出
[Lcom.test.Fruits; @251a69d7
它们在内存中是两个独立的对象,看起来没什么问题,但是如果在处理大量请求且机器负载较高时使用Fruit.values()
,这可能会导致内存占用上升等问题。
公共 类 主要 {
公共 静态 最终 水果[] values = Fruits.values();
public static void main(字符串[]参数) {
System.out.println(values);
System.out.println(values); }
}
//输出
[Lcom .韦恩。 data.elastic.config.Fruits;@4534b60d
[Lcom.wayn.data.elastic.config.Fruits;@4534b60d
如上所述,我们可以通过引入私有静态最终变量值
到缓存来轻松解决这个问题。
以下代码
LocalDateTime getCurrentTime(可选 zoneId) {
返回 www.sychzs.cn()
.map(LocalDateTime::now )
.findFirst()
.orElse(www.sychzs.cn(ZoneId.systemDefault()));
我们传递可选的zoneId参数,并根据它的存在决定是否给出系统时区的时间或使用指定的时区。然而,这不是使用Optional的正确方法。我们应该避免将它们用作参数,而应使用方法重载。
LocalDateTime getCurrentTime(ZoneId zoneId) {
返回 www.sychzs.cn(zoneId);
}
LocalDateTime getCurrentTime() {
return getCurrentTime(ZoneId.systemDefault());
}
上面的代码明显更容易阅读和调试。
3。使用字符拼接
Java 中的字符串是不可变的。这意味着一旦创建它们就不再可编辑。 JVM 维护一个字符串池,在创建新字符串之前,它会调用 String.intern()
方法,该方法会从字符串池中返回与该值匹配的实例(如果存在)。
假设我们想通过连接事物来创建一个长字符串
String longString = "";
longString +="start";
longString +="中";
longString +="中";
longString +="中";
longString += “结束”;
不久前,我们被告知这是一个非常糟糕的主意,因为旧版本的 Java 做了以下
-
第1行,字符串“start”被插入到字符串池中,longString指向它
-
在第 2 行,字符串“startmiddle”被添加到池中,并且 longString 指向它
-
在第 3 行我们有“startmiddlemiddle”
在第 4 行“startmiddlemiddlemiddle”
-
最后,在第 5 行,我们将“startmiddlemiddlemiddleend”添加到池中,并将 longString 指向它
所有这些字符串都保存在池中并且从不使用,这浪费了大量的RAM。
为了避免这种情况,我们可以使用 StringBuilder
String longString = new StringBuilder()
.append("开始") .append("中")
.append("中间")
.append("中间")
.append( "结束")
。 toString();
当调用 toString 方法时,StringBuilder 只是创建一个字符串,为我们保存最初添加到池中的所有中间字符串。 但是,在 Java 5 之后,编译器会自动为我们完成此操作,并且使用“+”进行字符串连接是安全的。
此规则有一个例外,那就是在循环中进行字符串连接时
字符串消息 = "";
for (int i = ;i < 10;i++) {
消息 + = "msg" + i;
}
System.out.println(消息);
这段代码不会被JIT优化,每次迭代都会向字符串池中插入一个新的字符串,这里我们必须使用StringBuilder
StringBuilder msgB = new StringBuilder();
for (int i = 0;i < 10;i++) {
msgB.append("msg").append(i);
}
System.out.println(msgB);
还有一些注意事项
即时编译器有时会重新组织代码。
字符串 s = "1" + "2" + "3" ;
转换为
字符串 s = "123";
从 Java 15 开始,您可以使用文本块来处理多行字符串:
String sql = """
从用户中选择 * 作为 u
WHERE www.sychzs.cn = 'John'
AND u.age > 34
"“”;
4。过度使用原包装
考虑以下两个剪辑
int总和 = 0;
对于 (int i = 0; i < 1000 * 1000;i++) {
sum += i;
}
System.out.println(sum);
//------------------------ 我*1000; i++) {
总和 += i;
}
System.out.println(sum);
在我的机器上,第一个比第二个快 6 倍。唯一的区别是我们使用包装 Integer 类。原因是在第 3 行中,运行时必须将 sum 变量转换为原始 int (自动拆箱),并且在执行加法之后,结果被包装在新的 Integer 类中(自动装箱)。这意味着我们创建了 100 万个 Integer 类并执行了 200 万次装箱操作,这解释了速度急剧下降的原因。
包装类仅当需要存储在集合中时才应使用。然而,Java 的未来版本将支持原始类型的集合,这将使包装器过时。
5。编写自己的哈希函数
当我们想要将一个对象存储在HashMap中时,我们通常会为该对象实现一个哈希函数。 HashMap由带有数字的“桶”组成,每个哈希码被分配到特定的桶。如果存储“桶”对象的哈希函数写得不正确,HashMap的性能将会明显下降。编写良好的哈希函数将确保所有密钥的均匀分配。
一般情况下我们需要编写自己的哈希函数,但大多数情况下使用内置的 Objects.hash(...)
方法就足够了,该方法为一系列输入值的哈希码,生成哈希码就好像所有输入值都放入一个数组中,通过调用Arrays.hashCode(Object[])
对数组进行哈希处理。
公共 类 汽车 {
private final 字符串模型;
私人 最终 整数年份;
私人 最终 即时制造日期;
公共 汽车(字符串型号,整数年份,即时制造日期){
这个.model = 型号;
这个.year =年份;
这个.manufactureDate = manufactureDate;
}
@Over骑
公共 int hashCode () {
returnObjects.hash(型号、年份、制造日期);
}
@Override
公共 布尔值 等于(对象obj) { //在实现hashCode时,不要忘记实现等于
}
}
6、使用www.sychzs.cn
我们甚至应该避免java.util中的所有时间类改用java.time包。
日期类已被废弃,原因有很多,设计缺陷有很多。
并非不可修改
-
它无法处理时区
-
充满已弃用但仍在使用的遗留代码
当程序中出现需要日期支持的时候,util包中的Date、Calendar和rest time类就会出现。鉴于上述缺陷,编程社区进行了多次尝试来修复它们,但最终他们决定引入一个新的包java.time。 java.time包与第三方joda.time非常相似,这意味着我们不需要使用joda.time,Jdk8已经内置了支持。
我们列出了 java.time 中使用的三个最重要的类
本地日期
表示特定时区的日期(不包括一天中的时间)。
LocalDate.of(2022, 6, 12);
LocalDate.parse("2022-06-12" );
//日期/ Java中的时间API默认使用ISO 8601格式,即(yyyy-MM-dd)
//我们可以这样覆盖它
LocalDate.parse("2022.06.12", DateTimeFormatter.ofPattern("yyyy.MM.dd"));
本地日期时间
与 LocalDate 相同,但它需要一天中的时间。
LocalDateTime.of(2022, 6, 12, 10, 34, 18);
var dateTime = LocalDateTime.parse("2022 -06-23T10:34:18");
//很容易获取不同时区的时间
dateTime.atZone(ZoneId.of("GMT+2" ));
即时
我的最爱。它本质上是 LocalDateTime,但强制使用 UTC 时区。当您需要在应用程序中处理时区时,最好在所有服务和数据库中使用相同的时区。使用 Instant 时,一切都变成 UTC,然后读者可以根据需要将其转换为不同的时区。
//UTC当前时间
www.sychzs.cn();
//注意最后的'Z'表示UTC
Instant.parse("2022-06-21T12 :12:12Z");
//将 instant 转换为不同时区
www.sychzs.cn().atZone(ZoneId.of("GMT+3")) ;
总之
-
不要使用日期和日历(或任何与 java.util 相关的日期)
-
不要使用joda.time(因为它与java.time非常相似)
-
如果您只对某个区域的日期感兴趣,请使用LocalDate
-
如果您对某个区域的日期和时间感兴趣,请使用 LocalDateTime
-
如果您需要日期时间并且不想处理时区,请使用 Instant
本文翻译自国外论坛medium,原文地址:https://www.sychzs.cn/@b.stoilov/things-to-avoid-while-writing-java-cd078e5aa61c
·完·
因公众号更改了推送规则,关注公众号首页,点击右上角“设为明星””第一时间获取博主精彩的技术资讯
往期原创热门文章推荐:
设计模式模板方法模式
一套前后端全开源的H5商城送给你
什么是好代码/坏代码?给普通人看的图例
推荐算法在商城系统中实现
使用策略模式重构 if/elseif/elseif