Six Java features to stay away from

I have spent countless hours troubleshooting different applications. Via the experience I can draw a conclusion about several Java SE features/APIs which most of the developers should just stay away from. When I refer to most of the developers, I have the regular Java EE developers in mind, not to the library designers / infrastructure engineers.

Full disclosure: I do honestly think that in the long run, most of the teams are better off staying away from the following features. But as always, there are exceptions. If you have a strong team and are fully aware of what you are doing, go ahead. In most cases though, if you start including following tools to your arsenal, you will regret it in the long run:

  • Reflection
  • Bytecode manipulation
  • ThreadLocals
  • Classloaders
  • Weak/Soft references
  • Sockets

But enough of the intro, let me go through the list of the warning signs backed with the explanation of the underlying problems:

Reflection. In popular libraries such as Spring and Hibernate, the reflection has its place. But introspecting a business code is something that is bad in so many reasons that I almost always recommend to avoid it:

First comes the code readability/tooling support. Open up your favourite IDE and find inter-dependencies in your Java code. Easy, isn’t it? Now, replace the method calls with reflection and try to repeat the process. Things go even more out of hand when you start modifying the state you should normally have encapsulated away. If you need an example, take a look at the following code:

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

Then you are about to miss compile-time safety. Seeing the same example above you can already see that making a typo in getDeclaredField() parameter is only discovered during runtime. As you might recall, discovering runtime bugs is a lot more tricky than getting rejected by your build script.

And last, there will be overhead. Reflection calls are optimized differently by the JIT. Some optimizations take longer to apply and some even cannot be applied. So the performance penalties on reflection can, sometimes, be orders of magnitude. But on a typical business application – you will not really notice the overhead, so this one is definitely a lesser of the evils.

To summarize, I can state that the only reasonable (indirect) reflection usage in you business code is via AOP. Other than this, you are better off staying away from the reflection.

Bytecode manipulation. If I see you are using CGLIB or ASM directly your Java EE application code, I feel like I immediately want to run away. Take the reasons I elaborated in reflection block, multiply the impact by five and you might start feeling the pain.

What makes things worse here is that you do not have the executable code anywhere in sight during the compile-time. Essentially, you do not know what code is in fact running in your production. So when facing troubles you are thrown into runtime troubleshooting and debugging – which – if you agree with me is a “bit” more time-consuming.

ThreadLocals. There are two unrelated reasons why seeing ThreadLocals in a business code makes me shiver. First, with the help of ThreadLocals, you could start feeling the temptation to use the variables without explicitly passing them down through the method invocation chain. Which could be useful on certain occasions. But when you are not careful, I can guarantee that you will end up creating lots of unexpected dependencies within your code.

The second reason is related to my day-to-day work. Storing data in ThreadLocals builds a highway to memory leaks. At least one out of ten permgen leaks I face is caused by extensive ThreadLocal usage. In conjunction with the classloaders and thread pooling, the good old “java.lang.OutOfMemoryError:Permgen space” is just around the corner.

Classloaders. To start with, the classloaders are a complex beasts. You must first understand them, the hierarchy, delegation mechanics, class caching, etc. And even if you think you have gotten it, the first (ten?) attempts will still not work properly. At minimum you will end up creating a classloader leak. So I can only recommend leaving this task to application servers.

Weak and Soft References. Just learned what they are and how do they work? Good. Now you understand Java internals a bit better. Got that brilliant idea of rewriting all your caches with soft references? Not good. I do know that being equipped with a hammer makes you look around for a nail.

Why I think caching is not a good nail for such a hammer here you might wonder. After all, building a cache upon soft references could be a good example of how to delegate some of the complexities to the GC instead of implementing it yourself.

Let us take an arbitrary cache as an example. You build it using soft references for values so that when memory is exhausted the GC can step in and start cleaning. But now you do not have control over which objects were removed from the cache and are more than likely to recreate them on next cache-miss. If memory is still scarce you trigger the GC to clean them again. I guess you can see the vicious circle forming and your app becoming CPU bound with Full GC constantly running.

Sockets. The plain-old is just too tricky to get right. I do think it is fundamentally flawed due to its blocking nature. When writing a typical Java EE application with a web-based front-end you need high degree of  concurrency to support your numerous users. What you now do not want to happen is to have your not-so-scalable-at-all thread pool sit there waiting for blocked sockets.

There are wonderful 3rd party libraries available for the task at hand though, so instead of trying to get things right yourself, go and grab Netty.

Reference: Six Java features to stay away from from our JCG partner Nikita Salnikov Tarnovski at the Plumbr Blog blog.
Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

Leave a Reply

2 + five =

Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books