Reference version: Java SE 17 (Long-Term Support)
Table of Contents
- 2.1 Introduction and target audience
- 2.2 Tools used in this course
- 2.3 What you will learn
- 2.4 Is Java dead?
- 2.5 The three-part Java platform
- 2.6 Compile and run Java code
- 2.7 Module 2 Summary
- 3.1 Java philosophy: readability
- 3.2 Java philosophy: stability
- 3.3 Java philosophy: openness and community
- 3.4 Java language
- 3.5 The Java runtime: portability
- 3.6 The Java runtime: a managed runtime
- 3.7 Comparison with other languages
- 3.8 Module 3 Summary
- 4.1 Java Desktop
- 4.2 Java Enterprise
- 4.3 Java in the Cloud
- 4.4 Language Features
- 4.5 Module 4 Summary
- 5.1 Spring Framework
- 5.2 Other popular Java libraries
- 5.3 Development tools
- 5.4 Alternative languages on the JVM
- 5.5 Conclusion and next steps
1. Course Overview
This course answers fundamental questions:
- What is the difference between Java and Java Enterprise?
- Why adopt Java?
- What can Java do for me?
Java 17 is used as a reference, but almost all concepts apply to all versions of Java. The goal is not to learn to program in Java, but to understand and appreciate the Java ecosystem as a whole.
This course is aimed at:
- A developer who wants to evaluate Java as a technology.
- A Java developer returning after several years away and wanting a refresh.
- A technical manager who wants to understand what his Java developers are talking about.
The course assumes no prior experience with Java, although general software development experience is helpful.
2. Introduce the Java platform
2.1 Introduction and target audience
Sander Mak identifies three categories of people who take this course:
- The non-Java developer: You want to evaluate Java as a technology or simply understand what it is.
- The Java Developer Returns: You haven’t developed on the platform in a while and want a refresher on modern Java and its ecosystem.
- The technical manager: you are not a developer but want to understand Java to communicate effectively with your teams.
Some code will be presented in this course, but only to illustrate the big picture. It’s never about teaching the code itself.
2.2 Tools used in this course
This course is based on Java SE 17, the Long-Term Support (LTS) version. You are welcome to install Java and try the examples, but this is neither necessary nor expected.
Version policy:
- Before Java 9: a major version every 3 to 4 years.
- Since Java 9: two major releases per year.
- LTS versions (Java 8, Java 11, Java 17…) receive the widest community adoption.
Java distributions: Many vendors package and distribute Java for installation. Everything you learn in this course also applies to any distribution, regardless of vendor:
- Oracle JDK
- Eclipse Temurin (Adoptium)
- Amazon Corretto
- Microsoft Build of OpenJDK
- Azul Zulu
- etc.
2.3 What you will learn
The course consists of four modules:
| Module | Title | Duration |
|---|---|---|
| 2 | Introducing the Java Platform | 17m 5s |
| 3 | Adopting Java | 25m 47s |
| 4 | Working with Java | 19m 53s |
| 5 | The Wider Java Ecosystem | 27m 2s |
- Module 2 — Introducing the Java Platform: the fundamental elements of the Java platform. Spoiler: it’s not just a programming language.
- Module 3 — Adopting Java: when is Java the right technology? Unique properties, philosophy, comparison with other languages.
- Module 4 — Working with Java: from desktop applications to the cloud, types of Java applications and language features.
- Module 5 — The Wider Java Ecosystem: the tools, libraries, frameworks and alternative languages that constitute the true power of Java.
Is Java 2.4 dead?
Java has been around since 1995. Since the turn of the millennium, voices have regularly declared Java dead:
- Articles in 2002, 2008, 2020, 2021, 2022…
“There are two types of languages: the ones people complain about and the ones nobody uses.”
Java is clearly in the first category — still very widely used. Its areas of application have evolved, but the platform is still alive.
Highlights:
- Java runs on devices as small as SIM cards and ATM cards, up to the most powerful servers in the cloud.
- Java literally runs on billions of devices.
- In 2022, Java appeared in the New York Times crossword: “programming language named for a drink named for an island”.
Java ≠ JavaScript: Despite the similarity in names, there is absolutely no relationship between Java and JavaScript — neither technical nor commercial. The name “JavaScript” was simply used by Netscape in the 90s to take advantage of the popularity of Java at that time.
2.5 The Java platform in three parts
The Java platform is made up of three parts:
┌─────────────────────────────────────────┐
│ Application Bytecode │
├─────────────────────────────────────────┤
│ Java Standard Edition APIs │
│ (Bibliothèque standard) │
├─────────────────────────────────────────┤
│ Java Virtual Machine (JVM) │
│ (Environnement d'exécution) │
└─────────────────────────────────────────┘
Hardware / OS (natif)
-
The Java programming language: governs the form of source code — what keywords to use, what rules to follow when writing code.
-
The execution environment (JVM — Java Virtual Machine): where the Java code executes. It abstracts the underlying operating system and processor architecture.
-
The standard library (Java Standard Edition APIs): contains commonly used features that you can reuse in your applications — collections framework, date and time management, HTTP client, and much more. This is what SE means in Java SE: Standard Edition.
When people talk about Java, they can talk about any of these parts individually or the combination of all three. This ambiguity is common.
The JDK — Java Development Kit: The three parts are grouped into what is called the JDK. You cannot develop Java applications without a JDK. It contains all the tools needed to create and run Java applications:
javac— the Java compilerjava— the command to launch the JVMjavadoc,jshell,jlink, etc.
Java application creation flow:
Code source Java (.java)
↓ [javac — Compilateur Java]
Bytecode (.class)
↓ [JVM — Java Virtual Machine]
Instructions machine natives
↓
Exécution sur le hardware
Bytecode is a mid-level representation, CPU and operating system agnostic. Applications can also use third-party libraries, which are also distributed in bytecode form.
2.6 Compile and run Java code
Here is a concrete example of a simple Java application, the classic Hello World:
Hello.java file:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello Pluralsight!");
}
}
Explanations:
- Any Java source file must end with the
.javaextension. - Inside, we declare a class (
Hello). The class groups the code in its braces and gives it a name so that it can be referred to from other codes. - The class contains a method (
main). A method groups together instructions that can be executed. System.out.println("Hello Pluralsight!")is an statement that prints text to the console.Systemis a class in the Java SE standard library.
Compile and run steps:
# Étape 1 : Compiler le source en bytecode
javac Hello.java
# Crée le fichier Hello.class contenant le bytecode
# Étape 2 : Exécuter le bytecode avec la JVM
java Hello
# Sortie : Hello Pluralsight!
The Hello.class file contains the bytecode — a low-level but still CPU- and OS-agnostic representation that can be more easily executed by the Java runtime.
2.7 Module 2 Summary
Key points:
- The Java platform is made up of three parts: the Java programming language, the standard library (Java SE APIs) and the runtime environment (JVM).
- The JDK (Java Development Kit) brings together these three parts plus the associated tools.
- The flow is: Java source code → compilation with
javac→ bytecode → execution on the JVM. - The JVM translates the bytecode into native machine instructions for the underlying hardware.
3. Adopt Java
This module answers the question: when and why to adopt Java?
3.1 Java philosophy: readability
There are an estimated 10 million Java developers worldwide, using the platform to build applications and services in almost every industry imaginable.
Java was created primarily as a language for getting work done. It is an industrial language, with no underlying academic motivations. Its goal is not to be at the cutting edge of programming languages, but to be practical and efficient for development teams.
Fundamental principle: Optimize for readability.
This principle is based on the observation that you read existing code much more often than you write it. Software development is a fundamentally collaborative effort. You spend a lot of time reading and understanding other people’s code.
Java tries to provide a clear way to express its intentions. Understandable code is encouraged over “intelligent” code:
“You may understand your ingenious hack today, but will you understand it tomorrow? In a month? In a year? Or will your colleague understand it?”
This is particularly crucial in large-scale collaborative development.
3.2 Java philosophy: stability
Java is conservative in adding new features. When a language has too many different ways to achieve the same result, its learning curve increases dramatically.
Guiding principles:
- First, do no harm: It is much more difficult to remove a poorly designed feature than to introduce a new one.
- All new features should empower developers without conflicting with existing features.
- New features should make Java developers more productive while maintaining the simplicity of the language.
Java 12 — Preview Features: Language features are first introduced behind an experimental flag. Real-world feedback can thus be collected to refine features before finalizing them.
*Backward Compatibility:
This is one of the most important principles of Java. Imagine the massive time wasted by 10 million developers when Java breaks backwards compatibility.
- Code written for earlier versions of Java will compile and run on newer versions unchanged.
- Code already compiled to bytecode and distributed (e.g. as a library) will also work on later versions.
- This allows the huge ecosystem of Java libraries to thrive.
- A rare compatibility break occurs with relatively long and predictable deprecation cycles.
Java is stable, but constantly modernizing. Coupled with the preview features mechanism and frequent Java releases, it works surprisingly well.
3.3 Java philosophy: openness and community
Oracle owns the Java brand and is the main engine behind the platform, but Java itself is an open technology, and this manifests itself in several ways.
1. Formal specifications:
Java is rigorously specified, unlike some other languages:
- The Java Virtual Machine Specification: how a JVM should execute bytecode.
- The Java Language Specification: What valid Java source code looks like and how it should be compiled.
These specifications are managed via the Java Community Process (JCP). JCP members are relevant vendors in the Java world and other leaders in the Java community, who together lead the future of the platform.
2. OpenJDK — Open source reference implementation:
Because Java is formally specified, there may be more than one implementation of the Java platform outside of Oracle’s efforts. For example:
- IBM has its own JDK implementation.
- Amazon, Microsoft, Azul, Red Hat, and other vendors offer their own Java distributions.
As of 2020, all JDK development has been moved to GitHub. You can look at the repositories and see exactly what’s going on. The OpenJDK project also hosts many experimental subprojects explored on the openjdk.net site. There are also Java Enhancement Proposals (JEPs), which provide insight into the direction of the platform.
3. The Java developer community:
- More than 10 million Java developers worldwide: a massive talent pool for any organization.
- Hundreds of Java User Groups around the world, organizing events where developers meet, exchange experiences and develop their skills.
3.4 Java language
Familiar syntax (C family): Java’s basic syntax and constructs are close to C and other C-family languages:
- The different scopes are delimited by braces
{}. - Familiar forms of
if-then-else,forloops,switchstatements.
Object-oriented programming: Java introduced a great innovation compared to more procedural languages like C: object-oriented. By introducing classes as a way to combine data (fields) and behavior (methods), a new paradigm for modeling code emerged. Classes can inherit from each other for increased modeling flexibility.
Scalable development model: Java offers a very scalable development model, suitable for large codebases:
Classe (class)
└── regroupe les données (fields) et comportements (methods)
Package
└── regroupe les classes connexes
Module
└── regroupe les packages connexes (ajout récent depuis Java 9)
Each level of abstraction allows internal details to be encapsulated and fine-grained access control between elements to be applied.
Static Typing:*
Java is a statically typing language: you must be explicit about the types of the variables you create. The compiler can detect type errors at compile time.
Example compilation error:
// Erreur : incompatibilité de types
public class Hello {
public static void main(String[] args) {
int message = "Hello Pluralsight!"; // String cannot be converted to int
System.out.println(message);
}
}
Error generated by the compiler:
error: incompatible types: String cannot be converted to int
In languages without static types, such an error would only be detected at runtime — which is problematic because a user will see an unexpected error at runtime. Types help detect bugs early, when they are reported by the compiler.
3.5 The Java runtime: portability
The JVM provides three main advantages:
- Portability
- Managed runtime (garbage collection, multithreading)
- Runtime performance
The WORA principle — Write Once, Run Anywhere:
Write your application once in Java, and the Java platform takes care of running it on Linux, Windows or other platforms, including different CPU architectures.
Why is this important? Apple has changed CPU architecture several times: PowerPC → Intel → ARM. Having to recompile or, worse, rewrite your application every time you want to target a different platform would be terrible.
Operation:
Application Bytecode
│
▼
┌───────────────────┐ ┌───────────────────┐
│ JVM Linux x64 │ │ JVM Windows x86 │
└───────────────────┘ └───────────────────┘
│ │
▼ ▼
Hardware Linux Hardware Windows
The same bytecode compiled once can run on different JVMs (Linux, Windows, ARM, etc.) without modification. JVMs take care of the translation to native machine instructions.
Limitation: If you need to write code that integrates deeply with the operating system (device drivers, strict real-time applications), the advantages of the JVM turn into disadvantages. Unmanaged languages are better suited in these cases.
Over the past 20 years, JVMs have emerged for everything from mainframes to embedded ARM devices. Java has truly proven that it is possible to write once and run everywhere.
3.6 The Java runtime: a managed runtime
The JVM is sometimes called a managed runtime and Java a managed language because the JVM handles many concerns for you.
1. Automatic Memory Management (Automatic Memory Management):
In low-level languages, you must indicate where and when to allocate memory for objects created in your code, and especially when to free this region of memory. In Java, the JVM manages memory allocation transparently. You don’t have to worry about the location of objects in memory.
2. Garbage Collection:
The garbage collector periodically checks the allocated memory and determines which objects are still in use and which are no longer in use. Memory belonging to unused objects is freed for reuse. Java developers only have to think about business logic, not memory management.
3. Multithreading:
Modern CPUs have many cores available. By creating threads from your Java code, you can easily use the resources of multiple cores in parallel. The JVM supports scheduling code running on different threads to all CPU cores. Combined with the concurrency APIs offered in the Java standard library, this makes it easier to write high-performance multi-threaded code.
4. Just-In-Time (JIT) compilation:
Although the JVM introduces a layer between code and machine instructions, this configuration allows JIT compilation:
- The JVM analyzes bytecode execution at runtime.
- It can decide to compile just-in-time into machine instructions very specific to the current CPU, unlocking maximum performance.
Comparison with unmanaged languages:
| Criterion | Java (managed) | C/C++ (unmanaged) |
|---|---|---|
| Memory management | Automatic (GC) | Manual |
| Portability | JVM by platform | Recompilation required |
| Compilation | Bytecode + JIT at runtime | Direct machine code (upfront) |
| CPU optimizations | CPU-specific in runtime | Generic (less optimized) |
| Risk of errors | Less (no segfaults) | Higher |
“When web companies grow up, they turn into Java shops.” — James Governor A notable example: Twitter was infamous for its outages. After migrating from Ruby to Java and the JVM, this problem has largely disappeared.
3.7 Comparison with other languages
Java vs C# (.NET):
C# on the .NET platform is the closest sibling of the Java platform.
| Criterion | Java | C#/.NET |
|---|---|---|
| Runtime | JVM | CLR (Common Language Runtime) |
| Bytecode | Java bytecode | Intermediate Language (IL) |
| Evolution of language | Slow, conservative | Faster, more features |
| Cross-platform | Yes, from the beginning | .NET Core yes, but fewer targets than the JVM |
| Open source | OpenJDK | .NET Core (open source) |
| Ecosystem | Bigger and more diverse | Strongly associated with Microsoft |
| Backward compatibility | Very strict | More liberal historically |
If your company is already heavily invested in the Microsoft ecosystem, choosing C# and .NET may make sense. Otherwise, Java provides access to the world’s largest open source library ecosystem.
Java vs C / C++:
C and C++ are low-level languages used for systems programming.
| Criterion | Java | C/C++ |
|---|---|---|
| Type | Managed | Unmanaged |
| Memory management | Automatic | Manual |
| Risk | Fewer memory bugs | Segfaults, possible corruptions |
| Portability | Very easy (JVM) | More difficult |
| Typical usage | Enterprise, web, cloud applications | Drivers, embedded systems, real-time |
| Compilation | Bytecode → JIT | Machine native code directly |
C and C++ are an excellent choice for system-level development (drivers, strict real-time systems), but generally less suitable for developing large-scale enterprise applications or web services.
Java vs JavaScript:
Reminder: Java and JavaScript are two completely different things.
| Criterion | Java | JavaScript (+ Node.js) |
|---|---|---|
| Typing | Static | Dynamic (TypeScript = optional) |
| Compilation | Compiled to bytecode | Interpreted (JIT in V8) |
| Multithreading | Native, multiple threads | Single-threaded (event loop) |
| Portability | JVM | Node.js on server, client browser |
| Memory management | Automatic GC | Automatic GC (V8) |
| Error detection | At compile time (types) | Especially in execution |
TypeScript (which adds optional typing to JavaScript) has gained popularity in part because preventing errors early offsets the inconvenience of having to specify types.
Single-threaded execution of JavaScript can be compensated for with Workers, but the use of multi-core hardware is not as seamless as with Java’s threading capabilities.
There is no “best” language or platform. You decide what best suits your needs and context.
3.8 Module 3 Summary
This module covered many perspectives that play a role in the potential adoption of Java:
- Philosophy: readability and stability are fundamental drivers. The goal of Java is not to write the shortest code possible, but to write clear and maintainable code.
- Openness: Java is open, both in the sense of an active community and in the sense of OpenJDK as an open source reference implementation.
- Object oriented with familiar syntax.
- Managed Runtime: Java allows developers to write high-level code while achieving excellent performance from the JVM, in a way that is portable across platforms.
- Java fits into a rich technology landscape with distinct advantages and disadvantages compared to C#, C/C++ and JavaScript.
4. Working with Java
This module explores how Java is used in practice: what types of applications people build with Java.
4.1 Java Desktop
Desktop applications are no longer as popular as they once were, with web applications becoming the norm. However, there are still valid reasons to create desktop applications:
- Offline use case.
- User interface complex enough to build in a browser.
- Interaction with hardware.
Swing:
Java comes with a complete GUI toolkit called Swing. It offers fully Java-based UI controls that do not rely on the operating system for rendering native UI controls. This means that Swing apps render and look the same on different platforms.
- Several looks and feels are available — some imitate the look of a particular OS, others aim for a uniform cross-platform look.
- Notable example: IntelliJ IDEA, the very popular Java IDE, is itself built with Swing.
JavaFX:
Later in Java’s history, a new GUI toolkit called JavaFX was developed.
- You can create code-driven UIs via JavaFX APIs.
- There is also a declarative XML-based format for UIs called FXML, allowing the design of the UI to be decoupled from the underlying application code.
- Much more advanced UI components than Swing.
- Support for hardware accelerated animations, effects and 3D graphics.
- Stylization via CSS (like for the web).
Examples of JavaFX at NASA:
- Deep Space Trajectory Design application.
- Ground Flight Dynamics System supporting James Webb Space Telescope operations.
Development:
- Until Java 10: JavaFX was included in the JDK.
- From Java 11: JavaFX is separated from the JDK and developed in its own project called OpenJFX (subproject of OpenJDK).
If you want to create a Java desktop application with just a standard JDK, Swing is your best choice. For a modern desktop application with all the advanced capabilities, OpenJFX is worth considering.
Desktop application development has become a relatively small niche. Mainstream Java development is geared more towards server-side applications.
4.2 Java Enterprise
Java plays a major role in the development of enterprise applications which generally have high complexity:
- Web front-ends
- Transactional databases
- Integration with many external systems
- Scheduled Tasks
- etc.
This goes well beyond what the Java SE APIs offer.
The problem of multiple dependencies:
Without a structured solution, you could end up with dozens or even hundreds of external libraries. The disadvantages:
- Library selection (expensive in time and effort).
- Making libraries work together (hard).
- Update management and ongoing compatibility.
Java EE (Java Enterprise Edition):
Java EE appeared as a solution to this problem. It offers an integrated set of technologies to efficiently develop enterprise applications. Launched in 1999, Java EE offers APIs for:
- Interact with databases (JPA — Java Persistence Architecture)
- Building web front ends (JSF — Jakarta Server Faces)
- Apply a uniform application security model
- Support asynchronous messaging via message brokers
- Manage XML and JSON (JSON-B, JAXB)
- Write services with transactional business logic (EJB — Enterprise JavaBeans)
Application Servers:
Java EE is just a specification. You need an implementation to run the applications:
| Application server | Supplier | Type |
|---|---|---|
| Oracle WebLogic | Oracle | Commercial |
| Red Hat WildFly | Red Hat | Open Source |
| IBM WebSphere | IBM | Commercial |
| Apache TomEE | Apache | Open Source |
| GlassFish | Eclipse/Oracle | Open Source |
| Payara | Payara | Open/Commercial |
Transition to Jakarta EE:
Oracle has decided to no longer develop Java EE specifications after Java EE 8 in 2017. The Eclipse Foundation has taken over, developing Java EE technologies into an open source project under a new name: Jakarta EE. Since the takeover, several versions of Jakarta EE have been published (site: jakarta.ee).
4.3 Java in the Cloud
The traditional model:
Data Center
├── Machine dédiée #1 ─► Application Server (WebLogic, WildFly)
│ └── App Java EE (monolithique)
├── Machine dédiée #2 ─► Application Server
└── Machine dédiée #3 ─► Application Server (cluster)
Traditional Java EE applications are large and complex in their monolithic structure.
The Cloud-Native model:
Cloud-native development (elastic cloud computing: AWS, Azure, Google Cloud) is fundamentally different:
Cloud Élastique
├── Microservice A (Docker container) ─► Scale x3
├── Microservice B (Docker container) ─► Scale x1
├── Microservice C (Docker container) ─► Scale x5
└── API Gateway / Service Mesh
New cloud-native concerns:
- Security in elastically scaling environments.
- Services are dynamically discovered to communicate.
- Fast deployments with Docker containers.
- Monitoring and tracing distributed between services.
Microframeworks:
New approaches have emerged in the Java ecosystem for building cloud-native services. These frameworks are often called microframeworks (micro referring both to microservices and to the fact that these frameworks are much lighter than traditional Java EE application servers):
| Framework | Description |
|---|---|
| Spring Boot | The most popular Java microframework. Built on Spring Framework + selected libraries for cloud issues. Runs directly on Java SE, without an application server. |
| MicroProfile | Born from Java EE. Selects a subset of enterprise Java specifications relevant to cloud-native. Allows you to reuse Java EE knowledge. |
| Vert.x | Responsive, open source framework (Red Hat). |
| Quarkus | Framework optimized for the cloud, designed for containers, open source (Red Hat). |
Spring Boot is arguably the most popular Java microframework. A Spring Boot service is ultimately bundled into a single Java application that runs on Java SE — no more big Java EE application server involved.
4.4 Language features
Java is an object-oriented language. Concepts like classes, objects, and interfaces have been around since the early days of Java. But over the last decade or so, features inspired by functional programming have been added.
Lambdas (Java 8)
With a lambda expression, you can define a function without having to declare a surrounding class.
// Déclaration d'une lambda
Function<Integer, Integer> increment = value -> value + 1;
// La lambda n'est pas encore appelée ici.
// Elle peut être passée à d'autres méthodes ou composée avec d'autres fonctions.
Streams API (Java 8)
Instead of imperatively iterating over collections with for loops, the Streams API allows elements in collections to be processed in a more functional way.
Example using JShell (the Java interactive environment, introduced in Java 9):
jshell> var result = List.of(1, 2, 3, 4)
...> .stream()
...> .filter(n -> n > 2) // Garde seulement 3 et 4
...> .map(increment) // Applique la lambda increment (3→4, 4→5)
...> .toList();
result ==> [4, 5]
Steps:
List.of(1, 2, 3, 4)— creates a list of 4 integers..stream()— transforms the collection into a stream, unlocking the Streams API..filter(n -> n > 2)— only keeps elements > 2 (3 and 4)..map(increment)— transforms each remaining element with theincrementfunction (3→4, 4→5)..toList()— materializes the stream into a new collection.
Together, lambdas and streams have profoundly changed the way people write Java code since Java 8.
Type Inference with var (Java 10)
Java is a statically typed language — all types must be known and checked at compile time.
// Avant Java 10 : déclarations de types explicites (redondance)
String message = "Hello";
List<String> names = new ArrayList<>();
Map<String, Integer> scores = new HashMap<>();
// Java 10+ : inférence de type avec var
var message = "Hello"; // String inféré
var names = new ArrayList<String>(); // ArrayList<String> inféré
var scores = new HashMap<String, Integer>(); // HashMap<String, Integer> inféré
With var, the Java compiler infers the actual type of the variable based on the type of the expression on the right. Each variable always has a type, but we let the compiler infer it for us. In many cases, var makes the code both shorter and clearer.
Records (Java 16)
Records are a special type of class to use when all you care about is the data the class should contain. They provide an efficient way to model immutable data classes.
Before records — lots of boilerplate code:
// Classe Product "classique" avec tous ses boilerplates nécessaires
public final class Product {
private final String name;
private final String vendor;
private final double price;
private final boolean inStock;
public Product(String name, String vendor, double price, boolean inStock) {
this.name = name;
this.vendor = vendor;
this.price = price;
this.inStock = inStock;
}
public String name() { return name; }
public String vendor() { return vendor; }
public double price() { return price; }
public boolean inStock(){ return inStock; }
// + equals(), hashCode(), toString() à implémenter...
}
With records (Java 16+):
// Une seule ligne remplace tout le code ci-dessus !
record Product(String name, String vendor, double price, boolean inStock) {}
// Utilisation dans JShell :
jshell> var p = new Product("Widget", "ACME", 9.99, true);
p ==> Product[name=Widget, vendor=ACME, price=9.99, inStock=true]
By default, record formatting displays all fields with their initialized values clearly.
Records provide Java developers with a much more concise way to model core objects in Java applications.
Other notable features
Although not detailed in this course, Java has continued to evolve including:
- Sealed classes (Java 17): Classes and interfaces that limit which other classes or interfaces can extend or implement them.
- Pattern matching for
instanceof(Java 16). - Switch expressions (Java 14).
4.5 Summary of Module 4
- Desktop Java: Swing (standard, cross-platform) and JavaFX/OpenJFX (modern, animations, CSS) for desktop applications.
- Enterprise Java: Java EE (now Jakarta EE) — its original approach based on monolithic application servers is still used, but the industry has changed dramatically with the rise of the cloud.
- Cloud-Native: development of microservices for the cloud, mainly via microframeworks like Spring Boot, which offer a much lighter development model.
- Language features: lambdas and streams (functional programming),
var(type inference), records (concise data classes) — Java maintains its focus on improving developer productivity while remaining true to the platform’s philosophy.
5. The expanded Java ecosystem
The real power of the Java platform is not Java itself, but all the tools, libraries and frameworks that surround it.
5.1 Spring Framework
Spring Framework has single-handedly changed the way enterprise Java development is done.
History:
- Around 2002, Spring Framework emerged as an alternative to traditional Java EE development.
- At the time, the Java EE programming model was considered cumbersome, and Java EE itself a slow-moving technology due to its extensive specification process.
- Spring arrived as an alternative, supposedly lighter approach.
- Since then, Spring has gained popularity and is widely used.
Dependency Injection (DI) — the core of Spring:
Spring is basically a dependency injection container. DI is a form of inversion of control (IoC), also called the Hollywood principle: “Don’t call us, we’ll call you”.
Problem without DI:
// Sans injection de dépendances :
public class LoginService {
// LoginService instancie directement ses dépendances
private UserService userService = new UserService(); // Couplage fort
private AccountService accountService = new AccountService(); // Difficile à tester
public boolean login(String username, String password) {
// utilise userService et accountService directement
}
}
Disadvantages:
- Strong coupling: LoginService depends on the exact implementation classes.
- Difficult to test: no way to replace dependencies with mocks.
Solution with Spring DI:
// Avec Spring et injection de dépendances :
@Service
public class LoginService {
private final UserService userService;
private final AccountService accountService;
// Spring injecte les dépendances via le constructeur
public LoginService(UserService userService, AccountService accountService) {
this.userService = userService;
this.accountService = accountService;
}
public boolean login(String username, String password) {
// utilise les interfaces injectées
}
}
- Spring takes responsibility for instantiating all classes and providing dependencies to
LoginService. - This decouples
LoginServicefrom other classes, especially if you introduce Java interfaces between the concrete implementations and their usage. - When testing, you can provide alternative implementations or mocks of dependencies.
Spring configuration:
Instructions can be provided:
- Via annotations on your code (
@Service,@Component,@Autowired, etc.). - Via explicit configuration code (
@Configuration,@Bean). - Via external XML configuration (possible but not recommended these days).
Spring Framework has evolved to become much more than a DI container:
| Spring component | Description |
|---|---|
| Spring Core | Basic DI/IoC Container |
| Spring Web MVC | Classic MVC web framework |
| Spring WebFlux | Responsive and Modern Alternative to Spring MVC |
| Spring Boot | Microframework for cloud-native microservices |
| Spring Data | Data access abstraction (JPA, MongoDB, Redis, etc.) |
| Spring Security | Applicator security (authentication, authorization) |
| Spring Cloud | Tools for distributed microservices architectures |
| Spring Batch | Framework for batch processing |
Spring Framework delivered on its promise and surpassed Java Enterprise in popularity. Chances are you’ll encounter Spring while working with Java.
Minimum Spring Boot example:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello from Spring Boot!";
}
}
5.2 Other Popular Java Libraries
The Java ecosystem offers libraries for virtually any task. Here is an overview of the most important ones:
Tests — JUnit
JUnit is the most popular library for testing code in isolation (unit tests). Simply annotate a method with @Test.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
void testAddition() {
Calculator calc = new Calculator();
int result = calc.add(1, 2);
assertEquals(3, result);
}
}
JUnit supports running all tests and producing reports, whether in an IDE or in an automatic build script. Unit testing is adopted by virtually every Java development team.
Tests — Mockito
Mockito allows real dependencies of a class to be substituted with mocks (fake controlled objects) during tests.
// Si LoginService dépend de UserService, Mockito permet de "mocker" UserService
UserService mockUserService = Mockito.mock(UserService.class);
Mockito.when(mockUserService.findUser("john")).thenReturn(new User("john"));
LoginService loginService = new LoginService(mockUserService, ...);
// Tester loginService sans vraie base de données
The developer has full control over all interactions between the code under test and its dependencies.
Persistence — Hibernate
Hibernate is a widely used library for data persistence. It allows you to map Java objects to tables in a relational database.
- ORM (Object-Relational Mapping): Hibernate translates between the Java object world and the SQL relational world.
- Built on the Java Database Connectivity API (JDBC), part of Java SE, but provides a higher level abstraction.
- Save and retrieve Java objects from a relational database with minimal boilerplate.
- Implements the JPA (Java Persistence API) specification, now part of Jakarta EE.
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
private double price;
// Hibernate génère les requêtes SQL nécessaires
}
JSON — Jackson / Gson / JSON-B
When developing microservices, it is inevitable to handle JSON serialization and deserialization.
| Library | Description |
|---|---|
| Jackson | Common choice, high performance |
| Gson | Developed and open source by Google |
| JSON-B | JSON Binding, part of Jakarta EE |
// Exemple avec Jackson
ObjectMapper mapper = new ObjectMapper();
// Sérialisation : Java → JSON
Product product = new Product("Widget", 9.99);
String json = mapper.writeValueAsString(product);
// {"name":"Widget","price":9.99}
// Désérialisation : JSON → Java
Product p = mapper.readValue(json, Product.class);
Apache Commons Libraries
Apache is an open source foundation that hosts many Java libraries. Commons libraries help with everyday coding tasks:
Commons CLI— create Java applications from the command line.Commons IO— various input/output tasks.Commons CSV— work with CSV files.- And many more at
commons.apache.org.
Apache also offers a widely used HTTP client, although starting with Java 11, Java now includes a modern HTTP client API, reducing the need for external libraries for HTTP calls.
Logging — SLF4J, Log4j, Logback
Every serious Java application needs logging for monitoring and troubleshooting in production.
| Library | Role |
|---|---|
| Log4j | Popular logging library (notorious for security incident in 2022) |
| Logback | Alternative to Log4j, very efficient |
| Commons Logging | Apache Logging Library |
| SLF4J | Simple Logging Facade for Java — unified facade for easy implementation switching |
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
public void processOrder(Order order) {
logger.info("Processing order: {}", order.getId());
// ...
}
}
SLF4J allows coding against a unified API and swapping logging implementations underneath if desired. This is similar to the notion of the Jakarta EE specification with various implementations.
Other notable libraries
| Library | Usage |
|---|---|
| Project Reactor / Akka | Responsive Programming |
| Netty | High performance network applications |
| Caffeine | Memory efficient cache strategy |
| Hazelcast / Ehcache | Distributed cache |
| Apache Kafka | Distributed messaging, event streaming |
When you use Java, there is always a library that has you covered for what you need to accomplish.
5.3 Development tools
IDEs (Integrated Development Environments)
Although Java source code is plain text, writing Java code in an IDE rather than a text editor is much more productive:
- Syntactic highlighting — clearly distinguishes the different elements of the source code.
- Code navigation — quickly navigate between related parts of your code, including relevant documentation.
- Real-time error detection — reports compilation errors and type errors without having to invoke the compiler manually.
- Intelligent auto-completion — provides context-based completions.
- Automatic refactoring — rewrites code structurally differently while preserving semantics.
- Integrated compilation and execution — compiles and launches your application directly from the IDE.
- Debugging — debug the code during execution, set breakpoints and inspect the current state of variables.
The two most used Java IDEs:
| IDE | Editor | License | Note |
|---|---|---|---|
| IntelliJ IDEA | JetBrains | Community Edition (free/open source) + Ultimate (commercial) | Recommended |
| Eclipse | Eclipse Foundation | Fully open source | Very widespread |
The most important thing is to master the features of your IDE, preferably with keyboard shortcuts, because it’s a huge productivity boost.
Build Tools
Compiling with javac and running with java works for simple examples, but to develop a real Java application, build tools are essential.
Objectives of the build tools:
- Describe the steps necessary to build and package a complete application.
- Allow reproducible builds (same result on different machines).
- Compile Java code, run tests, package for deployment.
- Manage the modularity of the application (submodules, dependencies between modules).
- Download external dependencies (third party libraries).
Maven:
Maven is the most used build tool in Java. Builds are described in an XML file called pom.xml (Project Object Model) — declarative in nature.
<!-- pom.xml — exemple de dépendance Spring Boot -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Maven standard directory structure:
my-app/
├── pom.xml
└── src/
├── main/
│ └── java/
│ └── com/example/
│ └── MyApp.java
└── test/
└── java/
└── com/example/
└── MyAppTest.java
Maven makes many assumptions about defaults in the codebase, which makes most Maven builds look the same and easy to get started with.
Gradle:
Gradle is the second most popular Java build tool. The biggest difference with Maven is that builds are not specified in declarative XML, but using the Groovy scripting language (or Kotlin DSL).
// build.gradle — exemple avec Groovy DSL
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.0'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.3'
}
repositories {
mavenCentral()
}
| Criterion | Maven | Gradle |
|---|---|---|
| Format | Declarative XML | Groovy/Kotlin DSL (scriptable) |
| Flexibility | More rigid, uniform | More flexible, but potentially more complex |
| Incremental builds | No (standard) | Yes (only modified code triggers steps) |
| Learning curve | Softer | More pronounced |
| Uniformity | Very uniform between projects | Varies depending on the projects |
Maven Central:
There is a huge public Java library repository called Maven Central, used by Maven and Gradle to download any library needed to build an application.
- You only need to specify the coordinates (groupId, artifactId, version).
- Maven or Gradle downloads the correct artifacts.
- The transitive dependencies of each specified dependency are also retrieved automatically.
With a Java and Maven Central build tool, you never have to manually locate and download external dependencies again.
5.4 Alternative languages on the JVM
The Java ecosystem includes many languages other than Java. For what ?
Code Source Kotlin/Scala/Groovy
↓ [Compilateur du langage]
Bytecode Java (.class)
↓ [JVM]
Exécution native
Any language can compile to Java bytecode and run on the JVM. These languages can thus:
- Focus on designing a different language.
- Leverage the JVM’s existing managed execution environment.
- Be interoperable with existing Java libraries (also distributed in bytecode).
- Access the entire huge ecosystem of Java libraries.
Reasons to choose alternative JVM languages:
- Developer productivity: a new language is not weighed down by the compromises of backward compatibility.
- Conciseness: some prefer more concise language.
- Different paradigms: functional programming, dynamic typing, etc.
Scala
Scala merges object-oriented programming with functional programming.
// Scala — exemple de fonctionnel
val numbers = List(1, 2, 3, 4)
val result = numbers
.filter(_ > 2)
.map(_ + 1)
// result: List[Int] = List(4, 5)
// Case class (équivalent des records Java)
case class Product(name: String, vendor: String, price: Double)
val p = Product("Widget", "ACME", 9.99)
| Characteristic | Scala |
|---|---|
| Paradigm | Multi-paradigm (OO + functional) |
| Typing | Static, very advanced type system |
| Java Interoperability | Good |
| Learning curve | Very high |
| Performance | Slow compiler on large codebases |
Experienced Scala developers can be very productive, but the learning curve is often perceived as very steep.
Kotlin
Kotlin is a more recent addition to the JVM language collection. Its objective: to rethink the Java language without backward compatibility constraints.
// Kotlin — exemple
val numbers = listOf(1, 2, 3, 4)
val result = numbers.filter { it > 2 }.map { it + 1 }
// result: [4, 5]
// Data class (inspire les records Java)
data class Product(val name: String, val vendor: String, val price: Double)
val p = Product("Widget", "ACME", 9.99)
// Null safety
var name: String? = null // Nullable
var nonNullName: String = "Hello" // Non-nullable, erreur à la compilation si null
| Characteristic | Kotlin |
|---|---|
| Creator | JetBrains (creators of IntelliJ) |
| Paradigm | Pragmatic, multi-paradigm |
| Typing | Static with null safety |
| Java Interoperability | Complete (more than Scala) |
| Targets | JVM, Android, iOS, Web (Kotlin Multiplatform) |
| Learning curve | Moderate |
Highlights:
- 2017: Google officially endorses Kotlin as the language for Android development.
- Kotlin Multiplatform: compiles to platforms other than the JVM (iOS, Android, web), allowing code to be shared between the browser and the back-end, or between the mobile app and the back-end.
- Kotlin’s data classes inspired the addition of records in Java.
Groovy
Groovy is a dynamic scripting language for the JVM, belonging to Apache.
// Groovy — exemple
def numbers = [1, 2, 3, 4]
def result = numbers.findAll { it > 2 }.collect { it + 1 }
// result: [4, 5]
// Syntaxe proche de Java, mais plus concise
class Product {
String name
double price
}
def p = new Product(name: "Widget", price: 9.99)
println p.name // Widget
| Characteristic | Groovy |
|---|---|
| Paradigm | Dynamic Scripting, OO |
| Typing | Dynamic (with optional typing via annotation) |
| Java Interoperability | Excellent (syntax very close to Java) |
| Compilation | Interpreted at runtime OR compiled to bytecode |
| Common use | Build scripts (Gradle), tests |
Typical use cases:
- Gradle uses Groovy as the language to configure builds.
- Introduced into existing codebases on top of Java code to write tests flexibly.
Comparison of alternative JVM languages:
| Criterion | Scala | Kotlin | Groovy |
|---|---|---|---|
| Paradigm | Multi (OO + FP) | Multi (pragmatic) | Dynamic Scripting |
| Typing | Static (advanced) | Static (null safe) | Dynamic (static opt-in) |
| Popularity | High but niche | Very high (Android) | Moderate |
| Learning curve | Very high | Moderate | Low |
| Java Interoperability | Good | Excellent | Excellent |
| Targets | JVM | JVM + multiplatform | JVM |
Many of these alternative JVM languages have inspired changes in the Java language itself. It’s actually a good thing for the Java platform as a whole to have these alternatives to keep moving forward.
5.5 Conclusion and next steps
Summary of the 4 modules:
- Introducing the Java Platform: Java is alive and cross-platform. It compiles into bytecode which runs on the JVM. The platform = language + standard library + JVM.
- Adopting Java: Philosophy of readability and stability. Portable and efficient managed runtime. Comparisons with C#, C/C++, JavaScript.
- Working with Java: Desktop (Swing, JavaFX), enterprise (Jakarta EE), cloud-native (Spring Boot, microframeworks). Lambdas, streams,
var, records. - The Wider Java Ecosystem: Spring Framework, JUnit/Mockito, Hibernate, logging, tools (IntelliJ, Eclipse, Maven, Gradle, Maven Central), JVM languages (Scala, Kotlin, Groovy).
Resources to go further:
| Resource | Description |
|---|---|
| Java SE Path (Pluralsight) | Complete course to learn Java |
| Java SE 17 Fundamentals (Pluralsight) | Learn basic Java programming skills |
| Spring Big Picture (Pluralsight) | Spring Framework Overview |
| Jakarta EE Big Picture (Pluralsight) | Jakarta EE Overview |
| What’s New in Java (Pluralsight) | Course by Java version (since Java 9) |
| Working with the Java Module System (Pluralsight) | Java 9+ system module by Sander Mak |
| Java 9 Modularity (O’Reilly) | Book by Sander Mak |
openjdk.net | OpenJDK project, JEPs, subprojects |
jakarta.ee | Jakarta EE Specifications |
commons.apache.org | Apache Commons Libraries |
Follow author:
- Twitter:
@Sander_Mak - LinkedIn: Sander Mak
- Pluralsight author page: follow for new training
6. Appendix: Java Platform Diagram
┌─────────────────────────────────────────────────────────────────┐
│ APPLICATION │
│ (Code source Java — .java files) │
└─────────────────────────────┬───────────────────────────────────┘
│ javac (Java Compiler)
▼
┌─────────────────────────────────────────────────────────────────┐
│ APPLICATION BYTECODE │
│ (.class files) │
│ ┌──────────────────────────────────────────┐ │
│ │ Bibliothèques tierces (bytecode) │ │
│ │ (Hibernate, Spring, JUnit, etc.) │ │
│ └──────────────────────────────────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────────▼───────────────────────────────────┐
│ JAVA STANDARD EDITION APIs │
│ (Collections, I/O, HTTP Client, Date/Time, etc.) │
└─────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────────▼───────────────────────────────────┐
│ JAVA VIRTUAL MACHINE (JVM) │
│ ┌─────────────────────┐ ┌─────────────────────────────────┐ │
│ │ Garbage Collector │ │ JIT Compiler │ │
│ │ (Memory Mgmt) │ │ (Bytecode → Machine Code) │ │
│ └─────────────────────┘ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Thread Scheduler (Multithreading Management) │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────────▼───────────────────────────────────┐
│ OS + HARDWARE (natif) │
│ Linux / Windows / macOS / ARM / x86 / ... │
└─────────────────────────────────────────────────────────────────┘
Search Terms
java · se · backend · architecture · full-stack · web · libraries · philosophy · platform · tools · development · features · language · languages · notable · runtime · tests