module java.sql {
requires transitive java.logging
requires transitive java.xml
uses java.sql.Driver
exports java.sql
exports javax.sql
exports javax.transaction.xa
}
this is a showcase, not a tutorial
slides at slides.codefx.org
Module System |
Language Changes |
New and Updated APIs |
New JVM Features |
Performance Improvements |
Delaying Java since 2008!
JARs have:
no explicit dependencies
no well-defined API
no concept of versions
Some consequences:
NoClassDefFoundError
no encapsulation across JARs
version conflicts
Modules are like JARs but have:
explicit dependencies
a well-defined API
no concept of versions
Some consequences:
reliable configuration
strong encapsulation
a couple of nice features
still version conflicts 😭
A file module-info.java
:
module java.sql {
requires transitive java.logging
requires transitive java.xml
uses java.sql.Driver
exports java.sql
exports javax.sql
exports javax.transaction.xa
}
module java.sql {
requires transitive java.logging
requires transitive java.xml
}
Module system enforces:
all required modules are present
no ambiguity
no static dependency cycles
no split packages
module java.sql {
exports java.sql
exports javax.sql
exports javax.transaction.xa
}
Say you want to access java.sql.ResultSet
.
Module system only grants access if:
ResultSet
is public
java.sql
is exported by java.sql
your module reads java.sql
The module system as a service registry:
module java.sql {
uses java.sql.Driver
}
module mysql.driver {
provides java.sql.Driver
with com.mysql.MySQLDriver;
}
Code in java.sql can now do this:
List<Driver> drivers = new ArrayList<>();
ServiceLoader
.load(Driver.class)
.forEach(drivers::add);
finer grained dependencies and exports
open packages and modules (for reflection)
unnamed and automatic modules (for migration)
layers (for containers)
jlink
to create runtime images
Enough complexity to feed authors, trainers, consultants for the next decade!
I’m writing a book!
EA soon ⇝ tiny.cc/fx-weekly
two-day Java 9 course
Zürich, 20.-21.04.
⇝ register at 42talents.com
Private Interface Methods |
Try-With-Resources |
Diamond Operator |
SafeVarargs |
Deprecation Warnings |
Private Interface Methods |
Try-With-Resources |
Diamond Operator |
SafeVarargs |
Deprecation Warnings |
Enabling reuse between default methods.
public interface InJava8 {
default boolean evenSum(int... numbers) {
return sum(numbers) % 2 == 0;
}
default boolean oddSum(int... numbers) {
return sum(numbers) % 2 == 1;
}
default int sum(int[] numbers) {
return IntStream.of(numbers).sum();
}
}
public interface InJava9 {
private int sum(int[] numbers) {
return IntStream.of(numbers).sum();
}
}
Just like private methods in abstract classes:
must be implemented
can not be overriden
can only be called in same source file
Private Interface Methods |
Try-With-Resources |
Diamond Operator |
SafeVarargs |
Deprecation Warnings |
Making try
-with-resources blocks cleaner.
void doSomethingWith(Connection connection)
throws Exception {
try(Connection c = connection) {
c.doSomething();
}
}
Why is c
necessary?
c
necessary?target of close()
must be obvious
⇝ resource should not be reassigned
easiest if resource is final
easiest if resource must be assigned
and can be made implicitly final
try(Connection c = connection)
But since Java 8 we have effectively final!
This works in Java 9:
void doSomethingWith(Connection connection)
throws Exception {
try(connection) {
connection.doSomething();
}
}
compiler knows that connection
is not reassigned
developers know what effectively final means
Private Interface Methods |
Try-With-Resources |
Diamond Operator |
SafeVarargs |
Deprecation Warnings |
A little more type inference.
Maybe the best example:
List<String> strings = new ArrayList<>();
used at a constructor call
tells Java to infer the parametric type
Diamond did not work with anon. classes:
<T> Box<T> createBox(T content) {
// we have to put the `T` here :(
return new Box<T>(content) { };
}
Reason are non-denotable types:
might be inferred by compiler for anon. classes
can not be expressed by JVM
Java 9 infers denotable types:
<T> Box<T> createBox(T content) {
return new Box<>(content) { };
}
Gives compile error if type is non-denotable:
Box<?> createCrazyBox(Object content) {
List<?> innerList = Arrays.asList(content);
// compile error
return new Box<>(innerList) { };
}
Private Interface Methods |
Try-With-Resources |
Diamond Operator |
SafeVarargs |
Deprecation Warnings |
One less warning you couldn’t do anything about.
Innocent looking code…
private <T> Optional<T> firstNonNull(T... args) {
return stream(args)
.filter(Objects::nonNull)
.findFirst();
}
Compiler warns (on call site, too):
Possible heap pollution from parameterized vararg type
For generic varargs argument T… args
,
you must not depend on it being a T[]
!
private <T> T[] replaceTwoNulls(
T value, T first, T second) {
return replaceAllNulls(value, first, second);
}
private <T> T[] replaceAllNulls(T value, T... args) {
// loop over `args`, replacing null with `value`
return args;
}
Compiler is aware of the problem and warns you.
If you think, everything’s under control:
@SafeVarargs
private <T> Optional<T> firstNonNull(T... args) {
return // [...]
}
Or not… In Java 8 this is a compile error!
Invalid SafeVarargs annotation. Instance
method <T>firstNonNull(T...) is not final.
The @SafeVarargs
annotation:
tells the caller that all is fine
only makes sense on methods
that can not be overriden
Which methods can’t be overriden?
⇝ final
methods
What about private
methods?
⇝ Damn! 😭
Looong story, here’s the point:
In Java 9 @SafeVarargs
can be applied to private methods.
Private Interface Methods |
Try-With-Resources |
Diamond Operator |
SafeVarargs |
Deprecation Warnings |
Another warning you couldn’t do anything about.
Should this code emit a warning?
// LineNumberInputStream is deprecated
import java.io.LineNumberInputStream;
public class DeprecatedImports {
LineNumberInputStream stream;
}
// LineNumberInputStream is deprecated
import java.io.LineNumberInputStream;
@Deprecated
public class DeprecatedImports {
LineNumberInputStream stream;
}
Java 9 no longer emits warnings
for importing deprecated members.
Warning free:
import java.io.LineNumberInputStream;
@Deprecated
public class DeprecatedImports {
LineNumberInputStream stream;
}
Stream Improvements |
Optional Improvements |
Collection Factories |
Reactive Streams |
Stack-Walking |
OS Processes |
Some APIs are new,
many existing ones were improved.
Stream Improvements |
Optional Improvements |
Collection Factories |
Reactive Streams |
Stack-Walking |
OS Processes |
Small improvements to Java 8 streams.
Stream::ofNullable
Creates a stream of zero or one elements:
long zero = Stream.ofNullable(null).count();
long one = Stream.ofNullable("42").count();
Stream::iterate
To use for
even less…
iterate(T seed,
Predicate<T> hasNext,
UnaryOperator<T> next);
Example:
Stream
.iterate(1, i -> i<=10, i -> 2*i)
.forEach(System.out::println);
// output: 1 2 4 8
Stream::iterate
Counter Example:
Enumeration<Integer> en = // ...
Stream.iterate(
en.nextElement(),
el -> en.hasMoreElements(),
el -> en.nextElement())
.forEach(System.out::println);
first nextElement()
then hasMoreElements()
⇝ fail
Stream::takeWhile
Stream as long as a condition is true:
Stream<T> takeWhile(Predicate<T> predicate);
Example:
Stream.of("a", "b", "c", "", "e")
.takeWhile(s -> !String.isEmpty(s));
.forEach(System.out::print);
// output: abc
Stream::dropWhile
Start streaming as soon as a condition is true:
Stream<T> dropWhile(Predicate<T> predicate);
Example:
Stream.of("a", "b", "c", "de", "f")
.dropWhile(s -> s.length <= 1);
.forEach(System.out::print);
// output: def
Stream Improvements |
Optional Improvements |
Collection Factories |
Reactive Streams |
Stack-Walking |
OS Processes |
Small improvements to Java 8 Optional
.
Optional::stream
Turns an Optional
into a Stream
of zero or one elements:
Stream<T> stream();
private Optional<Customer> findCustomer(int id) {
// ...
}
Stream<Customer> findCustomers(List<String> ids) {
return ids.stream()
.map(this::findCustomer)
// now we have a Stream<Optional<Customer>>
.flatMap(Optional::stream)
}
List<Order> getOrders(String id)
is expensive:
List<Order> findOrdersForCustomer(String id) {
return findCustomer(id)
.map(this::getOrders) // eager
.orElse(new ArrayList<>());
}
Steam<Order> findOrdersForCustomer(String id) {
return findCustomer(id)
.stream()
.map(this::getOrders) // lazy
.flatMap(List::stream);
}
Optional::or
Choose a non-empty Optional
:
Optional<T> or(Supplier<Optional<T>> supplier);
public interface Search {
Optional<Customer> inMemory(String id);
Optional<Customer> onDisk(String id);
Optional<Customer> remotely(String id);
default Optional<Customer> anywhere(String id) {
return inMemory(id)
.or(() -> onDisk(id))
.or(() -> remotely(id));
}
}
ifPresentOrElse
Like ifPresent
but do something if empty:
void ifPresentOrElse(
Consumer<T> action,
Runnable emptyAction);
Example:
void logLogin(String id) {
findCustomer(id)
.ifPresentorElse(
this::logCustomerLogin,
() -> logUnknownLogin(id));
}
Stream Improvements |
Optional Improvements |
Collection Factories |
Reactive Streams |
Stack-Walking |
OS Processes |
Creating ad-hoc collections more easily.
Wouldn’t this be awesome?
List<String> list = [ "a", "b", "c" ];
Map<String, Integer> map = [ "one" = 1, "two" = 2 ];
Not gonna happen!
language change is costly
binds language to collection framework
strongly favors specific collections
List<String> list = List.of("a", "b", "c");
Map<String, Integer> mapImmediate = Map.of(
"one", 1,
"two", 2,
"three", 3);
Map<String, Integer> mapEntries = Map.ofEntries(
entry("one", 1),
entry("two", 2),
entry("three", 3));
collections are immutable
(no immutability in type system, though)
null
values are forbidden
iteration order is random between JVM starts
Stream Improvements |
Optional Improvements |
Collection Factories |
Reactive Streams |
Stack-Walking |
OS Processes |
The JDK as common ground
for reactive stream libraries.
Publisher
produces items to consume
can be subscribed to
Subscriber
subscribes to publisher
onNext
, onError
, onComplete
Subscription
connection between publisher and subscriber
request
, cancel
create Publisher pub
and Subscriber sub
call pub.subscribe(sub)
pub creates Subscription script
and calls sub.onSubscription(script)
sub
can store script
sub
calls script.request(10)
pub
calls sub.onNext(element)
(max 10x)
pub
may call sub.OnError(err)
or sub.onComplete()
sub
may call script.cancel()
JDK only provides three interfaces
and one simple implementation.
(Also called Flow API.)
No JDK API uses them.
(No reactive HTTP connections etc.)
Stream Improvements |
Optional Improvements |
Collection Factories |
Reactive Streams |
Stack-Walking |
OS Processes |
Examining the stack faster and easier.
StackWalker::forEach
public static void main(String[] args) { one(); }
static void one() { two(); }
static void two() { three(); }
static void three() {
StackWalker.getInstance()
.forEach(System.out::println);
}
// output
StackWalkingExample.three(StackWalking.java:15)
StackWalkingExample.two(StackWalking.java:12)
StackWalkingExample.one(StackWalking.java:11)
StackWalkingExample.main(StackWalking.java:10)
StackWalker::walk
static void three() {
String line = StackWalker.getInstance().walk(
frames -> frames
.filter(f -> f.getMethodName().contains("one"))
.findFirst()
.map(f -> "Line " + f.getLineNumber())
.orElse("Unknown line");
);
System.out.println(line);
}
// output
Line 11
getInstance
takes options as arguments:
SHOW_REFLECT_FRAMES
for reflection frames
SHOW_HIDDEN_FRAMES
e.g. for lambda frames
RETAIN_CLASS_REFERENCE
for Class<?>
forEach
and walk
operate on StackFrame
:
class and method name
class as Class<?>
bytecode index and isNative
Can upgrade to StackTraceElement
(expensive):
file name and line number
creating StackTraceElement
is expensive
(for file name and line number)
lazy evaluation pays off for partial traversal
(Benchmarks performed by Arnaud Roger)
Stream Improvements |
Optional Improvements |
Collection Factories |
Reactive Streams |
Stack-Walking |
OS Processes |
Improving interaction with OS processes.
ls /home/nipa/tmp | grep pdf
Path dir = Paths.get("/home/nipa/tmp");
ProcessBuilder ls = new ProcessBuilder()
.command("ls")
.directory(dir.toFile());
ProcessBuilder grepPdf = new ProcessBuilder()
.command("grep", "pdf")
.redirectOutput(Redirect.INHERIT);
List<Process> lsThenGrep = ProcessBuilder
.startPipeline(asList(ls, grepPdf));
Process
Cool new methods on Process
:
boolean supportsNormalTermination();
long getPid();
CompletableFuture<Process> onExit();
Stream<ProcessHandle> children();
Stream<ProcessHandle> descendants();
ProcessHandle toHandle();
ProcessHandle
New functionality actually comes from ProcessHandle
.
Interesting static
methods:
Stream<ProcessHandle> allProcesses();
Optional<ProcessHandle> of(long pid);
ProcessHandle current();
ProcessHandle
can return Info
:
command, arguments
start time
CPU time
Many lower-level APIs.
Multi-Release JARs |
Redirected Platform Logging |
Multi-Release JARs |
Redirected Platform Logging |
"Do this on Java X, do that on Java Y."
Main
calls Version
:
public class Main {
public static void main(String[] args) {
System.out.println(new Version().get());
}
}
Version
exists twice:
public class Version {
public String get() { return "Java 8"; }
}
public class Version {
public String get() { return "Java 9"; }
}
(Btw, IDEs hate this!)
Now, here’s the magic:
compile Main
and Version[8]
to out/java-8
compile Version[9]
to out/java-9
use new jar
flag --release
:
jar9 --create --file out/mr.jar
-C out/java-8 .
--release 9 -C out/java-9 .
└ org
└ codefx ... (moar folders)
├ Main.class
└ Version.class
└ META-INF
└ versions
└ 9
└ org
└ codefx ... (moar folders)
└ Version.class
With java -cp out/mr.jar …Main
:
prints "Java 8"
on Java 8
prints "Java 9"
on Java 9
Great Success!
Multi-Release JARs |
Redirected Platform Logging |
Use your logging framework of choice
as backend for JDK logging.
New logging infrastructure inside the JDK:
new interface System.Logger
used by JDK classes
instances created by System.LoggerFinder
The interesting bit:
LoggerFinder
is a service!
Logger
public class SystemOutLogger implements Logger {
public String getName() { return "SystemOut"; }
public boolean isLoggable(Level level) { return true; }
public void log(
Level level, ResourceBundle bundle,
String format, Object... params) {
System.out.println(/* ...*/);
}
// another, similar `log` method
}
LoggerFinder
public class SystemOutLoggerFinder
implements LoggerFinder {
public Logger getLogger(
String name, Module module) {
return new SystemOutLogger();
}
}
Module descriptor for system.out.logger:
module system.out.logger {
provides java.lang.System.LoggerFinder
with system.out.logger.SystemOutLoggerFinder;
}
Module system and JDK take care of the rest!
Compact Strings |
Indified String Concatenation |
Compact Strings |
Indified String Concatenation |
Going from UTF-16 to ISO-8859-1.
20% - 30% of heap are char[]
for String
a char
is UTF-16 code unit ⇝ 2 bytes
most strings only require ISO-8859-1 ⇝ 1 byte
10% - 15% of memory is wasted!
For Java 9, String
was changed:
uses byte[]
instead of char[]
bytes per character:
1 if all characters are ISO-8859-1
2 otherwise
Only possible because String
makes
defensive copies of all arguments.
Simple benchmark:
(by Aleksey Shipilëv)
String method = generateString(size);
public String work() {
return "Calling method \"" + method + "\"";
}
Depending on circumstances:
throughput 1.4x
garbage less 1.85x
Compact Strings |
Indified String Concatenation |
"Improving" + "String" + "Concatenation"
What happens when you run:
String s = greeting + ", " + place + "!";
bytecode uses StringBuilder
JIT may (!) recognize and optimize
by writing content directly to new char[]
breaks down quickly
(e.g. with long
or double
)
new optimizations create new bytecode
new optimizations require recompile
test matrix JVMs vs bytecodes explodes
String::concat
?There is no such method.
concat(String… args)
requires toString
concat(Object… args)
requires boxing
Nothing fancy can be done
because compiler must use public API.
Invokedynamic came in Java 7:
compiler creates a recipe
runtime has to process it
defers decisions from compiler to runtime
(Used for lambda expressions and in Nashorn.)
With Indy compiler can express
"concat these things"
(without boxing!)
JVM executes by writing content
directly to new char[]
.
Depending on circumstances:
throughput 2.6x
garbage less 3.4x
(Benchmarks by Aleksey Shipilëv)
Depending on circumstances:
throughput 2.9x
garbage less 6.4x
(Benchmarks by Aleksey Shipilëv)
If you want the full picture, read
The Ultimate Guide To Java 9
Nicolai Parlog
codefx.org
sitepoint.com/java
@nipafx /
+NicolaiParlog
I’m writing a book!
EA soon ⇝ tiny.cc/fx-weekly
two-day Java 9 course
Zürich, 20.-21.04.
⇝ register at 42talents.com
or hire me!