A deprecated
option will produce a VM warning, while an unrecognized
(removed) option will cause the VM to exit.
Especially, -Xbootclasspath/p
is no longer a supported option. Use --patch-module
instead, which is the same effect as pre-pending the bootclasspath
in Java 8. See also JEP 261.
A java.lang.reflect.InaccessibleObjectException
indicates that you are trying to call setAccessible(true)
on a field or method of an encapsulated class. For example as below.
package io.github.nvxarm.access;
import jdk.internal.loader.URLClassPath;
import java.lang.reflect.Field;
public class InaccessibleObjectExample {
public static void main(String[] args) throws NoSuchFieldException {
Field field = URLClassPath.class.getDeclaredField("loaders");
field.setAccessible(true);
System.out.println(field.getName());
}
}
Use --add-opens
to give your code access to the non-public members of a package.
java --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED io.github.nvxarm.access.InaccessibleObjectExample
While a warning message like WARNING: An illegal reflective access operation has occurred
means that a module has not exported the package that is being accessed through reflection. For example as below.
package io.github.nvxarm.access;
import sun.nio.ch.Util;
public class IllegalReflectiveAccessExample {
public static void main(String[] args) {
System.out.println(Util.getTemporaryDirectBuffer(1).capacity());
}
}
Use --add-exports
to allow the target module to access the public types of the named package of the source module.
java --add-exports=java.base/sun.nio.ch=ALL-UNNAMED io.github.nvxarm.access.IllegalReflectiveAccessExample
However, either --add-opens
or --add-exports
should be considered as a work-around, not a long-term solution. Using these options breaks encapsulation of the module system. Try to revise your code by removing any reference to an internal API.
A java.lang.NoClassDefFoundError
can be caused by a split package problem, which is when a package is found in more than one library.
A java.lang.ClassNotFoundException
can be thrown when a removed package is in use. To resolve the issue, add a runtime dependency to your project. Below modules were deprecated in Java 9 and removed in Java 11. See also JEP 320.
Removed modules | Affected packages | Suggested dependencies |
---|---|---|
Java API for XML Web Services (JAX-WS) | java.xml.ws |
JAX WS RI Runtime (com.sun.xml.ws:jaxws-rt ) |
Java Architecture for XML Binding (JAXB) | java.xml.bind |
JAXB Runtime (org.glassfish.jaxb:jaxb-runtime ) |
JavaBeans Activation Framework (JAV) | java.activation |
JavaBeans Activation Framework (javax.activation:activation ) |
Common Annotations | java.xml.ws.annotation |
Javax Annotation API (javax.annotation:javax.annotation-api ) |
Common Object Request Broker Architecture (CORBA) | java.corba |
GlassFish CORBA ORB (org.glassfish.corba:glassfish-corba-orb ) |
Java Transaction API (JTA) | java.transaction |
Java Transaction API (javax.transaction:jta ) |
In Java 8, you can cast the system class loader to a URLClassLoader
. This is usually done by applications and libraries that want to inject classes into the classpath at runtime.
The class loader hierarchy has changed in Java 11. The system class loader is now an internal class. Casting to a URLClassLoader
will throw a ClassCastException
at runtime. There's no such API to dynamically augment the classpath at runtime, but it can be done through reflection, with the obvious caveats about using internal API.
Also, the boot class loader only loads core modules in Java 11. If you create a class loader with a null parent, it may not find all platform classes. In Java 11, you need to pass ClassLoader.getPlatformClassLoader()
instead of null
as the parent class loader in such cases.
The tool jdeps
can report internal APIs that are referenced by your classes. Please note it is a "static" analysis tool. That is, dynamic dependencies (for example, by reflective access like Java SPI) can not be reported. See also the online reference.
The list below comprises some of internal APIs, with recommended replacements respectively.
Internal APIs | Replacements |
---|---|
com.apple.eawt |
java.awt.Desktop since Java 9. |
com.sun.image.codec.jpeg , sun.awt.image.codec |
javax.imageio since Java 4. |
com.sun.net.ssl |
javax.net.ssl since Java 4. |
com.sun.org.apache.xml.internal.resolver |
javax.xml.catalog since Java 9. |
com.sun.org.apache.xml.internal.security |
javax.xml.crypto since Java 6. |
com.sun.rowset |
javax.sql.rowset.RowSetProvider since Java 7. |
com.sun.tools.javac |
javax.tools , javax.lang.model , com.sun.source since Java 6. |
java.awt.peer , java.awt.dnd.peer |
Code block if (c.getPeer() != null) { ... } could be replaced by if (c.isDisplayable()) { ... } . To test if a component has a LightweightPeer , use public boolean isLightweight() since Java 2. To obtain the color model of the component comes from the peer, method call getPanel().getPeer().getColorModel() could be replaced by public ColorModel getColorModel() . |
jdk.nashorn.internal.ir |
JEP 236 Parser API for Nashorn. |
org.relaxng.datatype |
org.relaxng was repackaged since Java 9. Users should include the org.relaxng types in the classpath. |
org.w3c.dom.xpath |
org.w3c.dom. , org.w3c.dom.xpath , org.w3c.dom.html , org.w3c.dom.css , org.w3c.dom.stylesheets since Java 9. |
java.lang.ClassLoader.defineClass() |
java.lang.invoke.MethodHandles.Lookup.defineClass() since Java 9. |
Security provider implementation class such as com.sun.net.ssl.internal.ssl.Provider , sun.security.provider.Sun , com.sun.crypto.provider.SunJCE |
java.security.Security.getProvider(NAME) since Java 3, where NAME is the security provider name such as "SUN" , "SunJCE" . |
sun.io |
java.nio.charsets since Java 4. |
sun.misc.BASE64Decoder , sun.misc.BASE64Encoder , com.sun.org.apache.xml.internal.security.utils.Base64 |
java.util.Base64 since Java 8. |
sun.misc.ClassLoaderUtil |
java.net.URLClassLoader.close() since Java 7. |
sun.misc.Cleaner |
java.lang.ref.PhantomReference since Java 2. |
sun.misc.Service |
java.util.ServiceLoader since Java 6. |
sun.misc.Timer |
java.util.Timer since Java 3. |
sun.misc.Unsafe |
java.lang.invoke.VarHandle , java.lang.invoke.MethodHandles.Lookup.defineClass() since Java 9. |
sun.reflect.Reflection.getCallerClass() |
java.lang.StackWalker.getCallerClass() since Java 9. |
sun.security.action |
java.security.PrivilegedAction to call System.getProperty or other action since Java 1. |
sun.security.krb5 |
Some provided in com.sun.security.jgss , javax.security.auth.kerkeros.EncryptionKey , javax.security.auth.kerkeros.KerberosCredMessage , javax.security.auth.kerberos.KerberosTicket.getSessionKey() since Java 9. |
sun.security.provider.PolicyFile() , sun.security.provider.PolicyFile(URL) |
java.security.Policy.getInstance("JavaPolicy", new java.security.URIParameter(uri)) since Java 6. |
sun.security.util.HostnameChecker |
javax.net.ssl.SSLParameters.setEndpointIdentificationAlgorithm("HTTPS" or "LDAPS") can be used to enabled hostname checking during handshaking, javax.net.ssl.HttpsURLConnection.setHostnameVerifier() can be customized hostname verifier rules for URL operations. |
sun.security.util.SecurityConstants |
java.lang.RuntimePermission , java.net.NetPermission , or specific Permission class since Java 1. |
sun.security.x509 |
javax.security.auth.x500.X500Principal since Java 4. |
sun.util.calendar.ZoneInfo |
java.util.TimeZone or java.time API since Java 8. |
There is a plugin for Maven, as below, which adds new goals jdkinternals
and test-jdkinternals
. If there is any usage detected of an internal API, the build will stop and fail.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jdeps-plugin</artifactId>
<version>3.1.2</version>
</plugin>
There is also a (third-party) plugin for Gradle, as below, which adds a new task jdeps
.
buildscript {
dependencies {
classpath 'org.kordamp.gradle:jdeps-gradle-plugin:0.16.0'
}
}
apply plugin: 'org.kordamp.gradle.jdeps'
The tool jdeprscan
can look for deprecated or removed APIs that are referenced by your classes. See also the online reference.
There is a (pre-release) plugin for Maven, as below, which adds new goals jdeprscan
and test-jdeprscan
.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jdeprscan-plugin</artifactId>
<version>3.0.0-alpha-1</version>
</plugin>
There is also a (third-party) plugin for Gradle, as below, which adds a new task jdeprscan
.
buildscript {
dependencies {
classpath 'org.kordamp.gradle:jdeprscan-gradle-plugin:0.10.0'
}
}
apply plugin: 'org.kordamp.gradle.jdeprscan'
- Script-like execution
Now java
can execute a single file directly, as below. You can leverage the static type language to build robust scripts. Surely, you can also take advantage of many libraries, such as java.net.http.HttpClient
- the official HTTP client introduced recently, which makes it quite easy to make an HTTP request and handle the response or any exception.
// save as: google.java
import java.net.*;
import java.net.http.*;
public class Google {
public static void main(String[] args) throws Exception {
var request = HttpRequest.newBuilder().uri(URI.create("https://www.google.com")).build();
var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
var html = response.body();
String title = html.replaceAll("(?is)(.*<title>)(.*?)(</title>.*)", "$2").replaceAll("\\R", " ");
System.out.println("Title: " + title);
System.out.println("URI: " + response.uri());
System.out.println("Status: " + response.statusCode());
response.headers().map().forEach((k, v) -> System.out.println("Header: " + k + "=" + v));
}
}
$ java google.java
Title: Google
URI: https://www.google.com
Status: 200
Header: cache-control=[private, max-age=0]
Header: content-length=[34277]
Header: content-type=[text/html; charset=UTF-8]
Header: expires=[-1]
Header: server=[gws]
...
You can even disable GC for applications with short lifecycle, to achieve maximized throughput.
$ java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+AlwaysPreTouch process-some-data.java
- Read–eval–print loop (REPL)
Using jshell
, you can enter program elements one at a time, immediately see the result, and make adjustments as needed.
It can either work in a console interactively, or read a file, as below.
$ jshell
jshell> "I can eat glass and it doesn't hurt me".replaceAll("(?i)[aeiou]", "*")
$1 ==> "_ c_n __t gl_ss _nd _t d__sn't h_rt m_"
// save as: google.jsh
import java.net.*;
import java.net.http.*;
var request = HttpRequest.newBuilder().uri(URI.create("https://www.google.com")).build()
var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString())
var html = response.body()
String title = html.replaceAll("(?is)(.*<title>)(.*?)(</title>.*)", "$2").replaceAll("\\R", " ")
System.out.println("Title: " + title)
System.out.println("URI: " + response.uri())
System.out.println("Status: " + response.statusCode())
response.headers().map().forEach((k, v) -> System.out.println("Header: " + k + "=" + v))
/exit
$ jshell google.jsh
Title: Google
URI: https://www.google.com
Status: 200
Header: cache-control=[private, max-age=0]
Header: content-length=[34277]
Header: content-type=[text/html; charset=UTF-8]
Header: expires=[-1]
Header: server=[gws]
...
- Type inference
Java compiler now looks at each variable declaration of var
and determines the type. Some limitations apply. For example as below.
private void test() {
var foo = "test"; // ok for local variables
System.out.println(foo);
foo = 1; // compile error: variables are still in static types
var bar; // compile error: a variable cannot be declared without initialization
bar = 2;
System.out.println(foo + bar);
var path = Paths.get("test.txt");
try (var lines = Files.lines(path)) { // ok in try-with-resources statements
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
var users = Map.of("Alice", 18, "Bob", 17, "Carol", 19);
var isChild = (Map.Entry<String, Integer> entry) -> entry.getValue() < 18; // compile error: type inference is not available in Lambda expressions
Predicate<Map.Entry<String, Integer>> isChild = entry -> entry.getValue() < 18; // you have to give the type explicitly
var children = users.entrySet().stream().filter(isChild).map(Map.Entry::getKey).collect(Collectors.toList());
System.out.println(children);
var list1 = new ArrayList<>(); // ok for diamond operators, the compiler will try to infer the most specific generic type
list1.add("test"); // note that "list1" will be inferred as type "ArrayList<Object>" in this case
Object o1 = list1.get(0); // because there's no enough information for the compiler to infer its generic type
List list2 = new ArrayList<>(); // mind the fact that the type "ArrayList<Object>" of "list1" is not the same as the raw type "ArrayList" of "list2"
list2.add("test"); // especially when you want to assign it to something that expects a generic list
Object o2 = list2.get(0); // for example as below
List<String> strings = list1; // compile error: "ArrayList<Object>" cannot be converted to "List<String>"
List<String> strings = list2; // compile successful with a warning: unchecked assignment "List" to "List<String>"
var list3 = new ArrayList<String>(); // "list3" will be inferred as type "ArrayList<String>" as expected
list3.add("test");
String s3 = list3.get(0);
List<String> list4 = new ArrayList<>(); // this is how you did prior to type inference was introduced, and "list4" is almost equivalent to "list3"
list4.add("test");
String s4 = list4.get(0);
list4 = List.copyOf(list3); // ok to assign the result to "list4", which can be any "List"
list3 = List.copyOf(list4); // compile error: since the result can be any "List", therefore it cannot be assigned to "list3", which must be an "ArrayList"
}
- Handy utilities
Some methods are introduced for creating small collections quickly, as below.
var list = List.of("a", "b", "c");
var set = Set.of("d", "e", "f");
var map = Map.of("k1", "v1", "k2", "v2", "k3", "v3");
Please note, collections created by above methods are immutable, which will throw an UnsupportedOperationException
when you are trying to modify them. Also, unlike ArrayList
, HashSet
and HashMap
, a NullPointerException
will be thrown when you are putting a null
value to collections created by above methods.
Interestingly, collections created by above methods are value-based. It means that factories are free to create a new instance or return an existing instance. Hence, if we create collections with same values, they may or may not refer to the same object on the heap. For instance, in below case, list1 == list2
may or may not evaluate to true
, depending on the JVM.
var list1 = List.of("a", "b", "c");
var list2 = List.of("a", "b", "c");
- Private methods in interface
For a better code structure inside interface, you are now able to add private methods and private static method in interfaces. Rules are as below.
-
Private interface method cannot be abstract.
-
Private method can be used only inside interface.
-
Private static method can be used inside other static and non-static interface methods.
-
Private non-static methods cannot be used inside private static methods.
Below is a summary of allowed keywords of some versions.
Keywords in interface | Java 7 | Java 8 | Java 11 |
---|---|---|---|
public abstract |
Allowed | Allowed | Allowed |
public static |
Allowed | Allowed | |
public default |
Allowed | Allowed | |
private |
Allowed | ||
private static |
Allowed |
- Modularity
A module is a group of packages and resources along with a descriptor. By default, all packages are private in a module, and even, you cannot use reflection on classes imported from other modules.
There are 4 types of modules as below.
-
System Modules – shipped with JDK, can be listed by
java --list-modules
command. -
Application Modules – your own code, defined by
module-info.class
which is included in the JAR you build. -
Unnamed Module – a module to maintain backward compatibility with legacy code, comprising all classes loaded onto the
classpath
but not themodule-path
-
Automatic Modules – generated by adding existing JAR files to the
module-path
, considered to be an interim compromise for plain old JAR files (those without module descriptors), so that you don't have to wait for your dependencies to be migrated to the modular structure.
Taking below as an example, build the project firstly.
$ gradle build
BUILD SUCCESSFUL in 1s
24 actionable tasks: 24 executed
Run the application without any engine and UI loaded. To shorten the java
command line, paths will be exported as shell variables, and required files will be copied into one single directory.
$ module_path=modularity-example/antivirus-app/build/libs
$ module=io.github.nvxarm.antivirus.app/io.github.nvxarm.antivirus.app.AntivirusApp
$ cp -t ${module_path} modularity-example/antivirus-{engine,ui,logger}/build/libs/*.jar
$ java -p ${module_path} -m ${module}
No antivirus engine has been loaded
No antivirus UI has been loaded
No antivirus logger has been loaded
Just put a random engine there, and run the application again, with the exactly same java
command line. You will see the engine is loaded by the application.
$ cp -t ${module_path} modularity-example/kaspersky-engine/build/libs/*.jar
$ java -p ${module_path} -m ${module}
No antivirus UI has been loaded
No antivirus logger has been loaded
Kaspersky engine is launched
Kaspersky engine is exiting
Then put a web UI there, with a console logger, and run the application again. You will be able to see a page opened in your default web browser. Log lines should also be formatted in your console.
$ cp -t ${module_path} modularity-example/{web-ui,console-logger,antivirus-io}/build/libs/*.jar
$ java -p ${module_path} -m ${module}
20:08:08 INFO - Kaspersky engine is launched
20:08:12 INFO - Kaspersky engine is exiting
Finally, put a desktop UI and a file logger there, with more engines to load, and run the application again. There will be a popup window, and you will be able to find log files, containing lines in a different format than the console lines.
$ cp -t ${module_path} modularity-example/{desktop-ui,file-logger,*-engine}/build/libs/*.jar
$ java -p ${module_path} -m ${module}
20:08:24 INFO - Avira engine is launched
20:08:25 INFO - Kaspersky engine is launched
20:08:26 INFO - McAfee engine is launched
20:08:30 INFO - Kaspersky engine is exiting
20:08:31 INFO - Avira engine is exiting
20:08:32 INFO - McAfee engine is exiting