Tuesday, December 11, 2018

Dropping Raw String Literals from JDK 12

It has been proposed that raw string literals (preview) be dropped from JDK 12 (which enters Rampdown Phase One on December 13). Brian Goetz has written a detailed description of the motivations for dropping this preview feature (JEP 326). There is also discussion on this on the Java subreddit.

In Goetz's explanation for the proposal to remove raw string literals preview functionality from JDK 12, he writes, "The Preview Feature mechanism is intended for features for which there is a high confidence that the feature is 'done', and the likelihood that significant changes would be made before making the feature permanent is low." Goetz adds, "I am no longer convinced that we've yet got to the right set of tradeoffs between complexity and expressiveness, or that we've explored enough of the design space to be confident that the current design is the best we can do. By withdrawing, we can continue to refine the design, explore more options, and aim for a preview that actually meets the requirements of the Preview Feature process (JEP 12)."

Goetz also provides a sample of the feedback items they've received regarding raw string literals preview design and implementation. He concludes the message with the statement, "Discussion on the technical details of this feature can continue to take place on the amber-* lists" (amber-dev, amber-spec-comments, amber-spec-experts, and amber-spec-observers).

It sounds like there are still plans for raw string literals to come to Java, but they'll be implemented differently than they are currently in JDK 12 Early Access Builds.

Friday, November 30, 2018

4 New JEPs Proposed for Java 12

Four JEPs have just been proposed for JDK 12 and they are briefly summarized in this post based on the JEPs' own text descriptions.

JEP 189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)

JEP 189 ["Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)"] is described as "a new garbage collection (GC) algorithm ... [that] reduces GC pause times by doing evacuation work concurrently with the running Java threads." The JEP text adds, "Shenandoah is an appropriate algorithm for applications which value responsiveness and predictable short pauses."

JEP 334: JVM Constants API

JEP 334 ["JVM Constants API"]'s "Summary" states that this JEP's goal is to "Introduce an API to model nominal descriptions of key class-file and run-time artifacts, in particular constants that are loadable from the constant pool." This JEP is also proposed for Java SE 12.

JEP 344: Abortable Mixed Collections for G1

JEP 344 ["Abortable Mixed Collections for G1"] aims to "make G1 mixed collections abortable if they might exceed the pause target."

JEP 346: Promptly Return Unused Committed Memory from G1

JEP 346 ["Promptly Return Unused Committed Memory from G1"] is another JEP related to the G1 garbage collector and it aims to "enhance the G1 garbage collector to automatically return Java heap memory to the operating system when idle."

Monday, November 26, 2018

Project Atlantis Proposed for JDK Performance Monitoring and Analysis Improvement Experimentation

The thread "Call For Discussion: New Project: Atlantis" on the OpenJDK discuss mailing list discusses JC Beyler's proposal of an OpenJDK project called "Project Atlantis" that "would provide a venue to explore and incubate monitoring and performance analysis features that could be integrated into the Hotspot JVM and the JVM Tool Interface." This proposed project would include "working, evaluating, and incubating a Thread-Sanitizer implementation for Java."

Beyler's proposal provides background for this proposed project based on work done at Google:

The Google platform team has historically worked on providing new features, including monitoring hooks due to certain monitoring features not being available in a low-overhead, turned on-by-default manner. Therefore, for Google Java users, the platform team augmented or added mechanisms to assess various metrics such as but not limited to:
  • Thread Sanitizer support for Java (see JDK-8208520) (though this one specifically is not a low-overhead one, sorry ;-))
  • Lock contention profiles to better understand where threads are spending time in locks
  • Heap dumping mechanisms to improve heap dump times for large heaps
  • Efficient thread tagging to help filter profiling
  • etc.
All of the above have added changes to the JDK in various forms and the Atlantis project proposes to investigate, in the open, how these changes could be made general enough to be pushed into the mainline, or should be dropped due to being too specific or could be done using other existing mechanisms.

That same proposal message is not much longer than the quotation above, but does add some details regarding "evaluation criteria" that would be used to determine which investigated features and improvements warranted a JEP for eventual integration into the JDK baseline.

The responses to the Project Atlantis proposal have generally been enthusiastic with a few key discussion points related to expressed concerns:

Beyler provides a summary of the thread's questions and concerns. In the concluding portion of that summary, he writes:

... this project is an attempt to have a venue to create conversations about current internal systems or non-existent ones and that we can see what "sticks" and what doesn't; where it would "stick" potentially, what it would like and then how could be best push it forward with the support of the whole community.

There are several aspects of this proposal that intrigue me. Perhaps the most immediately relevant for me would be anything that could be done to make it easier to analyze large heap dumps. Jonathan Lu has interest in similar capability and writes, "I'm especially interested in the heap dumping part. It costs a lot to create, transfer, and analyze huge heap dump files for my applications."

Based on the initial proposal and discussion, it seems to me that Project Atlantis could be of great benefit to users of OpenJDK (and likely the numerous JDKs that will be based on OpenJDK).

Friday, November 16, 2018

JDK 12's Files.mismatch Method

JDK 12 introduces a new method to the Files class. The method, Files.mismatch(Path,Path), has been introduced to JDK 12 via JDK-8202302 and is available in JDK 12 Early Access Build 20 (same early access build that supports the new {@systemProperty} Javadoc tag).

JDK-8202302 ["(fs) New Files.mismatch method for comparing files"] adds the Files.mismatch(Path,Path) method "to compare the contents of two files to determine whether there is a mismatch between them" and can be used to determine "whether two files are equal." There was talk at one time of adding a Files.isSameContent() method, but it was decided to use Files.mismatch(Path,Parh) because of its consistency "with the Arrays.mismatch and Buffer.mismatch methods."

The next code listing contains a simple Java class that demonstrates the new Files.mismatch(Path,Path) and contrasts it with Files.isSameFile(Path,Path).

package dustin.examples.jdk12.files;

import java.nio.file.Files;
import java.nio.file.Path;

import static java.lang.System.out;

/**
 * Demonstrate {@code Files.mismatch(Path,Path)} introduced with JDK 12
 * and useful for determining if two files have the same content even
 * if they're not the same files.
 */
public class FilesDemo
{
   public static void main(final String[] arguments) throws Exception
   {
      if (arguments.length < 2)
      {
         out.println("USAGE: FilesDemo <file1Name> <file2Name>");
         return;
      }

      final String file1Name = arguments[0];
      final Path file1Path = Path.of(file1Name);
      final String file2Name = arguments[1];
      final Path file2Path = Path.of(file2Name);

      out.println("\nFiles '" + file1Name + "' and '" + file2Name + "' are "
         + (Files.isSameFile(file1Path, file2Path) ? "the" : "NOT the")
         + " same.\n\n");
      out.println("\nFiles '" + file1Name + "' and '" + file2Name + "' are "
         + (Files.mismatch(file1Path, file2Path) == -1 ? "the" : "NOT the")
         + " same content.\n\n");
   }
}

When the above code is executed against various combinations of files, it provides results that are captured in the next table. Note that the Files.mismatch method does not itself return a boolean, but the code shown above adapts its response to the appropriate boolean.

Results of Code Snippet Using Files.isSameFile() and Files.mismatch()
Files Relationship Files.isSameFile(Path,Path) Files.mismatch(Path,Path)
Same File "Same" (true) "Same" (-1)
Copied File "Not Same" (false) "Same" (-1)
Different Files "Not Same" (false) "Not Same" (positive integer)
Soft-linked "Same" (true) "Same" (-1)
Hard-linked "Same" (true) "Same" (-1)
Either File Not Found FileNotFoundException FileNotFoundException

The addition of Files.mismatch(Path,Path) is another step in accomplishing JDK-6852033 ["Inputs/Outputs methods to make common I/O tasks easy to do"] and makes it easier to determine when two files that are not the same file are still "equal" or have the same content.

Thursday, November 15, 2018

JDK 12 Javadoc Tag for System Properties

JDK 12 Early Access Build 20 (2018/11/15) is available and can be used to try out the new Javadoc tag {@systemProperty}. The new {@systemProperty} Javadoc tag is discussed in the core-libs-dev mailing list message "FYI: new javadoc tag to document system properties" and was introduced in response to JDK-5076751 ["System properties documentation needed in javadocs"].

The {@systemPropery} Javadoc tag displays its contents as normal text in its generated output and makes the contents available to the Javadoc search introduced with JDK 9. This tag is intended to be used for documenting an application's system properties.

The following simple class will be used to demonstrate the new JDK 12 Javadoc tag {@systemProperty}:

package dustin.examples.jdk12.properties;

import static java.lang.System.out;

/**
 * Class with sole purpose to illustrate JDK 12's
 * support for {@literal {@systemProperty}}.
 */
public class PropertiesDemo
{
   /**
    * {@systemProperty blog.title} can be specified to
    * provide a blog title.
    */
   private final static String PROPERTY_NAME = "blog.title";

   public static void main(final String[] arguments)
   {
      final String property = System.getProperty(PROPERTY_NAME);
      out.println("Property " + PROPERTY_NAME + ": " + property);
   }
}

The above code example applies {@systemProperty} to the private attribute PROPERTY_NAME. Because the field if private, the Javadoc tool must be executed with the -private flag to have documentation generated for this field.

The next screen snapshot demonstrates the documentation generated for the simple class using the javadoc command-line tool included in JDK 12 Early Access Build 12 (which did not have support for {@systemProperty}).

The red ovals in the previous screen snapshot show that the {@systemProperty} tag is not handled properly in earlier versions of the JDK. The contents of that tag are NOT displayed and the "search" functionality does not match on the system property name.

The next screen snapshot demonstrates the documentation generated for this same class using the command-line javadoc that comes with JDK 12 Early Access Build 20.

The green ovals in the previous screen snapshot show that {@systemProperty} is better supported in Early Access Build 20 of OpenJDK JDK 12. The contents of that tag are correctly displayed in the Javadoc itself and the search capability now matches on the system property name. It is also worth noting that the text within {@systemProperty} is presented in the HTML with appearance similar to which {@code} is presented.

The addition of {@systemProperty} potentially makes it easier for developers to find relevant descriptions of system properties for an application among Javadoc-generated documentation. The aforementioned post "FYI: new javadoc tag to document system properties" discusses other Javadoc enhancements that could possibly be made to take advantage of this tag. The potential enhancements include "a 'summary page' that lists all the system properties", adding "information regarding the 'scope' of the definition", and allowing "a short description to be included in the {@systemProperty} tag" that "could be included in the search index, the A-Z index, and the summary page."

The Jonathan Gibbons FYI mailing list message that introduces {@systemProperty} also spells out its recommended usage:

Where should the tag be used? The tag should be used in the text of the defining instance of the property. This is where the characteristics of the system property are described, which may include information like: "what is the property for", "how and when is it set", "can it be modified", and so on.

The addition of {@systemProperty} to the Javadoc tool with JDK 12 Early Access Build 20 is a minor thing, but will allow developers to make documentation of important system properties more readily accessible for fellow developers.

Wednesday, November 14, 2018

Amazon Corretto: Another No-Cost JDK

In the blog post "A Tale of Two Oracle JDKs," I compared and contrasted the two JDKs provided by Oracle: Oracle OpenJDK and Oracle JDK (Java SE). There are numerous other JDK offerings available, most of which are based on OpenJDK. One of these is Amazon Corretto, which is the subject of this post.

Today's "What's New with AWS" post "Introducing Amazon Corretto (Preview)" announces Amazon Corretto as "a no-cost, multiplatform, production-ready distribution of the Open Java Development Kit (OpenJDK)." Amazon Corretto's main page describes what it has to offer: "Corretto comes with long-term support that will include performance enhancements and security fixes." This is significant because it advertises that Amazon will provide performance enhancements and security fixes to its JDK offerings past the 6 months for which Oracle has committed to making performance enhancements and security fixes to each new OpenJDK version.

The first version of Amazon Corretto is currently a preview version and is called Amazon Corretto 8 (which, of course, associates it with OpenJDK 8). The "List of Patches for Amazon Corretto 8 Preview" lists "the patches applied to OpenJDK for the Amazon Corretto 8 Preview." These currently include patches related to OpenJDK issues (including backports from OpenJDK 11) such as JDK-8187123 ["(reflect) Class#getCanonicalName and Class#getSimpleName is a part of performance issue"] and JDK-8213198 ["Not triggering mixed GCs in G1 leaves string table cleanup deferred"].

The Amazon Corretto FAQs presents the question, "What is included in Corretto's long-term support?" The answers to this question communicate that Amazon plans to "provide security updates for Corretto 8 until at least June 2023" and to "support Corretto 11 with quarterly updates until at least August 2024." That same answer tells us that plans are to release Amazon Corretto 11 in the first half of 2019. Current updates for both Amazon Corretto 8 and for Amazon Corretto 11 are planned to be made quarterly until at least their advertised ending month of support (June 2023 for Corretto 8 and August 2024 for Corretto 11).

There has been a lot of miscommunication recently about the required cost of purchasing Java in the future. Amazon Corretto is another supporte JDK distribution that will be free of cost. The Amazon Corretto FAQs feature the question, "Is there any cost associated with using Corretto?" The answers associated with that question are:

Corretto is distributed by Amazon under an Open Source license at no cost to you. It is licensed under the terms of the GNU Public License version 2 with the Class Path Exception (GPLv2 with CPE). Amazon does not charge for its use or distribution.

I highly recommend the Amazon Corretto FAQs to anyone potentially interested in adopting Amazon Corretto. In addition to providing answers about likely questions related to long-term support, licenses, and costs, these FAQs also explain the types of patches Amazon intends to make to OpenJDK distributions, explain the degree of drop-in replaceability of other OpenJDK-based distributions by Amazon Corretto, list the operating systems supported for running Amazon Corretto, and explain how Amazon Corretto can be used with or without use of Amazon Web Services (AWS).

Conclusion

These has been continuing confusion surrounding whether Java is still free or not. Amazon Corretto is another example of an OpenJDK-based JDK binary available free of cost with long-term support advertised.

Additional Resources

Tuesday, October 30, 2018

Java's Future-Looking Projects: Panama, Loom, Amber, and Valhalla

The press release "Oracle Code One Java Keynote Outlines the Future of Java" describes the Java Keynote at Oracle Code "highlight[ing] future projects" Project Valhalla, Project Panama, Project Amber, and Project Loom. This post provides brief summaries of each of these projects and some recent work associated with each of these projects for those who may not be familiar with the mentioned projects.

Project Panama

Project Panama is about "Interconnecting JVM and native code." Its introduction states, "We are improving and enriching the connections between the Java TM virtual machine and well-defined but 'foreign' (non-Java) APIs, including many interfaces commonly used by C programmers."

Build 0 (2018/10/24) of the Project Panama Early-Access Builds was released recently. As with other OpenJDK-related early access builds, this build is "intended for expert users" and "is provided as a convenience so that they don't need to build from the source code." One of its most significant issues to be aware of is, "Windows support not available."

Project Loom

The Project Loom page describes Loom: "The goal of this Project is to explore and incubate Java VM features and APIs built on top of them for the implementation of lightweight user-mode threads (fibers), delimited continuations (of some form), and related features, such as explicit tail-call."

The Project Loom proposal (titled "Project Loom: Fibers and Continuations for the Java Virtual Machine") begins, "Project Loom's mission is to make it easier to write, debug, profile and maintain concurrent applications meeting today's requirements."

The message "Kick off meeting for Project Loom" on the OpenJDK loom-dev mailing list states that "Project Loom has been up and running here for number of months doing some early exploration and prototyping of both delimited continuations and fibers." That message talks about expanding that effort with kick-off meeting and provides links to the aforementioned Project Loom Proposal and to the YouTube-hosted presentation "Project Loom with Ron Pressler and Alan Bateman."

Project Amber

The Project Amber page describes its goal "to explore and incubate smaller, productivity-oriented Java language features that have been accepted as candidate JEPs under the OpenJDK JEP process." There are some exciting features being explored via this project including Pattern Matching (JEP 305), Switch Expressions (JEP 325), Raw String Literals (JEP 326), Concise Method Bodies (draft JEP), and faster String.format and Objects.hash methods [JDK-8205637 ("Optimized invocation of String::format and Objects::hash")].

The list of JEPs just mentioned are evidence of the significant work underway on Amber.

Project Valhalla

All of these projects propose exciting additions to Java, but the one I'm most excited about is Project Valhalla and its Value Objects/Types (JEP 169). The Project Valhalla page describes this project's goal "to provide a venue to explore and incubate advanced Java VM and Language feature candidates."

Project Valhalla has seen significant developments in recent weeks, especially since and as a result of the Valhalla Executive Group Meeting on 10 October 2018. In the OpenJDK valhalla-spec-experts mailing list message "Entering the next phase of Project Valhalla," Brian Goetz writes that this meeting was a "super-productive meeting with broad attendance" that helped Valhalla developers "to thrash out where we are, and where we're going next." Goetz also states that "this marks the beginning of the Phase III of the project" and outlines what constituted Phase 1 and Phase 2. This same packed message also summarizes the next steps for Valhalla:

Looking ahead, our next target is L2 -- which will capture the choices we've made so far, provide a useful test bed for doing library experiments, and set the stage for drilling into the remaining open questions between here and L10. L10 is our target for a first preview, which should support value types and erased generics over values.

Another recent interesting Goetz message on the valhalla-spec experts mailing list is "Values and erased generics," in which Goetz provides a "a summary of the story we came up with for erased generics over values" that "builds on the typing story outlined in John's 'Q-Types in L-World' writeup." The title of that referenced John Rose writeup is currently, "Q-Types in L-World 10: Valhalla Working Group, Burlington, September 2018." A revision of proposed JVM specification changes for LW2 was announced earlier this month as well.

The amount of work being done related to Project Valhalla is made clear in the number of messages on the mailing list about this project. These include those previously mentioned as well as others such as "var, static factory method and value type constructor"; "Value types, encapsulation, and uninitialized values"; and "Array covariance".

There is also a series of Goetz messages focusing on specific lessons learned from the first two phases:

The OpenJDK valhalla-dev mailing list message "Valhalla project repository status: Branched off 'LW1' in preparation for 'LW2'" announces the creation of the "lw1" branch.

Conclusion

It's not surprising that these four projects were mentioned at Oracle Code One's Java Keynote. They cover many of the features to look forward to in Java's future.

Sunday, October 28, 2018

IBM to Acquire Red Hat: A Java-Oriented First Look

Oracle Corporation completed the process of acquiring Sun Microsystems nearly nine years ago. That was big news then and similarly big news was announced today: IBM and Red Hat have agreed to IBM's acquisition of Red Hat. The main IBM page announces "IBM to acquire Red Hat." It then states, "This changes everything." It is likely that this announced acquisition is going to leave many Java developers wondering what kinds of changes are coming to the Java ecosystem.

Since Oracle's acquisition of Sun Microsystems, Oracle has eliminated multiple products that were formerly competitive products. For example Oracle still has its JDeveloper IDE, but has provided NetBeans (acquired from Sun) to Apache Software Foundation. Oracle has also dropped commercial support for Sun-acquired GlassFish while retaining its WebLogic Java EE application server.

I, of course, have no idea what will happen to the products and tools available to Java developers as currently provided by IBM and Red Hat. However, there are some obvious overlaps that come to mind from this $34 billion (USD) acquisition.

Item Red Hat IBM
JDK Builds/Binaries OpenJDK IBM SDK / AdoptOpenJDK
Java EE Application Server JBoss / Wildfly WebSphere / Liberty
OpenJDK Support/Contributions OpenJDK Life Cycle and Support Policy IBM Supporting the Java Community
Java Community Process (JCP)
Executive Committee (EC)
Mark Little (Scott Stark alternate) Steve Wallin (Tim Ellison alternate)
Integrated Development Environment (IDE) Eclipse-based Red Hat JBoss Developer Studio Eclipse-based IBM Rational Application Developer for WebSphere

Both IBM's version and Red Hat's version of the acquisition press release (which share much of the same wording) state, "With this acquisition, IBM will remain committed to Red Hat's open governance, open source contributions, participation in the open source community and development model, and fostering its widespread developer ecosystem." Perhaps even more encouraging, the press releases also state, "Upon closing of the acquisition, Red Hat will join IBM's Hybrid Cloud team as a distinct unit, preserving the independence and neutrality of Red Hat's open source development heritage and commitment, current product portfolio and go-to-market strategy, and unique development culture. ... IBM intends to maintain Red Hat's headquarters, facilities, brands and practices."

Both IBM and Red Hat are significant contributors to OpenJDK and to Java in general. Therefore, it should not be surprising if Java developers wonder how this acquisition will affect these two organizations' respective contributions.

At the very least, I image the question "Why choose JBoss Enterprise Middleware over IBM WebSphere?" likely won't remain on the JBoss page after this acquisition.

Wednesday, October 10, 2018

Book Review: Java by Comparison

I accepted the invitation to review Java by Comparison: Become a Java Craftsman in 70 Examples (2018, The Pragmatic Bookshelf) because the premise of this book interested me. The book Software Craftsmanship: The New Imperative had a significant effect on me when I was a much less experienced software developer and I looked forward to reviewing Java by Comparison because of its connection with the concept of software craftsmanship and its examples being provided in the Java programming language. I was provided with the electronic version of the book and I chose the PDF format for my review.

Java by Comparison is written by Simon Harrer, Jörg Lenhard, and Linus Dietz and has over 160 pages of substantive content (not counting prefaces, forewords, table of contents, etc.). Java by Comparison features nine chapters and the 70 cases covered span the first eight of those nine chapters.

Java by Comparison uses the same format for eight of its nine chapters. Each of these eight chapters are divided into several items each and at least two code samples are presented for each item. The first code sample for each item is typically functionally correct code, but the second code sample for each item illustrates how that particular item's principle can be applied to improve that code. There are three samples of these items ("extracts") available on the book's web site to see what I'm attempting to explain here.

Java by Comparison covers specific principles that fall into the areas of clean code, stylistic considerations, readability considerations, comments, naming, exception handling, unit testing, object-oriented design, and using Java's functional capabilities that were largely introduced to the language with JDK 8. Simple code examples are used in each item to contrast minimally sufficient code with improved code.

The ninth and final chapter is different from the first eight chapters. That chapter has fewer code samples and more descriptive text of general practices experienced Java developers tend to adopt in areas such as static code analysis, build automation, continuous integration, logging, and working with concurrency. This chapter is mostly higher-level and provides a taste of these topics for the newer Java developer.

I consider the following to be strengths of Java by Comparison:

  • Content
    • Java by Comparison collects information useful to newer Java developers in a single location.
    • As Java by Comparison covers various topics, it also introduces useful side notes of value to newer Java developers. Here are some examples of this:
      • There is a side note about "Multiple Returns vs. Single Return per Method" in the section on avoiding unnecessary comparisons.
      • There is a side note about "Never Use Floating-Point Arithmetic for Money" in the section on using appropriate tolerance values in unit testing.
    • Java by Comparison uses several different parts of the JDK API to illustrate its points.
    • Most of the lessons discussed in Java by Comparison are ones that I've had to learn the hard way and I found myself agreeing with most of the recommendations (and I've even blogged on some of them).
  • Format
    • The format comparing working code with superior code is effective in illustrating the principle discussed in each item.
    • The authors find a nice balance of small, simple code examples and just enough text to cover the point.
  • Finish
    • Java by Comparison is polished and has very few grammatical errors or typos. This seems to be an increasingly rare characteristic of technical books.
    • Code listings have color-coded syntax and shaded background in the PDF version of the book.
    • PDF version provides links between pages that reference each other.
    • Links to online resources work from PDF.
    • An accessible errata is available.
  • Tone
    • The text in Java by Comparison is easy to read, highly approachable, succinct, and well suited for those newer to Java.
    • Java by Comparison makes recommendations, but is not overly prescriptive and on several occasions highlights that sometimes "best" is context-sensitive and can only be determined after trying different approaches.
    • Although written by three different authors, the writing of Java by Comparison is consistent and uniform.
  • References
    • Java by Comparison provides frequent references to books and online resources for additional details regarding the concepts it covers.
    • Java by Comparison's "Bibliography" lists several of the most influential books and posts in Java and software development.

Intended Audience

I would have liked to have had access to Java by Comparison when I was still relatively new to Java. I remember reading the first edition of Effective Java and learning some from it, but Java by Comparison would have been more appropriate for my level of comfort with Java at that time.

The "Acknowledgements" section of Java by Comparison provides insight into the intended audience. The authors started building "a collection of common issues" that students in their "Advanced Java Programming" class encountered. The "Who Should Read This Book" section of the book addresses the audience more directly, "This book is for people who are learning to program in Java at a beginner or intermediate level. It's also a classroom resource for teachers who coach new developers in their journey to become programmers." The authors add, "You should read this book after you’ve learned the basic Java syntax - after you're able to write small programs with conditions and loops and you know the basics of object-oriented programming." The authors even provide a FizzBuzz-based assessment for potential readers to determine if they have the minimum recommended Java ability to make this book useful to them.

I agree with the authors' recommendations regarding who will benefit most from Java by Comparison. However, I would add that even more experienced Java developers might benefit greatly from reading Java by Comparison if they have not spent much time reading things such as Effective Java, Clean Code, Holub on Patterns, or other books or online media regarding writing Java more effectively.

My Recommendation

Many developers new to Java who have started feeling comfortable with its syntax and basic APIs often ask what is the next best book from them to read. Java by Comparison now gives me an easy answer to that question. I will recommend this book to developers new to Java as an appropriate book for them to read before moving onto Clean Code and Effective Java.

Java by Comparison is also recommended for more experienced Java developers who have concentrated mostly on getting the job done in the past and would like to do a better job at writing code that is more than sufficient and is easier for the next developer who has to work with it.

Java by Comparison applies a novel approach to presenting ideas for beginning and intermediate Java developers to learn techniques they can practice to become better Java developers and to write better code that will benefit others and themselves.

Additional References

There are three sample "excerpts" available to give a prospective reader an opportunity to determine if he or she likes the format and style of writing. There are listed below. I have also included links to others' reviews of Java by Comparison because we all have different tastes and experiences and it might be helpful for prospective readers of Java by Comparison to read what others have said about the book.

Thursday, October 4, 2018

JDK 12 News (4 October 2018)

There has been significant news related to JDK 12 in recent days.

JDK 12 Early Access Build 14

JDK 12 Early Access Build 14 (2018/10/4) was released today. The release notes indicate that the fix for JDK-8210692 ["The 'com.sun.awt.SecurityWarning' class can be dropped"] is a significant part of this build. The class com.sun.awt.SecurityWarning, deprecated for removal with JDK 11, has now been removed altogether. Other links of interest related to OpenJDK 12 Early Access Build 12 include "Changes in this build" and "Issues addressed in this build". This build also includes, of course, the preview features switch expressions (JEP 325) and raw string literals (JEP 326) that I have blogged on previously.

JEP 340 Targeted for JDK 12

Mark Reinhold announced in the post "JEP proposed to target JDK 12: 340: One AArch64 Port, Not Two" that JEP 340 ["One AArch64 Port, Not Two"] is now targeted for JDK 12.

JEP 341 Targeted for JDK 12

Reinhold also announced in "JEP proposed to target JDK 12: 341: Default CDS Archives" that JEP 341 ["Default CDS Archives"] is also targeted for JDK 12.

JDK 12 Proposed Schedule Approved

In a third consecutive message on the OpenJDK jdk-dev mailing list, Reinhold announced in the message "Proposed schedule for JDK 12" that the proposed schedule for JDK 12 has been approved. General Availability of JDK 12 is currently planned for 19 March 2019. As of this writing, there are now four JEPs targeted for JDK: JEP 325, JEP 326, JEP 340, and JEP 341.

Thursday, September 27, 2018

A Tale of Two Oracle JDKs

There has been concern recently that Java developers will inadvertently use the wrong Oracle-provided JDK implementation now (as of JDK 11) that Oracle provides builds of the open source OpenJDK and also provides commercial JDK builds based largely on the OpenJDK source.

The table below compares and contrasts the two versions of JDK that Oracle provides (but Oracle won't be the only supplier of JDK builds available for free and/or for support charge). Please keep in mind this represents my best personal understanding of the differences and similarities of Oracle's two offerings; please check with an authoritative source before making decisions regarding which Oracle JDK implementation to use (or even whether to use an Oracle implementation).

JDK Builds from Oracle (https://jdk.java.net/)
Characteristic Oracle OpenJDK Builds Oracle JDK (Java SE Downloads)
Oracle's Descriptions "End users and developers looking for free JDK versions: Oracle OpenJDK offers the same features and performance as Oracle JDK under the GPL license." "Oracle Customers and ISVs targeting Oracle LTS releases: Oracle JDK is Oracle's supported Java SE version for customers and for developing, testing, prototyping or demonstrating your Java applications."
Web Address https://jdk.java.net/11/ https://www.oracle.com/technetwork/java/javase/downloads/jdk11-downloads-5066655.html
License GNU General Public License, version 2, with the Classpath Exception Oracle Technology Network License Agreement for Oracle Java SE
Build Platforms (Binaries) Linux / x64 (tar.gz)
macOS / x64 (tar.gz)
Windows / x64 (zip)
Linux / x64 (tar.gz, deb, rpm)
macOS / x64 (tar.gz, dmg)
Windows / x64 (zip, exe)
Solaris SPARC (tar.gz)
Pay for Production Use No Yes
Oracle Support Select bug fixes and security patches until next JDK version's General Availability release Java SE Subscription
(Support for LTS versions for up to 8 years)
Several Other Paid Support Offerings under "Oracle Customers"
java -version Example openjdk version "11" 2018-09-25
OpenJDK Runtime Environment 18.9 (build 11+28)
OpenJDK 64-Bit Server VM 18.9 (build 11+28, mixed mode)
java version "11" 2018-09-25
Java(TM) SE Runtime Environment 18.9 (build 11+28)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11+28, mixed mode)
Required to Accept License Agreement No Yes
Java Flight Recorder Yes Yes
Java Mission Control Yes Yes
Advanced Management Console No Yes
This table represents my personal understanding only; refer to Oracle documentation and OpenJDK documentation for more authoritative information (see "References" below).

There are other implementations of the JDK that will be available as well, with some being free and some requiring payment. I did not discuss those alternatively provided JDKs in this post in order to keep the comparison cleaner and simpler between the "Oracle OpenJDK builds" and the "Oracle JDK builds".

References

Tuesday, September 25, 2018

JDK 11 General Availability

As scheduled, it was announced today that JDK 11 was released for General Availability. Earlier this week, Iris Clark announced the "JSR 384 (Java SE 11) Final Release" and in that same message referenced the final release version of JSR 384, referenced the "Java SE 11 (18.9) Platform JSR (384)" specification page, and concluded, "The 384 EG is now disbanded."

The "JDK 11 General-Availability Release" page provides "production-ready open-source builds of the Java Development Kit, version 11, an implementation of the Java SE 11 Platform under the GNU General Public License, version 2, with the Classpath Exception." That same JDK 11 GA Release page also points to "commercial builds of JDK 11 from Oracle under a non-open-source license" that are "available for a wider range of platforms" and which "can be found at the Oracle Help Center."

The "JDK 11 GA Release" page also provides links to the detailed JDK 11 Release Notes, to the list of features (JEPs) associated with JDK 11, to the Java SE/JDK Version 11 API Specification (Javadoc), and to the Java SE 11 Tools and Command Reference.

In the message "Java 11 / JDK 11: General Availability", Mark Reinhold writes:

JDK 11, the reference implementation of Java 11 and the first long-term support release produced under the six-month rapid-cadence release model[1][2], is now Generally Available. We've identified no P1 bugs since we promoted build 28 over four weeks ago so that’s the official GA release, ready for production use.

JDK 11 is significant for several reasons, not the least of which is its status as the basis for Oracle's LTS offering and the likelihood of this being the version of Java many shops will move to if they are currently on Java 8. An interesting read along these lines for those who do not intend to purchase commercial support for Oracle JDK implementations is "The future of Java and OpenJDK updates without Oracle support." Other recent posts that are probably worth reading in light of JDK 11's release are "Oracle JDK Releases for Java 11 and Later", "Java Is Still Free", and "Introducing Java SE 11".

Oracle's "Java SE Development Kit 11 Downloads" page highlights (in orange-ish background and with emphasized title "Important changes in Oracle JDK 11 License") the "substantially different" Oracle Technology Network License Agreement that now applies to Oracle Java SE and provides a link to "this software under the GPL License on jdk.java.net/11" (Oracle's OpenJDK builds).

Monday, September 24, 2018

Raw String Literals Support in JDK 12 Early Access Build 12

The biggest news this week in the world of Java is likely going to be the General Availability of JDK 11. However, another exciting development is the release of JDK 12 Early Access Build 12 (20 September 2018). This Early Access Build 12 of JDK 12 is significant because it includes implementations related to JEP 326 ["Raw String Literals (Preview)"] via changesets such as changeset 51713:d424675a9743 (JDK-8206981), changeset 51714:975d3636a2f9 (JDK-8200434), and changeset 51725:ccea318862ae (JDK-8210674).

JEP 326 itself displays multiple examples of how raw string literals might be applied in Java code in common situations (file paths, multi-line, regular expressions, database/SQL, and polyglot). These examples also appear in an arguably more readable format in associated JDK-8196004. These "Raw String Literals" examples can be pasted into Java classes/methods and compiled successfully against JDK 12 Early Access Build 12. For convenience, I've placed slightly adapted versions of these in a single Java class on GitHub.

Note that JEP 326 is a "Preview Feature", so you must compile with the javac options --enable-preview and --release 12 or else you'll encounter the error message, "error: raw string literals are a preview feature and are disabled by default" with a pointer to the backtick used to demarcate the raw string literal. Similarly, the code must be run with java launcher option --enable-preview to run successfully and to avoid the error message, "... UnsupportedClassVersionError: Preview features are not enabled ..."

As far as I can determine, Early Access Build 12 does not include an implementation for JDK-8202442 ["String::unescape"]. Some of the library methods added to the String class related to raw string literals are part of JDK 11 and I discussed some of these in the post "Applying New JDK 11 String Methods".

Wednesday, September 19, 2018

JDK Bug System Time Wasters

Several possibilities of the message's possible contents crossed my mind when I saw the title of Jesper Wilhelmsson's message "Introducing time wasters" on the OpenJDK jdk-dev mailing list. In the second or so between reading that link and having the message appear after clicking on the link, I wondered if the message would be about one of the following topics:

  • People wasting the time of developers working on the JDK
  • People wasting the time of developers sharing ideas and responding to questions on the mailing lists
  • Trivial defect reports or reports of observations that are intentional (not defects)

It turns out that Wilhelmsson's topic was more interesting than those. Wilhelmsson opens the message with these two sentences (I added the emphasis), "As an experiment we are introducing a new label in JBS, timewaster. The label is used to tag bugs that for some reason is wasting engineering time." That message provides additional considerations to be made when deciding whether to label a bug in JDK Bug System (JBS) with the "timewaster" label.

The "Labels" section of the JBS Overview page describes the purpose of JBS labels: "Users can associate one or more labels with an issue. Such labels are often used to manage informal processes and record ad hoc information." Although a given label does not necessarily change the priority of a bug (it communicates importance informally rather than formally), Wilhelmsson points out that the "timewaster" label communicates additional urgency, "A time waster has higher urgency than other bugs."

Jesper Wilhelmsson's post spells out some examples where this "timewasters" label might be used. Here are some bugs from the JBS that may have been or may be appropriate for such a label given the text in the bug.

It will be interesting to see how the "timewaster" experiment works out and how the criteria for determining what is a "timewaster" and what is not develops. I just wish I could label some of the defects I get assigned as time wasters.

Monday, September 17, 2018

Java Subtlety with Arrays of Primitives and Variable Arguments

An interesting question was posed in a comment on the DZone-syndicated version of my recent blog post "Arrays.hashCode() Vs. Objects.hash()". The comment's author set up examples similar to those used in my blog post and showed different results than I saw. I appreciate the comment author taking the time to post this as it brings up a subtle nuance in Java that I think is worth a blog post.

The comment author showed the following valid Java statements:

int[] arr = new int[]{1,2,3,4};
System.out.println(Arrays.hashCode(arr));
System.out.println(Objects.hash(1,2,3,4));
System.out.println(Arrays.hashCode(new Integer[]{new Integer(1),new Integer(2),new Integer(3),new Integer(4)}));
System.out.println(Objects.hash(new Integer(1),new Integer(2),new Integer(3),new Integer(4)));

The author of the comment mentioned that the results from running the code just shown were exactly the same for all four statements. This differed from my examples where the result from calling Arrays.hashCode(int[]) on an array of primitive int values was different than calling Objects.hash(Object...) on that same array of primitive int values.

One response to the original feedback comment accurately pointed out that hash codes generated on different JVMs are not guaranteed to be the same. In fact, the Javadoc comment for the Object.hashCode() method states (I added the emphasis):

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

Having stated all of this, the hash codes calculated for integers will typically be consistent from run to run. It was also interesting that the original commenter's examples' output all had exactly the same values. While I might not expect those values to match my examples' values, it is surprising that all of the examples provided by the commenter had the same answer.

The difference between the examples provided in the feedback comment and my examples comes down to how the commenter's example invoked Objects.hash(Object...) for an array of primitive int values versus how my example invoked Objects.hash(Object...) for an array of primitive int values. In my example, I passed the same local array to all the method calls. The commenter's example passed an explicit array of primitive int values to Arrays.hashCode(int[]), but passed individual int elements to Objects.hash(Object...) instead of passing the array to that latter method. When I add another example to the commenter's set of examples that does pass the array of primitive int values to the Objects.hash(Object...) method, I get a generated hash code that is different than all of the others. That enhanced code is shown next.

final int[] arr = new int[]{1,2,3,4};
out.println("Arrays.hashCode(int[]):              " + Arrays.hashCode(arr));
out.println("Objects.hash(int, int, int, int):    " + Objects.hash(1,2,3,4));
out.println("Objects.hash(int[]):                 " + Objects.hash(arr));
out.println("Objects.hashCode(Object):            " + Objects.hashCode(arr));
out.println("int[].hashCode():                    " + arr.hashCode());
out.println("Arrays.hashCode(Int, Int, Int, Int): " + Arrays.hashCode(new Integer[]{1,2,3,4}));
out.println("Objects.hash(Int, Int, Int, Int):    " + Objects.hash(1,2,3,4));

Running the adapted and enhanced version of the code provided by the commenter leads to this output (with the examples I added highlighted):

Arrays.hashCode(int[]):              955331
Objects.hash(int, int, int, int):    955331
Objects.hash(int[]):                 897913763
Objects.hashCode(Object):            897913732
int[].hashCode():                    897913732
Arrays.hashCode(Int, Int, Int, Int): 955331
Objects.hash(Int, Int, Int, Int):    955331

Comparing the output to the code that generated it quickly shows that Arrays.hashCode(int[]) generates the same hash code value as Objects.hash(Object...) when the elements of the array of int values are passed to that latter method as individual elements. However, we can also see that when the array of primitive int values is passed in its entirety (as a single array instead of as the individual elements of the array), the Objects.hash(Object...) methods generates an entirely different hash code. The other two examples that I added (that are highlighted) are to show what the "direct" hash code is on the array of primitive int values by calling .hashCode() directly on the array or by getting the equivalent result via Objects.hashCode(Object). [It's not a coincidence that the hash code generated by Objects.hash(Object...) for the array of primitive int values is exactly 31 greater than the "direct" hash code generated for the array of primitive int values.]

All of this points to the real issue here: it is typically best to not pass an array of primitives to a method that accepts variable arguments (advertises ellipsis). SonarSource Rules Explorer (Java) provides more details on this in RSPEC-3878. What is particularly relevant in that rule description is the question related to ambiguity, "Is the array supposed to be one object or a collection of objects?"

The answer to the question just posed is that when the array of primitive int values is passed to the variable arguments accepting method Objects.hash(Object...), the entire array is treated as a single Object. In contrast, when an array of reference objects (such as Integer) is passed to that same method, it sees it as the same number of objects being passed to it as elements in the array. This is demonstrated by the next code listing and associated output.

package dustin.examples.hashcodes;

import static java.lang.System.out;

/**
 * Demonstrates the difference in handling of arrays by methods that
 * accept variable arguments (ellipsis) when the arrays have primitive
 * elements and when arrays have reference object elements.
 */
public class ArraysDemos
{
   private static void printEllipsisContents(final Object ... objects)
   {
      out.println("==> Ellipsis Object... - Variable Arguments (" + objects.length + " elements): " + objects.getClass() + " - " + objects);
   }

   private static void printArrayContents(final Object[] objects)
   {
      out.println("==> Array Object[] - Variable Arguments (" + objects.length + " elements): " + objects.getClass() + " - " + objects);
   }

   private static void printArrayContents(final int[] integers)
   {
      out.println("==> Array int[] - Variable Arguments (" + integers.length + " elements): " + integers.getClass() + " - " + integers);
   }

   public static void main(final String[] arguments)
   {
      final int[] primitiveIntegers = ArraysCreator.createArrayOfInts();
      final Integer[] referenceIntegers = ArraysCreator.createArrayOfIntegers();
      out.println("\nint[]");
      printEllipsisContents(primitiveIntegers);
      printArrayContents(primitiveIntegers);
      out.println("\nInteger[]");
      printEllipsisContents(referenceIntegers);
      printArrayContents(referenceIntegers);
   }
}
int[]
==> Ellipsis Object... - Variable Arguments (1 elements): class [Ljava.lang.Object; - [Ljava.lang.Object;@2752f6e2
==> Array int[] - Variable Arguments (10 elements): class [I - [I@1cd072a9

Integer[]
==> Ellipsis Object... - Variable Arguments (10 elements): class [Ljava.lang.Integer; - [Ljava.lang.Integer;@7c75222b
==> Array Object[] - Variable Arguments (10 elements): class [Ljava.lang.Integer; - [Ljava.lang.Integer;@7c75222b

The example code and associated output just shown demonstrate that the method expecting variable arguments sees an array of primitive values passed to it as a single element array. On the other hand, the same method sees an array passed to it with reference object types as being an array with the same number of elements.

Returning to the hash code generation examples with this in mind, the different hash code generated by Objects.hash(Object...) for an array of primitive int values than that generated by Arrays.hashCode(int[]) makes sense. Similarly, we now can explain why the arrays of object references lead to the same hash code regardless of which of those methods is called.

I mentioned earlier that it's not a coincidence that the hash code generated by Objects.hash(Object) is exactly 31 higher than the "direct" hash code of the overall array. This was not surprising because the OpenJDK implementation of Objects.hash(Object...) delegates to Arrays.hashCode(Object[]), which uses 31 as the prime number it multiplies by each element in the calculated hash code. The hash code value provided by Objects.hash(Object...) for an array of primitive int values appears to be exactly what the method's implementation would lead us to expect with the above observations in mind: the direct hash value of the overall array plus the 31 prime number. When that hash code method only loops over a single element (which is the case for an array of primitives passed to a method expecting variable arguments), its calculation is essentially 31 * 1 + <directHashValueOfOverallArray>.

It's worth noting here that even though an array of reference objects had its hash code calculated to the same result as when the elements were passed to the method accepting variable arguments, it is still probably best to avoid passing an array of reference objects to such a method. The javac compiler provides this warning when this occurs: "warning: non-varargs call of varargs method with inexact argument type for last parameter" and adds these useful details about potential ways to address this: "cast to Object for a varargs call" or "cast to Object[] for a non-varargs call and to suppress this warning". Of course, with JDK 8 and later, it's fairly straightforward to process an array in various other ways before providing it to a method expecting variable arguments.

I added a final paragraph to my original post (and its DZone-syndicated version) to attempt to quickly address this, but I have used this post to express this information in greater detail. The specific lessons learned here can be summarized as "Favor the appropriate overloaded Arrays.hashCode method for an array of primitives instead of using Objects.hash(Object...)" and "Favor Arrays.hashCode(Object[]) for arrays of reference types instead of using Objects.hash(Object...)." The more general guidelines are to be wary of passing an array of primitive values to a method expecting variable arguments of type Object if the number of elements the invoked method "sees" is important in any way and to be wary of passing an array of reference objects to a method expecting variable arguments to avoid compiler warnings and the ambiguity being warned about.

Thursday, September 13, 2018

JDK 12 News (13 September 2018)

With General Availability of JDK 11 planned for later this month (25 September 2018), it's a good time to start looking more closely at JDK 12.

In a message titled "Proposed schedule for JDK 12" on the OpenJDK jdk-dev mailing list, Mark Reinhold announced, "With JDK 11 nearly out the door, here's a proposed schedule for JDK 12." The schedule for JDK 12 proposed in that message includes the "Release-Candidate Phase" starting on 31 January 2019 and "General Availability" of JDK 12 on 19 March 2019.

There are already two JEPs targeted for JDK 12 and both of them are "preview features" (my previous blog posts on these JEPs are listed as sub-bullets):

JDK 12 Early Access Build 11 was also released today. Build 11 addresses several minor issues and implements multiple minor changes.

Wednesday, September 12, 2018

Arrays.hashCode(Object[]) versus Objects.hash(Object...)

Since JDK 1.5, the Arrays class has offered overloaded static methods with the name "hashCode"​. Most of the overloaded methods accept an array of a particular primitive type, but the Arrays.hashCode(Object[]) method can be used to calculate an int hash code for an array of reference types. Since its JDK 1.7 inception, the Objects class has provided a method called hash(Object...) that also returns an int hash code for a provided array of Java objects (the ellipsis [...] representing Java varargs is handled as an array and accepts an array). This post provides a brief comparison between Arrays.hashCode(Object) and Objects.hash(Object...).

We can look at the code in OpenJDK to see how OpenJDK implements the two methods being compared here. It turns out that Arrays.hashCode(Object[]) and Objects.hash(Object...) behave exactly the same way because Objects.hash(Object...) completely delegates to Arrays.hashCode(Object[]). This is shown in the next code listing extracted from the OpenJDK Objects.java class.

public static int hash(Object... values) {
    return Arrays.hashCode(values);
}

So, it turns out that the methods are really the same when one or more individual object references are passed. So, for many cases, whichever you choose is mostly a matter of taste. It may appeal to some to use the Arrays method directly given that's what's going to be called anyway. It is typically preferable to use the Arrays method when passing it a construct that is already known to be a Java array and to use the Objects method for situations where the values are being passed in a comma-separated combination without explicit array syntax being required (such as the case of implementing a custom class's hashCode() method and passing that class's attributes of arbitrary types for the hash code computation). When using an array of primitives of the same type, it is typically better to use the appropriate version of Arrays.hashCode for that particular primitive.

The simple class shown in the next code listing (and available on GitHub) demonstrates the differences and similarities in output between the overloaded versions of Arrays.hashCode and the Objects.hash(Object...) method.

package dustin.examples.hashcodes;

import java.util.Arrays;
import java.util.Objects;

import static java.lang.System.out;

/**
 * Demonstration that writes output to standard output with
 * hash codes generated for the same underlying array data by
 * both {@code Arrays.hashCode(Object[])} and by
 * {@code Objects.hash(Object...)}.
 */
public class HashesComparedDemo
{
   public static void main(final String[] arguments)
   {
      final int[] integers = ArraysCreator.createArrayOfInts();
      out.println("Arrays.hashCode(int[]) for int[]: " + Arrays.hashCode(integers));
      out.println("Objects.hash(Object...) for int[]:   " + Objects.hash(integers));
      out.println("Objects.hashCode(Object) for int[]:  " + Objects.hashCode(integers));

      final Integer[] refIntegers = ArraysCreator.createArrayOfIntegers();
      out.println("Arrays.hashCode(Object[]) for Integer[]: " + Arrays.hashCode(refIntegers));
      out.println("Objects.hash(Object...) for Integer[]:   " + Objects.hash(refIntegers));
      out.println("Objects.hashCode(Object) for Integer[]:  " + Objects.hashCode(refIntegers));

      final String[] strings = ArraysCreator.createArrayOfStrings();
      out.println("Arrays.hashCode(Object[]) for String[]: " + Arrays.hashCode(strings));
      out.println("Objects.hash(Object...) for String[]:   " + Objects.hash(strings));
      out.println("Objects.hashCode(Object) for String[]:  " + Objects.hashCode(strings));
   }
}

The code shown above passes three common data sets (an array of primitive int values, an array of reference Integer values, and an array of String values) to the methods Arrays.hashCode, Objects.hash(Object...), and the Objects.hashCode(Object) method that accepts a single Object (of which an overall array qualifies). The simple example then writes the respective hash code values generated by each method for each data set to standard output. The results of running this code are shown next.

Arrays.hashCode(int[]) for int[]: 1722319241
Objects.hash(Object...) for int[]:   356573628
Objects.hashCode(Object) for int[]:  356573597
Arrays.hashCode(Object[]) for Integer[]: 1722319241
Objects.hash(Object...) for Integer[]:   1722319241
Objects.hashCode(Object) for Integer[]:  1735600054
Arrays.hashCode(Object[]) for String[]: 448603921
Objects.hash(Object...) for String[]:   448603921
Objects.hashCode(Object) for String[]:  21685669

As we would expect, Arrays.hashCode(Object[]) and Objects.hash(Object...) return the same calculated hash code for the reference types Integer and String because they both effectively are the implementation of Arrays.hashCode(Object[]). The array of primitive int values leads to different results from Arrays.hashCode(int[]) than from Objects.hash(Object...) and this is, of course, because the array of primitives is passed to an overloaded Arrays.hashCode(int[]) method specifically implemented for that primitive data type rather than to Arrays.hashCode(Object[]).

If one compares the implementation of Arrays.hashCode(int[]) to the implementation of Objects.hash(Object...) delegated to Arrays.hashCode(Object[]), the implementations look essentially the same in terms of logic. The reason for the different result in the case of an array of primitive int values passed to Arrays.hashCode(int[]) when compared to the result from passing that same array of primitive int values to the Objects.hash(Object...) method is that the entire array of int values is treated as a single Object rather than as an array of individual objects when passed to Objects.hash(Object...). Favor the appropriate overloaded Arrays.hashCode method for an array of primitives instead of using Objects.hash(Object...) and favor Arrays.hashCode(Object[]) for arrays of reference types instead of using Objects.hash(Object...) to avoid compiler warnings and associated ambiguity.