I started my professional career with a Pascal program (I know that dates me a bit) and then continued with C and C++ and discovered Python around version 1.4 and used it heavily for over ten years, and really didn’t like Java when I first met it (version 1.3-1.4 time frame).
I’ve also used a ton of other languages for real deployed projects, like Python, Ruby, C, C++, Common Lisp (which I <3 love), C#, Clojure, Visual Basic, and Go, so I’m a not the average “blub” programmer saying this.
But since about 13 years into my career, it’s become my tool of first choice for most things.
There are a lot of arguments for strong typing versus dynamic typing, but one thing I’ve not heard much about is the argument for manifest typing; that is, having the types of variables specified on declaration, or initial assignment. While it’s all well and good that the compiler can figure out what types things need to be, it’d be really nice if I, as a human, didn’t have to figure out what they were.
On this point, old me would kvetch and complain about how stupid it
was, and that strong typing meant lots of typing whilst smugly
waggling my fingers in a typing motion. Current day me, now having
now read many code bases I didn’t write, in languages without
manifest typing, have changed my mind on the subject. For example,
what is the type of
The short answer is: without knowing what
you don’t. Is it a dict, a tuple, some other object type? Now you
need to find out where
getArtistRecord is defined and look at it’s
code to figure it out, and since
db was probably passed into the
current function, you now have to find the caller of the constructor
of the current class to find that out… Then to understand what the
eventual version of
getArtistRecord returns (is it returned via a
getattr hook?) you may have to again do this with the functions it
calls. Lather, rinse, repeat. Compare to:
I know exactly what
x is, without having to know the innards of
db.getArtistRecord, no searching required, and even if I did, in
Eclipse I can go right to the definition with Control-click, no muss,
no fuss. If it’s an interface, I can view the class hierarchy with a
This may sound trivial in your cases, but I’ve basically had to trace large chunks of a code base just to figure out what a simple function did. If your familiar with the code you work on, this doesn’t happen, but when you step into a largish code base you didn’t write, this is a significant barrier to being able to do anything. This coming from a guy that used Python heavily for over ten years. Compared to my experiences in stepping into large Java codebases whereby you could make minor changes fairly rapidly because it was easier to understand the function you need to deal with in isolation. This isn’t to say that you never have to traverse the code base to understand some chunks of code, but Java makes this so much easier, it’s not even comparable.
Compared to languages like Python, Ruby, etc. The tooling of Java is just incredible.
Things like Maven as a build system allow for totally reproducible builds, and ways to mitigate versioning hell. You can be sure that what you built your code with is exactly the same as what your codevelopers are using. With Maven Central, you can just specify the library you want and it magically appears at build time. Want your own jar repository? No problem. With Maven’s archetypes, it can generate it’s own boilerplate. It auto-runs unit tests. The plug-in system allows for a ton of high-level automation like generating project files for Eclipse, or documentation generation, or deploying to Tomcat, or any number of other things. Is Maven a panacea? Of course not, anyone who’s had to dig into some weird error message will tell you so, but it’s really good at what it does.
The JVM is just a great piece of software. JVM with hotspot is incredibly performant. In one particular case, one of my teams translated a web service written in Python to Java in a straightforward way – that is, we didn’t do any algorithmic changes, just a straight port – and saw a 12x improvement, saving us much $$$ in machines and the management of those machines.
The fact that the JVM is cross-platform is also really great. My main development box is sadly a Windows 7 box. So I run Eclipse and Tomcat on Windows for development, and deploy to a Linux machine, and it all the tooling and code Just Works ™. I just started migrating some services at work from Windows to Linux, and the Java stuff just worked. All the portability issues were about other things.
Threading? Sorry Python and Ruby (unless you use JRuby) you really lose here. Your GILs make things sad. They do work fine if you’re IO bound and you’re using it to multiplex without having to use something like Twisted.
Then there are a few “behind the scenes” kinds of things like the ever-improving memory management and garbage collection, the ability to have multiple class-loaders and the isolation you can have between multiple things running in the same JVM.
I’m an Eclipse guy (I keep trying IntelliJ every few years, but it’s just not my thing, and never was able to get NetBeans to run on Linux), but what I’m going to say applies equally to at least IntelliJ and probably NetBeans. These tools are amazing in what they allow for. There’s the refactoring support that if you’ve never used before, are like anonymous functions – if you’ve never had them, you underestimate their value, once you have had them, you wonder how you ever got around without them. Eclipse’s quick-fix (Control-1) is probably my most used feature. For example, I want to make a list, what I do is:
Eclipse will helpfully underline
myList in red squigglies because I didn’t
specify the type. So I hit Control-1, and
create local variable 'foo' is
the first option, I hit enter and now I see:
That menu contains a bunch of other options, like creating fields,
foo (perhaps I mistyped it) and a bunch of other things, so
my old “lots of typing” argument goes away. There’s functions to
generate constructors based upon the object fields, creating getters
and setters, and a zillion other things.
Then there’s the extensibility of Eclipse that allows for plugins to things like ZooKeeper, Spring, Git, Tomcat and more things you can shake a stick at.
I only have experience with Tomcat and Jetty, but both of these are extremely well done, and I expect that WebSphere and JBoss are similar. They’re high performance, and provide so much pre-baked functionality that if you’re doing a web service, unless you have very specific requirements that preclude them, you’d be a fool not to use one of them. They can handle the details of TLS, logging, configuration and management of DB connections, and so much more.
The Java Language and Environment
The Java language itself is ok. Not wonderful, but ok. I like
Python, Ruby is ok. Rust looks very promising. I wish I could use
Common Lisp or Clojure at work. I really want to like Go but the lack
of generics and the way it deals with errors are really offputting to
me, as are the distinctions between
make. If Java had a
macro system like Lisp’s that would be great – though annotation
processors can get you part way there.
Annotation processors are really cool. Things like Lombok allows for annotation based code generation, as do things like Google Dagger 2 and the processing of these things is built into the JDK itself. They can also be used for auto-documentation generation and source code scanning like I did for generating documentation for JAX-RS web services.
Flat or shaded jars are also really nice. Think of it as a statically
linked executable. They greatly simplify deployment as you can make
running your application as simple as
java -jar project.jar
path.to.Main. Likewise, if you’re working with servlet engines, you
can create a war files or ear which are self contained. Deployment
is generally just well thought out.
The selection of supported libraries for Java is just unmatched to just about any other language. The combination of a servlet engine, Jackson and Jersey make writing RESTful services, or just dealing with JSON, a breeze. There are tons of general utility libraries like Guava and apache-commons that have an extremely rich set of data types and threading utilties, among many other features. XML, YAML, or just about any other file format? You can bet there’s code to deal with it. Log4j just makes logging falling-off-a-log-(pun intended)-dead-stupid. JUnit and Mockito make unit and integration testing a breeze: if you add Docker and Helios, there’s the Helios testing framework which steps it up to whole other playing field. There’s JDBC which every notable SQL database has a supported client for, there’s Hibernate if you don’t like dealing with JDBC directly. There’s quality SOAP (yeah I know) support, and the list just goes on. Generally, if there’s a thing, there’s Java code to deal with it.
Java Is Not A Panacea
Java of course has it’s flaws. The JVM startup time isn’t wonderful,
which makes it less ideal for command-line tools. It doesn’t support
tail recursion, which while it’s not something you need often, is nice
to have. It would make Clojure be able to get rid of
I’m really not a fan of. Type erasure in generics and that the
generic parameters don’t survive to runtime stinks. The differences
Integer can be subtle and bite you unexpectedly.
I also wouldn’t use Java as a browser plugin.
Java 1.8 finally got lambdas which are nice, as well as getting rid of the permanent generation which should save a number of production headaches.
Java ain’t perfect, and other languages have their place, but for doing production services, it’s a really good choice.