More often than not, the keyword static
confuses Java programmers. As a consequence, Ken Fogel asks his students never to use static in Java unless explicitely told to do so. While that's a good hint for starters, it's only part of the story.
Funny thing is, I recommend to use static
as often as possible. There's a twist: Never use static
variables, but always use static
methods.
Static variables
I suppose most readers of BeyondJava.net are fairly advanced Java programmers, so I'll cut the stories about static
variables short: static
variables are global variables, and global variables are a highway to hell. In a blink of an eye you're going to struggle with side effects. Things get worse in a multithreaded environment, such as Tomcat. Many Java programmers aren't really aware of it, so that's a point to stress: web applications use multithreading, so it's a real challenge to use static
variables correctly. Just don't do it.
Static methods
Static
methods are a different story altogether. They avoid trouble. Use static
methods as often as possible.
That's because static
methods can't access the object's attributes. Static
methods aren't part of the object, so they don't have access to anything that belongs to the object. Instead, they belong to the underlying class of the object. Hence, static
methods can only access static
variables - and we've already declared them taboo.
The resulting effect is a functional programming style. Everything has to be passed as a parameter. That looks a bit cumbersome at first. After all, what are these nice objects for if we aren't allowed to modify their values? However, modifying the value of an object from within a method is a side effect. As long as you are aware of the side effect, everything is O.K., but when another programmer joins the team, they aren't aware of the side effect and runs into trouble. It may even happen to yourself when you return to the project after a year or two.
Static
methods - also known as functions - are even more useful when it comes to multithreading. This time, the multi-user environment of your web server isn't the problem. But you should be very careful when you start to create new threads yourself.
Actually, the new parallel stream API of Java 8 actively discourages side effects. The stream API works with Lambda expressions, which in turn are functions which are allowed to access variables outside the scope of the function - but only if they are effectively final. That's not exactly the same as a static method, but it's close enough to achieve the same goal. It's difficult[1] to implement a Lambda causing side effects running havoc in a multithreading environment. Many people - including myself - complained about Java 8 Lambdas being less powerful than full-blown closures. Taking multithreading into mind, that design decision may turn out to be a live-saver. Or save you from working extra hours, for that matter.
The exception from the rule
Of course, students might make their methods static
in order to be able to call them from the main
methods. That's not what I'm striving at. Embracing the functional programming style doesn't mean you should get rid of objects. There's a good reason why we do object oriented programming. Granted, recently many voices consider object oriented programming dangerous, but as long as we're using Java, we should stick to object oriented programming. Classes and packages are like the index of a library: a good means to structure your code. Objects are tremendously useful, too. My point is to avoid side effects. Scala programmers can exploit final objects to achieve this goal (using val
instead of var
). In Java, that's not quite as simple. Favoring functions over methods helps to get rid of side effects.
Static classes
What about static
classes? Well, I recommend to avoid them, but for a different reason. Do you know anybody who really knows what a static
class is? I don't. Even if you do, does every member of your team know static
classes? For some reasons, static
classes are fairly exotic, so using them might confuse your co-workers.
Java knows only Static
nested classes. You can't declare an arbitrary class static. It has to be an inner class. The meaning of static
is a bit confusing: it means there's no reference to the surrounding object. This allows you to create an object of the inner class without having to create an instance of the outer class, and it may avoid memory leaks because the garbage collector can remove the outer class instances even if an inner class instance exists.
However, I prefer to keep things simple. Simply make the inner class an ordinary class. In any case, despite inner classes are very attractive in theory, I tend to avoid them in practice (with the sole exception of short anonymous inner classes - the majority of which are soon to be replaced by Lambda expressions). Among other reasons, files containing multiple classes tend to be very long. For the sake of clarity, I prefer to private or inner classes into a dedicated package.
Static initializers
Have you ever seen this construction?
public class Demo { static { System.out.println("The Demo class has been loaded."); } }That's static
, followed by a block of code. No, it's not a typo. Instead, it's tremendously useful. It's a static
initializer. This piece of code is called when a class is loaded. In other words, it's called a single time during the life cycle of a class. Usually, classes are hardly ever removed from memory, so in most cases you can safely assume it is called only once (until the virtual machine is restarted).
The static
initializer is called before the first instance of the class is constructed, so you can only access static
variables and static
methods. Static
initializers are useful to populate caches that are shared among objects. This is one of the rare cases when you don't need to care about multithreading when you're writing or reading a static
variable.
Since initializers are widely unknown, I'd like to introduce the non-static initializer to you, even if it's not today's topic.
public class Demo { { System.out.println("A Demo object is being created."); } }This is a piece of code which is called when an object is constructed. It's similar to a constructor, except it can't take parameters. If a class contains both constructors and an initializer, the initializer is always called first.
Wrapping it up
The keyword static
shows four different faces in the Java programming language. Cutting a long story short, I consider static
variables dangerous, static
methods useful and static
classes sort of superfluous. Static initializers, in turn, are tremendously useful to populate caches and similar objects shared among objects and users.
Dig deeper
StackOverflow.com on static classes in Java
StackOverflow.com on static vs. inner classes in Java
Ken Fogel's video: Never use static in Java unless explicitely told to do so
- but not impossible↩