Java 18: Simple Web Server and UTF-8 by Default

Java 18: Simplifying Web Development
Java 18 (March 2022) introduced a simple, lightweight HTTP server API in the standard library. While short-lived (non-LTS), it demonstrates Java's commitment to simplifying common development tasks.
1. Simple Web Server API
Create HTTP servers without external frameworks.
Before Java 18:
// Using external library (Spring Boot, etc.)
// pom.xml dependency required
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}After Java 18:
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import java.io.IOException;
import java.net.InetSocketAddress;
public class SimpleWebServer {
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
// Simple GET endpoint
server.createContext("/", exchange -> {
String response = "Hello from Java 18!";
exchange.getResponseHeaders().set("Content-Type", "text/plain");
exchange.sendResponseHeaders(200, response.getBytes().length);
exchange.getResponseBody().write(response.getBytes());
exchange.close();
});
server.start();
System.out.println("Server running on http://localhost:8080");
}
}Advanced HTTP Handler:
// Routing requests
public class APIServer {
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
// GET /users
server.createContext("/users", exchange -> {
if ("GET".equals(exchange.getRequestMethod())) {
String response = "[{\"id\":1,\"name\":\"John\"}]";
sendJsonResponse(exchange, response, 200);
} else {
sendJsonResponse(exchange, "{\"error\":\"Method not allowed\"}", 405);
}
});
// POST /users
server.createContext("/api/users", exchange -> {
if ("POST".equals(exchange.getRequestMethod())) {
String body = new String(exchange.getRequestBody().readAllBytes());
// Process body
String response = "{\"id\":2,\"status\":\"created\"}";
sendJsonResponse(exchange, response, 201);
}
});
server.setExecutor(Executors.newFixedThreadPool(10));
server.start();
}
private static void sendJsonResponse(HttpExchange exchange,
String response, int code) throws IOException {
exchange.getResponseHeaders().set("Content-Type", "application/json");
exchange.sendResponseHeaders(code, response.getBytes().length);
exchange.getResponseBody().write(response.getBytes());
exchange.close();
}
}2. UTF-8 by Default
UTF-8 is now the default charset everywhere in the platform.
// Before Java 18: Charset varied by platform
// Windows: Windows-1252 (Western European)
// Unix/Linux: UTF-8
// Result: Cross-platform issues
// Files.readString() - platform-dependent
String content = Files.readString(Path.of("file.txt"));
// Charset was: US-ASCII on old systems
// After Java 18: Explicit UTF-8
String content = Files.readString(Path.of("file.txt")); // Now UTF-8!
// String encoding/decoding - now consistent
String encoded = "Hello 世界 🌍".getBytes().toString(); // Consistent UTF-8
// File I/O with emoji and international characters
String multilingual = "English, 中文, العربية, 日本語, 한글";
Files.writeString(Path.of("multilingual.txt"), multilingual);
// All text is now properly UTF-8 encoded
// No more platform-specific charset issues!Benefits:
// Before: Had to specify encoding
ObjectInputStream input = new ObjectInputStream(
Files.newInputStream(Path.of("file.dat"))
);
// Now: UTF-8 by default for all text operations
PrintWriter writer = new PrintWriter(
Files.newBufferedWriter(Path.of("output.txt"))
);
writer.println("Internationalization: " + "مرحبا, こんにちは, 你好");3. Finalized Features
Several preview features became standard.
Pattern Matching for switch (Preview 4):
// Still evolving, but approaching finalization
public String describePerson(Object person) {
return switch (person) {
case String s -> "Name: " + s;
case Integer age when age < 13 -> "Child";
case Integer age when age < 20 -> "Teen";
case Integer age -> "Adult";
default -> "Unknown";
};
}4. Stream API Enhancements
New stream operations for processing.
// Stream.toList() - shorthand for collect(Collectors.toList())
List<Integer> numbers = Stream.of(1, 2, 3, 4, 5)
.filter(n -> n > 2)
.toList(); // Immutable list
// Simpler than: .collect(Collectors.toList());
// Practical example
List<User> activeUsers = userStream
.filter(User::isActive)
.toList(); // Much cleaner!5. Foreign Function & Memory API (Preview 2)
Enhanced APIs for calling native code safely.
// Calling native strlen() function
import java.lang.foreign.*;
public class NativeInterop {
public static void main(String[] args) throws Throwable {
SymbolLookup stdlib = Linker.nativeLinker().defaultLookup();
Linker linker = Linker.nativeLinker();
// Find strlen function
MemorySegment strlenAddr = stdlib.find("strlen").get();
// Create function descriptor
FunctionDescriptor strlen = FunctionDescriptor.of(
ValueLayout.JAVA_LONG,
ValueLayout.ADDRESS
);
// Get downcall handle
MethodHandle strlenHandle = linker.downcallHandle(
strlenAddr,
strlen
);
// Call native function
Arena arena = Arena.ofAuto();
MemorySegment cString = arena.allocateUtf8String("Hello");
long length = (long) strlenHandle.invoke(cString);
System.out.println("Length: " + length); // 5
}
}6. Deprecation Warnings
More APIs marked for future removal.
// Enhanced deprecation warnings
import java.lang.Deprecated;
@Deprecated(since = "17",
forRemoval = true,
message = "Use newAPI() instead")
public void oldAPI() { }
// Compiler warnings:
// warning: [removal] oldAPI() in OldClass is deprecated
// and marked for removalDeveloper Impact
Positive:
- Simplified HTTP: No framework needed for simple APIs
- Internationalization: UTF-8 consistent across platforms
- Cleaner Code: Stream.toList() shorthand
- Cross-platform: No charset issues
Challenges:
- Limited HTTP API: Not suitable for complex applications
- Short Support: Non-LTS with 6-month lifecycle
- Learning Curve: Foreign Function API still incubating
Pros and Cons
Pros ✅
- Simple Web Server: Create HTTP servers without frameworks
- UTF-8 Default: Eliminates cross-platform charset issues
- Stream.toList(): Shorter, more readable code
- Less Boilerplate: Common tasks simplified
- Foreign Function API: Safer native code interop
Cons ❌
- Limited HTTP Server: Not production-ready for complex apps
- Short Support: Non-LTS release with 6-month window
- Learning Curve: Foreign Function API complex
- No Major Features: Incremental improvements only
Practical Example
// Quick REST API with Java 18
import com.sun.net.httpserver.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
public class TodoAPI {
static class TodoService {
List<String> todos = new CopyOnWriteArrayList<>();
public void addTodo(String todo) { todos.add(todo); }
public List<String> getTodos() { return new ArrayList<>(todos); }
}
public static void main(String[] args) throws IOException {
TodoService service = new TodoService();
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/todos", exchange -> {
if ("GET".equals(exchange.getRequestMethod())) {
String response = service.getTodos().toString();
exchange.getResponseHeaders().set("Content-Type", "application/json");
exchange.sendResponseHeaders(200, response.getBytes().length);
exchange.getResponseBody().write(response.getBytes());
}
exchange.close();
});
server.setExecutor(Executors.newFixedThreadPool(10));
server.start();
System.out.println("TODO API running on http://localhost:8080/todos");
}
}Conclusion
Java 18 shows Java's focus on developer experience with simpler APIs and better defaults. The simple web server API removes the need for frameworks for basic use cases. UTF-8 by default eliminates a major source of cross-platform bugs.
Not recommended for new projects, but useful for understanding Java's evolution toward simplicity. Join January 2023 for Java 21 LTS.
Key Points:
- Simple HTTP API is useful for prototypes and microservices
- UTF-8 default ensures global text handling
- Stream improvements make code more concise
- Short support window suggests staying on LTS releases