The constructor is easy to confuse during the learning process, and the following code now gives you two easily confusing constructors. The main method calls a constructor, but which one does it call? The output of the program depends on the answer to this question. So what exactly does it print out? Even if it is legal?
public class Confusing {
Private confusing (Object o) {
System.out.println ("Object");
}
Private confusing (double[] darray) {
System.out.println ("double array");
}
public static void Main (string[] args) {
New confusing (null);
}
}
The argument passed to the constructor is an empty object reference, so at first glance it seems that the program should call an overloaded version of the parameter type object and will print out object. arrays, on the other hand, are reference types, so null can also be applied to overloaded versions of type double[]. However, the program prints a double array.
This behavior may seem counterintuitive, but there is a good reason to explain it.The overloaded parsing process for Java is run in two stages。The first stage selects all available and applicable methods or constructors. The second stage selects the most accurate one in the method or constructor selected in the first stage. If one method or constructor can accept any parameter passed to another method or constructor, then we say that the first method lacks accuracy over the second method [JLS 15.12.2.5].
In a program, two constructors are available and can be applied. The constructor confusing (object) can accept any parameter passed to confusing (double[]), so confusing (object) is relatively imprecise. (each double array is an object, but each object is not necessarily a double array.) Therefore, the most accurate constructor is confusing (double[]), which explains why the program produces such output.
The key to understanding this puzzle is that when testing which method or constructor is most accurate, these tests do not use the actual parameters: that is, the arguments that appear in the call. These parameters are only used to determine which overloaded version is applicable. Once the compiler determines which overloaded versions are available and can be applied, it chooses the most accurate version of the overload, at which time only the formal parameter is used: The argument that appears in the declaration.
It is quite unpleasant to choose between multiple overloaded versions in this way. In your API, you should make sure that the client is not going to this extreme. Ideally, you should avoid overloading: take different names for different methods. Of course, sometimes this cannot be achieved, for example, the constructor has no name and therefore cannot be given a different name. However, you can mitigate this problem by setting the constructor to private and providing a public static factory [EJ Item 1]. If the constructor has many parameters, you can use the builder mode [GAMMA95] to reduce the demand for overloaded versions.
This puzzle is a test of your two most classic Java operators: instanceof and transformation.
public class Type1 {
public static void Main (string[] args) {
System.out.println (New Type1 () instanceof String);
}
}
public class Type2 {
public static void Main (String args[]) {
Type2 t2 = (Type2) new Object ();
}
}
The first program, Type1, shows how the instanceof operator behaves when testing an instance of a class to see if it is an instance of an unrelated class. You may expect the program to print out
False After all, the Type2 instance is not an instance of string, so the test should fail, right? No, the instanceof test fails at compile time, we can only get the following error message:
Type2.java:3: inconvertible Types
Found:type2, required:java.lang.String
System.out.println (New Type2 () instanceof String);
The program failed to compile because the instanceof operator has the requirement that if the type of two operands is a class, one must be another subtype [JLS 15.20.2, 15.16, 5.5]. Type2
and string are not each other's subtypes, so the instanceof test results in a compile-time error.This error helps you to be wary of instanceof tests, and they may not be doing what you want them to do.
The second program, TYPE2, shows the behavior of the transformation operator when the static type of the expression to be transformed is a superclass of the transformation type. As with the instanceof operation, if both types in a transition operation are classes, one must be a subtype of the other. Although this transformation is obviously a failure for us, the type system is not strong enough to be able to discern that the runtime type of the expression new Object () cannot be a subtype of Type2. Therefore, the program throws an ClassCastException exception at run time.
Sometimes, for a class, it is very useful to keep track of the number of instances it creates, and its typical implementation is done by incrementing its constructor by a private static domain. In the following program,
The creature class demonstrates this technique, and the creator class drills it to print out the number of creature instances that have been created. So, what does this program print?
public class Creator {
public static void Main (string[] args) {
for (int i = 0; i <; i++)
Creature creature = new Creature ();
System.out.println (creature.numcreated ());
}
}
Class Creature {
private static Long numcreated = 0;
Public creature () {
numcreated++;
}
public static long numcreated () {
return numcreated;
}
}
It's a matter of teasing people. The program looks like it should print 100, but it doesn't print anything because it doesn't compile at all. If you try to compile it, you'll find that the compiler's diagnostic information is basically useless.
a local variable declaration looks like a statement, but technically it is not; it should be a local variable declaration statement (local variable declaration statement) [JLS 14.4]. The Java language specification does not allow a local variable declaration statement to be repeated in a for, while, or do loop as a statement [JLS 14.12-14]. A local variable declaration can only appear directly in a block of statements as a single statement. (A block of statements consists of a pair of curly braces and the statements and declarations contained in this flower-enclosed exhibition.) )
There are two ways to revise this question. The most obvious way to do this is to use this declaration as a block of statements:
for (int i = 0; i <; i++) {
Creature creature = new Creature ();
}
Note, however, that the program does not use the local variable creature. Therefore, it would be more practical to replace the declaration with a constructor that does not have any adornments, which would emphasize that the reference to the newly created object is being discarded:
for (int i = 0; i <; i++)
New creature ();
Whichever of the above changes we make, the program will print out the 100 we expect.
Also note that the Create count policy in this puzzle is not thread-safe. If multiple threads can create objects in parallel, the code that increments the counter and the code that reads the counter should be synchronized:
Thread-safe Creation Counter
Class Creature {
private static long numcreated;
Public creature () {
Synchronized (Creature.class) {
numcreated++;
}
}
public static synchronized Long numcreated () {
return numcreated;
}
}
In Summary, a local variable declaration cannot be used as a repeating statement in a for, while, or do loop, and it can only appear in a block of statements as a single statement. In addition, a variable is used to create an instance of the
When building a count, use a long type instead of a variable of type int to prevent overflow. Finally, if you plan to create instances in multiple threads, either synchronize access to the instance counters or use a counter of type Atomiclong.
Java FAQ Learning Notes (ii)