Better Programming

Advice for programmers.

Follow publication

Why Java Is Perfectly Alive

Ivan Khodyrev
Better Programming
Published in
16 min readDec 30, 2020

cup of coffee with a heart pattern on top
Photo by Jonas Jacobsson on Unsplash

I wrote this long read as a response to the article “Why Java Is Dying.” My comment on this text was among the top-clapped, and I felt I have to make a full-size opposing statement, which should return balance.

The best short answer to the author’s attempt is the one by Coder:76 which received hundreds of claps:

“Java has been dying /dead for last 15 years for programming bloggers who wants some attention.”

It’s hard to disagree.

What’s Wrong With the Statement “Java Is Dying”?

raindrops reflecting thumbs-up signs against a backdrop of a large thumbs-down sign
Photo by Jonas Jacobsson on Unsplash

The “Why Java Is Dying” article received 70+ comments, most of which were critical, with tens and hundreds of claps each. Why did so many people react so negatively? The reason looks simple: The article is written in a provocative manner and contains many controversial statements that are far from reality for people who work with Java. Let’s have a look at some of them.

“Here, for instance, Spring is understandably setting up autowiring (bean injections) behind the scenes, but then where does Lombok live in the application context and how is message passing orchestrated between the two?”

This statement looks false for people who work with the technologies mentioned. Lombok is a compile-time library and Spring is a runtime library. They work on different levels at different times of the application lifecycle and do not directly interact. The right answer to the author’s question “where does Lombok live in the application context?” is “nowhere.”

“Java’s focus still appears to be on silly rules that dictate what class names should be, what packages they should be in, and if variables should be private or protected. Seriously, who cares?”

People who work on large, long-lasting projects care. Those rules are not silly for them.

“In contrast, ‘We’re all adults here’ is literally Python’s official response to the absence of access specifiers in the language.”

There is no problem that someone from the team assumes someone else is not an adult — it’s a shallow idea. The problem is that large projects which last for a long time and are created by big teams need rules; otherwise, they will fail. A large project is like a large city: It needs an architectural basis, planning, separation of concerns, private and public areas. If a skilled programmer separates language constructs into public and private, they most likely create “streets” that will lead others in the right way, saving their time, and hide auxiliary infrastructure “underground” so that no one gets lost in there.

There are many more disputable statements in the article “Why Java Is Dying,” but my goal here is not a detailed analysis. What I want to do is to use the chance to speak about the modern state of Java itself.

For years Java was the top choice among programming languages and at the same time the top choice for critics. Not because it was bad but because it was the big thing, and if you wanted to seem bigger than you really were, you had to say the word against something big and pray someone would pay attention. Java was a good target in this sense. But what about now? Is Java still a big thing or is it “dying” as some people say? Let’s discuss the most important and debatable topics in order to figure it out.

Syntax

a modern building next to the facade of an old building
Image by Harry Fabel from Pixabay

Java’s syntax is usually criticized the most: “not concise,” “not suitable for modern tasks,” “too much boilerplate,” etc. The only right answer to these “arguments” is to show the code. I will not discuss the special syntactic features here; there are many detailed guides around which cover all nuances of Java’s syntax. Rather I have selected five code fragments just for you to get an idea of how modern Java could look in action for different actual tasks.

Probably you’ve heard about the “good” old days when a simple web server in Java needed a few hundred lines of code and configuration to run? Now forget about them.

This code starts a simple web server using Spark Java on port 80 with HTTP GET method and /hello context path which returns a constant string when requested. Pretty straightforward and concise, isn’t it?

“Java is not made for database operations”, some people said. They were definitely joking.

This code uses the combination of Querydsl and Spring Data to fetch information from the SQL database. Everything looks pretty simple: the method getShopOrdersByDate returns orders of a particular shop on a particular date with additional date formatting. The interesting part: There is no SQL here, only Java constructs, which are translated to safe SQL inside libraries later. This means that the query itself is typesafe and is reliably checked in the compile-time, not randomly in the runtime. Also, IDEs help you with autocompletion, as usual with other Java code, making your life easier. Plenty of databases are supported out of the box, like PostgreSQL, MySQL, Oracle, and even MongoDB. Also, you don’t have to change the query code if you want to swap them.

Never thought about making a simple data analysis with Java? I suggest you try it.

No additional libraries here, just pure Java. Read all lines from the huge file, split them into separate words, filter out dumb words to keep everything clean, group words by the first letter, count how many words are in each group, create a string representation of resulting groups and counts, print the result. Pretty readable and maintainable. And of course, everything is done in parallel threads for appropriate speed-up out of the box so that your fresh 16-core monster justifies its cost. On my 4-core notebook with Java 15, this code executed in six seconds on average for a 1.2 Gb text file filled with random words. Not bad for such file size and straight nonoptimized code.

“You can’t do deep learning in Java”, they say. No, you can.

This simple example shows the stage of preparing the neural network structure before the learning phase, using Dl4j. Those who are in the deep learning domain will easily recognize many familiar words. Nothing extraordinary. Yes, you can do deep learning and machine learning in general with Java.

Lombok is an island next to Java. It’s so close, that you could even mix them up.

You hate boilerplate? Then use Lombok. This code fragment shows a full-featured data class with getters, setters, equals, hashcode, toString on all fields done right, and finally AllArgsConstructor. You may override any of those of course if you like.

People from outside the Java ecosystem usually argue that “Lombok makes Java not Java.” I have to disagree: Lombok IS Java. It’s a part of the modern Java ecosystem, uses legal Java mechanics to add its functionality, helps you with Java code with almost zero drawbacks, and is used and loved by many Java developers. How many? One of the most popular Java IDEs counts the number of Lombok plugin downloads as 11+ million. Many. It’s an instrument to make Java better, so in the practical sense, it is Java for millions of people who use it. Still, there is a part of the Java community that stands against Lombok, and they have the full right to do so — if you don’t like Lombok, no one forces you to.

Now imagine how these examples would look in other languages. You will probably find languages with somewhat fewer characters used to reach the same goals. But will that code be as reliable, readable, maintainable, and fast as a Java language? I hardly think so.

Another important thing related to syntax is IDE support. It’s not an abstract theoretical thing, like how powerful the language constructs are or how many symbols developers have to write to do something. IDE adds a practical layer converting all abstract questions to this one: how much developer time a particular task would take. Using well-designed language in combination with a modern IDE, developers could be much faster in reaching their goals than by using more powerful or concise but less IDE-friendly languages. Java has one of the best IDE supports, among others, thanks to its syntax: It’s not too complex and at the same time not too free-choice, so IDEs can understand the context you are currently working in and predict what you want to do next with great precision.

The last thing to say is that Java’s syntax allows different styles of programming to be used. Your code could be written in an object-oriented paradigm, where objects interact with each other; in a procedural paradigm, with the sequences of imperative procedure calls changing the global state; or in a functional paradigm, where you compose and apply functions to achieve your goal. Sometimes people distinguish more paradigms — for example, Java is well suited for either aspect-oriented or actor-based paradigms. Java gives you great flexibility in how you can think about your task, so it’s quite prepared to survive programming paradigm fashion shifts, which occur periodically.

To sum up, there is no problem with Java’s syntax. It is relatively simple, flexible, and expressive. Also, it allows IDEs to efficiently help developers in a number of ways, greatly improving their productivity. If you know Java well, write clear code, and use the right set of tools for your task, your programs will be beautiful, maintainable, and concise. Don’t let people deceive you on this topic.

Guarantees

a tattoo of the word “trust”
Photo by Joshua Hoehne on Unsplash

Unlike many other technologies, Java as a language and a platform gives you a number of guarantees which you can rely on.

Java language has the Java Language Specification, which is the main judge of how constructs of Java code should work and should be used. Why is it important? Because you can validate what you are doing and resolve questions or disputes in a strict, predictable way. For languages without a specification, you can’t be quite sure what’s going on: You could probably find some pieces of information in manuals, blogs, or language creator’s tweets, but until a language gets a specification, all these information pieces do not have a strong basis to guarantee anything.

Of course, specifications can be of different quality or different levels of detail and thus could miss something. Still, a language with a specification gives you a much higher level of confidence that you are doing things right than a language without one. Java’s specification is very deep and detailed, and it leaves almost no place for ambiguity.

Java versions are strongly backward compatible for things in the specification and public Java API. It means that if you take code that uses public Java API of version 1.3 which was written 20 years ago and run it today on Java 15, it will just work. At the same time, if you use private Java API, which consists of classes and methods/fields which are not supposed to be used by developers directly, you could be in trouble. I think it’s quite a fair deal: If you use something which has guarantees of backward compatibility, you can rely on it, but if you use things that are not intended to be used, please don’t expect them to work forever.

Java is safe and secure — not absolutely; rather practically. Java code is safe in the sense that developers are less likely to make mistakes with it than with many other languages. It is secure in the sense that Java code doesn’t have direct access to an operating system or hardware, and thus Java runtime can securely limit what Java programs can and can’t do.

Java code is portable. This means that you can compile Java code on one platform and run it on any platform for which Java Virtual Machine is implemented without recompilation. “Write once, run anywhere” — the old Java slogan, which is still viable today 25 years later. Wide portability accompanied by backward compatibility is a very strong combination of guarantees, which makes developers confident that their effort and knowledge will not become obsolete fast. Of course, there are corner cases, and some virtual machines allow only subsets of Java language specification due to different reasons like hardware limitations. For example, you can run Java code on 8kB RAM microcontrollers, but there are restrictions that you have to consider.

Java code is maintainable. Compared to languages like C++, Java’s syntax is a great simplification; it lacks many features, customizations, and powerful constructs C++ has. At the same time, compared to scripting languages, Java has many “rituals” that are redundant at first glance. In this sense, Java tries to keep a balance between complexity, power, and readability to maximize long-term code maintainability. What is maintainability? I describe it as the time needed for an average skilled developer to apply changes to an existing codebase (probably an old one) which leads to the developer’s goal and does not break anything else. The less time needed, the greater the maintainability.

In this sense Java is good. On the one hand, it is powerful enough to express many things needed by developers, but at the same time not as complex as languages where one can create beautiful, unsolvable mazes using extremely powerful language constructs that can be understood only by their creator. On the other hand, Java forces developers to write more verbose code with more explicit things than developers usually do in scripting languages, in order to increase readability and ease of understanding of what’s going on after the time passes.

Java is fast. If you try to research this theme, you will probably find dozens of articles like “Java is faster than X” and “X is faster than Java” which will contain contradictory statements and conclusions. If you try to experiment yourself, you will easily construct examples where Java is slow and where Java is blazing fast — the same trick you can do with other languages, by the way. There is a good comment about why Java was considered slow in the past. It’s a bit outdated by now. For example, strings in the latest Java runtime are processed much better than seven years ago, and also I don’t agree with some other statements. But the general conclusion that, with each release, Java gets optimizations that increase its performance is true. Remember the third example from the Syntax section of this article, where the huge 1.2 Gb file was processed? With Java 8, it took ten seconds on average on my notebook, while with Java 15, only six seconds with the same configuration. This is one of the important guarantees developers of the language give us: Java is fast enough for many tasks today, and it will be faster in the future.

The last important thing to say about guarantees is this: They have been fulfilled for more than 25 years already and there is no reason they will not be in the years to come.

Java Implements Modern Language Features Slowly

speedbump road sign
Photo by Makarios Tang on Unsplash

Yes. Is slowness a bad thing in general? No. Ask yourself the following. If you are riding a bicycle and occasionally see a wall in front of you, what would you prefer: speed up or slow down? I bet you choose the same as me.

So why does Java adopt features at a slower pace than some other languages? Because of guarantees, which we discussed in the previous section. These guarantees are the wall, which forces features to be carefully discussed, filtered, or transformed in some way, and slowness is a much better friend here than hurry. There is a formal process of how to apply changes to the Java language, called the Java Community Process. The main purpose of this process is to validate that the proposed features do not break guarantees. And it’s not an obvious and fast task sometimes.

Developers of the Java language try to keep a balance between innovation and guarantees, which is a much harder strategy than the one “let's add this cool feature to our language right now.” But it gives more profit in the long run because guarantees imply trust, which is a more valuable thing for the future than a cool feature set. There is a great talk regarding this strategy and overall philosophy of Java by Brian Goetz; please have a look.

Also, it’s worth mentioning the current Java release schedule. Every six months in September and March, a new version of Java is released that is totally ready for use and updated gradually for the next six months. Every three years, one such version becomes a long-term support (LTS) release with gradual updates for the next three years. Now Java 15 is the latest release and Java 11 is the current LTS. The next LTS release will be Java 17, planned for September 2021. Each version could include a number of “preview features,” for which there are no guarantees of compatibility in future releases. Their goal is to give developers the possibility to experiment with controversial innovations and leave feedback about them. If a feature is not marked as a preview, it means that it is included in the platform fully and will have all the guarantees Java provides.

Ecosystem

person staring up at bright starry sky
Photo by Greg Rakozy on Unsplash

Some people understand language ecosystem narrowly, limiting its notion only to a set of libraries that programmers could use. I prefer to think about an ecosystem in a broader way as an instrument to deal with problems. The more problems developers can solve with the language, the broader its ecosystem is. It doesn’t matter which meaning you prefer: Java has a huge ecosystem.

Here is a personal random list of questions I have asked myself previously to solve problems I had:

  • Could I write web apps in pure Java with zero knowledge of HTML, JS, and CSS? Yes.
  • Could I use terabytes of heap with millisecond-scale stop-the-worlds? Yes, easily.
  • Could I process images and videos in pure Java for portability? Yes.
  • Could I do deep learning with Java? Yes, please.
  • Could I program that robot I bought on Black Friday using Java? Yes.
  • Could I find an answer to my random silly question about Java? Yes.

This list is definitely personal, but I’m pretty sure in most cases the instances of “yes” would overwhelm the instances of “no” when the questions are in the programming context and involve Java.

With the help of the Java ecosystem, you can solve many problems in many domains, and, besides, usually you also have multiple choices of how to do it. If you want to get an idea of how vast the Java ecosystem is, have a look at this resource.

Startup Times and Memory Footprint

thin woman measuring her waist
Photo by Bill Oxford on Unsplash

Java code is compiled to an intermediate form called Java Byte Code, which then executes on the runtime platform called JVM. Besides complaining about Java syntax, critics usually complain about JVM memory footprint and startup times. Let's discuss that in more detail.

JVM stands for Java virtual machine, which means that your application runs in a sandbox by a virtual computer. There are many benefits to such an approach. Developers don’t need to think about the nuances of operating systems and hardware where applications would run. A virtual sandbox provides greater security capabilities because it doesn’t allow apps to interact with low-level things directly. The virtual environment stands outside of your app and can dynamically optimize it to increase performance in different situations, etc.

The drawbacks are that JVM needs additional resources, including memory and processor time, in order to function, and also it has a startup and warmup time.

In the usual use case, standard Oracle HotSpot JVM introduces tens or hundreds of megabytes of additional footprint and needs a few seconds of startup time on average, depending on the application. (JVM itself usually starts in less than a second, but some other libraries can increase this time due to their inner routines.) Also, in the first few seconds after the start, JVM can consume more CPU than average because it identifies and compiles “hot” parts of your bytecode to optimize their future usage.

In most situations, these drawbacks are reasonable for many types of applications. But there are cases when they are not, and you want to trade off the benefits and drawbacks in some way. What should you do, then? Should you abandon Java in favor of something else? Usually no — just take another runtime suitable for your particular task.

Consider, for example, the microservice domain. Modern microservice applications usually need minimal memory footprint and startup times in order to efficiently populate container orchestrators like Kubernetes. In order to fulfill this demand, developers of Java have created GraalVM. It allows developers to create native images from Java code which will run with tens of milliseconds startup time and only megabytes of additional memory footprint. Many Java web frameworks adapt GraalVM for the microservice domain: Quarkus, Micronaut, Spring, Helidon.

Traded drawbacks? You lose portability, and the built image can run exclusively on the platform for which it was compiled by GraalVM. But for microservices, it’s not really important because your app would most likely run in containers with the predefined environment. You could face a few other limitations as well. Anyway, when you hear that Java is not suitable for modern microservice demands, just remember: That statement is false.

Java does not imply excessive use of memory and slow startup times as many critics say. Memory usage and startup times mostly depend on the runtime used to finally run an application and on the additional libraries it’s using. In this sense, the Java ecosystem gives you choices depending on what you need.

What Is Java Really For?

jumbled pile of children’s building blocks
Photo by Xavi Cabrera on Unsplash

Java can be used for everything you could imagine: API and web servers, games and multimedia software, UI and web apps, IoT and robotics, AR and VR, machine learning and data streaming, databases and cloud-native microservices, huge enterprise systems and small prototypes.
Are there any exclusions? Technically no, practically yes. Java is not intended to be a low-level systems language, so it’s not a good idea to create the core of an operating system or hardware drivers in Java. Technically it’s possible, but there are better tools for these cases than Java.

But We Have to Pay for Java, Right?

hand holding burning 100 dollar bills
Photo by Jp Valery on Unsplash

No, you don’t have to pay for Java if you use free Java distributions. Java is open source, thus anyone can build ready-to-use Java distribution and there are a number of free prebuilt distributions like OpenJDK, AdoptOpenJDK, AWS Coretto, Azul Zulu, and others. Usage of these distributions is absolutely free of charge, and any of them will most likely satisfy your requirements. If you want to know more about this, please refer to this article.

Future of Java

long straight highway disappearing off the horizon
Photo by Karsten Würth on Unsplash

Summing everything up: Java is still a big thing.

Its role is to be the core technology in many areas, balancing innovation, power, and maintainability to sustainably support the projects it’s used in. If you like to experiment with cool new language ideas, please choose another technology. But if you see that a particular feature finally found its way to the Java specification, you can be sure it was added not by random occasion or a small fashion fluctuation but as a result of deep research and significant design effort to achieve the same level of guarantees other Java features have. In this sense, you can trust Java, unlike many other technologies on the market.

If you are questioning which computer language should you learn as your first or your next, just give Java a try. I’m sure Java will stay with us for a long time.

I bet there are many other good arguments which this article misses as to why Java will live long, and you are welcome to share them in the comments.

Responses (19)

Write a response