Java中10个最“坑爹”、最违反常识的功能点

一、前言

本文主要梳理一下Java中10个最“坑爹”、最违反常识的功能点,以排行榜的方式发布,以飨读者。说明一下,本文中的代码基于JDK8来编译实现。
当然,坑爹这个词加上了双引号,因为大部分时候,都是由于我们不够熟练、违反我们的常识才造成了令人不愉快的后果。

10. switch必须加上break才结束

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

	intcount = 1;
	switch(count){
	case1:
	   System. out.println( "one");
	case2:
	   System. out.println( "two");
	case3:
	   System. out.println( "three");
	}

上面的代码会输出:

	one
	two
	three

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

9. 逻辑运算符的“短路”现象

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

	intnum= 1;
	System.out.println( false&& (( num++)== 1));
	System.out.println( num);

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

8. 数组下标从零开始

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

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

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

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

我们知道,charAt的作用是获取字符串中某个位置的字符,然而,上面的代码并不是输出第一个字符h,而是e,因为数组是从零开始计数的,这个也是比较“坑爹”啊。当然,设计者这么做的原因是考虑到了内存偏移量的因素。每次在编写这样的代码时,都需要做这样的1到0的映射和转换(熟练了就是下意识的转换)

7. ArrayList遍历删除时报错

	Talk is cheap,先上代码:
	public static void main(String[] args){
	  List<String> list= newArrayList<String>;
	  list.add( "abc");
	  list.add( "bc");
	  list.add( "bc");
	  list.add( "abcd");
	  list.add( "abcdef");
	  //报错
	  intlength = list.size;
	  for( inti = 0;i < length;i++){
	    if( list.get(i).equals( "bc")){
	      list.remove(i);
	    }
	  }
	}

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

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

当然,这种方法也存在问题,建议使用迭代器的方式来删除元素。对于不太熟练的程序员来说,有时候就会掉入这样的陷阱之中

6. 字符转成数字的坑

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

	charsymbol = '8';
	System. out.println((int) symbol);

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

5. while循环体的“障眼法”

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

	inti = 0;
	while(i++< 3)
	System. out.print("A");
	System. out.print("B");

上面的代码会输出:

	AAAB

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

	inti = 0;
	while(i++< 3)
	System. out.print("A");System. out.print("B");

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

4. 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);
	}

上面的代码竟然输出:

	true
	false

这确实太出乎意料了,同样的代码,只是数值不同(而且差别不太大的样子),就产生了不一样的输出,这也太离谱了。原来,Integer中有一个静态内部类IntegerCache,在类加载的时候,它会把[-128, 127]之间的值缓存起来,而Integer a = 100这样的赋值方式,会首先调用Integer类中的静态valueOf方法,这个方法会尝试从缓存里取值,如果在这个范围之内就不用重新new一个对象了:

	public static Integer valueOf(inti){
	  if(i >= IntegerCache.low && i <= IntegerCache.high)
	    return IntegerCache.cache[i + (-IntegerCache.low)];
	    return new Integer(i);
	}

3. 空方法体导致死循环

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

	inti = 1;
	while(i< 4){
	    System. out.println(i++);
	}

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

	inti = 1;
	while(i< 4);{
	  System. out.println(i++);
	}

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

2. 神奇的=+

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

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

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

1. Java注释能够识别Unicode

先看看代码:

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

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数组章节作业 1、将一个数组的元素倒排过来,不能新开一个数组的临时存储空 间,只能在原数组上改。 2、写一个类用来模拟栈这种数据结构,要求底层 使用数组存储数据, 并给出相应的进栈和出栈的方法。MyStack int arr[]; int count;//栈元素个数 public MyStack(int n){ arr = new int[n]; } boolean push(int num){ if(count==arr.length){} arr[count++]=num; } int pop(){ if(count==0){ return -1;} return arr[--count]; } 3、实现在一个数组指定位置添加元素和删除元素的功能。 1、数组容量问题? 2、添加元素前后数组元素的变化 3、删除元素前后数组元素的变化 面向对象章节作业 1,写一个类,名为Animal,该类有两个私有属性, name(代表动物的名字),和legs(代表动物的腿的条 数)。并提供个两构造方法,一个无参,默认给name 赋值为AAA,给legs赋值为4;另一个需要两个参数, 分别用这两个参数给私有属性赋值。该类还有两个重载的move()方法,其一个无参,在屏幕上输出一行文字: XXX Moving!!(XXX为该动物的名字);另一个需要一个int参数n,在屏幕上输出n次 XXX Moving!! 2,写一个类Person,包含以下属性:String name; int age; boolean gender; Person partner。 为Person类写一个marry(Person p)方法,代表当前 对象和p结婚,如若可以结婚,则输出恭贺信息, 否则输出不能结婚原因。要求在另外一个类写一 个主方法,来测试以上程序。(下列情况不能结婚: 结婚年龄,男<24,女<22;3,某一方已婚) 3,写一个类,名为Animal,该类有两个私有属性,name(代表动物的名字),和legs(代表动物的腿的条数);要求为两个私有属性提供public 的访问方法。并提个两构造方法,一个无参,默认给name赋值为AAA,给legs赋值为4;另一个需要两个参数,分别用这两个参数给私有属性赋值。要求在第一个构造方法调用第二个构造方法。该类还有两个重载的move()方法,其一个无参,在屏幕上输出一行文字: XXX Moving!!(XXX为该动物的名字);另一个需要一个int参数n,在屏幕上输出n次 XXX Moving!! 4,写一个类Fish,继承自Animal类,并提供一个构造方法,该构造方法需要一个参数name,并给legs赋默认值0;该类还要求覆盖Animal类的无参move()方法,要求输出:XXX Swimming!! 5,写一个类Bird,继承自Animal类,并提供一个构造方法,该构造方法需要一个参数name,并给legs赋默认值2;该类还要求覆盖Animal类的无参move()方法,要求输出:XXX Flying!! 6,写一个类Zoo,要求分别生成若干个Animal,Fish和Bird。并调用他们的属性和方法。 7,写Shape类,要求如下: 1.int类型属性x和y,分别表示图形的坐标 2.无参构造器 3.构造器,对x和y进行初始化 4.draw()方法,输出"Shape draw" 写Circle类,继承Shape类,要求如下 1.double类型属性r,表示圆的半径 2.无参构造器,将r初始化为1.0 3.构造器,对r进行初始化 4.构造器,对x、y、r进行初始化 5.draw()方法,输出"draw in circle"和x,y,r的值 写Rectangle类,继承Shape类,要求如下 1.double类型属性height和width,表示矩形的高和宽 2.无参构造器,将height和width都初始化为1.0 3.构造器,对height和width进行初始化 4.构造器,对x、y、height、width进行初始化 5.draw()方法,输出"draw in rectangle"和x,y,height,width的值 使用ShapeTest类测试以上代码。 8,某公司的雇员分为以下若干类: Employee:这是所有员工总的父类,属性:员工的姓名,员工的生日月份。方法:getSalary(int month) 根据参数月份来确定工资,如果该月员工过生日,则公司会额外奖励100元。 SalariedEmployee:Employee的子类,拿固定工资的员工。属性:月薪 HourlyEmployee:Employee的子类,按小时拿工资的员工,每月工作超出160小时的部分按照1.5倍工资发放。属性:每小时的工资、每月工作的小时数 SalesEmployee:Employee的子类,销售人员,工资由月销售额和提成率决定。属性:月销售额、提成率 BasedPlusSalesEmployee:SalesEmployee的子类,有固定底薪的销售人员,工资由底薪加上销售提成部分。属性:底薪。 写一个程序,把若干各种类型的员工放在一个Employee数组里,写一个函数,打印出某月每个员工的工资数额。注意:要求把每个类都做成完全封装,不允许非私有化属性。 容器类章节作业 1、写MyStack类,实现栈功能。在类使用ArrayList保存数据。 2、使用TreeSet和Comparator,写TreeSetTest1 要求:对TreeSet的元素"HashSet"、"ArrayList"、"TreeMap"、"HashMap"、"TreeSet"、"LinkedList"进行升序和倒序排列 3、使用TreeSet和Comparator,写TreeSetTest2 要求:对TreeSet的元素1,2,3,4,5,6,7,8,9,10进行排列,排序逻辑为奇数在前偶数在后,奇数按照升序排列,偶数按照降序排列 4、使用TreeSet和Comparator,写TreeSetTestInner 要求: 对TreeSet的元素"HashSet"、"ArrayList"、"TreeMap"、"HashMap"、"TreeSet"、"LinkedList"进行升序和倒序排列 1. 使用匿名内部类实现 2. 使用静态内部类实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值