Why is Java Making a Comeback?
April 21, 2022
Java, the programming language powering over 3 billion devices, celebrated its 25th anniversary in 2020. Despite its age, this language is far from dead.
As Brian Goetz, Java Language Architect at Oracle, has said, “If I had $1 for every time Java was declared dead, I could probably retire!”
Yes, it’s easy to find someone who is bashing Java, often on social media and for no reason. Recently, for example, people blamed Java for a vulnerability related to Log4j. Many young developers consider Java to be old school compared to other JVM languages like Scala or Kotlin. However, the recent language rankings released by RedMonk, a developer-focused industry analyst firm, indicate otherwise: Java was tied for second place with Python. In their words:
The language once created to run cable set top boxes continues to be a workhorse, and importantly one that has consistently been able to find new work to do. Java’s performance on these rankings continues to impress, all these years later, and as it’s shown a remarkable ability to adapt to a rapidly changing landscape, it’s a language that would be difficult to bet against.
Over the years, Java has become more robust and continues to evolve fast. This article discusses the features of Java that suit modern development and why it is starting to gain steam again.
Java remains so widely used because it continues to work well for even modern use cases. Here are a few:
Java is a natural choice for tools developed to handle massive data in a distributed environment because, among other things, it features:
- Automatic Garbage Collection
- Core Networking APIs
- Rich Data Structures
- Security (e.g., code compiled to bytecode and run inside JVM)
- Robustness, eg, strong type checking
Distributed systems typically need to deliver high throughput. It is challenging to achieve it without using multithreading in the core API. Java supports threading, locks, and multithreaded data structures from its initial releases. In the recent Java version(JDK 17), developers added more advanced capabilities to the core Java API. Because of these features, Elasticsearch, the most popular distributed search engines, core API Lucene, and many other popular IMDG are written in Java.
Java offers a good compromise between developer productivity and runtime performance. Developers benefit from a simple, powerful, type-safe language with a wide range of high-quality libraries. Performance is generally good enough. When it falls short, native code has been used to keep overall performance in line with C and C++.
Debugging distributed, multithreaded applications is not an easy task. If you face such challenges, you can leverage live debugging tools like Rookout. Rookout to place non-breaking breakpoints in your Java application and get logs, traces and metrics needed to fix bugs…
In Java, some excellent libraries and game engines are explicitly used for game development, both mobile and PC:
As a developer, it’s easier to get up to speed using these libraries as you don’t have to start from scratch.
While you won’t find an AAA category game purely written in Java because modern consoles don’t support Java games, Java is well-suited for indie or mobile games.
Since its inception, Java has supported desktop application development using its toolkit Swing. Recently, AWT and Swing moved to the
jdk.desktop module, and they are not shipped as part of JDK, but you can still use it separately.
JavaFX is another open source alternative worth considering for desktop application development.
Java is generally preferred for backend development. However, that doesn’t mean you can’t use it to create feature-rich web applications.
Java developers can use technologies like servlet, JavaServer Pages, and JavaServer Faces, which all form part of the Java EE platform, for webdev. Other Java frameworks like Spring and Vaadin provide support for quickly building web applications with great UX.
As the great application workload migration to the cloud continues, cloud-native apps have become the new norm.
Traditionally, Java applications are developed to run in Java virtual machines (JVM). It means that strong servers are needed to host complex applications. But when adopting a cloud-native approach with Java, these monolithic applications get replaced by microservices that require much less computing. Because of the elasticity of cloud computing, this approach also allows organizations to easily scale applications up or down as the need arises.
Moreover, performance improvements in the recent Java releases and tools like GraalVM have gone a long way to solve Java’s cold start issue that made some folks prefer languages like Node.js as the backend for their function-as-a-service (FaaS) use cases.
Kubernetes-native frameworks like Quarkus are also trying their best to make Java a leading platform in serverless and containerized environments.
Java has been so enduring because of certain features. Here are the main ones.
Java is platform-independent, which means the byte code generated can be run on all operating systems. WORA (write once, run anywhere) sets Java apart from other languages due to its ability to run across platforms. Internally, the JVM, i.e., a virtual machine that executes Java class files, is primarily responsible for ensuring that a Java program runs the same on any device or operating system.
With modern multicore machines, it makes sense to use a language that best utilizes hardware resources. Java handles it perfectly by allowing you to execute multiple threads for better performance.
Multithreading is not new to Java. It was part of its initial releases, and it has set Java apart from other languages since the start. Threading API has evolved by introducing higher-level abstractions like Executor and the ForkJoin Pool framework.
With Project Loom, Java introduced the concept of virtual threads, which makes Java an even better option for writing high performance systems, as illustrated by the code samples below.
In the first code snippet below, we create threads using the traditional approach by extending the
Thread class and passing some runnable tasks to its constructor. In this approach, the thread created is tied to the OS.
Thread t = new Thread(new SomeRunnableTask()); t.start();
In the following code snippet, we create threads using the
startVirtualThread method of the new Loom API and pass some runnable tasks. Here the lightweight thread is created by the JVM, not the OS. It’s lightweight because JVM manages it and doesn’t suffer from context switching, which is usually an issue with traditional threads.
Many popular data processing frameworks that can handle massive parallel computations are written using JVM languages like Scala or Java. The engineers who created these frameworks know that Java is inherently optimized and can handle the parallel processing of massive data.
Java is widely used among embedded systems such as ATMs, printers, and POS systems.
Java’s cross-platform portability reduces development costs for embedded systems by easily porting code to a new architecture, compared to other languages like C that are traditionally used for embedded systems. Moreover, as opposed to the past, you no longer require large memory to use Java for embedded systems.
Data is the new oil, and traditional database management tools may not be capable of storing data that increases over time exponentially.
Java is the backend language that’s been used to develop the most popular big data tools. Apache Hadoop, the most popular big data management tool, is written completely in Java.
Many popular big data tools, such as Apache Spark, Apache Storm, and Apache Kafka, provide Java APIs.
Java was initially developed as a general-purpose language that can run on various platforms like mobile or desktop. As already mentioned, even back then it was written on the “write once, run everywhere” (WORA) principle to promote Java’s cross-platform abilities, one of its strongest features.
Even though Java was released by Sun Microsystems (acquired by Oracle in 2009), most of its parts are under open source licenses with OpenJDK. Quite a few builds of the OpenJDK have been offered by different vendors such as RedHat, Amazon, and SAP. Recently, Microsoft also started offering OpenJDK build.
Java has robust documentation and excellent community support. Oracle recently announced a new learning platform, dev.java, that contains all their Java learning resources.
With Java’s six-month release cadence, developers can leverage new features much faster, and enterprises that don’t want to update quickly can use the long-term support release every two years.
Java is a statically typed language, which means that while declaring variables, you have to specify data types. Even though Java 10 introduced the
var keyword, which gives you a feel of dynamic typing, its scope is limited, and Java is still statically typed.
The benefit of statically typed language is that it gives you some safety net from runtime errors compared to a dynamically typed language like Python. The language itself can’t stop you from writing terrible code, but it can prevent a lot of it at compile time.
Another benefit is the default performance optimization that you get with statically typed language like Java, since there isn’t runtime checking as compared to Python. Of course, measuring performance depends on many factors, so a debate about whether Java or Python is fastest is futile. This benchmark provides an interesting comparison between Python3 and Java, though.
Java 8 first introduced us to functional programming via functional interfaces and lambda expressions features. Even though Java is an object-oriented programming language and developers do coding using OOP concepts such as encapsulation, inheritance, polymorphism, and abstraction, adding the functional programming construct was welcomed by the Java community.
Even though lambda expressions implement functional interfaces, they technically are objects and not functions. But you can mimic functional programming using lambda expressions and treat functions as first-class citizens as per the functional programming concepts.
Java’s backward compatibility allows you to run code compiled in an older version of Java to a newer version. It includes both binary and source code compatibility. It also means that you can always upgrade to more recent and improved Java versions without worrying about whether your code will work or not.
It is a massive relief for enterprises as they don’t have to spend time and energy testing applications with different versions.
Suited for many use cases and robust features, the future of Java is bright. It’s further strengthened by a worldwide community investing in its continued development and growth.
Among Java’s strongest appeals are its platform independence, backward compatibility, and state-of-the-art Java virtual machine feature. Even though these features have made it popular among enterprises, they can impede Java’s growth compared to other languages. For example, backward compatibility has some disadvantages, such as slowing the pace of adding newer features to the language. It also results in some old deprecated classes and methods, which may never get deleted, hence cluttering up the Java API with each new version.
So even though it may not suit all your needs, it works well to help enterprises get things done efficiently.
If you’re considering using Java for your cloud-based applications, have a look at Rookout. It is a tool that lets you debug your distributed cloud-based Java applications while providing a familiar IDE. You get code-level observability with full logging of your data while still running your application live in production. To see how this works, head over to their sandbox and try it out for yourself.