吐槽,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);
}
上述方法调用的构造方法
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
到此你应该拨云见日豁然开朗。当我们调用字符串 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 到一点点共鸣,如果你比较感兴趣,就多多分享给身边的朋友吧。
吐槽,Java 设计的槽点的更多相关文章
- Swing 是一个为Java设计的GUI工具包
Swing 是一个为Java设计的GUI工具包. Swing是JAVA基础类的一部分. Swing包括了图形用户界面(GUI)器件如:文本框,按钮,分隔窗格和表. Swing提供许多比AWT更好的屏幕 ...
- 使用Java设计验证码生成程序
我们来设计一个简单的验证码生成程序:验证码一个由4位的数字.字母随机组合而成图像,为了避免被光学字元识别(OCR,Optical Character Recognition)之类的程序识别出图片中的数 ...
- 哈希表hashTable的Java设计
1:哈希表的概念 2:设计原理 3:哈希表的Java设计
- 吐槽:那些Java设计中不得不说的槽点
1. 求长度各有千秋 你是否曾经在面试的时候,经常被问到:数组有没有 length() 方法?字符串有没有 length() 方法? 集合有没有 length() 方法? 面对这个问题,那么不得不吐槽 ...
- MapDB:专为Java设计的高性能的数据库
MapDB是一个快速.易用的嵌入式Java数据库引擎,它提供了基于磁盘或者堆外(off-heap允许Java直接操作内存空间, 类似于C的malloc和free)存储的并发的Maps.Sets.Que ...
- java设计原则:16种原则
一 类的设计原则 1 依赖倒置原则-Dependency Inversion Principle (DIP) 2 里氏替换原则-Liskov Substitution Principle (L ...
- java设计原则---开闭原则
开闭原则:指的是一个软件实体应对对扩展开发,对修改关闭(Software entities should be open for extension, but closed for modificat ...
- Java设计模拟菜单
/***** * 设计一个模拟使用菜单的dos应用程序,用户可进行选择,选择错误时重新选择. * @author yanlong * 2017/5/5 */package java3;import j ...
- Java设计原则之依赖倒转原则
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象. 问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成.这种场景下,类A一 ...
随机推荐
- Xml反序列化记录
1.概述 公司项目遇到一个需要对接webservice的,webservice大部分用的都是xml来传输的,这里记录一下xml反序列化遇到的问题 2.xml工具类 xml序列化: public sta ...
- .netCore下的jwt的梳理-->借鉴于“老张的哲学”
之前在公司的项目中有用到jwt进行token验证,但是公司里用的框架已经集成好了jwt,所以对jwt的的了解不够清晰,感觉还是隔着一层.在看了“老张的哲学”的jwt部分后对jwt的认识才更加深刻了一些 ...
- jQuery上传文件按钮美化
效果图如下: 思路: 1:打开文件设置为透明,外面包一层标签,给标签设置颜色背景,给人点击浏览其实是点击打开文件的错觉.(给外标签相对定位,打开文件标签绝对定位). 2:点击浏览后,选择了文件,就把文 ...
- ES6语法:var、let、const的区别详解
今天来说说es6的语法,最基础的也就是var,let,const 的用法与区别了,我们来看看他们之间的恩怨情仇. 首先来说说var,这个只要是学过js的都知道,它是用来声明一个变量的,但是它在开发中也 ...
- 超级干货:动态防御WAF技术原理及编程实战!
本文带给大家的内容是动态防御WAF的技术原理及编程实战. 将通过介绍ShareWAF的核心技术点,向大家展示动态防御的优势.实现思路,并以编程实战的方式向大家展示如何在WAF产品开发过程中应用动态防御 ...
- freecplus框架,Linux平台下C/C++程序员提高开发效率的利器
目录 一.freecplus框架简介 二.freecplus开源许可协议 三.freecplus框架内容 字符串操作 2.xml解析 3.日期时间 4.目录操作 5.文件操作 6.日志文件 7.参数文 ...
- Python基础篇(四)_组合数据类型的基本概念
Python基础篇——组合数据类型的基本概念 集合类型:元素的集合,元素之间无序 序列类型:是一个元素向量,元素之间存在先后关系,通过序号进行访问,没有排他性,具体包括字符串类型.元组类型.列表类型 ...
- 阅读GitHub源码的正确打开方式
前言 近来发现阅读开源项目上手就整最新的代码不合适,缺少项目迭代的具体实现过程,想着若是可以看到针对问题的提交代码就好了,所以就有了本篇博客. 以文主要涉及:如何fork开源项目,如何保证本地仓库代码 ...
- LeetCode43,一题让你学会高精度算法
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode系列第22篇文章,今天讲的内容是高精度算法. 今天和大家讨论的算法是高精度,对应的LeetCode是第43题.题面其实 ...
- npm和yarn使用
npm和yarn使用 他们都属于js包管理工具,都可以安装包或者模块yarn 是由facebook.google等联合开发推出的 区别: npm 下载包的话 比如npm install,它是按照包的排 ...