6 Java Features Developers Should Avoid Using

by anonymous on 2013-11-16 13:18:53

Six Java Features to Stay Away From

The author of this article is a programmer with many years of Java development experience. He concludes from his experience that not all Java SE features/APIs are worth using by developers. For instance, the six listed in this article should be approached with caution. Below is an excerpt and translation of the original content.

Years of Java development have taught me that, in the long term, developers should stop using the following Java SE features/APIs:

Reflection

Bytecode manipulation

ThreadLocals

Classloaders

Weak/Soft references

Sockets

1. Reflection

Reflection is present in many popular libraries, such as Spring and Hibernate. After reflecting on its use in business code, I recommend avoiding reflection. Below are the reasons why I oppose its use:

Firstly, it affects code readability and tool support. Open your IDE and try to find dependencies in your Java code. Replace method calls with reflection and repeat the process. Things quickly become unmanageable. Normally, you should encapsulate before modifying state. Here's an example:

```java

public class Secret {

private String secrecy;

public Secret(String secrecy) {

this.secrecy = secrecy;

}

public String getSecrecy() {

return null;

}

}

public class TestSecrecy {

public static void main(String[] args) throws Exception {

Secret s = new Secret("TOP SECRET");

Field f = Secret.class.getDeclaredField("secrecy");

f.setAccessible(true);

System.out.println(f.get(s));

}

}

```

From the above code, we can see that the parameter for `getDeclaredField()` is only discoverable at runtime. As you know, bugs that occur at runtime are more difficult to handle than those caught during script execution.

Secondly, optimization of reflective calls is performed by JIT, and some optimizations may take a long time to apply, while others may never apply at all. Thus, performance optimizations related to reflection are sometimes quantified. However, in a typical business application, you may not even notice these performance costs.

In conclusion, developers should reasonably use reflection at the business layer through AOP. Otherwise, it’s best to stay far away from it.

2. Bytecode Manipulation

If I see you directly using CGLIB or ASM in a Java EE application, I might run away immediately.

The worst thing is having no executable code available during compilation. In reality, when the product runs, you don’t know which code is being executed. So, when you encounter problems, you naturally blame runtime troubleshooting and debugging, which only makes things worse.

3. ThreadLocals

There are two unrelated reasons why I shiver when I see ThreadLocals used in business-layer code. First, with the help of ThreadLocals, many variables are used without being explicitly passed down the method call chain. This can be useful in certain situations, but once you're careless, you'll build unexpected dependencies in your code.

The second reason relates to my daily work: storing data in ThreadLocals can cause memory leaks. At least one-tenth of the Permgen leaks I’ve encountered were caused by ThreadLocals. When combined with class loaders and thread pools, the "java.lang.OutOfMemoryError:Permgen space" exception may occur.

4. Classloaders

First, class loaders are complex beasts. You need to understand their hierarchy, delegation mechanism, class caching, etc. Even if you think you've mastered them, they may still malfunction. Eventually, this leads to a class loader leak issue. Therefore, I can only recommend leaving this task to the application server.

5. Weak/Soft References

Now, you should better understand Java's internal methods. Rewriting all caches with soft references isn't wise. I know, when you have a hammer, everything looks like a nail. But for a hammer, a cache isn't a good nail. Why? Building a cache based on soft references is a good example of delegating some complex factors to the GC instead of implementing them yourself.

For example, you create data with soft references, and when memory runs out, the GC enters and cleans up. However, the objects deleted from the cache are beyond your control and are likely to be recreated during the next cache miss. If memory is still insufficient, you can trigger the GC to clean up again. You may already see the vicious cycle of the entire operation process, where the entire application becomes a state of constant CPU and GC activity.

6. Sockets

Ordinary old-school java.net.Socket is too complex to get right easily. I believe blocking is its fundamental flaw. When you write a typical Java EE application with a Web frontend, the application needs high concurrency to support a large number of users. What you want to avoid most is a non-scalable thread pool sitting idle waiting for blocking sockets.

Currently, there are many excellent third-party libraries available that can better accomplish tasks, such as Netty. Developers might want to give it a try.

Via: Plumbr