Java编程最差实践(一)

Wesley13
• 阅读 646

    每天在写Java程序, 其实里面有一些细节大家可能没怎么注意, 这不, 有人总结了一个我们编程中常见的问题. 虽然一般没有什么大问题, 但是最好别这样做.
    另外这里提到的很多问题其实可以通过Findbugs( http://findbugs.sourceforge.net/ )来帮我们进行检查出来.

字符串连接误用

 Java编程最差实践(一) 错误的写法:

String s = "";  
for (Person p : persons) {  
    s += ", " + p.getName();  
}  
s = s.substring(2); //remove first comma

Java编程最差实践(一) 正确的写法:

StringBuilder sb = new StringBuilder(persons.size() * 16); // well estimated buffer  
for (Person p : persons) {  
    if (sb.length() > 0) sb.append(", ");  
    sb.append(p.getName);  
}

错误的使用StringBuffer

Java编程最差实践(一) 错误的写法:

StringBuffer sb = new StringBuffer();  
sb.append("Name: ");  
sb.append(name + '\n');  
sb.append("!");  
...  
String s = sb.toString();

    问题在第三行, append char比String性能要好, 另外就是初始化StringBuffer没有指定size, 导致中间append时可能重新调整内部数组大小. 如果是JDK1.5最好用StringBuilder取代StringBuffer, 除非有线程安全的要求. 还有一种方式就是可以直接连接字符串. 缺点就是无法初始化时指定长度.

Java编程最差实践(一) 正确的写法:

StringBuilder sb = new StringBuilder(100);  
sb.append("Name: ");  
sb.append(name);  
sb.append("\n!");  
String s = sb.toString();

或者这样写:

String s = "Name: " + name + "\n!";

测试字符串相等性

Java编程最差实践(一) 错误的写法:

if (name.compareTo("John") == 0) ...  
if (name == "John") ...  
if (name.equals("John")) ...  
if ("".equals(name)) ...

    上面的代码没有错, 但是不够好. compareTo不够简洁, ==原义是比较两个对象是否一样. 另外比较字符是否为空, 最好判断它的长度.

Java编程最差实践(一) 正确的写法:

if ("John".equals(name)) ...  
if (name.length() == 0) ...  
if (name.isEmpty()) ...

数字转换成字符串

Java编程最差实践(一) 错误的写法:

"" + set.size()  
new Integer(set.size()).toString()

Java编程最差实践(一) 正确的写法:

String.valueOf(set.size())

利用不可变对象(Immutable)

Java编程最差实践(一) 错误的写法:

zero = new Integer(0);  
return Boolean.valueOf("true");

Java编程最差实践(一) 正确的写法:

zero = Integer.valueOf(0);  
return Boolean.TRUE;

请使用XML解析器

Java编程最差实践(一) 错误的写法:

int start = xml.indexOf("<name>") + "<name>".length();  
int end = xml.indexOf("</name>");  
String name = xml.substring(start, end);

Java编程最差实践(一) 正确的写法:

SAXBuilder builder = new SAXBuilder(false);  
Document doc = doc = builder.build(new StringReader(xml));  
String name = doc.getRootElement().getChild("name").getText();

请使用JDom组装XML

Java编程最差实践(一) 错误的写法:

String name = ...  
String attribute = ...  
String xml = "<root>"  
            +"<name att=\""+ attribute +"\">"+ name +"</name>"  
            +"</root>";

Java编程最差实践(一) 正确的写法:

Element root = new Element("root");  
root.setAttribute("att", attribute);  
root.setText(name);  
Document doc = new Documet();  
doc.setRootElement(root);  
XmlOutputter out = new XmlOutputter(Format.getPrettyFormat());  
String xml = out.outputString(root);

XML编码陷阱

Java编程最差实践(一) 错误的写法:

String xml = FileUtils.readTextFile("my.xml");

    因为xml的编码在文件中指定的, 而在读文件的时候必须指定编码. 另外一个问题不能一次就将一个xml文件用String保存, 这样对内存会造成不必要的浪费, 正确的做法用InputStream来边读取边处理. 为了解决编码的问题, 最好使用XML解析器来处理

未指定字符编码

Java编程最差实践(一) 错误的写法:

Reader r = new FileReader(file);  
Writer w = new FileWriter(file);  
Reader r = new InputStreamReader(inputStream);  
Writer w = new OutputStreamWriter(outputStream);  
String s = new String(byteArray); // byteArray is a byte[]  
byte[] a = string.getBytes();

    这样的代码主要不具有跨平台可移植性. 因为不同的平台可能使用的是不同的默认字符编码.

Java编程最差实践(一) 正确的写法:

Reader r = new InputStreamReader(new FileInputStream(file), "ISO-8859-1");  
Writer w = new OutputStreamWriter(new FileOutputStream(file), "ISO-8859-1");  
Reader r = new InputStreamReader(inputStream, "UTF-8");  
Writer w = new OutputStreamWriter(outputStream, "UTF-8");  
String s = new String(byteArray, "ASCII");  
byte[] a = string.getBytes("ASCII");

未对数据流进行缓存

Java编程最差实践(一) 错误的写法:

InputStream in = new FileInputStream(file);  
int b;  
while ((b = in.read()) != -1) {  
   ...  
}

    上面的代码是一个byte一个byte的读取, 导致频繁的本地JNI文件系统访问, 非常低效, 因为调用本地方法是非常耗时的. 最好用BufferedInputStream包装一下. 曾经做过一个测试, 从/dev/zero下读取1MB, 大概花了1s, 而用BufferedInputStream包装之后只需要60ms, 性能提高了94%! 这个也适用于output stream操作以及socket操作.

Java编程最差实践(一) 正确的写法:

InputStream in = new BufferedInputStream(new FileInputStream(file));

无限使用heap内存

Java编程最差实践(一) 错误的写法:

byte[] pdf = toPdf(file);

    这里有一个前提, 就是文件大小不能讲JVM的heap撑爆. 否则就等着OOM吧, 尤其是在高并发的服务器端代码. 最好的做法是采用Stream的方式边读取边存储(本地文件或database).

Java编程最差实践(一) 正确的写法:

File pdf = toPdf(file);

    另外, 对于服务器端代码来说, 为了系统的安全, 至少需要对文件的大小进行限制.

不指定超时时间

Java编程最差实践(一) 错误的代码:

Socket socket = ...  
socket.connect(remote);  
InputStream in = socket.getInputStream();  
int i = in.read();

    这种情况在工作中已经碰到不止一次了. 个人经验一般超时不要超过20s. 这里有一个问题, connect可以指定超时时间, 但是read无法指定超时时间. 但是可以设置阻塞(block)时间.

Java编程最差实践(一) 正确的写法:

Socket socket = ...  
socket.connect(remote, 20000); // fail after 20s  
InputStream in = socket.getInputStream();  
socket.setSoTimeout(15000);  
int i = in.read();

    另外, 文件的读取(FileInputStream, FileChannel, FileDescriptor, File)没法指定超时时间, 而且IO操作均涉及到本地方法调用, 这个更操作了JVM的控制范围, 在分布式文件系统中, 对IO的操作内部实际上是网络调用. 一般情况下操作60s的操作都可以认为已经超时了. 为了解决这些问题, 一般采用缓存和异步/消息队列处理.

频繁使用计时器

Java编程最差实践(一) 错误的代码:

for (...) {  
  long t = System.currentTimeMillis();  
  long t = System.nanoTime();  
  Date d = new Date();  
  Calendar c = new GregorianCalendar();  
}

    每次new一个Date或Calendar都会涉及一次本地调用来获取当前时间(尽管这个本地调用相对其他本地方法调用要快).
    如果对时间不是特别敏感, 这里使用了clone方法来新建一个Date实例. 这样相对直接new要高效一些.

Java编程最差实践(一) 正确的写法:

Date d = new Date();  
for (E entity : entities) {  
  entity.doSomething();  
  entity.setUpdated((Date) d.clone());  
}

    如果循环操作耗时较长(超过几ms), 那么可以采用下面的方法, 立即创建一个Timer, 然后定期根据当前时间更新时间戳, 在我的系统上比直接new一个时间对象快200倍:

private volatile long time;  
Timer timer = new Timer(true);  
try {  
  time = System.currentTimeMillis();  
  timer.scheduleAtFixedRate(new TimerTask() {  
    public void run() {  
      time = System.currentTimeMillis();  
    }  
  }, 0L, 10L); // granularity 10ms  
  for (E entity : entities) {  
     entity.doSomething();  
     entity.setUpdated(new Date(time));  
  }  
} finally {  
  timer.cancel();  
}
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这