最近仔细阅读了秦晓波老师的《编写高质量代码改善Jaav程序的151个建议》。如果有151条建议,更恰当的说法是应该避免Java的一些冷门陷阱。这里有20个更有趣的重新学习建议。三元运算符的类型必须一致。三元运算符运算也称为三元运算。它的表达形式为:“条件表达式?表达式1:表达式2”。元运算符,目的是简化if-else,当条件表达式为真时执行表达式1,否则执行表达式2。我们来分析下面的代码:publicstaticvoidmain(String[]args){inti=80;字符串s=String.valueOf(i<100?80:100);字符串s1=String.valueOf(i<100?80:100.0);布尔等于=s.equals(s1);//两者是否相等:false,字符串s的值:80,字符串s1的值:80.0System.out.println("ArebothEqual:"+equals);System.out.println("字符串s的值:"+s);System.out.println("Valueofstrings1:"+s1);}解释:如果三元运算符号的类型不一致,返回的结果也不一致。避免使用可变长度参数的方法重载publicclassClient{privatestaticfinalLoggerlog=LoggerFactory.getLogger(Client.class);publicstaticvoidmain(String[]args){Clientclient=newClient();客户。calPrice(5000,80);}/***calPrice简单折扣计算**@paramprice价格*@paramdiscount折扣*@description*@authorluokangyuan*@date2019-4-214:58*@version1.0.0*/privatevoidcalPrice(intprice,intdiscount){floatknockdownPrice=price*discount/100.0F;log.info("简单折扣后的价格:{}",knockdownPrice);}/***calPrice**@parampriceprice*@paramdiscountsdiscount*@description复杂折扣计算*@authorluokangyuan*@date2019-4-215:08*@version1.0.0*/privatevoidcalPrice(intprice,int...discounts){floatknockdownPrice=price;for(intdiscount:discounts){knockdownPrice=knockdownPrice*折扣/100;}log.info("复杂折扣后的价格:{}",knockdownPrice);}}解释:方法重载是指方法名相同,但参数类型或参数个数不同。在上面的例子中,Java编辑器会找到方法签名合适和合适的方法,上面的测试调用简单的折扣计算,而不是复杂的折扣计算。不要只替换一个类publicclassConstant{publicfinalstaticintMAX_AGE=150;}publicclassClient{publicstaticvoidmain(String[]args){System.out.println("Thehumanlifelimitis:"+Constant.MAX_AGE);}}对于最终修饰的基本类型和String类型,编译器会认为它是稳定的(ImmutableStatus)所以在编译的时候直接把值编译成字节码,避免运行时引用(Run-timeReference)来提高代码的执行效率。对于我们的例子,Client类在编译时字节码中写的是“150”,这个常量,不是地址引用,所以不管后面怎么修改常量类,只要不重新编译Client类,输出一切如常。对于final修饰的类(即非基本类型),编译器会认为它不是稳定状态(MutableStatus),引用关系是在编译时建立的(这种类型也叫SoftFinal)。如果Client类引入的常量是一个类或者实例,那么会及时输出最新的值,不需要重新编译。用偶数判断代替奇数判断Strings=n%2==1?"奇数":"偶数";Strings1=n%2==0?"偶数":"奇数";注意:通常使用第二种判断偶数,使用第一种。-1也判断为偶数。处理整数类型的货币//0.40000000000000036System.out.println(10.00-9.60);说明:Java中的浮点数是不准确的,在货币处理中使用浮点数会出现问题,所以使用BigDecimal,类进行计算,或者使用整数,将需要计算的数放大100倍,然后计算后减少。1、使用BigDecimalBigDecimal是专门为弥补浮点数精确计算的不足而设计的类,同时也提供了加减乘除常用的数学算法。特别是与数据库中Decimal类型的字段进行映射时,BigDecimal是最优方案。2、使用整型将计算中涉及的值扩大100倍,转换成整型,显示时再缩小100倍。这种处理的优点是计算简单、准确,一般在非金融行业(如零售业)使用较多。这种方法也会用在一些零售POS机上,它们的输入输出都是整数,所以操作比较简单。使用String直接赋值publicstaticvoidmain(String[]args){Stringstr1="China";Stringstr2="中国";Stringstr3=newString("中国");字符串str4=str3.intern();系统.out.println(str1==str2);//真System.out.println(str1==str3);//错误System.out.println(str1==str4);//true}注意:推荐使用Stringstr1="China";该方法为字符串赋值,而不是newString("China");这样定义的常量会被存放在Java中的一个常量池中,如果池中存在,就不会重复定义,所以str1==str2返回true。new是对象,不会检查字符串常量池是否存在,所以str1==str3是异引用,返回false。经intern()处理后返回true,因为intern()会去对象常量池中检查是否有字面相同的引用对象。asList生成的列表不可修改privatestaticvoidarraysTest(){String[]week={"Mon","Tue","Wed","Thu"};Liststrings=Arrays.asList(week);strings.add("Fri");}注意:运行时报错,asList生成的列表不可修改。不要让null和空值威胁变长方法publicvoidcountSum(Stringtype,Integer...is){}publicvoidcountSum(Stringtype,String...strs){}publicstaticvoidmain(String[]args){ClientReloadclientReload=newClientReload();clientReload.countSum("中国",0);clientReload.countSum("中国","人民");//编译错误clientReload.countSum("China");//编译错误clientReload.countSum("China",null);}解释:也是一个变长参数的重载方法。如果外部调用使用了NULL或者空值,就会出现编译失败的错误。这个应该是NULL,Null值满足上面重载方法中的参数条件,所以编译器不知道调用哪个方法,这违反了重载方法设计中的KISS原则。同时在外部调用时,外部调用隐藏了实参。类型,调用代码修改如下,不会出现编译错误。Stringstrs=null;clientReload.countSum("中国",strs);警惕自增陷阱publicstaticvoidmain(String[]args){intcount=0;对于(inti=0;i<100;i++){inti1=count++;计数=i1;System.out.println("每个count++的值:"+i1);}System.out.println(count);}解释:结果是0,不是我们的100,这是count++是一个表达式,在加之前返回count的值。不要忘记打破publicstaticvoidmain(String[]args){Strings=toChineseNumber(2);log.info("转换结果:{}",s);}privatestaticStringtoChineseNumber(intn){StringchineseNumber="";switch(n){case0:chineseNumber="零";case1:chineseNumber="一";case2:chineseNumber="二";case3:chineseNumber="三";case4:chineseNumber="四";case5:chineseNumber="吴";case6:chineseNumber="Lu";case7:chineseNumber="种";case8:chineseNumber="八";case9:chineseNumber="九";case10:chineseNumber="pickup";}returnchineseNumber;}注意:switch的断点不能少。不要让类型静默转换/**lightspeed*/privatestaticfinalintLIGHT_SPEED=30*10000*1000;publicstaticvoidmain(String[]args){longdis=LIGHT_SPEED*60*8;//-2028888064System.out.println(dis);}说明:LIGHT_SPEED*60*8计算后为int类型,可能存在越界问题。虽然我们在代码中写了对Long类型的转换,但是在Java中,是先运算再处理类型。转换后的,即LIGHT_SPEED*60*8经过计算是int类型,超出长度从头开始,所以是负值,修改为显示的定义类型。如下:/**Speedoflight*/privatestaticfinallongLIGHT_SPEED=30L*10000*1000;publicstaticvoidmain(String[]args){longdis=LIGHT_SPEED*60*8;System.out.println(dis);}避免使用可变长度参数重载方法publicclassMainTest{publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException{System.out.println(PriceTool.calPrice(12,1));//1}}classPriceTool{publicstaticintcalPrice(intprice,intdiscount){return1;}publicstaticintcalPrice(intprice,int...discount){return2;}}解释:编译器会从最简单的开始猜测,只要满足编译条件即可。少用staticimportimportstaticjava.lang.Math.PI;publicdoublecalCircleArea(doubler){returnMath.PI*r*r;}publicdoublecalBallArea(doubler){return4*PI*r*r;}说明:静态导入可以减少代码量,但是不易阅读,可读性差。当心包装类型的空值publicstaticintf(Listlist){intcount=0;for(Integeri:list){count+=(i!=null)?我:0;}returncount;}解释:打包对象和未装箱对象确实可以自由转换,但是要剔除空值,空值不能转换为基本类型。对于这个问题,我们要牢记一件事:包装类型参与运算时,必须进行空值检查。以包装类型大小比较为例。我==j假。整数是引用类型。publicstaticvoidmain(String[]args){Integeri=newInteger(100);整数j=新整数(100);System.out.println(i==j);}避免instanceof的意外结果instanceof是一个简单的二元运算符,用于判断一个对象是否为类实例,两边的运算符需要有继承关系或实现关系。publicstaticvoidmain(String[]args){//String对象是否是Object的实例-true,“String”为字符串,字符串继承自Object,当然是Object的实例。booleanb1="String"instanceof对象;//String对象是否是String类的实例——true,一个类的对象当然是一个类的实例。布尔b2=newString()instanceofString;//Object对象是否是String的实例,编译错误,Object是父类。布尔b3=newObject()instanceofString;//unboxed类型是否是boxed类型的实例,编译错误,"A"是Char类型,是基本类型,instanceof只能用于对象判断。booleanb4="A"instanceof字符;//空对象是否是String的实例-false,instanceof的规则,左边为null,不管右边是什么类型,都返回false。布尔b5=nullinstanceof字符串;//类型转换后的空对象是否是String的实例-false,null可以说是没有类型,类型转换后还是null。booleanb6=(String)nullinstanceof字符串;//Date对象是否是String的实例,编译器报错,Date类和String类没有继承关系booleanb7=newDate()instanceofString;}不要只设置随机数seedinJava中,随机数的产生依赖于种子,随机数与种子的关系遵循以下两个原则:不同的种子会产生不同的随机数;即使实例不同,相同的种子也会生成相同的随机数。publicstaticvoidmain(String[]args){Randomr=newRandom();for(inti=1;i<4;i++){System.out.println("th"+i+"time:"+r.nextInt());}}运行结果:1sttime:8463391682ndtime:7560515033rdtime:1696875906程序启动后,生成的随机数会有所不同。但是每次启动程序,都会产生三个随机数。生成随机数和种子之间的关系如下:1)不同的种子生成不同的随机数。2)种子相同,即使实例不同,生成的随机数也相同。Random的默认seed(无参数构造)是System.nanoTime()的返回值(jdk1.5之前是System.currentTimeMillis()),这个值是距离固定时间点的纳秒数,不同的操作系统和的硬件固定时间点不同,随机数自然不同。避免在构造函数中初始化其他类publicclassClient35{publicstaticvoidmain(String[]args){Sonson=newSon();儿子.doSomething();}}//父类Father{publicFather(){newOther();}}//相关类classOther{publicOther(){newSon();}}//子类classSonextendsFather{publicvoiddoSomething(){System.out.println("Hi,showmeSomething!");}}解释:导致构造方法被循环调用。-128-127的整数对象首先使用整数池Integer进行缓存。所以通过装箱(Integer.valueOf())得到的对象是可以重复使用的。publicstaticIntegervalueOf(inti){如果(i>=IntegerCache.low&&i<=IntegerCache.high)returnIntegerCache.cache[i+(-IntegerCache.low)];返回新整数(i);}性能力考疑,首选数组privatestaticintgetSumForArray(int[]array){intsum=0;for(inti=0;ilist){returnlist.stream().mapToInt(Integer::intValue).sum();}