当前位置:人工智能 > 编写 Java 代码时应避免的 6 个陷阱

编写 Java 代码时应避免的 6 个陷阱

  • 发布:2023-10-09 21:23

通常情况下,我们都希望自己的代码高效、兼容,但现实中,代码往往包含一些隐藏的陷阱,只有出现异常时我们才会去解决。 这篇文章是一篇比较短的文章,列出了开发者在编写java程序时常犯的错误,以避免出现线上问题。

1。广泛使用Enum.values

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

如上所述,我们可以通过引入私有静态最终变量缓存来轻松解决这个问题。

2。将Optional作为方法参数传递

以下代码

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

·完·

因公众号更改了推送规则,关注公众号首页,点击右上角“设为明星”第一时间获取博主精彩的技术资讯


往期原创热门文章推荐:

  1. 设计模式模板方法模式

  2. 一套前后端全开源的H5商城送给你

  3. 什么是好代码/坏代码?给普通人看的图例

  4. 推荐算法在商城系统中实现

  5. 使用策略模式重构 if/elseif/elseif

相关文章