Showing posts with label Quiz. Show all posts
Showing posts with label Quiz. Show all posts

Monday, January 1, 2024

Quiz yourself: Serializing a primitive with ObjectOutputStream

Quiz yourself: Serializing a primitive with ObjectOutputStream

Primitives? Objects? What should you do?


You need to serialize a long primitive value, and you have been given the following code:

long l = 5L;
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) {
  ... //
}

Which statement is true? Choose one.

A. The code must box the primitive using oos.writeObject(Long.valueOf(l)).
B. The primitive must be included in the outer Java class as an instance variable to be serialized.
C. The serialization should be implemented as oos.writeObject(l).
D. The serialization should be implemented as oos.writeLong(l).
E. The serialization must delegate to the DataOutput class for writing primitives.

Answer. Java serialization provides a way to represent a graph of objects as a byte sequence. This mechanism also supports primitive data directly, as part of objects. Primitive wrapper classes also implement the Serializable interface and can be serialized too. Commonly, the byte sequence that results from serialization is written to disk for long-term storage or is transmitted across a network.

In most cases, you will use the class ObjectOutputStream to create the serialized byte stream. This class provides the writeObject method proposed in option C, and that method readily handles serializing an entire graph of objects. The result can be transparently restored using the ObjectInputStream class. Note, however, that the argument to the writeObject method is an object, not a primitive.

The previous discussion tells you something about option C: If it’s used, autoboxing would occur and you would not serialize the primitive value. Instead, you would have a serialized representation of the wrapper that was created. At this point in analyzing the exam question, it’s probably too early to reject this option, but you must decide whether any other answer is a better match with the proposition of the question—which does, after all, specifically mention that you are serializing a primitive value.

It turns out that ObjectOutputStream implements the java.io.DataOutput interface. That interface defines writeXXX methods for all primitives. As a side note, DataOutput is also implemented by the class java.io.DataOutputStream, but although that class works with primitives and strings, it cannot perform serialization. This capability is described in option D, and it will allow you to use the oos object to serialize the primitive directly. From this you can determine that option D is correct. It’s definitely better than option C, and you can now confidently reject option C as incorrect.

Although you should feel confident in the correctness of option D, you should still verify that the other options are incorrect. In an exam, you might discover another option that seems correct, which would cause you to revisit your reasoning. But if you’re tight on time, you might go with the first answer that appears correct.

Option A is incorrect: The ObjectOutputStream is able to serialize primitives directly, which means that boxing is not necessary. Of course, if your application calls for it, you can box or autobox the primitive, but it’s not necessary. Further, the result will take substantially more space in the resulting byte sequence.

Option B is also incorrect for essentially for the same reason as with options A and C. Certainly primitives that are members of objects are handled correctly, but it’s not necessary to wrap a primitive in such an object just to get it out into the byte sequence.

Option E is incorrect, because, as discussed earlier, java.io.DataOutput is an interface, not a class, as is suggested in the stem. The interface declares the signatures of various methods, including the writeLong method you will use, but writeLong is (and, in fact, all the methods of this interface are) abstract. The implementation is provided by the java.io.ObjectOutputStream class; therefore, delegation in the code is not necessary or possible.

Conclusion. The correct answer is option D.

Source: oracle.com

Monday, December 4, 2023

Quiz yourself: Read and write objects by using serialization

Test your knowledge of the java.io.Serializable interface.


The objective of this Java SE 11 quiz is to read and write objects by using serialization. Imagine you are working on a project that includes these two classes:

Quiz yourself: Read and write objects by using serialization

final class Item {
  String title;

  public Item(String title) {
    this.title = title;
  }

  String getTitle() {
    return title;
  }
}

class Order implements Serializable {
  private int id;
  private List<Item> items = new ArrayList<Item>();

  public Order(int id) {
    this.id = id;
  }

  public void addItem(Item i) {
    items.add(i);
  }

  public void getItem(int i) {
    items.get(i);
  }
}

Also imagine a colleague has already started working on a requirement that needs the ability to serialize order objects. Another requirement prohibits modifications of the Item class code.

Which of the following changes allows an order to be properly serialized?

A. Add this new method to the Order class:

private void writeObject(ObjectOutputStream s) throws IOException {  s.defaultWriteObject();
}

B. Add this new method to the Order class:

private void writeObject(ObjectOutputStream s) throws IOException {
  s.defaultWriteObject();
  for (Item itm : items) {
    s.writeObject(itm.getTitle());
  }
}

C. Add this new method to the Order class and mark the items instance variable transient:

private void writeObject(ObjectOutputStream s) throws IOException {
  s.defaultWriteObject();
  for (Item itm : items) {
    s.writeObject(itm.getTitle());
  }
}

D. None of the above

Answer. Here are some background rules regarding the behavior of the serialization system. When you serialize an object, the default behavior is to serialize all of the object’s instance data transitively—that is, to serialize the instance data of the object, to serialize each object to which the object refers, and so on until the entire object graph is serialized. (Note that static members are better viewed as elements of the class, not as elements of the object.)

This behavior can be modified in a number of ways, most simply by marking a field as transient. Another rule in the serialization system is that an attempt to serialize an instance of any class is permitted only if that class implements the Serializable interface. This interface is a “marker” interface (an idea that predates the introduction of annotations with Java 5). The serialization system will refuse to serialize an object unless the defining class of that object implements the Serializable interface—you can imagine that the Serializable interface marks the object as a permitted target for serialization.

Now, let’s look at the situation presented in the question. Here are three observations:

  • First, the Order class implements the Serializable marker interface. Presumably this is a result of the partial refactoring work already performed.
  • Second, the Order class has two fields. One is an int and the other is an ArrayList. Neither is marked transient, and both are serializable members of core Java.
  • Third, the list contains Item objects, but the Item class does not implement the Serializable interface. You are prohibited from altering the source code of the Item class and from subclassing the class, since it is final.

Given these three observations, you can conclude that the default serialization must fail when it attempts to serialize any instance of the Item class found in the list. Further, since you are prohibited from altering the Item class, it will always be impossible for those objects to be serialized.

All is not necessarily lost, however, since it is possible to modify the serialization mechanism and instead of serializing the Item directly, serialize some other data—probably the String representing the title—from which an Item might be reconstructed.

The documentation for the java.io.Serializable interface says the following regarding how a customized serialization mechanism can be implemented by adding some new methods to a class:

Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:

private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;

The default mechanism for saving the Object’s fields can be invoked by calling out.defaultWriteObject. The method does not need to concern itself with the state belonging to its superclasses or subclasses. State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.

Now, let’s look at the options for answering the proposed question.

Option A suggests implementing the writeObject method and directly calling the defaultWriteObject method from within it. However, this approach simply invokes the default serialization mechanism without changing anything. That will fail as soon as there is an attempt to serialize any Item instance and throw a java.io.NotSerializableException. Because of this, you can see that option A is incorrect.

Option B seems at first sight to address the problem by taking the approach of serializing the title of each Item. Since the title is a String, this aspect would work. However, the overall solution still fails because the first action of the writeObject method is still to call the defaultWriteObject utility, and that will still fail since it will try to serialize the Item objects. This solution will cause a NotSerializableException to be thrown before execution ever gets to the manual serialization behavior. Because of this, option B is also incorrect.

Option C improves the situation further because it marks the items variable as transient. This excludes the list from the default serialization and, therefore, the code does not crash when it calls defaultWriteObject. Further, the title of each item is saved to the output stream individually, because the java.lang.String object is serializable. From this you can determine that this code would run without throwing any exceptions and a representation of the object would be written to the output file.

However, the question requires “proper serialization.” What does that mean? The Serializable documentation notes: “The writeObject method is responsible for writing the state of the object for its particular class so that the corresponding readObject method can restore it.”

The problem with the code for option C is that there is no reliable way to deserialize the result. The failure is because the size of the list is not written, so there is no way to know how many times to read item titles and re-create Item instances. Because the resulting serialized data cannot be restored, option C is also incorrect.

Given that options A, B, and C are all incorrect, you can conclude that the correct answer in this case is option D: “None of the above.”

Of course, it’s rather unsatisfying simply to have determined that all these attempts are unsuccessful. What should you do to properly serialize these objects, so you can reliably restore them from the result?

To accomplish that, you can build on the proposal of option C (marking the items list as transient and representing the individual Item objects using only the String representing an item’s title), but you must also save the size of the list in the serialized data stream. You should do this before writing any of the Items (which still must be written as Strings representing the item titles). It seems reasonable to store the size using a primitive int value. On the basis of this, a valid method for the Order class might look as follows (don’t forget that you must still mark the items variable as transient):

private void writeObject(ObjectOutputStream s) throws IOException {
    // does not write items because the list is transient
    s.defaultWriteObject();
    // write size of the list
    s.writeInt(items.size());     
    for (Item itm : items) {
        s.writeObject(itm.getTitle());
    }
}

And the corresponding deserialization code in the Order class would look like this:

private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
    // restore the non-transient parts of the Order
    s.defaultReadObject();
    // determine the number of Items to be restored
    int n = s.readInt();
    // create a list for the Item objects
    items = new ArrayList<>();
    // read the correct number of titles, create the corresponding Item
    // and insert that item into the list
    for (int i = 0; i < n; i++) {
        String title = (String)s.readObject();
        items.add(new Item(title));
    }    
}

Conclusion: The correct answer is option D.

Source: oracle.com

Friday, September 29, 2023

Quiz yourself: Deserializing objects that have a nonserializable parent

Quiz Yourself, Oracle Java, Core Java, Java Prep, Java Preparation, Java Certification, Java Career, Java Tutorial and Materials, Java Guides

What’s correct and what’s not correct about the Java deserialization process?


You are working to enhance a legacy application, in particular to add serialization support and add more constraints to new business objects. The legacy application class looks like this.

public class Person {
  private String name = null;
  public Person() {}
  public Person(String s) {
    name = s;
  }
}

You’ve also added a new application class.

public class EnhancedPerson extends Person implements Serializable {
  public EnhancedPerson(String s) {
    super(s);
    if (s == null || s.length() == 0) {
      throw new IllegalArgumentException("Invalid name");
    }
  }
}

Which statement is correct? Choose one.

A. The EnhancedPerson class may not participate in serialization because its superclass is not serializable.
B. The EnhancedPerson class may not participate in serialization because it does not have a zero-argument constructor.
C. The EnhancedPerson class can be serialized but cannot be deserialized.
D. Immediately after deserialization, the name field of an EnhancedPerson will have a null value.
E. The EnhancedPerson class will always throw IllegalArgumentException during deserialization.

Answer. This question investigates the deserialization process of an object that has a nonserializable parent type. Of course, every object has a nonserializable parent because, even if nothing else were true, java.lang.Object itself falls into this category.

This first observation tells you that option A must be incorrect because a nonserializable parent type is inevitable and, therefore, cannot possibly prevent serialization and deserialization.

Two more rules regarding the serialization system are relevant to answering this question.

  • When a serializable object is deserialized, the nonserializable parent aspects are initialized by calling an accessible zero-argument constructor in the immediate parent that’s not serializable.
  • Except for record types, deserialization does not use any of the constructor or initialization code for the elements of that object hierarchy that are serializable.

From those two points, you can see that option B is incorrect.

Option C is also incorrect because nothing in the code prevents deserialization. If the zero-argument constructor of Person were private, or if it did not exist, this option would be correct.

Option D is correct because the name field belongs to the Person aspect of the EnhancedPerson object. Such fields are not part of the serialized information. As noted earlier, when the Person instance is re-created, this is done by calling the zero-argument constructor of Person. Because of this, the name field will be initialized to null. Of course, this would also be the case if the assignment of null were not present in the code, because the memory allocated for an object is always zeroed before it becomes available for use as the new object.

Regarding why option E is incorrect, recall that the deserialization process for (nonrecord) classes does not call any constructor in the Serializable aspects of the object. This means that the code in the EnhancedPerson constructor that validates the supplied name will not be called; therefore, no exception can be thrown.

This discussion raises questions. In the current design, deserialization creates an EnhancedPerson object with a null name field. The validation in the constructor is clearly intended to make this impossible. There would be aspects to address in restoring this integrity.

One aspect would be to ensure that the name field is immutable (the Person class currently has no setters and name is private, but the class is clearly a skeleton because it has no usable methods). Alternatively, you could ensure that all changes are controlled by the EnhancedPerson in a way that keeps it valid.

The second aspect, which is more relevant to this question, would be to ensure that any EnhancedPerson object deserialized from a data stream conforms to the validity rules. This can be accomplished by providing a private readObject method. This method allows you to take control of the deserialization process. In the following example, the object is read from the stream using the default mechanism, but then it’s checked to determine if it is valid. If it’s not valid, the exception that’s thrown will cause the deserialization to be abandoned.

private void readObject(ObjectInputStream ois) throws
  IOException,
  ClassNotFoundException {
  ois.defaultReadObject();
  if (name == null || name.length() == 0) {
    throw new InvalidObjectException("Invalid name");
  }
}

Conclusion. The correct answer is option D.

Source: oracle.com

Wednesday, September 27, 2023

Quiz yourself: Serializing a primitive with ObjectOutputStream

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

Primitives? Objects? What should you do?


You need to serialize a long primitive value, and you have been given the following code:

long l = 5L;
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) {
  ... //
}

Which statement is true? Choose one.

A. The code must box the primitive using oos.writeObject(Long.valueOf(l)).
B. The primitive must be included in the outer Java class as an instance variable to be serialized.
C. The serialization should be implemented as oos.writeObject(l).
D. The serialization should be implemented as oos.writeLong(l).
E. The serialization must delegate to the DataOutput class for writing primitives.

Answer. Java serialization provides a way to represent a graph of objects as a byte sequence. This mechanism also supports primitive data directly, as part of objects. Primitive wrapper classes also implement the Serializable interface and can be serialized too. Commonly, the byte sequence that results from serialization is written to disk for long-term storage or is transmitted across a network.

In most cases, you will use the class ObjectOutputStream to create the serialized byte stream. This class provides the writeObject method proposed in option C, and that method readily handles serializing an entire graph of objects. The result can be transparently restored using the ObjectInputStream class. Note, however, that the argument to the writeObject method is an object, not a primitive.

The previous discussion tells you something about option C: If it’s used, autoboxing would occur and you would not serialize the primitive value. Instead, you would have a serialized representation of the wrapper that was created. At this point in analyzing the exam question, it’s probably too early to reject this option, but you must decide whether any other answer is a better match with the proposition of the question—which does, after all, specifically mention that you are serializing a primitive value.

It turns out that ObjectOutputStream implements the java.io.DataOutput interface. That interface defines writeXXX methods for all primitives. As a side note, DataOutput is also implemented by the class java.io.DataOutputStream, but although that class works with primitives and strings, it cannot perform serialization. This capability is described in option D, and it will allow you to use the oos object to serialize the primitive directly. From this you can determine that option D is correct. It’s definitely better than option C, and you can now confidently reject option C as incorrect.

Although you should feel confident in the correctness of option D, you should still verify that the other options are incorrect. In an exam, you might discover another option that seems correct, which would cause you to revisit your reasoning. But if you’re tight on time, you might go with the first answer that appears correct.

Option A is incorrect: The ObjectOutputStream is able to serialize primitives directly, which means that boxing is not necessary. Of course, if your application calls for it, you can box or autobox the primitive, but it’s not necessary. Further, the result will take substantially more space in the resulting byte sequence.

Option B is also incorrect for essentially for the same reason as with options A and C. Certainly primitives that are members of objects are handled correctly, but it’s not necessary to wrap a primitive in such an object just to get it out into the byte sequence.

Option E is incorrect, because, as discussed earlier, java.io.DataOutput is an interface, not a class, as is suggested in the stem. The interface declares the signatures of various methods, including the writeLong method you will use, but writeLong is (and, in fact, all the methods of this interface are) abstract. The implementation is provided by the java.io.ObjectOutputStream class; therefore, delegation in the code is not necessary or possible.

Conclusion. The correct answer is option D.

Source: oracle.com

Friday, September 15, 2023

Quiz yourself: Deserializing objects with readObject

Quiz Yourself, Oracle Java Career, Oracle Java Skill, Oracle Java Jobs, Oracle Java Prep, Oracle Java Preparation, Oracle Java Object

You should know how the readObject method works—and when it won’t compile.

Given the following Person record

record Person(String name) implements Serializable {}

and the following method fragment

try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename))) {
    ... // code here
    System.out.println(person.name());
}

Assume that the try block is completed with necessary catch blocks and the file contains a serialized Person and opens correctly.

The code from which option, when placed where the comment is, properly deserializes the object and allows the rest of the code to work? Choose one.

A. var person = (Person) null;
if (in.readObject() instanceof Person p) {
  person = p;
}
B. var person = in.readObject();

C. Person person = null;
if (in.readObject() instanceof Person) {
  person = in.readObject();
}

D. var person = null;
Object o = in.readObject();
if (o instanceof Person) {
  person = (Person) o;
}

Answer. We will skip over option A for the moment.

Option B would read the serialized object from the file, but the return type of the readObject method is declared as Object; therefore, var will infer the type of the person variable to be Object too. This means that person.name() will not compile. From this you can see that option B is incorrect.

Option C is incorrect for two reasons. The semantics of the code would cause the input stream to be read twice, and the result of the first reading operation would be lost. However, there is a more severe problem; as already mentioned, the return type of readObject is Object, and the following assignment to the person variable (which is declared explicitly as being of type Person in this option) would fail unless an explicit cast were added:

person = in.readObject(); // assignment fails!

The following cast would fix the problem:

person = (Person)in.readObject(); // this could work

Option D is also incorrect. When var is used to declare a local variable (other than a lambda formal parameter), the compiler must be able to decide, unambiguously, the intended type of the variable based on the expression that is assigned for initialization. The null value does not provide type information—it is assignment-compatible with any reference type. So, compilation would fail.

Changing the declaration to the following would allow option D to work:

var person = (Person) null;

Option A is correct. It uses the newer “pattern matching for instanceof” feature that was added (after several previews) in Java 16 as JEP 394. The effect is that if the deserialized object is, in fact, assignment-compatible with the Person type, the variable p is declared and initialized with the reference to that object. The scope of p is such that it’s usable only in the parts of the code where it has definitely been initialized. For example, if there were an else clause on the if statement, p would be out of scope in that else clause.

Conclusion. The correct answer is option A.

Source: oracle.com

Friday, September 8, 2023

Quiz yourself: Collectors, comparators, and type inferencing in Java

Quiz Yourself, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Prep, Oracle Java Preparation, Oracle Java Preparation


Your colleague is working on an application that must find a most-frequently used word in a Stream<String>, and each element of that stream is a single word. The stream is provided by the reference strm.

Which of these pipeline expressions can perform this task? Choose two.

A.

strm.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting())).entrySet().stream().sorted((e1,
e2)-> e2.getValue().compareTo(e1.getValue())).findFirst()

B.

strm.collect(Collectors.groupingBy((String s) -> s, 
Collectors.counting())).entrySet().stream().sorted(
Map.Entry.comparingByValue()).findFirst()

C.

strm.collect(Collectors.groupingBy(a -> a,
Collectors.counting())).entrySet().stream().sorted(
Map.Entry::comparingByValue).findFirst()

D. 

strm.collect(Collectors.groupingBy(Function.identity(), 
Collectors.counting())).entrySet().stream().sorted((e1, 
e2)-> e2.getValue() - e1.getValue()).findFirst()

E.

strm.collect(Collectors.groupingBy(___ -> ___, 
Collectors.counting())).entrySet().stream().max(Comparator.
comparing(e -> e.getValue()))

Answer. This question investigates aspects of the Collectors utilities, ordering using a Comparator, type conversion rules, and type inferencing in Java’s generics system.

All but one of the question’s code fragments take the following four-step approach to the problem, with variations in the implementation details:

Step 1. Take each word and use the groupingBy collector to build a map in which each key is a word from the stream, and the value accumulates the number of occurrences of the word in the stream. The groupingBy method is a factory that builds a collector, and in the form used here it takes two arguments. The first derives a key for the resulting map from the object in the stream. In this case, the object in the stream is the word you want to use as the key, so no change is necessary. The second argument is Collectors.counting(), which is used as a downstream collector that modifies the value stored against the key in the map to be a count of the number of times the key has been seen, instead of being a list of the objects that produced that key.

Step 2. The code then extracts a stream from that map. The Map interface cannot directly provide a Stream, but it provides access to a Set<Map.Entry> using the entrySet method. A Map.Entry<K, V> is a single key-value pair (a tuple of K and V, essentially, although Java does not provide tuples at a language syntax level). A Set does allow drawing a Stream directly, which is the next step.

Step 3. Sort the stream of entries. This must be done in descending order of the value part of the entry since that’s the count of occurrences of the word represented in the key.

Step 4. The reason that descending order is essential is that the final step is to pull the first entry object from the resulting stream using a findFirst method. If the stream were sorted in ascending order, you’d get the least-used word rather than the most-used one. It’s worth noting that if two or more words tie for top place in usage, you’ll get one of them with no control over which one you get. But you can ignore that possibility in this question; notice the specification says, “a most-frequently used word,” rather than “the most-frequently used word.” Therefore, you don’t need to worry about a tie.

Option A is a correct implementation of this approach. The first argument to the groupingBy factory method should be a function that takes the stream object and returns the key. As noted, in this situation the key is the same as the stream object, and Function.identity() is a factory for a Function object that returns its own argument unchanged. The second argument is the counting factory, and that’s exactly what was described above. Next, the sorting is performed using Comparator explicitly coded as a lambda expression. The effect is to compare the value of the two entry objects, but because the code is written as e2 … compareTo … e1, the ordering will be reversed and, therefore, descending.

Option B is incorrect. Although it is syntactically valid, the stream is sorted in ascending order; therefore, it will return an entry with a least-frequently used word. You could correct this code by reversing the ordering of the comparator, as follows:

strm.collect(Collectors.groupingBy((String s) -> s, 
Collectors.counting())).entrySet().stream().sorted(Map.Entry.<String, 
Long>comparingByValue().reversed()).findAny();

Notice the use of <String, Long> before comparingByValue().reversed(). The code fails to compile without this because the Java compiler cannot infer the generic type parameters in this situation. Therefore, this syntax is used to specify the types explicitly.

Another difference in the approach taken by option B, which is valid in isolation, is the replacement of Function.identity() with an explicit lambda: (String s) -> s. This is valid, and in this code has the same effect of returning its argument unchanged. The argument type is redundant, but it is valid because the objects in the stream are of String type.

Option C also changes Function.identity() to an explicit lambda: a -> a. As with option B, this has the same effect and is not a problem.

Looking at the second argument to groupingBy—the downstream collector—you know this must be an object of Collector type. However, Map.Entry::comparingByValue is a method reference; if the code were compilable in this location, it would create an object equivalent to a lambda expression such as x -> Map.Entry.comparingByValue(x). That would be a Function that returns a Comparator. However, Function is not valid at this point, because the code needs an actual Comparator object. From this you can see that option C is incorrect.

Option D is also incorrect. As already mentioned, Collectors.counting() returns a collector that counts the number of occurrences of elements. The total is accumulated as a Long. Later in the hand-coded comparator, Long is subtracted from Long, and this gives a Long result (which could, under appropriate conditions, be autounboxed to a long primitive). However, the return type of the comparator’s compare method must be int, and it’s not legal to convert a Long (or even a long) to an int without an explicit cast. The following code includes the necessary cast and would work correctly:

(e1, e2)-> (int)(e2.getValue() - e1.getValue())

Option E changes two things. First, it uses an explicit lambda instead of Function.identity(). You saw this in two previous examples, but here the variable name is odd. It’s ___ (which is a triple underscore) rather than something more normal such as s or a. It turns out that this is actually a valid identifier in Java. A single underscore is a reserved keyword (since Java 9), but a leading underscore followed by more characters (even if they’re just more underscores) or an underscore embedded in the middle of a name is legal. It’s worth noting that using a triple underscore as a variable name would likely get some raised eyebrows in a code review, so don’t tell anyone we said it’s a good idea. We definitely are not suggesting that it’s anything other than a curiosity!

Another difference in this example is the use of the max terminal operation rather than ordering followed by findFirst. This works correctly and might be more efficient because it doesn’t require the processing of the second stream to build a structure containing references to all the elements; instead, only a reference to the largest element so far needs to be kept. In any case, option E is correct.

Here are three interesting side notes.

First: You can read about the difference between Function.identity() and a -> a in our earlier quiz “Quiz yourself: Mixing and matching Java primitives with generics in a stream.”

Second: In the case of English, the most-frequently used word is generally “the,” but in any given body of text, that might not be the case. Of course, this quiz’s code could be used to analyze text in any language.

Third: The stem of the question mentions that the items listed are expressions. As such they’re not complete Java code but must be used in some larger context, perhaps to provide a value to be assigned to a variable or as an argument to a method invocation. However, a general guide for the exam is that if code is valid as shown but incomplete, you should assume there’s enough supporting code around it to allow it to work if it can do so; you’re being asked solely about the validity of what’s shown, not about what you can’t see. The Java SE 17 Developer exam (1Z0-829) has a section called “Assume the following” under the expandable tab “Review exam topics.” Among other things, it specifies the following:

If sample code does not include package or import statements, and the question does not explicitly refer to these missing statements, then assume that all sample code is in the same package, or import statements exist to support them.

Conclusion. The correct answers are options A and E.

Source: oracle.com

Thursday, August 31, 2023

Quiz yourself: Try-with-resources and PreparedStatement database access

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

The best uses—and some limitations—of try-with-resources


Imagine that your system runs against a JDBC driver and a target database that fully and correctly implement the JDBC specifications, and your system has code that handles database resources properly by using the following try-with-resource statement:

String url = … //
var sql = "SELECT * FROM EMPLOYEE";
try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement ps = conn.prepareStatement(sql);
     ResultSet rs = ps.executeQuery()) {
  while (rs.next()) {
    … //
  }
}

You want to refine the selection so it returns not all rows but only rows that match a certain criteria. Therefore, you changed the SQL code to the following:

var sql = "SELECT * FROM EMPLOYEE WHERE NAME LIKE ?";

Which of the following will work correctly with the new SELECT statement while ensuring that the database resources are handled properly? Choose one.


A.

try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement ps = conn.prepareStatement(sql);
     ps.setString(1, "E%");
     ResultSet rs = ps.executeQuery()) {
  while (rs.next()) {
    … //
  }
}

B.

Connection conn = DriverManager.getConnection(url);
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "E%");
ResultSet rs = ps.executeQuery();
try (conn;ps;rs) {
  while (rs.next()) {
    … //
  }
}

C.

try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement ps = conn.prepareStatement(sql)) {
  ps.setString(1, "E%");
  ResultSet rs = ps.executeQuery();
  while (rs.next()) {
    … //
  }
}

D.

try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement ps = conn.prepareStatement(sql)) {
  ps.setString(1, "E%");
}
try (ResultSet rs = ps.executeQuery()) {
  while (rs.next()) {
    … //
  }
}

Answer. This question investigates the use of the try-with-resources construction and also uses a PreparedStatement.

First, look at the usage of the PreparedStatement. You have modified the SQL statement to the following form:

"SELECT * FROM EMPLOYEE WHERE NAME LIKE ?"

The question mark at the end is a placeholder for a value and, before the statement is executed, a value must be provided for this. The value can be supplied correctly by the method call ps.setString(1, "E%"), which is present in all the answers’ proposed code.

Two further issues remain. One is whether the structure of the code is valid. Another is whether the rather vague requirement, “ensuring that the database resources are handled properly,” is satisfied. This latter stipulation means that all the database resources must be closed reliably by the end of the code.

The try-with-resources structure was added in Java 7 to simplify the reliable closing of resources, which can be a bit messy using only the finally block provided before then. It closes all the resources that are declared inside the parentheses that immediately follow the keyword try. As a side note, the resources are closed in the opposite order in which they are listed inside the parentheses. However, there is a restriction that code placed inside the parentheses must be one of two types of elements, as follows:

◉ An initialized declaration of a final or effectively final variable of a type that implements the interface AutoCloseable.
◉ A simple reference to a final or effectively final variable of a type that implements the interface AutoCloseable that was initialized before the start of the try-with-resources structure. (This syntax feature was added in Java 9.)

From the description above, you can immediately determine that option A presents invalid syntax because it places the call to setString inside the resources block; therefore, option A is incorrect.

In option B, you can see that the syntax is correct. It uses the second syntax option described above; it simply names effectively final resources that were declared before entry into the try block. However, although the code will compile and run, it’s not a safe approach for closure of the resources. If an exception were to arise after some of the resources have been created but before entry to the try structure, no attempt would be made to close the resources. Because of this, option B is incorrect.

Option D is also incorrect. The code deals with three resources: a Connection, a PreparedStatement, and a ResultSet. There are two separate try-with-resource statements: one for the Connection and PreparedStatement and another for the ResultSet. There are two problems with this; one is syntactic, and one is semantic. The syntax problem is that resource variables declared and initialized inside the parentheses of a try-with-resources structure behave as formal parameters. Such variables have a scope that ends with the closing curly brace of the pair that immediately follows the closing parenthesis of the try structure. This is the same as with method formal parameters. Because of this, the ps variable is out of scope before the second try-with-resource begins with the following line:

try (ResultSet rs = ps.executeQuery()) {

Additionally, even if the scope problem didn’t cause the code to fail to compile, it’s semantically incorrect. The issue is that the PreparedStatement ps and the Connection conn that supports it would have been closed at the end of the first try-with-resources and would not work for use in the second try-with-resources.

What about option C? The syntax is valid, and the code properly handles all opened resources. That might seem surprising because the ResultSet is declared in the body of the try block instead of in the resource list inside the parentheses. Because of this, the resource is not automatically closed. However, this is allowed. The documentation for Statement—which is a parent interface of PreparedStatement—explicitly says

Note: When a Statement object is closed, its current ResultSet object, if one exists, is also closed.

Because the PreparedStatement is declared inside the parentheses, it will be automatically closed, and that guarantees that the ResultSet will be closed reliably, too. Therefore, option C is correct.

A couple of side notes are worth raising.

First, what’s that waffle in the opening of this question about “fully and correctly implement the JDBC specifications”? It turns out that not all databases and drivers implement the specification fully and correctly; some actually keep a ResultSet opened after closure of their Statement. Therefore, in practice option C might let you down. A more robust approach would be to use two try-with-resource blocks nested, as follows:

try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement ps = conn.prepareStatement(sql)) {
  ps.setString(1, "E%");
  try (ResultSet rs = ps.executeQuery()) {
    while (rs.next()) {
      … //
    }
  }
}

The second side note is that the items in the resource list inside the parentheses of try-with-resources must be separated using semicolons, but also permitted (but not required) is a trailing semicolon. This is similar to the array literal syntax that allows a trailing comma. Such a feature might seem odd, but it can simplify reordering the elements (since the semicolons just move around with their statements and nothing will be missing). It can also simplify machine-generated code (because the machine can simply tack a semicolon on the end of each resource without having to decide if it’s the last one).

Conclusion. The correct answer is option C.

Source: oracle.com

Friday, August 25, 2023

Quiz yourself: The overloaded submit(…) methods in Java’s ExecutorService

Quiz Yourself, Java’s ExecutorService, Oracle Java Career, Java Skills, Java Jobs, Java Prep, Java Preparation, Java Tutorial and Materials

Know when to use Runnable and Callable in multithreaded code.


Given the following code fragment

00:  ExecutorService es = ...
01:  // es.submit(() -> {;} );
02:  // es.submit(() -> null );
03:  // es.submit(() -> { throw new NullPointerException(); });
04:  // es.submit(() -> { throw new IOException(); });
05:  // es.submit(() ->  new SQLException());

Which line or lines, when uncommented individually, will compile successfully? Choose one.

A. Only line 01
B. Only lines 01 and 02
C. Only lines 01, 02, and 03
D. Only lines 01, 02, 03, and 04
E. All lines will compile successfully

Answer. The java.util.concurrent.ExecutorService has three overloaded submit(...) methods.

  • <T> Future<T> submit(Callable<T> task);
  • Future<?> submit(Runnable task);
  • <T> Future<T> submit(Runnable task, T result);

Each of these methods takes an object that defines a task and returns an object that allows you to interact with that task and, in particular, obtain a result from it after it is completed. In each case, the task is defined by a particular method on the argument object, and that task-defining method is declared in the interface Callable or Runnable, depending on the submit method invoked. Those interfaces have the following forms:

public interface Runnable {
    public abstract void run();
}

public interface Callable<V> {
    V call() throws Exception;
}

Notice that there are two significant differences between them.

  • A Callable returns a value, whereas the Runnable declares a void method.
  • A Callable may throw a checked exception, but a Runnable can throw only an unchecked exception.

Consider the ExecutorService methods listed above in light of this. In the first overloaded submit(…) method, the Future will give access—after the task is completed—to the value of type T that is returned by the Callable or, if the method threw a checked exception, to that exception. This access happens using the get() method of the Future. If the task was completed normally, the get() method typically returns the value returned by the task. If the task threw an exception, the get() method throws an ExecutionException, the cause of which is the exception thrown by the task.

Did you notice the vague wording “the get() method typically returns…” in the description above? The second and third overloaded submit(…) methods both take a Runnable, so no value can be provided by the task. In the case of the second method, the Future returns null if the task is completed normally. By contrast, the Future returned by the third method will return the value passed as the second argument (named result in the signature shown) when Runnable is completed normally.

It’s time to see how the compiler will view each of the proposed tasks—defined by lambda expressions—in the quiz question.

Line 01: () -> {;}

This lambda body does not return any value, so it can implement only Runnable. The method has an empty body and correctly forms a void method. It’s perhaps a little surprising to see the semicolon standing by itself, and certainly that is redundant, but Java allows semicolons to be scattered in source code anywhere that a statement is expected. From this you can see that line 01 is valid and will compile.

Line 02: () -> null

This form creates a Callable because it returns a value. A Runnable must not return anything at all; null is a value and is not compatible with a void return. The essential detail here is that the code compiles; therefore, line 02 is also valid.

In lines 03 and 04, the lambda has a body that consistently throws an exception. A Runnable can throw only unchecked exceptions, but a Callable may throw any exception. This means that line 03 could be either a Runnable or a Callable, but line 04 must be a Callable. Again, however, the essence is that both lines are valid and will compile.

Line 05: () -> new SQLException()

This one is a little surprising in that it returns an exception, rather than throwing an exception. However, exceptions are objects, so the code forms a Callable because it returns a value. Therefore line 05 is valid and will compile.

Since all five lambdas are valid, the correct answer is option E.

Conclusion. The correct answer is option E.

Source: oracle.com

Wednesday, August 16, 2023

Quiz yourself: Using anonymous classes in Java

Quiz Yourself, Java Exam, Java Exam Prep, Java Tutorial and Materials, Java Prep, Java Preparation, Java Certification, Java Guides

How are anonymous classes related to the Liskov substitution principle?


Imagine that you are doing an audit of a third-party desktop Java application that interacts with users’ input and has the following code:

var c = new Control();
c.registerHandler(
  new Handler<Event>() {
    @Override
    void handle(Event e) {
      System.out.println("Event occurred: " + e);
    }
  }
);

Based on the provided code fragment, which statement is true about the Handler type? Choose one.

A. It must be an interface.
B. It must be an abstract class.
C. It can be a concrete class.
D. It can be either a class or an interface.
E. It can be a class, an interface, or an enum.
F. It can be a class, an interface, an enum, or a record.

Answer. In this era in which Java has lambda expressions, it’s not unusual to hear the idea that anonymous classes are irrelevant. However, anonymous inner classes provide capabilities that are not possible with lambda expressions. How is that?

In general, an anonymous class can extend a class, either a concrete or an abstract class, or it can implement an interface. If an abstract type is specialized, all the abstract methods in that type must be implemented. However, an anonymous class can declare only a single parent type, regardless of whether it’s an interface or a class. This limitation derives largely from the syntax, which provides only a single place in the code at which to define the parent type.

The declaration/instantiation of an anonymous class includes a parameter list. If the parent type is a class (either abstract or concrete), that parameter list is passed to the parent class’s constructor and, of course, there must be a matching constructor for that delegation. If the parent type is an interface, the parameter list must be empty.

An anonymous class can override methods of the parent type, implement abstract methods, and define arbitrary new methods and fields, even static ones, though that’s not likely to be useful.

From the description above, you can conclude that an anonymous class is not constrained to either implement an interface or extend an abstract class. Therefore, options A and B are incorrect.

You also know that, in general, an anonymous class can be derived from a concrete or abstract class or from an interface. This seems to make options C and D both look good, though the question requires a single answer. So, you have perhaps guessed there’s a bit more to this question, which we’ll get to in a moment.

Enum types place strict limits on their subtypes: Specifically, any such subtype must itself be an anonymous class declared inside the enum, and any such subtype is implicitly final. An enum that does not declare any anonymous subtypes is itself implicitly final. This means an anonymous class declared in the form shown in the question cannot possibly be a subtype of an enum. Therefore, you can reject both options E and F as incorrect.

Further, record types are always final, which makes option F impossible.

So, how can you choose between options C and D? Turn your attention to interfaces. Any method declared in an interface will default to being public if no explicit modifier is given, and most methods in an interface can be declared explicitly public. Static and concrete instance methods can also be declared as private. Notably, however, no interface method can have any intermediate accessibility—that is, no interface method can have package level, or protected, accessibility.

In addition to the restrictions on interface methods, Java seeks to impose the Liskov substitution principle on overriding or implementing methods. This principle broadly says that if a method substitutes for a method in a parent type, it should not cause any surprises. Putting it another way, the child’s method should be consistent with the declaration of the parent’s method.

Java seeks to enforce this guidance in several ways, and one of them is to prevent an overriding or implementing method from being less accessible than the method it replaces. This means that any method that claims to override an interface method must be public. (Note that you can’t use @Override to override a private method in any situation.)

In this case, however, the method handle(Event e) in the anonymous class has package accessibility. This can be valid only if the method being overridden also has package accessibility, and that tells you that the parent type must be a class and not an interface. That parent class could be either abstract or concrete, but the only option that’s valid is option C, which says the parent can be a concrete class.

So, you can conclude that option C is correct, and option D is incorrect.

Conclusion. The correct answer is option C.

Source: oracle.com

Friday, August 11, 2023

Quiz yourself: Abstract classes and the difference between Java’s super() and this()

Quiz Yourself, Abstract classes, Java Career, Java Skills, Java Jobs, Java Prep, Java Preparation


Given the following two classes

01:  abstract class SupA {
02:    SupA() { this(null); }
03:    SupA(SubA s) {this.init();}
04:    abstract void init();
05:  }
06:  class SubA extends SupA {
07:    void init() {System.out.print("SubA");}
08:  }

Which statement is correct if you try to compile the code and create an instance of SubA? Choose one.

A. Compilation fails at line 02.
B. Compilation fails at line 03.
C. A runtime exception occurs at line 02.
D. A runtime exception occurs at line 03.
E. SubA is printed.
F. SubASubA is printed.

Answer. This question investigates object initialization, overridden method invocation, and the difference between this() and this.

Consider the process of instantiating and initializing an object, and assume that the class and all its parent types are fully loaded and initialized at the point of instantiation.

◉ First, the invocation of new causes the allocation of memory for the entire object, including all the parent elements of which it is made. That memory is also zeroed in this phase.
◉ Next, assuming no errors (such as running out of memory) occur in the first step, control is transferred to the constructor with an argument type sequence that matches, or is compatible with, the actual parameters of the invocation.

In contemporary releases of Java, including Java 17 and later, all constructors start with one of three code elements. First, there is an implicit call to super() with no arguments. This is followed by either an explicit call to super(...), which may take arguments, or by a call to this(...), which, again, may take arguments. Strictly, evaluation of any actual parameters to these calls executes before those delegating calls. (Note that there’s a proposal to allow explicit code to be placed before those calls, provided no reference is made to the uninitialized this object, but that’s for the future.)

The class SupA has two constructors. The one on line 02 delegates to the one on line 03 using this(null), while the one on line 03 has an implicit call to super().

The class SubA has no explicit constructors; therefore, the compiler gives it an implicit constructor, which delegates using super().

From that outline, consider the flow of construction and initialization of an instance of SubA.

First, the invocation new SubA() begins by allocating and zeroing memory for the entire object, including the storage necessary for the SubA, SupA, and Object parts. There are no instance fields in the code you see in this question, but the principle is the same.

Next, the newly allocated object is passed as the implicit this argument into the implicit constructor for SubA. That constructor immediately delegates—using super()—to the zero-argument constructor for SupA. That constructor in turn immediately delegates to the one-argument constructor for SupA on line 03 using the explicit call this(null).

The body of the constructor on line 03 begins with an implicit call to super() that was generated by the compiler. That call passes control up to the zero-argument constructor of java.lang.Object. When control returns from the constructor, any instance initialization on the SupA class would be executed, but of course there is none in this case. So, execution continues with the explicit body of the constructor. The constructor body calls this.init();, which invokes the implementation on line 07 and prints the message SubA. At that point, the constructor on line 03 is finished and control returns to the constructor on line 02, which also is finished since there’s no more code after this(null).

After all the constructors for SupA have finished, control returns to the implicit constructor of SubA. The implicit constructor would then perform any instance initialization called for by the SubA class, but there is none. At this point, the construction and initialization process has been completed.

Notice that in the description above, the message SubA was printed exactly once, which makes option E the correct answer and options A, B, C, D, and F incorrect.

To dive deeper, here are some specific considerations for clarification and additional points.


A call of the form this() (with parentheses) is a call to an overloaded constructor in the same class. Contrast this with the reference this, which is an explicit reference to the current instance of the class. The second form (this without parentheses) is the prefix used explicitly to invoke the init() method. Also note that init() is an ordinary instance method that implements the abstract method declared in SupA; Java does not attach any special meaning to the name init.

The constructor on line 02 passes null to the constructor on line 03. There’s nothing tricky here. If the constructor on line 03 attempted to refer to the object, it would cause a null pointer exception, but because no such reference is made, there is no problem.

The call to this.init() on line 03 is entirely valid and safe. It’s not possible to enter a constructor except via a call to new, and new must be followed by a concrete class name. This in turn means that the object referred to by this on line 03 must have a proper implementation of the init() method.

Creating a new instance of SubA does not cause an instance of SupA to be created, so there is only one instance, and when you call this.<something>, it will be tried on SubA. If this element is missing, it will be looked up for an inherited element in superclasses. In the case of the init() method, it will be directly present in SubA, so it will be called when the this.init(); statement runs.

One more point regarding good coding practice: Although it’s done in this question, it’s a bad idea to call overridable methods during instance initialization, for two reasons.

◉ The code that’s executed might not be what the programmer intended when the parent class was written.
◉ If the invocation occurs during initialization of a parent class but invokes an implementation in a subclass, the subclass implementation might refer to fields in the subclass that have not yet been initialized. This can cause unexpected behavior and commonly causes null pointer exceptions.

Conclusion. The correct answer is option E.

Source: oracle.com

Wednesday, August 2, 2023

Quiz yourself: How Java resolves access to static elements in a type declaration

Quiz Yourself, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Guides, Oracle Java Learning, Oracle Java Tutorial and Materials

Oh no! The situation seems to be complicated for several reasons.


Given the following code

class SuperS {
  public static String msg = "SuperS";
  public static String method() { return "SuperM"; }
}
class SubS extends SuperS {
  private static String msg = "SubS";
  public static String method() { return "SubM"; }
}
class Super {
  static SuperS superS;
  public static void main(String[] args) {
    var v = superS;
    System.out.println(v.msg);
    System.out.println(v.method());
    v = (SubS) v;
    System.out.println(v.msg);
    System.out.println(v.method());
  }
}

What is the result? Choose one.

A. Compilation fails.

B. NullPointerException is thrown at runtime.

C. ClassCastException is thrown at runtime.

D. The following is printed:
SuperS
SuperM
SubS
SubM

E. The following is printed:
SuperS
SuperM
SuperS
SuperM

Answer. This question investigates some aspects of how Java resolves access to static elements in a type. In this question, the situation seems to be complicated for several reasons.

◉ The reference variable v is declared using var rather than an explicit type name such as SuperS.
◉ The qualifying prefix to the static elements is a variable rather than a class name.
◉ The variable v that’s used as a prefix contains a null reference rather than pointing to an actual object. You can see this because the static field superS is not explicitly initialized in the code and, as such, is guaranteed to be initialized to null.
◉ It appears that the public static field msg in the parent class is shadowed by a private field of the same name in the subclass.

Consider the first of those issues. In this question, the var pseudotype is used to request that the compiler infer the type of the variable v from the type of the expression used to initialize v. That expression is the static field superS, which has type SuperS and the value null; therefore, v is also of type SuperS and has the value null. From that point forward, there is no difference in the behavior of the variable v from how it would behave if it had been given an explicit type. In particular, var does not create dynamic typing in the way that occurs in languages such as JavaScript and Python.

From this, you know that this declaration

var v = superS;

is identical in effect to the following explicit form

SuperS v = superS;

and, in this case, it has identical effect to this form

SuperS v = null;

Note the use of a reference variable (v), instead of a class name, as the prefix in an expression referring to a static element. Java borrowed a lot of C++ syntax, and one of those syntax elements is the ability to use an instance expression as a prefix in the way used here. When static methods were added to interfaces in Java 8, this ability was not propagated to that feature, presumably because it creates ambiguous code.

Given this syntax, there’s a tendency to assume that the reference is followed to the actual object, and then the element (field or method) is found in that object (which approximately describes the behavior if the element in question were an instance feature). However, this is not a valid model for the behavior with static elements. Static elements belong to the class, not to any particular instance of that class. The compiler creates code that simply determines the target class and obtains the element from that. Notice there’s nothing like late binding or polymorphism going on in this case. As a side note, there’s also nothing of that sort going on with instance fields; late-binding behavior relates only to overridable instance methods.

This tells you that the value of the prefix object is entirely irrelevant because it plays no part in accessing static elements. That illuminates the remaining pieces of this part of the puzzle: It doesn’t matter if the reference is the null value, because it’s never used.

Also, casting the value to the subtype and reassigning it, as in the following line, is also irrelevant:

v = (SubS) v;

An assignment like that might change the value of the variable (although here it does not), but it cannot change the type of that variable, which is determined entirely by its declaration. Indeed, nothing can change the type of a variable at runtime, even though the cast on the right of the assignment does create a temporary expression that has the cast type.

One more issue to consider is whether a shadowing variable in a subclass can be less accessible than the variable that it shadows, as is the situation with the two msg fields in this example. Perhaps surprisingly, this is permitted.

Why might that be a surprise? Well, broadly, the Liskov substitution principle tells you that substitute elements in a child type should not cause surprises when they are compared with their original elements in a parent type. For this reason, an overriding method in Java cannot be less accessible than the overridden method, cannot declare checked exceptions that are not permitted from the overridden method, and (somewhat simplified) must provide an assignment-compatible return type. However, these are static fields, one shadowing the other, not overriding methods, so they’re not really substitutes, and it turns out that they’re not subject to the same rules.

What’s perhaps odder is that if a static method in a class hides a static method in a parent class, the method in the subclass is subject to these rules even though it’s not really overriding either. But, since both method() methods are declared public in this question, that does not cause a problem here.

From this, remembering that the type of the variable v is SuperS, you can determine that the code will print the text from the SuperS class twice, and it will never print the text from the SubS class. You also know that no errors are reported during compilation or execution. Therefore, the correct answer is option E and options A, B, C, and D are incorrect.

Conclusion. The correct answer is option E.

Source: oracle.com

Wednesday, July 26, 2023

Quiz yourself: Java’s sealed types—true-or-false quiz

Oracle Java, Java Certification, Java Guides, Java Learning, Java Tutorial and Materials

Test your knowledge of valid sealed classes, subclasses, and the permits section.

Which statement is true? Choose one.

A. A sealed type declaration must have a permits section.

B.  A sealed class must have at least one direct subclass.

C. A sealed interface must have at least one direct implementation.

D. A sealed type can be an enum.

E. A sealed type can be a record.

Answer. The goal of a sealed type hierarchy is to have source-level specifications that make it very clear what types are permitted to be assignment-compatible with a given base type.

Clearly, even if you don’t have a sealed type hierarchy, you can find this information if you search all the source files used in your codebase. However, that’s laborious at best, and it’s impractical if you have libraries that came from other teams—or from third parties.

As a broad overview, the sealed type mechanism allows the source for one type to explicitly enumerate all its permitted subtypes. Each of those subtypes in turn is subject to restrictions that are generally intended to keep the entire tree clearly visible from the source code.

One possibility is that a subtype is final (either explicitly or implicitly, which is the case with a record type). In this case, you’ve reached the end of the type hierarchy on this branch of the tree.

Another possibility is to open up the hierarchy and allow arbitrary subtypes. This, to an extent, spoils the point of a sealed hierarchy, but it is permitted because practical considerations might warrant it. This is achieved by marking the type as non-sealed.

A third possibility is that the subtype is an enum. An enum can never be named in an extends clause, which means that the sealed parent type must be an interface. Also, subtypes of an enum can exist only when they are declared as inner classes. As inner classes, they’re in the source code of the enum itself, which makes it very clear what, if any, subtypes exist under this branch of the type tree.

The final option is for the subtype itself to be marked as sealed. In this case, its children can be expressly listed. Also in this case, the subtypes’ children are subject to these rules. Once again, it’s very clear from the source which subtypes are possible.

While it’s usual for a sealed type to enumerate its permitted children with permits, there is one situation in which this can be omitted (note that permits is mandatory otherwise). That situation is when all the child types are declared in the same source file (or compilation unit) as the parent type. This includes the possibility of those types being inner or nested classes within the parent type.

This last possibility tells you that option A is incorrect.

The description above omitted an important detail: The purpose of a sealed type hierarchy is to enumerate clearly the permitted child types. If you want zero children of a given type, that class should be marked as final (and, of course, it must be a concrete class). A sealed type must have at least one subtype. This tells you that option B is correct.

Option C is incorrect. Any sealed type must have some child type, but it’s not necessary for an interface to have a direct implementation; it’s perfectly acceptable for it to have a subinterface. Note that an interface cannot be marked as final, so a subinterface of a sealed interface must be either sealed or non-sealed to be valid.

Although an enum cannot be marked as final, a subtype of an enum must be an inner class. This already makes for tight control of the types that can ever be assignment-compatible with it. Of course, this is why an enum can be declared on a sealed type hierarchy without being labeled either sealed, non-sealed, or final. As noted earlier, the sealed parent type of an enum must be an interface. It’s not useful—and is, in fact, not permitted—for an enum type to be marked as sealed. Hence, option D is incorrect.

Record types are implicitly final; hence, they cannot permit subtypes. You know from the discussion related to option B that at least one subclass is mandatory, so you can deduce that option E is also incorrect.

Don’t forget that it’s entirely correct for a record type to appear in a sealed type hierarchy; it simply must be a leaf node, not the root of the hierarchy. It’s also worth observing that since a record type cannot declare a parent class, all the parent types of a record in a sealed hierarchy, up to and including the root of that hierarchy, must be interfaces.

Conclusion. The correct answer is option B.

Source: oracle.com

Monday, July 24, 2023

Quiz yourself: Comparing Java arrays and interpreting the mismatch method

Quiz Yourself, Java Arrays, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Prep, Oracle Java Preparation

The java.util.Arrays class methods aren’t always intuitive.

Given the following code fragment

int a1[] = { 5, 24, 54, 22 };

int a2[] = { 5, 23, 56, 202 };

int a3[] = { 3, 8, 19, 39, 56 };

System.out.print(Arrays.compare(a1, a2));

System.out.print(Arrays.compare(a3, a2));

System.out.print(Arrays.mismatch(a1, a1));

System.out.print(Arrays.mismatch(a2, a1));

Which output is guaranteed? Choose one.

A. 1-1-11

B. 2-1-12

C. 11-11

D. 2-202

E. None of the above

Answer. This question explores the Java API for the java.util.Arrays class, specifically the compare() and mismatch() methods.

The compare() method compares two arrays lexicographically. That’s a fancy way of saying it compares them in the same way that textual words are ordered in a dictionary. Importantly, that’s not the same way numbers are ordered.

Notice that for words such as Hello and Goodbye, a dictionary will order Goodbye before Hello, because G comes before H in the alphabet. Even though there are more letters in Goodbye, the first difference determines the ordering.

Contrast this with numbers; for example, 90,000 would come before 5,000,000 even though the digit 5 is less than the digit 9. The ordering imposed by Arrays.compare works this way, even when comparing arrays of numbers.

In addition, Arrays.compare checks corresponding pairs of elements at the same positions, starting at index position 0 and checking increasing index positions. As long as pairs of elements compare as equal at the same subscript, it keeps checking. If the comparison process reaches the end of both arrays simultaneously, which can happen only if no differences have been discovered, the arrays are deemed to be equal. If the comparison process reaches the end of one array before the other, but the two are identical up to the end of the shorter array, the longer array is considered to be larger.

By the way, a null array reference is considered to be smaller than any non-null array. Two null array references are considered to be equal.

In the case of this question, the elements of arrays a1 and a2 differ at index 1. At that position, the value in a1 (24) is larger than that in array a2 (23), which makes array a1 larger than array a2.

For the comparison of array a3 with array a2 (note that a3 is the first of the two arguments), the arrays differ at index 0, with array a3 being the smaller.

So, what is the result of the Arrays.compare method when the arrays have differing values at an index, as in this case? To determine that, you must go on a bit of a treasure hunt. The documentation for Arrays.compare(int[], int[]) states the following:

...the lexicographic comparison is the result of comparing two elements, as if by Integer.compare(int, int).

That leads to the documentation for Integer.compare(int, int) which states

Compares two int values numerically. The value returned is identical to what would be returned by: Integer.valueOf(x).compareTo(Integer.valueOf(y))

By following the trail, you’ll see that the documentation of Integer.compareTo(Integer) states that the result will be

...the value 0 if this Integer is equal to the argument Integer; a value less than 0 if this Integer is numerically less than the argument Integer; and a value greater than 0 if this Integer is numerically greater than the argument Integer (signed comparison).

It’s worth noting that this is consistent with the documentation for the compare method in the Comparator interface.

If you create the code for this question and try it, you’ll (almost certainly) find that the output of the comparisons is actually 1, followed by -1, which would make option A look likely to be correct (although the output from the second pair of methods has not been considered yet). But the documentation doesn’t say that the method will return 1, 0, or -1. Rather, it merely says the result will be positive, zero, or negative.

Consequently, you can’t state that the correct answer is A. Rather, the correct answer must be option E.

Although you now know the correct answer, these questions are, of course, intended for learning, rather than for merely seeing if you get the answer right or wrong. So, it’s worth investigating the behavior of the mismatch() method.

The mismatch() method returns the index value of the first point in the arrays where the values of the elements differ. If the arrays are identical in contents and length, the method returns -1. Also, if either array reference is null, a NullPointerException is thrown. (Keep in mind that Java array indexes are zero-based.)

Given these rules, the output of the mismatch operation with both arguments provided as in array a1 must be -1. The output of the mismatch operation with the arguments for array a2 and array a1 will be 1, because the index contains the values 23 and 24. This is consistent with option A.

So, if you run the code, the output will almost certainly match option A, and this would be correct if the question were phrased as “Which output is possible?” However, as noted, a valid JDK or JRE could have a library implementation that returns values other than +1 and -1 from the compare method.

We hope you won’t hate us for this question; precision and avoidance of unsound assumptions can be important in day-to-day programming as well as during exam taking!

Conclusion. The correct answer is option E.

Source: oracle.com