Java编程常见问题汇总.doc_第1页
Java编程常见问题汇总.doc_第2页
Java编程常见问题汇总.doc_第3页
Java编程常见问题汇总.doc_第4页
Java编程常见问题汇总.doc_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

软件英才网 软件行业驰名招聘网站Java编程:常见问题汇总每天在写Java程序,其实里面有一些细节大家可能没怎么注意,这不,有人总结了一个我们编程中常见的问题。虽然一般没有什么大问题,但是最好别这样做。另外这里提到的很多问题其实可以通过Findbugs( / )来帮我们进行检查出来。字符串连接误用错误的写法:1 String s = ; 2 for (Person p : persons) 3 s += , + p.getName(); 4 5 s = s.substring(2); /remove first comma 正确的写法:6 StringBuilder sb = new StringBuilder(persons.size() * 16); / well estimated buffer 7 for (Person p : persons) 8 if (sb.length() 0) sb.append(, ); 9 sb.append(p.getName); 10 错误的使用StringBuffer错误的写法:11 StringBuffer sb = new StringBuffer(); 12 sb.append(Name: ); 13 sb.append(name + n); 14 sb.append(!); 15 . 16 String s = sb.toString(); 问题在第三行,append char比String性能要好,另外就是初始化StringBuffer没有指定size,导致中间append时可能重新调整内部数组大小。如果是JDK1.5最好用StringBuilder取代StringBuffer,除非有线程安全的要求。还有一种方式就是可以直接连接字符串。缺点就是无法初始化时指定长度。正确的写法:17 StringBuilder sb = new StringBuilder(100); 18 sb.append(Name: ); 19 sb.append(name); 20 sb.append(n!); 21 String s = sb.toString(); 或者这样写:22 String s = Name: + name + n!; 测试字符串相等性错误的写法:23 if (pareTo(John) = 0) . 24 if (name = John) . 25 if (name.equals(John) . 26 if (.equals(name) . 上面的代码没有错,但是不够好。compareTo不够简洁,=原义是比较两个对象是否一样。另外比较字符是否为空,最好判断它的长度。正确的写法:27 if (John.equals(name) . 28 if (name.length() = 0) . 29 if (name.isEmpty() . 数字转换成字符串错误的写法:30 + set.size() 31 new Integer(set.size().toString() 正确的写法:32 String.valueOf(set.size() 利用不可变对象(Immutable)错误的写法:33 zero = new Integer(0); 34 return Boolean.valueOf(true); 正确的写法:35 zero = Integer.valueOf(0); 36 return Boolean.TRUE; 请使用XML解析器错误的写法:37 int start = xml.indexOf() + .length(); 38 int end = xml.indexOf(); 39 String name = xml.substring(start, end); 正确的写法:40 SAXBuilder builder = new SAXBuilder(false); 41 Document doc = doc = builder.build(new StringReader(xml); 42 String name = doc.getRootElement().getChild(name).getText(); 请使用JDom组装XML错误的写法:43 String name = . 44 String attribute = . 45 String xml = 46 + name + 47 +; 正确的写法:48 Element root = new Element(root); 49 root.setAttribute(att, attribute); 50 root.setText(name); 51 Document doc = new Documet(); 52 doc.setRootElement(root); 53 XmlOutputter out = new XmlOutputter(Format.getPrettyFormat(); 54 String xml = out.outputString(root); XML编码陷阱错误的写法:55 String xml = FileUtils.readTextFile(my.xml); 因为xml的编码在文件中指定的,而在读文件的时候必须指定编码。另外一个问题不能一次就将一个xml文件用String保存,这样对内存会造成不必要的浪费,正确的做法用InputStream来边读取边处理。为了解决编码的问题, 最好使用XML解析器来处理。未指定字符编码错误的写法:56 Reader r = new FileReader(file); 57 Writer w = new FileWriter(file); 58 Reader r = new InputStreamReader(inputStream); 59 Writer w = new OutputStreamWriter(outputStream); 60 String s = new String(byteArray); / byteArray is a byte 61 byte a = string.getBytes(); 这样的代码主要不具有跨平台可移植性。因为不同的平台可能使用的是不同的默认字符编码。正确的写法:62 Reader r = new InputStreamReader(new FileInputStream(file), ISO-8859-1); 63 Writer w = new OutputStreamWriter(new FileOutputStream(file), ISO-8859-1); 64 Reader r = new InputStreamReader(inputStream, UTF-8); 65 Writer w = new OutputStreamWriter(outputStream, UTF-8); 66 String s = new String(byteArray, ASCII); 67 byte a = string.getBytes(ASCII); 未对数据流进行缓存错误的写法:68 InputStream in = new FileInputStream(file); 69 int b; 70 while (b = in.read() != -1) 71 . 72 上面的代码是一个byte一个byte的读取,导致频繁的本地JNI文件系统访问,非常低效,因为调用本地方法是非常耗时的。最好用BufferedInputStream包装一下。曾经做过一个测试,从/dev/zero下读取1MB,大概花了1s,而用BufferedInputStream包装之后只需要60ms,性能提高了94%! 这个也适用于output stream操作以及socket操作。正确的写法:73 InputStream in = new BufferedInputStream(new FileInputStream(file); 无限使用heap内存错误的写法:74 byte pdf = toPdf(file); 这里有一个前提,就是文件大小不能讲JVM的heap撑爆。否则就等着OOM吧,尤其是在高并发的服务器端代码。最好的做法是采用Stream的方式边读取边存储(本地文件或database)。正确的写法:75 File pdf = toPdf(file); 另外,对于服务器端代码来说,为了系统的安全,至少需要对文件的大小进行限制。不指定超时时间错误的代码:76 Socket socket = . 77 socket.connect(remote); 78 InputStream in = socket.getInputStream(); 79 int i = in.read(); 这种情况在工作中已经碰到不止一次了。个人经验一般超时不要超过20s。这里有一个问题,connect可以指定超时时间,但是read无法指定超时时间。但是可以设置阻塞(block)时间。正确的写法:80 Socket socket = . 81 socket.connect(remote, 20000); / fail after 20s 82 InputStream in = socket.getInputStream(); 83 socket.setSoTimeout(15000); 84 int i = in.read(); 另外,文件的读取(FileInputStream, FileChannel, FileDescriptor, File)没法指定超时时间, 而且IO操作均涉及到本地方法调用, 这个更操作了JVM的控制范围,在分布式文件系统中,对IO的操作内部实际上是网络调用。一般情况下操作60s的操作都可以认为已经超时了。为了解决这些问题,一般采用缓存和异步/消息队列处理。频繁使用计时器错误代码:85 for (.) 86 long t = System.currentTimeMillis(); 87 long t = System.nanoTime(); 88 Date d = new Date(); 89 Calendar c = new GregorianCalendar(); 90 每次new一个Date或Calendar都会涉及一次本地调用来获取当前时间(尽管这个本地调用相对其他本地方法调用要快)。如果对时间不是特别敏感,这里使用了clone方法来新建一个Date实例。这样相对直接new要高效一些。正确的写法:91 Date d = new Date(); 92 for (E entity : entities) 93 entity.doSomething(); 94 entity.setUpdated(Date) d.clone(); 95 如果循环操作耗时较长(超过几ms),那么可以采用下面的方法,立即创建一个Timer,然后定期根据当前时间更新时间戳,在我的系统上比直接new一个时间对象快200倍:96 private volatile long time; 97 Timer timer = new Timer(true); 98 try 99 time = System.currentTimeMillis(); 100 timer.scheduleAtFixedRate(new TimerTask() 101 public void run() 102 time = System.currentTimeMillis(); 103 104 , 0L, 10L); / granularity 10ms 105 for (E entity : entities) 106 entity.doSomething(); 107 entity.setUpdated(new Date(time); 108 109 finally 110 timer.cancel(); 111 捕获所有的异常错误的写法:112 Query q = . 113 Person p; 114 try 115 p = (Person) q.getSingleResult(); 116 catch(Exception e) 117 p = null; 118 这是EJB3的一个查询操作,可能出现异常的原因是:结果不唯一;没有结果;数据库无法访问,而捕获所有的异常,设置为null将掩盖各种异常情况。正确的写法:119 Query q = . 120 Person p; 121 try 122 p = (Person) q.getSingleResult(); 123 catch(NoResultException e) 124 p = null; 125 忽略所有异常错误的写法:126 try 127 doStuff(); 128 catch(Exception e) 129 log.fatal(Could not do stuff); 130 131 doMoreStuff(); 这个代码有两个问题, 一个是没有告诉调用者, 系统调用出错了. 第二个是日志没有出错原因, 很难跟踪定位问题。正确的写法:132 try 133 doStuff(); 134 catch(Exception e) 135 throw new MyRuntimeException(Could not do stuff because: + e.getMessage, e); 136 重复包装RuntimeException错误的写法:137 try 138 doStuff(); 139 catch(Exception e) 140 throw new RuntimeException(e); 141 正确的写法:142 try 143 doStuff(); 144 catch(RuntimeException e) 145 throw e; 146 catch(Exception e) 147 throw new RuntimeException(e.getMessage(), e); 148 149 try 150 doStuff(); 151 catch(IOException e) 152 throw new RuntimeException(e.getMessage(), e); 153 catch(NamingException e) 154 throw new RuntimeException(e.getMessage(), e); 155 不正确的传播异常错误的写法:156 try 157 catch(ParseException e) 158 throw new RuntimeException(); 159 throw new RuntimeException(e.toString(); 160 throw new RuntimeException(e.getMessage(); 161 throw new RuntimeException(e); 162 主要是没有正确的将内部的错误信息传递给调用者. 第一个完全丢掉了内部错误信息, 第二个错误信息依赖toString方法, 如果没有包含最终的嵌套错误信息, 也会出现丢失, 而且可读性差. 第三个稍微好一些, 第四个跟第二个一样。正确的写法:163 try 164 catch(ParseException e) 165 throw new RuntimeException(e.getMessage(), e); 166 用日志记录异常错误的写法:167 try 168 . 169 catch(ExceptionA e) 170 log.error(e.getMessage(), e); 171 throw e; 172 catch(ExceptionB e) 173 log.error(e.getMessage(), e); 174 throw e; 175 一般情况下在日志中记录异常是不必要的, 除非调用方没有记录日志。异常处理不彻底错误的写法:176 try 177 is = new FileInputStream(inFile); 178 os = new FileOutputStream(outFile); 179 finally 180 try 181 is.close(); 182 os.close(); 183 catch(IOException e) 184 /* we cant do anything */ 185 186 is可能close失败, 导致os没有close正确的写法:187 try 188 is = new FileInputStream(inFile); 189 os = new FileOutputStream(outFile); 190 finally 191 try if (is != null) is.close(); catch(IOException e) /* we cant do anything */ 192 try if (os != null) os.close(); catch(IOException e) /* we cant do anything */ 193 捕获不可能出现的异常错误的写法:194 try 195 . do risky stuff . 196 catch(SomeException e) 197 / never happens 198 199 . do some more . 正确的写法:200 try 201 . do risky stuff . 202 catch(SomeException e) 203 / never happens hopefully 204 throw new IllegalStateException(e.getMessage(), e); / crash early, passing all information 205 206 . do some more . transient的误用错误的写法:207 public class A implements Serializable 208 private String someState; 209 private transient Log log = LogFactory.getLog(getClass(); 210 211 public void f() 212 log.debug(enter f); 213 . 214 215 这里的本意是不希望Log对象被序列化. 不过这里在反序列化时, 会因为log未初始化, 导致f()方法抛空指针, 正确的做法是将log定义为静态变量或者定位为具备变量。正确的写法:216 public class A implements Serializable 217 private String someState; 218 private static final Log log = LogFactory.getLog(A.class); 219 220 public void f() 221 log.debug(enter f); 222 . 223 224 225 public class A implements Serializable 226 private String someState; 227 228 public void f() 229 Log log = LogFactory.getLog(getClass(); 230 log.debug(enter f); 231 . 232 233 不必要的初始化错误的写法:234 public class B 235 private int count = 0; 236 private String name = null; 237 private boolean important = false; 238 这里的变量会在初始化时使用默认值:0, null, false, 因此上面的写法有些多此一举。正确的写法:239 public class B 240 private int count; 241 private String name; 242 private boolean important; 243 最好用静态final定义Log变量244 private static final Log log = LogFactory.getLog(MyClass.class); 这样做的好处有三:可以保证线程安全静态或非静态代码都可用不会影响对象序列化选择错误的类加载器错误的代码:245 Class clazz = Class.forName(name); 246 Class clazz = getClass().getClassLoader().loadClass(name); 这里本意是希望用当前类来加载希望的对象, 但是这里的getClass()可能抛出异常, 特别在一些受管理的环境中, 比如应用服务器, web容器, Java WebStart环境中, 最好的做法是使用当前应用上下文的类加载器来加载。正确的写法:247 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 248 if (cl = null) cl = MyClass.class.getClassLoader(); / fallback 249 Class clazz = cl.loadClass(name); 反射使用不当错误的写法:250 Class beanClass = . 251 if (beanClass.newInstance() instanceof TestBean) . 这里的本意是检查beanClass是否是TestBean或是其子类, 但是创建一个类实例可能没那么简单, 首先实例化一个对象会带来一定的消耗, 另外有可能类没有定义默认构造函数. 正确的做法是用Class.isAssignableFrom(Class) 方法。正确的写法:252 Class beanClass = . 253 if (TestBean.class.isAssignableFrom(beanClass) . 不必要的同步错误的写法:254 Collection l = new Vector(); 255 for (.) 256 l.add(object); 257 Vector是ArrayList同步版本。正确的写法:258 Collection l = new ArrayList(); 259 for (.) 260 l.add(object); 261 错误的选择List类型根据下面的表格数据来进行选择 ArrayListLinkedListadd (append)O(1) or O(log(n) if growingO(1)insert (middle)O(n) or O(n*log(n) if growingO(n)remove (middle)O(n) (always performs complete copy)O(n)iterateO(n)O(n)get by indexO(1)O(n) HashMap size陷阱错误的写法:262 Map map = new HashMap(collection.size(); 263 for (Object o : collection) 264 map.put(o.key, o.value); 265 这里可以参考guava的Maps.newHashMapWithExpectedSize的实现. 用户的本意是希望给HashMap设置初始值, 避免扩容(resize)的开销. 但是没有考虑当添加的元素数量达到HashMap容量的75%时将出现resize。正确的写法:266 Map map = new HashMap(1 + (int) (collection.size() / 0.75); 对Hashtable, HashMap 和 HashSet了解不够这里主要需要了解HashMap和Hashtable的内部实现上, 它们都使用Entry包装来封装key/value, Entry内部除了要保存Key/Value的引用, 还需要保存hash桶中next Entry的应用, 因此对内存会有不小的开销, 而HashSet内部实现其实就是一个HashMap. 有时候IdentityHashMap可以作为一个不错的替代方案. 它在内存使用上更有效(没有用Entry封装, 内部采用Object). 不过需要小心使用. 它的实现违背了Map接口的定义. 有时候也可以用ArrayList来替换HashSet.这一切的根源都是由于JDK内部没有提供一套高效的Map和Set实现。对List的误用建议下列场景用Array来替代List:list长度固定,比如一周中的每一天对list频繁的遍历,比如超过1w次需要对数字进行包装(主要JDK没有提供基本类型的List)比如下面的代码。错误的写法:267 List codes = new ArrayList(); 268 codes.add(Integer.valueOf(10); 269 codes.add(Integer.valueOf(20); 270 codes.add(Integer.valueOf(30); 271 codes.add(Integer.valueOf(40); 正确的写法:272 int codes = 10, 20, 30, 40 ; 错误的写法:273 / horribly slow and a memory waster if l has a few thousand elements (try it yourself!) 274 List l = .; 275 for (int i=0; i l.size()-1; i+) 276 Mergeable one = l.get(i); 277 Iterator j = l.iterator(i+1); / memory allocation! 278 while (j.hasNext() 279 Mergeable other = l.next(); 280 if (one.canMergeWith(other) 281 one.merge(other); 282 other.remove(); 283 284 285 正确的写法:286 / quite fast and no memory allocation 287 Mergeable l = .; 288 for (int i=0; i l.length-1; i+) 289 Mergeable one = li; 290 for (int j=i+1; j Integer.MAX_VALUE) throw new IllegalStateException(int ov

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论