当前位置: 首页 > 后端技术 > Python

90%的Java开发者都会犯的5个错误

时间:2023-03-26 14:20:59 Python

前言作为Java开发者,不知道你有没有遇到过一些匪夷所思的bug。这些错误通常需要数小时才能解决。当你找到它们时,你可能会默默地称自己为傻瓜。是的,这些可笑的bug基本上都是你忽略了一些基础知识造成的。其实都是很低级的错误。今天,我总结了一些常见的编码错误,然后给出解决方案。希望大家在日常的编码中能够避免这样的问题。使用Objects.equals来比较对象的方法相信大家都不陌生,甚至很多人都经常使用。是JDK7提供的一种方法,可以快速实现对象的比较,有效避免烦人的空指针检查。但是这种方法很容易用错,例如:LonglongValue=123L;System.out.println(longValue==123);//trueSystem.out.println(Objects.equals(longValue,123));//falseCopy为什么代码中用Objects.equals()替换==会得到不同的结果?这是因为使用==编译器会得到包类型对应的基本数据类型longValue,然后与这个基本数据类型进行比较,也就是说编译器会自动将常量转换为比较的基本数据类型,而不是包类型。使用Objects.equals()方法后,编译器默认常量的基本数据类型为int。下面是Objects.equals()的源码,其中a.equals(b)使用Long.equals()判断对象类型,因为编译器已经认为常量是int类型的,所以比较结果必须是假的。publicstaticbooleanequals(Objecta,Objectb){返回(a==b)||(a!=null&&a.equals(b));}publicbooleanequals(Objectobj){if(objinstanceofLong){返回值==((Long)obj).longValue();}returnfalse;}复制代码知道了原因,解决方法就很简单了。直接声明常量的数据类型,如Objects.equals(longValue,123L)。其实如果逻辑严密,就不会出现上面的问题。我们需要做的是保持良好的编码习惯。日期格式错误在我们日常开发中,经常需要对日期进行格式化,但是很多人使用的格式不对,从而导致出现意想不到的情况。请看下面的例子。Instantinstant=Instant.parse("2021-12-31T00:00:00.00Z");DateTimeFormatter格式化程序=DateTimeFormatter.ofPattern("YYYY-MM-ddHH:mm:ss").withZone(ZoneId.systemDefault());System.out.println(formatter.format(instant));//2022-12-3108:00:00复制上面的代码,格式为YYYY-MM-dd,年份从2021变成了2022,为什么?这是因为java的DateTimeFormatter模式YYYY和yyyy之间存在细微差别。它们都代表一年,但yyyy代表日历年,YYYY代表一周。这是一个细微的差别,只在一年左右的变化中引起了问题,所以你的代码本来可以一直正常工作,只是在新的一年里引起了问题。12月31日按星期计算的年份是2022年,正确的做法应该是用yyyy-MM-dd来格式化日期。这个错误特别微妙。这通常不是问题。它只会在新的一年到来时触发。我公司就是因为这个bug导致了生产事故。在ThreadPool中使用ThreadLocal如果您创建了一个ThreadLocal变量,访问该变量的线程将创建一个线程局部变量。合理使用ThreadLocal可以避免线程安全问题。但是,如果在线程池中使用ThreadLocal,就要小心了。您的代码可能会产生意想不到的结果。举个很简单的例子,假设我们有一个电商平台,用户购买了商品后需要发送邮件确认。privateThreadLocalcurrentUser=ThreadLocal.withInitial(()->null);privateExecutorServiceexecutorService=Executors.newFixedThreadPool(4);publicvoidexecutor(){executorService.submit(()->{Useruser=currentUser.get();IntegeruserId=user.getId();sendEmail(userId);});}如果我们使用ThreadLocal保存用户信息,这里会有一个隐藏的bug。因为使用了线程池,可以复用线程,所以在使用ThreadLocal获取用户信息时,极有可能误获取到别人的信息。您可以使用会话来解决这个问题。使用HashSet去除重复数据在编码的时候,我们经常会有去重的需求。一想到去重,很多人首先想到的就是使用HashSet去重。但是,不小心使用HashSet可能会导致去重失败。Useruser1=newUser();user1.setUsername("test");Useruser2=newUser();user2.setUsername("test");Listusers=Arrays.asList(user1,user2);HashSet<用户>sets=newHashSet<>(users);System.out.println(sets.size());//大小为2细心的读者应该已经猜到失败的原因了。HashSet使用hashcode对哈希表进行寻址,使用equals方法判断对象是否相等。如果自定义对象没有重写hashcode方法和equals方法,则默认使用父对象的hashcode方法和equals方法。所以HashSet会认为这是两个不同的对象,所以去重失败。线程池中异常被吃掉ExecutorServiceexecutorService=Executors.newFixedThreadPool(1);executorService.submit(()->{//dosomethingdoubleresult=10/0;});复制代码上面的代码模拟了一个线程池抛出异常的场景。我们真正的业务代码要处理各种可能出现的情况,所以很有可能会因为某些特定的原因而触发RuntimeException。但是如果没有特殊处理,这个异常就会被线程池吃掉。这会导致你甚至不知道的问题,这是一个非常严重的后果。因此,最好在线程池中的trycatch中捕获异常。小结本文总结了开发过程中容易犯的5个错误。希望大家能养成良好的编码习惯。