Friday, March 31, 2023

Java Modules: An Introduction

Java Modules, Oracle Java Certification, Java Learning, Jav Prep, Java Guides, Java Tutorial and Materials, Java Preparation, Java Learning, Java Guides

Java is one of the most widely used programming languages in the world. It has been around for over two decades and has been used to develop some of the most popular software applications in use today. However, Java has always had a few limitations that have hindered its progress in recent times. One of these limitations is the lack of a modular system that makes it easier to develop large and complex software systems. With the release of Java 9, this limitation has been addressed through the introduction of Java modules. In this article, we will take a closer look at Java modules and what they mean for Java developers.

What are Java Modules?


Java Modules are a new feature introduced in Java 9 that allow developers to partition a Java application into smaller, more manageable pieces called modules. Each module contains a set of related packages and classes that are grouped together based on their functionality.

Modules help developers to encapsulate code and reduce the complexity of large-scale applications. They also provide better control over dependencies, allowing developers to specify which modules can access other modules and which cannot. This makes it easier to manage the dependencies between different parts of the application and avoid conflicts and errors.

Modules are defined using a module-info.java file, which specifies the module's name, its dependencies on other modules, and the packages it exports to other modules. When the application is compiled and run, the Java runtime system loads and initializes the modules in a specific order, ensuring that all dependencies are satisfied.

Overall, Java Modules provide a more modular and flexible way to develop and maintain Java applications, making it easier to manage large and complex codebases.

Why do we need Java Modules?


There are several reasons why Java Modules are beneficial and necessary in modern Java development:

1. Modularity: Java applications can become large and complex over time, making it difficult to maintain and evolve them. Java Modules provide a way to modularize the code, allowing developers to break down large applications into smaller, more manageable pieces.

2. Encapsulation: Java Modules promote encapsulation, which is the practice of hiding implementation details from other parts of the application. This helps to reduce dependencies and makes it easier to change the implementation without affecting other parts of the application.

3. Dependency Management: Java Modules provide better control over dependencies, allowing developers to specify which modules can access other modules and which cannot. This makes it easier to manage dependencies between different parts of the application and avoid conflicts and errors.

4. Security: Java Modules provide better security by enforcing stronger access controls between modules. This makes it harder for malicious code to access or manipulate sensitive data or resources.

5. Performance: Java Modules can improve performance by reducing the amount of code that needs to be loaded and executed at runtime. This can result in faster startup times and reduced memory usage.

Overall, Java Modules provide a more flexible, scalable, and maintainable way to develop and manage Java applications, making it easier for developers to build and maintain high-quality software.

How do Java Modules work?


Java Modules work by dividing a Java application into smaller, self-contained units of code called modules. Each module contains a set of related packages and classes that are grouped together based on their functionality.

To create a Java Module, you need to create a module-info.java file that specifies the module's name, dependencies, and exports. The module-info.java file is located in the module's root directory and is compiled along with the module's source code.

When you compile a Java Module, the Java compiler checks the module-info.java file and generates a module descriptor that contains information about the module's name, dependencies, and exports. The module descriptor is stored in the module's JAR file.

At runtime, the Java Virtual Machine (JVM) reads the module descriptor and loads the module's classes and resources. The JVM uses a modular class loader to load classes from the module's JAR file and its dependencies, ensuring that all dependencies are satisfied before the module is loaded.

Once a Java Module is loaded, its classes and resources are encapsulated from other modules by default. This means that they cannot be accessed from outside the module unless they are explicitly exported or opened to other modules.

Java Modules also provide a way to declare services that can be provided by a module and consumed by other modules. This allows modules to interact with each other in a standardized way, without creating unnecessary dependencies.

Overall, Java Modules provide a modular, flexible, and secure way to develop and maintain Java applications, making it easier to manage large and complex codebases.

How to use Java Modules?


To use Java Modules in your application, you need to follow these steps:

1. Install Java 9 or higher: Java Modules were introduced in Java 9, so you need to have Java 9 or higher installed on your system to use them.

2. Create a module-info.java file: The module-info.java file is used to define a module. You need to create this file in the root directory of your module and specify the module's name, dependencies, and exports.

3. Compile your modules: Once you have created the module-info.java file, you need to compile your modules. You can compile your modules using the javac command with the -d option to specify the output directory.

4. Package your modules: After compiling your modules, you need to package them into JAR files. You can package your modules using the jar command with the --create option.

5. Run your application: Finally, you can run your application using the java command with the -p option to specify the module path and the -m option to specify the main module. You can also use the --module option to specify additional modules.

Here's an example of a module-info.java file:

module com.example.mymodule {
    requires java.base;
    requires com.example.mylibrary;

    exports com.example.mymodule.api;
}

In this example, the module "com.example.mymodule" requires the "java.base" module and the "com.example.mylibrary" module. It also exports the package "com.example.mymodule.api" to other modules.

To compile and package this module, you can use the following commands:

javac -d out/production/mymodule module-info.java com/example/mymodule/api/*.java
jar --create --file mymodule.jar -C out/production/mymodule.

To run this module, you can use the following command:

java -p mymodule.jar:mylibrary.jar -m com.example.mymodule/com.example.mymodule.Main

In this example, we specify the module path using the -p option and the main module using the -m option. We also include the "mylibrary.jar" file in the module path as it is a dependency of our module.

Advantages of using Java Modules


There are several advantages to using Java Modules in your application:

1. Modularity: Java Modules provide a modular architecture for developing applications. By breaking down a large application into smaller, more manageable modules, it becomes easier to develop, test, and maintain.

2. Encapsulation: Java Modules promote encapsulation by hiding the implementation details of a module from other modules. This reduces the coupling between modules and makes it easier to evolve and change the implementation of a module without affecting other modules.

3. Dependency Management: Java Modules provide a better way to manage dependencies between modules. Modules can specify which modules they require and which modules they export, making it easier to ensure that all dependencies are satisfied and avoiding conflicts.

4. Stronger Access Control: Java Modules provide stronger access control between modules, making it harder for malicious code to access or manipulate sensitive data or resources.

5. Improved Performance: Java Modules can improve application performance by reducing the amount of code that needs to be loaded and executed at runtime. This can result in faster startup times and reduced memory usage.

6. Standardized Service Provider Interface: Java Modules provide a standardized service provider interface that allows modules to provide services to other modules in a consistent and standardized way.

Overall, Java Modules provide a more flexible, scalable, and maintainable way to develop and manage Java applications, making it easier for developers to build and maintain high-quality software.

Disadvantages of using Java Modules


While there are many advantages to using Java Modules, there are also some potential disadvantages that should be considered:

1. Learning Curve: The introduction of Java Modules requires developers to learn new concepts and techniques for developing modular applications. This can require additional time and resources for training and development.

2. Compatibility: Applications that use Java Modules may not be compatible with older versions of Java that do not support modules. This can limit the portability and compatibility of applications that use modules.

3. Complexity: Developing modular applications using Java Modules can increase the complexity of the development process, especially for large applications that require many modules. This can make it harder to develop, test, and maintain the application.

4. Interoperability: Java Modules may not be compatible with existing frameworks or libraries that were not designed to work with modular applications. This can require additional effort to integrate existing code into a modular application.

5. Build and Deployment: Building and deploying applications that use Java Modules can be more complex than traditional Java applications. This can require additional tools and configuration to manage the build and deployment process.

Overall, while Java Modules provide many benefits, they may not be the best choice for every application. Developers should carefully consider the complexity, compatibility, and learning curve associated with using Java Modules before deciding to adopt them in their projects.

Wednesday, March 29, 2023

Quiz yourself: Java’s date and time TemporalAccessor generation interface

Core Java, Java Tutorial and Materials, Java Career, Java Skills, Java Jobs, Java Prep, Java Preparation

By the way, how’s your working knowledge of the Liskov substitution principle?


Given the following code fragment

String pattern = "dd MMM yyyy HH:mm";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
LocalDate x = LocalDate.of(2022, 11, 1);
System.out.println(formatter.format(x));

What is the result if the default locale is en_US? Choose one.


A. 01 Nov 2022 is printed.
B. 01 11 2022 is printed.
C. 01 N 2022 is printed.
D. 01 November 2022 is printed.
E. Compilation fails.
F. A runtime exception occurs.

Answer. The Date and Time API added to Java 8 seeks to provide a generalizable API for calendars and time systems that might vary substantially across cultures. For example, the Gregorian calendar is widely used throughout the world, and it has 12 months, most of which have 30 or 31 days.

However, some calendars have different numbers of months in a year, and some have different numbers of days in their months. This means that the concepts of date and time are surprisingly complex.

When faced with complexity, software engineers typically try to find generalizations that allow simplifications and abstractions, and they use them to allow client code to be simpler.

The Date and Time API generalizes many representations of date and time to an interface called TemporalAccessor. In particular, this interface is implemented by LocalDate, LocalTime, LocalDateTime, and ZonedDateTime, among many other classes. The interface generalizes access to fields for the hour of the day, the month of the year, and so forth. The most common of these fields are enumerated in the constants of the enum type ChronoField.

The formatting mechanisms offered by DateTimeFormatter are generalized in terms of TemporalAccessor as well. It’s at this point that a problem arises: The formatter is capable of formatting all manner of fields, including the fields for the hour and minute of time, the day, and the month and year of the date. In the template string, all five of those elements are presented with their placeholders. The pattern characters H and m correspond to the hour of the day (0 through 23) and the minute of the hour, respectively.

However, a LocalDate does not carry any time information, so an attempt to access these values cannot be fulfilled in any sensible way. Rather than present dummy values, the implementation of LocalDate throws an exception in response to requests it cannot properly fulfill.

Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay

Therefore, option F is correct.

The generalization TemporalAccessor, which is an interface, declares the same access methods regardless of the capabilities of the implementing class. Therefore, you can conclude that any code that attempts to access potentially unsupported fields through this interface will not generate compilation errors. This means that option E is incorrect.

It’s interesting to note that this approach, while allowing a powerful generalization over date and time types, contravenes the Liskov substitution principle (LSP) (which represents the L in SOLID, the set of five object-oriented design principles).

Very loosely, this principle suggests that if you believe an object is a generalization of something (in this case, that a LocalDate is a TemporalAccessor), that thing should substitute for the generalization in all uses without causing unexpected results. Surely throwing an exception on an otherwise valid method invocation qualifies as an unexpected result!

However, the benefit of breaking this principle in this situation is that the resulting API is much simpler and, most of the time, easier to use. As a further note, it’s possible to ask the TemporalAccessor whether it supports any given field using the method isSupported(TemporalField tf). However, the formatter does not check; if you use a given pattern character, the formatter requests that field directly, and the exception arises if that field is not supported by the target.

The suggested output shown in options A through D can be extracted successfully from a LocalDate—provided an appropriate formatter is used. For the sake of illustration, consider the following format strings, which would be needed to create those four output variations.

Option A would be correct for this pattern.

String pattern = "dd MMM yyyy";

Option B would be correct for this pattern.

String pattern = "dd L yyyy";

Option C would be correct for this pattern.

String pattern = "dd MMMMM yyyy";

And option D would be correct for this pattern.

String pattern = "dd MMMM yyyy";

Conclusion. The correct answer is option F.

Source: oracle.com

Friday, March 24, 2023

Announcing GraalVM Enterprise 22.3

GraalVM Enterprise 22.3, Oracle Java, Java Certification, Java Prep, Java Preparation, Java Tutorial and Materials, Announcing GraalVM Enterprise, Java Learning, Java Guides

The GraalVM 22.3 release delivers several new features including much anticipated support for Java 19 along with preview support for Project Loom virtual threads in both JVM JIT and Native Image ahead-of-time compiled applications. The release also includes compiler performance improvements, new monitoring and debugging features in Native Image, Python enhancements, and a new name for GraalPython!

Java 19


Java 19 support arrives in GraalVM 22.3 for Java and Native Image. Java 19 introduces language improvements from project Amber (Record Patterns and Pattern Matching for Switch), library enhancements to interoperate with non-Java Code (Foreign Function and Memory API), and the ability to leverage vector instructions (Vector API) from project Panama. It also adds preview support for Project Loom (Virtual Threads and Structured Concurrency).

Loom's virtual threads are a huge step forward for Java and can drastically reduce the effort required to write and maintain high-throughput, concurrent applications in Java. Now in GraalVM 22.3 for JDK 19 there's preview support for virtual threads. Native Image ahead-of-time compiled applications using vitual threads exhibit the same low resource requirements typically seen in native executables. This was demonstrated in a recent Devoxx conference session using a version of Conway's Game of Life that uses virtual threads with Native Image.

jlink


The JDK jlink utility generates a generates a custom runtime image that contains just the modules you need to run an application. Now in GraalVM 22.3 you can run jlink to generate a custom runtime image that uses the optimizing Graal JIT compiler for small high-performance application--these are ideal for use in containers when Native Image is not suitable.

Performance


GraalVM 22.3 introduces a new Graal JIT compiler optimization that uses a separate isolate for each compilation thread.  This change resulted in a significant 38% improvement in compilation speed on Renaissance, DeCapo, and Scala-DeCapo benchmarks.  This optimization is enabled by default and requires no configuration change.

Native Image


As GraalVM Native Image adoption continues to grow, additional developer tools and production monitoring and debugging support is needed. For monitoring, GraalVM 22.3 adds new JDK Flight Recorder (JFR) events (JavaMonitorEnter, JavaMonitorWait, and ThreadSleep) that build on the previous support for custom JFR events. JFR support will continue to be enhanced until it is on par with the JVM to ensure that ahead-of-time compiled applications enjoy the same level of support as JIT applications.

GraalVM Enterprise 22.3, Oracle Java, Java Certification, Java Prep, Java Preparation, Java Tutorial and Materials, Announcing GraalVM Enterprise, Java Learning, Java Guides
Performance analysis of native executables is improved with new support for the Linux perf command so that perf report prints Java method names in its output histogram. This means you can map native machine code performance issues to your original Java source. You can read more about using perf and valgrind in the GraalVM documentation.

GraalVM 22.3 provides initial support for jvmstat along with a new command line option to enable it. The -enable-monitoring=<all,heapdump,jfr,jvmstat> option provides fine-grained control over monitoring features included in native executables. The -H:+AllowVMInspection option is now deprecated.

Python


GraalVM support for Python has some big advances in the 22.3 release and also gets a name change. Starting with GraalVM 22.3, "GraalPython" is now "GraalPy". In accordance with this change, the launcher has been renamed to graalpy and symlinks from python and python3 are added to provide better integration with other tools.

GraalPy pip support is enhanced to automatically choose the best version for a known package. For example, "pip install pandas" selects the versions of pandas and numpy that have been tested with GraalPy. And with the addition of support for the sys.settrace API, you can now use GraalPy in PyCharm as the standard Python interpreter and with other standard tools.

GraalVM is known for its excellent performance and that applies to Python with a number of improvements in the 22.3 release including:

◉ A new bytecode-based interpreter that provides better startup performance and reduces memory footprint while retaining good JIT-compiled performance
◉ A 20-25% improvement in interpreter performance
◉ A 25% reduction in memory footprint in all benchmarks
◉ For developers on Apple silicon, GraalPy AArch64 binaries for Darwin and Linux are now available.

Source: oracle.com

Thursday, March 23, 2023

Java EE 7 Application Developer (1Z0-900) Certification: Is It Worth It?

1z0-900, java ee 7 application developer 1z0-900 study guide pdf, 1z0-900 book, java ee 7 application developer, java ee 7 application developer exam, java ee 7 application developer certification, 1z0-900 exam, 1z0-900 certification

The Java EE 7 course, aligned with the Oracle Java EE 7 Application Developer (1Z0-900) exam, certifies programming skills for developing and deploying Java Platform, Enterprise Edition 7 applications. Enterprise Edition (Java EE) is the industry standard for community-driven enterprise applications. Industry professionals, commercial and open source organizations, Java User Groups, and countless people contributed to the development of Java EE through the Java Community Process.

Anyone who has finished Java SE training and wants to expand their knowledge into the Java enterprise world should take this course. The Oracle Java EE 7 Application Developer exam verifies programming skills for developing and deploying Java Platform, Enterprise Edition 7 applications.

Importance of Oracle 1Z0-900 Certification for IT Professionals

Oracle Certification allows professionals to demonstrate in-demand abilities, improve overall job performance, and expand their earning potential while enhancing on-the-job confidence.

Candidates with the Oracle Foundations Associate Certification have a solid understanding of critical concepts and services. These are entry-level certifications for individuals with technical and non-technical backgrounds who want to demonstrate their knowledge of Oracle technologies and acquire an Oracle Certification. Oracle Certified Professionals are experts in Oracle products. This level of certification verifies advanced knowledge of Oracle ideas and abilities, as well as hands-on experience using them in real-world circumstances.

It is generally a program or action used to execute administration activities such as configuring, monitoring, managing users and their roles, starting and terminating MySQL servers with backups, and other similar operations.

The main benefits of Oracle Java EE Application Developer certification are:

  • Professional Credibility: Credibility in the workplace is earned through a combination of training and certification. Through the certification preparation process, knowledge is retained for a more extended time.
  • Empowerment: Earning a certification can help you earn more money, which will benefit you throughout your career.
  • Job Satisfaction: Certification can help you feel more satisfied at work, whether because you are learning new skills, making more money, obtaining more excellent opportunities, or receiving special recognition.
  • Opportunities and Recognition Have Increased: "Improve professional profile or status" is the most common reason for getting certification. Being viewed as knowledgeable and capable is closely tied to the opportunities you obtain in the corporate world.

With the 1Z0-900 certification, you will have the opportunity to work in better jobs. The 1Z0-900 certification will help you acquire more excellent work opportunities. Adding the Java EE 7 certification to your resume and a list of other qualifications qualifies you for talents in mobile and online application development that are in high demand.

Once you get certified, you can expect a better future as a Java programmer, Java enterprise developer, and Application Developer in one of the top companies in the world.

Java EE 7 Application Developer 1Z0-900 Exam

The Oracle Java EE 7 Application Developer (1Z0-900) Certification exam verifies your competence to develop and deploy Java Platform, Enterprise Edition 7 applications.

It also verifies programming skills for developing and deploying Java Platform Enterprise Edition 7 applications. It's for intermediate to advanced Java Front-End and Back-End application developers, architects, and software engineers that know how to use Java EE 7 technology to design web-based user interfaces with JavaScript, JSPs, JSFs, and servlets, as well as handle business logic.

Skills Required for 1Z0-900 Certification

You should have current training, hands-on programming experience, and previous Java SE certification. The entry-level requirement for the Oracle Java EE 7 Application Developer exam is to have Grade 12, Matric & Maths Literacy for NQF level (National Certificate).

This certification assesses Java EE programming skills, including the ability to:

  • Hands-on programming experience in Java SE is required.
  • Java SE certification (OCA / OCP) is desirable.

1Z0-900 Exam Details

Before moving on to exam details, let us discuss the syllabus. The topic that you need to master to clear this exam is given below:

  • Understand Java EE Architecture
  • Manage Persistence using JPA Entities and BeanValidation
  • Implement Business Logic by Using EJBs
  • Use Java Message Service API
  • Implement SOAP Services by Using JAX-WS and JAXB APIs
  • Create Java Web Applications using Servlets
  • Create Java Web Applications using JSPs
  • Implement REST Services using JAX-RS API
  • Create Java Applications using WebSockets
  • Develop Web Applications using JSFs
  • Secure Java EE 7 Applications
  • Use CDI Beans
  • Use Concurrency API in Java EE 7 Applications
  • Use Batch API in Java EE 7 Applications

Now it is time to get deeper into studies and explore more about SQL concepts. Also, remember to fill out the form for Oracle certification.

  • The exam number for Oracle Certified Professional, Java EE 7 Application Developer exam is 1Z0-900.
  • Only Oracle Certified Associates are eligible to write this exam.
  • This exam paper contains 70 multiple-choice questions that must be answered within 120 minutes.
  • The passing score for this exam is 66%. The 1Z0-900 exam price of this certificate is USD 245 (Pricing may vary by country or by localized currency).
  • The exam is available at any Pearson VUE testing center around the world.

1Z0-900 Exam Preparation Tips and Resources

To build your Java career with Java EE 7 Application Developer 1Z0-900 certification, follow these steps:

  • Gain Basic Knowledge of Java: Start by learning the basics of Java programming language. Understand syntax, data types, control structures, and object-oriented programming.
  • Learn Java EE: Once you understand Java, start learning Java Enterprise Edition (Java EE). This includes Servlets, JSP, JPA, EJB, and other related technologies.
  • Read the Oracle Documentation: Refer to the official Oracle documentation for Java EE 7, available online. This documentation provides detailed information about the Java EE APIs and their usage.
  • Get Hands-on Experience: Practice writing code and building applications using Java EE technologies, which will help you understand the concepts better and gain practical experience.
  • Enroll in a 1Z0-900 Certification Course: Enroll in a Java EE 7 Application Developer 1Z0-900 certification course. This course will give you the necessary knowledge and skills to pass the certification exam.
  • Take the 1Z0-900 Certification Exam: Once you have completed the course, take the Java EE 7 Application Developer 1Z0-900 certification exam. This exam tests your knowledge and skills in Java EE 7 and related technologies.
  • Keep Learning: Java constantly evolves, and new technologies and features are regularly added. Stay up-to-date with the latest developments in Java EE and related technologies by reading blogs, attending conferences, and participating in online forums.

Following these steps, you can build a successful Java career with Java EE 7 Application Developer 1Z0-900 certification.

Conclusion

Obtaining the Java EE 7 Application Developer 1Z0-900 certification can significantly enhance your career prospects in the Java industry. This certification validates your skills and expertise in Java EE 7 technologies, making you a valuable employer asset.

It is important to note that more than certification is needed to build a successful career in Java. Continuous learning, being up-to-date with the latest technologies, and gaining practical experience through hands-on projects are crucial for success in this field.

Following the above steps and continuously improving your skills, you can build a successful and fulfilling career as a Java EE 7 Application Developer.

Good luck on your journey!

Wednesday, March 22, 2023

Quiz yourself: The strange case of the Java developer’s birthday

Quiz Yourself, Java developer’s Birthday, Oralce Java Certification, Java Career, Java Prep, Java Guides, Java Tutorial and Materials, Java Learning, Java Guides Exam

You want to know what the day of the week will be, one year from today.


On her birthday one August, a Java developer decided to check what day of the week her next birthday will fall on.

Given the following partial code

LocalDate birthday = LocalDate.now();
// line n1
DayOfWeek dow = birthday.getDayOfWeek();

Which two of the following could be added at line n1 to perform this calculation? Choose two.

A. birthday = birthday.plus(365, ChronoUnit.DAYS);
B. birthday.plus(365, ChronoUnit.DAYS);
C. birthday = birthday.plus(52, ChronoUnit.WEEKS);
D. birthday = birthday.plus(12, ChronoUnit.MONTHS);
E. birthday = birthday.plus(1, ChronoUnit.YEARS);
F. birthday.plusYears(1);
G. birthday.plus(1, ChronoUnit.YEARS);

Answer. This question investigates some simple rules related to the Gregorian calendar and crucial elements of the Date and Time API added with the release of Java 8.

First, consider the Gregorian calendar. In the options above, you are offered choices involving adding 365 days, 52 weeks, 12 months, and 1 year. Only the last two are reliable, because leap years have 366 days, and no year has a full 52 weeks. So, you can immediately reject options A, B, and C as being incorrect from a logic perspective.

Options D, E, F, and G all look like they attempt to add either 12 months or 1 year, so they look believable from the perspective of business logic. To move forward, though, you must determine if the code is correct from the API perspective.

A key feature of the Date and Time API is that the classes within it (with some exceptions, notably the exceptions it defines) are immutable. This means that, as with the String class, when you invoke a method that changes a date-time element, you do not actually change the contents of the existing object; instead, you create a new object that represents the result of the change.

Therefore, if you wrote this String code

String s = "Hello";
s.toUpperCase();
System.out.println(s);

the output would be Hello because you failed to store the uppercase String HELLO that was created in the second line. The same error is made in options F and G. Both of those options correctly create a date exactly one year in the future—but that object is lost, so the third line of the original code sample would still be operating on today’s date. From this you can determine that options F and G are both incorrect.

You now have only two options left: options D and E. These are both correct from a business logic perspective: They are valid API methods, and the new LocalDate object they create is properly stored for use on the final line. From this you can determine that options D and E must be the correct answers.

However, there’s another wrinkle in this question, which explicitly mentions that it’s August. This has significance; that’s been ignored until now, but the logic given above is not complete without considering this.

When the developer runs the query on her birthday, if that day happens to be February 29 (which means that the day is in a leap year) a decision is forced on the API. Clearly it would be an error if the result were February 29 in the following year because the following year will not be a leap year. Therefore, the API would decide to treat the result as February 28 in the following year. That decision may be arbitrary, but it’s documented and therefore predictable.

That’s why, without a stipulation that precludes “today” from being February 29, even options D and E would be questionable—though they’d still be better choices than the others. If you encounter such a situation in an exam, you would be right to choose them in the absence of any other options.

Conclusion. The correct answers are options D and E.

Source: oracle.com

Monday, March 20, 2023

Go Native with Spring Boot 3 and GraalVM

Introduction


There has been a lot of interest in the past few years about "native Java". You've probably read articles or seen conference talks on the subject.

TL;DR - "native Java"


Traditionally Java programs are compiled to bytecode. At runtime the bytecode is first interpreted and eventually compiled to native machine code by a JVM.

Native Java is the idea of using ahead-of-time (AOT) compilation to produce a native executable or "native image" for a Java application.

A native image can run standalone without relying on a JVM.

This approach potentially offers advantages in terms of:

◉ faster application startup
◉ lower latency
◉ lower memory and CPU footprint and cost

GraalVM is an open source JDK that can compile Java applications to native images, as well as bytecode in the traditional way.

Native Java and Spring

Spring is the most popular Java application framework used today.

Over the last three years, the Spring and GraalVM teams have been working to make it easier for developers to deliver their Spring applications as native images.

Experimental support for native image was achieved via the Spring Native project.

With Spring Boot 3 and Spring 6 (due to be released November 24, 2022), support for native image will be available as a core feature.

Since the release candidate for Spring Boot 3 is now available, this seems a good time to try out the native image support.

Trying it out


So lets have a go at using the Spring Boot 3 release candidate and GraalVM to create a simple web application, compiled to a native executable.

Pre - requisites

You need to have GraalVM and it's native-image tool installed on your machine.

You can download these from here.

Note that you need GraalVM 22.3.0 and the equivalent version of native-image.

I'm using GraalVM Enterprise and native-image 22.3.0

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

The Spring Boot App

The easiest way to get started with building a Spring Boot app is to use the spring initializr.

I'm going to walk through using both Maven and Gradle, so you can pick whichever one you pefer.

You can either enter the configuration by hand, or re use one of:


After choosing the build engine, I selected:

  • Spring Boot: 3.0.0 (RC1)
  • Language: Java
  • Artifact: Vanilla
  • Dependencies:
    • GraalVM Native Support
    • Spring Web

Once you are happy with your configuration click the "GENERATE" button.

Then copy the downloaded file to a directory of your choice, unzip it and cd into the vanilla directory.

Gradle

Start by running the application:

./gradlew bootRun

The application should start in a second or so:

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

You can to test the application:

curl localhost:8080

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

404 isn't a good look, so let's add a RestController to say something more meaningful.

Copy Hello.java from the code directory into ./src/main/java/com/example/vanilla (or write your own).

If we run the application (./gradlew bootRun) and test it (curl localhost:8080) again we should see a more meaningful response this time:

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

We can also build a stand alone jar:

./gradlew assemble

And run that:

java -jar ./build/libs/vanilla-0.0.1-SNAPSHOT.jar

Again you should see the app start in about a second:

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

So now we can take the next step and build our native image without having to make any changes to the project:

./gradlew nativeCompile

This may be a good opportunity to have a tea or coffee as it will take two or three minutes as opposed to a couple of seconds for building a jar (on my Mac, YMMV).

Once the build completes:

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

We can run the executable:

./build/native/nativeCompile/vanilla

You should see the application start in around ~0.1 seconds (so about 10x faster).

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

Looking at memory usage on my machine, the native image uses about 1/3 of the memory compared to running the application from the jar.

Maven

Before you start, make sure that $JAVA_HOME is set to point to your GraalVM installation:

$ $JAVA_HOME/bin/java --version
java 17.0.5 2022-10-18 LTS
Java(TM) SE Runtime Environment GraalVM EE 22.3.0 (build 17.0.5+9-LTS-jvmci-22.3-b07)
Java HotSpot(TM) 64-Bit Server VM GraalVM EE 22.3.0 (build 17.0.5+9-LTS-jvmci-22.3-b07, mixed mode, sharing)

Then run the application:

./mvnw spring-boot:run

The application should start in a second or so:

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

You can use curl localhost:8080 to test the application:

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

404 isn't a good look, so let's add a RestController to say something more meaningful.

Copy Hello.java from the code directory into ./src/main/java/com/example/vanilla (or write your own).

If we run the application (./mvnw spring-boot:run) and test it (curl localhost:8080) again we should see a more meaningful response this time:

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

We can also build a stand alone jar:

/.mvnw package

And run that:

java -jar ./target/vanilla-0.0.1-SNAPSHOT.jar

Again you should see the app start in about a second.

So now we can take the next step and build our native image without having to make any changes to the project:

./mvnw -Pnative native:compile

This may be a good opportunity as it will take two or three minutes as opposed to a couple of seconds for building a jar (on my Mac, YMMV).

Once the build completes:

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

We can run the executable ./target/vanilla

You should see the application start in around ~0.1 seconds (so about 10x faster).

GraalVM, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Jobs, Oracle Java Skills

Looking at memory usage on my machine, the native image uses about 1/3 of the memory compared to running the application from the jar.

Source: oracle.com

Wednesday, March 15, 2023

Quiz yourself: Deserialization of Java enum types and records


Imagine you are given the following enum and record:

Quiz Yourself, Deserialization of Java Enum, Java Career, Java Skill, Java Tutorial and Materials, Java Job, Java Learning, Java Guides

enum Gender {
  MALE("M"), FEMALE("F"), OTHER;
  Gender() {
    System.out.print("Gender ");
  }
  Gender(String s) {
    System.out.print("GenderS ");
  }
}
record Person(int age, String name, Gender g) implements Serializable {
  Person() {
    this(0, "", Gender.OTHER);
    System.out.print("Person ");
  }
  Person {
    System.out.print("PersonC ");
  }
}

Previously an instance of Person was constructed like the following and serialized to a file:

var v = new Person(60, "John Doe", Gender.MALE);

What might be printed to the console when a program deserializes the contents of the file mentioned above? Choose two.

A. Gender PersonC Person
B. Gender PersonC
C. PersonC Person
D. PersonC
E. Gender Gender Gender Person PersonC
F. GenderS GenderS Gender Person PersonC
G. GenderS GenderS Gender PersonC

Answer. This question investigates the mechanisms of deserialization for both enum types and records.

When a record is deserialized, each of the attributes—in this case the age, name, and gender attributes—must be deserialized to their respective objects or values first. (Note that the age primitive will not create an object.) These attributes are then passed to the canonical constructor of the record. Note that this behavior with records is quite different from deserializing instances of normal classes. No output is printed in handling the int age and the String name attributes, but it’s possible that output might be printed for Gender. You should look at this first.

Whenever code running in a JVM refers to a particular enum value, execution cannot proceed unless that enum value has been initialized. In most cases, the very first reference to one of these values will require several steps to be performed: class loading, class linking, and class preparation. Those three steps must happen in the order listed.

It’s fairly unusual for developers to notice the fact that these steps are separate, but that really doesn’t matter. Class preparation is the step that performs static initialization, and therefore it’s the step that creates the values of the enum itself. This quiz question is unaffected by the earlier steps, so the following discussion will consider the enum to be fully prepared, ignoring the fact that that’s actually the last of several steps. (If you’re interested in this, you can find more in section 12.2 and section 12.3 of the Java Language Specification, which in turn refer to the Java Virtual Machine Specification.)

For this question, you need to know three things.

◉ An attempt to deserialize an object that contains a field of an enum type will make a reference to an enum value.
◉ Any reference to an enum value requires the enum class to be fully prepared.
◉ Therefore, the enum must have been fully prepared—complete with the initialization of all the enum’s constants—before deserialization can proceed.

In view of the above, there are now two distinct possibilities.

First, suppose that at the moment when an instance of Person is deserialized, the enum has not been fully prepared. In this case, it will be necessary to perform that preparation before Person can proceed with deserialization. That initialization includes instantiating all three of the enum’s values. In this situation, expect the three instances to be initialized using their appropriate constructors. (Remember this is the initialization process, not deserialization itself, that is causing this behavior.) So, in this situation, you will see GenderS GenderS Gender as the output, preceding any output referring to Person.

By contrast, if the Gender type has been fully prepared before the moment when an instance of Person is deserialized, the three instances already exist, and no further constructor behavior will be invoked for the Gender type. In such a situation, no messages related to Gender will be output by the deserialization process.

Once the enum is fully prepared, deserialization of the record type can proceed; note that record types differ from other classes in how this happens. A particular difference is that the canonical constructor for a record type is invoked to perform the initialization of the newly allocated object. By contrast, regular serializable classes do not have any constructor invoked, nor is any of the normal instance initialization of the serializable class itself run. (Note, however, that nonserializable parent types will have their default constructors executed during deserialization.)

Given that the canonical constructor is invoked, you will see the message PersonC printed as output. Note that in this case, the canonical constructor is presented in the compact form whereas the argument list is omitted, as is the code that initializes the fields of the record.

Because you might or might not see GenderS GenderS Gender, and you will see PersonC in the output, the two valid answers are

PersonC
GenderS GenderS Gender PersonC

These match options D and G, which tells you that those are the correct options and, by elimination, the other options are incorrect.

If you want to dive more deeply into the serialization process, it is, of course, in the Java specification. See the section that describes the process as it relates to records. In particular, the documentation notes the following:

During deserialization, if the local class equivalent of the specified stream class descriptor is a record class, then first the stream fields are read and reconstructed to serve as the record’s component values; and second, a record object is created by invoking the record’s canonical constructor with the component values as arguments…

 

The process related to enums is documented in this section of the Java specification, which notes

To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.lang.Enum.valueOf method, passing the constant’s enum type along with the received constant name as arguments.

 

Notice that the enum deserialization process does not instantiate the values of the enum unless, by necessity, the static initialization process must be completed to complete the deserialization.

As a side note, the descriptions above touched on the idea that there are several steps on the way to class preparation. Most JVMs will perform these steps lazily, simply ensuring that a class is prepared before it’s actually needed. The exact details can vary from one JVM to the next even if the behavior of the developer’s code conforms to the expectations listed in the Java specification.

This lazy approach can be quite beneficial. For example, consider a program with a help system that is built with many classes that are unique to it. If the help system is never used during an execution of that program, those classes might not be needed, and they might never be loaded at all. Further, even if a class is loaded, it might not need to be prepared. Either of these situations can save memory.

To see how this works, try building the following code and observing at what point in the process the MyLazySingleton class is prepared. (Remember that preparation relates to the static initialization.)

class MyLazySingleton {
  static {
    System.out.println("preparing MyLazySingleton");
  }
  private static final MyLazySingleton singleton =
    new MyLazySingleton();
  private MyLazySingleton() {
    System.out.println("instantiating MyLazySingleton");
  }
  public static MyLazySingleton get() {
    return singleton;
  }
}

public class TryStuff {
  public static void main(String[] args) {
    System.out.println("Starting");
    MyLazySingleton mls = null;
    System.out.println("mls variable initialized to null");
    Class<?> cl = MyLazySingleton.class;
    System.out.println("MyLazySingleton.class evaluated.");
    mls = MyLazySingleton.get();
    System.out.println("singleton retrieved");
  }
}

If you run this code under a tracing tool, you’ll probably notice that the file myclasspath/packagename/MyLazySingleton.class is read during the reference to the java.lang.Class object MyLazySingleton.class. However, you’ll also see that the static initialization and, with it, the instantiation of the single instance of MyLazySingleton, does not occur until the code invokes MyLazySingleton.get().

Conclusion. The correct answers are options D and G.

Source: oracle.com

Monday, March 13, 2023

TruffleRuby on OCI Container Instances

Elegance at Speed


Ruby is one of my favourite programming languages. I really appreciate how expressive it is, how productive (and how much fun) it makes development.

Unfortunately Ruby has a reputation for being slow compared to other languages such as JavaScript.

While the performance of the default Ruby interpreter has improved, it still hasn't had the engineering investment that goes into V8.

Friday, March 10, 2023

Quiz yourself: Verifying the operation of stinky Java code


Your colleague is working on an application that analyzes statistics for vehicles of different makes and models. A business rule requirement is that each car instance in the application must have a unique manufacturing year; creating a second instance of a vehicle with the same year must fail.

Quiz yourself, Java code, Oracle Java Career, Java skills, Java Jobs, Java Tutorial and Materials, Java Learning, Java Prep, Java Preparation, Java Certification

The Car class created by the colleague is as follows:

Copy code snippet
Copied to ClipboardError: Could not CopyCopied to ClipboardError: Could not Copy
public class Car {
  Integer year;
  String make;
  String model;
  List<Integer> existingYears = new ArrayList<>();
  public Car(Integer year, String make, String model) {
    this.year = year;
    this.make = make;
    this.model = model;
    existingYears.add(year);
    checkYear(year);
  }
  void checkYear(Integer year) {
    for (int i=0; i < existingYears.size()-1; i++ ) {
      if (existingYears.get(i).equals(year)) {
        throw new IllegalArgumentException("Duplicated year !");
      }
    }
  }
}
public class Car {
  Integer year;
  String make;
  String model;
  List<Integer> existingYears = new ArrayList<>();
  public Car(Integer year, String make, String model) {
    this.year = year;
    this.make = make;
    this.model = model;
    existingYears.add(year);
    checkYear(year);
  }
  void checkYear(Integer year) {
    for (int i=0; i < existingYears.size()-1; i++ ) {
      if (existingYears.get(i).equals(year)) {
        throw new IllegalArgumentException("Duplicated year !");
      }
    }
  }
}

You wonder if the business constraint is properly implemented and decide to test the code using the following code fragment:

new Car(2018, "Honda", "Civic"); // line n1
new Car(2021, "Hyundai", "Accent"); // line n2
new Car(2018, "Ford", "Expedition"); // line n3

Which statement is true? Choose one.

A. The test code runs without throwing an exception.
B. Line n1 will cause IllegalArgumentException.
C. Line n1 will cause IndexOutOfBoundsException.
D. Line n3 will cause IllegalArgumentException.
E. Line n2 will cause IndexOutOfBoundsException.

Answer. This example is perhaps not code you’d be proud of, but you didn’t write it, so there’s no need for embarrassment here.

Look at the code presented and notice that it seems the intention is roughly as follows:

◉ For each call to the constructor, add the proposed list to a list called existingYears.
◉ Call the method checkYear, which performs two things. First it determines if that year has been used before and, if it has, it throws an IllegalArgumentException.

The root problem here is that the existingYears list is created as an instance member of a Car object, and therefore every Car object has a List of its own. Consequently, the years in which other Car objects were created will not be found in each List. The code does not work as intended and will not prevent two separate instances of Car from having the same year value.

If instead the existingYears field carried the static modifier, only one instance List would exist in the program. In that case, the comparison would check the current car’s year with those seen before and line n3 would throw an IllegalArgumentException. That change would make option D correct. But since that wasn’t done, option D is incorrect.

You might notice that the current car’s year is added to the list before the check is run. If you fail to pay close attention to the control of the loop that searches through the list, you might expect that an exception would be thrown for every new car, based on an expected collision with its own year. That would lead you to believe that the code would throw an IllegalArgumentException for any and all attempts to instantiate a Car. In that situation, the first exception would presumably prevent further execution, and you would expect an exception at line n1. However, a more thorough investigation shows that the loop stops before the last item is checked, which is why option B is incorrect.

Notice the following test in the loop:

i < existingYears.size()-1

Because the size value is reduced by one, the code would ignore the last item in the list. That item will be the current car’s year, so the feared collision would not occur. Further, because existingYears is an instance field, the list’s size in the checkYear method is always 1, and the body of the loop never executes at all.

Two of the options suggest that an IndexOutOfBoundsException might be thrown. However, as just noted, the size of the list is always 1 when the checkYear method executes, and the body of the loop will never execute. No code outside that loop attempts to access data from the list using an index; therefore, no code that could possibly throw an IndexOutOfBoundsException will ever be executed. From this you can conclude that options C and E are incorrect.

From the observations above, you can conclude that no exceptions are thrown, and option A must be correct.

Now that the answer has been determined, here are some of the least pleasant aspects of the code—besides the fact that it doesn’t work.

◉ Even though the code for avoiding a collision with the current car’s year is functionally correct, if an attempt is made to add a car that has a duplicate year, existingYears will contain the same year twice. While this would not cause errors in the code shown so far, unexpected behaviors such as this are confusing to programmers trying to make sense of things later, both when they are reading code and when they are debugging. It’s also common for this sort of situation to lead to bugs later when the peculiarity is not thoroughly understood by all. It would be far better to add the year only after it has been approved.

◉ Creating any object with nonprivate mutable fields is generally unwise. It allows changes to be made by any code in the same package. If such changes are made without understanding the implications and, thereby violating some consistency rule pertaining to the data, it’s easy to cause bugs that are hard to track down, especially because such a large amount of code (the entire package or more) might have been responsible for the change.

◉ Checking to see if a Collection contains a given object should be done using the contains method. It’s immediately and unambiguously clear what this method does. By contrast, writing a loop describing how to perform the action leaves the programmer who is reading the code with the responsibility for working out what the code is intended to achieve. Generally speaking, it’s preferable that code have a more declarative nature and a less imperative one. Of course, to be able to use the contains method, you must first ensure that the current car’s year is not added before validation.

◉ Iterating lists using the traditional C-style for loop to control an index for accessing list elements should be avoided. Some lists in Java’s collections (typically those with array backing) iterate tolerably efficiently this way, but if the implementation changes to use a different structure (such as a LinkedList), the resulting efficiency will be relatively or exceedingly poor. If possible, give any data structure the chance to perform its own iteration, since the programmer who created that structure should know best how to iterate the elements efficiently. Instead of using an index-counting loop such as the C-style loop, use Java’s enhanced for loop or extract an Iterator (or perhaps ListIterator, if the situation calls for it) and use that.

Conclusion. The correct answer is option A.

Source: oracle.com

Monday, March 6, 2023

Pseudorandom numbers in Java, Part 2: Randomness with Java 17


Java 17 introduced significant changes to the platform’s pseudorandom number generation capabilities. The first article in this series, “Pseudorandom numbers in Java, Part 1: The Background,” offers a discussion of pseudorandom number generators (PRNGs) and how they work. This article discusses the changes made to PRNGs in Java 17.

Pseudorandom Numbers, Java 17, Oracle Java Tutorial and Materials, Core Java, Java Career, Java Skills, Java Jobs, Java Certification, Java Prep, Java Prepararation

Random number generation was perhaps not given quite enough love in early releases of Java. This inspired a Wikipedia discussion of poor-quality PRNGs that noted

As an illustration, consider the widely used programming language Java. Up until 2020, Java still relied on a linear congruential generator (LCG) for its PRNG, which is of low quality…

Developers have been able to choose between several random generators in Java. The best-known is java.util.Random; another well-known interface, java.lang.Math.random(), simply delegates to java.util.Random.nextDouble().

Java 17 introduced a new interface, java.util.random.RandomGenerator, to consolidate the implementations of existing and new random number generators. The new code is in the java.util.random package, which is also new in Java 17. RandomGenerator has the same methods as java.util.Random, including both the legacy methods and the Java 8 methods that generate Java streams of randoms of various types. For example, for double values, the new interface contains the methods described in Listing 1.

Listing 1. Methods from RandomGenerator with double and DoubleStream values

public default double nextDouble();
    public default double nextDouble(double min);
    public default double nextDouble(double min, double max);
    public default DoubleStream doubles();
    public default DoubleStream doubles(double min, double max);
    public default DoubleStream doubles(long count);
    public default DoubleStream doubles(long count, double min,
        double max);
    public default double nextGaussian();
    public default double nextGaussian(double min, double max);
    public default double nextExponential();

There are similar methods for int and long numeric types. Raw bytes and booleans do not get the same treatment; there is only void nextBytes(byte[]) and boolean nextBoolean().

The legacy classes java.util.Random and java.util.SecureRandom were backfitted to implement this interface, so if you switch the declaration to the new interface, the legacy implementations become interchangeable with the new generators. The legacy classes’ algorithms were not improved, however, to avoid breaking Java’s ever-important backwards compatibility. In fact, given the same seed, the java.util.Random class in both Java 11 and Java 17 will output exactly the same sequence.

In the following example, Random5 is a modified version of the random histogram program shown in Part 1 of this series, which starts with a fixed seed. It should (and does) produce the same stream with both implementations. The withjava command is a script of my own that I use to run Random5 on a particular version of Java.

$ withjava 11 java -Xms4G -Xmx4G Random5.java
Using JAVA_HOME=/usr/local/jdk-11 java -Xms4G -Xmx4G Random5.java in /home/ian/git/Randomness/src/main/java/com/darwinsys/random/15vs17
First 5 random ints are:
1553932502 -2090749135 -287790814 -355989640 -716867186
Generating 100000000 randoms using Random
Generating randoms took 6640 mSec
$ withjava 17 java -Xms4G -Xmx4G Random5.java
Using JAVA_HOME=/usr/local/jdk-17 java -Xms4G -Xmx4G Random5.java in /home/ian/git/Randomness/src/main/java/com/darwinsys/random/15vs17
First 5 random ints are:
1553932502 -2090749135 -287790814 -355989640 -716867186
Generating 100000000 randoms using Random
Generating randoms took 7448 mSec
$

In my tests, over multiple runs, the Java 17 implementation was consistently 10% to 12% slower than the Java 11 version, while generating exactly the same outputs.

SecureRandom has always been about generating better random numbers for use in security-related software. The implementation is intended to meet the requirements specified in section 4.9.1 of the US National Institute of Standards and Technology (NIST) FIPS PUB 140-2, “Security Requirements for Cryptographic Modules,” and the Internet Engineering Task Force (IETF) RFC 4086, “Randomness Requirements for Security.”

Java has two other legacy RandomGenerator implementations: ThreadLocalRandom (from Java 1.7) and SplittableRandom (from Java 1.8). These are listed in the Java 17 RandomGenerator documentation. Of these, ThreadLocalRandom is meant for use in multithreaded designs, and the SplittableRandom class is designed for use in fork/join algorithms where each split should have its own independent PRNG. These two do not meet the security requirements mentioned above; SecureRandom should be used when security is needed.

It turns out that most of the methods of the RandomGenerator interface have default methods coded in terms of nextLong() (the only abstract method in the interface); a complete (but not very usable) implementation of this interface is shown in Listing 2.

Listing 2. A minimal implementation of Java 17’s RandomGenerator

public class CrappyRandom implements RandomGenerator {

    // The attacker has a hard time guessing the
    // exact nanosecond at which this is initialized.
    private long x0 = (21 * System.nanoTime()) & 0xffff;
    private long a = (long) Math.pow(2,7) + 1;
    private long c = 1024;   // The increment
    private long mod = (long) Math.pow(2,32);  // The modulus
    private long lastVal = x0;

    public void setKnuthBadParams() {
        // As per Knuth , to get 7 6 9 0, 7 6 9 0, ...
        mod = 10;
        lastVal = x0 = a = c = 7;
    }

    @Override
    /**
     * This is a crude implementation just to get going.
     * Tests will reveal its quality (or lack thereof).
     */
    public long nextLong() {
        return lastVal = (a * lastVal + c) % mod;
    }
}

Important note: Please do not use this sample code for anything! It produces terribly bad random numbers. It is just here to show that if you can write a decent algorithm, the interface will massage its results to provide all the other types needed to complete the interface.

To avoid the poor behavior of LCG algorithms used on their own, Java 17 introduces a new class of random number algorithms called LXM. The name stands for “LCG + XOR + Mix,” representing the three stages: an LCG, an XOR subgenerator, and a mixer to combine them. The first two stages are based on the best-available LCG values and an exclusive-or (XOR) function; this combination has been shown to provide the best results.

To avoid creating dozens of new public classes for this family of algorithms, Java 17 introduces the new RandomGeneratorFactory class, which allows you to create an implementation of a particular algorithm, as follows:

String algorithm = "L64X1024MixRandom";
RandomGenerator genx = RandomGeneratorFactory.of(algorithm).create();
genx.doubles(1000).doSomethingWithValues();

These algorithms are implemented by nonpublic classes with the same names in the private jdk.random package. For example, the snippet above assigns genx to an instance of jdk.random.L64X1024MixRandom. The list of new algorithms is

  • L32X64MixRandom
  • L32X64StarStarRandom
  • L64X128MixRandom
  • L64X128StarStarRandom
  • L64X256MixRandom
  • L64X1024MixRandom
  • L128X128MixRandom
  • L128X256MixRandom
  • L128X1024MixRandom
  • Xoshiro256PlusPlus
  • Xoroshiro128PlusPlus

The RandomGenerator interface includes a getDefault() default method, which, as expected, returns a usable implementation of the interface without specifying too much about its behavior. In Java 17 on my system, it returns a jdk.random.L32X64MixRandom, but that is not documented and should not be counted on. However, note that getDefault() will be a decent choice unless you need one of the others.

RandomGeneratorFactory also has an all() method that returns a stream of all the providers. Running my simple ShowAllGenerators program displays the following:

Group Name                          Period
      LXM L128X1024MixRandom          Infinity
      LXM L128X128MixRandom         1.15792e+77
      LXM L128X256MixRandom         3.94020e+115
      LXM L32X64MixRandom           7.92282e+28
      LXM L64X1024MixRandom           Infinity
      LXM L64X128MixRandom          6.27710e+57
      LXM L64X128StarStarRandom     6.27710e+57
      LXM L64X256MixRandom          2.13599e+96
   Legacy Random                    2.81475e+14
   Legacy SecureRandom                 0.00000
   Legacy SplittableRandom          1.84467e+19
  Xoshiro Xoshiro256PlusPlus        1.15792e+77
Xoroshiro Xoroshiro128PlusPlus      3.40282e+38

The few algorithms that show Infinity for their period in the output aren’t truly infinite, of course, but they are too large to display in a double. (The RandomGeneratorFactory.period() method actually returns a BigInteger, but I displayed them as double to show the scale instead of a vast run of digits.)

If you know which algorithm you want, you can use the of method, as in the following:

RandomGenerator random = RandomGenerator.of("L128X128MixRandom");

Choosing the best algorithm


The following is excerpted verbatim from the documentation for the java.util.random package, which explains the trade-offs clearly and concisely. So there’s no reason for me to paraphrase it.

There are three groups of random number generator algorithm [sic] provided in Java: the Legacy group, the LXM group, and the Xoroshiro/Xoshiro group.

The legacy group includes random number generators that existed before JDK 17: Random, ThreadLocalRandom, SplittableRandom, and SecureRandom. Random (LCG) is the weakest of the available algorithms, and it is recommended that users migrate to newer algorithms. If an application requires a random number generator algorithm that is cryptographically secure, then it should continue to use an instance of the class SecureRandom.

The algorithms in the LXM group are similar to each other. The parameters of each algorithm can be found in the algorithm name. The number after “L” indicates the number of state bits for the LCG subgenerator, and the number after “X” indicates the number of state bits for the XBG subgenerator. “Mix” indicates that the algorithm uses an 8-operation bit-mixing function; “StarStar” indicates use of a 3-operation bit-scrambler.

The algorithms in the Xoroshiro/Xoshiro group are more traditional algorithms (see David Blackman and Sebastiano Vigna, “Scrambled Linear Pseudorandom Number Generators,” ACM Transactions on Mathematical Software, 2021); the number in the name indicates the number of state bits.

For applications (such as physical simulation, machine learning, and games) that do not require a cryptographically secure algorithm, this package provides multiple implementations of interface RandomGenerator that provide trade-offs among speed, space, period, accidental correlation, and equidistribution properties.

For applications with no special requirements, L64X128MixRandom has a good balance among speed, space, and period, and is suitable for both single-threaded and multi-threaded applications when used properly (a separate instance for each thread).

If the application uses only a single thread, then Xoroshiro128PlusPlus is even smaller and faster, and certainly has a sufficiently long period.

For an application running in a 32-bit hardware environment and using only one thread or a small number of threads, L32X64MixRandom may be a good choice.

For an application that uses many threads that are allocated in one batch at the start of the computation, either a “jumpable” generator such as Xoroshiro128PlusPlus or Xoshiro256PlusPlus may be used, or a “splittable” generator such as L64X128MixRandom or L64X256MixRandom may be used.

For an application that creates many threads dynamically, perhaps through the use of spliterators, a “splittable” generator such as L64X128MixRandom or L64X256MixRandom is recommended. If the number of generators created dynamically may be very large (millions or more), then using generators such as L128X128MixRandom or L128X256MixRandom, which use a 128-bit parameter rather than a 64-bit parameter for their LCG subgenerator, will make it much less likely that two instances use the same state cycle.

For an application that uses tuples of consecutively generated values, it may be desirable to use a generator that is k-equidistributed such that k is at least as large as the length of the tuples being generated. The generator L64X256MixRandom is provably 4-equidistributed, and L64X1024MixRandom is provably 16-equidistributed.

For applications that generate large permutations, it may be best to use a generator whose period is much larger than the total number of possible permutations; otherwise it will be impossible to generate some of the intended permutations. For example, if the goal is to shuffle a deck of 52 cards, the number of possible permutations is 52! (52 factorial), which is larger than 2225 (but smaller than 2226), so it may be best to use a generator whose period is at least 2256, such as L64X256MixRandom or L64X1024MixRandom or L128X256MixRandom or L128X1024MixRandom. (It is of course also necessary to provide sufficiently many seed bits when the generator is initialized, or else it will still be impossible to generate some of the intended permutations.)

There are other interesting methods in the factory class; running javap (or looking at the documentation) reveals them all.

$ javap java.util.random.RandomGeneratorFactory
Compiled from "RandomGeneratorFactory.java"
public final class RandomGeneratorFactory<T extends RandomGenerator> {
  static <T extends RandomGenerator> T of(String, Class<T>) throws IllegalArgumentException;
  static <T extends RandomGenerator> RandomGeneratorFactory<T> factoryOf(String, Class<T>) throws IllegalArgumentException;
  public static <T extends RandomGenerator> RandomGeneratorFactory<T> of(String);
  public static RandomGeneratorFactory<RandomGenerator> getDefault();
  public static java.util.stream.Stream<RandomGeneratorFactory<RandomGenerator>> all();
  public String name();
  public String group();
  public int stateBits();
  public int equidistribution();
  public java.math.BigInteger period();
  public boolean isStatistical();
  public boolean isStochastic();
  public boolean isHardware();
  public boolean isArbitrarilyJumpable();
  public boolean isJumpable();
  public boolean isLeapable();
  public boolean isSplittable();
  public boolean isStreamable();
  public boolean isDeprecated();
  public T create();
  public T create(long);
  public T create(byte[]);
}
$

The informational methods (such as isHardware() and isJumpable()) are in the RandomGeneratorFactory but not in the RandomGenerator instances themselves. The full list of informational methods is shown in Table 1.

Table 1. Informational methods in RandomGeneratorFactory

Name Meaning 
isStatistical() Returns true if the generated values are computed using an algorithm that is statistically deterministic.
isStochastic()  Returns true if the generated values are computed using some external or entropic input(s).
isHardware()  Returns true if the random number generator (RNG) is implemented as a hardware random number generator (HRNG). 
isJumpable()  Returns true if the RNG can jump ahead in the cycle of random data. 
isArbitrarilyJumpable()  Returns true if the RNG can jump arbitrarily far into the cycle of data. 
isLeapable()  Returns true if the RNG can jump to a very distant point in the cycle. 
isSplittable()  Returns true if the RNG can be cloned to make a second generator with the same properties but positioned further in the cycle. 
isStreamable();  Returns true if the RNG can output into a Java stream. 
isDeprecated()  Returns true if the implementation of RandomGenerator has been marked for deprecation. 

Suppose you wanted to determine which RandomGenerator instances are streamable, that is, which can emit random values as a stream.

// From ShowStreamableGenerators
RandomGeneratorFactory.all()
    .filter(rgFactory -> rgFactory.isStreamable())
    .map(rgFactory-> rgFactory.name())
    .sorted()
    .forEach(System.out::println);

As you might expect, all the new PRNGs are streamable, and all the legacy ones are not.

Testing randomness


Two of the main test suites for RNGs are TestU01 and PractRand.

TestU01, by Pierre L’Ecuyer and Richard Simard at the University of Montreal, includes a 200-page user manual that is highly recommended for those who are mathematically oriented; it’s useful even if you don’t plan to use the test suite. The authors note the following, in passing:

There is no universal test or battery of tests that can guarantee, when passed, that a given generator is fully reliable for all kinds of simulations. Passing many tests improves one’s confidence in the RNG, although it never proves that the RNG is foolproof. In fact, no RNG can pass every conceivable statistical test. One could say that a bad RNG is one that fails simple tests, and a good RNG is one that fails only very complicated tests that are extremely hard to find or impractical to run.

The LXM algorithms have been tested against both TestU01 and PractRand, and they passed. The work for Java 17 was done under JEP 356, Enhanced pseudo-random number generators, which contains more information on the choices made, the tests performed and passed, and so on.

Performance


If you need only a few random numbers, performance isn’t a big deal. But if you need millions or billions, speed matters. To measure the relative performance of each generator, I wrote a simple timing loop called TimeAllGenerators. I generated 10 million randoms with each generator to get measurable values.

dalai-LAPTOP-random $ java TimeAllGenerators.java | sort -k5n
Generating 10000000 randoms took 55 mSec using SplittableRandom
Generating 10000000 randoms took 56 mSec using Xoroshiro128PlusPlus
Generating 10000000 randoms took 57 mSec using L64X128MixRandom
Generating 10000000 randoms took 57 mSec using L64X128StarStarRandom
Generating 10000000 randoms took 58 mSec using L32X64MixRandom
Generating 10000000 randoms took 59 mSec using Xoshiro256PlusPlus
Generating 10000000 randoms took 63 mSec using L64X1024MixRandom
Generating 10000000 randoms took 65 mSec using L64X256MixRandom
Generating 10000000 randoms took 76 mSec using L128X128MixRandom
Generating 10000000 randoms took 77 mSec using L128X256MixRandom
Generating 10000000 randoms took 89 mSec using L128X1024MixRandom
Generating 10000000 randoms took 207 mSec using Random
Generating 10000000 randoms took 2571 mSec using SecureRandom

As you can see, the legacy implementation java.util.Random takes around four times as long as the more modern algorithms and gives less-reliable randomness. SecureRandom is 10 times slower but gives good randomness for security-based applications. If you need to generate a lot of random numbers, take this into account. If you need only a small number of randoms, any of the generators will do.

Migrating to the new APIs


Should you change to a newer API? Migrate if good random performance and reliability is important. If you are just using one or two random values, it may not be worthwhile. Otherwise, yes; you should migrate.

The first step in migrating would be to change

Random random = new Random();

to

RandomGenerator random = new Random();

Then, at least you are depending on the interface. A better option would be to change to the following:

RandomGenerator random = RandomGenerator.default();

Then, you would be using one of the newer implementations, which is chosen for you.

If you want to ask for a particular implementation, based on the performance figures I generated and on the notes on choosing a generator given previously, use the following:

RandomGenerator random = RandomGenerator.of("L64X1024MixRandom");

Finally, consider moving the code to a streams approach, since you now have a stream-capable random generator. Note that migrating iterative code to streams is a topic for another article. For that, I’ll refer you to Raoul-Gabriel Urma’s article, “Processing data with Java SE 8 streams, Part 1.”

In summary, Java 17 provides a new framework for building multiple implementations of PRNGs that provide a standard interface and whose implementations may more readily be exchanged. While the legacy implementations are not changed and still use LCGs, the new ones use the best-known LCG parameters and combine these with an XOR operation to be more random.

My final advice: For general use, if you want good randomness, use RandomGenerator.default() in new code, and consider migrating to that while maintaining legacy code. For security work, continue to use SecureRandom.

Source: oracle.com