Java for beginners
406 subscribers
49 photos
29 links
Learn Java daily with bite-sized lessons, practical code examples, output previews, and fun quizzes.
Download Telegram
๐Ÿš€ Day 43: Dive Deep into Advanced Java! Today, we're exploring the future of concurrent programming with Project Loom! ๐Ÿงต Forget the complexities of traditional thread pools and embrace the power of Virtual Threads and `StructuredTaskScope`. Learn how to build highly scalable, robust, and readable concurrent applications with elegant error handling. Get ready to master the next generation of JVM concurrency! โœจ

```java
import java.time.Duration;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;

public class Day43Loom {

public static void main(String[] args) throws Exception {
System.out.println("Starting concurrent data fetch with StructuredTaskScope...");

// Use ShutdownOnFailure to ensure that if any subtask fails,
// all other running subtasks are cancelled.
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> userData = scope.fork(() -> {
System.out.println(" [Task 1] Fetching user data (on a Virtual Thread)...");
Thread.sleep(Duration.ofSeconds(2)); // Simulate network call
System.out.println(" [Task 1] User data fetched.");
return "User: Alice (ID: 101)";
});

Future<String> orderData = scope.fork(() -> {
System.out.println(" [Task 2] Fetching order data (on a Virtual Thread)...");
Thread.sleep(Duration.ofSeconds(3)); // Simulate another network call
System.out.println(" [Task 2] Order data fetched.");
return "Order: #XYZ789 (Items: 3)";
});

// Blocks until all forked tasks complete, or the scope is shut down due to a failure.
scope.join();
// Propagates any exception thrown by a subtask.
scope.throwIfFailed();

String user = userData.resultNow(); // Retrieves result without blocking further
String order = orderData.resultNow();

System.out.println("n--- Combined Results ---");
System.out.println(user);
System.out.println(order);

} catch (Exception e) {
System.err.println("nError during concurrent operation: " + e.getMessage());
// In a real application, you might log the error or return a default value.
}

System.out.println("nMain thread finished.");
}
}
```

Output example:
```
Starting concurrent data fetch with StructuredTaskScope...
[Task 1] Fetching user data (on a Virtual Thread)...
[Task 2] Fetching order data (on a Virtual Thread)...
[Task 1] User data fetched.
[Task 2] Order data fetched.

--- Combined Results ---
User: Alice (ID: 101)
Order: #XYZ789 (Items: 3)

Main thread finished.
```

๐Ÿ’ก **Challenge:** Modify the `orderData` task to throw a `RuntimeException` (e.g., `new RuntimeException("Database connection timed out!")`) after 1 second. Observe how `StructuredTaskScope.ShutdownOnFailure` handles the failure and what the main thread prints. How would you use `StructuredTaskScope.ShutdownOnSuccess` differently if you only cared about the first successful result?

---
Welcome to Day 47 of our Java learning journey! ๐Ÿš€ Today, we're diving into one of the most exciting advancements in modern Java: **Structured Concurrency and Virtual Threads (Project Loom)**. ๐Ÿ’ก Tired of callback hell, complex error handling in async code, and debugging messy thread pools? Virtual Threads offer a lightweight, high-throughput solution, while Structured Concurrency brings clarity and robustness to concurrent task management. โœจ Let's reimagine how we write asynchronous Java!

Virtual Threads provide "threads for free," allowing you to write blocking code as if it were non-blocking. Structured Concurrency, especially with `StructuredTaskScope`, ensures that the lifecycle of child tasks is tied to their parent, drastically simplifying error propagation and cancellation.

Here's an example using `StructuredTaskScope.ShutdownOnFailure` to manage two concurrent virtual tasks:

```java
import java.time.Duration;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeoutException;

// Requires --enable-preview --source 21 (or higher) to run
public class Day47StructuredConcurrency {

public static void main(String[] args) throws InterruptedException {
System.out.println("Starting main task at " + System.currentTimeMillis());

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
StructuredTaskScope.Subtask<String> task1 = scope.fork(() -> {
System.out.println("Task 1 (Virtual Thread) starting...");
Thread.sleep(Duration.ofSeconds(3)); // Simulate work
System.out.println("Task 1 completed.");
return "Data from Service A";
});

StructuredTaskScope.Subtask<String> task2 = scope.fork(() -> {
System.out.println("Task 2 (Virtual Thread) starting...");
Thread.sleep(Duration.ofSeconds(1)); // Simulate work
if (System.currentTimeMillis() % 2 != 0) { // Simulate occasional failure
throw new RuntimeException("Task 2 failed unexpectedly!");
}
System.out.println("Task 2 completed.");
return "Data from Service B";
});

scope.join(); // Wait for all subtasks to complete or fail
scope.throwIfFailed(); // Propagate any exception from subtasks

String result1 = task1.get();
String result2 = task2.get();

System.out.println("Main task completed successfully!");
System.out.println("Result 1: " + result1);
System.out.println("Result 2: " + result2);

} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Main task interrupted: " + e.getMessage());
} catch (ExecutionException e) {
System.err.println("One or more subtasks failed: " + e.getCause().getMessage());
} finally {
System.out.println("Ending main task at " + System.currentTimeMillis());
}
}
}
```

Output example (Failure scenario, if `System.currentTimeMillis() % 2 != 0` evaluates to true):
```
Starting main task at 1701234567890
Task 1 (Virtual Thread) starting...
Task 2 (Virtual Thread) starting...
One or more subtasks failed: Task 2 failed unexpectedly!
Ending main task at 1701234569123
```
*(Note: In this `ShutdownOnFailure` scenario, as soon as `task2` fails, the scope initiates shutdown, potentially interrupting `task1` before it can complete its `Thread.sleep`.)*

**Small Challenge:**
Modify the code to use `StructuredTaskScope.ShutdownOnSuccess`. How does the output change if one task completes successfully while another is still running or fails later? What are the implications for your application logic?

---
Here's your Day 48 Java learning series post!

Hey Java Devs! ๐Ÿ‘‹ Day 48 is here, and we're diving deep into crafting truly resilient and expressive domain models! Forget simple POJOs; today we explore how modern Java features like `record` types, `sealed classes`/`interfaces`, and `pattern matching` elevate immutability. Learn to build domain objects that are not only safe and predictable but also incredibly clear and compiler-verified. Let's make our domain models rock-solid! ๐Ÿงฑโœจ

Let's model different payment methods using these powerful features, ensuring our code is both expressive and robust:

```java
// Define a sealed interface for payment methods
// This restricts which classes can implement it, providing compile-time control.
sealed interface PaymentMethod permits CreditCardPayment, PayPalPayment, BankTransferPayment {
String description(); // Common behavior for all payment methods
}

// Record for Credit Card Payment - concise, immutable data carrier
record CreditCardPayment(String cardNumber, String expiryDate, String cvv) implements PaymentMethod {
@Override
public String description() {
return "Credit Card (ends in " + cardNumber.substring(cardNumber.length() - 4) + ")";
}
}

// Record for PayPal Payment
record PayPalPayment(String email) implements PaymentMethod {
@Override
public String description() {
return "PayPal (" + email + ")";
}
}

// Record for Bank Transfer Payment
record BankTransferPayment(String bankName, String accountNumber) implements PaymentMethod {
@Override
public String description() {
return "Bank Transfer (" + bankName + ")";
}
}

public class DomainModelDemo {

// Using pattern matching with switch expressions for exhaustive handling
public static String processPayment(PaymentMethod method) {
return switch (method) {
case CreditCardPayment cc -> "Processing credit card payment: " + cc.description();
case PayPalPayment pp -> "Processing PayPal payment: " + pp.description();
case BankTransferPayment bt -> "Processing bank transfer payment: " + bt.description();
// The compiler will warn you if you don't handle all permitted types!
};
}

public static void main(String[] args) {
PaymentMethod card = new CreditCardPayment("1234-5678-9012-3456", "12/25", "123");
PaymentMethod paypal = new PayPalPayment("user@example.com");
PaymentMethod bank = new BankTransferPayment("AwesomeBank", "987654321");

System.out.println(processPayment(card));
System.out.println(processPayment(paypal));
System.out.println(processPayment(bank));
}
}
```

Output example:
```
Processing credit card payment: Credit Card (ends in 3456)
Processing PayPal payment: PayPal (user@example.com)
Processing bank transfer payment: Bank Transfer (AwesomeBank)
```

๐Ÿ’ก **Your Turn!** Try adding a new payment method, say `CryptocurrencyPayment`, to our `sealed interface`. What happens if you forget to add a `case` for it in the `processPayment` switch expression? See how the compiler guides you towards exhaustive handling! ๐Ÿค“ This is the power of resilient domain modeling!

---
๐Ÿš€ **Day 49: Diving Deep into Modern Java Concurrency!** ๐Ÿš€

Hello Java Enthusiasts! Today, we're unlocking the full potential of **Project Loom** in Java. Forget the limitations of traditional threads โ€“ we're talking about architecting systems capable of handling *millions* of concurrent virtual threads with ease! ๐Ÿคฏ Get ready to explore how Virtual Threads redefine scalability and simplify highly concurrent programming. No more worrying about thread pool sizes or context switching overhead as much. Let's see them in action! ๐Ÿ‘‡

```java
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class VirtualThreadMillions {

private static final int NUMBER_OF_TASKS = 500_000; // Let's aim high but not *too* high for quick demo

public static void main(String[] args) throws InterruptedException {
System.out.println("๐Ÿ”ฅ Starting " + NUMBER_OF_TASKS + " virtual threads...");
Instant start = Instant.now();

try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, NUMBER_OF_TASKS).forEach(i -> {
executor.submit(() -> {
try {
// Simulate some non-CPU bound work, like an I/O operation
Thread.sleep(Duration.ofMillis(1));
// In a real app, this would be an actual blocking call
// System.out.println("Task " + i + " finished by " + Thread.currentThread()); // Too much output
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
});
} // ExecutorService is automatically shut down here

Instant end = Instant.now();
System.out.println("โœ… All " + NUMBER_OF_TASKS + " virtual threads completed in " + Duration.between(start, end).toMillis() + " ms.");
System.out.println("๐ŸŽ‰ Scalability just got a massive boost!");
}
}
```

**Output example:**
```
๐Ÿ”ฅ Starting 500000 virtual threads...
โœ… All 500000 virtual threads completed in 568 ms.
๐ŸŽ‰ Scalability just got a massive boost!
```

**Small challenge:**
Modify the `NUMBER_OF_TASKS` to 1 million or even 5 million. How does the execution time change? Now, try to achieve the same with `Executors.newFixedThreadPool(200)` (using platform threads) for simulating I/O-bound tasks. What differences do you observe in performance and resource usage (e.g., memory)? Share your findings! ๐Ÿ“ˆ

---
๐ŸŽ‰ **Day 50: Beyond the JIT - Deep Dive into GraalVM Native Images!** ๐ŸŽ‰

Congratulations on reaching Day 50! Today, we're not just running Java; we're launching it into a new era! ๐Ÿš€ Forget traditional JVM startup times. We're exploring GraalVM Native Images, a revolutionary way to compile Java applications into standalone executables. This means incredibly fast startup, tiny memory footprints, and deployable binaries without needing a JVM installed. It's Java, but faster and lighter than ever! Get ready to see how Ahead-of-Time (AOT) compilation transforms your applications.

```java
// Day50NativeApp.java
public class Day50NativeApp {
public static void main(String[] args) {
System.out.println("๐Ÿš€ Hello from Day 50's Native Image!");
System.out.printf("Arguments received: %d%n", args.length);
if (args.length > 0) {
System.out.println("First argument: " + args[0]);
}
}
}
```

To compile and run this as a native image (assuming GraalVM `native-image` tool is installed):
1. `javac Day50NativeApp.java`
2. `native-image --no-fallback -jar Day50NativeApp.jar Day50NativeApp` (This command assumes you've packaged it or use the class name directly)
* *Simpler for this example:* `native-image -cp . Day50NativeApp`
3. `./day50nativeapp`

Output example:
```
๐Ÿš€ Hello from Day 50's Native Image!
Arguments received: 0
```

Small challenge:
Install GraalVM (if you haven't already!) and compile the `Day50NativeApp.java` file into a native executable. Compare its startup time (using `time ./day50nativeapp`) with running it traditionally via `java -cp . Day50NativeApp`. You'll be amazed by the difference! Share your results in the comments! โšก

---
๐Ÿš€ Day 52: The Loom Revolution! ๐Ÿงตโœจ

Get ready to transform your understanding of Java concurrency! Today, we're diving deep into **Project Loom** and its game-changing **Virtual Threads**. This isn't just an update; it's a fundamental shift, enabling you to architect applications for *millions* of concurrent users with unprecedented ease and efficiency. Say goodbye to the limitations of traditional threads and embrace a future of lightweight, high-throughput concurrency!

```java
import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.stream.IntStream;

public class VirtualThreadDemo {

public static void main(String[] args) throws InterruptedException {
System.out.println("๐Ÿš€ Day 52: Virtual Threads in Action!");
long startTime = System.currentTimeMillis();

// Create an ExecutorService that spawns a new virtual thread for each task
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 5000).forEach(i -> { // Let's launch 5000 virtual threads!
executor.submit(() -> {
try {
// Simulate a short, non-CPU-intensive blocking operation (e.g., I/O call)
Thread.sleep(Duration.ofMillis(5));
// Uncomment to see thread names, notice many 'VirtualThread'
// System.out.println("Task " + i + " completed on: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
});
} // The ExecutorService is automatically shut down here, waiting for all submitted tasks to complete.

long endTime = System.currentTimeMillis();
System.out.println(String.format("โœ… %d virtual threads completed their tasks in %d ms.", 5000, (endTime - startTime)));
System.out.println("Main thread continues. Current thread: " + Thread.currentThread().getName());
}
}
```

Output example:
```
๐Ÿš€ Day 52: Virtual Threads in Action!
โœ… 5000 virtual threads completed their tasks in 35 ms. (Actual time may vary based on system)
Main thread continues. Current thread: main
```

**Challenge:**
Modify the code to launch 100,000 virtual threads. Observe the execution time and compare it with trying to launch 100,000 traditional platform threads (be cautious when attempting with platform threads, as this might consume significant memory/OS resources on your system!). How does Project Loom enable such massive concurrency?

---
๐Ÿš€ Day 53: Level up your Java architecture! ๐Ÿฐ We're diving into **Type-Safe Kingdoms** with Java's powerful **Sealed Types** and **Exhaustive Pattern Matching**. These modern features let you sculpt incredibly robust APIs, ensuring compile-time guarantees for your domain models. No more runtime surprises! Learn to define a hierarchy of types where you know *exactly* what's possible, making your code safer, cleaner, and easier to refactor. Get ready to build more resilient systems! โœจ

```java
public class TypeSafeKingdoms {

// 1. Define a sealed interface: Shape can only be implemented by permitted types
sealed interface Shape permits Circle, Rectangle, Triangle {
double area(); // All shapes must implement an area calculation
}

// 2. Implementations as records: concise and immutable data carriers
record Circle(double radius) implements Shape {
@Override
public double area() {
return Math.PI * radius * radius;
}
}

record Rectangle(double width, double height) implements Shape {
@Override
public double area() {
return width * height;
}
}

record Triangle(double base, double height) implements Shape {
@Override
public double area() {
return 0.5 * base * height;
}
}

// 3. Method using Pattern Matching for switch expressions (Java 21+)
public static String describeShape(Shape shape) {
// This switch expression is exhaustive: the compiler ensures all
// permitted sub-types of Shape (Circle, Rectangle, Triangle) are handled.
// If you remove a 'case', the compiler will flag it as a non-exhaustive switch!
return switch (shape) {
case Circle c -> "A circle with radius " + c.radius() + ", area: " + c.area();
case Rectangle r -> "A rectangle with width " + r.width() + " and height " + r.height() + ", area: " + r.area();
case Triangle t -> "A triangle with base " + t.base() + " and height " + t.height() + ", area: " + t.area();
// No 'default' case is needed here because the switch is exhaustive
// due to the nature of sealed types and pattern matching.
};
}

public static void main(String[] args) {
Shape circle = new Circle(5.0);
Shape rectangle = new Rectangle(4.0, 6.0);
Shape triangle = new Triangle(3.0, 4.0);

System.out.println(describeShape(circle));
System.out.println(describeShape(rectangle));
System.out.println(describeShape(triangle));
}
}
```

Output example:
```
A circle with radius 5.0, area: 78.53981633974483
A rectangle with width 4.0 and height 6.0, area: 24.0
A triangle with base 3.0 and height 4.0, area: 6.0
```

**Small challenge:**
How would you extend the `Shape` hierarchy to include a `Square`? Would you make `Square` directly implement `Shape` or extend `Rectangle`? What are the implications for your `describeShape` method and the `permits` clause of `Shape`? Share your thoughts! ๐Ÿ‘‡

---
๐Ÿš€ Day 56: Dive into the future of high-performance Java! ๐Ÿ’ฅ Today we explore **Project Panama's Foreign Memory API (FFM API)**. Say goodbye to the complexities and dangers of JNI and `sun.misc.Unsafe`! The FFM API offers a *safe, efficient, and idiomatic* way to interact with off-heap memory and native code, unlocking incredible performance gains for data-intensive applications. Get ready to manage memory outside the JVM heap with native precision!

```java
import java.lang.foreign.*; // This API requires Java 19+ (incubating) or Java 22+ (stable)
// For older Java versions (19-21), you might need --enable-preview

public class PanamaMemoryDemo {
public static void main(String[] args) {
// Allocate a memory segment off-heap to hold 5 integers.
// Arena.ofConfined() creates a confined arena, which manages the lifecycle
// of allocated memory. It's automatically closed (and memory deallocated)
// at the end of the try-with-resources block.
try (Arena arena = Arena.ofConfined()) {
// Allocate space for 5 integers (each JAVA_INT is 4 bytes).
// The total size will be 5 * 4 = 20 bytes.
MemorySegment segment = arena.allocate(ValueLayout.JAVA_INT, 5);

System.out.println("Writing values to off-heap memory:");
// Write values to the off-heap segment
for (int i = 0; i < 5; i++) {
int valueToWrite = i * 100;
// setAtIndex writes an int at the given logical index (not byte offset)
segment.setAtIndex(ValueLayout.JAVA_INT, i, valueToWrite);
System.out.println("Wrote " + valueToWrite + " at index " + i);
}

System.out.println("nReading values from off-heap memory:");
// Read values back from the off-heap segment
for (int i = 0; i < 5; i++) {
// getAtIndex reads an int from the given logical index
int value = segment.getAtIndex(ValueLayout.JAVA_INT, i);
System.out.println("Index " + i + ": " + value);
}

} // Arena is closed here, automatically deallocating the 'segment' memory.
}
}
```

Output example:
```
Writing values to off-heap memory:
Wrote 0 at index 0
Wrote 100 at index 1
Wrote 200 at index 2
Wrote 300 at index 3
Wrote 400 at index 4

Reading values from off-heap memory:
Index 0: 0
Index 1: 100
Index 2: 200
Index 3: 300
Index 4: 400
```

Small Challenge:
Your Challenge: Modify the code to allocate a `MemorySegment` for 10 `long` values and populate it with their squares (0, 1, 4, 9, ...), then print them out! Remember to use `ValueLayout.JAVA_LONG`. ๐Ÿ’ก