Java“坑爹”的10大功能点

开发 后端
今天我们就来梳理一下Java中最“坑爹”、最违反常识的功能点,以排行榜的方式发布,以飨读者。

 

作为一门面向对象的编程语言,Java凭借其简单易用、功能强大的特点受到了广大编程爱好者的青睐,伴随着开源社区的推波助澜,Java语言更是席卷全球,势不可挡,在世界各地都有Java技术的从业者,它也常年高居编程语言排行榜的首位,足以表明Java的强悍与王者之风。

然而,即便是如此强大的编程语言,也有很多“坑爹”的功能,稍不注意,我们就会掉入坑里,轻则遭到同事的嘲笑和鄙视,重则造成悲惨后果而不得不跑路。

当然,坑爹这个词加上了双引号,因为大部分时候,都是由于我们不够熟练、违反我们的常识才造成了令人不愉快的后果。

今天我们就来梳理一下Java中最“坑爹”、最违反常识的功能点,以排行榜的方式发布,以飨读者。说明一下,本文中的代码基于JDK8来编译实现。

1、switch必须加上break才结束

对于多重分支选择,一系列的if-else-if语句会让代码的可读性变差,建议使用switch语句来代替,然而switch case中的分支判断,必须加上break语句才会中止其它case的执行,比如: 

int count = 1;    
switch(count){    
    case 1:    
        System.out.println("one");    
    case 2:    
        System.out.println("two");    
    case 3:    
        System.out.println("three");   
}   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

上面的代码会输出: 

one    
two    
three   
  • 1.
  • 2.
  • 3.

然而,这并不是我们想要的,或者说违反了我们的常识。满足了某种条件,当然就只需要执行这种条件下的逻辑即可,其他的case应该不予理会、直接跳过,象上面这段代码,只需要输出one就行了。

当然,在每个case结尾处加上break就可以达到我们期望的效果。Java 12 骚 switch 居然还能这样玩,推荐看下这篇。

这个功能点稍显“坑爹”,也是初学者常犯的错误,所以它也光荣上榜,排名第10位。

2、逻辑运算符的“短路”现象

使用逻辑运算符时,我们会遇到“短路”的现象:一旦能够确定整个表达式的值,就不会计算余下的部分了,当然,这个功能点其实是非常有用的,但对于初学者来说,可能会感觉比较惊讶,使用不当就会产生“坑爹”后果。比如下面的代码: 

int num = 1;    
System.out.println(false && ((num++)==1));    
System.out.println(num);   
  • 1.
  • 2.
  • 3.

就会输出false和1,因为逻辑与&&的前半部分为false,不管后半部分为true还是false,整个表达式都会返回false,所以就不会再计算后面的部分了,如果把false改成true,那么后半部分就会得到执行,num也就变成2了。

它在“坑爹”榜单中位列第9位。

3、数组下标从零开始

Java程序员都清楚,数组的下标是从零开始的,比如,我们要遍历一个数组,可以采用如下的方式: 

int[] arr = new int[]{1,3,5,7,9};    
for(int i=0;i<arr.length;i++){    
System.out.println("the element is:"+arr[i]);    

  • 1.
  • 2.
  • 3.
  • 4.

这跟我们日常生活中的经验是相违背的,正常情况都是从第1个元素开始计数的,特别是对于初学者来说有点难以接受,会觉得很惊讶。即使对于经验丰富的程序员来说,有些地方也需要格外注意,比如: 

String str = "hello world";    
System.out.println(str.charAt(1)); 
  • 1.
  • 2.

我们知道,charAt的作用是获取字符串中某个位置的字符,然而,上面的代码并不是输出第一个字符h,而是e,因为数组是从零开始计数的,这个也是比较“坑爹”啊。当然,设计者这么做的原因是考虑到了内存偏移量的因素。

每次在编写这样的代码时,都需要做这样的1到0的映射和转换(熟练了就是下意识的转换),确实也有点“坑爹”,所以它也不能幸免,排在第8位。

4、ArrayList遍历删除时报错

Talk is cheap,先上代码: 

public static void main(String[] args) {    
   List<String> list = new ArrayList<String>();    
   list.add("abc");    
   list.add("bc");    
   list.add("bc");    
   list.add("abcd");    
   list.add("abcdef");    
   //报错    
   int length = list.size();    
   for(int i = 0;i < length;i++){    
       if(list.get(i).equals("bc")){    
           list.remove(i);    
       }    
   }    

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

想从ArrayList中删除某个元素,于是,我们就写了上面的代码,但是它却抛出了IndexOutOfBoundsException异常,原因是ArrayList在删除元素后会重新计算数量,把list.size放在for循环中即可: 

for(int i=0;i<list.size();i++){    
   if(list.get(i).equals("bc")){    
       list.remove(i);    
   }    

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

当然,这种方法也存在问题,建议使用迭代器的方式来删除元素。ArrayList 增删千万不要乱用…推荐看一下这篇。

对于不太熟练的程序员来说,有时候就会掉入这样的陷阱之中。这是排名第7的情况。关注微信公众号:Java技术栈,在后台回复:Java,可以获取我整理的 N 篇 Java 教程,都是干货。

5、字符转成数字的坑

有时候,我们想把字符直接通过类型转换变成整数,比如像下面这样: 

char symbol = '8';    
System.out.println((int) symbol);   
  • 1.
  • 2.

我们想要的结果是8,然而,上面的代码却输出了56,略显“坑爹”,具体原因参考ASCII的知识。

6、while循环体的“障眼法”

对于while循环语句,如果你没有加上大括号,即使后面的语句挨在一起,也只会执行第一条statement,比如: 

int i = 0;    
while(i++<3)    
    System.out.print("A");    
    System.out.print("B"); 
  • 1.
  • 2.
  • 3.
  • 4.

上面的代码会输出: 

AAAB   
  • 1.

而不是3个A、3个B,更“坑爹”的是,如果两条语句放在一行上,迷惑性会更大: 

int i = 0;    
while(i++<3)    
    System.out.print("A");System.out.print("B"); 
  • 1.
  • 2.
  • 3.

上面这种写法同样是输出AAAB。所以,象这样的情况,哪怕只有一条语句,也建议加上大括号,完美避坑。

7、Integer类有缓存

这个功能点也是面试的高频热点之一,稍不注意,也有可能被带入沟里,我们看看下面这段代码: 

public static void main(String[] args){    
   Integer a = 100;    
   Integer b = 100;    
   Integer c = 200;    
   Integer d = 200;    
   System.out.println(a==b);    
   System.out.println(c==d);    
}   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

上面的代码竟然输出: 

true    
false   
  • 1.
  • 2.

这确实太出乎意料了,同样的代码,只是数值不同(而且差别不太大的样子),就产生了不一样的输出,这也太离谱了。

原来,Integer中有一个静态内部类IntegerCache,在类加载的时候,它会把[-128, 127]之间的值缓存起来,而Integer a = 100这样的赋值方式,会首先调用Integer类中的静态valueOf方法,这个方法会尝试从缓存里取值,如果在这个范围之内就不用重新new一个对象了: 

public static Integer valueOf(int i) {    
   if (i >= IntegerCache.low && i <= IntegerCache.high)    
       return IntegerCache.cache[i + (-IntegerCache.low)];   
   return new Integer(i);    
}   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

此功能入选“坑爹”排行榜的第4名。IntegerCache的妙用和陷阱,推荐看一下。

8、空方法体导致死循环

如果循环的方法体为空,则会导致死循环,比如,下面的代码打印出数字1,2,3: 

int i = 1;    
while(i<4){    
   System.out.println(i++);    
}   
  • 1.
  • 2.
  • 3.
  • 4.

如果你在敲键盘的时候,不小心在while结尾处加了一个分号(如果方法体没有加大括号,更容易产生这种情况): 

int i = 1;    
while(i<4);{    
   System.out.println(i++);    
}   
  • 1.
  • 2.
  • 3.
  • 4.

你猜怎么着,上面的代码可以正常编译并运行,然而,它却陷入了死循环……是不是非常“坑爹”?for循环也存在类似的情况。

它高居排行榜的第3位。

9、神奇的=+

我们知道,对于类似a=a+b这样的赋值语句,有一种简写方式:a +=b,然而,如果你不小心写成了a =+ b,结果又会是什么呢?我们看看下面的代码: 

int i = 100;    
i =+ 2;  //注意,加号在后面    
System.out.println(i);   
  • 1.
  • 2.
  • 3.

上面的代码既不会输出102,也不会报错,而是输出2,这的确出乎意料,完全不是我们期望的结果,太神奇了,非常的“坑爹”。

所以,它排名第2,稳居榜眼的位置。

10、Java注释能够识别Unicode

先看看代码: 

public static void main(String[] args){    
    // \\u000d System.out.println("Hello World!");    
}   
  • 1.
  • 2.
  • 3.

乍一看,代码都被注释掉了,当然不会输出任何东西,然而,它还是输出每个程序员都倍感亲切的Hello World,这是因为,unicode解码发生在代码编译之前,编译器将\u样式的代码进行文本转义,即使是注释也是这样,然后\u000a被转换成\n换行符,所以println代码得以正常执行。 

这样的功能着实“坑爹”,极其违反常识,它必须要上榜,必须要荣登状元的位置。 

 

责任编辑:庞桂玉 来源: Java技术栈
相关推荐

2012-10-16 09:25:33

Windows 8

2009-12-07 16:47:33

Windows 7Linux系统特征

2019-04-09 09:50:34

2014-07-15 11:25:18

2012-05-09 09:32:48

智能手机

2015-08-05 13:24:15

2017-03-21 10:02:34

2012-05-08 23:45:37

智能手机

2016-08-02 13:38:31

2009-05-07 11:28:48

CSS 3圆角多背景

2009-09-18 12:35:01

Red Hat红帽linux

2009-01-08 09:46:44

2021-05-08 09:02:19

Java加载器

2011-12-15 09:45:21

PhoneGap

2011-12-22 19:57:38

PhoneGap

2016-06-13 13:53:24

iosgif苹果

2011-05-24 15:12:15

Android

2013-05-02 09:58:28

网吧路由器路由器功能

2018-01-11 16:30:03

数据库MySQLSQL

2012-08-21 15:53:42

点赞
收藏

51CTO技术栈公众号