Friday, June 30, 2023

Quiz yourself: Java’s sealed-type mechanism and the module-info.java file


Imagine you are working on a new unit testing framework for Java classes and start with the following folder hierarchy:

src
 └───com
    └───acme
        └───test
              └───Test.java

There’s a single base Java class inside the project.

Core Java, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Preparation, Oracle Java Tutorial and Materials, Oracle Java Guides, Oracle Java Certification

package com.acme.test;

// import statement(s) here if any

abstract sealed public class Test permits StaticTest, DynamicTest {
    abstract public void test();
}

Which statements correctly describe the conditions when the code compiles successfully? Choose two.

A. If you put the StaticTest and DynamicTest classes in the com.acme.test package, you do not need to provide module-info.java.

B. If you put the StaticTest and DynamicTest classes in the com.acme.test.util package, you do not need to provide module-info.java.

C. If you provide module-info.java, you must put the StaticTest and DynamicTest classes in the same module with the Test class.

D. If you provide module-info.java, you must put the StaticTest and DynamicTest classes in the com.acme.test package.

E. If all modules have a proper module-info.java file, you may put the StaticTest and DynamicTest classes in different modules.

Answer. This question investigates some key restrictions of the sealed-type mechanism.

The sealed-type mechanism allows source code to make it very clear that only a specific set of subtypes are assignment-compatible with the sealed base type. Of course, it’s always been possible to limit the number of types that are assignment-compatible with a given type simply by not coding more classes than you want. But in that older approach, the design intent is not explicit in the code, and if others who are unfamiliar with your intentions work on the code, they might break this intention and create bugs by mistake.

Another benefit of sealed types is that if a naively made change adds a new assignment-compatible type, it causes a syntax error, alerting the programmer that the change should be discussed with others who are thoroughly familiar with the design.

A third benefit, which is expected to be realized fully when the final version of pattern matching for switch is released, is the ability to alert programmers to other areas of code that need to be updated if new assignment-compatible subtypes are added by the proper syntactic mechanism. (Specifically, the switch statement will be able to issue an error if the new type is omitted from the set of patterns mentioned in case clauses.)

In pursuit of these goals, Java 17’s JEP 409 (sealed classes) makes the following comment:

The classes specified by permits must be located near the superclass: either in the same module (if the superclass is in a named module) or in the same package (if the superclass is in the unnamed module).

Notice this enumerates two possibilities.

◉ All elements of the sealed-type hierarchy must be in the same module.
◉ If the code that’s compiled and run is not in an explicit module (that is, the code on the classpath is in the unnamed module, which is not explicit), the elements of the hierarchy must be in the same package.

It’s worth noting that the Java compiler determines whether code is intended for use in the module system by determining the presence or absence of a module-info.java file. This is somewhat approximate, since code can be compiled into a module but then be executed from the unnamed module if the JAR file containing the code is located on the classpath during execution.

From the rules listed in JEP 409, you know that it’s mandatory that all elements of the sealed hierarchy be in the same package if the code is not running in a named module. This makes option A correct.

You also know that it’s permissible to place members of the sealed hierarchy in different packages only if the code is running in a named module. Since the compiler approximates this determination based on the presence or absence of a module-info.java file, option B is incorrect.

Option C is also correct because, as mentioned above, permitted types must be as close as possible to sealed types, and the most permissive interpretation is that all the types must be in the same module. One factor here is that modules are commonly developed and compiled independently, which means it’d be much harder to keep track of the total hierarchy if this rule did not exist.

Option D is incorrect. It is not mandatory to put all the permitted types into the same package when you build a sealed-type hierarchy with a module-info.java file, which the compiler interprets as building a module (even if the code might not be run in that way).

Option E is also incorrect, because you can never spread a sealed type and its permitted types across different modules.

Conclusion. The correct answers are options A and C.

Source: oracle.com

Wednesday, June 28, 2023

Handling time zones in distributed systems, Part 1

Learn how to use Java APIs to handle time zone conversions in your applications.


Handling dates and times in applications is a common and challenging problem requiring a particular design and implementation to ensure a correct solution. The problem is compounded because applications are required to handle customers across the globe, each with a different time zone. In many cases, those users’ countries (or regions within their countries) use Daylight Saving Time (DST), which must be considered.

Many countries, even large ones such as China and India, have only a single official time zone. On the other hand, some countries have several time zones. The United States has 4 time zones in the contiguous states and 5 more in Alaska, Hawaii, and other US territories. Russia has 11 time zones, and Australia has 3 time zones. And each of these countries has one or more separate daylight-saving policies. For example, part of the US state of Arizona follows DST. However, most parts of Arizona don’t follow DST.

Such challenges affect saving and searching for data with date filters, which can affect the functionality of applications.

This article explores the issue and presents a sample application implementation, with an API, to provide an end-to-end solution for a distributed environment. The sample code works well in Java 18 or later.

Stating the problem


In this article’s example business case, your customers are spread across multiple locations, and they might also travel through different time zones while using your software. You need to anticipate that your customers will create data in different time zones and access data while in different time zones, as shown in Figure 1.

Oracle Java Certification, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Tutorial and Materials, Oracle Java Systems

Figure 1. A scenario for handling time zones in distributed systems

Imagine there’s a customer who will create data while in Egypt today but tomorrow will travel to Morocco and access the data. Meanwhile, that same data will be accessed by a colleague in Australia.

You can see that your infrastructure should spread across multiple time zones; otherwise, your users may gain access to the system from different time zones. This issue is not straightforward, as you can see in Figure 2. In this figure, one user and a server are in the US time zone called Central Time (“1” in the figure). Similarly, a user in Sydney is in the same time zone as a server there (“2” in the figure).

Oracle Java Certification, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Tutorial and Materials, Oracle Java Systems

Figure 2. Some users and servers in the same time zone

What if a server doesn’t have users in the same time zone? That’s the situation shown in “3” of Figure 3.

Oracle Java Certification, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Tutorial and Materials, Oracle Java Systems

Figure 3. A server in a time zone that doesn’t have users

Meanwhile, users in some time zones might need to access servers in different time zones, as shown in “4” and “5” of Figure 4.

Oracle Java Certification, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Tutorial and Materials, Oracle Java Systems

Figure 4. Customers accessing a server from a different time zone

Time zone configuration


How are time zones configured in users’ computers (such as desktops or mobile devices) and in servers? Usually, when you install a service or application—especially a JVM-based application, a database server, or an application server—it will get the default time zone as well as the date and time configuration from the operating system. In the case of a mobile device, the time zone may be reset periodically by the operating system when the device talks to a local network, such as when you turn your phone back on after a flight.

Because this article’s example service is running on a distributed infrastructure, you can’t rely on the server’s default time zone; you need to override the default time zone configuration and assume that customers might create data from a different time zone.

In such a situation, the general approach is to capture the date and time, in database files and in logs, in a standard time format, which is usually Coordinated Universal Time (UTC), also known as Greenwich Mean Time (GMT). Therefore, when a customer is accessing date-related attributes from a different time zone, you need to identify the user’s local time zone and convert the UTC time zone data attribute into the user’s local time zone on the fly.

Figure 5 shows that a customer from Berlin (“1” in the figure) created a data record, and his local time was 21:30. (I’m using 24-hour time to keep things simple.) The record will be saved on the server as 20:30 after converting it to UTC (“2” in the figure). The customers from Tokyo (“3” in the figure) and Sydney (“4” in the figure) will access the same record and read the record creation time as 05:30 and 07:30 local time, respectively, of the following day.

Oracle Java Certification, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Tutorial and Materials, Oracle Java Systems

Figure 5. Distributed time zone architecture

Representing date and time information in Java


Java provides several ways to represent date and time information.

The java.sql Date and Timestamp classes. Java offers two classes, Date and Timestamp, in the JDBC API to represent date and time information separately. They reside in the package java.sql. I don’t recommend using these classes because they will tie your Java Persistence API (JPA) entities’ date and time representation to JDBC-related API classes.

The util.Date class. The single class java.util.Date represents both date and time information. In JPA entities, the date and time will be differentiated with a @Temporal type annotation such as the following:

@Temporal(TemporalType.DATE)
private Date date;
@Temporal(TemporalType.TIMESTAMP)
private Date time;

I don’t recommend this approach either, because you need to use annotations to differentiate the date and time.

Java Date and Time API. With Java 8 in 2014, the java.time package was added to the platform as JSR 310, and it has been enhanced occasionally. It provides the Date and Time API, which is supported by all tools and frameworks and is considered the standard API for perfect date and time conversion.

By the way, if you are using the built-in java.util.Date and java.util.Calendar classes, you should migrate to the Data and Time API. Here’s why.

◉ For the most part, these date and calendar classes are not thread-safe.
◉ There are few practical methods to do date and time conversions.
◉ The time zone support they provide, especially for time zone conversions, is not straightforward.

The following are the advantages of using the java.time APIs:

◉ The java.time classes are immutable and thread-safe.
◉ They are ISO-centric especially with respect to the ISO 8601 date and time format, and they use consistent domain models for dates, time, durations, and periods. In addition, they have comprehensive utility methods for date and time operations.
◉ The java.time classes provide comprehensive functionality to manipulate date and time attributes among multiple time zones.
◉ Finally, java.time classes have been backported to Java 6 and Java 7.

The ZonedDateTime class. I want to call attention to the dedicated domain model in java.time, ZonedDateTime, which has a wide selection of interfaces and methods for time zone operations. In part, the documentation says the following:

public final class ZonedDateTime
extends Object
implements Temporal, ChronoZonedDateTime<LocalDate>, Serializable

A date-time with a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30+01:00 Europe/Paris.

ZonedDateTime is an immutable representation of a date-time with a time-zone. This class stores all date and time fields, to a precision of nanoseconds, and a time-zone, with a zone offset used to handle ambiguous local date-times. For example, the value “2nd October 2007 at 13:45.30.123456789 +02:00 in the Europe/Paris time-zone” can be stored in a ZonedDateTime.

This class handles conversion from the local time-line of LocalDateTime to the instant time-line of Instant. The difference between the two time-lines is the offset from UTC/Greenwich, represented by a ZoneOffset.

Converting between the two time-lines involves calculating the offset using the rules accessed from the ZoneId. Obtaining the offset for an instant is simple, as there is exactly one valid offset for each instant. By contrast, obtaining the offset for a local date-time is not straightforward. There are three cases:

◉ Normal, with one valid offset. For the vast majority of the year, the normal case applies, where there is a single valid offset for the local date-time.
◉ Gap, with zero valid offsets. This is when clocks jump forward typically due to the spring daylight savings change from “winter” to “summer”. In a gap there are local date-time values with no valid offset.
◉ Overlap, with two valid offsets. This is when clocks are set back typically due to the autumn daylight savings change from “summer” to “winter”. In an overlap there are local date-time values with two valid offsets.

External libraries


Several external libraries exist to address the deficiencies of the old java.util package classes. One is Joda-Time; however, that library’s authors recommended that you use Java 8’s java.time package.

Another alternative is Apache’s DateUtils class, which is described as a suite of utilities surrounding the use of the Calendar and Date object. Its documentation says

DateUtils contains a lot of common methods considering manipulations of Dates or Calendars. Some methods require some extra explanation. The truncate, ceiling and round methods could be considered the Math.floor(), Math.ceil() or Math.round versions for dates This way date-fields will be ignored in bottom-up order. As a complement to these methods we’ve introduced some fragment-methods. With these methods the Date-fields will be ignored in top-down order. Since a date without a year is not a valid date, you have to decide in what kind of date-field you want your result, for instance milliseconds or days.

Several methods are provided for adding to Date objects, of the form addXXX(Date date, int amount). It is important to note these methods use a Calendar internally (with default time zone and locale) and may be affected by changes to daylight saving time (DST).

One more library I’ll mention here is Apache Commons Lang3’s DateUtils class.

No matter which library you use, I urge you to follow a single approach or use a compatible library to avoid date and time inconsistencies. Think about maintenance when you’re working with multiple external code libraries. If you use external libraries extensively, evaluate each library’s current health and the future of each library, especially the number of active contributions.

I always tend to use one approach in my personal and business projects. I use Java 8’s java.time package classes as they are very fluent and easy to use; in addition, because of all the advantages mentioned above, those classes are always maintained as part of the standard JDK.

Zoned date and time conversion in action


Consider the following story: Mohamed is accepted as a speaker at Oracle’s JavaOne conference and would like to talk with Duke prior to the event.

Mohamed lives in the Europe/Belgrade time zone, also known as Central European time, and he created a calendar appointment by entering a date and time for when a phone call with Duke will occur. A meeting invitation was sent to Duke, who lives in San Francisco, California. When Duke views the invitation, it should reflect his time zone: Pacific Time.

Let’s see how this is done in code. The vital point is that the meeting will be held at the same instant worldwide; therefore, the code uses the Instant object for persistence, and without any time zone knowledge, the object’s value should be only in UTC. The rest of the story is shown in the code comments.

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class MeetingApplication {

  public static void main(String[] args) {
  
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /*
      1. Mohamed (in the "Europe/Belgrade" time zone)
     is creating a meeting slot with Duke in San Francisco.
    */

    // Mohamed's meeting date and time
    String mohamedTime = "2022-07-10 10:30:00";

    // Mohamed's time zone
    ZoneId mohamedZone = ZoneId.of("Europe/Belgrade");

    // Creating the meeting
    ZonedDateTime parsed = LocalDateTime.parse(mohamedTime, formatter)
                       .atZone(mohamedZone);

    System.out.println("1. Mohamed booked a meeting according to his time zone at: " + parsed);
    // will print: 2022-07-10T10:30+02:00[Europe/Belgrade]

    // 2. Send the calendar invite and save the event
    Instant instant = parsed.toInstant();

    // Invitation (instant) is stored in the database
    System.out.println("2. Mohamed meeting time saved in database as UTC equivalent: " + instant);
    // will print: 2022-07-10T08:30:00Z

    /*
      Duke (in the "US/San Francisco" time zone) is viewing the meeting
  DateTime Mohamed has booked to determine when exactly the meeting is.
    */

    // Initialize Duke time zone.
    ZoneId dukeZone = ZoneId.of("America/Los_Angeles");

    // The meeting time is retrieved from the database (instant) with Duke's time zone.
    ZonedDateTime dukeTime = ZonedDateTime.ofInstant(instant, dukeZone);

    System.out.println("3.1. Duke meeting will be at (formatted): " + dukeTime.format(formatter));
    // will print: 2022-07-10 01:30:00

    System.out.println("3.2. Duke meeting will be at: " + dukeTime);
    // will print: 2022-07-10T01:30-07:00[America/Los_Angeles]

    // Mohamed would like to make sure of the meeting time
    System.out.println("4. Again, Mohamed is checking the meeting time: " + ZonedDateTime
            .ofInstant(instant, mohamedZone)
            .format(formatter)); // will print: 2022-07-10 10:30:00
  }
}

This simple example gives you an idea about how to convert from one zoned date and time to another in a distributed system.

Consider that the Instant class is for computers. Date and time variant classes are for humans. The Instant class is the natural way of representing the time for computers, but it is often useless to humans. Instant is usually preferred for storage (for example, in a database), but you may need to use other classes such as ZonedDateTime when presenting data to a user. Thus

◉ The date and time are always saved in the database in the UTC format.
◉ The time zone should always be present for conversion from Instant to any date and time equivalent classes.

Source: oracle.com

Monday, June 26, 2023

JVM

The Untold Story of JVM Internals: Unveiling the Inner Workings of Java's Engine

JVM Internals, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Prep, Oracle Java Preparation, Oracle Java Tutorial and Mateirals

Introduction


Welcome to an in-depth exploration of the Java Virtual Machine (JVM), the powerhouse behind Java's exceptional performance and platform independence. In this comprehensive article, we will delve into the intricacies of JVM internals, uncovering its hidden mechanisms and shedding light on its remarkable functionality. Prepare to embark on a journey that reveals the untold story of JVM internals, exposing the inner workings of Java's engine.

Understanding the JVM


The Java Virtual Machine is an integral component of the Java platform, responsible for executing Java bytecode and ensuring cross-platform compatibility. By interpreting the bytecode into machine-specific instructions, the JVM enables Java programs to run seamlessly on various operating systems.

Class Loading and Verifying


At the heart of JVM internals lies the class loading and verifying process. When a Java program is executed, the JVM dynamically loads classes as they are needed, ensuring efficient memory management. This dynamic loading mechanism allows for flexibility and adaptability in Java applications, enabling them to evolve dynamically.

Additionally, the JVM verifies the loaded classes to ensure their integrity and security. Through a series of checks, the verifier examines the bytecode for potential errors or security vulnerabilities, mitigating the risks associated with malicious code execution.

Memory Management and Garbage Collection


Efficient memory management is a crucial aspect of JVM internals. Java's garbage collection mechanism automatically reclaims memory occupied by objects that are no longer in use, preventing memory leaks and optimizing resource utilization. The JVM employs various garbage collection algorithms, such as the generational garbage collector, to efficiently manage memory and enhance performance.

Just-In-Time Compilation (JIT)


One of the JVM's most remarkable features is its ability to dynamically optimize code execution through Just-In-Time (JIT) compilation. The JVM analyzes frequently executed bytecode and compiles it into native machine code, significantly improving the performance of Java applications. This adaptive compilation process ensures that critical code paths are executed efficiently, providing enhanced speed and responsiveness.

Runtime Data Areas


To facilitate the execution of Java programs, the JVM utilizes runtime data areas, which include the method area, heap, stack, and program counter. These areas store critical information such as class metadata, object instances, method invocations, and thread-specific data. By meticulously managing these runtime data areas, the JVM ensures efficient memory allocation and execution of Java applications.

Advanced Optimizations and Performance Tuning


In addition to its fundamental mechanisms, the JVM offers advanced optimizations and performance tuning options to further enhance the execution of Java programs. By fine-tuning various JVM parameters, developers can optimize memory allocation, garbage collection behavior, and thread management, among other aspects. These optimizations play a pivotal role in maximizing the performance and scalability of Java applications, empowering developers to create robust and efficient software solutions.

Conclusion

In conclusion, the JVM's internals constitute a fascinating realm of technology, where intricate mechanisms combine to power the Java platform. Through its class loading, memory management, JIT compilation, and runtime data areas, the JVM ensures optimal performance, security, and cross-platform compatibility. By gaining a deeper understanding of JVM internals, developers can unlock new possibilities and unleash the true potential of their Java applications.

Friday, June 23, 2023

Method handles: A better way to do Java reflection

Oracle Java, Java Certification, Java Jobs, Java Prep, Java Preparation, Java Tutorial and Materials

Java 18 introduced method handles, which offer a better way to do reflective programming.


What is reflection? Why does it matter? The previous articles in this series (see “Reflection for the modern Java programmer” and “The performance implications of Java reflection”) explored the topic and discussed the implementation of reflection as it shipped in versions of Java up to and including Java 17. This series concludes by building upon this base and explaining the new implementation that ships with Java 18 and later.

Note that to maintain backward compatibility, the Reflection API must be maintained with all its flaws and historical baggage, so I am talking only about internal changes here.

Why is there a new implementation?


Java 17 and earlier implementations of reflection rely on a delegation pattern—specifically a class called DelegatingMethodAccessorImpl. The delegate for this class starts off as a class that relies on native code to perform reflective invocation. However, once a threshold value is passed, the delegate is replaced by a custom class (it’s said that it has been patched out). This custom class is created dynamically at runtime, and this is a relatively expensive operation, which is why it is not performed until an invocation threshold is passed.

This implementation is sometimes referred to as the inflating implementation because the native delegate is inflated to a custom bytecode implementation, removing the need for a native call. This works well enough, but the inflating implementation is rather complex, as it has two separate code paths for reflection—one for native methods and one for the custom class—which are dynamically generated bytecode stubs.

Not only that, but due to the complexities of bytecode verification, the dynamically spun reflective method accessors need to be special-cased by the JVM through the MagicAccessorImpl class. This adds yet more complexity to an already somewhat baroque system.

Overall, these details mean that there is a lot of code to maintain, especially alongside the new MethodHandles API, which also provides similar capabilities for reflection.

This leads to an intriguing possibility: What if the entirety of the inflating implementation could be replaced by an equivalent capability based upon method handles? The relevant plan for this idea is JEP 416: Reimplement core reflection with method handles, which was delivered in Java 18.

The rest of this article will dive deeper into the new implementation and contrast it with the existing inflating implementation, initially by looking at a relevant security mechanism.

Enhanced field filtering


Since the very first release of OpenJDK 7, the reflection subsystem has had the ability to filter out certain fields from being visible to users, even when reflection and setAccessible() are used. This is achieved by maintaining a set of field names in an internal Reflection helper class; these field names are not allowed to appear in the returned value for getDefinedFields().

In Java 7 and Java 8, not many fields are filtered—basically only those required to protect a Java security manager (if one is set) and the filtering mechanism itself.

Java 11 produced the famous warning, “An illegal reflective access operation has occurred,” but the filtering mechanism did not change much from Java 8, except that it moved into a new, nonexported jdk.internal.reflect package in the java.base module.

However, the arrival of Java 12 in March 2019 altered things significantly. The code in jdk.internal.reflect.Reflection was updated to substantially increase the number of fields that are inaccessible to reflective code, as you can see in the following:

/** Used to filter out fields and methods from certain classes from public
    view, where they are sensitive or they may contain VM-internal objects.
    These Maps are updated very rarely. Rather than synchronize on
    each access, they use copy-on-write */
private static volatile Map<Class<?>, Set<String>> fieldFilterMap;
private static volatile Map<Class<?>, Set<String>> methodFilterMap;
private static final String WILDCARD = "*";
public static final Set<String> ALL_MEMBERS = Set.of(WILDCARD);

static {
    fieldFilterMap = Map.of(
        Reflection.class, ALL_MEMBERS,
        AccessibleObject.class, ALL_MEMBERS,
        Class.class, Set.of("classLoader"),
        ClassLoader.class, ALL_MEMBERS,
        Constructor.class, ALL_MEMBERS,
        Field.class, ALL_MEMBERS,
        Method.class, ALL_MEMBERS,
        Module.class, ALL_MEMBERS,
        System.class, Set.of("security")
    );
    methodFilterMap = Map.of();
}

Note that access to these fields is handled differently than access to general fields.

$ j11
openjdk version "11.0.13" 2021-10-19
OpenJDK Runtime Environment Temurin-11.0.13+8 (build 11.0.13+8)
OpenJDK 64-Bit Server VM Temurin-11.0.13+8 (build 11.0.13+8, mixed mode)

$ java javamag.reflection.ex2.ReflectTheReflect
class java.lang.reflect.Method
Hello world
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by javamag.reflection.ex2.ReflectTheReflect (file:/Users/ben/projects/writing/Oracle/Articles/reflection/src/main/java/) to field java.lang.reflect.Method.methodAccessor
WARNING: Please consider reporting this to the maintainers of javamag.reflection.ex2.ReflectTheReflect
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
class jdk.internal.reflect.DelegatingMethodAccessorImpl

$ j12
openjdk version "12.0.2" 2019-07-16
OpenJDK Runtime Environment AdoptOpenJDK (build 12.0.2+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 12.0.2+10, mixed mode, sharing)

$ java javamag.reflection.ex2.ReflectTheReflect
class java.lang.reflect.Method
Hello world
java.lang.NoSuchFieldException: methodAccessor
   at java.base/java.lang.Class.getDeclaredField(Class.java:2416)
   at javamag.reflection.ex2.ReflectTheReflect.main(ReflectTheReflect.java:16)

The practical effect of these changes is that from Java 12 onwards, some fields in some key classes in java.base are completely inaccessible to user code, even when reflection is used. Even Unsafe doesn’t help; dangerous techniques such as the “static offset” approach no longer work, as they require a reflective Field object to obtain the offset.

The field and the method filter are a security requirement but while they do encapsulate the internals, note that they are not directly related to the strong encapsulation provided by modularity (which is what people typically think of when they think of strong encapsulation of Java’s internals).

For example, as you can see from the example above, this change significantly predates the general enforcement of JEP 396: Strongly encapsulate JDK internals by default, which appeared in Java 16 (March 2021). Field filtering also can’t be worked around with options (such as --add-opens), while the encapsulation from modularity typically can be.

(The basics of strong encapsulation can be found in “A peek into Java 17: Encapsulating the Java runtime internals.”)

One result of the enhanced field filter is that the only real way for a Java developer to see the new implementation of reflection is by using an IDE debugger. These tools can access internal JVM data structures at a much deeper level than is possible with Java code, but they are much less convenient than just running some reflective Java code.

Method handles


Method handles are intended to be used in similar circumstances where a programmer might reach for the Reflection API.

However, by gleaning from over 20 years of practical experience since reflection debuted, this second attempt at a reflective invocation capability is intended to provide (among other things) a better API for direct use.

Method type objects. One useful starting point for this better API is to consider that reflection represents the type signature of methods as instances of Class[]. As noted in “Reflection for the modern Java programmer,” this is because the Reflection API predates the Collections API (and, unfortunately, given Java’s extreme focus on backward compatibility, this is an API that cannot be withdrawn).

However, in the MethodHandles API, this is done with the MethodType class instead, which avoids the problem of using arrays as domain objects. MethodType is a class that has immutable instances that are created from a static factory method that is variadic in class objects, as follows:

public static MethodType methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
    // ...
}

For example, the object that represents the compare() method for a string comparator is obtained as follows:

var mtStringComparator = MethodType.methodType(int.class, String.class, String.class);

The first argument to methodType() is the return type of the method—which is, in this case, int. After that, the types of the arguments to the method follow in positional order.

Notice that this MethodType doesn’t include either the method name or the type of the object that the method will be called upon. This actually makes it a more useful abstraction because it can be used in cases such as lambdas (where the method’s name doesn’t matter); therefore, it can be used for both static and virtual methods.

Lookup objects. A second issue that method handles resolve is the question of setAccessible(), which allows you to break the rules of the Java language by allowing you to call code to selectively disable access control.

Having this capability was an explicit nongoal for method handles. Instead, the designers wanted to make sure that encapsulation could be properly protected in the new mechanism.

To achieve this protection, a new concept called a lookup context was introduced. To understand how this works, recall that all Java code executes inside a method, and that every method lives inside a class. Therefore, executing code runs within a class—and so there is a set of methods that the code could call.

For example, the code could call

◉ Any private method of the current class (or an enclosing class)
◉ Any public method of a public class

Similarly, this applies for the package-private and protected methods, according to the rules of the Java language. (The ability to call public methods is also affected by the modules system in recent versions of Java.)

The point of a lookup context is to encapsulate the knowledge of which methods it is legal to call at the point where the lookup object is created. Inaccessible methods are not visible from the lookup context, which removes the distinction that reflection has between getMethod() and getDeclaredMethod().

Because the lookup object encapsulates a capability to look up methods and fields, you need to be careful about sharing the object, especially with untrusted code.

The MethodHandles class also has a publicLookup() method that may be preferable, as it is a minimally trusted lookup that can be freely shared but is restricted in what it can look up.

The most common way to obtain a lookup context is to call the static helper method MethodHandles.lookup(), which returns an object that represents methods accessible from the current class. Once you have the lookup object, you can obtain method handles from it by calling one of its find*() methods, such as findVirtual() or findConstructor().

For example, you can obtain a method handle for toString() as follows:

// Define a method type object corresponding to toString()
// This is the method type for methods that return String and take no parameters
var mt = MethodType.methodType(String.class);
System.out.println("MT: "+ mt);

// Create a lookup object for the current class context
var lk = MethodHandles.lookup();

try {
  // Create a method handle for toString()
  var mh = lk.findVirtual(getClass(), "toString", mt);
  System.out.println("mh.MT: "+ mh.type());

  // ... do something with the method handle

} catch (NoSuchMethodException | IllegalAccessException mhx) {
  throw new RuntimeException(mhx);
}

Because toString() is always public, the lookup should always find the method. However, there’s another point about method types here, which you can see by looking at the output of the following two println() statements:

MT: ()String
mh.MT: (MTMain)String

These show that after an MHs.Lookup.findVirtual() call, the MethodType given by MH.type() will have the receiver type as the first argument type. In other words, lookups don’t require the receiver type to be manually added, but once a method handle has been resolved, the receiver type (assuming there is one) is known.

Invoking a method handle. In the example above, I used findVirtual() because I want to call the correct override of toString() when invoking the method handle. To see this in action, define a simple class, MTMain, with an implementation of toString(), as follows:

public class MTMain {
    @Override
    public String toString() {
        return "MTMain {}";
    }

    public static void main(String[] args) {
        var mh = getToStringHandle();

        var main = new MTMain();
        try {
            var s = mh.invoke(main);
            System.out.println(s);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public static MethodHandle getToStringHandle() {

        // Performs the lookup shown in the previous example
        // and returns the method handle

    }
}

This, as expected, has the same overall effect as calling System.out.println(main.toString()). However, there are some important differences. Consider the very similar (but reflective) class RefMain.

public class RefMain {
    @Override
    public String toString() {
        return "RefMain {}";
    }

    public static void main(String[] args) {
        var main = new RefMain();
        var mh = getToStringMethod(main);
        try {
            var s = mh.invoke(main);
            System.out.println(s);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public static Method getToStringMethod(Object o) {
        Class<?> clazz = o.getClass();
        try {
            return clazz.getMethod("toString");
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
}

Here’s the bytecode.

public static void main(java.lang.String[]);
    Code:
       0: new           #9                  // class javamag/reflection/ex1/RefMain
       3: dup
       4: invokespecial #11                 // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokestatic  #12                 // Method getToStringMethod:(Ljava/lang/Object;)Ljava/lang/reflect/Method;
      12: astore_2
      13: aload_2
      14: aload_1
      15: iconst_0
      16: anewarray     #2                  // class java/lang/Object
      19: invokevirtual #16                 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
      22: astore_3
      23: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
      26: aload_3
      27: invokevirtual #28                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V

It’s quite similar to the method handle case’s bytecode, up until #15.

public static void main(java.lang.String[]);
    Code:
       0: new           #9                  // class javamag/reflection/mh/MTMain
       3: dup
       4: invokespecial #11                 // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokestatic  #12                 // Method getToStringHandle:(Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;
      12: astore_2
      13: aload_2
      14: aload_1
      15: invokevirtual #16                 // Method java/lang/invoke/MethodHandle.invoke:(Ljavamag/reflection/mh/MTMain;)Ljava/lang/Object;
      18: astore_3
      19: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
      22: aload_3
      23: invokevirtual #28                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V

Oracle Java, Java Certification, Java Jobs, Java Prep, Java Preparation, Java Tutorial and Materials
Notice there’s something different about the signature of Method.invoke() compared to MethodHandle.invoke() in the bytecode. The reflective case has a signature of

Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;

which exactly corresponds to the following source code in Method.java:

// Method.java
    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        // ...
    }

This is quite different from the MethodHandle case, which (from the bytecode) has a signature like the following, which means: “a method that acts on an object of type MTMain and returns Object.”

Method java/lang/invoke/MethodHandle.invoke:(Ljavamag/reflection/mh/MTMain;)Ljava/lang/Object;

However, the source code for invoke() on a method handle is declared as follows:

// MethodHandle.java
public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;

What’s going on? The answer is in the annotation @PolymorphicSignature—which indicates that invoke() (and a couple of other methods of MethodHandle) is signature polymorphic.

Per the Java Language Specification, a signature polymorphic method is one that can operate with essentially any argument and return types. When the Java source code compiler encounters a call to a signature polymorphic method, the code will compile, regardless of how the method is being called. Effectively, it is as though a signature polymorphic method is not a single method, but an entire family of methods with every possible signature available.

To illustrate this, the following code alludes to the string comparator example shown earlier:

public int compareTwoStrings(String s1, String s2) {
    return 0;
}

private static MethodHandle getStringCompHandle() {
    // MethodType of compareTwoStrings()
    var mt = MethodType.methodType(int.class, String.class, String.class);
    var lk = MethodHandles.lookup();

    try {
        return lk.findVirtual(lk.lookupClass(), "compareTwoStrings", mt);
    } catch (NoSuchMethodException | IllegalAccessException mhx) {
        throw new RuntimeException(mhx);
    }
}

This example uses the lookupClass() method present on the lookup object. Here, this method returns the class object of the current class, but it works from both static and instance methods.

Here’s a bit of code to drive this example.

var mhSc = getStringCompHandle();
try {
    var s = mhSc.invoke(main, "foo", "bar");
    System.out.println(s);
} catch (Throwable e) {
    e.printStackTrace();
}

Sure enough, the bytecode for this snippet shows the signature polymorphic call to invoke().

Method java/lang/invoke/MethodHandle.invoke:(Ljavamag/reflection/mh/MTMain;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;

This is all fine at compile time, but what about at runtime? For example, what happens if you try to invoke the method handle with the wrong arguments, as in the following:

var s = mhSc.invoke(main, "foo");

Well, this will cause an exception and, helpfully, the JVM reports that the method handle you are attempting to call is expecting three arguments—one of type MTMain and two strings—but it was called with only an MTMain and one String.

java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(MTMain,String,String)int to (MTMain,String)Object

These examples show that the method handle approach has more information in the bytecode to help the runtime, and it avoids some of the overhead that is present in reflective calls. In particular, the boxing of primitive method arguments and collecting the arguments into an array can be avoided because the shape of the method call is known.

Rebasing reflection on top of method handles


Method handles can be used as a replacement for reflection in modern Java code, because they have essentially the same set of capabilities. There are some differences—method name changes and, in particular, the use of lookup objects—but overall, Java programmers who are already familiar with reflection should not experience any serious difficulties coming to grips with method handles.

All of this raises an intriguing possibility—could method handles be used as the engine to perform introspection operations, with the old Reflection API retrofitted on top of it?

Yes! In fact, JDK 18 did indeed switch over to using method handles to implement reflection, without changing the interface.

The old (Java 1.1) reflection interface must be maintained because Java always tries to retain backward compatibility, but the implementing code can be changed if that constraint holds.

The MethodAccessor interface is maintained, but instead of the MethodAccessorImpl classes that you are already familiar with, java.lang.reflect.Method instead contains evidence of method handles, as you can see in the source code.

public MethodAccessor newMethodAccessor(Method method, boolean callerSensitive) {
    // use the root Method that will not cache caller class
    Method root = langReflectAccess.getRoot(method);
    if (root != null) {
        method = root;
    }

    if (useMethodHandleAccessor()) {
        return MethodHandleAccessorFactory.newMethodAccessor(method, callerSensitive);
    } else {
        if (noInflation() && !method.getDeclaringClass().isHidden()) {
            return generateMethodAccessor(method);
        } else {
            NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
            return acc.getParent();
        }
    }
}

The method handles implementation is the default—and so useMethodHandleAccessor() will default to true—but the old inflating implementation is temporarily still available. It can be activated via the following switch, but this is purely for compatibility reasons and will be removed in a future JDK release:

-Djdk.reflect.useDirectMethodHandle=false

Unfortunately, demonstrating the existence of the new implementation from within Java code is not very easy. As you saw earlier in the article, recent Java releases have started removing certain fields from the list of defined fields returned by introspection. This has the effect of making these fields impossible to access reflectively, and so you can’t do the sort of tricks shown in “The performance implications of Java reflection” to show the method handles present inside Method objects.

Finally, a word on performance: It might be tempting to pose the question, “How does the new implementation compare with the original, inflating code?” There is no simple, well-defined answer. Many aspects of the reflection subsystem have changed (such as access control, boxing, and volatile access to accessors), and the overall aggregate effect is impossible to reason about.

I could provide some Java Microbenchmark Harness (JMH) benchmarks that purport to show the difference between the two implementations, but everything I wrote in the previous article about microbenchmarks—that they represent individual data points and not a representation of some deeper underlying truth—continues to apply.

Instead, remember the somewhat mundane truth at the heart of performance analysis: If you want to know the performance impact of a particular language or JVM feature on your specific application within a certain range of inputs, you will have to test your specific application.

Conclusion

This series of articles provided an in-depth tour of reflection. As well as discussing the APIs, it covered reflection as a runtime technology that shows the real difference between the statically typed Java language and the dynamic nature of the JVM.

I’ve discussed how reflection is implemented, and I introduced a little of the JVM internals that support the capability. This included showing how recent releases of Java have closed off access to the reflection internals from meddling programmers, which allows the platform developers to make changes more freely.

This final article introduced the new MethodHandles API and showed how this has been used to reimplement the Reflection API. It also showed how it can be used directly as a more convenient and modern way to do introspection.

Source: oracle.com

Wednesday, June 21, 2023

Quiz yourself: Locking mechanisms and Java’s postincrement ++ operator

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

Let’s see if this quiz code will work consistently.


Imagine that your colleagues want to perform calculations in parallel. In pursuit of thread safety, they implemented the following locking mechanism:

var lock = new ReentrantLock();
var s = IntStream.of(1, 2, 3);
var i = s.parallel().map(
  v -> {
    lock.tryLock();
    var res = v++;
    lock.unlock();
    return res;
  }
).sum();
System.out.println(i);

Which statement is correct about this code? Choose one.

A. It consistently prints 12 to console.
B. It might print 12 to console.
C. It consistently throws an exception.
D. It might throw an exception.
E. It prints nothing, hanging indefinitely.

Answer. This question centers on the behavior of the ReentrantLock.tryLock() method, and it expects you to understand how the postincrement ++ operator works.

Consider the computation performed by the block lambda in the map step of the stream. It takes its argument, v, copies that value to the local variable res, and then returns the value of res. Notice that the increment operator used on v is a postincrement operator. As a result, the numbers that are in the stream at the point of the sum() operation are 1, 2, and 3. Therefore, if any value is printed, it could only be 6. This tells you that options A and B, both of which suggest a possible output of 12, must be incorrect.

Now consider the locking behavior. The tryLock method never blocks—if the lock cannot be obtained, the method immediately returns false, and execution proceeds with no lock held. On the other hand, if the lock is obtained successfully, the method returns true and, as before, continues without blocking.

Because tryLock never blocks, you can determine that option E must be incorrect; there’s no reason for the code to hang indefinitely.

Now you get to the crux of the question. If an unlock call is made on a ReentrantLock that is not in the locked state, it throws an IllegalMonitorStateException. You must decide whether this will definitely happen or is simply a possibility.

Looking at the code, it’s easy to imagine that as the three numbers arrive at the map method, they could do so with a timing that simply results in three nonoverlapping sets of calls: tryLock, unlock; tryLock, unlock; and tryLock, unlock. If that sequence happens, no exception will arise.

This means that the exception might arise but is not guaranteed. From this you know that option D is correct because the code might throw an exception. You also know that option C is incorrect because there’s no guarantee the exception would be thrown consistently.

By the way, the code can definitely be fixed.

Fundamentally, there is no need for locking in this code because the map operation’s lambda does nothing to the data in the stream, and the map operation can simply be deleted. If this code were a placeholder or an error—perhaps the intention was to double the values (expecting the answer 12)—all that’s needed is the following simple step in place of the current map operation:

.map(v -> 2 * v)

This code needs no lock because it interacts only with method-local data and not with anything shared; thus, there’s no thread-safety issue.

If, however, the intention was actually to mutate some shared data (there’s no indication of that effect in this code), locking might be a suitable solution (though such side effects are strongly discouraged in stream code anyway). If that were intended, tryLock would be unsuitable for two reasons: The operation would be unprotected if no lock were obtained, and perhaps the operation would simply be skipped over if the failure to lock caused the code to skip the operation.

The following modification to the existing code would avoid the exception, but it would not actually guarantee a lock and would skip the operation if no lock were obtained:

.map(
  v -> {
    var res = v;
    if (lock.tryLock()) {
      v++; // still useless and skipped if no lock obtained!
      lock.unlock(); // only unlock if lock succeeded
    }
    return res;
  }
)

To actually protect an operation with mutual exclusion, the code could look like the following:

.map(
  v -> {
    var res = v;
    lock.lock() // blocking call, waits for locking to succeed
    try {
      v++; // still useless, but protected by a lock 
    } finally {
      lock.unlock(); // unlock reliably
    }
    return res;
  }
}

Notice the recommended location for an unlock() operation is in a finally block, which ensures that no matter how the body of the try block proceeds or terminates, the lock will definitely be released.

Conclusion. The correct answer is option D.

Source: oracle.com

Monday, June 19, 2023

Oracle Java: The Key to Developing Next-Generation Software

Oracle Java, Next-Generation Software, Oracle Java Certification, Java Career, Java Skills, Java Jobs, Java Tutorial and Materials, Java Guides, Java Learning, Java Preparation

Introduction


In today's rapidly advancing technological landscape, the demand for cutting-edge software development has never been higher. To meet these evolving needs, developers require a powerful and versatile programming language. Oracle Java emerges as the ultimate solution, empowering developers to create next-generation software applications that redefine the boundaries of innovation. In this article, we delve into the exceptional features and benefits of Oracle Java, highlighting why it is the key to unlocking unparalleled software development capabilities.

Unleashing the Power of Oracle Java


1. Versatility and Platform Independence

Oracle Java stands out for its remarkable versatility, making it an ideal choice for developers across various domains. Whether you're developing web applications, mobile apps, enterprise software, or even embedded systems, Java caters to all your needs. Its platform independence allows code to run seamlessly on different operating systems, including Windows, macOS, Linux, and more. This flexibility ensures wider reach and compatibility, propelling your software to new heights.

2. Robustness and Reliability

When it comes to software development, robustness and reliability are paramount. Oracle Java's exceptional architecture and extensive testing processes ensure that your applications are robust, stable, and capable of handling complex tasks. With Java's advanced memory management, automatic garbage collection, and built-in exception handling mechanisms, your software remains resilient even in demanding environments. This reliability instills confidence in users, contributing to enhanced user experience and customer satisfaction.

3. Vast Ecosystem and Community Support

Oracle Java boasts a vast ecosystem with an extensive range of libraries, frameworks, and tools. This rich collection empowers developers to expedite development cycles and streamline their workflows. Whether you need powerful frameworks like Spring or popular libraries like Apache Commons, Java offers a comprehensive set of resources at your disposal. Furthermore, Java's active and passionate community ensures continuous support, regular updates, and a wealth of knowledge sharing. This collaborative environment fosters growth, enabling developers to overcome challenges and achieve extraordinary results.

4. Scalability and Performance

In today's fast-paced digital landscape, scalability and performance are crucial factors for success. Oracle Java excels in this aspect, providing developers with robust features to optimize their applications. With Java's efficient memory management, multithreading capabilities, and just-in-time (JIT) compilation, your software achieves exceptional performance levels, even under heavy workloads. Additionally, Java's modular architecture facilitates seamless scalability, allowing your software to adapt to changing business requirements effortlessly.

5. Security and Safety

Software security is a paramount concern in an increasingly interconnected world. Oracle Java prioritizes security at every level, ensuring that your applications are well-guarded against potential threats. Java's built-in security features, such as bytecode verification, sandboxing, and secure class loading, provide a strong foundation for developing secure software. Additionally, Java's constant updates and prompt vulnerability patching further reinforce its reputation as a secure programming language. By leveraging Java's robust security measures, you can safeguard sensitive data and protect your users' privacy.

Conclusion

In conclusion, Oracle Java emerges as the key to developing next-generation software applications. Its versatility, robustness, vast ecosystem, scalability, and security features make it the ultimate choice for developers worldwide. By harnessing the power of Java, you unlock endless possibilities, propelling your software to new heights of excellence. Stay ahead of the competition, embrace the power of Oracle Java, and embark on a journey of innovation and success.

Friday, June 16, 2023

Announcing Graal Cloud Native

We're pleased to announce the general availability of Graal Cloud Native (GCN) v3.8.5.

GCN is a curated set of open source Micronaut® framework modules that simplify multicloud application development and are designed from the ground up to be compiled ahead-of-time with GraalVM Native Image. GCN enables you to easily build portable cloud native Java microservices that start instantly and use fewer resources to reduce compute costs.

Research shows that cloud native and multicloud are becoming more prevalent. As per Gartner and IDC, the majority of new applications will be cloud native by 2025. According to Oracle’s second annual multicloud survey results, multicloud is the new reality with 98% of enterprises surveyed already using or planning to use multicloud. Cost optimization, cloud vendor lock-in concerns, and workload portability are among the top motivators and challenges for multicloud.

Graal Cloud Native, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Preparation

Multicloud Portability


Today, building cloud native applications requires the use of cloud-proprietary APIs and libraries. As a result, cloud native applications are tied to the cloud platform and need to be rewritten to deploy and run on another cloud platform.

With GCN, you can write applications once and run them on any public cloud - Oracle Cloud Infrastructure (OCI), Amazon Web Services (AWS), Google Cloud Platform (GCP)*, and Azure*. GCN modules provide platform-independent libraries for a set of core public cloud services including object storage, secret management, streaming, logging, and more. GCN enables you to easily deploy and run the same application on multiple public clouds, and thus prevents cloud lock-in.

Faster and cheaper


Cost optimization is one of the biggest motivators for enterprises running applications in the cloud. GCN applications compiled by GraalVM Native Image start up to 100x faster, operate at peak performance immediately, and require less memory and CPU. As a result, GCN applications need fewer resources to run at scale, and help you meet your cost optimization goals.

Developer Productivity


Graal Cloud Native, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Preparation
You can reduce development time with out-of-the-box cloud service integrations and compile-time validation that detects errors at build time, not runtime. Micronaut improves Java developer productivity by detecting errors at compile time instead of runtime, and using annotations instead of runtime reflection.

GCN also provides tooling to make it easy to develop Micronaut applications in VS Code including auto-completion for code and configuration files. The tooling makes it easy to transition from local development to cloud-based development from within VS Code.

You can use the GCN Launcher to generate multicloud application starters to quickly get started with multicloud development.

Source: oracle.com

Wednesday, June 14, 2023

Quiz yourself: Delegation using super(...) and this(...) during constructor execution

Oracle Java Certification, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Prep, Oracle Java Preparation, Oracle Java Tutorial and Materials, Oracle Java Learning

There are rules for explicit constructor invocations. Do you know them?


Given the following two classes

class Building {
  Building nextBuilding;
  Building() {}
  Building(Building nb) {
    nextBuilding = nb;
  }
}

class Hotel extends Building {
  final Building nb = new Building();
}

Which constructor can be added to the Hotel class without causing compilation errors? Choose two.

A. Hotel() { super(nb);  }
B. Hotel() { super(nextBuilding);  }
C. Hotel() { super(super);  }
D. Hotel() { super(this);  }
E. Hotel() { super(null);  }
F. Hotel() { super(new Hotel() {});  }

Answer. One conceptual model of the relationship between the parent type aspects and the subtype aspects of an object is to consider the parent type aspects as the foundation upon which the subtype parts are built, rather like putting a house on a foundation and then putting a roof on that house. Clearly, if you try to work on the roof before you build the walls or you work on the walls before preparing the foundation, you will have a problem.

Java tries hard to enforce this notion. The delegation behavior of super(...) and this(...) during constructor execution is such that it mostly enforces the initialization of an object to start from Object and work its way from the parent aspect to the child aspect. Somewhat simplified, the order of operations is as follows:

1. Invocation of new allocates and clears memory for the entire object’s storage and passes the reference to that storage as the implicit this argument into the matching constructor.

2. Calls to this(...) or super(...)—which appear, or can be implicit, as the first statement in a constructor—force execution up to the constructor for Object, which does not have a call to super() since it has no superclass.

3. Upon returning from each super(...) constructor, the instance initializers of the current class are executed, and then these are followed by the body of the constructor(s) at the current level.

4. After the current level finishes processing, control returns to the next subclass level down.

Given this outline, consider what would happen if calls to this(...) or super(...) could refer to any instance aspect of the object being initialized. Such an aspect would still be uninitialized. That’s not a very smart way to go about preparing a well-built object: It’s like trying to paint the roof before the walls are in place.

Here are two things you should know.

◉ A call to this(...) or super(...) must be the first statement in the constructor—and super() will be added implicitly if neither this nor super is coded.
◉ The argument list to these calls must not refer to this either explicitly or indirectly by an unqualified identifier that would be resolved using an implicit this reference. The super prefix, which is essentially a reference to this but with a different type, is also prohibited.


An explicit constructor invocation statement introduces a static context (§8.1.3), which limits the use of constructs that refer to the current object. Notably, the keywords this and super are prohibited in a static context (§15.8.3, §15.11.2), as are unqualified references to instance variables, instance methods, and type parameters of lexically enclosing declarations (§6.5.5.1, §6.5.6.1, §15.12.3).

In view of the restriction just described, consider the quiz options.

In option A, the actual parameter to the super call is nb. This can be resolved only as an implicit reference to this.nb and, as such, the constructor is not valid and option A is incorrect.

Option B fails by the same reasoning because the identifier nextBuilding is resolved as this.nextBuilding and is not valid as an argument to the super call.

Option C is incorrect for two reasons. First, super is not usable as a standalone reference; it can be used only as a prefix. Second, as already mentioned, super can’t appear in the argument list of this(...) or super(...).

Option D is similarly incorrect because this cannot appear in the argument list of this(...) or super(...).

Option E is correct. While it might not be helpful from a semantic perspective, null is a valid parameter to pass to this(...) or super(...) provided there is a target constructor that takes an argument of reference type.

Option F is also correct because it shows valid syntax constructing an anonymous subclass of Hotel. Such an expression is acceptable as an argument to the super(...) invocation and will delegate to the Building constructor of the form Building(Building nb).

As a side note, it has always been permitted to place expressions as actual parameters to this(...) and super(...) calls, provided they make no direct or indirect reference to this. Such expressions can make unrestricted use of static members (methods and fields) of the class being initialized or other classes.

However, as of Java 17, it’s not permitted to put any code prior to the statement containing this(...) or super(...), and an implicit super() will always precede any code we write ourselves.

At this time, however, there is a JEP preview that proposes to allow code before a this() or super() call, which might allow tidier preparation of arguments.

Conclusion. The correct answers are options E and F.

Source: oracle.com