吐槽:那些Java设计中不得不说的槽点

1. 求长度各有千秋

你是否曾经在面试的时候,常常被问到:数组有没有 length() 方法?字符串有没有 length() 方法? 集合有没有 length() 方法?面试

面对这个问题,那么不得不吐槽一下,Java 中获取长度的方式,设计着实有点乱,对刚入门的程序猿而言,那绝对是一脸的懵逼。编程

String[] array = {"abc", "def"};
String str = "abcedf";
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("def");
System.out.println("数组的长度: " + array.length);
System.out.println("字符串的长度: " + str.length());
System.out.println("集合的长度: " + list.size());
复制代码

正式科普一下,但愿可以铭记你心中。数组求长度用 length 属性;字符串求长度用 length() 方法;集合求长度用 size() 方法。数组

2. 字符串截取有深意

对于程序猿来讲,编程规范可以养成良好的编程习惯,提升代码质量,减小沟通成本。阿里 Java 开发手册编程规约中记载,【强制】方法名、參数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须听从驼峰形式。bash

看到这里,不得不提 String 中的 substring 方法,你是否是常常把“substring”写成“subString”。本次这个命名不是吐槽的重点。主要想分享以下代码片断。ui

public class StringInterview {
   public static void main(String[] args) {
       String str = "......abcdefgh.......";
       String subStr = str.substring(1,3);
       str = null;
       System.out.println(subStr);
   }
}
复制代码

假如上述这段程序在 Java 1.6 中运行,代码中虽然强制使 str 引用为空,本意是释放 str 占用的空间,可是这个时候,GC 是没法回收这个大的 char 数组的,由于还在被 subStr 字符串内部引用着,虽然 subStr 只截取这个大数组的一小部分。当 str 是一个很是大字符串的时候,这种浪费是很是明显的,甚至会带来内存泄露问题。this

深刻 Java 1.6 中 substring 的设计一探究竟。编码

public String substring(int beginIndex, int endIndex) {
  if (beginIndex < 0) {
      。。 。。 。。
  }
  if (endIndex > count) {
       。。 。。 。。
  }
  if (beginIndex > endIndex) {
       。。 。。 。。
  }
  return ((beginIndex == 0) && (endIndex == count)) ? this :
   new String(offset + beginIndex, endIndex - beginIndex, value);
}
复制代码

到此你应该拨云见日豁然开朗。当咱们调用字符串 str 的 substring 获得字符串 subStr,其实这个操做,无非就是调整了一下 subStr 的 offset 和 count ,用到的内容仍是 str 以前的 value 字符数组,并无从新建立新的专属于 subStr 的内容字符数组。若是 subStr 的生命周期要长于 str 或者手动设置 str 为null,当垃圾回收进行后,str 被回收掉,subStr 没有回收掉,那么内存占用依旧存在,由于 subStr 持有 str 字符数组的引用。spa

正式科普一下,这个问题出如今 Java 1.6,而且 Java 1.7 中已经修复。虽然已经修复,并不表明咱们就不须要了解,若是你正在求职路上,稍微了解一下,说不定会加分。设计

3. 一条 if 语句引起不满

先给各位抛一段 Java LinkedList 类的代码片断,一块儿吐槽吐槽。code

public E getFirst() {
   final Node<E> f = first;
  if (f == null)
       throw new NoSuchElementException();
   return f.item;
}
复制代码

JDK 中 if 语句后只有一条语句,大部分都是这么实现的。原则上,if 语句若是后面跟着只有一句话,是能够不加的。可是在咱们实际开发中,有些现象却会让你匪夷所思,不信你看看下面的代码片断。 片断一

if (f == null)
   //抛出异常,或者加一条打印语句,加上此句注释逻辑就变了
   throw new NoSuchElementException();
复制代码

片断二

public class Interview {
   public static void main(String[] args) {
       Class c = Interview.class;
       try {
           Object o = c.newInstance();
           if (o instanceof Interview)
               Interview tt = (Interview) o; //为何会报错?请各位解释缘由
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}
复制代码

正式科普一下,看似一个简单的编码规范,背后隐藏了多少坑啊,因此为了良好的编程习惯,建议仍是统一加上大括号为好,良好的编码习惯是真重要啊。

4. 时间实现也找茬

Tiago Fernandez 作过一次投票,选举最烂的 Java API,排第二的就是日期 API(Date 和Calender)。一言不合就抛代码,以下片断是计算两个日期之间的天数。

public static void main(String[] args) {
   Calendar begin = Calendar.getInstance();
   begin.set(1990, Calendar.JUNE, 17);
   Calendar end = Calendar.getInstance();
   System.out.println(alculatedDays(begin, end));
   System.out.println(alculatedDays(begin, end)); // 为何显示 0?
}
复制代码
public static long alculatedDays(Calendar begin, Calendar end) {
   long days = 0;
   while (begin.before(end)) {
       begin.add(Calendar.DAY_OF_MONTH, 1);
       days++;
   }
   return days;
}
复制代码

alculatedDays 方法,若是连续计算两个 Date 实例的话,第二次会取得 0,由于 Calendar 状态是可变的,考虑到重复计算的场合,最好复制一个新的 Calendar,改造以下

public static long alculatedDays(Calendar begin, Calendar end) {
   Calendar calendar = (Calendar) begin.clone(); // 复制
   long days = 0;
   while (calendar.before(end)) {
       calendar.add(Calendar.DAY_OF_MONTH, 1);
       days++;
   }
   return days;
}
复制代码

不过万物都在向前进化,由于因为原来老旧的日期 API 一直被人诟病,因此 JDK 1.8 中对日期的改动是特别大的,基本上是引入了一套全新易用的 API,各位有时间能够体验一下。

好了,吐槽中见真谛,今天就讲这么多吧。但愿你能 get 到一点点共鸣,若是你比较感兴趣,就多多关注和分享给身边的朋友吧

相关文章
相关标签/搜索