Today I'm reviewing my reading notes about scala implicit and experiment on some sample project to furthur understand the mechnism.
The second rule of implicit states that the implicit function applicable should be a single identifier or some one defined in the "Source" or "Target" companion object.
So from the statements, we can see that if the implicit function exists in the "Source" or "Target" companion object, it's not necessary to be a single identifier. Below codes demonstrated it:
Source.scala
package sean.implicits
class Source (val name : String)
Target.scala
package sean.implicits
class Target (val s : Source) {
def sourceName = s.name
}
Running Test.scala will print "Test" in the console. Here the implicit function is defined in the companion object of class Target which is the expected parameter type of function p.
However I found that "Source" and "Target" is with respect to the situation when "a specific type is expected". For the situation when a non-existed method is invoked, compiler doesn't expect any specific target type, instead , it's just looking for a type that has the corresponding method. So in that case, there is only "Source" class but "Target" class. e.g. :
Source.scala
package sean.implicits
class Source (val name : String)
Target.scala
package sean.implicits
class Target (val s : Source) {
def sourceName = s.name
}
To compile the Test.scala above, you will get compilation error because here compiler is looking for a type that has the method "sourceName" , it's not necessary to be class "Target" , so the object Target won't be searched for implicit functions. After moving the implicit function source2Target from object Target to object Source, you are able to compile the Test.scala and running it will print "Test". So here the method receiver is new Source("Test"), the class Source is the "Source" class, its companion object will be searched for implicit functions.
One more thing come to my mind is that if we have a method call on the receiver while the method name is correct while the argument type is incorrect. Will the compiler first try to convert the argument to correct type or it will try to convert the method receiver to a type with the matching method signaure? Below example illustrates how it works :
Source.scala
package sean.implicits
class Source (val name : String) {
def show(t : Target) = println(name)
}
object Test extends App{
new Source("Source") show "Target"
}
Here class Source has a method show whose parameter type is Target and it will print the name of the method receiver. while class Target also has a method show but its parameter type is String and it will print the passed string. When invoking show on object of type Source and passing it a String as argument, compiler will first try to look for an implicit function to convert the argument to the correct type, thus implicit function string2Target was applied. Running Test.scala will print "Source". However if we remove the implicit function string2Target above, the implicit function source2Target will be applied. Running the Test.scala will print "Target".
Also, I was thinking about why case class can't be implicit. And in the Scala documentation below :
It's even required that "There may not be any method, member or object in scope with the same name as the implicit class." However such statement is not precise and the example codes list there is not true. Below codes :
object Test extends App{
def im = println("ll")
implicit class im(i:Int)
5 : im
}
or below codes :
object Test extends App{
val im = 3
implicit class im(i:Int)
5 : im
}
and below codes:
object Test extends App{
obj im
implicit class im(i:Int)
5 : im
}
can work correctly.
The reason why case class can't be implicit was found in below post:
https://issues.scala-lang.org/browse/SI-6227
for example if you have below codes:
implicit case class im(i:Int)
Then the scala compiler will generate an implicit constructor im : (Int) => im and an implicit companion object im with method apply : (Int) => im. So the object im itself is an implicit function. So in the same scope, we have two implicit function that can convert Int to im which is ambiguous.
So below codes :
object Test extends App{
def im(i :Int) = i //actually the return type doesn't matter , it's not part of the method signature
implicit class im(i:Int)
5 : im
}
and below codes :
object Test extends App{
object im {
def apply(i:Int) = new im(i)
}
implicit class im(i:Int)
5 : im
}
won't compile. The first piece of codes have two functions with same signature im : (Int) defined in the same scope. The compiler will complain : method im is defined twice
And the second piece of codes have one implicit constructor im : (Int) => im and one object im with method apply : (Int) => im. The compiler will complain :
ambiguous reference to overloaded definition, both method im in object Test of type (i: Int)sean.implicits.Test.im and object im in object Test of type sean.implicits.Test.im.type match argument types (Int) and expected result type sean.implicits.Test.im
What I don't quite understand is that here object im is not implicit, why it will be looked up for implicit conversion?
1. Implicit conversions are often helpful for working with two bodies of software that were developed without each other in mind. Each library has its own way to encode a concept that is essentially the same thing. Implicit conversions help by reducing the number of explicit conversions that are needed from one type to another.
2. Implicit definitions are those that the compiler is allowed to insert into a program in order to fix any of its type errors. Implicit conversions are governed by the following general rules:
Marking rule: Only definitions marked implicit are available. You can use implicit to mark any variable, function, or object definition. Variables and singleton objects marked implicit can be used as implicit parameters.
Scope rule: An inserted implicit conversion must be in scope as a single identifier, or be associated with the source or target type of the conversion. If you want to make someVariable.convert available as an implicit, you would need to import it, which would make it available as a single identifier. The compiler will also look for implicit definitions in the companion object of the source or expected target types of the conversion. For example, if you're attempting to pass a Dollar object to a method that takes a Euro, the source type is Dollar and the target type is Euro. You could, therefore, package an implicit conversion from Dollar to Euro in the companion object of either class, Dollar or Euro.
One-at-a-time rule: Only one implicit is inserted. The compiler will never rewrite x + y to convert1(convert2(x)) + y. However, it's possible to circumvent this restriction by having implicits take implicit parameters.
Explicits-first rule: Whenever code type checks as it is written, no implicits are attempted. You can always replace implicit identifiers by explicit ones, thus making the code longer but with less apparent ambiguity.
3. There are three places implicits are used in the language: conversions to an expected type, conversions of the receiver of a method, and implicit parameters.
4. Implicit conversions to an expected type let you use one type in a context where a different type is expected. The rule is simple. Whenever the compiler sees an X, but needs a Y, it will look for an implicit function that converts X to Y.
5. The scala.Predef object, which is implicitly imported into every Scala program, defines implicit conversions that convert "smaller" numeric types to "larger" ones.
6. Implicit conversions also apply to the receiver of a method call, the object on which the method is invoked. This kind of implicit conversion has two main uses. First, receiver conversions allow smoother integration of a new class into an existing class hierarchy. And second, they support writing domain-specific languages (DSLs) within the language.
7. -> is a method of the class ArrowAssoc, a class defined inside the standard Scala preamble (scala.Predef). The preamble also defines an implicit conversion from Any to ArrowAssoc:
package scala
object Predef {
class ArrowAssoc[A](x: A) {
def -> [B](y: B): Tuple2[A, B] = Tuple2(x, y)
}
implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x)
...
}
8. An implicit class is a class that is preceded by the implicit keyword. For any such class, the compiler generates an implicit conversion from the class's constructor parameter to the class itself. An implicit class cannot be a case class, and its constructor must have exactly one parameter. Also, an implicit class must be located within some other object, class, or trait.
9. The compiler will sometimes replace someCall(a) with someCall(a)(b), or new SomeClass(a) with new SomeClass(a)(b), thereby adding a missing parameter list to complete a function call. It is the entire last curried parameter list that's supplied, not just the last parameter. Every argument you provide as the default values of the parameters should be marked implicit and the whole last parameter list should be marked as implicit. Note that the implicit keyword applies to an entire parameter list. The compiler selects implicit parameters by matching types of parameters against types of values in scope, implicit parameters usually have "rare" or "special" enough types that accidental matches are unlikely.
10. The maxListOrdering function below is an example of an implicit parameter used to provide more information about a type mentioned explicitly in an earlier parameter list. To be specific, the implicit parameter ordering, of type Ordering[T], provides more information about type T—in this case, how to order Ts. Type T is mentioned in List[T], the type of parameter elements, which appears in the earlier parameter list. Because elements must always be provided explicitly in any invocation of maxListOrdering, the compiler will know T at compile time and can therefore determine whether an implicit definition of type Ordering[T] is available. If so, it can pass in the second parameter list, ordering, implicitly:
def maxListOrdering[T](elements: List[T])(implicit ordering: Ordering[T]): T
This pattern is so common that the standard Scala library provides implicit "ordering" methods for many common types.
11. When you use implicit on a parameter, not only will the compiler try to supply that parameter with an implicit value, but the compiler will also use that parameter as an available implicit in the body of the method.
12. The following method is defined in the standard library:
def implicitly[T](implicit t: T) = t
The effect of calling implicitly[Foo] is that the compiler will look for an implicit definition of type Foo. It will then call the implicitly method with that object, which in turn returns the object right back. Thus you can write implicitly[Foo] whenever you want to find an implicit object of type Foo in the current scope.
13. Instead of below function definition:
def maxList[T](elements: List[T])(implicit ordering: Ordering[T]) : T
Scala lets you leave out the name of this parameter and shorten the method header by using a context bound. Using a context bound, you would write the signature of maxList as below :
def maxList[T : Ordering](elements: List[T]): T
The syntax [T : Ordering] is a context bound, and it does two things. First, it introduces a type parameter T as normal. Second, it adds an implicit parameter of type Ordering[T]. When using a context bound you don't know what the implicit parameter will be called. It's usually used to provide an implicit parameter for some function call within maxList. A context bound is quite flexible. It allows you to use code that requires orderings—or any other property of a type—without having to change the definition of that type.
14. If multiple implicit conversions are in scope and each would work, Scala refuses to insert a conversion in such a case. If one of the available conversions is strictly more specific than the others, then the compiler will choose the more specific one. To be more precise, one implicit conversion is more specific than another if one of the following applies:
The argument type of the former is a subtype of the latter's.
Both conversions are methods, and the enclosing class of the former extends the enclosing class of the latter.
Method overloading has the same relaxation. If one of the available foo methods takes a String while the other takes an Any, then the String version is chosen for foo(null) call.
15. When you are debugging a program, it can sometimes help to see what implicit conversions the compiler is inserting. The -Xprint:typer option to the compiler is useful for this. If you run scalac with this option, the compiler will show you what your code looks like after all implicit conversions have been added by the type checker.
1. Methods are never implemented in the interface. You cannot put instance fields or static methods in an interface (because you can't implement it and its implementation cann't override it due to static), while you can supply constants in them. Methods in an interface are automatically public, fields are always publicstaticfinal. As of Java SE 8, you are allowed to add static methods to interfaces.
2. Double.compare method returns a negative if the first argument is less than the second argument, 0 if they are equal, and a positive value otherwise.
3. If you know that the IDs are not negative or that their absolute value is at most (Integer.MAX_VALUE - 1) / 2, you are safe to use integer substraction in the compareTo method. Of course, the subtraction trick doesn’t work for floating-point numbers. Subtraction of two floating-point numbers can round to 0 if they are close enough.
4. If you flip the parameters of compareTo, the sign (but not necessarily the actual value) of the result must also flip. This implies that x.compareTo(y) must throw an exception if y.compareTo(x) throws an exception. The documentation of the Comparable interface suggests that the compareTo method should be compatible with the equals method. That is, x.compareTo(y) should be zero exactly when x.equals(y).
5. If subclasses have different notions of comparison, then you should outlaw comparison of objects that belong to different classes. Each compareTo method should start out with the test:
if (getClass() != other.getClass()) throw new ClassCastException();
If there is a common algorithm for comparing subclass objects, simply provide a single compareTo method in the superclass and declare it as final.
6. You can use instanceof to check whether an object implements an interface.
7. Each class can have only one superclass but implement multiple interfaces.
8. The clone method is a protected method of Object, which means that your code cannot simply call it. The rules for protected access make sure that a subclass can call a protected clone method only to clone its own objects.
9. Think about the way in which the Object class can implement clone. It knows nothing about the object at all, so it can make only a field-by-field copy. If all data fields in the object are numbers or other basic types, copying the fields is just fine. But if the object contains references to subobjects, then copying the field gives you another reference to the same subobject, so the original and the cloned objects still share some information. The default cloning operation is “shallow”.—it doesn’t clone objects that are referenced inside other objects.
10. If subobjects are mutable, and you must redefine the clone method to make a deep copy that clones the subobjects as well. A class must implement the Cloneable interface and redefine the clone method to call super.clone() and assign it the public access modifier to allow objects to be cloned by any method.
11. The Cloneable interface has no method and merely serves as a tag, indicating that the class designer understands the cloning process. Objects are so paranoid about cloning that they generate a checked exception (CloneNotSupportedException) if an object requests cloning but does not implement that interface.
12. There is no problem not to override the clone method in the subclass if superclass provides the implementation of clone if the fields of subclass are all primitive types.
13. All array types have a clone method that is public, not protected. You can use it to make a new array that contains copies of all elements (elements are not deeply copied).
14. A common pattern in programming is the callback pattern. In this pattern, you want to specify the action that should occur whenever a particular event happens. Consider using an interface in Java for callback function.
15. There are three reasons for inner classes:
a) Inner class methods can access the data from the scope in which they are defined—including the data that would otherwise be private.
b) Inner classes can be hidden from other classes in the same package.
c) Anonymous inner classes are handy when you want to define callbacks without writing a lot of code.
16. An object that comes from an inner class has an implicit reference to the outer class object that instantiated it. Through this pointer, it gains access to the total state of the outer object. static inner classes do not have this added pointer.
17. A method could refer to the data fields of the object invoking the method. An inner class method gets to access both its own data fields and those of the outer object creating it. For this to work, an object of an inner class always gets an implicit reference to the object that created it.
18. The outer class reference is set in the constructor. The compiler modifies all inner class constructors, adding a parameter for the outer class reference.
19. Only inner classes can be private. Regular classes always have either package or public visibility. Any static fields declared in an non-static inner class must be final and a compile-time constant. A non-static inner class cannot have static methods or initializers.
20. The proper syntax for the outer reference is OuterClass.this.
21. You can invoke the inner object constructor more explicitly using:
22. You refer to an inner class as OuterClass.InnerClass when it occurs outside the scope of the outer class.
23. Inner classes are a phenomenon of the compiler, not the virtual machine. Inner classes are translated into regular class files with $ (dollar signs) delimiting outer and inner class names, and the virtual machine does not have any special knowledge about them.
24. When you run javap -private OuterClass$InnerClass, you can see the compiler has generated an additional final instance field, this$0, for the reference to the outer class. You can also see the OuterClass parameter for the constructor.
25. Inner classes are genuinely more powerful than regular classes because they have more access privileges. When you run javap -package OuterClass, you can see the compiler has generated a package access static method (with name like access$0 and accept outer class instance as parameter) to access the private field of outer class if inner class contains codes to visit it. And the compiler has replaced the access to the outer class field in inner class with that generated method.
26. If an inner class accesses a private data field, then it is possible to access that data field through other classes added to the package of the outer class, but to do so requires skill and determination. A programmer cannot accidentally obtain access but must intentionally build or modify a class file for that purpose.
27. For private inner class, there are no private classes in the virtual machine, so the compiler produces the next best thing: a package-visible class with a private constructor:
private OuterClass$InnerClass(OuterClass)
And there is a second package-visible constructor ( for
OuterClass$InnerClass (OuterClass, OuterClass$1)
And the compiler translates the call of outerObj.new InnerClass() to new OuterClass$InnerClass(outerObj, null)
28. You can define the class locally in a single method. Local classes are never declared with an access specifier (public or private). Their scope is always restricted to the block in which they are declared.
29. Local inner classes can not only access the fields of their outer classes but also even access local variables. However, those local variables must be effectively final. That means, they may never change once they have been assigned. Before Java SE 8 they must be declared as final.
30. The local inner class must have copied the local variable of its declaring method as an instance field, before the local variable value went away when the method exits. The compiler synthesizes the name OuterClass$1InnerClass for the local inner class. The compiler detects access of local variables, makes matching instance fields for each one of them, and copies the local variables into the constructor so that the instance fields can be initialized. When you use javap to peek at the local inner class, you can see the compiler has generated an additional final field (with name like var$LocalVariableName) to store the local variable it accesses and added one more parameter to the constructor to pass in the local variable value. The value of the local variable will be passed to the local inner class constructor when it's instantiated. Since the methods of a local class can refer only to local variables that are effectively final, it is guaranteed that the local variable and the copy made inside the local class will always have the same value.
31. You don’t have to initialize a final variable when you define it. A final variable that isn’t initialized when it is defined is often called a blank final variable.
32. If you do want to change the local variable in the local inner class, you can use an array of length 1. The array variable is still effectively final, but that merely means that you can’t have it refer to a different array. You are free to mutate the array elements.
33. If you want to make only a single object of a local inner class, you don’t even need to give the class a name. Such a class is called an anonymous inner class. The syntax is:
new SuperType(construction parameters)
{
inner class methods and data
}
Here, SuperType can be an interface (the inner class implements it ) or a class ( the inner class extends it ).
34. The anonymous inner class doesn’t have a name thus it cannot have a constructor, Instead, the construction parameters are given to the superclass constructor. You can initialize it via instance initializer block.
35. To instantiate and initialize an anonymous ArrayList :
new ArrayList<String>() {{ add("Harry"); add("Tony"); }}
36. getClass() fails in a static method, Use the following expression instead:
new Object(){}.getClass().getEnclosingClass()
Here, new Object(){} makes an object of an anonymous subclass of Object, and getEnclosingClass gets its enclosing class—that is, the class containing the static method.
37. If you want to use an inner class simply to hide one class inside another—but you don’t need the inner class to have a reference to the outer class object. You can suppress the generation of that reference by declaring the inner class static. A static inner class is exactly like any other inner class, except that an object of a static inner class does not have a reference to the outer class object that generated it.
38. Inner classes that are declared inside an interface are automatically static and public.
39. You can use a proxy to create, at runtime, new classes that implement a given set of interfaces. Proxies are only necessary when you don’t yet know at compile time which interfaces you need to implement. To construct an instance of an actual class, you can simply use the newInstance method or use reflection to find a constructor. But you can’t instantiate an interface. You need to define a new class in a running program. To overcome this problem, some programs generate code, place it into a file, invoke the compiler, and then load the resulting class file. Naturally, this is slow, and it also requires deployment of the compiler together with the program.
40. The proxy class can create brand-new classes at runtime. Such a proxy class implements the interfaces that you specify. In particular, the proxy class has the following methods:
a) All methods required by the specified interfaces
b) All methods defined in the Object class
41. An invocation handler is an object of any class that implements the InvocationHandler interface. That interface has a single method:
Whenever a method is called on the proxy object, the invoke method of the invocation handler gets called, with the Method object and parameters of the original call. The invocation handler must then figure out how to handle the call.
42. To create a proxy object, use the newProxyInstance method of the Proxy class. The method has three parameters:
a) A class loader. As part of the Java security model, different class loaders can be used for system classes, classes that are downloaded from the Internet, and so on. We can specify null to use the default class loader.
b) An array of Class objects, one for each interface to be implemented.
c) An invocation handler.
43. Proxies can be used for many purposes, such as
a) Routing method calls to remote servers
b) Associating user interface events with actions in a running program
c) Tracing method calls for debugging purposes
44. The proxy objects belong to a class that is defined at runtime. It has a name such as $Proxy0.
45. All proxy classes extend the class Proxy. A proxy class has only one instance field—the invocation handler, which is defined in the Proxy superclass. Any additional data required to carry out the proxy objects’ tasks must be stored in the invocation handler. All proxy classes override the toString, equals, and hashCode methods of the Object class and these methods simply call invoke on the invocation handler.
46. There is only one proxy class for a particular class loader and ordered set of interfaces. That is, if you call the newProxyInstance method twice with the same class loader and interface array, you get two objects of the same class. You can also obtain that class with the getProxyClass method of the Proxy class.
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException
47. A proxy class is always public and final. If all interfaces that the proxy class implements are public, the proxy class does not belong to any particular package. Otherwise, all non-public interfaces must belong to the same package, and the proxy class will also belong to that package.
48. You can test whether a particular Class object represents a proxy class by calling the isProxyClass method of the Proxy class..
49. The rules for resolving method conflicts in multiple inheritances are :
Superclasses win. If a superclass provides a concrete method, default methods with the same name and parameter types are simply ignored.
Interfaces clash. If a superinterface provides a default method, and another interface supplies a method with the same name and parameter types (default or not), then you must resolve the conflict by overriding that method.
50. Use SuperInterface.super.method() to call default method in super interface. super is always referencing super class ( to be backward compatible).
51. You can never make a default method that redefines one of the methods in the Object class.
52. A lambda expression is a block of code that you can pass around so it can be executed later, once or multiple times.
53. The form of lambda expressions in Java is : parameters, the -> arrow, and an expression. If the code carries out a computation that doesn’t fit in a single expression, enclose it in {} with explicit return statements. If the parameter types of a lambda expression can be inferred, you can omit them. If a method has a single parameter with inferred type, you can even omit the parentheses. You never specify the result type of a lambda expression. It is always inferred from context.
54. It is illegal for a lambda expression to return a value in some branches but not in others.
55. You can supply a lambda expression whenever an object of an interface with a single abstract method is expected. Such an interface is called a functional interface.
56. It has always been possible for an interface to redeclare methods from the Object class such as toString or clone, and these declarations do not make the methods abstract.
57. The Java designers decided to stick with the familiar concept of interfaces instead of adding function types to the language. Conversion to a functional interface is the only thing that you can do with a lambda expression in Java. You can’t even assign a lambda expression to a variable of type Object—Object is not a functional interface.
58. The ArrayList class has a removeIf method whose parameter is a Predicate.
59. The expression System.out::println is a method reference that is equivalent to the lambda expression x -> System.out.println(x).
60. The :: operator in method reference separates the method name from the name of an object or class. There are three principal cases:
object::instanceMethod
Class::staticMethod
Class::instanceMethod
In the first two cases, the method reference is equivalent to a lambda expression that supplies the parameters of the method. In the third case, the first parameter becomes the target of the method.
When there are multiple overloaded methods with the same name, the compiler will try to find from the context which one you mean. Just like lambda expressions, method references don’t live in isolation. They are always turned into instances of functional interfaces.
61. You can capture the this parameter in a method reference.(this::equals) It is also valid to use super. The method reference super::instanceMethod uses this as the target and invokes the superclass version of the given method.
62. Constructor references are just like method references, except that the name of the method is new.
63. You can form constructor references with array types : int[]::new is a constructor reference with one parameter: the length of the array. Array constructor references are useful to overcome a limitation of Java. It is not possible to construct an array of a generic type T. The expression new T[n] is an error since it would be erased to new Object[n].
64. A lambda expression has three ingredients:
A block of code
Parameters
Values for the free variables, that is, the variables that are not parameters and not defined inside the code
The data structure representing the lambda expression must store the values for the free variables, We say that such values have been captured by the lambda expression.
65. The technical term for a block of code together with the values of the free variables is a closure.
66. In a lambda expression, you can only reference variables whose value doesn’t change (either inside the lambda or outside the lambda). The rule is that any captured variable in a lambda expression must be effectively final. An effectively final variable is a variable that is never assigned a new value after it has been initialized.
67. The body of a lambda expression has the same scope as a nested block. The same rules for name conflicts and shadowing apply. It is illegal to declare a parameter or a local variable in the lambda that has the same name as a local variable outside the lambda.
68. The point of using lambdas is deferred execution.
69. The following table lists the most important functional interfaces that are provided in the Java API:
70. Below table lists the 34 available specializations for primitive types int, long, and double. It is a good idea to use these specializations to reduce autoboxing :
p, q is int, long, double; P, Q is Int, Long, Double.
71. Most of the standard functional interfaces have non-abstract methods for producing or combining functions. Predicate.isEqual(a) is the same as a::equals, but it also works if a is null. There are default methods and, or, negate for combining predicates.
72. If you design your own interface with a single abstract method, you can tag it with the @FunctionalInterface annotation. This has two advantages. The compiler gives an error message if you accidentally add another non-abstract method. And the javadoc page includes a statement that your interface is a functional interface.
73. The Comparator interface has a number of convenient static methods for creating comparators. The staticcomparing method takes a “key extractor” function that maps a type T to a comparable type (such as String). The function is applied to the objects to be compared, and the comparison is then made on the returned keys. You can chain comparators with the thenComparing method for breaking ties. You can specify a comparator to be used for the keys that the comparing and thenComparing methods extract:
both the comparing and thenComparing methods have variants that avoid boxing of int, long, or double values.
74. The staticnullsFirst and nullsLast methods take an existing comparator and modify it so that it doesn’t throw an exception when encountering null values but ranks them as smaller or larger than regular values :
75. The naturalOrder method makes a comparator for any class implementing Comparable. The staticreverseOrder method gives the reverse of the natural order. To reverse any comparator, use the reversed instance method.
1. The prefixes super and sub come from the language of sets used in theoretical computer science and mathematics. The set of all employees contains the set of all managers, and this is said to be a superset of the set of managers. Or, to put it another way, the set of all managers is a subset of the set of all employees.
2. When defining a subclass by extending its superclass, you only need to indicate the differences between the subclass and the superclass. When designing classes, you place the most general methods into the superclass and more specialized methods in its subclasses. Factoring out common functionality by moving it to a superclass is common in object-oriented programming.
3. Some people think of super as being analogous to the this reference. However, that analogy is not quite accurate: super is not a reference to an object. For example, you cannot assign the value super to another object variable. Instead, super is a special keyword that directs the compiler to invoke the superclass method.
4. Inheritance can never take away any fields or methods.
5. To initialize the fields of superclass, the constructor is invoked with the special super syntax. The call using super must be the first statement in the constructor for the subclass. If the subclass constructor does not call a superclass constructor explicitly, the no-argument constructor of the superclass is invoked. If the superclass does not have a no-argument constructor and the subclass constructor does not call another superclass constructor explicitly, the Java compiler reports an error.
6. The fact that an object variable can refer to multiple actual types is called polymorphism. Automatically selecting the appropriate method at runtime is called dynamic binding. Dynamic binding is the default behavior for Java. If you do not want a method to be dynamic binding, you tag it as final.
7. The “is–a” rule states that every object of the subclass is an object of the superclass. It’s a simple rule can help you decide whether or not inheritance is the right design for your data. Another way of formulating the “is–a” rule is the substitution principle. That principle states that you can use a subclass object whenever the program expects a superclass object.
8. In the Java programming language, object variables are polymorphic.
9. In Java, arrays of subclass references can be converted to arrays of superclass references without a cast. However, to make sure no corruption can occur, all arrays remember the element type with which they were created, and they monitor that only compatible references are stored into them. For example, the array created as new Manager[10] remembers that it is an array of managers. Attempting to store an Employee reference causes an ArrayStoreException.
10. The return type is not part of the signature. However, when you override a method, you need to keep the return type compatible. A subclass may change the return type to a subtype of the original type. The subclass method must be at least as visible as the superclass method.
11. When a method call is applied to an object:
a) The compiler looks at the declared type of the object and the method name. Let’s say we call x.f("hello"), and the implicit parameter x is declared to be an object of class C. Note that there may be multiple methods, all with the same name, f, but with different parameter types. The compiler enumerates all methods called f in the class C and all accessible methods called f in the superclasses of C.
b) Next, the compiler determines the types of the parameters that are supplied in the method call. If among all the methods called f there is a unique method whose parameter types are a best match for the supplied parameters, then that method is chosen to be called. This process is called overloading resolution. If the compiler cannot find any method with matching parameter types or if multiple methods all match after applying conversions, the compiler reports an error.
c) If the method is private, static, final, or a constructor, then the compiler knows exactly which method to call. This is called static binding. Otherwise, the method to be called depends on the actual type of the implicit parameter, and dynamic binding must be used at runtime.
d) When the program runs and uses dynamic binding to call a method, then the virtual machine must call the version of the method that is appropriate for the actual type of the object to which x refers. Let’s say the actual type is D, a subclass of C. If the class D defines a method f(String), that method is called. If not, D’s superclass is searched for a method f(String), and so on.
It would be time-consuming to carry out this search every time a method is called. Therefore, the virtual machine precomputes for each class a method table that lists all method signatures and the actual methods to be called. When a method is actually called, the virtual machine simply makes a table lookup. In our example, the virtual machine consults the method table for the class D and looks up the method to call for f(String). That method may be D.f(String) or X.f(String), where X is some superclass of D. There is one twist to this scenario. If the call is super.f(param), then the compiler consults the method table of the superclass of the implicit parameter.
12. Classes that cannot be extended are called final classes, and you use the final modifier in the definition of the class to indicate this. You can also make a specific method in a class final. If you do so, then no subclass can override that method. There is only one good reason to make a method or class final: to make sure that its semantics cannot be changed in a subclass.
13. Some programmers used the final keyword hoping to avoid the overhead of dynamic binding. If a method is not overridden, and it is short, then a compiler can optimize the method call away—a process called inlining. Fortunately, the just-in-time compiler in the virtual machine can do a better job than a traditional compiler. It knows exactly which classes extend a given class, and it can check whether any class actually overrides a given method. If a method is short, frequently called, and not actually overridden, the just-in-time compiler can inline the method. What happens if the virtual machine loads another subclass that overrides an inlined method? Then the optimizer must undo the inlining. That’s slow, but it happens rarely.
14. To actually make a cast of an object reference, use a syntax similar to what you use for casting a numeric expression. Surround the target class name with parentheses and place it before the object reference you want to cast.
15. There is only one reason why you would want to make a cast—to use an object in its full capacity after its actual type has been temporarily forgotten.
16. The compiler checks that you do not promise too much when you store a value in a variable. If you assign a subclass reference to a superclass variable, you are promising less, and the compiler will simply let you do it. If you assign a super-class reference to a subclass variable, you are promising more. Then you must use a cast so that your promise can be checked at runtime. When the program runs, the Java runtime system notices the broken promise and generates a ClassCastException.
17. You can cast only within an inheritance hierarchy. The compiler will not let you make a cast if there is no chance for the cast to succeed. Use instanceof to check before casting from a superclass to a subclass.
18. If you use the abstract keyword, you do not need to implement the method at all. A class with one or more abstract methods must itself be declared abstract. When you extend an abstract class, you have two choices. You can leave some or all of the abstract methods undefined; then you must tag the subclass as abstract as well. Or you can define all methods, and the subclass is no longer abstract.
19. Abstract classes cannot be instantiated. A class can even be declared as abstract even though it has no abstract methods.
20. Methods of a specific subclass can peek inside the protected field or method of its instances only, not of other subclass (even the superclass) instances. (Except it’s in the same package of the superclass) This restriction is made so that you can’t abuse the protected mechanism by forming subclasses just to gain access to the protected fields.
21. Here is a summary of the four access modifiers in Java that control visibility:
a) Visible to the class only (private).
b) Visible to the world (public).
c) Visible to the package and all subclasses (protected).
d) Visible to the package—the (unfortunate) default. No modifiers are needed.
22. In Java, only the primitive types (numbers, characters, and boolean values) are not objects. All array types, no matter whether they are arrays of objects or arrays of primitive types, are class types.
23. The equals method, as implemented in the Object class, determines whether two object references are identical.
24. The call Objects.equals(a, b) returns true if both arguments are null, false if only one is null, and calls a.equals(b) otherwise.
25. When you define the equals method for a subclass, first call equals on the superclass. If that test doesn’t pass, then the objects can’t be equal. If the super-class fields are equal, then you are ready to compare the instance fields of the subclass.
26. The Java Language Specification requires that the equals method has the following properties:
a) It is reflexive: For any non-null reference x, x.equals(x) should return true.
b) It is symmetric: For any references x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
c) It is transitive: For any references x, y, and z, if x.equals(y) and y.equals(z) return true, then x.equals(z) should return true.
d) It is consistent: If the objects to which x and y refer haven’t changed, then repeated calls to x.equals(y) return the same value.
e) For any non-null reference x, x.equals(null) should return false.
27. Using instanceof instead of getClass() to check the type of parameter in equals() may break the symmetric rule. The way we see it, there are two distinct scenarios:
a) If subclasses can have their own notion of equality, then the symmetry requirement forces you to use the getClass test.
b) If the notion of equality is fixed in the superclass, then you can use the instanceof test and allow objects of different subclasses to be equal to one another. (and mark equals final)
28. Check out the API documentation of the java.sql.Timestamp class, where the implementers note with some embarrassment that they have painted themselves in a corner. The Timestamp class inherits from java.util.Date, whose equals method uses an instanceof test, and it is impossible to override equals to be both symmetric and accurate.
29. Here is a recipe for writing the perfect equals method:
a) Name the explicit parameter otherObject—later, you will need to cast it to another variable that you should call other.
b) Test whether this happens to be identical to otherObject:
if (this == otherObject) return true;
c) Test whether otherObject is null and return false if it is:
if (otherObject == null) return false;
d) Compare the classes of this and otherObject. If the semantics of equals can change in subclasses, use the getClass test:
if (getClass() != otherObject.getClass()) return false;
If the same semantics holds for all subclasses, you can use an instanceof test:
if (!(otherObject instanceof ClassName)) return false;
e) Cast otherObject to a variable of your class type:
ClassName other = (ClassName) otherObject;
f) Now compare the fields, as required by your notion of equality. Use == for primitive type fields, Objects.equals for object fields. Return true if all fields match, false otherwise. If you redefine equals in a subclass, include a call to super.equals(other).
30. If you have fields of array type, you can use the static Arrays.equals method to check that the corresponding array elements are equal.
31. Hash codes should be scrambled—if x and y are two distinct objects, there should be a high probability that x.hashCode() and y.hashCode() are different.
32. The hashCode method is defined in the Object class. Therefore, every object has a default hash code. That hash code is derived from the object’s memory address. No hashCode method has been defined for the StringBuilder class (neither has equals method).
33. As of Java 7, it is better to use the null-safe method Objects.hashCode. It returns 0 if its argument is null and the result of calling hashCode on the argument otherwise. Even better, when you need to combine multiple hash values, call Objects.hash with all of them. It actually invokes Arrays.hashCode.
34. Your definitions of equals and hashCode must be compatible: If x.equals(y) is true, then x.hashCode() must return the same value as y.hashCode().
35. Most (but not all) toString methods follow this format: the name of the class, followed by the field values enclosed in square brackets.The Object class defines the toString method to print the class name plus @ plus the hash code of the object.
36. Whenever an object is concatenated with a string by the “+” operator, the Java compiler automatically invokes the toString method to obtain a string representation of the object.
37. The ArrayList class is similar to an array, but it automatically adjusts its capacity as you add and remove elements, without any additional code. It is a generic class with a type parameter to specify the type of the element objects that the array list holds.
38. As of Java 7, you can omit the type parameter on the right-hand side:
ArrayList<Employee> staff = new ArrayList<>();
The compiler checks what happens to the new value. If it is assigned to a variable, passed into a method, or returned from a method, then the compiler checks the generic type of the variable, parameter, or method. It then places that type into the <>.
Commented by Sean: “passed into a method” is not allowed util Java 8, Compiler will infer the actual type parameter only from Java 8.
39. If you call ArrayList.add and the internal array is full, the array list automatically creates a bigger array and copies all the objects from the smaller to the bigger array. If you already know, or have a good guess, how many elements you want to store, call the ArrayList.ensureCapacity method before filling the array list. You can also pass an initial capacity to the ArrayList constructor.
40. Once you are reasonably sure that the array list is at its permanent size, you can call the ArrayList.trimToSize method. This method adjusts the size of the memory block to use exactly as much storage space as is required to hold the current number of elements. The garbage collector will reclaim any excess memory.
41. Do not call list.set(i, x) until the size of the array list is larger than i. Use the add method instead of set to fill up an array, and use set only to replace a previously added element.
42. For compatibility, the compiler translates all typed array lists into raw ArrayList objects after checking that the type rules were not violated. In a running program, all array lists are the same—there are no type parameters in the virtual machine. You can assign a typed ArrayList to a raw one without any error or warning. Conversely, when you assign a raw ArrayList to a typed one, you get a warning.
43. The wrapper classes have obvious names: Integer, Long, Float, Double, Short, Byte, Character, Void, and Boolean. (The first six inherit from the common superclass Number.) The wrapper classes are immutable and final.
44. The primitive types and their wrappers are one and the same. There is just one point in which they differ considerably: identity. The == operator, applied to wrapper objects, only tests whether the objects have identical memory locations.
45. The autoboxing specification requires that boolean, byte, char ≤ 127, short, and int between -128 and 127 are wrapped into fixed objects.
46. Finally, let us emphasize that boxing and unboxing is a courtesy of the compiler, not the virtual machine. The compiler inserts the necessary calls when it generates the bytecodes of a class. The virtual machine simply executes those bytecodes.
47. The Object... parameter type is exactly the same as Object[]. It is legal to pass an array as the last argument of a method with variable parameters. It is legal to pass no values for varargs parameter but illegal for array parameter.
Commented by Sean: You can pass objects separated by comma to methods with variable parameter, but you can't do that for methods with array parameter.
48. You can add constructors, methods, and fields to an enumerated type. Of course, the constructors are only invoked when the enumerated constants are constructed.
49. All enumerated types are subclasses of the class Enum.
50. Each enumerated type has a instance toString method, which returns the name of the enumerated constant.The converse of toString is the static valueOf method. Static values method returns an array of all values of the enumeration. The ordinal method yields the position of an enumerated constant in the enum declaration, counting from zero.
51. You can use reflection to:
a) Analyze the capabilities of classes at runtime;
b) Inspect objects at runtime—for example, to write a single toString method that works for all classes;
c) Implement generic array manipulation code;
d) Take advantage of Method objects that work just like function pointers in languages such as C++.
52. The Java runtime system always maintains what is called runtime type identification on all objects. This information keeps track of the class to which each object belongs. Runtime type information is used by the virtual machine to select the correct methods to execute. You can also access this information by working with a special Java class. The class that holds this information is called Class. The getClass() method in the Object class returns an instance of Class type.
53. Class.getName() returns the name of the class. The package name is part of the class name.
54. You can obtain a Class object corresponding to a class name by using the static forName method.
55. At startup, the class containing your main method is loaded. It loads all classes that it needs. Each of those loaded classes loads the classes that it needs, and so on. You can give users of your program the illusion of a faster start with the following trick: Make sure that the class containing the main method does not explicitly refer to other classes. In it, display a splash screen. Then manually force the loading of other classes by calling Class.forName.
56. If T is any Java type, then T.class is the matching class object.
57. Note that a Class object really describes a type, which may or may not be a class. For example, int is not a class, but int.class is nevertheless an object of type Class.
58. The virtual machine manages a unique Class object for each type. Therefore, you can use the == operator to compare class objects.
59. newInstance() lets you create an instance of a class on the fly. It calls the no-argument constructor to initialize the newly created object. An exception is thrown if the class does not have a no-argument constructor. If you need to provide parameters for the constructor of a class, you must use the newInstance method in the Constructor class.
60. There are two kinds of exceptions: unchecked exceptions and checked exceptions. With checked exceptions, the compiler checks that you provide a handler.
61. The three classes Field, Method, and Constructor in the java.lang.reflect package describe the fields, methods, and constructors of a class, respectively. All three classes have a method called getName that returns the name of the item. The Field class has a method getType that returns an object, again of type Class, which describes the field type. The Method and Constructor classes have methods to report the types of the parameters, and the Method class also reports the return type. All three of these classes also have a method called getModifiers that returns an integer, with various bits turned on and off, that describes the modifiers used, such as public and static. You can then use the static methods in the Modifier class in the java.lang.reflect package to analyze the integer that getModifiers returns. Use methods like isPublic, isPrivate, or isFinal in the Modifier class to tell whether a method or constructor was public, private, or final. You can also use the Modifier.toString method to print the modifiers. The getFields, getMethods, and getConstructors methods of the Class class return arrays of the public fields, methods, and constructors that the class supports. This includes public members of superclasses. The getDeclaredFields, getDeclaredMethods, and getDeclaredConstructors methods of the Class class return arrays consisting of all fields, methods, and constructors that are declared in the class. This includes private, package, and protected members, but not members of superclasses.
62. getDeclaringClass() returns the Class object for the class that defines the constructor, method, or field.
63. If f is an object of type Field (for example, one obtained from getDeclaredFields) and obj is an object of the class of which f is a field, then f.get(obj) returns an object whose value is the current value of the field of obj. If the field to be got is not accessible (i.e. private) , the get method will throw an IllegalAccessException. You can override access control by invoke the setAccessible method on a Field, Method, or Constructor object. AccessibleObject.setAccessible can help to set access for array of AccessibleObject objects. The call f.set(obj, value) sets the field represented by f of the object obj to the new value.
64. The Array class in the java.lang.reflect package allows you to create arrays dynamically. This is used in the implementation of the copyOf method in the Arrays class.
65. A Java array remembers the type of its entries—that is, the element type used in the new expression that created it. It is legal to cast an Employee[] temporarily to an Object[] array (no need for explicit cast) and then cast it back, but an array that started its life as an Object[] array can never be cast into an Employee[] array.
66. The staticnewInstance method of the Array class constructs a new array. You must supply the type for the entries and the desired length as parameters to this method. The static getLength method of the Array class returns the length of an array. The isArray method of the Class class test whether the Class objects represent arrays. The getComponentType method of the Class class (which is defined only for class objects that represent arrays) to find the right type for the array.
67. The integer array type int[] can be converted to an Object, but not to Object[].
68. The Method class has an invoke method that lets you call the method that is wrapped in the current Method object. The first parameter is the implicit parameter, and the remaining objects provide the explicit parameters. For a static method, the first parameter is ignored—you can set it to null.
69. Code that uses reflection to invoke methods is significantly slower than code that simply calls methods directly. Using interfaces and inner classes for the callbacks leads to code that runs faster and is a lot more maintainable.
70. Besides providing the name of the class, you must also supply the parameter types of the desired method to Class.getMethod:
71. Design Hints for Inheritance:
a) Place common operations and fields in the superclass.
b) Don’t use protected fields.
c) Use inheritance to model the “is–a” relationship.
d) Don’t use inheritance unless all inherited methods make sense.
e) Don’t change the expected behavior when you override a method.
f) Use polymorphism, not type information.
g) Don’t overuse reflection.
72. The following codes print all features of a class:
public class ReflectionTest
{
public static void main(String[] args)
{
// read class name from command line args or user input
String name;
if (args.length > 0) name = args[0];
else
{
Scanner in = new Scanner(System.in);
System.out.println("Enter class name (e.g. java.util.Date): ");
name = in.next();
}
try
{
// print class name and superclass name (if != Object)
Class cl = Class.forName(name);
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print("class " + name);
if (supercl != null && supercl != Object.class) System.out.print(" extends "
+ supercl.getName());
System.out.print("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
System.exit(0);
}
/**
* Prints all constructors of a class
* @param cl a class
*/
public static void printConstructors(Class cl)
{
Constructor[] constructors = cl.getDeclaredConstructors();
for (Constructor c : constructors)
{
String name = c.getName();
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(name + "(");
// print parameter types
Class[] paramTypes = c.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
/**
* Prints all methods of a class
* @param cl a class
*/
public static void printMethods(Class cl)
{
Method[] methods = cl.getDeclaredMethods();
for (Method m : methods)
{
Class retType = m.getReturnType();
String name = m.getName();
System.out.print(" ");
// print modifiers, return type and method name
String modifiers = Modifier.toString(m.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(retType.getName() + " " + name + "(");
// print parameter types
Class[] paramTypes = m.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
/**
* Prints all fields of a class
* @param cl a class
*/
public static void printFields(Class cl)
{
Field[] fields = cl.getDeclaredFields();
for (Field f : fields)
{
Class type = f.getType();
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.println(type.getName() + " " + name + ";");
}
}
}
73. The following codes implement a generic toString method :
package objectAnalyzer;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
public class ObjectAnalyzer
{
private ArrayList<Object> visited = new ArrayList<>();
/**
* Converts an object to a string representation that lists all fields.
* @param obj an object
* @return a string with the object's class name and all field names and
* values
*/
public String toString(Object obj)
{
if (obj == null) return "null";
if (visited.contains(obj)) return "...";
visited.add(obj);
Class cl = obj.getClass();
if (cl == String.class) return (String) obj;
if (cl.isArray())
{
String r = cl.getComponentType() + "[]{";
for (int i = 0; i < Array.getLength(obj); i++)
{
if (i > 0) r += ",";
Object val = Array.get(obj, i);
if (cl.getComponentType().isPrimitive()) r += val;
else r += toString(val);
}
return r + "}";
}
String r = cl.getName();
// inspect the fields of this class and all superclasses
do
{
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
// get the names and values of all fields
for (Field f : fields)
{
if (!Modifier.isStatic(f.getModifiers()))
{
if (!r.endsWith("[")) r += ",";
r += f.getName() + "=";
try
{
Class t = f.getType();
Object val = f.get(obj);
if (t.isPrimitive()) r += val;
else r += toString(val);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
r += "]";
cl = cl.getSuperclass();
}
while (cl != null);
return r;
}
}
1. Encapsulation (sometimes called information hiding) is a key concept in working with objects. Formally, encapsulation is nothing more than combining data and behavior in one package and hiding the implementation details from the user of the object. A change in the state of an object must be a consequence of method calls.
2. To work with OOP, you should be able to identify three key characteristics of objects:
The object’s behavior—what can you do with this object, or what methods can you apply to it?
The object’s state—how does the object react when you invoke those methods?
The object’s identity—how is the object distinguished from others that may have the same behavior and state?
A simple rule of thumb in identifying classes is to look for nouns in the problem analysis. Methods, on the other hand, correspond to verbs.
3. The most common relationships between classes are:
a) Dependence (“uses–a”)
b) Aggregation (“has–a”)
c) Inheritance (“is–a”)
A class depends on another class if its methods use or manipulate objects of that class. Try to minimize the number of classes that depend on each other. In software engineering terminology, you want to minimize the coupling between classes.
4. UML Notation for Class Relationships:
5. It is important to realize that an object variable doesn’t actually contain an object. It only refers to an object. In Java, the value of any object variable is a reference to an object that is stored elsewhere. The return value of the new operator is also a reference.
6. Local variables are not automatically initialized to null. You must initialize them, either by calling new or by setting them to null.
7. When you use the Date class, the time is represented by the number of milliseconds (positive or negative) from a fixed point, the so called epoch, which is 00:00:00 UTC, January 1, 1970. UTC is the Coordinated Universal Time, the scientific time standard that is, for practical purposes, the same as the more familiar GMT or Greenwich Mean Time.
8. The library designers decided to separate the concerns of keeping time and attaching names to points in time. Therefore, the standard Java library contains two separate classes: the Date class, which represents a point in time, and the GregorianCalendar class ( or LocalDate class introduced in
Java 8), which expresses dates in the familiar calendar notation. The job of a calendar is to compute attributes, such as the date, weekday, month, or year, of a certain point in time. The standard library does not contain any calendar implementations besides the Gregorian calendar.
9. The Date class has only a small number of methods that allow you to compare two points in time (before() and after() ). Other methods of Date became deprecated when calendar classes were introduced.
10. Methods that change instance fields are called mutator methods, and those that only access instance fields without modifying them are called accessor methods. A common convention is to prefix accessor methods with the prefix get and mutator methods with the prefix set.
11. In Calendar, the months are counted from 0.
12. Suppose you know the year, month, and day and you want to make a Date object with those settings. Since the Date class knows nothing about calendars, first construct a GregorianCalendar object and then call the getTime method to obtain a date. Conversely, if you want to find the year, month, or day of a Date object, construct a GregorianCalendar object, set the time, and then call the get method.
13. There are varying conventions about the starting day of the week. In the United States, the week starts with Sunday and ends with Saturday, whereas in Europe, the week starts with Monday and ends with Sunday. The Java virtual machine is aware of the locale of the current user. The locale describes local formatting conventions, including the start of the week and the names of the weekdays. The getFirstDayOfWeek method of Calendar gets the starting weekday in the current locale. (Calendar.Sunday = 1)
14. The getShortWeekdays method of DateFormatSymbols returns an array of String with short weekday names in the user’s language (such as "Sun", "Mon", and so on in English). The array is indexed by weekday values.
15. The name of the java file must match the name of the public class. You can only have one public class in a source file, but you can have any number of nonpublic classes.
16. When you type: javac EmployeeTest.java
The Java compiler sees the Employee class being used inside EmployeeTest.java, it will look for a file named Employee.class (starting from the current path where javac is invoked). If it does not find that file, it automatically searches for Employee.java and compiles it. Moreover, if the timestamp of the version of Employee.java that it finds is newer than that of the existing Employee.class file, the Java compiler will automatically recompile the file.
17. In every method, the keyword this refers to the implicit parameter.
18. Mutator methods can perform error checking, whereas code that simply assigns to a field may not go into the trouble.
19. Be careful not to write accessor methods that return references to mutable objects. Always use clone whenever you need to return a copy of a mutable data field.
20. A method can access the private data of all objects of its class, not just of the implicit parameter.
21. You can define an instance field as final. Such a field must be initialized when the object is constructed. That is, you must guarantee that the field value has been set after the end of every constructor (It can only be initialized once.). Afterwards, the field may not be modified again.
22. If you look at the System class, you will notice a method setOut that sets System.out to a different stream. You may wonder how that method can change the value of a final variable. However, the setOut method is a native method, not implemented in the Java programming language. Native methods can bypass the access control mechanisms of the Java language. This is a very unusual workaround that you should not emulate in your programs.
23. The term call by value means that the method gets just the value that the caller provides. In contrast, call by reference means that the method gets the location of the variable that the caller provides. Thus, a method can modify the value stored in a variable passed by reference but not in one passed by value. The Java programming language always uses call by value.
24. The compiler must sort out which method to call. It picks the correct method by matching the parameter types in the headers of the various methods with the types of the values used in the specific method call. A compile-time error occurs if the compiler cannot match the parameters or if more than one match is possible. (This process is called overloading resolution.)
25. If you don’t set a field explicitly in a constructor, it is automatically set to a default value: numbers to 0, boolean values to false, and object references to null.
26. You get a free no-argument constructor only when your class has no other constructors.
27. If the first statement of a constructor has the form this(. . .), then the constructor calls another constructor of the same class.
28. Class declarations can contain arbitrary blocks of code, called initialization block. The initialization block runs first, and then the body of the constructor is executed.
29. It is legal to set fields in initialization blocks even if they are only defined later in the class. However, to avoid circular definitions, it is not legal to reference/read fields that are initialized later. We suggest that you always place initialization blocks after the field definitions.
30. Here is what happens in detail when a constructor is called:
a) All data fields are initialized to their default values (0, false, or null).
b) All field initializers and initialization blocks are executed, in the order in which they occur in the class declaration.
c) If the first line of the constructor calls a second constructor, then the body of the second constructor is executed.
d) The body of the constructor is executed.
31. If the static fields of your class require complex initialization code, use a static initialization block. Place the code inside a block and tag it with the keyword static. Static initialization occurs when the class is first loaded. All static field initializers and static initialization blocks are executed in the order in which they occur in the class declaration.
32. The method call System.runFinalizersOnExit(true) guarantees that finalizer methods are called before Java shuts down. However, this method is inherently unsafe and has been deprecated. An alternative is to add “shutdown hooks” with the method Runtime.addShutdownHook.
33. A class can use all classes from its own package and all public classes from other packages.
34. You can only use the * notation to import a single package. You cannot use import java.* or import java.*.* to import all packages with the java prefix.
35. Locating classes in packages is an activity of the compiler. The bytecodes in class files always use full package names to refer to other classes.
36. A form of the import statement permits the importing of static methods and fields, not just classes. For example, if you add the directive: import static java.lang.System.*;
to the top of your source file, then you can use the static methods and fields of the System class without the class name prefix
37. The compiler operates on files (with file separators and an extension .java), whereas the Java interpreter loads a class (with dot separators). The compiler does not check the directory structure when it compiles source files ( but it will lookup the dependent source file following the directory structure, see item 16).
38. Starting with version 1.2, the JDK implementors rigged the class loader to explicitly disallow loading of user-defined classes whose package name starts with "java.".
39. Starting with Java SE 6, you can specify a wildcard for a JAR file directory in class path such as /home/user/archives/'*' in UNIX or c:\archives\* in Windows. All JAR files (but not .class files) in the archives directory are included in the class path. In UNIX, the * must be escaped as ‘*’ to prevent shell expansion.
40. The javac compiler always looks for files in the current directory, but the java virtual machine launcher only looks into the current directory if the “.” directory is on the class path. If you have no class path set, this is not a problem—the default class path consists of the “.” directory.
41. The class path lists all directories and archive files that are starting points for locating classes. The compiler has a harder time locating files than does the virtual machine. If you refer to a class without specifying its package, the compiler first needs to find out the package that contains the class. It consults all import directives as possible sources for the class. Then it searches for each of these classes in all of the locations of the class path. Classes must be unique, so the order of the import statements doesn’t matter. The runtime library files (rt.jar and the other JAR files in the jre/lib and jre/lib/ext directories) are always searched for classes first.
42. It is best to specify the class path with the -classpath (or -cp) option in java command.
43. If your comments contain links to other files such as images, place those files into a subdirectory of the directory containing the source file, named doc-files. The javadoc utility will copy the doc-files directories and their contents from the source directory to the documentation directory. You need to use the doc-files directory in your link, for example <img src="doc-files/uml.png" alt="UML diagram"/>.
44. You can use hyperlinks to other relevant parts of the javadoc documentation, or to external documents, with the @see and @link tags.
a) @see reference adds a hyperlink in the “see also” section. It can be used with both classes and methods. The reference part can be one of the following:
1) package.class#feature label
2) <a href="...">label</a>
3) "text"
For the first case, you supply the name of a class, method, or variable, and javadoc inserts a hyperlink to the documentation. For example, @see com.horstmann.corejava.Employee#raiseSalary(double). If the @see tag is followed by a < character, then you need to specify a hyperlink. You can link to any URL you like. In each of these cases, you can specify an optional label that will appear as the link anchor. If you omit the label, the user will see the target code name or URL as the anchor. If the @see tag is followed by a " character, then the text is displayed in the “see also” section.
b) You can place hyperlinks to other classes or methods anywhere in any of your documentation comments. Insert a special tag of the form: {@link package.class#feature label}
anywhere in a comment. The feature description follows the same rules.
45. To generate package comments, you need to add a separate file in each package directory. You have two choices:
a) Supply an HTML file named package.html. All text between the tags <body>...</body> is extracted.
b) Supply a Java file named package-info.java. The file must contain an initial Javadoc comment, delimited with /** and */, followed by a package statement. It should contain no further code or comments.
46. You can also supply an overview comment for all source files. Place it in a file called overview.html, located in the parent directory that contains all the source files. All text between the tags <body>...</body> is extracted. This comment is displayed when the user selects “Overview” from the navigation bar. To type monospaced code, use {@code ... } instead of <code>...</code>.
47. The javadoc program can be fine-tuned by numerous command-line options. You can use the -author and -version options to include the @author and @version tags in the documentation. (By default, they are omitted.) If you use the -linksource option, each source file is converted to HTML (without color coding, but with line numbers), and each class and method name turns into a hyperlink to the source. Another useful option is -link, to include hyperlinks to standard classes: javadoc -d outputDir -link http://docs.oracle.com/javase/8/docs/api *.java
For other options refer to http://docs.oracle.com/javase/8/docs/technotes/guides/javadoc/.
48. To produce documentation in a format other than HTML—you can supply your own doclet to generate the output in any form you desire. Refer to http://docs.oracle.com/javase/8/docs/technotes/guides/javadoc/doclet/overview.html for details.
49. Class Design Hints:
a) Always keep data private.
b) Always initialize data.
c) Don’t use too many basic types in a class.
d) Not all fields need individual field accessors and mutators.
e) Break up classes that have too many responsibilities.
f) Make the names of your classes and methods reflect their responsibilities.
50. LocalDate.now() constructs a new object that represents the date at which the object was constructed. You can construct an object for a specific date by supplying year, month, and day : LocalDate.of(1999, 12, 31). You can find out the year, month, and day with the methods getYear, getMonthValue, and getDayOfMonth. The plusDays method yields a new LocalDate that is a given number of days away from the object to which you apply it.
51. We call the getValue method of DayOfWeek object to get a numerical value for the weekday. This yields an integer that follows the international convention where the weekend comes at the end of the week, returning 1 for Monday, 2 for Tuesday, and so on. Sunday has value 7.
52. Local variables shadow the instance fields with the same name. You have to be careful in all of your methods to not use variable names that equal the names of instance fields.
53. You can think of static methods as methods that don’t have a this parameter.
54. Since Java SE 7, the java program first checks that there is a main method before executing the static initialization block.
55. From the point of view of the compiler, there is absolutely no relationship between nested packages. For example, the packages java.util and java.util.jar have nothing to do with each other. Each is its own independent collection of classes.
56. The compiler does not check the directory structure whether it matches the package declaration when it compiles source files.
57. The javadoc utility extracts information for the following items:
Packages
Public classes and interfaces
Public and protected fields
Public and protected constructors and methods
58. The class comment must be placed after any import statements, directly before the class definition. Each method comment must immediately precede the method that it describes. @param tag adds an entry to the “parameters” section of the current method. @return tag adds a “returns” section to the current method. @throws tag adds a note that this method may throw an exception.