编写Java代码时应该避免的6个坑

这篇具有很好参考价值的文章主要介绍了编写Java代码时应该避免的6个坑。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

通常情况下,我们都希望我们的代码是高效和兼容的,但是实际情况下代码中常常含有一些隐藏的坑,只有等出现异常时我们才会去解决它。本文是一篇比较简短的文章,列出了开发人员在编写 Java 程序时常犯的错误,避免线上问题。

1、大量使用 Enum.values

Enum.Values() 的问题在于,按照规范它的返回必须是一个不可变的列表。为了实现这一点,它在每次调用时返回一个带有枚举值的新数组实例。

public enum Fruits {
    APPLE, PEAR, ORANGE, BANANA;

    public static void main(String[] args) {
        System.out.println(Fruits.values());
        System.out.println(Fruits.values());
    }
}
// output
[Lcom.test.Fruits;@7ad041f3
[Lcom.test.Fruits;@251a69d7

复制

它们是内存中的两个独立对象,这好像也没啥事,但是如果在处理大量请求时使用 Fruit.values() 并且机器负载很高,这可能会导致内存升高等问题。

public class Main {
    public static final Fruits[] values = Fruits.values();

    public static void main(String[] args) {
        System.out.println(values);
        System.out.println(values);
    }
}
// output
[Lcom.wayn.data.elastic.config.Fruits;@4534b60d
[Lcom.wayn.data.elastic.config.Fruits;@4534b60d

复制

如上我们可以通过引入私有静态最终变量 values 来缓存它们来轻松解决此问题。

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

如下代码

LocalDateTime getCurrentTime(Optional<ZoneId> zoneId) {
    return zoneId.stream()
        .map(LocalDateTime::now)
        .findFirst()
        .orElse(LocalDateTime.now(ZoneId.systemDefault()));
}

复制

我们传递可选的 zoneId 参数,并根据它的存在来决定是在系统时区中给出时间还是使用指定的时区。但是,这不是正确使用 Optional 的方式。我们应该避免将它们用作参数,而是使用方法重载。

LocalDateTime getCurrentTime(ZoneId zoneId) {
  return LocalDateTime.now(zoneId);
}

LocalDateTime getCurrentTime() {
  return getCurrentTime(ZoneId.systemDefault());
}

复制

如上代码明显更易于阅读和调试。

3、使用字符拼接

Java 中的字符串是不可变的。这意味着一旦创建它们就不再可编辑。 JVM 维护一个字符串池,在创建一个新字符串之前,它调用 String.intern() 方法,该方法从字符串池中返回一个与值匹配的实例(如果存在)。

假设我们想通过连接东西来创建一个长字符串

String longString = "";
longString +="start";
longString +="middle";
longString +="middle";
longString +="middle";
longString +="end";

复制

不久前,我们被告知这是一个非常糟糕的主意,因为Java的旧版本执行以下操作

  • 在第 1 行中,字符串 "start" 被插入到字符串池中,longString 指向它
  • 在第 2 行中,字符串 "startmiddle" 被添加到池中,longString 指向它
  • 在第 3 行,我们有 "startmiddlemiddle"
  • 在第 4 行 "startmiddlemiddlemiddle"
  • 最后,在第 5 行,我们将 "startmiddlemiddlemiddleend" 添加到池中并将 longString 指向它

所有这些字符串都保留在池中并且从不使用,这会浪费大量 RAM。

为了避免这种情况,我们可以使用 StringBuilder

String longString = new StringBuilder()
  .append("start")
  .append("middle")
  .append("middle")
  .append("middle")
  .append("end")
  .toString();

复制

调用 toString 方法时,StringBuilder 仅创建一个字符串,从而为我们保存了最初添加到池中的所有中间字符串。但是,在 Java 5 之后,编译器会自动为我们完成此操作,并且可以安全地使用带有 "+" 的字符串连接。

此规则有一个例外,那就是在循环中进行字符串连接时

String message = "";
for (int i = 0; i < 10; i++) {
  message += "msg" + i;
}

System.out.println(message);

复制

这段代码不会被 JIT 优化,每次迭代都会将新的字符串插入到字符串池中,这里我们必须使用 StringBuilder

StringBuilder msgB = new StringBuilder();
for (int i = 0; i < 10; i++) {
  msgB.append("msg").append(i);
}

System.out.println(msgB);

复制

这里还有几件事要注意

即时编译器有时会重新组织代码。

String s = "1" + "2" + "3";

复制

转换成

String s = "123";

复制

从 Java 15 开始,可以使用文本块处理多行字符串:

String sql = """
  SELECT * FROM users as u
  WHERE u.name = 'John'
  AND u.age > 34
""";

复制

4、过度使用原始包装器

考虑以下两个片段

int sum = 0;
for (int i = 0; i < 1000 * 1000; i++) {
  sum += i;
}
System.out.println(sum);

// ----------------------

Integer sum = 0;
for (int i = 0; i < 1000 * 1000; i++) {
  sum += i;
}
System.out.println(sum);

复制

在我的机器上,第一个比第二个快 6 倍。唯一的区别是我们使用包装器 Integer 类。这样做的原因是,在第 3 行中,运行时必须将 sum 变量转换为原始 int(自动拆箱),并且在执行添加后,结果将包装在一个新的 Integer 类中(自动装箱)。这意味着我们创建了 100 万个 Integer 类并执行了 200 万个装箱操作,这解释了速度急剧下降的原因。

仅当需要将包装类存储在集合中时才应使用包装类。但是,未来的 Java 版本将支持原始类型的集合,这将使包装器过时。

5、自己编写哈希函数

当我们想将对象存储在 HashMap 中时,通常会实现对象的哈希函数。该 HashMap 由带有数字的 "桶" 组成,每个哈希码都分配给一个特定的桶。如果存入 "桶" 对象的哈希函数没有正确编写,HashMap 的性能将显着降低。一个写得很好的散列函数将确保所有键的平均分配。

在一般情况下我们需要自己编写哈希函数,但在大多数情况下,使用内置的 Objects.hash(...) 方法就行,该方法为一系列输入值生成哈希代码,生成散列代码的方式就像将所有输入值都放入一个数组中一样,并且通过调用 Arrays.hashCode(Object[]) 对该数组进行散列。

public class Car {
    private final String model;
    private final Integer year;
    private final Instant manufactureDate;

    public Car(String model, Integer year, Instant manufactureDate) {
        this.model = model;
        this.year = year;
        this.manufactureDate = manufactureDate;
    }

    @Override
    public int hashCode() {
        return Objects.hash(model, year, manufactureDate);
    }

    @Override
    public boolean equals(Object obj) {
        // 在实现 hashCode 时,不要忘记实现 equals
    }
}

复制

6、使用 java.util.Date

我们甚至应该避免 java.util 中的所有时间类改用 java.time 包。

Date 类已被弃用,原因有很多,它有很多设计缺陷。

  • 它不是无法被修改的
  • 它无法处理时区
  • 充满已弃用但仍在使用的遗留代码

当程序中出现对日期支持的需求时,util 包中的 Date、Calendar 和 rest time 类就出现了。鉴于如上缺陷,程序界有几次修复它们的尝试,但最后他们决定引入一个新的包 java.time。 java.time 包与第三方的 joda.time 非常相似,这意味着我们不需要在使用 joda.time,Jdk8 已经有了内置支持。

我们列出 java.time 中使用的三个最重要的类

LocalDate

表示特定时区的日期(不包括一天中的时间)。

LocalDate.of(2022, 6, 12);
LocalDate.parse("2022-06-12");

// The Date/Time API in Java works with the ISO 8601 format by default, which is (yyyy-MM-dd)
// We can overwrite it like this
LocalDate.parse("2022.06.12", DateTimeFormatter.ofPattern("yyyy.MM.dd"));

复制

LocalDateTime

与 LocalDate 相同,但它有一天中的时间。

LocalDateTime.of(2022, 6, 12, 10, 34, 18);
var dateTime = LocalDateTime.parse("2022-06-23T10:34:18");

// it's easy to get the time in a different zone
dateTime.atZone(ZoneId.of("GMT+2"));

复制

Instant

我最喜欢的。它本质上是 LocalDateTime,但强制使用 UTC 时区。在应用程序中需要处理时区时,最好在所有服务和数据库中使用同一个时区。当使用 Instant 时,一切都变成了 UTC,然后读者可以根据需要将其转换为不同的时区。

// Current time in UTC
Instant.now();

// Note the 'Z' at the end it means UTC
Instant.parse("2022-06-21T12:12:12Z");

// Convert instant to a different time zone
Instant.now().atZone(ZoneId.of("GMT+3"));

复制

简单来说文章来源地址https://www.toymoban.com/news/detail-471968.html

  • 不要使用日期和日历(或任何与 java.util 相关的日期)
  • 不要使用 joda.time(因为它与 java.time 非常相似)
  • 如果只对某个区域的日期感兴趣,请使用 LocalDate
  • 如果对某个区域的日期和时间感兴趣,请使用 LocalDateTime
  • 如果需要日期时间并且不想处理时区,请使用 Instant

到了这里,关于编写Java代码时应该避免的6个坑的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 在uni-app中,如果data中的对象属性改变了,但是页面没有相应更新的情况,通常有以下几点需要注意:

    1. 使用this.$set更新对象属性直接修改对象属性是无法触发页面更新的,需要使用this.$set方法: 2. 确保数据层级不太深如果对象层级过深,改变内层属性也可能无法触发更新。建议关键数据不要超过2层。 3. 使用深度 watcher可以在watch中用深度watcher的方式监听整个对象的变化: 4. 使用

    2024年02月16日
    浏览(44)
  • Java避免死锁的几个常见方法(有测试代码和分析过程)

    目录 Java避免死锁的几个常见方法 死锁产生的条件 上死锁代码 然后 :jstack 14320 jstack.text Java避免死锁的几个常见方法 Java避免死锁的几个常见方法 避免一个线程同时获取多个锁。 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。 尝试使用定时锁,使

    2023年04月16日
    浏览(77)
  • 160wifi如何有效避免家用wifi漏洞情况的出现

    国内30.2%的家用无线路由器存在“弱密码”漏洞,更有4.7%的家用路由器已被黑。 因为,大部分家庭长期使用“Admin”、“root”等路由器出厂时默认的弱密码。此时,电脑若访问带有攻击代码的恶意网页,路由器设置就会被黑客篡改,在上网时弹出指定广告甚至钓鱼网站。

    2024年02月06日
    浏览(35)
  • 对三门问题的思考,应该细分两种情况

    三门问题来源于一个娱乐节目。节目中有一位参与者和一位主持人,在参与者的面前有三扇关闭的门,其中两扇门的后面是空的,剩下一扇门后是一辆法拉利跑车。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,是空门。主持人其后会

    2023年04月25日
    浏览(39)
  • 【JAVA】我们该如何规避代码中可能出现的错误?(三)

    个人主页:【😊个人主页】 系列专栏:【❤️初识JAVA】 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误00有时候是可以避免的,学习一些异常处理方式往往可以使我们编程的时间大大减少。 注:本文为系列文章,前文可点击观看: ➡️【JAVA】我们该如

    2024年02月08日
    浏览(37)
  • 【JAVA】我们该如何规避代码中可能出现的错误?(二)

    个人主页:【😊个人主页】 系列专栏:【❤️初识JAVA】 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误00有时候是可以避免的,学习一些异常处理方式往往可以使我们编程的时间大大减少。 注:本文为系列文章,前文可点击观看➡️【JAVA】我们该如何

    2024年02月08日
    浏览(36)
  • 【JAVA】我们该如何规避代码中可能出现的错误?(一)

    个人主页:【😊个人主页】 系列专栏:【❤️初识JAVA】 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的,学习一些异常处理方式往往可以使我们编程的时间大大减少。 检查性异常 :最具代表的检查性异常是 用户错误或问题引起的异

    2024年02月12日
    浏览(45)
  • Office显示未授权,需要激活这种情况应该怎么处理

    如图 1、先看自己的订阅是否已经到期 登录微软账户:https://account.microsoft.com/services 点击服务和订阅如下图所示 这种情况下说明目前用的office版本是高级版本,显示已过期 2、要想继续使用,先看账户有没有低版本不收费的,这里可以看到家庭和学生版本是可用的,但是电脑

    2024年02月13日
    浏览(48)
  • 如何将项目(工程/代码)文件上传到gitee?(注意一下,有几个坑)

    折腾了半个下午,碰到了一些bug,于是写一篇博客说道说道。 git官网链接:https://git-scm.com/ 如果官网打不开或者很卡的话,去git国内镜像网站下载。 git国内镜像网站地址:https://registry.npmmirror.com/binary.html?path=git-for-windows/ 下载自己系统对应的版本即可,此处不再啰嗦,自行下

    2023年04月09日
    浏览(44)
  • 新技术越来越多,作为程序员,我们应该怎么规划职业生涯? | 社区征文

    随着科技的不断进步,新技术不断涌现,对程序员的要求也在不断提高。作为一名程序员,要想在这个竞争激烈的行业中立足,就需要制定一份明确的职业规划,不断学习和掌握新技术,提升自己的职业能力和竞争力。 首先,程序员需要明确自己的职业方向和目标。程序员的

    2024年02月06日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包