Java features applicability
Java language and standard library is powerful, but with great power comes great responsibility. After seeing a lot of user code misusing or abusing rare Java features on one hand and completely forgetting about most basic feature on the other, I decided to compose this summary. This is not a list of requirements and areas every Java developer should explore, know and use. It’s quite the opposite! I group Java features in three categories: day to day, occasionally and never (frameworks and libraries only). The rule is simple: if you find yourself using given feature more often then suggested, you are probably over-engineering or trying to build something too general and too reusable. If you don’t use given feature often enough (according to my subjective list), you’re probably missing some really interesting and important opportunities.
Note that I only focus on Java, JVM and JDK. I do not suggest which frameworks and how likely you should use. Also I assume typical, server-side business-facing application.
Day to day
The following features of the Java language are suppose to be used every day. If you have never seen some of them or find yourself using them very rarely, you might take a closer look, they are really helpful:
- classes, interfaces, packages – seriously. Put your code in classes. You remember from the university that class is an encapsulated data + methods acting upon that data? Class with only state is barely a structure. Class with only methods is just a namespace enclosing functions. Also use interfaces whenever needed. But think twice before creating an interface with only one implementation. Maybe you don’t need a middleman? Nevertheless, put everything in packages, following well established naming convention.
- static methods – don’t be afraid of them. But use them only for stateless utility methods. Don’t encode any business logic inside
static
method, ever. ExecutorService
– thread pools – creating and effectively using thread pools, understanding how queueing andFuture<T>
works is a must. Don’t reimplement thread pools, think about them every time someone says producer-consumer.Atomic
-* family – don’t usesynchronized
to barely read/update some counter or reference atomically.Atomic
-* family of classes use effective compare-and-swap low-level instructions to be amazingly efficient. Make sure you understand the guarantees these classes provide.- design patterns – Not technically a Java language part, but essential. You should, know, understand, and use them willingly but sparingly. Just like with interfaces – don’t go overboard. GoF or even EI patterns should often occur in the code base. But let patterns emerge during your thought process, rather than you letting your thought process be driven by patterns.
- built-in collections, including concurrent – you absolutely must know and use built in collections, understanding the differences between
List
,Map
andSet
. Using thread-safe collections should not be an issue for you. Understand performance characteristics and have basic overview of the implementation behind them. This is really basic. Also know and use variousBlockingQueue
implementations. Concurrency is hard, don’t make it even harder by reimplementing some of this stuff yourself. - Built-in annotations – annotations are here to stay, learn to use
@Override
(and@Deprecated
to some degree) every day consistently. - exceptions – use unchecked exceptions to signal abnormal, exceptional failure that requires action being taken. Learn to live with checked exceptions. Learn to read stack traces.
- try-with-resources – familiarize yourself with this fabulous language construct. Implement
AutoCloseable
if your class requires any cleanup. - Blocking IO – using
Reader
/Writer
,InputStream
/OutputStream
classes is something you should be really familiar with. Understand the difference between them, using buffering and other decorators without fear.
This ends the list of everyday tools you should use. If you’ve never heard of some of them or used them only occasionally, study them more carefully as they might become your lifesavers.
Occasionally
Following are the language features you should not be afraid to use, but they should not be abused as well. If you find yourself exploiting them every day, if these are kind of features you see several times before lunch, there may be something wrong with your design. I am looking from a back-end, enterprise Java developer perspective. These types of features are useful, but not too often.
- inheritance and abstract classes – really, it turns out I don’t use inheritance that often and I don’t really miss it. Polymorphism driven by interfaces is by far more flexible, especially with a painful lack of traits in Java. Also prefer composition over inheritance. Too many levels of inheritance lead to very unmaintainable code.
- regular expressions – Some people, when confronted with a problem, think ‘I know, I’ll use regular expressions.’ Now they have two problems.. The world without regular expressions would be much more boring and cumbersome. They are wonderful for parsing regular languages (but not HTML) but its way too easy to overuse them. If you find yourself crafting, testing, fixing and coursing whole day in front of regular expressions, you are probably using wrong tool for the job. My all time favourite:
public static boolean isNegative(int x) { return Integer.toString(x).matches('-[0-9]+'); }
-
Semaphore
,CountDownLatch
,CyclicBarrier
and others – they are all extremely useful better by an order of magnitude than infomouswait()
/notify()
pair. But even them won’t prevent you from concurrency bugs when abused. Consider thread-safe collections or some frameworks when you see these synchronization mechanism too often. - generic types in user code– using built-in collections and other classes that have generic types should not only be a day to day practice, it should be obvious for you. But I mean developing code yourself taking or returning generic types. Something like this:
public <T, F> ContractValidator<T extends Contract> T validate(Validator<T>, F object)
It is sometimes necessary to use generics in your own code, but don’t go too meta-. Of course static typing and type safety should be your priority, but maybe you can avoid too many generic, complex types?
- Scripting languges in JVM – do you know JDK has a built-in JavaScript interpreter? And that you can plug virtually any other language like Groovy or JRuby? Sometimes it’s simpler to embed small script inside your application that can be changed even by the customer. It’s not often, but in very fast changing markets redeploying might not be an option. Just remember that if the total number of lines of scripted code exceeds 1% of the total amount of your code, you should start worrying about maintenance.
- Java NIO – it is hard to get it right and even harder to actually benefit from it. But in rare cases you actually have to use NIO to squeeze as much performance and scalability as you can. However prefer libraries that can do it for you. Also in normal circumstances blocking IO is typically enough.
synchronized
keyword – you should not use it too often for a simple reason. The more often it’s used, the more often it’s executed, thus impacting performance. Consider thread-safe collections and atomic primitive wrappers instead. Also make sure you always understand which object is used as a mutex.
I consider features above valuable and important, but not necessarily on a day-to-day basis. If you see any of them every single day it might be a sign of over-engineered design or… inexperienced developer. Simplicity comes with experience. However, you might also have very unusual requirements, which applies to the third group as well.
Never (think: framework and library developers only)
You should know and understand the principles behind the features below in order to understand frameworks and libraries. And you must understand them to effectively us them, I see way too many questions on StackOverflow that could have been avoided if the person in question simply read the code of a library in use. But understanding doesn’t mean use. You should almost never use them directly, they are mostly advanced, dirty and complicated. Even one occurrence of such feature can lead to major headaches.
- sockets – seriously, sockets. You must understand how TCP/IP stack works, be very conscious with regards to threading, careful when interpreting the data, vigilant with streams. Stay away from using pure sockets, there are hundreds of libraries wrapping them and providing higher level abstractions – HTTP, FTP, NTP, SMB, e-mail… (e.g. see Apache Commons net). You’ll be amazed how hard it is to write decent HTTP client or server. And if you need to write a server for some proprietary protocol, definitely consider Netty.
- reflection – there is no place for introspecting classes and methods in business code. Frameworks can’t live without reflection, I can’t live with. Reflection makes your code slower, unsafe and ugly. Typically AOP is just enough. I would even say that passing instances of
Class<T>
around is a code smell. - dynamic proxies and byte code manipulation –
Proxy
class is great, but just like reflection, should be used only by the frameworks and libraries that support you. They are a basic building block of lightweight AOP. If your business application (not framework or library, even Mockito uses these techniques!) requires byte code generation or manipulation (e.g. ASM or CGLIB) –you’re in a deep sh**tI will pray for you. - class loaders – everything that has anything to do with class loaders. You must understand them, the hierarchy, bytecode, etc. But if you write your own class loaders, it’s a road to hell. Not that it’s so complicated, but it’s probably unnecessary. Leave it to application servers.
Object.html#clone()
– honestly, I don’t remember if I ever used that method in my entire (Java developer’s) life. I just… didn’t… And I can’t find any rationale behind using it. I either have an explicit copy constructor or better use immutable objects. Do you have any legitimate use cases for it? It seems so 1990s…- native methods – there are a few in JDK, even for such small tasks like computing sine function. But Java is no longer the slowest kid in the class, it’s actually quite the opposite. Also I can’t imagine what kind of logic you need that can’t be achieved using standard library or 3rd-party libraries. Finally, native methods are quite hard to get right, and you can expect low-level, nasty errors, especially around memory management.
- custom collections – implementing brand new collection following all contracts defined in original JavaDoc is surprisingly hard. Frameworks like Hibernate use special persistent collections. Very rarely you need a collection so specific to your requirements that none of the built-in ones are good enough.
ThreadLocal
– Libraries and frameworks use thread locals quite often. But you should never try to exploit them for two unrelated reasons. First of all,ThreadLocal
is often a hidden semi-global parameter you want to sneak-in. This makes your code harder to reason about and test. Secondly,ThreadLocal
s can easily introduce memory leaks when not cleaned up properly (see this, this, this and this and…)- WeakReference and SoftReference – these classes are quite low-level and are great when implementing caches playing well with garbage collection. Luckily there are plenty of open-source caching libraries, so you don’t have to write one yourself. Understand what these classes do, but don’t use them.
com.sun.*
andsun.*
packages, especiallysun.misc.Unsafe
– stay away from these packages, just… don’t go there. There is no reason to explore these proprietary, undocumented and not guaranteed to preserve backward compatibility classes. Just pretend they’re not there. And why would you useUnsafe
?
Of course the list above is completely subjective and most likely not definitive. I encourage you to comment and suggest, if you feel some items are in wrong place or maybe something is missing entirely. I would like to build a summary that can be given as a reference during code review or when a project is evaluated.
Reference: Java features applicability from our JCG partner Tomasz Nurkiewicz at the Java and neighbourhood blog.
Interesting list. Good to see design patterns in the day-to-day list. I mostly agree with all of them, Reflection is one which might be required to use occasionally…
Reflection
If my application runs as a PHP front end and Java backend, where I want to make use of PHP to Java library I definitely need reflection to generalize the call from PHP to Java. Do you know any better solution here? What’s your thought on this one?
I wouldn’t call PHP to Java integration (without going into details) day-to-day use case. However if you need it, it falls into “framework and library developers only” category. Thus you are free to use all the tools in the world. Moreover it’s just my opinion, you can have yours.
How do you, precisly, integrate Java with PHP? Web services? JNI?
I meant Reflection to be in the Occasionally category, Integration uses php-java-bridge available – http://php-java-bridge.sourceforge.net/. However web services were our initial choice but we finalized on the open source in the above link.
Reflection is used in multiple cases, For example, one of my code project uses it (widely used in my project) – https://code.google.com/p/tostring-implementation/.