- What’s new in Java
- JetBrains research
- Java 9, 10, 11, 12, 13, 14 and further ideas for development
- Support problems with sun.misc.unsafe packages
- Update is not always easy and painless
- Libraries Update
- Jdpes and jdeprscan
About new features in Java and migration of the PLM system
A lot has changed in Java. A few years have passed since the release of its most popular version – 8. According to a recent survey by Jetbrains ( https://www.jetbrains.com/lp/devecosystem-2019/java/ ) version 8 is still the one most used by developers. What is the reason? We already have the premiere of Java 13 behind us. There might be several reasons:
- Fast publishing cycle. Java is now released every six months – around April and March. The LTS version (long time support) every 3 years. Such a frequency of changes means that nobody decides to update every six months.
- A lot of projects have been started some time ago and are usually written in Java 8. Migration to the next version of LTS can be problematic and companies don’t want to waste money on upgrades. However, incurring a technical debt may prove fatal in the future.
- Universities in the curriculum rather teach in Java 8.
All this may change when the official free support for Java 8 ends – at the end of 2020.
Free or paid?
Some time ago information about a paid version of Java was released. Ultimately, it turned out that the devil was not so black as he was painted, and that there was nothing to fear. Java from Oracle remains free. The difference is in the magic word – support. Oracle develops the language in two distributions – Oracle JDK and OpenJDK. OracleJDK can be used free of charge for non-commercial purposes. You must have a license purchased for commercial use. However, you can use OpenJDK, which is completely free for commercial use. However, in the case of OpenJDK support lasts only 6 months.
The cost of the version with extended support
The cost will be 25$ for each processor on the server and cloud instances. However, for Java installed on the user’s computer 2.5$/month per user. You can choose one-, two- or three-year subscriptions.
About what’s new and interesting features
For some people, new in Java is synonymous with lambdas and streams added in Java 8. I do not deny that it was a big leap in language development, but a lot has happened since then, and the changes from version 8 are our daily bread. In the article, I would like to show the most important changes from Java 9 to Java 13, as well as those planned in the next versions. Of course, I will focus on the most important updates from the developer point of view.
Java 9 Merging JDK and JRE
Before this version, we distinguished between JDK (Java Development Kit) and JRE (Java Runtime Environment). JDK contained a JRE, but also a compiler (javac) and various tools such as javadoc. From Java 9, we get one package – JDK – with modified, merged structure of earlier JRE and JDK directories. The most important modification is moving the compiled JDK (rt.jar) and tools (tools.jar) in a slightly changed form to the lib directory.
The @Deprecated annotation functionality has been extended, giving us more information on the API marked in this way (i.e. field, class or method). We can now set two parameters, since – telling which Java version the entity is deprecated from and forRemoval – taking the logical value true or false (default value). If the API has been marked as forRemoval = true, we can expect it to completely disappear in the next or one of the next releases without backward compatibility! In addition, we have received a new tool – jdeprscan – for analyzing our codebase (single classes or whole jar files) for using the outdated API. With the jdeprscan –for-removal flag, the command will only return the API marked for removal. An interesting fact about this tool is that having it (from JDK 9), we are able to scan files compiled with earlier versions of Java, namely from version 6+.
Java 9 has introduced a new level of abstraction above packages. The module is the next level of encapsulation. It can be seen as a collection of closely related packages. Work on modularity lasted for many years under the name Project Jigsaw. The main idea was to enable faster language development. Thanks to the division of the entire JDK into many modules, it became possible (probably Jigsaw was one of the reasons for the new, faster release cycle).
What benefits does the module give us? The module as a container for packages is a layer that allows access control to its content. Simply put, it hides all contained APIs from other modules. Only explicit declaration in the so-called module descriptor allows us to make the selected APIs available to us outside.
Subjectively, the most important thing for a developer of all knowledge about modularization should be the new ladder of API access levels.
JLink, as in the picture, allows us to shrink our Java.
It is a tool with which you can match custom JREs to your chosen program. Instead of using the entire JDK, the runtime is assembled only from modules that are actually used in our application. JRE created and delivered in this way is able to execute our application without pre-installing the entire JDK. Custom JREs created for a simple HellowWorld, weigh ~ 40MB instead of 300MB (the weight of the last JDK).
Java in the console
Yes, it’s possible thanks to JShell. This is a mechanism known in many other languages under the name REPL (Read-Eval-Print Loop – a simple, interactive programming environment. The user can enter a command that is immediately executed and the result is displayed on the screen.
Is it even worth getting to know this tool?
There are several areas where JShell might work. My number one is teaching beginners the basics of programming. Thanks to the simple jshell -> enter, we do not have to start with running a heavy IDE with many icons, several windows and probably hundreds of unnecessary functions at the beginning. It would only obscure the whole. Also, try to explain to a person completely new in the world of programming what public static void main or public class is. Starting with object-orientation is not the best idea.
Thanks to REPL, we can start from the real basics. 2+2 -> enter assigns the result of the expression to the automatically generated variables $1, $2 etc. Of course, we can declare our own variables int i = 2 + 2 -> enter. And they got a preview of all the variables generated so far using the vars command. In a similar way, but with a full signature, we can generate methods and classes. And here a small tidbit – we can declare elements using non-existent types. The method or class created in this way simply cannot be used until all dependencies have been defined.
Java 10 : Var – local variable type inference
Similar solutions have already appeared in other languages, e.g. auto from C++. This change allows you to write code with hidden type in the source code, for example:
Our exampleVariable variable will still have the String type and we cannot assign another type of data to such a variable, e.g. Integer, in the following lines of code.
However, these “var” variables should be used with caution. I think that we shouldn’t obfuscate the code with an unnecessary shortening of the type name – it’s better to write the standard “String” rather than still use “var”. There are, however, a few cases in which you can safely take advantage of the new functionality:
For nested var collections, this works great – it shortens our code and gets rid of the structure that is difficult to read (at a glance).
Var also has some limitations. Here are a few of them:
- it can be used only for local variables in the method (it will not work for a field in the class),
- we can’t assign it to null (because null has no type)
- Java is still a strongly typed language, and var is just a guide,
- for backwards compatibility we can still call our variables the name var, which means the following construction is possible: var var = method ();
In addition to restrictions, we also get new opportunities. Var opens the way for us to one trick that could not have been done before. This is a trick that is better not to use on your project because it gently obscures the readability of the code. However, it is worth knowing that such a possibility exists. We’re talking about adding fields and methods in an ad hoc way to an existing class. We don’t need to create new source files with classes that would inherit from the existing class. You can use anonymous classes and our var to do this:
Java 11 (LTS): Running the program from source code
The first LTS in the new release cycle enables direct source code execution using the interpreter. The source code is compiled into memory and then executed by the interpeter. The restriction is that all classes must be defined within the same file. Therefore, we should not stick to this solution with larger programs.
This functionality is especially useful for beginners who want to try simple applications they have written. Together with jshell it will be a great set of tools for people starting to learn Java.
“Hello World” example.
We put our code in a file with the extension .java.
Then we run our program using the command:
Additionally, we can use this command in a configuration with the –source flag. It allows us to run our code in accordance with a specific version of Java (Java 7 and newer). Additionally, you can use this flag when the source file has a different extension than .java (or none).
If we would save our code in the file WitajSwiecie.app, the command would look like this:
Built-in HTTP Client
Until Java 11, we had a very weak API for making HTTP requests (for JDK queries). There were many problems with the HttpURLConnection class. Among other things, it did not support the HTTP 2(http/2) protocol, had many undocumented behaviors, had a blocking effect (one query per one thread). It also existed in Java since version JDK 1.1, and its maintenance has become very difficult. As an alternative, we could use one of many external libraries.
Due to the weak standard API and the frequent need to add external dependencies (after all, how many larger applications do not need to send an HTTP requests(http query)?), it was decided to standardize and introduce a new HTTP Client straight into JDK.
This API is really transparent, and the execution of a full HTTP request(comes down to a few lines of code:
String class extension
We also got a set of new methods in the java.lang.String class:
- strip – an improved version of the trim method, which has existed since the beginning of java, since times when character standards were not as developed as they are today. Trim simply cut out all characters whose code (decimal value) was equal to or less than 32 (space character). Strip actually cuts out all whitespace, which in today’s standards also appear in further positions.
- stripLeading and stripTrailing – the twin methods work similarly to strip, but they cut whitespace only at the beginning and end, respectively:
- isBlank – returns true if String consists only of whitespaces.
- lines – converts a single string into a Stream of String objects, where each subsequent object is a single line from the initial string. Useful when there is a need to perform operations for each line separately, and not for the entire text.
New input/output methods
There are also two very practical static methods in the FIles class:
- writeString – allows you to quickly save a String to a file.
- readString – the inverse of the first method allows easy file reading, returns content as a String object. It is very practical to combine it with the lines method of the String class. You can read the file and immediately interpret it line by line.
Released in March 2019, it introduces one very interesting change – switch expressions. A small revolution on the existing switch-case construction. This functionality is available as so-called preview feature.
These are the language functionalities that we must “unlock”. Preview feature may be changed or withdrawn in future language versions, so be careful with using such features in the production code. You can unlock the preview feature by adding the -enable-preview flag when compiling and running the program.
IntelliJ can do it for us, because the project structure has ready configurations with different levels of language, including preview features.
In switch expressions we get a new syntax for the old and good switch-case. For backwards compatibility, both forms of syntax are correct, except that if we decide on one way, we must continue it within one switch case.
What is striking is the syntax reminiscent of lambda syntax. You can also see that there are no breaks after each case. The new syntax ensures there is no so-called fall through – the code from the next cases will not be executed despite the lack of the break keyword.
Besides changing the syntax and design. The switch from “statement” became “expression”. What is the difference between such creations? Statement does not return value but expression does.
For example, Statement is an if-else construct. Since the new switch returns a value to us, it can be assigned to the variable. If case is one-line, then the value returned by the expression will be returned from the switch. If we would like to assign the result from the “old” switch to a variable, then we would have to create a variable before the switch and then assign a value to it in each case. That’s what it looks like:
With the new design, this can be significantly shortened and made much more readable:
I have already mentioned that if the expression in case has one line, its result will be returned. However, if we would like to include more logic, then similar to lambda we must wrap our code block with braces. Finally, we’ll use the break keyword with the return value. This is quite an unusual solution and may look a bit strange. Why not the return keyword? Return is used to return values from the entire method, so this would be unintuitive.
Switch Expression – exhaustiveness
It has also changed in terms of checking if all possible values in the case have been handled. In previous versions of Java, the compiler did not care whether all paths were checked in the source code. Only IDE and tools for static code analysis as for example sonar lint suggested that something may be missing in a particular place. The compiler only generated a warning. In Java 12 this situation will generate a compilation error.
Java 13: Text Blocks
Text blocks are another preview feature. They make it easier to handle large blocks of text that we have to paste into the code and assign to String. Currently, to paste such a large block, we need to concatenate each line or write the entire block in one line, using a newline character – “\n”. Of course, IDE comes to the rescue, which can generate concatenations and newline characters, but still such a record can be very illegible and simply inelegant.
What are Java text blocks and what are their benefits?
- They increase the readability of the code.
- They facilitate programming.
- The text block is still string.
- Do not confuse text block with raw string.
- They use the triple quotation mark operator- “””
Here are some examples of text blocks:
New methods have been added to the string class together with the text blocks. It is noteworthy that they are all marked as deprecated for removal. This is because the text blocks were introduced as a preview feature.
- String::stripIndent() – throws random spaces from the text block that we could have entered there.
- String::translateEscapes() – converts special characters of type \b \n \t to their unicode encoding.
- String::formatted(Object…args) – equivalent to String.format (), the difference is that String.format () is a static method and formatted is not.
More can be found in the official JEP description – https://openjdk.java.net/jeps/355
Switch Expression Update
In Java 13, we once again deal with switch expressions, which is still preview feature. This Java version introduces one small change to its functionality. As I mentioned earlier, we had to use the break keyword to return the value from case. However, we now have a slightly clearer and less surprising solution. The creators of the language have introduced a new keyword – yield and it replaces the previous break.
New Socket API
A change that is hardly visible from the programmer’s perspective, but very important for several other reasons is rewriting and rejuvenation of the Sockets API. The classes from the java.net package have been implemented anew, in particular the java.net.ServerSocket class. The new implementation is completely compatible with the previous API. Why was it decided to rewrite the sockets? There are several reasons, and here are some of them:
- The implementation of sockets came from the time of JDK 1.0
- Sockets were a mix of JDK1.0 code and C language, which made debugging code very difficult
- Important for the Loom project(https://openjdk.jnet/projects/loom/) – the project introduces to Java the concept of light threads – fibers
It was finally decided to work on the source of most problems generated by applications written in Java – the null pointer exception. The new version of the most popular exception has been tweaked a little. The new NPE shows us the exact location of the exception in its stack trace. Compared to the well-known NPE, it shows which object called our NPE, not just the line of code in which the exception occurred. This is quite a help for novice programmers who may have problems understanding NPE. This functionality is widely used during a long series of method calls. At first glance, you don’t really see what caused the exception, and you would need a debugger to find out. The new NullPointerException looks something like this
Of course, the changes described here are not all the language creators are currently working on. There are plenty of functionalities that are under development and have not yet been announced for a specific version of Java.
A brief history of updating the PLM system to java!1 Motivation?
After recalculating the costs of buying extended support for Java 8, the customer decided to migrate the large PLM system to the next version of LTS – Java 11. Updating such a bigproduct is not an easy undertaking, due to the million lines of code and long development (lasting several years) of the application. Due to the huge complexity of the application, we came across several interesting and unusual cases that can be considered useful when updating such a large product.
Our team was mainly responsible for updates and checking support and compatibility of libraries used by the system. We started our work by cataloging all libraries, checking their latest update dates and whether they are compatible with the new Java version. Some of this information required contact with library creators. It turned out that several libraries caused us a problem with their lack of compatibility. In this case, it was necessary to update to the latest version of the library (if it existed). If it didn’t, some replacement had to be found.
This library analysis would be very difficult without the right tools. Fortunately, there are several ready-made solutions that turn out to be very useful.
Jdeps & jdeprscan
To start with, all you need is the tools that are available with JDK. Jdeps is used to analyze the dependencies of our classes. The -jdkinternals flag is especially useful on the new Java. The effect of running the jdeps -jdkinternals jararchive.jar command will be a list of all classes that use the Java internal API. In general, no library should use code intended for use only within Java. Unfortunately, this is not so wonderful and over the years many libraries have often used packages such as sun.misc.* If this command does not return any result, it means that the jar archive is “clean” in terms of using Java’s internal API.
In the case of jmockit, it turns out that one class uses the sun.reflect package. Jdeps returns a fairly detailed description of the dependencies and we get a link with the suggested replacement.
Jdeprscan is another useful command. Particularly useful in combination with the -for-removal flag. This tool shows us all the uses of the deprecated API by a given JAR archive. The only deprecated uses of JDK methods are shown. We cannot use this to check for deprecation in a 3rd party jar. This functionality is provided by other tools, which we will discuss later. An interesting idea is also the –release switch which allows us to choose the Java version for which we will check the deprecated API, so using Java 13 in one terminal, we can still check depreciation for Java 11.
Java API compliance checker is an open source project written in Perl. which is used to compare the API of two different versions of libraries. The tool generates a report in HTML, which allows us to easily view changes.
To install the tool you need to make a clone of the official repository and run the make program.
An example of using the tool looks like this:
Learn more about JAPICC on github: https://github.com/lvc/japi-compliance-checker
A tool we didn’t use, but we came across during the project. Provides similar functionality to JAPICC. Sample reports from JDiff can be found in the Guava repository – https://guava.dev/releases/28.1-jre/api/diffs/
If we do not want to install any new software, we can always use the web application. ABI Laboratory (https://abi-laboratory.pro/) is an interesting solution that aggregates the previous one. One big disadvantage is that we’ll find the most popular open source libraries there. In order to check the less popular ones, use dedicated software.
How did the new Java features help us with the project?
Due to the fact that the project involved migration, we did not have much room for maneuver when it comes to new language features. More attention have been paid to API compatibility and support for 3rd party libraries. New features in Java had an impact on writing quick applications to automate certain activities. New methods on the String and Files class have proved very useful.
Single-file programs are a functionality to write an application that generates quick reports that can then be connected to a pipeline. We are convinced that the migration of such systems is a very good solution. The smaller technological debt is incurred, the easier it is to maintain the system later.