Wednesday, May 31, 2023

Quiz yourself: How private is a Java private inner class?


Given the following interface and two classes

Quiz Yourself, Oracle Java Certified, Oracle Java Certification, Oracle Java Career, Oracle Java Skill, Oracle Java Jobs, Oracle Java Prep, Oracle Java Preparation

interface Paintable {
  void paint(String color);
}
class House implements Paintable {
  private class Floor implements Paintable {
    public void paint(String color) {
      System.out.println("Floor painted to " + color);
    }
  }
  Floor f;
  public void paint(String color) {
    System.out.println("House painted to " + color);
  }
  public House() {f = new Floor();  }
  public Floor getFloor() {return f;}
}

and a method fragment

01: var itemsToPaint = new ArrayList<Paintable>();
02: var house = new House();
03: var floor = house.getFloor();
04: itemsToPaint.add(house); 
05: itemsToPaint.add(floor);
06: itemsToPaint.stream().forEach(p -> {
07:     p.paint("PINK");
08: });

Which statement is true? Choose one.

A. To make the method compilable, you must change the code as follows:
     03: House.Floor floor = house.getFloor();
B. To make the method compilable, you must change the inner class access modifier as follows:
     public class Floor implements Paintable
C. To make the method compilable, you must change the code as follows:
     05: itemsToPaint.add((Paintable) floor);
D. The method compiles without changes.

Answer. This question investigates some subtlety in the meaning of private as it relates to Java types. The code in the question declares a private inner class, Floor, within the House class. Because the class is private, the type prevents arbitrary usage outside the House class. However, this type is also the type returned by the public method getFloor(). This raises the question of how the caller of that method (in line 03 of the method fragment) will see that returned type.

Option A suggests declaring the floor variable on line 03 with the type House.Floor, but this is the private type. You should know that in code outside the House class, this approach will not compile. From this you can quickly reject option A as incorrect.

Options B and C both suggest that you must change the code—in other words, they both assert that the code does not compile as it stands. It turns out that these options are both incorrect: The code does in fact compile as written. Therefore, option D is correct.

There are three ways the variable floor on line 03 can be declared to allow that line of code to be compiled. Each declaration has slightly different consequences.

The first way. Any reference type can be stored in a variable of type Object, and the same is true at line 03. If you do this, you would be able to execute the basic methods of Object on the reference variable floor. This isn’t very helpful, however, since the variable would not be a valid argument for the add method on line 05, which expects a Paintable argument.

The second way. You can make floor of type Paintable. If both the Paintable interface and the code in the method fragment are in the same package, the compiler will let you store the returned value of getFloor in a variable of this type. Because the compiler knows that the House.Floor type is Paintable, and that interface is accessible to the code, the compiler is happy. Doing this also allows the floor variable to be a valid argument to the add method on line 05. Clearly this is preferable to using Object for the type of the floor variable and would allow the code to work—but as noted, the change isn’t necessary, because the example code is already correct.

The third way. The compiler will also allow you to declare floor using the var pseudotype. This, of course, is what the code already does. So, what happens when you do this? The compiler ascribes the variable as a so-called nondenotable synthetic type. The effect is that the method fragment knows almost nothing about the object to which floor refers. You can’t invoke the paint method on it and, in fact, you can’t even invoke methods of Object (such as equals or toString) on it—that’s surely a surprise! However, the compiler keeps track of the fact that this object refers to an instance of House.Floor, and if you pass this object into a call for any method for which that’s a valid argument type, the receiving method invocation will be valid.

In a sense, the compiler says “OK, method fragment: I know what this object is, even though you aren’t allowed to know. If you correctly pass the object to something else, I’ll handle that, but you can’t do anything else with this.”

That means that you can call the add method on line 05 because that needs a Paintable, and the compiler knows that the House.Floor type implements Paintable. In addition, if you had an accessible method defined in House that took a House.Floor argument, you could also call that with floor as an argument. But, as noted, the method fragment can’t call any method on the floor reference.

With all that said, the bottom line is that the code works as written, and no changes are needed.

This discussion is a simplification of the full story and is an attempt to explain the practical side of the behavior. If you want all the nitty-gritty details, enumerated as facts rather than as explanations, you can read more in the four references listed in the “Dig deeper” section below.

As a side note, this question almost certainly goes beyond the depth of the exam, so consider this an interesting puzzler and insight into Java, rather than an authentic example of an exam question.

Conclusion. The correct answer is option D.

Source: oracle.com

Monday, May 29, 2023

JDK

What is Java Development Kit?

Oracle Java Development Kit, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Prep, Oracle Java Preparation, Oracle Java Preparation Exam, Oracle Java Guides, Oracle Java Learning, Oracle Java Materials

In the ever-evolving world of technology, Java has emerged as one of the most popular and widely used programming languages. With its versatility and extensive functionality, Java has found its place in a wide range of applications, from web development to mobile app development and beyond. At the core of Java lies the Java Development Kit (JDK), a powerful toolkit that enables developers to create robust and efficient Java applications. In this article, we will delve into the intricacies of the Java Development Kit, exploring its components, features, and the role it plays in the development process.

Understanding the Java Development Kit (JDK)


The Java Development Kit, often referred to as JDK, is a comprehensive software development environment specifically designed for Java programming. It provides developers with all the necessary tools, libraries, and resources required to write, compile, and debug Java code. The JDK serves as a platform for developing, testing, and deploying Java applications across various operating systems and computing environments.

Components of the Java Development Kit (JDK)


The JDK comprises several essential components that contribute to its functionality and versatility. Let's take a closer look at these components:

1. Java Compiler (javac): The Java compiler is a fundamental component of the JDK. It translates the human-readable Java source code into a machine-readable format known as bytecode. This bytecode can then be executed on any platform that has a Java Virtual Machine (JVM) installed.

2. Java Virtual Machine (JVM): The JVM is an integral part of the JDK that executes Java bytecode. It acts as a virtual computer, providing a runtime environment for Java applications. The JVM interprets the bytecode and translates it into machine code that can be executed by the underlying operating system.

3. Java Runtime Environment (JRE): The JRE is a subset of the JDK that includes the JVM and other necessary libraries and files required to run Java applications. While the JDK is primarily used for development purposes, the JRE is used for executing Java programs on end-user machines.

4. Java Development Tools: The JDK offers a wide range of development tools that streamline the software development process. These tools include the Java debugger (jdb), the Java Archive (JAR) tool for packaging Java applications, the Java documentation generator (javadoc), and many more.

5. Java Class Library: The JDK provides an extensive class library that encompasses pre-written code modules and APIs (Application Programming Interfaces). The Java Class Library simplifies the development process by offering ready-made components for various tasks, such as handling input and output, networking, database connectivity, and graphical user interface (GUI) creation.

Benefits of Using the Java Development Kit (JDK)


By harnessing the power of the Java Development Kit, developers can unlock numerous benefits and capabilities. Let's explore some of the key advantages of utilizing the JDK in Java development:

1. Platform Independence: Java is renowned for its "write once, run anywhere" mantra, and the JDK plays a pivotal role in achieving platform independence. With the help of the JVM, Java applications can run seamlessly on diverse operating systems, including Windows, macOS, Linux, and more.

2. Robustness and Security: Java is designed with a strong emphasis on reliability and security. The JDK incorporates various mechanisms, such as exception handling and memory management, to ensure robustness and prevent common programming errors. Furthermore, Java's sandbox security model adds an extra layer of protection against malicious activities.

3. Vast Ecosystem: Java has a vast and thriving ecosystem, thanks in large part to the extensive libraries, frameworks, and tools provided by the JDK. Developers can leverage this ecosystem to expedite development, access ready-made solutions, and tap into the collective knowledge and expertise of the Java community. Whether it's utilizing popular frameworks like Spring or libraries like Apache Commons, the JDK empowers developers with a wealth of resources to enhance their productivity.

4. Scalability and Performance: The Java Development Kit is engineered to deliver scalability and high-performance capabilities. Java's inherent scalability allows developers to build applications that can handle increased workloads and accommodate future growth. Additionally, the JVM's efficient memory management and just-in-time (JIT) compilation contribute to Java's ability to deliver robust performance, making it suitable for enterprise-level applications.

5. Cross-Platform Compatibility: With the JDK, developers can develop applications that seamlessly run on various platforms, making it an ideal choice for organizations that operate in heterogeneous computing environments. Java's compatibility across different operating systems ensures that applications can be deployed without the need for major modifications or rewrites.

Staying Ahead with Java Development Kit (JDK)


In today's fast-paced digital landscape, staying ahead of the competition is crucial. With the Java Development Kit (JDK), developers have a powerful ally that enables them to build cutting-edge, feature-rich applications. The JDK's comprehensive set of tools, libraries, and resources empowers developers to harness the true potential of Java and deliver high-quality solutions.

From startups to large enterprises, organizations across the globe rely on the JDK to build robust web applications, scalable enterprise systems, mobile apps, and much more. Java's versatility and the extensive capabilities of the JDK make it a preferred choice for developers seeking reliability, security, and platform independence.

By leveraging the JDK's components, such as the Java compiler, JVM, and Java Class Library, developers can streamline their development process, improve code quality, and enhance productivity. The JDK's vast ecosystem, including popular frameworks and libraries, further accelerates development and fosters innovation.

Java, backed by the Java Development Kit, has stood the test of time and continues to evolve, adapt, and thrive in an ever-changing technological landscape. Its strong community, regular updates, and dedication to backward compatibility ensure that developers can rely on Java for their long-term projects.

In conclusion, the Java Development Kit (JDK) is a vital toolkit for Java developers, offering a wealth of resources, tools, and libraries to create powerful and efficient applications. By harnessing the capabilities of the JDK, developers can unlock the true potential of Java, enabling them to stay ahead in today's competitive software development landscape.

Friday, May 26, 2023

Oracle Java: The Secret Ingredient Behind High-Performance Applications

Oracle Java, Oracle Java Certification, Oracle Java Prep, Java Tutorial and Mateirals, Java Certifications

In the world of software development, high-performance applications are the gold standard. Businesses and organizations strive to create applications that deliver exceptional speed, reliability, and scalability. Achieving such excellence requires the use of powerful tools and technologies, and one such tool that stands out from the crowd is Oracle Java.

Java has long been hailed as a versatile and robust programming language, with a rich ecosystem of libraries, frameworks, and tools. It has been the foundation for countless successful applications across various domains, including finance, e-commerce, healthcare, and more. In this article, we will explore why Oracle Java is the secret ingredient behind high-performance applications and how it can help your business stay ahead in the competitive digital landscape.

1. Performance Optimization


When it comes to building high-performance applications, performance optimization is of paramount importance. Oracle Java provides developers with a multitude of features and techniques to fine-tune the performance of their applications. From efficient memory management to advanced just-in-time (JIT) compilation, Java offers a plethora of optimization options.

The Java Virtual Machine (JVM), which serves as the runtime environment for Java applications, incorporates sophisticated algorithms and runtime optimizations. These optimizations enable Java applications to run faster and consume fewer system resources, resulting in superior performance.

2. Platform Independence


One of the key strengths of Oracle Java is its platform independence. Java programs can run on any platform that supports the JVM, including Windows, macOS, Linux, and even mobile devices. This cross-platform compatibility empowers businesses to reach a wider audience and ensures consistent performance across different environments.

By leveraging Java's write-once-run-anywhere capability, developers can focus on building feature-rich applications without worrying about the underlying operating system or hardware variations. This versatility not only saves development time and effort but also enhances the user experience by providing a seamless and consistent application interface.

3. Scalability and Concurrency


Scalability is a critical factor in high-performance applications, especially in the age of cloud computing and distributed systems. Oracle Java excels in this area by offering robust support for concurrent programming and scalability. The Java platform provides powerful libraries and frameworks, such as the Java Concurrency API and the Fork/Join framework, which facilitate the development of highly scalable and parallelizable applications.

With Java's thread management capabilities and built-in synchronization mechanisms, developers can efficiently handle concurrent tasks, maximize resource utilization, and achieve excellent scalability. This is particularly advantageous for applications dealing with large datasets, real-time processing, or high traffic volumes.

4. Security and Reliability


In today's digital landscape, security and reliability are of paramount importance. Java has a strong focus on these aspects, making it an ideal choice for building secure and reliable high-performance applications. Oracle regularly releases security updates and patches to address vulnerabilities and ensure the safety of Java applications.

Java's built-in security features, such as the Security Manager and the Java Authentication and Authorization Service (JAAS), provide a robust foundation for implementing access control, encryption, and other security measures. Additionally, Java's exception handling mechanism and strong type-checking contribute to the overall reliability of Java applications, minimizing the risk of crashes and errors.

5. Vast Ecosystem and Community Support


Oracle Java benefits from a vast and vibrant ecosystem comprising libraries, frameworks, and tools that extend its capabilities. The Java community actively contributes to this ecosystem, continuously developing new libraries and frameworks to address emerging needs and trends.

From enterprise-grade frameworks like Spring and Hibernate to powerful testing frameworks like JUnit, Java offers an extensive array of resources that simplify development and enhance productivity. The abundance of community support, online resources, and developer forums further augments the accessibility and knowledge-sharing within the Java community.

Conclusion

In conclusion, Oracle Java is the secret ingredient behind high-performance applications. Its performance optimization capabilities, platform independence, scalability and concurrency support, security and reliability features, and vast ecosystem make it a powerful tool for developers. By leveraging the strengths of Oracle Java, businesses can create applications that deliver exceptional speed, reliability, and scalability.

To stay ahead in the competitive digital landscape, it is crucial to embrace the power of Oracle Java. Whether you are developing a web application, a mobile app, or a complex enterprise system, Java provides the tools and technologies necessary to create high-performance solutions.

By harnessing the performance optimization features of Java, developers can fine-tune their applications to deliver blazing-fast speeds and efficient resource utilization. The platform independence of Java ensures that your applications can run seamlessly on various operating systems and devices, expanding your reach and user base.

Scalability and concurrency are essential for modern applications, and Java excels in these areas. With powerful libraries and frameworks, Java empowers developers to build highly scalable and parallelizable applications that can handle large datasets, real-time processing, and high traffic volumes.

The security and reliability features of Oracle Java provide peace of mind for businesses and users alike. Java's built-in security measures and regular updates ensure that your applications are protected against vulnerabilities and threats. Moreover, Java's exception handling and strong type-checking contribute to the overall reliability of your applications, minimizing crashes and errors.

Lastly, the vast ecosystem and strong community support surrounding Java are invaluable resources for developers. With a wide range of libraries, frameworks, and tools available, developers can leverage the collective knowledge and expertise of the Java community to enhance productivity and streamline development processes.

In a world where high-performance applications are in demand, Oracle Java stands as a trusted and powerful choice. By harnessing its capabilities, businesses can create applications that not only meet but exceed user expectations.

Wednesday, May 24, 2023

Quiz yourself: When is a Java object still reachable?

Quiz yourself, Java object, Oracle Java, Oracle Java Certification, Oracle Java Prep, Java Career, Java Guides, Java Learning, Java Object

The garbage collector can’t collect an object until it’s no longer available to any live thread.


Given the following code

interface Repairable {}
class Car implements Repairable {
  class Clutch implements Repairable {}
  private Clutch c;
  public Car() {
    c = new Clutch();
  }
  public Clutch getClutch() {
    return c;
  }
}

and a method fragment

01: var rl = new ArrayList<Repairable>();
02: var car = new Car();
03: var clutch = car.getClutch();
04: var engine = (Repairable) null;
05: rl.add(car);
06: rl.add(clutch);
07: car = null;
08: clutch = null;
09: rl.add(engine);
10: rl.set(2, engine);
11: rl.remove(0);
12: rl.remove(1);

After which line will the Car instance created at line 02 become eligible for garbage collection? Choose one.

A. It will become eligible after line 07.
B. It will become eligible after line 08.
C. It will become eligible after line 10.
D. It will become eligible after line 11.
E. It will become eligible after line 12.
F. It will not become eligible after line 12.

Answer. An object becomes eligible for the garbage collection process when it is no longer reachable. Section 12.6.1 of the Java Language Specification states the following about finalization:

A reachable object is any object that can be accessed in any potential continuing computation from any live thread.

For this question, it’s sufficient to consider only the single thread executing this code and the variables that are declared within it. There are four such variables: rl, car, clutch, and engine. From those variables, you should consider the transitive reachability. That is, if you have added car to the list, you can get at car through the reference rl, because you can follow rl to the list and then from the list you can access car. All such transitive reachability must be considered.

Now, look at the state of these variables at key points in the code.

By the end of line 06, the variable rl refers to the ArrayList, and that list contains references to both the Car and the Clutch objects. At that same point, the car variable also refers to the Car object. Clearly the Car object is reachable at this point.

By the end of line 10, the car and clutch variables have been nulled out—but the list still contains references to both the Car and Clutch objects, so the Car object is still reachable.

At this point, the list’s contents are the car, the clutch, and null. Note that line 09 appends engine (which contains null) to the list, placing null at index 2. Then line 10 sets the value at index 2 to the engine value: null. Line 10, therefore, has no meaningful effect on the state of things.

Line 11 removes the item at index 0, which is the car. At first sight, that might seem to be the last reference to the car, but you’ll see in a moment that it’s not. Continuing to trace the execution, at this point the list now contains the clutch and null.

Line 12 removes the item at index 1, which is the null. The list, therefore, contains nothing except the clutch.

What’s not immediately obvious is that the Clutch object itself, which is an inner class, contains a reference to its enclosing instance, which is the Car object. Because of that reference, the Car object is still reachable after line 12, which makes option F the correct answer and the other options incorrect.

This reference to the enclosing instance is usable by the programmer. If you were to add a toString method, such as the following, to the Clutch the reference, Car.this would provide access to the car to which this clutch belongs. It’s worth noting that in Java, the access control rules are such that the features of a Car object are accessible from the Clutch class even if they are private, and the features of Clutch are likewise accessible to Car—again, even if they are private.

class Clutch implements Repairable {
  @Override
  public String toString() {
    return "Clutch of car " + Car.this;
  }
}

Conclusion. The correct answer is option F.

Source: oracle.com

Monday, May 22, 2023

Quiz yourself: Java text blocks and escape sequences

Quiz Yourself, Oracle Java, Oracle Java Certification, Oracle Java Guides, Oracle Java Learning, Oracle Java Prep, Java Preparation

If a triple double quote sequence indicates the beginning and end of a text block, how do you assign a triple double quote sequence to a string?


Which of the following are valid Java text blocks? Choose three.

A. var v1 = """""";.

B. var v2 = """abc""";.

C. var v3 = """
         "
         """;

D. var v4 = """
         \ \s
         """;

E. var v5 = """
         \"
         """;

F. var v6 = """
         \"""
         """;

Answer. A Java text block starts with triple (three) double quote marks, and that character sequence must form the end of that line of source code. It’s permitted to have spaces or tabs before the actual newline character, but nothing else is permitted.

In view of this, option A is incorrect because the text block opening delimiter (""") has no newline character after it. Instead, it’s followed immediately by another triple double quote sequence.

You can see that option B is also incorrect because there are text characters (abc) on that line of code in addition to another triple double quote sequence.

Option C forms a valid text block that creates a string that contains a single double quote sequence followed by a newline character. Therefore, option C is correct.

One of the useful characteristics of Java’s text block feature is that it removes the special meaning of many characters, notably the double quote mark sequence. Because of this, it’s possible to write text far more cleanly. Consider trying to embed JavaScript or SQL code into a Java string; you’ll need double quote marks frequently, and in a regular form of string these would have to be “escaped.” However, with the text block, you can avoid escaping—unless you need three double quote marks in a row; then you’ll need to escape at least one of them.

Option D shows a single backslash followed by a space (and then another backslash followed by s).

A backslash must introduce an accepted escape sequence, and you are probably familiar with some of the most common ones: \n (line feed), \t (horizontal tab), and \\, which represents a single backslash in the resulting string. This sequence can be any one of those specified in the Java Language Specification, section 3.10.7.

However, this section expressly says the following:

It is a compile-time error if the character following a backslash in an escape sequence is not a Line Terminator or an ASCII b, s, t, n, f, r, ", ', \, 0, 1, 2, 3, 4, 5, 6, or 7.

In view of this, you can see that the backslash in option D, followed as it is by the space character, is an invalid combination; thus, option D is incorrect.

Option E is correct and demonstrates the long-standing approach for adding a double quote mark into a string. While this was the only way to achieve this prior to the text block, it’s not necessary to escape one (or two sequential) double quote marks in a text block. If you want three double quote marks in a row, you must escape at least one of them, or they will be taken as the end of the block. Notice that the string that results in option E will be identical to that of option C.

In addition, Option F is correct and shows one way to add three consecutive double quote marks inside a text block. As mentioned in the discussion about option E, you can escape the first, the second, or the third double quote mark—or, indeed, any combination of them. All these approaches are valid and identical, if there are not three unescaped double quote marks in sequence (which would terminate the block). Therefore, the following variants are also valid:

// good too
var v6 = """
         "\""
         """;

// the same as previous
var v6 = """
         \"\"\"
         """;

Conclusion. The correct answers are options C, E, and F.

Friday, May 19, 2023

What Java Version is Java 18? A Comprehensive Guide

Oracle Java Certification, Java Skills, Java Jobs, Java Learning, Java Preparation, Java Guides

As technology continues to advance at a rapid pace, it's crucial for developers and businesses to stay up to date with the latest programming languages and frameworks. Java, being one of the most popular and widely used programming languages, has seen numerous updates and versions over the years. In this comprehensive guide, we will delve into the details of Java 18 and shed light on its features, improvements, and the benefits it brings to the table.

Introduction to Java 18


Java 18 is the latest version in the Java programming language series, released by Oracle Corporation. This release builds upon the strengths of its predecessors, introducing new features, enhancements, and bug fixes to improve performance, security, and developer productivity. It provides developers with a more robust and efficient environment for creating enterprise-level applications, web services, and mobile applications.

Key Features of Java 18


1. Pattern Matching for Switch: One of the standout features of Java 18 is the enhanced pattern matching for switch statements. This allows developers to write more concise and expressive code by matching complex patterns directly in switch cases. It simplifies the codebase, improves readability, and reduces the chances of introducing bugs.

2. Records: Java 18 introduces the concept of records, which are immutable classes specifically designed to store data. Records provide a concise syntax for defining classes that only contain state and automatically generate useful methods such as constructors, accessors, and equals() and hashCode() implementations. They simplify the process of creating immutable data objects and contribute to writing clean and maintainable code.

3. Sealed Classes: With Java 18, sealed classes are introduced, allowing developers to control the inheritance hierarchy of their classes. By explicitly specifying which classes can extend or implement a sealed class, developers gain better control over their codebase, ensuring encapsulation and preventing unauthorized inheritance. This feature promotes design clarity and enhances code robustness.

4. Foreign Function & Memory API (Incubator): Java 18 introduces the Foreign Function & Memory API as an incubator module, enabling developers to efficiently and safely access native code and memory from Java. This opens up new possibilities for integration with existing native libraries, system-level programming, and low-level memory management.

5. Enhanced Packed Objects (Incubator): Another incubating feature in Java 18 is Enhanced Packed Objects (EPO). EPO provides a more memory-efficient representation for objects by eliminating padding bytes and optimizing memory usage. This can result in significant memory savings, particularly when working with large data sets or in resource-constrained environments.

Benefits of Upgrading to Java 18


Upgrading to Java 18 offers numerous benefits for developers, businesses, and end-users alike. Here are some of the key advantages:

1. Improved Performance: Java 18 brings performance optimizations, including faster startup times, reduced memory footprint, and enhanced garbage collection algorithms. These improvements lead to more responsive applications and better overall system performance.

2. Enhanced Security: Keeping up with the latest Java version ensures that your applications are protected against security vulnerabilities. Oracle regularly addresses security issues and provides patches and updates to mitigate potential risks. By upgrading to Java 18, you can leverage the latest security enhancements and protect your systems from emerging threats.

3. Language Enhancements: Java 18 introduces language enhancements, such as pattern matching and records, that enable developers to write cleaner, more concise, and expressive code. These features enhance developer productivity, simplify code maintenance, and reduce the likelihood of introducing bugs.

4. Access to New Libraries and Frameworks: With each new Java version, a multitude of libraries and frameworks are updated or released, offering developers access to cutting-edge tools and resources.

5. Compatibility and Support: Upgrading to Java 18 ensures compatibility with the latest ecosystem of tools, libraries, and frameworks. It allows you to leverage the advancements and updates made by the Java community, ensuring that your applications can integrate seamlessly with other components of your technology stack.

6. Bug Fixes and Performance Enhancements: Java 18 incorporates bug fixes and performance enhancements from previous versions, addressing known issues and providing a more stable and efficient environment for your applications. These improvements contribute to smoother execution, reduced downtime, and enhanced user experience.

7. Long-Term Support (LTS): Java 18 is an LTS release, which means it receives long-term support from Oracle. LTS releases are recommended for production environments as they provide extended support, including bug fixes and security patches, for an extended period. This ensures stability, reliability, and peace of mind for businesses relying on Java for their critical systems.

Migrating to Java 18


Migrating your existing Java applications to Java 18 requires careful planning and consideration. Here are some steps to help you navigate the migration process:

1. Evaluate Compatibility: Assess the compatibility of your current codebase and dependencies with Java 18. Identify any potential issues or conflicts that may arise during the migration process. Ensure that your libraries, frameworks, and third-party components are compatible with Java 18 or have updated versions available.

2. Test and Refactor: Create a comprehensive test suite to validate the behavior and performance of your application after the migration. Refactor your codebase to leverage new language features, remove deprecated APIs, and optimize performance where possible. Conduct thorough testing to ensure the stability and correctness of your application.

3. Update Build and Deployment Tools: Update your build and deployment tools to support Java 18. This includes updating your build scripts, configuring your continuous integration/continuous deployment (CI/CD) pipelines, and ensuring compatibility with your chosen development environment or IDE.

4. Gradual Rollout: Consider a gradual rollout strategy, starting with non-production environments and gradually migrating to production. This approach allows you to identify and resolve any issues in a controlled manner, minimizing the impact on your users and ensuring a smooth transition.

5. Monitoring and Performance Tuning: Monitor the performance of your application after the migration. Identify any performance bottlenecks or areas for optimization. Fine-tune your application to leverage the performance improvements introduced in Java 18, ensuring optimal resource utilization and responsiveness.

Conclusion

Java 18 brings a host of new features, enhancements, and performance improvements that can significantly benefit developers and businesses. By upgrading to Java 18, you can take advantage of the latest language capabilities, improve application performance, enhance security, and ensure compatibility with the evolving Java ecosystem.

Migrating to Java 18 requires careful planning, testing, and refactoring. However, the benefits of upgrading far outweigh the challenges, as it provides a solid foundation for developing robust, secure, and high-performing applications.

Stay ahead of the curve by embracing Java 18 and unlocking its potential for your software development projects.

Wednesday, May 17, 2023

Quiz yourself: Crossing Java’s CyclicBarrier in a multithreaded environment

Oracle Java Certification, Core Java, Oracle Java Learning, Java Prep, Oracle Java Learning, Oracle Java Quiz


Given the following class

public class Escape {
  static class Task implements Runnable {
    int i;
    CyclicBarrier cb;
    public Task(int i, CyclicBarrier cb) {this.i=i; this.cb=cb;}
    @Override
    public void run() {
      try {
        cb.await();
      } catch (Exception e) {}
      System.out.printf("Task %d executed%n", i);
    }
  }
  public static void main(String[] args) {
    final ExecutorService es = Executors.newFixedThreadPool(2);
    final CyclicBarrier cb = new CyclicBarrier(3);
    IntStream
      .rangeClosed(1, 10)
      .mapToObj(i -> new Task(i, cb))
      .forEach(t -> es.execute(t));
    es.shutdown();
  }
}

What is the result? Choose one.

A. Nothing is printed.
B. Only tasks 1 and 2 execute in arbitrary order.
C. Only tasks 1, 2, and 3 execute in arbitrary order.
D. Only tasks 1 through 9 execute in arbitrary order.
E. All tasks execute in arbitrary order.

Answer. As often happens with exam questions, the complexity of this code disguises the relative simplicity of the solution. Spotting the thing that really matters to solve the question quickly is really a manifestation of debugging skill. As such, it is not a trick question, but it can certainly be a little frustrating if you miss the key point. Never spend too long on one question until you’ve answered all the others—but we’ve made that point before.

This code uses the rangeClosed method of the IntStream class. This method produces a series of monotonically increasing int values starting with the first argument value and ending with the second argument value; that means 1 through 10 in this case. (If instead the range method had been used, the second argument would behave as a fence value; that is, the range produced would stop short of that second argument’s value.)

These 10 numbers are used to build tasks described by the Task class. The tasks all share a single CyclicBarrier, and each task is passed to the executor service for execution.

In the question, the code uses a thread pool that has two threads and a CyclicBarrier initialized with an argument value of 3.

The behavior of the CyclicBarrier may be thought of as a door with a handle. In this example, the door handle must be turned three times to open the door (that’s due to the argument of 3 in the constructor of the CyclicBarrier). The await method turns the door handle when it’s called. If a call is not the third call to await, the calling thread stops executing until other threads call await again, and at the third call the door opens. When the door opens, those three threads continue executing—they pass through the door, to continue this analogy—and the door closes behind them. Further threads calling await will again be made to wait, until another three calls to await have been made. This process continues; that’s why the CyclicBarrier is cyclic.

In this quiz’s code, there are exactly two threads in the pool. This means that only two tasks can be in process at any one instant. Note that tasks that are blocked in methods such as await are still in process. Therefore, the code has asked the pool to execute 10 tasks, but it gave the pool only two threads, so only two tasks can be in process at one time. As soon as the first two tasks have called await, their execution is on hold. The tasks are in process but waiting for the door to open. Because the pool only has two threads, no threads are available to execute a third task, and the door handle can never be turned for the crucial third time. The system simply stops right there, never to make any more progress. Also note that at this point, no output has been generated.

Next, consider the shutdown() method. This waits until all the in-process tasks are completed, but the two tasks that are waiting for that door to open will never continue executing, and they will never be complete. This also means that the other eight tasks never even start. Because of this, the program never ends, and no output is ever printed. This makes option A correct and options B, C, D, and E incorrect.

To understand how this works, imagine some variant scenarios.

Suppose you created three threads in the pool with Executors.newFixedThreadPool(3). In this situation, three tasks can be in process, and they will be able to call await the necessary three times. At that point, the first three tasks will move on and print the “Task executed” message. After those first three tasks are completed, the threads from the pool will become available and will pick up the next tasks, which are tasks 4, 5, and 6. These too will then be able to execute the await method three times, print their message, and finish. Then tasks 7, 8, and 9 will proceed to completion too. Unfortunately, the 10th task will be stuck, as there will not be the necessary additional two calls to await to allow it to be completed. Notice that in this scenario, the problem is that no task exists to make the call, not that there are no threads to run the tasks. But the bottom line is that task 10 would never print its message, and the program will still never terminate.

Another possible change would be to have two threads with Executors.newFixedThreadPool(2) and require two calls to await with new CyclicBarrier(2) instead of three calls. In this case, the tasks finish in groups of two because you need to turn the door handle only twice to open the door. Consequently, all 10 tasks would be completed, each printing its message. After all 10 have been completed, the program would shut down.

One final point: When three threads have called the await method, there’s no particular expectation that they will resume executing in the same order in which they arrived at the await method. Even if they did, the operating system’s thread scheduling could arbitrarily hold one up while letting another run freely, so that their relative arrival times at the printf statement would still not be predictable. Because of this, any output from the groups of two or three threads in the two alternative scenarios must be assumed to be printed in arbitrary order.

Conclusion. The correct answer is option A.

Source: oracle.com

Monday, May 15, 2023

JVM

Reduce technical debt by valuing comments as much as code


Comments have long occupied a distinctly secondary place in the output of developers. Comments are so secondary, and so undervalued, that in today’s code they appear infrequently except as routine copyright and licensing headers at the top of files. This is a lost opportunity because good comments that are actively maintained are one of the most effective ways of reducing technical debt—that is, they lighten the workload of future programmers who will work on the code.

Because the culture of many development organizations undervalues comments, team leads (and managers) allow comments to get out of sync with the code—thereby increasing technical debt instead of reducing it.

And because undervaluing is the rule, developers learn to blame comments rather than the culture that allows them to get out of sync with the code. This negative view is articulated by Bob Martin’s assertion that every comment represents “a failure to express yourself in code” (see Figure 1). Note that in his book Clean Code, Martin makes this curious statement and then spends 15 pages enumerating the many instances when comments are highly useful.

Oracle Java, Java Career, Java Prep, Java Learning, Java Tutorial and Materials, Java Certification, Java Guides
Figure 1. Bob Martin has strong views about comments.

Why write comments?


Comments eliminate technical debt in several ways.

Comments explain the code’s purpose. A comment at the beginning of a file explaining what functionality is found in the code removes the need to read through all methods to gather an understanding of the file. If you’re looking for a specific file, being able to read a single sentence that describes its contents greatly speeds the search. For this reason, most coding standards urge the inclusion of just such a comment in each file.

The use of keywords in file-level comments can further facilitate searching through a large codebase for specific functionality.

Javadoc comments at the beginning of classes can substitute for file-level comments. If the comments describe the class’s role in the larger project, they further aid the reader. A description of why the class is needed, if it’s not obvious, helps others understand the class’s role. And these comments are an especially useful place for presenting any unusual information in the API of the class.

Comments explain the code. Many developers have the quixotic belief that if the code is clear enough, they don’t need to use comments. This is what Bob Martin is asserting in Figure 1. It is a lovely premise that does not hold—at all. The first problem is that most developers are under great time pressure and don’t have the time to make the code so utterly clear that it requires no further comment. In fact, the far more common experience programmers have is looking at code they wrote just six months earlier and thinking “I can’t believe I wrote that!” Don’t kid yourself that you can write code so clear that it requires no comments to be understood.

Another limitation of the clear-code objection is that code explains only how a thing is done, not why it is done that way, especially if there are obvious alternatives; if the why is not evident and obvious, technical debt accrues in the absence of an explanation. Note that without such explanatory comments, code can be exceedingly difficult to maintain because no one dares to touch it, which is the very definition of technical debt.

Comments highlight the potential gotchas. Google’s Java guidelines, for example, require a comment anytime a case statement in a switch falls through to the next case. Likewise, the guidelines require noting and explaining an empty exception catch block, as well as all other items that are either potentially overlooked, not conventional, or a frowned-upon but occasionally necessary step.

Comments provide landmarks for future work. The term self-admitted technical debt refers to the TODO and FIXME comments often encountered in codebases. On the surface, these look like detritus left behind by well-meaning programmers who were subsequently occupied with other tasks. However, an interesting study on well-maintained open source projects, “An empirical study on the removal of self-admitted technical debt,” found that 74% of such entries are removed within 180 days, most often by the developers who put them there. In other words, such comments often represent useful landmarks for future work, rather than detritus.

While not specifically a countermeasure to technical debt, I use a third marker, CURR, for “current.” This is a breadcrumb of sorts that tells me where I left off coding. I often include several lines of comments containing the information I might need when resuming work. I allow only one CURR in a codebase, so if I resume work elsewhere, I convert the existing CURR to a TODO.

Oracle Java, Java Career, Java Prep, Java Learning, Java Tutorial and Materials, Java Certification, Java Guides
Figure 2. An example of excellent commenting.

The Ousterhout approach to comments


The benefits of comments in the previous scenarios cannot be denied. They reduce technical debt, and well-maintained comments also make it easier for new team members to understand the codebase without spending hours spelunking through dead ends or posing hundreds of questions to existing staff.

However, a culture of valuing comments can encourage their use for far more than remediation of technical debt.

John Ousterhout’s excellent book, “A Philosophy of Software Design,” describes best practices for large codebases; it picks up where Martin leaves off and is much more oriented towards real-world situations. Ousterhout was the designer and implementer of the Tcl language; he later cofounded Electric Cloud, a company that specialized in DevOps tools for massive codebases (that is, codebases with more than 1 million lines of code).

While Ousterhout’s book is only 175 pages, it dedicates two full chapters to comments. The first chapter replies to the most common objections to writing and valuing comments; the second focuses on refining the quality of comments.

Ousterhout’s general dictum is that comments in code should convey whatever is in the programmer’s mind that cannot be reflected accurately in the code itself. This is an excellent guideline and one that directly countermands the contention that “every comment is a failure.” The real failure is omitting clarifying and contextual information a subsequent coder might need.

Comments can also help you clarify your own thinking. More than once, as I was writing comments for a piece of code, I recognized a defect in my implementation. This is much the same sensation as describing a problem to a colleague and having the solution appear as you’re explaining the problem—a common experience. I described an extension of this benefit in a hybrid approach that takes elements from Ousterhout’s book.

When you create a class, consider using the following useful steps, which are a significant expansion of the use of comments:

◉ Write the class interface comment first.
◉ Write the interface comments and signatures of the most important public methods, but leave the method bodies empty.
◉ Write comments and declarations for the most important instance variables.
◉ Fill in the bodies of the methods, adding implementation comments as you go along.
◉ As you discover the need for more methods, write the comments before the body.

According to Ousterhout’s experience, the benefits of these steps are threefold.

◉ When the code is done, it’s properly commented and the comments are entirely up to date.
◉ The comment-first approach enables you to focus on the abstractions rather than being distracted by the implementation.
◉ The comments reveal code complexity—if a method or variable requires a long, complex comment, it probably needs to be rethought and simplified.

That’s a lot of benefits!

Of the things to comment, the most important in Ousterhout’s view are abstractions (which are difficult to tease out from reading the implementation code) and an explanation of why the code exists. In sum, a developer working on your code for the first time should be able to scan the class’s comments and have a good idea of what the class does and understand the most important implementation aspects.

If this approach appeals to you—as it does to me—Ousterhout suggests that you use it until you’re accustomed to writing code this way. He contends, and I agree, that doing so will convert you by delivering cleaner, clearer code that is fully commented.

The luxury of Javadoc


Java developers have an excellent tool for pursuing this deeper approach: Javadoc annotations. If you write code in more than one language, you’ll quickly come to realize how paltry the commenting options are in other languages. Most languages do not have any built-in counterpart to Javadoc. Or, as in the case of Go and a few other languages, if they have a counterpart, it’s threadbare by comparison.

Javadoc lets you convert comments into elegant, well-formatted, and usable documentation. And as you can see from the excellent docs in the JDK distribution, the Java team has made full use of the tool in a thoughtful, conscientious discipline. (By the way, the JVM itself is extensively commented.)

Comments in open source projects


A final note: If you’re contributing to an open source project, you should deeply value comments. One of the biggest obstacles prospective collaborators have when considering joining a project is the difficulty of learning the codebase—most often because it contains no comments (outside of the license header).

Good open source project leaders work hard at providing accessible documentation and well-commented code so that potential contributors can grok the code easily and quickly fix a small bug without excessive effort. Projects that pride themselves on good documentation and heavy commenting, such as Redis, are rewarded with good participation.

In one project I work on, Jacobin, which is a JVM written in Go, the contributors use comments extensively. Potential contributors are also provided with an additional resource: an overview of the codebase that makes it easy to understand the overall architecture of the project and how it maps to the code layout.

These steps take time, but they help the project’s contributors as much as they help the project’s intended audience by forcing Jacobin’s developers to be explicit in our assumptions, clear in our thinking, and disciplined in our work.

Conclusion


Poorly maintained comments are a clear source of technical debt. To avoid this problem, value comments as much as you do code. For example, when doing code reviews, pay particular attention not only to the comments around the new code but also to whether higher-level comments in the file are still correct in light of the new functionality.

Because the JDK bundles Javadoc, you have the unique opportunity to thoroughly benefit from disciplined appreciation of comments—thereby eliminating technical debt.

Source: oracle.com

Friday, May 12, 2023

The hidden and not-so-hidden gems in Java 20

Oralce Java, Java Exam, Java Exam Prep, Java Prep, Java Preparation, Java Tutorial and Materials, Java Certification, Java Learning


March 2023 marked the latest feature release of the Java platform, which was delivered on time through the six-month release cadence as Java 20. Like Java 19, which became generally available in September 2022, Java 20 targeted JEPs—seven, in this case—from the Panama, Amber, and Loom projects.

I am using the Java 20 jshell tool to demonstrate the code in this article. To follow along, download JDK 20, fire up your terminal, check your version, and run jshell. Note that you might see a newer version of the JDK; that’s okay.

[mtaman]:~ java -version
 openjdk 20 2023-03-21
 OpenJDK Runtime Environment (build 20+36-2344)
 OpenJDK 64-Bit Server VM (build 20+36-2344, mixed mode, sharing)

[mtaman]:~ jshell --enable-preview
|  Welcome to JShell -- Version 20
|  For an introduction type: /help intro

Jshell>

Three JEPs in Java 20 are published as incubator modules to solicit developer feedback. An incubator module’s API could be altered or disappear entirely—that is, not be released in future JDK releases. Therefore, you shouldn’t use incubator features in production code. To use the incubator modules, use the --add-modules JVM flag.

The other JEPs in Java 20 are preview features. Those features are fully specified and implemented but are provided in an early release to gather feedback. You should assume that preview features will change and not use them in production code. Use the --enable-preview switch to use such features.

That’s not to say you shouldn’t use Java 20 itself in production—you should! Beyond the JEPs, there are other small improvements and fixes. The only things you shouldn’t use in production are the incubator and preview features.

In this article, I’ll describe those seven JEPs and then dive into other changes in Java 20, including changes that didn’t rise to the level of JEPs, platform enhancements, bug fixes and changes, and deprecations and removals.

The JEPs in Java 20


Seven successful JEPs made it into the Java 20 release (one more than predicted in my previous article). Scoped values are a new incubating feature, while the remaining six JEPs are resubmissions and updates of already-known incubator and preview features that appeared in Java 19 or earlier.

These JEPs are often grouped based on their long-term Java projects, as shown in Table 1.

Table 1. Seven JEPs in the Java 20 release

JEP Project Loom, which is focused on concurrency 
429 Scoped values (first incubator)
436  Virtual threads (second preview) 
437  Structured concurrency (second incubator) 
  Project Amber, for new language features
432 Record patterns (second preview) 
433  Pattern matching for switch (fourth preview) 
  Project Panama, for non-Java libraries
434 Foreign function and memory API (second preview) 
438  Vector API (fifth incubator)

Project Loom


Project Loom is designed to deliver JVM features and APIs that support easy-to-use, high-throughput lightweight concurrency (virtual threads) and new programming models (structured concurrency). New in the project is a handy construct (scoped values) that provides a thread (and, if needed, a group of child threads) with a read-only, thread-specific value during its lifetime. Scoped values are a modern alternative to thread-local variables.

Scoped values (first incubator), JEP 429. With the advent of virtual threads, the issues with thread-local variables have worsened. Thread-local variables require more complexity than is typically required for data sharing and come at a high cost that cannot be avoided.

That’s why the Java platform is now offering, in incubator form, scoped values. (For a short time, they were called extent-local variables.) They help developers move toward lightweight sharing for thousands or millions of virtual threads. JEP 429 proposes to maintain immutable and inheritable per-thread data. These per-thread variables allow for effective data sharing between child threads.

Additionally, the lifetime of per-thread variables ought to be constrained: Once the method that initially shared the data finishes, any data shared via a per-thread variable should no longer be accessible.

Virtual threads (second preview), JEP 436. Virtual threads fundamentally redefine the interaction between the Java runtime and the underlying operating system, removing significant barriers to scalability. Still, they don’t dramatically change how you create and maintain concurrent programs. Virtual threads behave almost identically to the familiar threads, and there is barely any additional API.

Java 19 introduced virtual threads to the Java platform as a first preview. In Java 20, JEP 436 provides a second preview of virtual threads in the older JEP 425 to allow time for further feedback collection. If there is no more feedback or if no significant enhancements are made to JEP 436, virtual threads will likely be a production-ready feature in the upcoming Java 21 release.

Structured concurrency (second incubator), JEP 437. The structured concurrency API aims to make multithreaded programming easier to help simplify error management and cancellation; it treats concurrent tasks operating in distinct threads as a single unit of work—improving observability and dependability.

JEP 437 essentially republishes the Java 19 version of JEP 428—to allow time to gather more feedback. The only significant change is an updated StructuredTaskScope class that supports the inheritance of scoped values (from JEP 429) by threads created in a task scope. This streamlines the sharing of immutable data across all child threads.

Project Amber


JEP 432 and JEP 433 are connected to Project Amber, which is designed to improve developers’ productivity.

Record patterns (second preview), JEP 432. Record patterns were proposed as a preview feature in Java 19’s JEP 405. Record patterns provide an elegant way to access a record’s elements after a type check. JEP 432 is a second preview that provides further refinements. The main changes since the first preview

  • Add support for an inference of type arguments for generic record patterns
  • Add support for record patterns to appear in the header of an enhanced statement
  • Remove support for named record patterns

Pattern matching for switch (fourth preview), JEP 433. Pattern matching for switch was proposed as a preview feature several times since Java 17. JEP 433 offers a fourth preview to enable the continued coevolution of pattern matching with the record patterns preview feature in JEP 432. The following are the main changes since the third preview:

  • An exhaustive switch (that is, a switch expression or a pattern switch statement) over an enum class now throws a MatchException rather than an IncompatibleClassChangeError if no switch label applies at runtime.
  • The switch label’s grammar has been simplified.
  • Inference of type arguments for generic record patterns is now supported in switch expressions and statements, along with the other constructs that support patterns.

Project Panama


This initiative, which includes JEP 434 and JEP 438, aims to improve interoperability between the JVM and well-defined “foreign” (non-Java) APIs. These APIs often include interfaces that are used in C libraries.

Foreign function and memory API (second preview), JEP 434. This JEP defines an API through which Java programs can interoperate with code and data outside the Java runtime. By efficiently invoking foreign functions that are outside the JVM—and safely accessing foreign memory that the JVM doesn’t manage—this API enables Java programs to call native libraries and process native data without the brittleness and danger of Java Native Interface (JNI).

There have been several versions of this feature since Java 17. Initially two separate APIs were proposed, but those were integrated into a single JEP in Java 19. JEP 434 incorporates the following additional refinements based on feedback:

  • The MemorySegment and MemoryAddress abstractions are unified, so zero-length memory segments now model memory addresses.
  • The sealed MemoryLayout hierarchy facilitates usage with pattern matching in the switch expressions and statements provided by JEP 433.
  • MemorySession has been split into an arena scope and a SegmentScope to facilitate sharing segments across maintenance boundaries. An arena scope provides a bounded and deterministic lifetime; it is alive from the time when a client opens an arena until the client closes the arena.

Vector API (fifth incubator), JEP 438. This API helps you express vector computations that reliably compile at runtime to optimal vector instructions on supported CPU architectures. The goal is to achieve much faster performance than with scalar computations. Versions of this API have been incubated since Java 16. This fifth version has a small set of bug fixes and performance enhancements.

Hidden gems: The most important non-JEP changes


Java 20 shipped with hundreds of performance, stability, and security improvements beyond the seven JEPs described earlier. Here are the most significant non-JEP changes.

Warnings about type casts in compound assignments with possible lossy conversions. When I ask developers about computations that involve compound assignments, many of them give a wrong answer because they don’t know that when the right-hand operand’s type in a compound assignment is not compatible with the type of the variable, a type cast is implied. Data may be lost due to possible lossy conversion.

So, for example, what is the difference between the following code lines when they are compiled?

a = a + b;
a += b;

Many Java developers will say there is no difference. However, these two operations are not always equivalent in Java. If a is a short and b is an int, the second operation will result in the following compiler error because a + b returns an int that cannot be assigned to the short variable a without an explicit cast:

LossyConversions.java:8: error: incompatible types: possible lossy conversion from int to short
               a = a + b;
                     ^

By contrast, a += b is allowed because the compiler inserts an implicit cast in a compound assignment. The statement a += b is equivalent to a = (short) (a + b), where the left 16 bits of the int result are truncated when casting to a short, resulting in the potential loss of information. The following interface will compile without any warning; however, when you run it, you may get unexpected results:

public interface LossyConversions {

       static void main(String... args) {

              short a = 30000;
              int b   = 45000;

              a += b;
              System.out.println(a);
       }
}

[mtaman]:~ javac LossyConversions.java
[mtaman]:~ java LossyConversions
    9464

Notice that the result isn’t the expected 75,000. It’s the truncated result of conversion loss: 9,464.

To help with this issue, Java 20 introduced a compiler (javac) lint option called lossy-conversions, which generates warnings about type casts in compound assignments (such as += or *=) that could result in data loss. Warnings such as the following alert developers about potentially undesirable behavior:

[mtaman]:~ javac -Xlint:lossy-conversions LossyConversions.java
LossyConversions.java:8: warning: [lossy-conversions] implicit cast from int to short in compound assignment is possibly lossy
              a += b;
                   ^
1 warning

You can silence these warnings using the @SuppressWarnings("lossy-conversions") annotation, but you shouldn’t.

New APIs for TLS and DTLS key-exchange named groups. Java 20 added two new APIs to allow customization of the named groups of key-exchange algorithms used in Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS) connections on a per-connection basis.

javax.net.ssl.SSLParameters.getNamedGroups()
javax.net.ssl.SSLParameters.setNamedGroups()

The underlying key provider may define the default named groups for each connection. However, the named groups can be customized by setting the jdk.tls.namedGroups system property or by using the setNamedGroups() method. If the system property is not null and the setNamedGroups() method is used, the named groups that are passed will override the default named groups for the specified connection.

Note that some providers may not support these new APIs. In such cases, the provider may ignore the named groups set.

New JMOD command-line option. The jmod tool lets you create JMOD archives, a new type of archive introduced in Java 9 that provides a modular way to package Java runtime components and their dependencies. Java 20 provides the --compress option, which lets you specify the compression level to use when you create a JMOD archive.

By default, the jmod tool compresses the contents of the JMOD archive using the ZIP file format. The accepted values for the --compress option are zip-0 through zip-9, where zip-0 indicates no compression and zip-9 provides the best compression. The default value is zip-6.

Why not always choose zip-9? If you are optimizing for speed of compression or decompression, you might want to choose a lower value or even no compression.

GarbageCollectorMXBean for remark and cleanup pause time in G1. The Garbage-First (G1) garbage collector now has a new GarbageCollectorMXBean called G1 Concurrent GC. This bean reports the duration and occurrence of the remark and cleanup garbage collection pauses.

These pauses occur during a complete concurrent mark cycle, increasing the collection counter by two (one for the remark pause and one for the cleanup pause), similar to the CGC field of the jstat -gcutil command. During these pauses, G1 Concurrent GC also updates the memory pool for the G1 Old Gen MemoryManagerMXBean.

Improved control of G1 concurrent refinement threads. In Java 20, the G1 garbage collector has a new controller for concurrent refinement threads; it allocates fewer threads and delays refinement to improve the efficiency of the write barrier.

As a result, the old command-line options that were used to provide parameter values for the old controller are deprecated. Specifying any of the following options on the command line will print a warning message:

-XX:-G1UseAdaptiveConcRefinement
-XX:G1ConcRefinementGreenZone=buffer-count
-XX:G1ConcRefinementYellowZone=buffer-count
-XX:G1ConcRefinementRedZone=buffer-count
-XX:G1ConcRefinementThresholdStep=buffer-count
-XX:G1ConcRefinementServiceIntervalMillis=msec

These options will be removed entirely in the future, and their use after that time will terminate the startup of the JVM.

Preventive garbage collections disabled by default. The G1 garbage collector in Java 17 introduced preventive garbage collections, thereby avoiding costly evacuation failures caused by allocation bursts when the heap was almost full.

However, those preventive collections result in additional garbage collection work because object aging is based on the number of garbage collections. This causes premature promotion into the old generation, resulting in more data and increased garbage collection work to remove these objects. The current prediction to trigger preventive garbage collections is very conservative, which triggers garbage collections unnecessarily.

In most cases, this feature is more detrimental than beneficial. Because evacuation failures are handled more efficiently, this feature has been disabled by default in Java 20. It can still be re-enabled using the command-line options -XX:+UnlockDiagnosticVMOptions and -XX:+G1UsePreventiveGC.

Unicode 15.0 support. Java 20 has been upgraded to Unicode version 15.0, which includes several updates, such as the Unicode Character Database and Unicode Standard Annexes #9, #15, and #29. For more details, see the Unicode Consortium’s release notes.

On a similar note, Java’s java.text.BreakIterator now follows the Extended Grapheme Cluster breaks defined in the Unicode Consortium’s Standard Annex #29 for character boundary analysis. This update may result in deliberate behavioral changes because the prior implementation primarily split at code point boundaries for most characters.

Also in Java 20, the locale data has been upgraded to use the Unicode Consortium’s Common Locale Data Repository (CLDR) version 42. Some noteworthy upstream changes that may impact formatting are the following:

  • The use of at for the standard date and time format has been discontinued.
  • The nonbreaking space has been prefixed to a instead of a regular space.
  • The first-day-of-the-week information for China (CN) has been fixed.
  • Japanese now supports numbers up to 9,999京.

Time zone data updated to IANA version 2023c. Version 2023c tz incorporates modifications from the 2022b and 2022a releases, which have combined various regions with identical time stamp data post-1970 into a single time zone database. While all time zone IDs remain the same, the combined time zones refer to a shared zone database.

Hidden gems: Java 20 enhancements


Optimized intrinsic features for encryption algorithms. Java 20 provides two new intrinsic features. Remember that flags controlling intrinsics require the option -XX:+UnlockDiagnosticVMOptions.

  • Java 20 provides the Poly1305 intrinsic on x86_64 platforms with AVX512 instructions. This optimizes the Poly1305 message authentication code algorithm of the SunJCE provider on x86_64 platforms using AVX512 instructions. The Poly1305 intrinsic feature is enabled by default on supporting x86_64 platforms. However, to disable this feature, you can use the -XX:-UsePoly1305Intrinsics command-line option.
  • Java 20 provides the ChaCha20 intrinsics on x86_64 and aarch64 platforms. This provides optimized intrinsic implementations for the ChaCha20 cipher used by the SunJCE provider. These optimized routines are designed for x86_64 chipsets that support the AVX, AVX2, or AVX512 instruction sets and for aarch64 chips that support the Advanced SIMD instruction set. These intrinsics are enabled by default on supported platforms, but you can disable them using the -XX:-UseChaCha20Intrinsics command-line option.

New JDK Flight Recorder events. Java 20 provides two new events.

  • jdk.InitialSecurityProperty: This event records details of initial security properties loaded via the java.security.Security class, and it contains two fields. The first is a key, which represents the security property key, and the second is a value corresponding to the security property value. This new event is enabled by default. The system property java.security.debug=properties will print initial security properties to the standard error stream with this new event and the existing jdk.SecurityPropertyModification event (which is not enabled by default). A JDK Flight Recorder recording can monitor the initial settings of all security properties and any subsequent changes.
  • jdk.SecurityProviderService: This event records details of calls made to the method java.security.Provider.getService(String type, String algorithm); its fields include the type of service, algorithm name, and security provider. It is not enabled by default but can be activated through JDK Flight Recorder configuration files or standard options.

Javadoc improvements. There are three Javadoc improvements.

  • Javadoc can now autogenerate unique ID attributes for all HTML headings in documentation comments. These IDs can be used as link anchors for easier navigation within the generated documentation. This update is especially useful for larger documents with many sections and subsections, allowing users to jump to specific content quickly.
  • The generalized {@link}, {@linkplain}, and @see tags have been improved to enable linking to specific user-defined anchors in the Javadoc-generated documentation for an element. A double hash mark (##) separates the element name from the URI fragment to distinguish these references from member references. This enhancement provides more flexibility in navigating the documentation, allowing links to be created to specific sections or headings within a document.
  • The Javadoc documentation now includes detailed information about the JEPs related to the preview features on the Preview API page. This gives you a better understanding of the origin and purpose of preview features, as you can see by comparing Figure 1 and Figure 2.

Oralce Java, Java Exam, Java Exam Prep, Java Prep, Java Preparation, Java Tutorial and Materials, Java Certification, Java Learning
Figure 1. Java 19 documentation preview list

Oralce Java, Java Exam, Java Exam Prep, Java Prep, Java Preparation, Java Tutorial and Materials, Java Certification, Java Learning
Figure 2. Java 20 documentation preview list

New constructors for the java.security.InvalidParameterException class. Two new constructors— InvalidParameterException(String, Throwable) and InvalidParameterException(Throwable)—simplify the creation of InvalidParameterException objects with a cause.

Methods for converting to and from half-precision floating-point format. Two new methods, java.lang.Float.floatToFloat16 and java.lang.Float.float16ToFloat, can convert to and from the IEEE 754 binary16 half-precision format. However, when the JIT compiler optimizes these methods, they may return different NaN (not a number) results.

To prevent the JIT compiler from optimizing these methods, use the following command-line options:

-XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_floatToFloat16,_float16ToFloat

Hidden gems: Java 20 bug fixes and related changes


Java XSL Template limitations. Suppose you use the JDK’s XSLT processor to convert stylesheets to Java objects. If the XSL template is too large, you may encounter an exception.

com.sun.org.apache.xalan.internal.xsltc.compiler.util.InternalError: Internal XSLTC error: a method in the translet exceeds the Java Virtual Machine limitation on the length of a method of 64 kilobytes. This is usually caused by templates in a stylesheet that are very large. Try restructuring your stylesheet to use smaller templates.

To avoid this issue, you can restructure your stylesheet to smaller templates or use third-party JAR files in the classpath to override the JDK’s XSLT processor.

Changes to the java.net core libraries. The following four changes were made:

  • HTTP response input streams will throw an IOException upon interrupt. When you use the ResponseSubscribers::ofInputStream method, it returns an InputStream instance known as the HTTP response input stream. In Java 20, the default implementation of the read method in these streams has been changed.
    • Previously, if the thread performing the read operation was interrupted, the interruption was ignored. But now, if the same thread is interrupted while blocking the read operation, the request will be canceled; the input stream will be closed; the thread’s interrupt status will be set to true; and an IOException will be thrown.
  • URL constructors called with malformed input may throw a MalformedURLException in cases where it was not thrown previously. In Java 20, how input is parsed in the URL constructors has become stricter. If you call these constructors with input that is not correctly formatted, you may get a MalformedURLException exception thrown that wouldn’t have been thrown before.
    • Previously, some validation and parsing checks were done when the URL::openConnection or URLConnection::connect methods were called. Now, these checks are done earlier, within the URL constructors themselves. This means that if there’s an issue with the URL’s formatting that would have previously been delayed until the connection was opened, it may now cause a MalformedURLException to be thrown when the URL is constructed.
    • This change affects only URLs that use the JDK’s built-in stream handler implementations. URLs using third-party implementations are not affected. To return to the previous behavior, set the system property -Djdk.net.url.delayParsing=true when you run your program. Note that this property is provided for backward compatibility and may be removed in the future.
  • The default timeout value for idle connections created by java.net.http.HttpClient has been changed in Java 20. The HttpClient default keep-alive time is now 30 seconds. This means that if a connection remains idle for 30 seconds, it will be closed by the HttpClient. Previously, the default timeout value for HTTP/1.1 and HTTP/2 connections was 1,200 seconds.
  • Java 20 introduces idle connection timeouts for HTTP/2. The jdk.httpclient.keepalivetimeout property can be used to set a systemwide value in seconds to close idle connections for HTTP/1.1 and HTTP/2 when the HttpClient is used. Additionally, you can use the jdk.httpclient.keepalivetimeout.h2 property to set a timeout value exclusively for HTTP/2, regardless of whether the jdk.httpclient.keepalivetimeout property is set at runtime.

Changed behavior for java.math.BigDecimal.movePointLeft() and java.math.BigDecimal.movePointRight(). Suppose you use these two methods with an argument of zero on a negative scale target. With Java 20, the methods will return a result numerically equivalent to the target but with a different unscaled value and scale.

In earlier releases, these methods returned a result with the same unscaled value and scale, which did not align with the specification. However, this change applies only to cases where the target has a negative scale and the argument is zero; in other cases, the behavior remains the same.

Object identity used for IdentityHashMap’s remove and replace methods. IdentityHashMap is a unique type of map that considers keys to be equal only if they are identical. A comparison using the == operator returns true rather than the equals() method.

However, when the default remove(Object key, Object value) and replace(K key, V oldValue, V newValue) methods were added to the Map interface in Java 8, they were not overridden in IdentityHashMap to use == instead of equals(), leading to a bug. That has been fixed in Java 20.

FileChannel positional write unspecified in APPEND mode. The Java specification for java.nio.channels.FileChannel has been updated to provide clarity on the behavior of the FileChannel::write(ByteBuffer, long) method when an attempt to write to a specific position is performed.

When java.nio.File.StandardOpenOption.APPEND is passed to FileChannel::open when a channel is opened, the behavior of writing to a specific position is system-dependent. At the same time, the channel is opened in append mode.

This means that the outcome can differ depending on the operating system. In some operating systems, bytes will be written to the specified position. In other operating systems, the given position will be ignored, and the bytes will be appended to the end of the file.

Filenames for Unicode Normalization Format D (NFD) not normalized on macOS. The normalization of filenames for Apple’s NFD version has been changed on macOS. Previously, filenames were normalized to NFD on HFS+ prior to macOS 10.13. However, this normalization is no longer performed on the APFS file system on macOS 10.13 and later.

If you want to revert to the previous behavior and normalize filenames to NFD, set the system property jdk.nio.path.useNormalizationFormD to true.

HelloVerifyRequest messages used for DTLS resumption. In Java 20, a fix was made to the SunJSSE DTLS implementation that enables the exchange of cookies for all handshakes, including new and resumed handshakes, by default. You can disable this feature for resumed handshakes by setting the system property jdk.tls.enableDtlsResumeCookie to false. This property affects only the cookie exchange for resumed handshakes.

Improved enum switches. Java 20 has a change related to switch expressions over an enum type. When you enable preview features with --enable-preview, if the selector expression produces an unexpected enum constant value, a MatchException will be thrown instead of an IncompatibleClassChangeError.

This situation can occur only if a new enum constant is added to the enum class after the switch has been compiled.

FXML JavaScript engine disabled by default. Starting with Java 20, the JavaScript script engine for FXML is disabled by default, and an exception will be thrown when loading .fxml files that contain a JavaScript processing instruction (PI).

To enable this feature if your JDK has a JavaScript script engine, set the system property -Djavafx.allowjs=true.

JMX connections use ObjectInputFilter by default. For Java 20, the default Java Management Extensions (JMX) agent has been updated to restrict the types the server will deserialize using an ObjectInputFilter on the remote method invocation (RMI) connection. This should be fine for the normal usage of MBeans in the JDK. However, if applications have registered their MBeans in the platform’s MBeanServer, they may need to extend the filter to include any additional types their MBeans accept as parameters.

The filter pattern is set in the JDK/conf/management/management.properties file using the property com.sun.management.jmxremote.serial.filter.pattern. The default filter covers any type that OpenMBeans and MXBeans might use.

The serialization filtering and the filter pattern format are described in detail in the Java Core Libraries Developer Guide. If any additional Java types need to be passed, the default filter can be overridden by using the -Dcom.sun.management.jmxremote.serial.filter.pattern= option.

Hidden gems: Java 20 deprecation and removals


Methods changed to throw an UnsupportedOperationException. Java 20 removes the ability to suspend or resume a thread using the Thread.suspend() and Thread.resume() methods. Those methods were prone to deadlock and have been deprecated since JDK 1.2. They have been changed to throw an UnsupportedOperationException.

Similarly, the ability to stop a thread using the Thread.stop() method has been removed due to its unsafe nature of causing a java.lang.ThreadDeath exception.

java.net.URL constructors deprecated. As of Java 20, you should use java.net.URI for parsing or constructing URLs. Suppose an instance of java.net.URL is still needed to open a connection. In that case, java.net.URI can construct or parse the URL string by calling URI::create() and then calling URI::toURL() to create the URL instance.

For example, here’s the old code.

URL url = new URL("https://blogs.oracle.com/authors/mohamed-taman");

And here’s the new code.

URL url = URI.create("https://blogs.oracle.com/authors/mohamed-taman").toURL();

When a custom stream handler is required to construct a URL, use the new URL::of(URI, URLStreamHandler) methods.

JMX management applets deprecated for removal. The JMX management applet (m-let) feature is no longer useful for modern applications and will be removed in a future release. The following public classes are being deprecated and will no longer be supported: MLet, MLetContent, PrivateMLet, and MLetMBean.

This deprecation will not affect the JMX agent used for monitoring the built-in instrumentation of the JVM or any tooling that relies on JMX.

Thread text removed from Subject.current. In Java 20, the Subject.current specification has been changed. Previously, the Subject was expected to be inherited when a new thread was created. However, this expectation has been dropped, and the Subject is now stored in the AccessControlContext. The AccessControlContext is inherited when platform threads are created but not for virtual threads, because they do not capture the caller context at thread creation time.

DTLS 1.0 and TLS-ECDH_* disabled. In Java 20, the DTLS 1.0 protocol has been disabled by default due to its weak security and lack of support for strong cipher suites. To achieve this, DTLS 1.0 has been added to the jdk.tls.disabledAlgorithms security property in the java.security configuration file. An attempt to use DTLS 1.0 will result in an SSLHandshakeException.

You can re-enable this protocol at your own risk by removing DTLSv1.0 from the jdk.tls.disabledAlgorithms security property.

Similarly, the TLS_ECDH_* cipher suites have been disabled by adding ECDH to the jdk.tls.disabledAlgorithms security property in the java.security configuration file. These cipher suites lack forward-secrecy and are hardly ever used in practice. While some of them were already disabled due to the use of disabled algorithms such as 3DES and RC4, the rest of them have now been disabled. Any attempts to use cipher suites starting with TLS_ECDH_ will result in an SSLHandshakeException.

You can re-enable these cipher suites at your own risk by removing ECDH from the jdk.tls.disabledAlgorithms security property.

Error thrown if the default java.security file fails to load. Previously, when the default security configuration file conf/security/java.security failed to load, the JDK would fall back to using a hardcoded default configuration. However, in Java 20, if the default configuration file fails to load, the JDK throws an InternalError instead of using the hardcoded default configuration.

This change is intended to make misconfigurations more obvious and avoid potential security issues caused by unexpected behavior.

Conclusion

There’s a lot more to a new Java release than the widely publicized JEPs. Study all these changes; even if you don’t use the preview and incubator features, there are sufficient bug fixes and other enhancements to make Java 20 worth testing and using on production systems today.

Source: oracle.com