Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the common problems in Java programming

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article introduces the knowledge of "what are the common problems in Java programming?" in the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Misuse of string concatenation

Wrong way of writing:

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

The correct way to write:

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

Incorrect use of StringBuffer

Wrong way of writing:

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

The problem is on the third line, append char performs better than String, and there is no size specified for initializing StringBuffer, which may resize the internal array when you are in the middle of append. In the case of JDK1.5, it is best to replace StringBuffer with StringBuilder, unless there is a thread safety requirement. Another way is to concatenate strings directly. The disadvantage is that the length cannot be initialized.

The correct way to write:

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

Or write something like this:

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

Test string equality

Wrong way of writing:

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

The above code is not wrong, but not good enough. CompareTo is not concise enough, and the original meaning of = = is to compare whether two objects are the same. In addition, if you compare whether a character is empty, it is best to judge its length.

The correct way to write:

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

Convert numbers to strings

Wrong way of writing:

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

The correct way to write:

String.valueOf (set.size ())

Using immutable objects (Immutable)

Wrong way of writing:

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

The correct way to write:

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

Please use the XML parser

Wrong way of writing:

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

The correct way to write:

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

Please use JDom to assemble XML

Wrong way of writing:

String name =... String attribute =... String xml = "" + "" + name + "" + ""

The correct way to write:

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 coding trap

Wrong way of writing:

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

Because the encoding of the xml is specified in the file, the encoding must be specified when reading the file. Another problem is that you can't save a xml file with String at a time, which will cause unnecessary waste of memory. The correct way to do this is to use InputStream to read while processing. To solve the coding problem, it is best to use a XML parser to handle it.

No character encoding specified

Wrong way of writing:

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 ()

Such code is mainly not portable across platforms. Because different platforms may use different default character encodings.

The correct way to write:

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")

The data stream is not cached

Wrong way of writing:

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

The above code is a byte-by-byte read, resulting in frequent local JNI file system access, which is very inefficient because it is very time-consuming to call local methods. You'd better wrap it in BufferedInputStream. I once did a test that it took about 1 second to read 1MB from / dev/zero, but only 60ms was needed after packaging with BufferedInputStream, which improved the performance by 94%! This also applies to output stream operations as well as socket operations.

The correct way to write:

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

Unlimited use of heap memory

Wrong way of writing:

Byte [] pdf = toPdf (file)

There is a premise here, that is, the file size can not talk about JVM's heap burst. Otherwise, wait for OOM, especially in highly concurrent server-side code. The best approach is to use Stream to read and store (local files or database) at the same time.

The correct way to write:

File pdf = toPdf (file)

In addition, for server-side code, for the security of the system, at least the size of the file needs to be limited.

Do not specify timeout

Wrong code:

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

This situation has been encountered more than once at work. Generally speaking, personal experience should not exceed 20s. There is a problem here. Connect can specify the timeout, but read cannot specify the timeout. However, you can set the block time.

The correct way to write:

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

In addition, the reading of files (FileInputStream, FileChannel, FileDescriptor, File) cannot specify the timeout, and IO operations all involve local method calls, which operate within the control scope of JVM. In distributed file systems, the operation on IO is actually a network call. In general, an operation of 60s can be considered to have timed out. In order to solve these problems, caching and asynchronous / message queuing processing are generally used.

Frequent use of timers

Error code:

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

One Date or Calendar per new involves a local call to get the current time (although this local call is faster than other local method calls).

If it is not particularly time-sensitive, the clone method is used here to create a new Date instance. This is more efficient than direct new.

The correct way to write:

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

If the loop takes a long time (more than a few ms), you can create a Timer immediately and then update the timestamp periodically based on the current time, which is 200 times faster than directly new a time object on my system:

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 ();}

Catch all exceptions

Wrong way of writing:

Query Q =... Person p; try {p = (Person) q.getSingleResult ();} catch (Exception e) {p = null;}

This is a query operation of EJB3, and the possible reasons for exceptions are: the result is not unique; there is no result; the database cannot be accessed, and all exceptions are caught, and setting to null will mask all kinds of exceptions.

The correct way to write:

Query Q =... Person p; try {p = (Person) q.getSingleResult ();} catch (NoResultException e) {p = null;}

Ignore all exceptions

Wrong way of writing:

Try {doStuff ();} catch (Exception e) {log.fatal ("Could not do stuff");} doMoreStuff ()

There are two problems with this code, one is that it does not tell the caller that the system call has gone wrong. The second is that there is no cause of error in the log, so it is difficult to track and locate the problem.

The correct way to write:

Try {doStuff ();} catch (Exception e) {throw new MyRuntimeException ("Could not do stuff because:" + e.getMessage, e);}

Repackaged RuntimeException

Wrong way of writing:

Try {doStuff ();} catch (Exception e) {throw new RuntimeException (e);}

The correct way to write:

Try {doStuff ();} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new RuntimeException (e.getMessage (), e);} try {doStuff ();} catch (IOException e) {throw new RuntimeException (e.getMessage (), e);} catch (NamingException e) {throw new RuntimeException (e.getMessage (), e);}

Incorrect propagation anomaly

Wrong way of writing:

Try {} catch (ParseException e) {throw new RuntimeException (); throw new RuntimeException (e.toString ()); throw new RuntimeException (e.getMessage ()); throw new RuntimeException (e);}

The main reason is that the internal error message is not passed on to the caller correctly. The first one completely discards the internal error message, and the second error message depends on the toString method. If it does not contain the final nested error message, it will be lost and has poor readability. The third is a little better, and the fourth is the same as the second.

The correct way to write:

Try {} catch (ParseException e) {throw new RuntimeException (e.getMessage (), e);}

Log exceptions

Wrong way of writing:

Try {...} catch (ExceptionA e) {log.error (e.getMessage (), e); throw e;} catch (ExceptionB e) {log.error (e.getMessage (), e); throw e;}

In general, it is not necessary to record exceptions in the log unless the caller does not log.

Exception handling is not complete

Wrong way of writing:

Try {is = new FileInputStream (inFile); os = new FileOutputStream (outFile);} finally {try {is.close (); os.close ();} catch (IOException e) {/ * we can't do anything * /}}

Is may fail to close, resulting in os without close

The correct way to write:

Try {is = new FileInputStream (inFile); os = new FileOutputStream (outFile);} finally {try {if (is! = null) is.close ();} catch (IOException e) {/ * we can't do anything * /} try {if (os! = null) os.close ();} catch (IOException e) {/ * we can't do anything * /}}

Catch impossible exceptions

Wrong way of writing:

Try {... Do risky stuff...} catch (SomeException e) {/ / never happens}. Do some more...

The correct way to write:

Try {... Do risky stuff...} catch (SomeException e) {/ / never happens hopefully throw new IllegalStateException (e.getMessage (), e); / / crash early, passing all information}. Do some more...

Misuse of transient

Wrong way of writing:

Public class An implements Serializable {private String someState; private transient Log log = LogFactory.getLog (getClass ()); public void f () {log.debug ("enter f");...}}

The intention here is that you don't want Log objects to be serialized. However, when deserialization occurs here, the f () method will empty the pointer because log is not initialized. The correct thing to do is to define log as a static variable or locate it as having a variable.

The correct way to write:

Public class An implements Serializable {private String someState; private static final Log log = LogFactory.getLog (A.class); public void f () {log.debug ("enter f");...}} public class An implements Serializable {private String someState; public void f () {Log log = LogFactory.getLog (getClass ()); log.debug ("enter f");...}}

Unnecessary initialization

Wrong way of writing:

Public class B {private int count = 0; private String name = null; private boolean important = false;}

The variables here will be initialized with default values: 0, null, false, so the above is a bit superfluous.

The correct way to write:

Public class B {private int count; private String name; private boolean important;}

It is best to define Log variables with static final

Private static final Log log = LogFactory.getLog (MyClass.class)

There are three benefits to this:

It can ensure thread safety.

Both static and non-static code are available

Does not affect object serialization

Select the wrong class loader

Wrong code:

Class clazz = Class.forName (name); Class clazz = getClass (). GetClassLoader (). LoadClass (name)

The intention here is to use the current class to load the desired object, but getClass () here may throw an exception, especially in some managed environments, such as application server, web container, Java WebStart environment, it is best to use the classloader of the current application context to load.

The correct way to write:

ClassLoader cl = Thread.currentThread () .getContextClassLoader (); if (cl = = null) cl = MyClass.class.getClassLoader (); / / fallback Class clazz = cl.loadClass (name)

Improper use of reflection

Wrong way of writing:

Class beanClass =... If (beanClass.newInstance () instanceof TestBean)...

The intention here is to check whether beanClass is TestBean or its subclass, but creating an instance of a class may not be that simple. Instantiating an object first will instantiate an object, and it is possible that the class does not define a default constructor. The right thing to do is to use the Class.isAssignableFrom (Class) method.

The correct way to write:

Class beanClass =... If (TestBean.class.isAssignableFrom (beanClass))...

Unnecessary synchronization

Wrong way of writing:

Collection l = new Vector (); for (...) {l.add (object);}

Vector is a synchronized version of ArrayList.

The correct way to write:

Collection l = new ArrayList (); for (...) {l.add (object);}

Wrong choice of List type

Select based on the following tabular data

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 trap

Wrong way of writing:

Map map = new HashMap (collection.size ()); for (Object o: collection) {map.put (o.key, o.value);}

Here you can refer to the implementation of guava's Maps.newHashMapWithExpectedSize. The user's intention is to set the initial value for HashMap to avoid the overhead of resize. However, it is not considered that resize will appear when the number of elements added reaches 75% of the capacity of HashMap.

The correct way to write:

Map map = new HashMap (1 + (int) (collection.size ()

Don't know enough about Hashtable, HashMap and HashSet

Here we mainly need to understand the internal implementation of HashMap and Hashtable, they all use Entry wrapper to encapsulate key/value. In addition to saving the reference of Key/Value, the internal Entry also needs to save the application of next Entry in the hash bucket, so there will be a lot of memory overhead, while the internal implementation of HashSet is actually a HashMap. Sometimes IdentityHashMap can be a good alternative. It is more efficient in memory usage (no Entry encapsulation, internal Object []). But you need to be careful with it. Its implementation violates the definition of Map interface. Sometimes you can replace HashSet with ArrayList.

The root cause of all this is the lack of an efficient set of Map and Set implementations within JDK.

Misuse of List

It is recommended that Array be used instead of List in the following scenarios:

List is of a fixed length, such as every day of the week

Frequent traverses of list, for example, more than 1 week

The numbers need to be wrapped (the main JDK does not provide a basic type of List)

For example, the following code.

Wrong way of writing:

List codes = new ArrayList (); codes.add (Integer.valueOf (10)); codes.add (Integer.valueOf (20)); codes.add (Integer.valueOf (30)); codes.add (Integer.valueOf (40))

The correct way to write:

Int [] codes = {10,20,30,40}

Wrong way of writing:

/ / horribly slow and a memory waster if l has a few thousand elements (try it yourself!) List l =...; for (int item0; I

< l.size()-1; i++) { Mergeable one = l.get(i); Iterator 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方法来执行排序。 用数组来描述一个结构 错误用法: /** * @returns [1]: Location, [2]: Customer, [3]: Incident */ Object[] getDetails(int id) {... 这里用数组+文档的方式来描述一个方法的返回值. 虽然很简单, 但是很容易误用, 正确的做法应该是定义个类。 正确的写法: Details getDetails(int id) {...} private class Details { public Location location; public Customer customer; public Incident incident; } 对方法过度限制 错误用法: public void notify(Person p) { ... sendMail(p.getName(), p.getFirstName(), p.getEmail()); ... } class PhoneBook { String lookup(String employeeId) { Employee emp = ... return emp.getPhone(); } } 第一个例子是对方法参数做了过多的限制, 第二个例子对方法的返回值做了太多的限制。 正确的写法: public void notify(Person p) { ... sendMail(p); ... } class EmployeeDirectory { Employee lookup(String employeeId) { Employee emp = ... return emp; } } 对POJO的setter方法画蛇添足 错误的写法: private String name; public void setName(String name) { this.name = name.trim(); } public void String getName() { return this.name; } 有时候我们很讨厌字符串首尾出现空格, 所以在setter方法中进行了trim处理, 但是这样做的结果带来的副作用会使getter方法的返回值和setter方法不一致, 如果只是将JavaBean当做一个数据容器, 那么最好不要包含任何业务逻辑. 而将业务逻辑放到专门的业务层或者控制层中处理。 正确的做法: person.setName(textInput.getText().trim()); 日历对象(Calendar)误用 错误的写法: Calendar cal = new GregorianCalender(TimeZone.getTimeZone("Europe/Zurich")); cal.setTime(date); cal.add(Calendar.HOUR_OF_DAY, 8); 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 TimeZone的误用 错误的写法: Calendar cal = new GregorianCalendar(); cal.setTime(date); cal.set(Calendar.HOUR_OF_DAY, 0); 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(); 时区(Time Zone)调整的误用 错误的写法: public static Date convertTz(Date date, TimeZone tz) { Calendar cal = Calendar.getInstance(); cal.setTimeZone(TimeZone.getTimeZone("UTC")); cal.setTime(date); cal.setTimeZone(tz); return cal.getTime(); } 这个方法实际上没有改变时间, 输入和输出是一样的. 关于时间的问题可以参考这篇文章: http://www.odi.ch/prog/design/datetime.php 这里主要的问题是Date对象并不包含Time Zone信息. 它总是使用UTC(世界统一时间). 而调用Calendar的getTime/setTime方法会自动在当前时区和UTC之间做转换。 Calendar.getInstance()的误用 错误的写法: Calendar c = Calendar.getInstance(); c.set(2009, Calendar.JANUARY, 15); Calendar.getInstance()依赖local来选择一个Calendar实现, 不同实现的2009年是不同的, 比如有些Calendar实现就没有January月份。 正确的写法: Calendar c = new GregorianCalendar(timeZone); c.set(2009, Calendar.JANUARY, 15); Date.setTime()的误用 错误的写法: account.changePassword(oldPass, newPass); Date lastmod = account.getLastModified(); lastmod.setTime(System.currentTimeMillis()); 在更新密码之后, 修改一下最后更新时间, 这里的用法没有错,但是有更好的做法: 直接传Date对象. 因为Date是Value Object, 不可变的. 如果更新了Date的值, 实际上是生成一个新的Date实例. 这样其他地方用到的实际上不在是原来的对象, 这样可能出现不可预知的异常. 当然这里又涉及到另外一个OO设计的问题, 对外暴露Date实例本身就是不好的做法(一般的做法是在setter方法中设置Date引用参数的clone对象). 另外一种比较好的做法就是直接保存long类型的毫秒数。 正确的做法: account.changePassword(oldPass, newPass); account.setLastModified(new Date()); SimpleDateFormat非线程安全误用 错误的写法: public class Constants { public static final SimpleDateFormat date = new SimpleDateFormat("dd.MM.yyyy"); } SimpleDateFormat不是线程安全的. 在多线程并行处理的情况下, 会得到非预期的值. 这个错误非常普遍! 如果真要在多线程环境下公用同一个SimpleDateFormat, 那么做好做好同步(cache flush, lock contention), 但是这样会搞得更复杂, 还不如直接new一个实在。 使用全局参数配置常量类/接口 public interface Constants { String version = "1.0"; String dateFormat = "dd.MM.yyyy"; String configFile = ".apprc"; int maxNameLength = 32; String someQuery = "SELECT * FROM ..."; } 很多应用都会定义这样一个全局常量类或接口, 但是为什么这种做法不推荐? 因为这些常量之间基本没有任何关联, 只是因为公用才定义在一起. 但是如果其他组件需要使用这些全局变量, 则必须对该常量类产生依赖, 特别是存在server和远程client调用的场景。 比较好的做法是将这些常量定义在组件内部. 或者局限在一个类库内部。 忽略造型溢出(cast overflow) 错误的写法: public int getFileSize(File f) { long l = f.length(); return (int) l; } 这个方法的本意是不支持传递超过2GB的文件. 最好的做法是对长度进行检查, 溢出时抛出异常。 正确的写法: public int getFileSize(File f) { long l = f.length(); if (l >

Integer.MAX_VALUE) throw new IllegalStateException ("int overflow"); return (int) 1;}

Another object where the overflow bug is cast is incorrect, such as the first println below. The correct one should be the one below.

Long a = System.currentTimeMillis (); long b = a + 100; System.out.println ((int) bmura); System.out.println ((int) (bMura))

Use = = actions for float and double

Wrong way of writing:

For (float f = 10f; f = 10f; f = 0.1f) {System.out.println (f);}

The floating-point decrement above will only be infinitely close to 0 and not equal to 0, which will cause the above for to enter an endless loop. Usually never use the = = operation with float and double. The operation is greater than and less than. If the java compiler can warn against this situation. Or it's best if the floating-point type of = = operation is not supported in the java language specification.

The correct way to write:

For (float f = 10f; f > 0; f Murray 0.1) {System.out.println (f);}

Save money with floating-point numbers

Wrong way of writing:

Float total = 0.0f; for (OrderLine line: lines) {total + = line.price * line.count;} double a = 1.14 * 75; / / 85.5 is expressed as 85.4999. System.out.println (Math.round (a)); / / the output value is 85 BigDecimal d = new BigDecimal (1.14); / / resulting in loss of precision

This is also an old mistake. For example, if you calculate 100 orders of 0. 30 yuan each, the final result is 29.9999971. If you change the float type to double type, the result will be 30.000001192092896. The reason for this is that humans and calculations count differently. Humans use the decimal system, while computers are binary. Binary is very good for computers, but it can cause errors in scenarios that involve accurate calculations. Such as applications in banking and finance.

So never use floating-point types to save money data. The calculation results obtained by floating-point numbers are not accurate. Even multiplication with the int type produces an inaccurate result. That's because there has been a loss of precision when using binary to store a floating-point number. The best thing to do is to express it with a string or a fixed number of points. In order to be accurate, this representation needs to specify the corresponding precision value.

BigDecimal meets the needs mentioned above. If the loss of precision in the process of calculation exceeds the given range, a runtime exception.

The correct way to write:

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")

Do not use finally blocks to release resources

Wrong way of writing:

Public void save (File f) throws IOException {OutputStream out = new BufferedOutputStream (new FileOutputStream (f)); out.write (...); out.close ();} public void load (File f) throws IOException {InputStream in = new BufferedInputStream (new FileInputStream (f)); in.read (...); in.close ();}

The above code opens a file output stream and the operating system assigns a file handle to it, but the file handle is a very scarce resource and must be properly released and reclaimed by calling the corresponding close method. In order to ensure that the resource can still be recycled correctly under abnormal circumstances, it must be placed in the finally block. The above code uses BufferedInputStream to wrap the file stream into a buffer stream, which will cause the buffer stream to be written to disk only when the close method is called. If you fail in close, the data will be written incompletely. However, the close operation of FileInputStream in finally block will be ignored here.

If the BufferedOutputStream.close () method executes smoothly, everything will be fine. If it fails, there is a potential bug (when http://bugs.sun.com/view_bug.do?bug_id=6335274): invokes the flush operation inside the close method, if an exception occurs, it will simply ignore it. So to minimize data loss, explicitly invoke the flush operation before executing the close.

The following code has a small flaw: if the allocation of file stream succeeds, but the allocation of buffer stream fails (in the case of OOM), the file handle will not be released correctly. But generally don't worry about this situation, because JVM's gc will help us clean up.

/ / code for your cookbook public void save () throws IOException {File f =... OutputStream out = new BufferedOutputStream (new FileOutputStream (f)); try {out.write (...); out.flush (); / / don't lose exception by implicit flush on close} finally {out.close ();}} public void load (File f) throws IOException {InputStream in = new BufferedInputStream (new FileInputStream (f)); try {in.read (...);} finally {try {in.close ();} catch (IOException e) {}

Database access involves a similar situation:

Car getCar (DataSource ds, String plate) throws SQLException {Car car = null; Connection c = null; PreparedStatement s = null; ResultSet rs = null; try {c = ds.getConnection (); s = c.prepareStatement ("select make, color from cars where plate=?"); s.setString (1, 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;}

Misuse of finalize method

Wrong way of writing:

Public class FileBackedCache {private File backingStore;... Protected void finalize () throws IOException {if (backingStore! = null) {backingStore.close (); backingStore = null;}

This problem is explained in detail in the book Effective Java. The main reason is that the finalize method depends on the call of GC, which may be called immediately or a few days later, so it is unpredictable. This is misleading in JDK's API documentation: it is recommended to release the Icano resource in this method.

The correct thing to do is to define a close method, and then the external container is responsible for calling and releasing the resource.

Public class FileBackedCache {private File backingStore;... Public void close () throws IOException {if (backingStore! = null) {backingStore.close (); backingStore = null;}

An AutoClosable interface has been introduced in JDK 1.7 (Java 7). When a variable (not an object) is outside the scope of try-catch 's resource usage, the close method is automatically called.

Try (Writer w = new FileWriter (f)) {/ / implements Closable w.write ("abc"); / / w goes out of scope here: w.close () is called automatically in ANY case} catch (IOException e) {throw new RuntimeException (e.getMessage (), e);}

Misuse of Thread.interrupted method

Wrong way of writing:

Try {Thread.sleep (1000);} catch (InterruptedException e) {/ / ok} or while (true) {if (Thread.interrupted ()) break;}

The main thing here is that the interrupted static method not only returns the interrupt state of the current thread, but also resets the current thread state.

The correct way to write:

Try {Thread.sleep (1000);} catch (InterruptedException e) {Thread.currentThread () .interrupt ();} or while (true) {if (Thread.currentThread (). IsInterrupted ()) break;}

Create a thread when a static variable is initialized

Wrong way of writing:

Class Cache {private static final Timer evictor = new Timer ();}

A thread is new inside the Timer constructor, and the thread inherits various properties from its parent thread (that is, the current thread). Such as context classloader, threadlocal, and other security attributes (access rights). The thread that loads the current class may be uncertain, such as a random thread in a thread pool. If you need to control the properties of a thread, the best thing to do is to put its initialization in a static method, so that initialization will be determined by its caller.

The right thing to do:

Class Cache {private static Timer evictor; public static setupEvictor () {evictor = new Timer ();}}

Cancelled timer tasks still hold status

Wrong way of writing:

Final MyClass callback = this; TimerTask task = new TimerTask () {public void run () {callback.timeout ();}}; timer.schedule (task, 300000L); try {doSomething ();} finally {task.cancel ();}

The above task contains an application of an external class instance, which will cause the reference to be not immediately recycled by GC. Because Timer will keep the TimerTask after the specified time before it is released. Therefore, the external class instance corresponding to task will be reclaimed in 5 minutes.

The correct way to write:

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 () That's all for the content of "what are the common problems in Java programming". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report