“There are only two kinds of programming languages: the ones people complain about and the ones nobody uses.” — Bjarne Stroustrup.

Digmo + Java 21

When I learned Java, the OOP hype was at its height (late 90s), and Java was pretty much the only language actually implementing those concepts (I did learn C++ before, but come on).

I actually liked how Java evolved in the meantime. I did look into Python, but I never got the fever it seems to cause in people; then again, maybe I just didn’t get in deep enough. I’ve worked on really big Java projects, and they handled (reasonably) fine; I can’t imagine Python at that scale. Then again, newer projects tend to go into the microservice direction – guess Python’s fine for that, but I wouldn’t know. I know Java is.

When I learned Java, the OOP hype was at its peak in the late ’90s, and Java stood out as the language actually implementing those concepts (I did learn C++ before, but come on). I appreciated Java’s platform independence.

I prefer structure and consistency over simplicity is my main point for sticking with Java I guess.


Throughout my career, I’ve had my fair share of encounters with less-than-ideal Java codebases, and there were times when I found myself getting a bit bored with Java.  Then, I worked on many other great projects and started to love Java again.  

It seems like those who criticize and complain about Java are often younger, with more exposure to JavaScript than anything else. Compared to JS, Java can come off as a bit heavy and restrictive—boilerplate everywhere, a type system rigorously enforced by the compiler, and so on. But given the choice, I’d opt for a suboptimal Java codebase over a JS one, no doubt about it. 

It’s only after accumulating some real-world experience, dealing with code scattered across dozens or hundreds of files, that you start to realize the so-called “limitations” of Java are actually safeguards preventing you from shooting yourself in the foot.

“There are only two kinds of programming languages: the ones people complain about and the ones nobody uses.” This is a famous quote attributed to Bjarne Stroustrup, the creator of C++.

Do you think Java has a high number of complaints? You should look at C++. People love and hate that language like it’s an inescapable abusive relationship. 

Python has had its fair share of complaints too.

People constantly talk about how the GIL (global interpreter lock) prevents true, efficient multithreading. So many parts of the Python core rely on the GIL that it will probably never be extricated. As a result, people have had to work around it by using multiple processes and creating message-passing schemes to communicate between the processes. Python performance is also extremely slow unless you rewrite the hotpaths in C/C++. And, of course, the whole Python 2 to 3 transition, etc.


Also. I once worked on a Django project (when it was the cool thing) and thought that Python was better than typed languages(It has its uses but not in a complex system with 1000s of classes).

Once this project scaled up from me being the only developer on it to have multiple people and more than 10k lines of code, it became a complex piece to maintain.

Discovering and switching back to Java was a revelation. It made me realize that I love Java and its ecosystem. That’s why I decided to jot down some of my favorite things about the Java ecosystem. So, if anyone ever badmouths Java, you have 25 reasons to tell them why they are wrong.

1. The Ecosystem is mature

Java has been around for more than 25 years.  Having been a developer working within this ecosystem, it’s fascinating to reflect on how this ecosystem has matured over the years. 

One of the best things about Java’s extensive ecosystem is the wide range of libraries, build tools, and frameworks that I can choose from. 

JVM ecosystem is completely spoilt, with best-in-class libraries for almost every problem, all high performance and well maintained. Plenty of choices when it comes to building tools, including Gradle, Maven, and Bazel for fast reproducible builds. If you’re still new to the ecosystem, Java provides default implementations for various functionalities, such as logging, database connectivity, messaging, and application servers, which is a great starting point.

For instance, let’s say you need logging for your application. No worries, Java has you covered straight out of the box with a default logging option seamlessly integrated into the JDK. What if you don’t like the default option, or it’s not enough? The default logging is just a reference implementation to the logging API. There are other nice logging libraries you can choose from. 

And it’s not just logging. The Java ecosystem gives you options for database connectivity, messaging, application servers, servlets, and more.

2. Write once, run everywhere (WORA)

This is a slogan that we often use to refer to the cross-platform benefits of the Java language. Without sounding vindictive here, I know most developers learning Java nowadays may not realize how groundbreaking this feature was to the development of software. 

Here is some context. A decade before Java was born, C++ was the predominant programming language. However, one of the challenges developers faced was the platform-dependent nature of C++. Code written in C++ needed to be recompiled and often modified to run on different operating systems or hardware architectures.

Source: Geeks for Geeks

3. Backward compatibility

Can you imagine if you had to rewrite code for your program every time there is a new version of Java? That would be very costly and time-consuming, especially for large organizations. 

Java has been around for a while, and that means that there exist software products established on older versions of Java and form the backbone of many enterprises, serving essential functions in various sectors. 

In enterprise development, where projects are large and complex, migration of such systems to the latest Java versions requires careful planning and execution.

Java’s commitment to backward compatibility is very important as it gives assurance to any developer or organization that has invested a lot into developing a system that it will remain operational and can be maintained without requiring a complete rewrite. Backward compatibility of Java (JVM) also streamlines the migration process, facilitating the adoption of new features and improvements without jeopardizing the stability of existing systems.

4. Java strong type system

Java is a strongly typed language as opposed to languages such as Python, which are loosely typed. If you have worked with Python, you would immediately feel the flexibility of assigning values of different types to the same variable, and the language would adapt dynamically. 

 int age = 25;
 String name = "John";

But this flexibility comes at a cost. I remember working on a financial application that dealt with complex calculations involving different numerical data types. With Java’s strong typing, the compiler would flag any attempt to mix incompatible data types or perform operations that could lead to data loss or unexpected results. Some of these obvious bugs might have gone unnoticed with a language such as Python until runtime. 

This is one of the reasons why Java is attractive for developing enterprise applications, especially in industries like banking and finance, where reliability and security are important. Apart from reducing runtime errors that you have to deal with, Java’s strong type system improves code readability by making it easier to understand the intended data types of variables, parameters, and return values.

5. Faster release cycle – constant improvements

Traditionally, as Java developers, we were used to getting new Java features following a major release every few years. However, to keep up with the requirements of modern programming, Java’s release cadence has since changed to only six months after the release of Java 9. However, for enterprise organizations that do not need to jump into new versions, Oracle proposed that it will be releasing an LTS version every three years. 

The more frequent, smaller releases reduce the complexity and risk associated with upgrading to a new Java version. Developers are less likely to face major compatibility issues, as the incremental changes are designed to be more backward-compatible.

6. Best IDEs

Java has come a long way with so many changes and features that make it a great fit for modern development. However, this would only be useful if developers are supported by powerful Integrated Development Environments (IDEs) like IntelliJ IDEA, Eclipse, and NetBeans.

I can’t imagine what it is like writing code in an environment lacking features like intelligent code completion, automated refactoring, seamless version control integration, and many more. However, it is also important to pause and appreciate that this has not always been the case, especially in the early Java days. 

Fast forward to modern IDEs like IntelliJ and Eclipse, which have made development in Java a breeze.  These IDEs integrate seamlessly with build tools like Maven and Gradle, handling compilation, dependency resolution, and project management. Features like intelligent code completion make Java feel less verbose, built-in static code analysis tools make debugging easier, while access to plugins allows you to customize your environment as you like.

7. GraalVM Native Image Support

We have already discussed how the JVM is a wonderful beast that has propelled Java to glory. But it is also true that the JVM has long been plagued by the perception of slow startup times. This can be very problematic, especially now that almost every developer I know is moving towards microservices, serverless computing, and environments where rapid startup and optimized resource consumption are critical. 

There have been efforts to lower memory footprints and achieve faster startup times compared to traditional Java applications running on the JVM. One of the solutions that I’m excited about is the  GraalVM Native Image technology. Oracle GraalVM is a high-performance JDK that can speed up the performance of Java and JVM-based applications using an alternative just-in-time (JIT) compiler known as the Graal compiler.

GraalVM also includes a native image utility that compiles Java Bytecode ahead of time(AOT), allowing applications to start almost instantaneously. The Graal compiler also works as an AOT compiler, producing native executables. So, the input is Java bytecode, and the output is native executables.

Here is an example of a small Java program that reverses a String using recursion.

public class Example {

    public static void main(String[] args) {
        String str = "Native Image is awesome";
        String reversed = reverseString(str);
        System.out.println("The reversed string is: " + reversed);
    }

    public static String reverseString(String str) {
        if (str.isEmpty())
            return str;
        return reverseString(str.substring(1)) + str.charAt(0);
    }
}

You can compile it and build a native image from the Java class.

javac Example.java
native-image Example

The native image builder ahead-of-time compiles the Example class into a standalone executable, example, in your current working directory. You can then run the executable.

./example

Native executables are small, start very fast, and require significantly less CPU and memory. This also makes them ideal for containers and cloud deployments where cost optimization is essential.

And after a very long time coming, I’m very excited about recent developments in JDK 21, Project Loom, and ZGC. GraalVM’s native image also supports virtual threads. We can create GraalVM native images that use Spring Boot (via Spring Boot 3.2) and Java 21’s virtual threads.

8. Open source libraries and frameworks.

Open-source libraries and frameworks stand out as one of the key reasons why Java holds a special place in my toolkit. 

        Source: Google

These libraries and frameworks are like building blocks I can seamlessly integrate into my projects, sparing you as a developer the need to reinvent the wheel for common functionalities. It’s like having a store of well-written and test code readily available for use.

The sheer volume of these libraries means that I cannot get stuck on one solution. I can always pick one that works best for my needs. The open nature of these libraries encourages transparency and accountability. It also means that I can dive into the source code, understand how things work under the hood, and even contribute improvements. 

The list of open source libraries in Java includes JSON parsing libraries like Jackson and Gson, logging libraries like Log4j, SLF4j, and LogBack, unit testing libraries including JUnit, Mockito, and PowerMock, database connection libraries, messaging libraries, and many more.

Java also owes at least part of its popularity to its expansive ecosystem of frameworks. Spring and Springboot is one of my favorite combinations. Other frameworks I have worked with also include the Jakarta Faces, Struts, Hibernate, and Quarkus.

9. Multithreading

Java supports multithreading which means that I can design applications that seamlessly juggle multiple tasks simultaneously, whether it’s processing data, handling user interactions, or managing background computations. Java supports multithreading by either implementing a Runnable interface or by extending Thread Class.

The java.util.concurrent package in Java also provides high-level concurrency utilities, including ExecutorService, ScheduledExecutorService, Future, CyclicBarrier, and more for developing concurrent applications.

public class MyRunnable implements Runnable {
   public void run() {
       // code to be executed in the new thread
   }
}

MyRunnable myRunnable = new MyRunnable();
Thread myThread = new Thread(myRunnable);
myThread.start();

Multicore processors have become the standard in modern computing, featuring multiple processor cores on a single chip. Java’s support for multithreading allows us to capitalize on the modern capabilities of multicore CPUs. This also means that we can develop even more performant Java applications for resource-intensive activities such as gaming, video editing, and scientific simulations.

10. Java’s object-oriented nature

I know what you’re thinking: Java is not the only object-oriented language, so what makes it any special from the likes of Python and C ? Well, unlike some programming languages that have adopted elements of OOP or have introduced features that support OOP concepts, Java was designed with object-oriented principles from the ground up.

Java’s adherence to object-oriented principles such as abstractions, inheritance, polymorphism, and encapsulation makes it a good choice for building complex, scalable, and maintainable software systems. There are many benefits that I find beneficial from Java’s support for the OOP paradigm. These benefits include building modular, flexible, readable, maintainable, and scalable Java applications.

11. Memory management and garbage collection

   Source: Digital Ocean

I’ll be honest, I hate taking out the trash. It’s one of those chores that always feels like a hassle, and I’m sure many can relate. In manual memory management, developers are responsible for allocating and deallocating memory, much like deciding when and how to dispose of each piece of trash. Forget to take out the trash, and you end up with clutter, just as forgetting to deallocate memory leads to memory leaks and performance issues.

Now, consider Java’s automatic memory management; it is like having a reliable trash collection service. In Java, the garbage collector assumes the role of the diligent garbage collector, autonomously identifying and disposing of memory that is no longer needed. 

Having worked with C++ before transitioning to Java, I have had the experience of both sides. C++ provides unparalleled control over memory management but also a lot of responsibility to you as a developer to ensure that there are not memory leaks. 

Java, on the other hand, there is no need to worry about the low-level system technical details or matters of manual garbage collection, underlying OS, or tracking of memory allocations and deallocations. The garbage collector also automatically identifies and reclaims memory that is no longer in use. This reduces the risk of memory leaks.

As a developer, this means that I can focus more on the business logic and higher-level aspects of application development. And if you’re working with a team, Java’s automatic memory management improves your development cycles and increases productivity.

12. Observability and monitoring

Until recent years, many developers I know were primarily engaged in developing and maintaining monolithic applications. Troubleshooting and resolving bugs had a certain level of straightforwardness, with the entirety of the application residing in a single, cohesive codebase. Tracking down issues in these types of applications is like navigating a well-mapped territory.

This has changed dramatically with the rise of microservices, serverless computing, and distributed systems. Identifying and resolving issues is increasingly difficult because microservices run as independent services communicating over a network. When an issue arises, it may not be confined to a single codebase. Here are some of my favorite tools that I use when working with Java.

Standard Profilers.

VisualVM.

VisualVM is like my trusty sidekick when it comes to understanding what’s happening under the hood of my Java applications. It brings together JConsole and VisualGC, giving me a visual playground for monitoring threads, heap usage, and CPU profiling. Plus, it plays nicely with various JDK utilities, making it a reliable tool.

YourKit.

This profiler is like having a secret agent in my toolkit. It digs deep into method-level details, exposing execution times and memory allocations. It gives you a simple, easy, and secure way to profile in clouds, containers, and clustered environments.

APM Tools.

New Relic.

New Relic is my go-to for Application Performance Monitoring (APM). It feels like having a personal assistant watching over my applications 24/7. From real-time insights to detailed transaction traces. The alerting features are my safety net, ensuring I’m alerted to any unexpected behavior.

AppDynamics.

AppDynamics, my performance artist! It takes a holistic approach to monitoring, observing and visualizing my full technology stack, from database and server to cloud-native and hybrid environments. I appreciate how it helps me understand the impact of performance on end-users, making it not just a monitoring tool but a user satisfaction tool.

Logging Solutions.

Log4j.

This is like the elder statesman of logging frameworks. It’s been with me through thick and thin, faithfully logging events and errors. The flexibility it offers in configuring appenders and filters makes it a reliable workhorse for any logging needs.

SLF4J.

SLF4J is like my logging Swiss Army knife. It doesn’t log directly but acts as a facade, allowing me to plug in different logging implementations seamlessly. This flexibility makes it a smart choice when working with various libraries that have their logging preferences.

Observability in Java.

Digma: Continuous Feedback (FC).

If you can’t see how your code performs in the real world, you can’t make informed design decisions and assess the impact of your changes. By closing the loop between observability and code, Digma opens the way for a new method of development.

Digma is a Continuous Feedback(CF) tool that is meant to streamline the work of collecting and processing data about your code from OTEL observability sources. Digma runs locally as an IDE plugin and collects data about your code, from traces to logs and metrics, while you’re coding. This means you can catch issues and get insights in real time. 

Prometheus and Grafana.

Prometheus and Grafana, my dynamic duo! Prometheus scrapes metrics from my applications, and Grafana turns those metrics into beautiful, customizable dashboards. The open-source nature and the active community around these tools make them my dynamic observability duo.

Elastic Stack (ELK).

An Open, extensible, full-stack observability built on AI and search. Elasticsearch, Logstash, and Kibana together they create a powerhouse for searching, analyzing, and visualizing logs. The ability to correlate logs, metrics, and traces gives me a complete investigative toolkit. 

13. Functional programming support

Function programming is yet another programming paradigm supported by Java from the release of Java 8.  In functional programming, we treat functions as first-class citizens, meaning they can be assigned to variables, passed as arguments, and returned as values. 

Some of the features in this paradigm make Java an attractive programming language. Embracing the functional programming features in Java has been a game-changer for me as a developer. The introduction of lambda expressions and functional interfaces has allowed me to write code that’s not only more concise but also incredibly expressive. With its ability to harness parallel processing on multicore processors, the Stream API has given my applications a performance boost.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        
        int sum = list.stream().reduce(0, (a, b) -> a + b);

        System.out.println(sum);
    }
}

I’ve found that adopting a declarative style, as encouraged by functional programming, makes my code more readable and easier to understand. The emphasis on immutability and avoiding side effects has had a tangible impact, making my code more predictable and maintainable. And when it comes to testing, the prevalence of pure functions has made testing easier.

14. Java’s rich documentation

If you have a chance to work on a team project, you will certainly understand the importance of documentation. Personally, I consider documentation as a user manual that explains what the code does, how it does it, and why it does it that way. And if you’re a beginner learning Java, it is like having a mentor by your side. 

The documentation includes a rich set of code samples, tutorials, developer guides, API documentation, and more that you can use to quickly develop your prototype and scale it up to a real-world application. 

Documentation is not any good if it’s not up to date. Java’s documentation is kept up to date and revised regularly to reflect new developments in the ecosystem by developers and experts. The documentation is also structured in such a way that it is easy to find information about a specific class, method, or concept. 

15. Build tools and dependency management

An average Java/Spring boot project could have dozens, if not hundreds, of direct and transitive dependencies. Managing such dependencies manually can be a headache, especially when working on a large enterprise project, because of issues such as version compatibility. By leveraging build tools, you can unify disconnected builds when working with multiple developers who are running their builds locally.

However, build tools like Maven and Gradle simplify building, testing, and managing dependencies, making your work easier as a developer. To begin with, these tools also save you the trouble of staying informed about updates and security patches about different dependencies by fetching dependencies from repositories and checking for updates automatically.

Build tools also enforce conventions and standards for project structure and configuration, making it easy to understand and work with a team of other Java developers. 

16. Robust testing features

As much as we developers dread resolving bugs and working with QA engineers, we also acknowledge that comprehensive testing is one of the most effective ways to ensure our applications are as bug-free as possible.  

Choosing a language with robust testing features can significantly ease the burden and contribute to a more reliable and maintainable codebase. This is one of the reasons why I vouch for Java as the go-to programming language for building bug-free software.

Whether it’s unit, integration, or end-to-end testing, Java provides a comprehensive set of tools to write comprehensive tests. JUnit is the de facto standard for unit testing in Java. It provides a simple and elegant way to write and execute tests.  JUnit uses annotations such as @Test, @Before, @After, @BeforeClass, and @AfterClass to define the lifecycle of test methods. This allows you to set up preconditions before running tests.

As much as we developers dread resolving bugs and working with QA engineers, we also acknowledge that comprehensive testing is one of the most effective ways to ensure our applications are as bug-free as possible.  

Choosing a language with robust testing features can significantly ease the burden and contribute to a more reliable and maintainable codebase. This is one of the reasons why I vouch for Java as the go-to programming language for building bug-free software.

Whether it’s unit, integration, or end-to-end testing, Java provides a comprehensive set of tools to write comprehensive tests. JUnit is the de facto standard for unit testing in Java. It provides a simple and elegant way to write and execute tests.  JUnit uses annotations such as @Test, @Before, @After, @BeforeClass, and @AfterClass to define the lifecycle of test methods. This allows you to set up preconditions before running tests.

// EmployeeServiceTest.java

import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

@SpringBootTest
public class EmployeeServiceTest {

    @Mock
    private EmployeeRepository employeeRepository;

    @InjectMocks
    private EmployeeService employeeService;

    @Test
    public void testGetAllEmployees() {
        // Mocking repository response
        when(employeeRepository.findAll()).thenReturn(new ArrayList<>());

        // Test case for getAllEmployees method
        List<Employee> result = employeeService.getAllEmployees();

        // Assertion
        assertEquals(0, result.size());

        // Verify that the repository method was called
        verify(employeeRepository, times(1)).findAll();
    }

}

Writing tests with JUnit is straightforward and integrates seamlessly with popular Java IDEs (Integrated Development Environments) such as Eclipse, IntelliJ IDEA, and NetBeans. 

Other tools I have found useful for testing in the Java ecosystem include TestNG for integration testing, Cucumber for behavior-driven development, and Selenium for automating functional and regression test cases. Mockito is also a powerful mocking framework that you can use in conjunction with other testing frameworks such as JUnit and TestNG.

17. Large community

The Java community has been instrumental in my journey as a Java developer. I have lost count of the number of times that I have sought for help and guidance from the Java community. Whether I’m stuck on a bug, exploring a new library, or seeking best practices when implementing a new solution, the community has proven to be an invaluable source of information. 

The Java community is one of the most responsive community and you will always be sure to receive help almost instantly. For instance, I’m part of the r/java community of Reddit, consisting of thousands of Java developers.  Other communities across platforms such as Stack Overflow and Github include a diverse range of developers, from beginners to seasoned professionals. This diversity is a strength, as it means there’s a wealth of expertise covering various domains and levels of experience.

Apart from online communities, several Java events, conferences, and meetups provide opportunities for developers to connect, share experiences, and learn from one another in person. These gatherings promote networking and collaboration, contributing to the sense of community among Java developers.

18. Java annotations support

ava annotations are a very popular topic but also contentious. Annotations were introduced in Java 5, and we all got excited. Personally, I shared in this enthusiasm. The introduction of annotations signaled a departure from the era of extensive Hibernate or Spring XML configuration files. Instead, annotations allowed us to embed information, instructions, or configurations directly within the source code precisely where needed. 

Predefined annotations
@Deprecated
@Override
@SuppressWarnings
@SafeVarargs
@FunctionalInterface

2. Meta-annotations
@Retention
@Documented
@Target
@Inherited
@Repeatable

3. Custom annotations
It is also possible to create our own custom annotations.

Java annotations have certainly improved code clarity and expressiveness by reducing the need to refer to external documentation. Annotations can also help cut down some boilerplate code by encapsulating repetitive tasks or configurations. For instance, using annotations, you can define aspects like dependency injection, ORM mappings, and transaction boundaries. 

Some developers feel that although annotations are convenient and have many benefits, they remain skeptical about some of the annotations.


Security features

Programs we write are not just lines of code. They also handle sensitive information, such as personal user data, financial details, and proprietary business information. Users expect us to protect their information and provide a secure environment. For businesses, especially those  involved in software development or innovation, securing applications is important to prevent the theft of intellectual property. Protecting source code, algorithms, and proprietary information is crucial for maintaining a competitive edge.

Java has a lot of features that make it easier to develop secure applications. Some of these features include cryptography, public key infrastructure, secure communication, authentication, and access control. You can also access plenty of APIs and tools, allowing you to seamlessly integrate security measures into your Java applications, from cryptography to smart card I/O and other authentication protocols that ensure secure communication.

This list is far from complete. You can learn about many more features that you can leverage from the official Java Security Guide. 

20. Rich API set

Java is also known for its rich set of Application Programming Interfaces (APIs) that provide a standardized way for us developers to interact with various software components, libraries, and services. These APIs include a rich collection of pre-compiled classes, interfaces, and methods that provide ready-to-use functionality.

Let’s say you are building a car from scratch. Java APIs make it feel like you assembling already manufactured parts sourced from suppliers as opposed to manufacturing the parts yourself before assembling them. In this case, you have the flexibility of choosing APIs that exactly match your needs. The APIs are standardized and well-documented, making it easy to use them.

Java’s API is that it abstracts away the complex details of component building, allowing you to focus on assembling a fully functional car, which is your application in this case. These APIs can help you accomplish different tasks across domains such as networking, IO,  file handling, database connectivity, multithreading, and many more.

The Java API is organized into several packages. Some of the most common ones include java.lang, java.util, java.io, and java.net.

21. Java’s Performance

Another thing that has kept me in the loop with Java over a long period is witnessing the continuous improvements in Java’s performance over time, which has validated my choice as the primary programming language.              

These improvements in terms of performance have been great in fixing issues and also allowing me to build performant applications capable of serving the modern customer. Some of the notable.

For instance, the Java Virtual Machine (JVM) has seen significant optimizations with each new release. Just-in-time (JIT) compiler improvements, garbage collection enhancements, and better runtime profiling have collectively contributed to faster execution and reduced memory overhead.

Project Valhalla introduced value types, allowing us to define more efficient, compact data structures. This reduces memory consumption and also enhances cache locality, resulting in substantial performance gains, especially in scenarios with large amounts of data.

More recently, we have see JDK 21 usher in 15 features, including a key encapsulation mechanism API, virtual threads, and previews of string templates and structured concurrency which significantly enhance Java.

A leap forward with Virtual threads in JDK 21 and other features

22. Structured concurrency

The proposed API for structured concurrency remains a purview feature in JDK 21 with an aim to simplify concurrent programming by treating groups of related tasks in different threads as a single unit of work. This approach enhances error handling, cancellation, reliability, and observability.  The feature is not intended to replace existing concurrency constructs in java.util.concurrent. 

This feature will allow us to do away with the complexity of managing tasks and subtasks in concurrent programming using existing constructs like ExecutorService and Future. The lack of inherent relationships between tasks and subtasks leads to challenges in error handling, cancellation, and observability. The proposed structured concurrency approach seeks to align the syntactic structure of the code with the runtime hierarchy of tasks, making concurrent programming more readable, maintainable, and reliable.

23. Virtual Threads

Initially introduced as a preview feature in JDK 19 and JDK 20, virtual threads have now officially arrived in JDK 21. Each instance of java.lang.Thread has traditionally been associated with a platform thread, binding it to an underlying OS thread throughout its lifecycle. 

However, virtual threads bring a paradigm shift. There are still instances of java.lang.Thread but operate in a manner where they run Java code on an underlying OS thread without monopolizing it. This breakthrough enables multiple virtual threads to share a single OS thread efficiently. Unlike platform threads, virtual threads do not lock down precious OS threads, and their numbers can scale well beyond the constraints of OS thread availability.

Virtual threads are a compelling addition to JDK 21. They can have a substantial positive impact on application performance, particularly in scenarios where there’s a high volume of concurrent tasks, often exceeding several thousand.

You now have the flexibility to choose between virtual threads and traditional platform threads based on your application’s specific requirements. 

24. Pattern Matching for Switch

This exciting feature had its origins in the JDK 17 proposal and went through a series of refinements in JDK 18, JDK 19, and JDK 20. Now, it’s all set to become a part of JDK 21, with additional improvements based on the feedback and experience from the Java community.

The main aim of this feature is to expand the capabilities and versatility of switch expressions and statements. It is also about letting patterns play a more prominent role in case labels, allowing for a bit more flexibility when it comes to handling null values and making switch statements safer by requiring comprehensive coverage of all possible input values using pattern switch statements.

 And don’t worry if you’re already using switch expressions and statements; the goal is to ensure that they’ll continue to work just as they do now, without requiring any changes.

25. String Templates

String templates have arrived in Java, finally bringing support for string interpolation, something developers have longed for. Until now, combining multiple strings or wrangling with string.format was the only way to go, and let’s admit it, it was annoying. But with Java 21, we’re in for a treat. This new feature empowers developers to take Java’s string literals and text blocks and infuse them with string templates. 

int x = 10;
int y = 20;
String s = STR."\{x} + \{y} = \{x + y}";


The main goal is to streamline the creation of dynamic Strings by simplifying the expression of values that can be compiled at runtime. Another goals is aim is to enhance readability, addressing the verbosity associated with StringBuilder and StringBuffer classes. Finally, string templates will also help us overcome security concerns associated with existing string interpolation techniques in other programming languages.

Summing it up

Java is not easier; it’s just better! Also, modern Java is very fast. Easy to read -> easy to maintain. It is hard trying to be smart in Java -> don’t need to deal with rockstar coworkers. And let’s all be honest; we do care about the salary, and Java developers get high salaries. There are lots of jobs available as most corporations and large organizations use Java as the main language. Some I know who are still working with Java 8 and are happy.

FAQ.

What makes Java a popular programming language?

Java has gained popularity due to its platform independence, robustness, and extensive community support. It’s widely used in various domains, from web development to enterprise applications.

What are the advantages of using Java for enterprise-level applications?

Java provides a stable and scalable environment, supports multithreading, and has many of libraries and frameworks like Spring Boot that simplify enterprise application development.

How does Java support cross-platform development?

Java achieves cross-platform compatibility through the “write once, run anywhere” principle, allowing Java code to be executed on any device with a Java Virtual Machine (JVM).


Install Digma for Free!

Spread the news:

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *