Java编程最差实践
刘达人186
2024-09-16 03:12:55
原文
原文地址:http://www.odi.ch/prog/design/newbies.php
每天在写Java程序, 其实里面有一些细节大家可能没怎么注意, 这不, 有人总结了一个我们编程中常见的问题. 虽然一般没有什么大问题, 但是最好别这样做. 另外这里提到的很多问题其实可以通过Findbugs( http://findbugs.sourceforge.net/ )来帮我们进行检查出来.
转载说明:
); //remove first comma
正确的写法:
- StringBuilder sb = new StringBuilder(persons.size() * 16); // well estimated buffer
- for (Person p : persons) {
- if (sb.length() > 0) sb.append(", ");
- sb.append(p.getName);
- }
);
sb.append("Name: ");
sb.append(name);
sb.append("\n!");
String s = sb.toString();
或者这样写:
- String s = "Name: " + name + "\n!";
) ...
if (name == "John") ...
if (name.equals("John")) ...
if ("".equals(name)) ...
上面的代码没有错, 但是不够好. compareTo不够简洁, ==原义是比较两个对象是否一样. 另外比较字符是否为空, 最好判断它的长度.
正确的写法:
- if ("John".equals(name)) ...
- if (name.length() == 0) ...
- if (name.isEmpty()) ...
);
return Boolean.valueOf("true");
正确的写法:
- zero = Integer.valueOf(0);
- return Boolean.TRUE;
) {
...
}
上面的代码是一个byte一个byte的读取, 导致频繁的本地JNI文件系统访问, 非常低效, 因为调用本地方法是非常耗时的. 最好用BufferedInputStream包装一下. 曾经做过一个测试, 从/dev/zero下读取1MB, 大概花了1s, 而用BufferedInputStream包装之后只需要60ms, 性能提高了94%! 这个也适用于output stream操作以及socket操作.
正确的写法:
- InputStream in = new BufferedInputStream(new FileInputStream(file));
); // fail after 20s
InputStream in = socket.getInputStream();
socket.setSoTimeout(15000);
int i = in.read();
另外, 文件的读取(FileInputStream, FileChannel, FileDescriptor, File)没法指定超时时间, 而且IO操作均涉及到本地方法调用, 这个更操作了JVM的控制范围, 在分布式文件系统中, 对IO的操作内部实际上是网络调用. 一般情况下操作60s的操作都可以认为已经超时了. 为了解决这些问题, 一般采用缓存和异步/消息队列处理.
;
private String name = null;
private boolean important = false;
}
这里的变量会在初始化时使用默认值:0, null, false, 因此上面的写法有些多此一举.
正确的写法:
- public class B {
- private int count;
- private String name;
- private boolean important;
- }
+ (int) (collection.size() / 0.75));
));
codes.add(Integer.valueOf(20));
codes.add(Integer.valueOf(30));
codes.add(Integer.valueOf(40));
正确的写法:
- int[] codes = { 10, 20, 30, 40 };
错误的写法:
- // horribly slow and a memory waster if l has a few thousand elements (try it yourself!)
- List<Mergeable> l = ...;
- for (int i=0; i < l.size()-1; i++) {
- Mergeable one = l.get(i);
- Iterator<Mergeable> j = l.iterator(i+1); // memory allocation!
- while (j.hasNext()) {
- Mergeable other = l.next();
- if (one.canMergeWith(other)) {
- one.merge(other);
- other.remove();
- }
- }
- }
正确的写法:
- // quite fast and no memory allocation
- Mergeable[] l = ...;
- for (int i=0; i < l.length-1; i++) {
- Mergeable one = l[i];
- for (int j=i+1; j < l.length; j++) {
- Mergeable other = l[j];
- if (one.canMergeWith(other)) {
- one.merge(other);
- l[j] = null;
- }
- }
- }
实际上Sun也意识到这一点, 因此在JDK中, Collections.sort()就是将一个List拷贝到一个数组中然后调用Arrays.sort方法来执行排序.
);
date = cal.getTime();
这里主要是对date, time, calendar和time zone不了解导致. 而在一个时间上增加8小时, 跟time zone没有任何关系, 所以没有必要使用Calendar, 直接用Date对象即可, 而如果是增加天数的话, 则需要使用Calendar, 因为采用不同的时令制可能一天的小时数是不同的(比如有些DST是23或者25个小时)
正确的写法:
- date = new Date(date.getTime() + 8L * 3600L * 1000L); // add 8 hrs
);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
Date startOfDay = cal.getTime();
这里有两个错误, 一个是没有没有将毫秒归零, 不过最大的错误是没有指定TimeZone, 不过一般的桌面应用没有问题, 但是如果是服务器端应用则会有一些问题, 比如同一时刻在上海和伦敦就不一样, 因此需要指定的TimeZone.
正确的写法:
- Calendar cal = new GregorianCalendar(user.getTimeZone());
- cal.setTime(date);
- cal.set(Calendar.HOUR_OF_DAY, 0);
- cal.set(Calendar.MINUTE, 0);
- cal.set(Calendar.SECOND, 0);
- cal.set(Calendar.MILLISECOND, 0);
- Date startOfDay = cal.getTime();
, Calendar.JANUARY, 15);
Calendar.getInstance()依赖local来选择一个Calendar实现, 不同实现的2009年是不同的, 比如有些Calendar实现就没有January月份.
正确的写法:
- Calendar c = new GregorianCalendar(timeZone);
- c.set(2009, Calendar.JANUARY, 15);
;
String someQuery = "SELECT * FROM ...";
}
很多应用都会定义这样一个全局常量类或接口, 但是为什么这种做法不推荐? 因为这些常量之间基本没有任何关联, 只是因为公用才定义在一起. 但是如果其他组件需要使用这些全局变量, 则必须对该常量类产生依赖, 特别是存在server和远程client调用的场景.
比较好的做法是将这些常量定义在组件内部. 或者局限在一个类库内部.
;
System.out.println((int) b-a);
System.out.println((int) (b-a));
; f-=0.1) {
System.out.println(f);
}
上面的浮点数递减只会无限接近0而不会等于0, 这样会导致上面的for进入死循环. 通常绝不要对float和double使用==操作. 而采用大于和小于操作. 如果java编译器能针对这种情况给出警告. 或者在java语言规范中不支持浮点数类型的==操作就最好了.
正确的写法:
- for (float f = 10f; f>0; f-=0.1) {
- System.out.println(f);
- }
.0f;
for (OrderLine line : lines) {
total += line.price * line.count;
}
double a = 1.14 * 75; // 85.5 将表示为 85.4999...
System.out.println(Math.round(a)); // 输出值为85
BigDecimal d = new BigDecimal(1.14); //造成精度丢失
这个也是一个老生常谈的错误. 比如计算100笔订单, 每笔0.3元, 最终的计算结果是29.9999971. 如果将float类型改为double类型, 得到的结果将是30.000001192092896. 出现这种情况的原因是, 人类和计算的计数方式不同. 人类采用的是十进制, 而计算机是二进制.二进制对于计算机来说非常好使, 但是对于涉及到精确计算的场景就会带来误差. 比如银行金融中的应用.
因此绝不要用浮点类型来保存money数据. 采用浮点数得到的计算结果是不精确的. 即使与int类型做乘法运算也会产生一个不精确的结果.那是因为在用二进制存储一个浮点数时已经出现了精度丢失. 最好的做法就是用一个string或者固定点数来表示. 为了精确, 这种表示方式需要指定相应的精度值.
BigDecimal就满足了上面所说的需求. 如果在计算的过程中精度的丢失超出了给定的范围, 将抛出runtime exception.
正确的写法:
- BigDecimal total = BigDecimal.ZERO;
- for (OrderLine line : lines) {
- BigDecimal price = new BigDecimal(line.price);
- BigDecimal count = new BigDecimal(line.count);
- total = total.add(price.multiply(count)); // BigDecimal is immutable!
- }
- total = total.setScale(2, RoundingMode.HALF_UP);
- BigDecimal a = (new BigDecimal("1.14")).multiply(new BigDecimal(75)); // 85.5 exact
- a = a.setScale(0, RoundingMode.HALF_UP); // 86
- System.out.println(a); // correct output: 86
- BigDecimal a = new BigDecimal("1.14");
, plate);
rs = s.executeQuery();
if (rs.next()) {
car = new Car();
car.make = rs.getString(1);
car.color = rs.getString(2);
}
} finally {
if (rs != null) try { rs.close(); } catch (SQLException e) { }
if (s != null) try { s.close(); } catch (SQLException e) { }
if (c != null) try { c.close(); } catch (SQLException e) { }
}
return car;
}
);
} catch (InterruptedException e) {
// ok
}
or
while (true) {
if (Thread.interrupted()) break;
}
这里主要是interrupted静态方法除了返回当前线程的中断状态, 还会将当前线程状态复位.
正确的写法:
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- or
- while (true) {
- if (Thread.currentThread().isInterrupted()) break;
- }
在静态变量初始化时创建线程
错误的写法:
- class Cache {
- private static final Timer evictor = new Timer();
- }
Timer构造器内部会new一个thread, 而该thread会从它的父线程(即当前线程)中继承各种属性. 比如context classloader, threadlocal以及其他的安全属性(访问权限). 而加载当前类的线程可能是不确定的, 比如一个线程池中随机的一个线程. 如果你需要控制线程的属性, 最好的做法就是将其初始化操作放在一个静态方法中, 这样初始化将由它的调用者来决定.
正确的做法:
- class Cache {
- private static Timer evictor;
- public static setupEvictor() {
- evictor = new Timer();
- }
- }
已取消的定时器任务依然持有状态
错误的写法:
- final MyClass callback = this;
- TimerTask task = new TimerTask() {
- public void run() {
- callback.timeout();
- }
- };
- timer.schedule(task, 300000L);
- try {
- doSomething();
- } finally {
- task.cancel();
- }
上面的task内部包含一个对外部类实例的应用, 这将导致该引用可能不会被GC立即回收. 因为Timer将保留TimerTask在指定的时间之后才被释放. 因此task对应的外部类实例将在5分钟后被回收.
正确的写法:
- TimerTask task = new Job(this);
- timer.schedule(task, 300000L);
- try {
- doSomething();
- } finally {
- task.cancel();
- }
- static class Job extends TimerTask {
- private MyClass callback;
- public Job(MyClass callback) {
- this.callback = callback;
- }
- public boolean cancel() {
- callback = null;
- return super.cancel();
- }
- public void run() {
- if (callback == null) return;
- callback.timeout();
- }
- }
- Java编程最差实践常见问题详细说明(1)转
Java编程最差实践常见问题详细说明(1)转 原文地址:http://www.odi.ch/prog/design/newbies.php 每天在写Java程序, 其实里面有一些细节大家可能没 ...
- Java编程最差实践常见问题详细说明(2)转
Java编程最差实践常见问题详细说明(2)转 2012-12-13 13:57:20| 分类: JAVA | 标签:java |举报|字号 订阅 反射使用不当 错误的写法: Java代 ...
- Java编程最差实践(常见编程错误典范)
转载自 http://macrochen.iteye.com/blog/1393502 每天在写Java程序,其实里面有一些细节大家可能没怎么注意,这不,有人总结了一个我们编程中常见的问题.虽然一般 ...
- Java编程最差代码
字符串连接误用 错误的写法: String s = ""; for (Person p : persons) { s += ", " + p.getName( ...
- [Java并发编程(四)] Java volatile 的理论实践
[Java并发编程(四)] Java volatile 的理论实践 摘要 Java 语言中的 volatile 变量可以被看作是一种 "程度较轻的 synchronized":与 ...
- Java 编程中关于异常处理的 10 个最佳实践
异常处理是Java 开发中的一个重要部分.它是关乎每个应用的一个非功能性需求,是为了处理任何错误状况,比如资源不可访问,非法输入,空输入等等.Java提供了几个异常处理特性,以try,catch 和 ...
- 零基础从实践出发学java编程【总结篇】
1.背景 很多人学习java的第一步就是系统的学习java基础语法,有的java基础语法还没学完就崩溃了,确实java基础语法太多太细,而且都是理论,学着让人很懵: 好不容易学完基础语法,又要学框架. ...
- 【转】JAVA学习笔记----PL/SQL最差实践
1. 超长的PL/SQL代码 影响:可维护性,性能 症状: 在复杂的企业应用中,存在动辄成百上千行的存储过程或上万行的包.为什么是最差: 太长的PL/SQL代码不利于阅读,第三方工 ...
- Java编程性能优化一
转自:http://my.oschina.net/xianggao/blog/77224 在JAVA程序中,性能问题的大部分原因并不在于JAVA语言,而是程序本身.养成良好的编码习惯非常重要,能够显著 ...
随机推荐
- solr学习四(关于性能的杂知识)
将所有只用于搜索的,而不需要作为结果的field(特别是一些比较大的field)的stored设置为false 比如我们在solr中index了一篇word,对于这篇word,我们只需要这篇文章的下载 ...
- 模板引擎jade学习
语言参考 标签列表 doctype Tags Block Expansion Attributes Boolean Attributes Class Attributes Class Literal ...
- netty异步
通俗理解:http://lingnanlu.github.io/2016/08/16/netty-asyc-callback 异步的小demo:https://blog.csdn.net/coder_ ...
- 优化Django ORM中的性能问题(含prefetch_related 和 select_related)
Django是个好工具,使用的很广泛. 在应用比较小的时候,会觉得它很快,但是随着应用复杂和壮大,就显得没那么高效了.当你了解所用的Web框架一些内部机制之后,才能写成比较高效的代码. 怎么查问题 W ...
- centos 修改时区及NTP时间同步
在我们使用CentOS系统的时候,也许时区经常会出现问题,有时候改完之后还是会出错,下面我们就来学习一种方法来改变这个状况.如果没有安装,而你使用的是 CentOS系统 那使用命令 yum insta ...
- Scrapy下xpath基本的使用方法
Scrapy是基于python的开源爬虫框架,使用起来也比较方便.具体的官网档:http://doc.scrapy.org/en/latest/ 之前以为了解python就可以直接爬网站了,原来还要了 ...
- opencv读取并播放avi视屏
视屏的本质是一些静态的图像的集合,opencv可以不断读取视屏中的图片,显示,就产生了类似电影的效果. 这样也就可以通过opencv对实时的视屏流进行处理了. #include "stdaf ...
- Qt常用类及类方法简介之 QAction类
1.QAction::QAction ( const QString & text, QObject * parent ) QAction类的构造函数之一,利用text,parent创建 ...
- <<精通正在表达式>> 书评
IT产业新技术日新月异,令人目不暇给,然而在这其中,真正称得上伟大东西的却寥寥无几.1998年,被誉为“软件世界的爱迪生”,发明了BSD. TCP/IP.csh.vi和NFS的SUN首席科学家Bil ...
- webstorm打开后无法显示文件夹目录
最近接触webstorm,上午研究怎么删除项目,不小心把项目在目录中删除了,然后重新创建时,无法显示了. 状况类似这样的 百度上搜了一些没搜到,关键字是“webstorm 项目 目录 无法显示文件夹” ...