Java编程最差实践(一)

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

字符串连接误用

 错误的写法:

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

正确的写法:

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

错误的使用StringBuffer

错误的写法:

1StringBuffer sb = new StringBuffer();   2sb.append("Name: ");   3sb.append(name + '\n');   4sb.append("!");   5...   6String s = sb.toString();

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

正确的写法:

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

或者这样写:

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

测试字符串相等性

错误的写法:

1if (name.compareTo("John") == 0) ...   2if (name == "John") ...   3if (name.equals("John")) ...   4if ("".equals(name)) ...

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

正确的写法:

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

数字转换成字符串

错误的写法:

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

正确的写法:

String.valueOf(set.size())

利用不可变对象(Immutable)

错误的写法:

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

正确的写法:

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

请使用XML解析器

错误的写法:

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

正确的写法:

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

请使用JDom组装XML

错误的写法:

1String name = ...   2String attribute = ...   3String xml = "<root>"   4            +"<name att=\""+ attribute +"\">"+ name +"</name>"   5            +"</root>";

正确的写法:

1Element root = new Element("root");   2root.setAttribute("att", attribute);   3root.setText(name);   4Document doc = new Documet();   5doc.setRootElement(root);   6XmlOutputter out = new XmlOutputter(Format.getPrettyFormat());   7String xml = out.outputString(root);

XML编码陷阱

错误的写法:

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

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

未指定字符编码

错误的写法:

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

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

正确的写法:

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

未对数据流进行缓存

错误的写法:

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

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

正确的写法:

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

无限使用heap内存

错误的写法:

byte[] pdf = toPdf(file);

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

正确的写法:

File pdf = toPdf(file);

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

不指定超时时间

错误的代码:

1Socket socket = ...   2socket.connect(remote);   3InputStream in = socket.getInputStream();   4int i = in.read();

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

正确的写法:

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

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

频繁使用计时器

错误的代码:

1for (...) {   2  long t = System.currentTimeMillis();   3  long t = System.nanoTime();   4  Date d = new Date();   5  Calendar c = new GregorianCalendar();   6}

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

正确的写法:

1Date d = new Date();   2for (E entity : entities) {   3  entity.doSomething();   4  entity.setUpdated((Date) d.clone());   5}

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

1private volatile long time;   2Timer timer = new Timer(true);   3try {   4  time = System.currentTimeMillis();   5  timer.scheduleAtFixedRate(new TimerTask() {   6    public void run() {   7      time = System.currentTimeMillis();   8    }   9  }, 0L, 10L); // granularity 10ms   10  for (E entity : entities) {   11     entity.doSomething();   12     entity.setUpdated(new Date(time));   13  }   14} finally {   15  timer.cancel();   16}
点赞
收藏

评论区

加载中...

相关推荐

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

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java日期时间API系列31

  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )