Java中这7个方法,一不小心就用错了!

最近我们通过sonar静态代码检测,同时配合人工代码review,发现了项目中很多代码问题。除了常规的bug和安全漏洞之外,还有几处方法用法错误,引起了我极大的兴趣。我为什么会对这几个方法这么感兴趣呢?因为它们极具迷惑性,可能会让我们傻傻分不清楚。

1. replace会替换所有字符?

很多时候我们在使用字符串时,想把字符串比如:ATYSDFA*Y中的字符A替换成字符B,第一个想到的可能是使用replace方法。

如果想把所有的A都替换成B,很显然可以用replaceAll方法,因为非常直观,光从方法名就能猜出它的用途。

那么问题来了:replace方法会替换所有匹配字符吗?

jdk的官方给出了答案。

该方法会替换每一个匹配的字符串。

既然replacereplaceAll都能替换所有匹配字符,那么他们有啥区别呢?

  1. replace有两个重载的方法。

其中一个方法的参数:char oldChar 和 char newChar,支持字符的替换。

source.replace('A', 'B')

另一个方法的参数是:CharSequence target 和 CharSequence replacement,支持字符串的替换。

source.replace("A", "B")
  1. replaceAll方法的参数是:String regex 和 String replacement,基于正则表达式的替换。普通字符串替换:

source.replaceAll("A", "B")

正则表达替换(将*替换成C):

source.replaceAll("\\*", "C")

顺便说一下,将*替换成C使用replace方法也可以实现:

source.replace("*", "C")

无需对特殊字符进行转义。

不过,千万注意,切勿使用如下写法:

source.replace("\\*", "C")

这种写法会导致字符串无法替换。

还有个小问题,如果我只想替换第一个匹配的字符串该怎么办?

这时可以使用replaceFirst方法:

source.replaceFirst("A", "B")

2. Integer不能用==判断相等?

不知道你在项目中有没有见过,有些同事对Integer类型的两个参数使用==比较是否相等?

反正我见过的,那么这种用法对吗?

我的回答是看具体场景,不能说一定对,或不对。

有些状态字段,比如:orderStatus有:-1(未下单),0(已下单),1(已支付),2(已完成),3(取消),5种状态。

这时如果用==判断是否相等:

  Integer orderStatus1 = new Integer(1);Integer orderStatus2 = new Integer(1);System.out.println(orderStatus1 == orderStatus2);

返回结果会是true吗?

答案:是false

有些同学可能会反驳,Integer中不是有范围是:-128-127的缓存吗?

为什么是false

先看看Integer的构造方法:它其实并没有用到缓存。

那么缓存是在哪里用的?

答案在valueOf方法中:

如果上面的判断改成这样:

String orderStatus1 = new String("1");
String orderStatus2 = new String("1");
System.out.println(Integer.valueOf(orderStatus1) == Integer.valueOf(orderStatus2));

返回结果会是true吗?

答案:还真是true

我们要养成良好编码习惯,尽量少用==判断两个Integer类型数据是否相等,只有在上述非常特殊的场景下才相等。

而应该改成使用equals方法判断:

Integer orderStatus1 = new Integer(1);
Integer orderStatus2 = new Integer(1);
System.out.println(orderStatus1.equals(orderStatus2));

3. 使用BigDecimal就不丢失精度?

通常我们会把一些小数类型的字段(比如:金额),定义成BigDecimal,而不是Double,避免丢失精度问题。

使用Double时可能会有这种场景:

double amount1 = 0.02;
double amount2 = 0.03;
System.out.println(amount2 - amount1);

正常情况下预计amount2 - amount1应该等于0.01

但是执行结果,却为:

0.009999999999999998

实际结果小于预计结果。

Double类型的两个参数相减会转换成二进制,因为Double有效位数为16位这就会出现存储小数位数不够的情况,这种情况下就会出现误差。

常识告诉我们使用BigDecimal能避免丢失精度。

但是使用BigDecimal能避免丢失精度吗?

答案是否定的。

为什么?

BigDecimal amount1 = new BigDecimal(0.02);
BigDecimal amount2 = new BigDecimal(0.03);
System.out.println(amount2.subtract(amount1));

这个例子中定义了两个BigDecimal类型参数,使用构造函数初始化数据,然后打印两个参数相减后的值。

结果:

0.0099999999999999984734433411404097569175064563751220703125

不科学呀,为啥还是丢失精度了?

jdk中BigDecimal的构造方法上有这样一段描述:

大致的意思是此构造函数的结果可能不可预测,可能会出现创建时为0.1,但实际是0.1000000000000000055511151231257827021181583404541015625的情况。

由此可见,使用BigDecimal构造函数初始化对象,也会丢失精度。

那么,如何才能不丢失精度呢?

BigDecimal amount1 = new BigDecimal(Double.toString(0.02));
BigDecimal amount2 = new BigDecimal(Double.toString(0.03));
System.out.println(amount2.subtract(amount1));

使用Double.toString方法对double类型的小数进行转换,这样能保证精度不丢失。

其实,还有更好的办法:

BigDecimal amount1 = BigDecimal.valueOf(0.02);
BigDecimal amount2 = BigDecimal.valueOf(0.03);
System.out.println(amount2.subtract(amount1));

使用BigDecimal.valueOf方法初始化BigDecimal类型参数,也能保证精度不丢失。在新版的阿里巴巴开发手册中,也推荐使用这种方式创建BigDecimal参数。

4. 字符串拼接不能用String?

String类型的字符串被称为不可变序列,也就是说该对象的数据被定义好后就不能修改了,如果要修改则需要创建新对象。

String a = "123";
String b = "456";
String c = a + b;
System.out.println(c);

在大量字符串拼接的场景中,如果对象被定义成String类型,会产生很多无用的中间对象,浪费内存空间,效率低。

这时,我们可以用更高效的可变字符序列:StringBuilderStringBuffer,来定义对象。

那么,StringBuilderStringBuffer有啥区别?

StringBuffer对各主要方法加了synchronized关键字,而StringBuilder没有。所以,StringBuffer是线程安全的,而StringBuilder不是。

其实,我们很少会出现需要在多线程下拼接字符串的场景,所以StringBuffer实际上用得非常少。一般情况下,拼接字符串时我们推荐使用StringBuilder,通过它的append方法追加字符串,它只会产生一个对象,而且没有加锁,效率较高。

String a = "123";
String b = "456";
StringBuilder c = new StringBuilder();
c.append(a).append(b);
System.out.println(c);

接下来,关键问题来了:字符串拼接时使用String类型的对象,效率一定比StringBuilder类型的对象低?

答案是否定的。

为什么?

使用javap -c StringTest命令反编译:

从图中能看出定义了两个String类型的参数,又定义了一个StringBuilder类的参数,然后两次使用append方法追加字符串。

如果代码是这样的:

String a = "123";
String b = "789";
String c = a + b;
System.out.println(c);

使用javap -c StringTest命令反编译的结果会怎样呢?

我们会惊讶的发现,同样定义了两个String类型的参数,又定义了一个StringBuilder类的参数,然后两次使用append方法追加字符串。跟上面的结果是一样的。

其实从jdk5开始,java就对String类型的字符串的+操作做了优化,该操作编译成字节码文件后会被优化为StringBuilder的append操作。

5. isEmpty和isBlank的区别

我们在对字符串进行操作的时候,需要经常判断该字符串是否为空。如果没有借助任何工具,我们一般是这样判断的:

if (null != source && !"".equals(source)) {System.out.println("not empty");
}

但是如果每次都这样判断,会有些麻烦,所以很多jar包都对字符串判空做了封装。目前市面上主流的工具有:

  • spring中的StringUtils

  • jdbc中的StringUtils

  • apache common3中的StringUtils

不过spring中的StringUtils类只有isEmpty方法,没有isNotEmpty方法。

jdbc中的StringUtils类只有isNullOrEmpty方法,也没有isNotNullOrEmpty方法。

所以在这里强烈推荐一下apache common3中的StringUtils类,它里面包含了很多实用的判空方法:isEmpty、isBlank、isNotEmpty、isNotBlank等,还有其他字符串处理方法。

问题来了,isEmptyisBlank有啥区别?

使用isEmpty方法判断:

 StringUtils.isEmpty(null)      = trueStringUtils.isEmpty("")        = trueStringUtils.isEmpty(" ")       = falseStringUtils.isEmpty("bob")     = falseStringUtils.isEmpty("  bob  ") = false

使用isBlank方法判断:

StringUtils.isBlank(null)      = true
StringUtils.isBlank("")        = true
StringUtils.isBlank(" ")       = true
StringUtils.isBlank("bob")     = false
StringUtils.isBlank("  bob  ") = false

两个方法关键的区别在于这种" "空字符串的情况,isNotEmpty返回false,而isBlank返回true。

6. mapper查询结果要判空?

有次代码review的时候,当时有个同事说这里的判空可以去掉,让我记忆犹新:

List<User> list = userMapper.query(search);
if(CollectionUtils.isNotEmpty(list)) {List<Long> idList = list.stream().map(User::getId).collect(Collectors.toList());
}

因为按常理,一般调用方法查询出来的集合,可能为null,需要判空的。但是,这里比较特殊,我查了一下mybatis的源码,这个判空的代码还真的可以去掉。

怎么回事呢?

mybatis的查询方法最终都会调到DefaultResultSetHandler类的handleResultSets方法:

该方法会返回一个multipleResultsList集合对象,在方法刚开始就new出来了,肯定是不会为空。

所以,如果你在项目的代码中看到有人直接使用查询出的结果,不判空也不要惊讶:

List<User> list = userMapper.query(search);
List<Long> idList = list.stream().map(User::getId).collect(Collectors.toList());

因为mapper底层已经处理过的,它不会出现空指针异常。

7. indexOf方法的正确用法

有次在review别人代码的时候,看到有个地方indexOf使用了这种写法,让我印象比较深刻:

String source = "#ATYSDFA*Y";
if(source.indexOf("#") > 0) {System.out.println("do something");
}

你们说这段代码会打印出do something吗?

答案是否定的。

为什么呢?

jdk官方说了不存在的情况会返回-1indexOf方法返回的是指定元素在字符串中的位置,从0开始。而上面的例子#在字符串的第一个位置,所以调用indexOf方法后的值其实是0。所以,条件是false,不会打印do something

如果想通过indexOf判断某个元素是否存在时,要用:

if(source.indexOf("#") > -1) {System.out.println("do something");
}

其实,还有更优雅的contains方法:

if(source.contains("#")) {System.out.println("do something");
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544820.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

这样设置,让你的 IDEA 好看到爆炸

今天这期我们来分享几个美化 IDEA 设置技巧&#xff0c;让你的 IDEA 与众不同。首先我们来看下 IDEA 默认设置&#xff0c;虽然不丑&#xff0c;但就是太单调&#xff0c;千篇一律。默认主题接着&#xff0c;我们来看下美化以后的界面&#xff0c;总体看起来是不是比默认好看了…

RabbitMQ 集群

2019独角兽企业重金招聘Python工程师标准>>> Clustering Guide A RabbitMQ broker is a logical grouping of one or several Erlang nodes, each running the RabbitMQ applicationand sharing users, virtual hosts, queues, exchanges, etc. Sometimes we refer …

使用uuid作为数据库主键,被技术总监怼了!

一、前言在日常开发中&#xff0c;数据库中主键id的生成方案&#xff0c;主要有三种数据库自增ID采用随机数生成不重复的ID采用jdk提供的uuid对于这三种方案&#xff0c;我发现在数据量少的情况下&#xff0c;没有特别的差异&#xff0c;但是当单表的数据量达到百万级以上时候&…

ThreadLocal不好用?那是你没用对!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在 Java 中&#xff0c;如果要问哪个类使用简单&#xff0c;但用好最不简单&#xff1f;我想你的脑海中一定会浮现出一次词—…

记一则js替换字符串的问题

2019独角兽企业重金招聘Python工程师标准>>> 软件的一处功能用到EasyUI的表单提交&#xff0c;返回一串字符串&#xff0c;这串字符串里有一段HTML代码&#xff0c;正常的情况下这段HTML代码里的双引号“ 是用 \ 转义过的。在IE中没问题&#xff0c;但是在Firefox和…

『图解Java并发』面试必问的CAS原理你会了吗?

在并发编程中我们都知道i操作是非线程安全的&#xff0c;这是因为 i操作不是原子操作。如何保证原子性呢&#xff1f;常用的方法就是加锁。在Java语言中可以使用 Synchronized和CAS实现加锁效果。Synchronized是悲观锁&#xff0c;线程开始执行第一步就是获取锁&#xff0c;一旦…

SimpleDateFormat线程不安全的5种解决方案!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;1.什么是线程不安全&#xff1f;线程不安全也叫非线程安全&#xff0c;是指多线程执行中&#xff0c;程序的执行结果和预期的…

mac地址漂移flapping的前因后果

一、什么是mac地址flapping?mac地址漂移是指&#xff1a;在同一个vlan内&#xff0c;mac地址表项的出接口出现变更。如图&#xff1a;二、产生的原因1、因为环路或VRRP切换&#xff0c;导致的MAC地址漂移告警。&#xff08;不予关注&#xff09;2、因为无线用户漫游&#xff0…

时间转换竟多出1年!Java开发中的20个坑你遇到过几个?

前言最近看了极客时间的《Java业务开发常见错误100例》&#xff0c;再结合平时踩的一些代码坑&#xff0c;写写总结&#xff0c;希望对大家有帮助&#xff0c;感谢阅读~1. 六类典型空指针问题包装类型的空指针问题级联调用的空指针问题Equals方法左边的空指针问题ConcurrentHas…

Oracle RAC Failover 详解

2019独角兽企业重金招聘Python工程师标准>>> Oracle RAC 同时具备HA(High Availiablity) 和LB(LoadBalance). 而其高可用性的基础就是Failover(故障转移). 它指集群中任何一个节点的故障都不会影响用户的使用&#xff0c;连接到故障节点的用户会被自动转移到健康节…

一个ThreadLocal和面试官大战30个回合

开场杭州某商务楼里&#xff0c;正发生着一起求职者和面试官的battle。面试官&#xff1a;你先自我介绍一下。安琪拉&#xff1a;面试官你好&#xff0c;我是草丛三婊&#xff0c;最强中单&#xff08;妲己不服&#xff09;&#xff0c;草地摩托车车手&#xff0c;第21套广播体…

图文并茂的聊聊Java内存模型!

在面试中&#xff0c;面试官经常喜欢问&#xff1a;『说说什么是Java内存模型(JMM)&#xff1f;』面试者内心狂喜&#xff0c;这题刚背过&#xff1a;『Java内存主要分为五大块&#xff1a;堆、方法区、虚拟机栈、本地方法栈、PC寄存器&#xff0c;balabala……』面试官会心一笑…

AngularJS入门心得2——何为双向数据绑定

前言&#xff1a;谁说Test工作比较轻松&#xff0c;最近在熟悉几个case&#xff0c;差点没疯。最近又是断断续续的看我的AngularJS&#xff0c;总觉得自己还是没有入门&#xff0c;可能是自己欠前端的东西太多了&#xff0c;看不了几行代码就有几个常用函数不熟悉的。看过了大漠…

Java中那些内存泄漏的场景!

虽然Java程序员不用像C/C程序员那样时刻关注内存的使用情况&#xff0c;JVM会帮我们处理好这些&#xff0c;但并不是说有了GC就可以高枕无忧&#xff0c;内存泄露相关的问题一般在测试的时候很难发现&#xff0c;一旦上线流量起来可能马上就是一个诡异的线上故障。内存泄露定义…

ThreadLocal内存溢出代码演示和原因分析!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;前言ThreadLocal 翻译成中文是线程本地变量的意思&#xff0c;也就是说它是线程中的私有变量&#xff0c;每个线程只能操作自…

彻夜怒肝!Spring Boot+Sentinel+Nacos高并发已撸完,快要裂开了!

很多人说程序员是最容易实现财富自由的职业&#xff0c;也确实&#xff0c;比如字节 28 岁的程序员郭宇不正是从普通开发一步步做起的吗&#xff1f;回归行业现状&#xff0c;当开发能力可以满足公司业务需求时&#xff0c;拿到超预期的 Offer 并不算难。最近我也一直在思考这个…

湖南多校对抗5.24

据说A,B,C题都比较水这里就不放代码了 D:Facility Locations 然而D题是一个脑经急转弯的题&#xff1a;有m行&#xff0c;n列&#xff0c;每个位置有可能为0&#xff0c;也可能不为0&#xff0c;问最多选K行是不是可以使得每一列都至少有一个0&#xff0c;其中代价c有个约束条件…

PPT演讲计时器

下载 GitHub 源码地址 如果访问不到的话&#xff0c;可以从百度盘下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1bK4sug-eK85fmPgi9DzhcA 提取码&#xff1a;0vp3 文件&#xff1a;VB.Equal.Timer-VB计时器软件-绿色无残留 写在前面 转眼也工作了两年了&…

2万字!66道并发面试题及答案

我花了点时间整理了一些多线程&#xff0c;并发相关的面试题&#xff0c;虽然不是很多&#xff0c;但是偶尔看看还是很有用的哦&#xff01;话不多说&#xff0c;直接开整&#xff01;01 什么是线程&#xff1f;线程是操作系统能够进⾏运算调度的最⼩单位&#xff0c;它被包含在…

25种代码坏味道总结+优化示例

前言什么样的代码是好代码呢&#xff1f;好的代码应该命名规范、可读性强、扩展性强、健壮性......而不好的代码又有哪些典型特征呢&#xff1f;这25种代码坏味道大家要注意啦1. Duplicated Code &#xff08;重复代码&#xff09;重复代码就是不同地点&#xff0c;有着相同的程…