In January/February 2017 issue of Java Magazine have been published three questions regarding 1Z0-809 Oracle Certified Professional, Java SE 8 Programmer II (OCP 8) exam:
Question 1. Given this code:
public class Scratch { public class Itch { static String type = "Text"; // line n1 } }
Which of the following are true? Choose two.
a. The code compiles successfully.
b. The code would compile if line n1
included the modifier private
.
c. The code would compile if line n1
included the modifier final
.
d. The code would compile if line n1
included the modifier public
.
e. The code would compile if the modifier static
were removed from line n1
.
Question 2. Given this code:
class Ordered implements Comparable<Ordered> { int order; String name; public Ordered(int order, String name) { this.order = order; this.name = name; } public int compareTo(Ordered other) { return this.order - other.order; } }
And this code:
Set<Ordered> so = new TreeSet<>(); so.add(new Ordered(9, "Mélina")); so.add(new Ordered(3, "Sonia")); so.add(new Ordered(9, "Jaquelina")); so.add(new Ordered(11, "Alaïs")); so.forEach(i->System.out.println(i.name));
What is the result? Choose one.
a. Mélina
[nbsp count=2]Sonia
[nbsp count=2]Jaquelina
[nbsp count=2]Alaïs
b. Sonia
[nbsp count=2]Mélina
[nbsp count=2]Jaquelina
[nbsp count=2]Alaïs
c. Sonia
[nbsp count=2]Mélina
[nbsp count=2]Alaïs
d. Sonia
[nbsp count=2]Jaquelina
[nbsp count=2]Alaïs
e. The order of output is platform-dependent.
Question 3. Given the following code fragment:
// line n1 Connection conn = DriverManager.getConnection( "jdbc:derby://localhost:1527/sample; " + "user=app;password=app"); // line n2 Statement statement = conn.createStatement(); // line n3 ResultSet rs = statement.executeQuery( "SELECT * FROM APP.CUSTOMER"); // line n4 while (rs.next()) { String name = rs.getString("NAME"); System.out.printf("%s\n", name); } // line n5
Which releases all the database resources? Choose one.
a. Insert the following:
[nbsp count=2]
At line n1: try (
[nbsp count=2]
At line n3: ){
[nbsp count=2]
At line n5: }
b. Insert the following:
[nbsp count=2]
At line n2: try (
[nbsp count=2]
At line n4: ){
[nbsp count=2]
At line n5: }
c. Insert the following:
[nbsp count=2]
At line n3: try (
[nbsp count=2]
At line n4: ){
[nbsp count=2]
At line n5: }
d. Insert at line n5: rs.close(); statement.close();
- C, E
- C
- A
Explanations
The correct answers are options C and E. This question probes an aspect of your understanding of inner classes. The term inner class is used fairly loosely most of the time, but it actually has a fairly specific meaning in the documentation. An inner class is a nonstatic class declared inside another class. If the class is static, then it’s considered to be a nested class, not an inner class. And there are corner-case rules about inner classes. Section 8.1.3 of the Java Language Specification, “Inner Classes and Enclosing Instances,” states the following: “It is a compile-time error if an inner class declares a member that is explicitly or implicitly static, unless the member is a constant variable.” You might well ask, what is a constant variable? Section 4.12.4 of the specification states that a constant variable is “a final variable of primitive type or type String that is initialized with a constant expression.” Interestingly, the compiler even rejects the value null as the constant expression for this assignment.
These rules mean that the code shown in the question can’t work, because it has an inner class attempting to declare a static variable that doesn’t qualify as a constant variable. As a result, option A must be incorrect. Also, nothing in the documentation suggests that this rule varies based on accessibility, so options B and D are incorrect, too.
That leaves only options C and E as the two correct answers. I should explain why these two are correct, not simply accept the elimination of all the others (even if doing that would have been sufficient for Sherlock Holmes).
Considering option C, if you make the declaration final, you actually have achieved a constant variable. Recall that the definition of a constant variable required a primitive or String, that the variable be final, and that it be initialized with a constant expression. I should justify the assertion that you have a constant expression. Section 15.28 of the specification, “Constant Expressions,” describes the criteria for this, and one of the first items in a fairly long list is the String literal. So, option C is correct.
Option E actually describes the case where the field is reverted to an instance field rather than a static one. There are no restrictions on a regular instance field in an inner class, so option E is also correct.
There’s just one thing left to consider, and that is why this rule exists. It probably seems a little arbitrary. I don’t have insight into the minds of those who put this together, but I do have an explanation that makes reasonable sense and might serve to make the rule seem sensible—and, therefore, easier to remember—even if it’s not actually the logic that led to the rule.
With an inner class—that is, a nonstatic inner class—it’s as if the class itself is a member of the enclosing instance. That would require that every new outer instance create a new inner class, just like a new instance creates a new copy of every field. In this case, the inner class is based on the same
source code as all the other inner classes. Each inner class would have its own version of any static members, but how would you refer to those static members? You’d need to distinguish which class you’re referring to when you want to use one of these fields. That could be complex, ugly, error-prone, and—as history has shown—unnecessary. (Java has survived with this restriction since the inception of inner classes in Java 1.1.)
I’ll reiterate that this perspective might not be what the designers had in mind, but they did call these things inner classes and not inner objects. Either way, I hope the mental model serves to help you remember that statics are not permitted in inner classes. Nested classes, however, have no such restriction.
The correct answer is option C. This question investigates the behavior of sets in the Java APIs. The most fundamental behavior of a set is that it does not permit duplicate entries. In the API documentation for the Set interface, this requirement is expressed in terms of the equals method: specifically, that no two non-null entries may return true when one is tested against the other using the equals method, and also that at most one null item is permitted. To do this, before actually adding a new item, the set must test to see if the offered item is already in the set. A simple check of every item in turn would be very time-consuming, particularly as the number of items in the set grows. Therefore, the concrete implementations provided by Java seek to improve the speed of this check.
In TreeSet, the items are added to the set in order, and this means that the set can use a binary search to locate an item (or determine that it’s not present). A binary search is much more efficient, particularly as the number of items in the set grows. However, in the example in the question, the ordering is determined solely by the int field called order, and this means that the two objects with order equal to 9 are at the same point in the ordering, even though they have clearly different values. The Java documentation refers to this as “ordering that is inconsistent with equals.” The documentation for TreeSet warns: “Note that the ordering maintained by a set. . .must be consistent with equals if it is to correctly implement the Set interface.”
The effect is that TreeSet treats those two objects that have the value 9 in the order field as being equal (in fact, the implementation of TreeSet never uses the equals method when considering the items in the set). As a result, the second item with order equal to 9 is considered a duplicate. In this
particular example, there is no override of the equals method, which strictly means that every object is considered unique, and even objects created with identical field values should properly be permitted to be simultaneous set members.
Because of this, the example considers that the two items with the names Mélina and Jaquelina are duplicates. As a result, they cannot both be in the set at the same time. Therefore, options A and B must be false.
The next consideration is exactly how a Set behaves when presented with a duplicate. Does it reject the new item or replace the existing item? This information allows you to decide if option C or option D correctly defines the contents of the set. This consideration is resolved by the documentation for the add method of the Set interface, which states: “Adds the specified element to this set if it is not already present. . . . If this set already contains the element, the call leaves the set unchanged.”
Because of this, you know that the first of the two items that have the same value for order will remain in the set, and the second will be rejected. Therefore, Mélina remains, and Jaquelina does not make it into the set. This means that option C is possible, but option D must be wrong.
Finally, option E suggests that the order might not be predictable. This is an interesting issue. On one hand, a Set differs from a List in that it does not honor the order in which items are added. But, the reason that no such guarantee is offered is that the Set implementation is at liberty to use an internal order that speeds up the search for duplicates. In the case of TreeSet, the order will be the order specified for use in the tree-building mechanism. In this example, that’s simply the order defined by the compareTo method of the Ordered class. At this point, you could reasonably object to a question that appears to depend on an implementation detail as the underpinning of the correct answer. However, TreeSet also implements another interface, NavigableSet, that makes the order part of the public contract. Further, the documentation for TreeSet states that in a NavigableSet implementation, the elements are ordered using their natural ordering or by a comparator provided at set creation time, depending on which constructor is used.
As a result, the order will not vary between implementations and option E is incorrect while option C is correct.
As a final observation, Java’s API documentation recommends that whenever possible, natural ordering should be “consistent with equals.” The API documentation for Comparable states the following: “It is strongly recommended (though not required) that natural orderings be consistent
with equals. This is because sorted sets (and sorted maps) without explicit comparators behave ‘strangely’ when they are used with elements (or keys) whose natural ordering is inconsistent with equals. In particular, such a sorted set (or sorted map) violates the general contract for set (or map),
which is defined in terms of the equals method.” Notice that the documentation explicitly calls out the weirdness this question has been investigating.
While this is a reasonable goal for the Comparable interface and the “natural ordering,” the whole point of the Comparator interface is to permit the expression of multiple different orderings for a class. This is important, because I might, for example, want a list of students in grade order or, if I’m the basketball coach, in height order. Similarly, if I’m the accountant, I might want students in order of the amount of money each owes the school. It’s not possible to have multiple orders and expect them all to be consistent with equals. However, that’s generally still considered to be a good thing
if it’s possible, as the documentation for Comparator notes: “Caution should be exercised when using a comparator capable of imposing an ordering inconsistent with equals to order a sorted set (or sorted map).”
Mostly, however, it’s important to be aware of the wrinkles that arise when order and equality are inconsistent.
The correct answer is option A. Proper release of nonmemory resources is often crucial to the correct long-term running of any server-type program. Database connections are certainly important resources requiring proper treatment; and in Java 7, the JDBC API was extended to make good use of the try-with-resources feature. In particular, Connection, Statement, and ResultSet all implement
AutoCloseable.
It’s probably tempting to guess that option D, which doesn’t use try-with-resources, is incorrect, but you can’t reject it based on not liking it. Closer inspection, however, shows that this option does not close the main database connection. There’s no mandate that absolutely requires that a connection be used once and then released, but the question specifically asks which option releases all the resources. Because of this, option D can properly be rejected because it leaves the connection open.
The other three options all use the try-with-resources mechanism but auto-close different resources. Option A closes the Connection and the Statement but not the ResultSet. Option B closes the Statement and the ResultSet but not the Connection. Option C closes the ResultSet alone. Is it possible that none of these works fully correctly, according to the requirements of the question? Well, it turns out that the specification of these behaviors helps out. If you look at the API documentation for the Connection.close() method, you can see that it says the method “releases this Connection object’s database and JDBC resources immediately.” This suggests that if you auto-close the Connection alone, you have fulfilled the requirements of the question. However, there’s no such option.
Option A does, however, close both the Connection and the Statement. These will close in reverse order, so the Statement is closed first and then the Connection. Regardless, the effect is that all resources will be closed. In fact, there’s a comment about the Statement.close() method that mirrors the one for the Connection.close() method: “Releases this Statement object’s database and JDBC resources immediately. . . . It is generally good practice to release resources as soon as you are finished with them to avoid tying up database resources.” This tells you that option A achieves the desired effect, even though it does not explicitly close the ResultSet.
The auto-closing of “subresources” can be significant in this type of coding. Imagine that you’ve prepared a result set containing the data you need, and then you decide to be a good citizen and release the unneeded Statement and Connection. This, unfortunately, would close your ResultSet, too, taking your data away—potentially before you have finished using it.