Saturday, 22 August 2015

Points to Remember in Java

Overloading

• Overload resolution takes place entirely at compile time. In the next section, you’ll learn about runtime polymorphism where the method call is resolved at runtime.
• You cannot overload methods with the methods differing in return types alone. 
• You cannot overload methods with the methods differing in exception specifications alone.
• For overload resolution to succeed, you need to define methods such that the compiler finds one exact match. If the compiler finds no matches for your call or if the matching is ambiguous, the overload resolution fails and the compiler issues an error.

Abstract classes and Abstract methods

 • The abstract keyword can be applied to a class or a method but not to a field.
• An abstract class cannot be instantiated. You can, however, create reference variables of an abstract class type. In fact, you can create objects of the classes derived from an abstract class and make the abstract class references refer to the created derived class objects.
 • An abstract class can extend another abstract class or can implement an interface.
• An abstract class can be derived from a concrete class! Although the language allows it, it is not a good idea to do so. Chapter 4
• An abstract class need not declare an abstract method, which means it is not necessary for an abstract class to have methods declared as abstract. However, if a class has an abstract method, it should be declared as an abstract class.
• A subclass of an abstract class needs to provide implementation of all the abstract methods; otherwise you need to declare that subclass as an abstract class.
• An abstract class may have methods or fields declared static.

Final

• The final modifier can be applied to a class, method, or variable. All methods of a final class are implicitly final (hence non-overridable).
• A final variable can be assigned only once. If a variable declaration defines a variable as final but did not initialize it, then it is referred to as blank final. You need to initialize a blank final all the constructors you have defined in the class; otherwise the compiler will complain.
• The keyword final can even be applied to parameters. The value of a final parameter cannot be changed once assigned. Here, it is important to note that the “value” is implicitly understood for primitive types. However, the “value” for an object refers to the object reference, not its state. Therefore, you can change the internal state of the passed final object, but you cannot change the reference itself.

Static
• The main() method, where the main execution of the program starts, is always declared static. Why? If it were an instance method, it would be impossible to invoke it. You’d have to start the program to be able to create an instance and then call the method, right?
• You cannot override a static method provided in a base class. Why? Based on the instance type, the method call is resolved with runtime polymorphism. Since static methods are associated with a class (and not with an instance), you cannot override static methods, and runtime polymorphism is not possible with static methods.
• A static method cannot use the this keyword in its body. Why? Remember that static methods are associated with a class and not an instance. Only instance methods have an implicit reference associated with them; hence class methods do not have a this reference associated with them.
• A static method cannot use the super keyword in its body. Why? You use the super keyword for invoking the base class method from the overriding method in the derived class. Since you cannot override static methods, you cannot use the super keyword in its body.
 • Since static methods cannot access instance variables (non-static variables), they are most suited for utility functions. For example, all methods in the java.util.math library are static.
• Calling a static method is considered to be slightly more efficient compared to calling an instance method. This is because the complier need not pass the implicit this object reference while calling a static method, unlike an instance method.

Static nested classes (and Interfaces)

• The accessibility (public, protected, etc.) of the static nested class is defined by the outer class.
• The name of the static nested class is expressed with OuterClassName.NestedClassName syntax.
• When you define an inner nested class (or interface) inside an interface, the nested class is declared implicitly public and static. This point is easy to remember: any field in an interface is implicitly declared public and static, and static nested classes have this same behavior.
 • Static nested classes can be declared abstract or final.
 • Static nested classes can extend another class or it can be used as a base class.
• Static nested classes can have static members. (As you’ll see shortly, this statement does not apply to other kinds of nested classes.)
• Static nested classes can access the members of the outer class (only static members, obviously).
• The outer class can also access the members (even private members) of the nested class through an object of nested class. If you don’t declare an instance of the nested class, the outer class cannot access nested class elements directly.

Inner classes and Interfaces

• The accessibility (public, protected, etc.) of the inner class is defined by the outer class.
• Just like top-level classes, an inner class can extend a class or can implement interfaces. Similarly, an inner class can be extended by other classes, and an inner interface can be implemented or extended by other classes or interfaces.
• An inner class can be declared final or abstract.
• Inner classes can have inner classes, but you’ll have a hard time reading or understanding such complex nesting of classes. (Meaning: Avoid them!)

Local classes

• You can create a non-static local class inside a body of code. Interfaces cannot have local classes, and you cannot create local interfaces.
 • Local classes are accessible only from the body of the code in which the class is defined. The local classes are completely inaccessible outside the body of the code in which the class is defined.
• You can extend a class or implement interfaces while defining a local class.
• A local class can access all the variables available in the body of the code in which it is defined. You can pass only final variables to a local inner class
Enums

• Enums are implicitly declared public, static, and final, which means you cannot extend them.
 • When you define an enumeration, it implicitly inherits from java.lang.Enum. Internally, enumerations are converted to classes. Further, enumeration constants are instances of the enumeration class for which the constant is declared as a member.
• You can apply the valueOf() and name() methods to the enum element to return the name of the enum element.
• If you declare an enum within a class, then it is by default static.
• You cannot use the new operator on enum data types, even inside the enum class.
• You can compare two enumerations for equality using == operator.
• When an enumeration constant’s toString() method is invoked, it prints the name of the enumeration constant.
• The static values() method in the Enum class returns an array of the enumeration constants when called on an enumeration type.
 • Enumeration constants cannot be cloned. An attempt to do so will result in a CloneNotSupportedException.
 • If enumeration constants are from two different enumerations, the equals() method does not return true.

Interfaces

• An interface cannot be instantiated.
• An interface can extend another interface. Use the extends (and not the implements) keyword for this. • Interfaces cannot contain instance variables. If you declare a data member in an interface, it should be initialized, and all such data members are implicitly treated as “public static final” members.
• An interface cannot declare static methods. It can only declare instance methods.
• You cannot declare members as protected or private. Only public access is allowed for members of an interface.
• All methods declared in an interface are implicitly considered to be abstract. If you want, you can explicitly use the abstract qualifier for the method.
• You can only declare (and not define) methods in an interface.

Design principles

• Adhere to the OO design principle of “favor composition over inheritance.” Composition encourages you to follow another useful OO design principle: “program to an interface, not to an implementation.” This second injunction means that the functionality of a class should depend only on the interface of another abstraction and not on the specific implementation details of that abstraction. In other words, implementation of a class should not depend on the internal implementation aspects of the other class. Wherever suitable, composition is the technique of choice.
• In OOP, there are many terms related to composition, such as association and aggregation. Association is the most general form of a relationship between two objects, whereas composition and aggregation are special forms of association. In general, the terms aggregation and composition are used interchangeably. Although these two terms are very similar, they do have a subtle difference. In composition, the lifetime of the contained object and the container object is the same, whereas that is not the case with aggregation. For example, a computer object and a CPU object share a composition relationship, while a library object and a book object share an aggregation relationship.
Here are points to remember for the OCPJP 7 exam:
• You saw the factory design pattern implementation in the DAO design pattern. You may also employ abstract factory design pattern if you have multiple DAO objects and you have multiple persistence mechanisms.
• Note that you declared TransferObject (e.g., CircleTransfer) as serializable. Any idea why you did that? Well, if you are using the transfer object between two JVMs, then the transfer object has to be serializable.
• In OOP, a useful and important design principle is “separation of concerns.” This principle states that concerns (or features) should be separated (to attain minimum overlap) in order to overcome the inherent complexity involved with software design. The DAO design pattern helps you comply with this design principle. If you are not using DAO, then your business logic will be exposed to the concrete implementation details of the persistence mechanisms—an undesirable state of affairs. Use of the DAO design pattern ensures that you separate your core logic from your persistence mechanism.

Points to Remember

• It’s possible to define or declare generic methods in an interface or a class even if the class or the interface itself is not generic.
• A generic class used without type arguments is known as a raw type. Of course, raw types are not type safe. Java supports raw types so that it is possible to use the generic type in code that is older than Java 5 (note that generics were introduced in Java 5). The compiler generates a warning when you use raw types in your code. You may use @SuppressWarnings({ "unchecked" }) to suppress the warning associated with raw types.
• List is a supertype of any List type, which means you can pass List, or List, or even List
where List is expected. 
• Implementation of generics is static in nature, which means that the Java compiler interprets the generics specified in the source code and replaces the generic code with concrete types. This is referred to as type erasure. After compilation, the code looks similar to what a developer would have written with concrete types. Essentially, the use of generics offers two advantages: first, it introduces an abstraction, which enables you to write generic implementation; second, it allows you to write generic implementation with type safety. 
• There are many limitations of generic types due to type erasure. A few important ones are as follows: 
• You cannot instantiate a generic type using new operator. For example, assuming mem is a field, the following statement will result in a compiler error: T mem = new T(); // wrong usage - compiler error • You cannot instantiate an array of a generic type. For example, assuming mem is a field, the following statement will result in a compiler error: T[] amem = new T[100]; // wrong usage - compiler error 
• You can declare non-static fields of type T, but not of static fields of type T. For example, class X { T instanceMem; // okay static T statMem; // wrong usage - compiler error } 
• It is not possible to have generic exception classes; as a result, the following will not compile: class GenericException extends Throwable { } // wrong usage - compiler error • You cannot instantiate a generic type with primitive types—in other words, Listcannot be instantiated. However, you can use boxed primitive types. 
• The meaning of "extends" and "super" changes in the context of generics. For instance, when you say , you refer to all types that extend X and the type X itself.


Points to Remember


• The difference between an ArrayList and ArrayDeque is that you can add an element anywhere in an array list using an index; however, you can add an element only either at the front or end of the array deque. That makes insertion in array deque much efficient than array list; however, navigation in an array deque becomes more expensive than in an array list.
• There is one more thing you need to remember about using the List returned from the Arrays.asList() method. Though you cannot add elements to the List returned by the asList() method, you can modify the List! Also, the modifications you make through the List are reflected in the original array. For example, if you modify the temperature of the first day from 31.1 to 35.2 in the List, the original array gets modified, as shown in Listing 6-33.

Points to Remember

• If you do not specify any string formatting specifier, the printf() method will not print anything from the given arguments!
• Flags such as "-", "^", or "0" make sense only when you specify width with the format specifier string.
• You can also print the % character in a format string; however, you need to use an escape sequence for it. In format specifier strings, % is an escape character—which means you need to use %% to print a single %.
• If you do not provide the intended input data type as expected by the format string, then you can get an IllegalFormatConversionException. For instance, if you provide a string instead of an expected integer in your printRow() method implementation, you will get following exception: Exception in thread "main" java.util.IllegalFormatConversionException: d != java.lang.String at java.util.Formatter$FormatSpecifier.failConversion (Unknown Source)
• If you want to form a string and use it later rather than just printing it using the printf() method, you can use a static method in the String class—format(). We have reimplemented the printRow() method used in the last example using the format() method, as shown:
void printRow(String player, int matches, int goals){
 String str = String.format("%-15s \t %5d \t\t %d \t\t %.1f \n", player, matches, goals, ((float)goals/(float)matches));
 System.out.print(str);
}

Java I/O

• When you use buffered streams, you should call flush() once you are done with data transmission. The internal buffer might be holding some data that will be cleared and sent to the destination once you call flush(). However, the method close() on the stream will automatically call flush().
• You might have observed that you can combine stream objects. You can create an object of BufferedInputStream that takes a FileInputStream object. In this way, the output of one stream is chained to the filtered stream. This is the important, useful, and beautiful way to customize the stream in a desired way.
• The Serializable interface is a marker interface. That means the Serializable interface does not declare any method inside it.
• If you want to customize the process of serialization, you can implement readObject() and writeObject(). Note that both of these methods are private methods, which means you are not overriding or overloading these methods. JVM checks the implementation of these methods and calls them instead of the usual methods. It sounds weird but it is the way the customization of serialization process is implemented in the JVM.
• As discussed in earlier sections, a serialized object can be communicated over the network and deserialized on another machine. However, the class file of the object must be in the path of the destination machine, otherwise only the state of the object will be restored—not the whole object (i.e., you cannot invoke a method on the restored object).
• You can create your own protocol for serialization. For that, you just need to implement the Externalizable interface instead of the Serializable interface.
 • When you are not specifying serialVersionUID in a serialized class, JVM computes it for you. However, each JVM implementation has different mechanism to compute it; hence, it is not guaranteed that your serialized class will work on two different JVMs when you have not specified the serialVersionUID explicitly. Therefore, it is strongly recommended that you provide serialVersionUID in a class implementing the Serializable interface

There are a few points to remember when using the Files.delete() method. In the case of a directory, the delete() method should be invoked on an empty directory; otherwise the method will fail. In the case of a symbolic link, the link will be deleted, not the target file of the link. The file you intend to delete must exist; otherwise you will get a NoSuchFileException. If you silently delete a file and do not want to be bothered about this exception, then you may use the deleteIfExists() method, which will not complain if the file does not exist and deletes it if the file exists

Points to Remember

• Do not confuse File with Files, Path with Paths, and FileSystem with FileSystems: they are different. File is an old class (Java 4) that represents file/directory path names, while Files was introduced in Java 7 as a utility class with comprehensive support for I/O APIs. The Path interface represents a file/directory path and defines a useful list of methods. However, the Paths class is a utility class that offers only two methods (both to get the Path object). FileSystems offer a list of factory methods for the class FileSystem, whereas FileSystem provides a useful set of methods to get information about a file system.
 • The file or directory represented by a Path object might not exist.
• You learned how to perform a copy for files/directories. However, it is not necessary that you perform copy on two files/directories only. You can take input from an InputStream and write to a file, or you can take input from a file and copy to an OutputStream. Methods copy(InputStream, Path, CopyOptions.. .) and copy(Path, OutputStream, CopyOptions.. .) could be used here.
• You must be careful about performing an operation when walking a file tree. For instance, if you are performing a recursive delete, you should first delete all the containing files before deleting the directory that is holding these containing files.
 • The Visitor design pattern is used to enable walking through a file tree.
• In the context of a watch service, a state is associated with a watch key. A watch key might be in ready state (ready to accept events), in signed state (when one or more events are queued), or in invalid state (when the watch key is not valid). If the key is in the signed state, it is required to call the reset() method; otherwise the state of the key will not change to ready state and you will not receive any further event notification.
 • Your program may receive an OVERFLOW event even if the program is not registered for this event.
 • If you are watching a directory using the watch service offered by Java 7, then only files contained in that directory will be watched—and not the files contained in the subdirectories of that directory. If you intend to watch the whole subtree of the file system, you need to recursively register each directory in the subtree.

Points to Remember
Here are a couple of points that could be helpful on your OCPJP exam:
• The boolean absolute(int) method in ResultSet moves the cursor to the passed row number in that ResultSet object. If the row number is positive, it moves to that position from the beginning of the ResultSet object; if the row number is negative, it moves to that position from the end of the ResultSet object. Assume that there are 10 entries in the ResultSet object. Calling absolute(3) will move the cursor to the third row. Calling absolute(−3) will move the cursor to the 10–3, seventh row. If you give out of range values, the cursor will move to either beginning or end.
 • In a ResultSet object, calling absolute(1) is equivalent to calling first(), and calling absolute(−1) is equivalent to calling last().

Points to Remember

• You can use column name or column index with ResultSet methods. The index you use is the index of the ResultSet object, not the column number in the database table.
• A Statement object closes the current ResultSet object if a) the Statement object is closed, b) is re-executed, or c) is made to retrieve the next set of result. That means it is not necessary to call close() explicitly with ResultSet object; however, it is good practice to call close() once you are done with the object.
 • You may use the column name of a ResultSet object without worrying about the case: getXXX() methods accept case insensitive column names to retrieve the associated value.
• Think of a case when you have two columns in a ResultSet object with the same name. How you can retrieve the associated values using the column name? If you use a column name to retrieve the value, it will always point to the first column that matches with the given name. Hence, you have to use column index in this case to retrieve values associated with both columns.
• You might remember that the PreparedStatement interface inherits from Statement. However, PreparedStatement overrides all flavors of execute() methods. For instance, the behavior of executeUpdate() might be different from its base method.
• It is your responsibility to issue a correct SQL command; a JDBC Statement will not check for its correctness. For example, if there is a syntax error in the SQL command string, then you will not get a compiler error. Rather, you’ll get a MySQLSyntaxErrorException at runtime.
• You may call the appropriate get() method immediately after inserting a row using the insertRow() method. However, the values of the row are undefined.
• You may cancel any update you made using the method cancelRowUpdates(). However, you must call this method before calling the method updateRow(). In all other cases, it has no impact on the row.
 • While connecting to the database, you need to specify the correct username and password. If the provided username or password is not correct, you will get a SQLException.
• JDBC 4.1 introduces the capability to use try-with-resources statement to close resources (Connection, ResultSet, and Statement) automatically.

Throwing /Handling Exceptions
Here are some interesting points related to throwing /handling exceptions and releasing resources in a finally block:
• You can catch exceptions and wrap them into more generic exceptions and throw them higher up in the call stack. When you catch an exception and create a more general exception, you can retain reference to the original exception; this is called exception chaining. catch(LowLevelException lle) { // wrap the low-level exception to a higher-level exception; // also, chain the original exception to the newly thrown exception throw new HighLevelException(lle); } Chaining exceptions is useful for debugging purposes. When you get a general exception, you can check if there is a chained lower-level exception and try to understand why that lowerlevel exception occurred.
• The finally statement is always executed irrespective of whether the code in the try block throws an exception or not. Consider the following method. Will it return true or false to the caller? static boolean returnTest() { try { return true; } finally { return false; } } This method will always return false because finally is always invoked. In fact, if you use the –Xlint option, you’ll get this compiler warning: “finally clause cannot complete normally.” (Note that you can have a try block followed by either catch block or finally block or both blocks.)

Points to Remember

• You cannot assign to the resource variables declared in the try-with-resources within the body of the try-with-resources statement. This is to make sure that the same resources acquired in the try-with-resources header are released in the finally block.
• It is a common mistake to close a resource explicitly inside the try-with-resources statement. Remember that try-with-resources expands to calling the close() method in the finally block, so the expanded code will have a double call to the close() method. Consider the following code: try(Scanner consoleScanner = new Scanner(System.in)) { System.out.println("You typed the integer value: " + consoleScanner.nextInt()); consoleScanner.close(); // explicit call to close() method - remember that try-with-resources // statement will also expand to calling close() in finally method; // hence this will result in call to close() method in Scanner twice! } The documentation of the close() method in the Scanner class says that if the scanner object is already closed, then invoking the method again will have no effect. So, you are safe in this case. However, in general, you cannot expect all the resources to have implemented a close() method that is safe to call twice. So, it is a bad practice to explicitly call the close() method inside a try-with-resource statement.
Points to Remember
Here are some noteworthy points about the throws statement that could help you in the OCPJP 7 exam: • If a method does not have a throws clause, it does not mean it cannot throw any exceptions; it just means it cannot throw any checked exceptions.
 • It is a good practice to use the @throws JavaDoc tag to document the specific situations or cases in which an exception (both checked and unchecked) might be thrown from the method.
• It is a bad practice to use a throws clause to list unchecked exceptions that a method may throw. Why? Since the compiler cannot force the callers to handle unchecked exceptions, it does not make sense to list them in the throws clause. Rather, if a method can throw an unchecked exception, it is better to use the @throws clause to document that possibility.
• Static initialization blocks cannot throw any checked exceptions. Why? Remember that static initialization blocks are invoked when the class is loaded, so there is no way to handle the thrown exceptions in the caller. Further, there is no way to declare the checked exceptions in a throws clause.
 • Non-static initialization blocks can throw checked exceptions; however, all the constructors should declare those exceptions in their throws clause. Why? The compiler merges the code for non-static initialization blocks and constructors during its code generation phase, hence the throws clause of the constructor can be used for declaring the checked exceptions that a non-static initialization block can throw.
• An overriding method cannot declare more exceptions in the throws clause than the list of exceptions declared in the throws clause of the base method. Why? The callers of the base method see only the list of the exceptions given in the throws clause of that method and will declare or handle these checked exceptions in their code (and not more than that).
• An overriding method can declare more specific exceptions than the exception(s) listed in the throws clause of the base method; in other words, you can declare derived exceptions in the throws clause of the overriding method.
 • If a method is declared in two or more interfaces, and if that method declares to throw different exceptions in the throws clause, the implementation should list all of these exceptions.

Points to Remember

• There are many ways to get or create a Locale object. We list four options here for creating an instance of Italian locale that corresponds to the language code of it. Option 1: Use the constructor of the Locale class: Locale(String language, String country, String variant): Locale locale1 = new Locale("it", "", ""); Option 2: Use the forLanguageTag(String languageTag) method in the Locale class: Locale locale2 = Locale.forLanguageTag("it"); Option 3: Build a Locale object by instantiating Locale.Builder and then call setLanguageTag() from that object: Locale locale3 = new Locale.Builder().setLanguageTag("it").build(); Option 4: Use the predefined static final constants for locales in the Locale class: Locale locale4 = Locale.ITALIAN; You can choose the way to create a Locale object based on your need. For example, the Locale class has only a few predefined constants for locales. If you want a Locale object from one of the predefined ones, you can straightaway use it, or you’ll have to check which other option to use.
• Instead of calling Locale’s getDisplayCountry() method, which takes no arguments, you can choose the overloaded version of getDisplayCountry(Locale), which takes a Locale object as an argument. This will print the name of the country as in the passed locale. For example, for the call Locale.GERMANY.getDisplayCountry(), you’ll get the output “Deutschland” (that’s how Germans refer to their country); however, for the call Locale.GERMANY. getDisplayCountry(Locale.ENGLISH), you’ll get the output “Germany” (that’s how British refer to the country name Germany).

Points to Remember

• It is possible to achieve what the Fork/Join framework offers using basic concurrency constructs such as start() and join(). However, the Fork/Join framework abstracts many lower-level details and thus is easier to use. In addition, it is much more efficient to use the Fork/Join framework instead handling the threads at lower levels. Furthermore, using ForkJoinPool efficiently manages the threads and performs much better than conventional threads pools. For all these reasons, you are encouraged to use the Fork/Join framework.
 • Each worker thread in the Fork/Join framework has a work queue, which is implemented using a Deque. Each time a new task (or subtask) is created, it is pushed to the head of its own queue. When a task completes a task and executes a join with another task that is not completed yet, it works smart. The thread pops a new task from the head of its queue and starts executing rather than sleeping (in order to wait for another task to complete). In fact, if the queue of a thread is empty, then the thread pops a task from the tail of the queue belonging to another thread. This is nothing but a work-stealing algorithm.
• It looks obvious to call fork() for both the subtasks (if you are splitting in two subtasks) and call join() two times. It is correct—but inefficient. Why? Well, basically you are creating more parallel tasks than are useful. In this case, the original thread will be waiting for the other two tasks to complete, which is inefficient considering task creation cost. That is why you call fork() once and call compute() for the second task.
• The placement of fork() and join() calls are very important. For instance, let’s assume that you place the calls in following order: first.fork(); resultFirst = first.join(); resultSecond = second.compute(); This usage is a serial execution of two tasks, since the second task starts executing only after the first is complete. Thus, it is less efficient even than its sequential version since this version also includes cost of the task creation. The take-away: watch your placement of fork/join calls.

• Performance is not always guaranteed while using the Fork/Join framework. One of the reasons we mentioned earlier is the placement of fork/join calls.

No comments:

Post a Comment