吐槽:那些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 风格,必须遵从驼峰形式。

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

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 是一个非常大字符串的时候,这种浪费是非常明显的,甚至会带来内存泄露问题。

深入 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 字符数组的引用。

正式科普一下,这个问题出现在 Java 1.6,并且 Java 1.7 中已经修复。虽然已经修复,并不代表我们就不需要了解,如果你正在求职路上,稍微了解一下,说不定会加分。

3. 一条 if 语句引发不满

先给各位抛一段 Java LinkedList 类的代码片段,一起吐槽吐槽。

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 到一点点共鸣,如果你比较感兴趣,就多多关注和分享给身边的朋友吧

原文地址:https://blog.51cto.com/14295088/2417328

时间: 2024-10-10 06:40:32

吐槽:那些Java设计中不得不说的槽点的相关文章

Java基础__慕课网学习(25):Java第二季4.7 UML与PowerDesigner在Java设计中的应用,找了一篇比较好的博客,转载在这里

面向对象模型 面向对象模型是利用UML(统一建模语言)的图形来描述系统结构的模型,它从不同角度实现系统的工作状态.这些图形有助于用户,管理人员,系统分析人员,开发人员,测试人员和其他人员之间进行信息交流.这里主要介绍用例图,序列图和类图.   1.面向对象模型OOM 面向对象模型是利用UML的图形描述系统结构的模型,可以利用PowerDesigner的面向对象模型进行创建.PowerDesigner支持UML的下列图形. 用例图(User Case Diagram):通常用来定义系统的高层次草图

Java的GUI设计中如何跨界面传值

在Java设计中我们会遇到登录界面的信息,在后面的某个情况也需要使用. 比如这是笔者的一个登录界面 可以看到获取密码和账号 在这个时候的功能的完成需要密码和账号 1 // 登录信息的获取 2 public String setLogin_Name_Text(String login_Name_Text) { 3 name = login_Name_Text; 4 return name; 5 } 6 7 public String setLogin_Password_Text(String lo

在Java API设计中,面向接口编程的思想,以及接口和工厂的关系

现在的java API的设计中,提倡面向接口的编程,即在API的设计中,参数的传递和返回建议使用接口,而不是具体的实现类,如一个方法的输入参数类型应该使用Map接口,而不是HashMap或Hashtable等具体的实现类.这样做的好处是,程序容易扩展.如果使用Map作为参数,用户可以使用任何实现Map接口的类作为参数,而不是仅仅限制使用HashMap或Hashtable作为参数,使程序的实现更加灵活. 接口(Java的Interface),只定义了一些抽象的方法(也可以定义一些常量,但不鼓励这么

Java设计模式中的单例设计

/** * 单例设计模式 * 应用场合:只需要一个对象的 * 作用:保证整个应用程序中某个实例有且只有一个 * 类型有:饿汉模式.懒汉模式 * 下面的例子是一个饿汉模式的例子 */ class SingleDemo { // 1.将构造方法私有化,不允许外部直接创建使用 private SingleDemo() {} // 2.创建类的唯一实例,使用private static修饰 private static SingleDemo instance = new SingleDemo(); //

Java编程中“为了性能”尽量要做到的一些地方

下面是参考网络资源总结的一些在Java编程中尽可能要做到的一些地方. 1. 尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面: 第一,控制资源的使用,通过线程同步来控制资源的并发访问: 第二,控制实例的产生,以达到节约资源的目的: 第三,控制数据共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信. 2. 尽量避免随意使用静态变量 要知道,当某个对象被定义为stataic变量所

java编程中&#39;为了性能&#39;一些尽量做到的地方

java编程中'为了性能'一些尽量做到的地方 2011-08-16 14:34:59|  分类: JAVA |  标签:java编程  缓存经常使用的对象  |举报|字号 最近的机器内存又爆满了,出了新增机器内存外,还应该好好review一下我们的代码,有很多代码编写过于随意化,这些不好的习惯或对程序语言的不了解是应该好好打压打压了. 下面是参考网络资源和总结一些在java编程中尽可能做到的一些地方- 1.尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并

java编程中的性能提升问题

java编程中的性能提升 软件产品犹如一栋大楼,大楼在建设初期,会有楼房规划,建筑构想,打牢地基,后面才是施工人员进行进行实质性的建设.要保证软件产品的高质量,优秀的架构,优秀的产品设计,是产生高质量的前提.同时,没有过硬的编码实现,一样得不到预期的效果.纵观现在的产品,产品架构没多大差别,基本运用基线版本进行局点定制.而系统中的一些功能性能常常不过关,问题往往就出在编码实现上.这块是开发人员在开发过程中需要注意的.在JAVA程序中,性能问题的大部分原因并不在于JAVA语言,而是程序本身.养成良

【Java心得总结五】Java容器中——Collection

在[Java心得总结五]Java容器上——容器初探这篇博文中,我对Java容器类库从一个整体的偏向于宏观的角度初步认识了Java容器类库.而在这篇博文中,我想着重对容器类库中的Collection容器做一个着重的探索与总结. Collection:一个独立元素的序列,这些元素都服从一条或多条规则.(注:Collection其实就是将一组数据对象按照一维线性的方式组织起来)List必须按照插入的顺序保存元素,而set不能有重复元素.Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序

Java编程中“为了性能”需做的26件事

摘要:最近的机器内存又爆满了,除了新增机器内存外,还应该好好review一下我们的代码,有很多代码编写过于随意化,这些不好的习惯或对程序语言的不了解是应该好好打压打压了. 本文参考网络资源总结的一些在Java编程中尽可能要做到的一些地方. 最近的机器内存又爆满了,除了新增机器内存外,还应该好好review一下我们的代码,有很多代码编写过于随意化,这些不好的习惯或对程序语言的不了解是应该好好打压打压了. 下面是参考网络资源总结的一些在Java编程中尽可能要做到的一些地方. 1.尽量在合适的场合使用