case-insensitive string sort

Collections.sort(stringList, String.CASE_INSENSITIVE_ORDER);

Singleton

A singleton is a class that can be instantiated only one time in a JVM per class loader. Repeated calls always return the same instance. Ensures that a class has only one instance, and provide a global point of access. It can be an issue if singleton class gets loaded by multiple class loaders.
private static variable, private construtor, public static getInstance.

public class OnlyOne {
    private static OnlyOne one = new OnlyOne();
    private OnlyOne(){… } //private constructor. This class cannot be instantiated from outside.
    public static OnlyOne getInstance() {
        return one;
    }
}
When to use: Use it when only a single instance of an object is required in memory for a single point of access.
  • Accessing application specific properties through a singleton object, which reads them for the first time from a properties file and subsequent accesses are returned from in-memory objects. Also there could be another piece of code, which periodically synchronizes the in-memory properties when the values get modified in the underlying properties file. This piece of code accesses the in-memory objects through the singleton object (i.e. global point of access).
  • Accessing in-memory object cache or object pool, or non-memory based resource pools like sockets, connections etc through a singleton object (i.e. global point of access).

Threads block on I/O

When threads are blocked (say due to time consuming reads or writes) on an I/O call inside an object’s synchronized method and also if the other methods of the object are also synchronized then the object is essentially frozen while the thread is blocked.
Be sure to not synchronize code that makes blocking calls, or make sure that a non-synchronized method exists on an object with synchronized blocking code. Although this technique requires some care to ensure that the resulting code is still thread safe, it allows objects to be responsive to other threads when a thread holding its locks is blocked.
Note: The java.nio.* package was introduced in JDK1.4. The coolest addition is nonblocking I/O (aka NIO that stands for New I/O).

2 synchronized methods in one object

If 2 different threads hit 2 different synchronized methods in an object at the same time will they both continue?  No. Only one method can acquire the lock.

Thread communication

  • The wait(), notify(), and notifyAll() methods are used to provide an efficient way for threads to communicate with each other. This communication solves the ‘consumer-producer problem’. This problem occurs when the producer thread is completing work that the other thread (consumer thread) will use.
  • wait and notify should be placed within synchronized code to ensure that the current code owns the monitor

  • The notify method will wake up one thread waiting to reacquire the monitor for the object. You cannot be certain which thread gets woken. If you have only one waiting thread then you do not have a problem. If you have multiple waiting threads then it will probably the thread that has been waiting the longest that will wake up. However you cannot be certain, and the priorities of the threads will influence the result. As a result you are generally advised to use notifyAll instead of notify, and not to make assumptions about scheduling or priorities. Of course this is not always possible and you may have to try to test your code on as many platforms as possible.

daemon thread

Daemon threads are sometimes called "service" threads. These are threads that normally run at a low priority and provide a basic service to a program or programs when activity on a machine is reduced. An example of a daemon thread that is continuously running is the garbage collector thread. This thread is provided by the JVM.

synchronization level

  • method: synchronize aMethod() {}
  • statement: synchronize(anyObject) {}
  • Often using a lock on a method level is too coarse. Why lock up a piece of code that does not access any shared resources by locking up an entire method. Since each object has a lock, dummy objects can be created to implement block level synchronization. The block level is more efficient because it does not lock the whole method.

thread, lock, monitor

thread synchronization A monitor is like a building that contains one special room that can be occupied by only one thread at a time. The room usually contains some data. From the time a thread enters this room to the time it leaves, it has exclusive access to any data in the room.
  • Entering the monitor building is called "entering the monitor."
  • Entering the special room inside the building is called "acquiring the monitor."
  • Occupying the room is called "owning the monitor,"
  • and leaving the room is called "releasing the monitor."
  • Leaving the entire building is called "exiting the monitor."
In addition to being associated with a bit of data, a monitor is associated with one or more bits of code, which in this book will be called monitor regions. To implement the mutual exclusion capability of monitors, the Java virtual machine associates a lock (sometimes called a mutex) with each object and class. A lock is like a privilege that only one thread can "own" at any one time. Threads need not obtain a lock to access instance or class variables. If a thread does obtain a lock, however, no other thread can obtain a lock on the same data until the thread that owns the lock releases it. Note that as a Java programmer, you never explicitly lock an object. Object locks are internal to the Java virtual machine. In your Java programs, you identify the monitor regions of your program by writing synchronized statements and methods. As the Java virtual machine runs your program, it automatically locks an object or class every time it encounters a monitor region. We have seen that critical sections are guarded by monitors and only the thread which has a lock associated with that monitor can access the resources in that critical section. In simple terms, consider a synchronized method or a synchronized block of code. Any thread before executing that code takes the lock over the object, the object in case of the synchronized method is the instance on which the synchronized method is called; in case of a synchronized block, it could any other object whose reference is given there. For example, synchronized (anyObject) { }.

yield vs sleep (thread)

When a task invokes yield(), it changes from running state to runnable state. When a task invokes sleep(), it changes from running state to waiting/sleeping state.

Thread states

  • Runnable — waiting for its turn to be picked for execution by the thread schedular based on thread priorities.
  • Running: The processor is actively executing the thread code. It runs until it becomes blocked, or voluntarily gives up its turn with this static method Thread.yield(). Because of context switching overhead, yield() should not be used very frequently.
  • Waiting: A thread is in a blocked state while it waits for some external processing such as file I/O to finish.
  • Sleeping: Java threads are forcibly put to sleep (suspended) with this overloaded method: Thread.sleep(milliseconds), Thread.sleep(milliseconds, nanoseconds);
  • Blocked on I/O: Will move to runnable after I/O condition like reading bytes of data etc changes.
  • Blocked on synchronization: Will move to Runnable when a lock is acquired.
  • Dead: The thread is finished working.

Creation of thread

  • extends Thread. Thread t = new ExtendThread();
  • implements Runnable. Thread t = new Thread(new ImplementRunnable());
t.start() The runnable interface is preferred, as it does not require your object to inherit a thread because when you need multiple inheritance, only interfaces can help you.

Process/Thread

A process is an execution of a program but a thread is a single execution sequence within the process. A process can contain multiple threads. A thread is sometimes called a lightweight process. A JVM runs in a single process and threads in a JVM share the heap belonging to that process. That is why several threads may access the same object. Threads share the heap and have their own stack space. This is how one thread’s invocation of a method and its local variables are kept thread safe from other threads. But the heap is not thread-safe and must be synchronized for thread safety.

throw/throws

throw new MyException(“I threw my own exception.”) To declare an exception: public myMethod() throws MyException {…} throws in the method signature.

Best Practice for exception handling

  • Catch subclass exception first.
  • Throw an exception early
The exception stack trace helps you pinpoint where an exception occurred by showing us the exact sequence of method calls that lead to the exception. By throwing your exception early, the exception becomes more accurate and more specific. Use if(file==null) throw new IllegalArgumentException instead of new FileInputStream(file)
  • Avoid suppressing or ignoring exceptions.
  • Also avoid using exceptions just to get a flow control.
  • Catch the exception at the appropriate layer
You should not try to catch the exception before your program can handle it in an appropriate manner. The natural tendency when a compiler complains about a checked exception is to catch it so that the compiler stops reporting errors. The best practice is to catch the exception at the appropriate layer (e.g. an exception thrown at an integration layer can be caught at a presentation layer in a catch {} block), where your program can either meaningfully recover from the exception and continue to execute or log the exception only once in detail, so that user can identify the cause of the exception. Note: Due to heavy use of checked exceptions and minimal use of unchecked exceptions, there has been a hot debate in the Java community regarding true value of checked exceptions.
  • Use checked exceptions when the client code can take some useful recovery action
  • Use unchecked exception when client code cannot do anything based on information in exception.
For example, convert your SQLException into another checked exception if the client code can recover from it and convert your SQLException into an unchecked (i.e. RuntimeException) exception, if the client code cannot do anything about it.

Exception

All the exceptions except RuntimeException are compiler checked exceptions. If a method is capable of throwing a checked exception it must declare it in its method header or handle it in a try/catch block. Failure to do so raises a compiler error. So checked exceptions can, at compile time, greatly reduce the occurrence of unhandled exceptions surfacing at runtime in a given application at the expense of requiring large throws declarations and encouraging use of poorly constructed try/catch blocks. A RuntimeException class represents exceptions that occur within the Java virtual machine (during runtime). NullPointerException, ArrayOutOfBoundException. The cost of checking for the runtime exception often outweighs the benefit of catching it. Attempting to catch or specify all of them all the time would make your code unreadable and unmaintainable. The compiler allows runtime exceptions to go uncaught and unspecified. If you like, you can catch these exceptions just like other exceptions. However, you do not have to declare it in your “throws" clause or catch it in your catch clause. In addition, you can create your own RuntimeException subclasses and this approach is probably preferred at times because checked exceptions can complicate method signatures and can be difficult to follow.

PreparedStatement

http://forum.java.sun.com/thread.jsp?forum=48&thread=538747&tstart=0&trange=15 A prepared statement gets compiled by the database during the first execution. Most databases have a query cache, and the query will stay there for a while. So, the next invocation of the statement won't need to be recompiled by the database (if it still is in the cache). That will speed things up. The best reasons for using PreparedStatements are these: (1) Executing the same query multiple times in loop, binding different parameter values each time, and (2) Using the setDate()/setString() methods to escape dates and strings properly, in a database-independent way. (3) SQL injection attacks on a system are virtually impossible when using Prepared Statements. I have read the statement has less initial overhead so its better for a one time call - for instance to get some information to populate a combo box. PreparedStatement has more initial overhead but it is dramatically reduced on subsequent calls because its precompiled at the DB so it would be used when you need to execute the same statement repeatedly with different parameters. I don't worry about the performance hit until it becomes a problem. Network latency is usually the bigger problem, and that has to do with the way queries are done rather the Statement vs PreparedStatement.

Try-Catch-Finally

Throw early catch later.

In the normal case, control reaches the end of the try block and then proceeds to the finally block, which performs any necessary cleanup. If control leaves the try block because of a return, continue, or break statement, the finally block is executed before control transfers to its new destination.

If an exception occurs in the try block, and there is an associated catch block to handle the exception, control transfers first to the catch block and then to the finally block. If there is no local catch block to handle the exception, control transfers first to the finally block, and then propagates up to the nearest containing catch clause that can handle the exception.

If a finally block itself transfers control with a return, continue, break, or throw statement or by calling a method that throws an exception, the pending control transfer is abandoned, and this new transfer is processed. For example, if a finally clause throws an exception, that exception replaces any exception that was in the process of being thrown. If a finally clause issues a return statement, the method returns normally, even if an exception has been thrown and has not been handled yet.

try and finally can be used together without exceptions or any catch clauses. In this case, the finally block is simply cleanup code that is guaranteed to be executed, regardless of any break, continue, or return statements within the try clause.

Joins: Innter, Outer,Equi, Natural..

An inner join essentially combines the records from two tables A and B based on a given join predicate. The cross product of all records in table is computed. Thus, each record in table A is combined with every record in table B. Only those records in the joined table that satisfy the join predicate remain. This is the most common type of join used in applications, and is considered the default join type.
 
An equi-join is a specific type of comparator-based join, or theta join, that uses only equality comparisons in the join predicate. Using other comparison operators such as < disqualifies a join as equi-join. The query 2 sections above is already an example for equi-joins.
 
A natural join is a further specialization of equi-joins. The join predicate is implicitly given by comparing all columns in both tables that have the same column name in the tables to be joined. The resulting joined table contains only one column for each pair of equally-named columns.
 
Outer joins do not require that each record in the two joined tables has a matching record in the other table. The record is retained in the joined table if no other matching record exists. Outer joins are subdivided further into left outer joins, right outer joins, and full outer joins, depending from which table the rows shall be retained (left, right, or both).
 
The result of a left outer join for tables A and B always contains all records of the "left" table (A), even if the join condition does not find any matching record in the "right" table (B). This means that if the ON clause matches 0 (zero) records in B, a row in the result will still be returned — but with NULL in each column from B.
 
A right outer join is much like a left outer join, except that the tables are reversed. Every record from the "right" table (B) will be in the joined table at least once. If there is no matching row from the "left" table (A), and NULL will be used for columns from A for those records that have any match in A.
 
A full outer join combines the results of both left and right outer joins. The joined table will contain all records from both tables, and fill in NULLs for missing matches on either side.

garbage collector

  • Each time an object is created in Java, it goes into the area of memory known as heap. The Java heap is called the garbage collectable heap. The garbage collection cannot be forced. The garbage collector runs in low memory situations. When it runs, it releases the memory allocated by an unreachable object. The garbage collector runs on a low priority daemon (background) thread. You can nicely ask the garbage collector to collect garbage by calling System.gc() but you can’t force it.
  • What is an unreachable object? An object’s life has no meaning unless something has reference to it. If you can’t reach it then you can’t ask it to do anything. Then the object becomes unreachable and the garbage collector will figure it out. Java automatically collects all the unreachable objects periodically and releases the memory consumed by those unreachable objects to be used by the future reachable objects.
  • We can use the following options with the Java command to enable tracing for garbage collection events. -verbose:gc reports on each garbage collection event.

type casting

  • Type casting means treating a variable of one type as though it is another type.
  • upcast = implicit, downcast = explicit
  • When up casting primitives as shown below from left to right, automatic conversion occurs. But if you go from right to left, down casting or explicit casting is required. byte 􀃆 short 􀃆 int 􀃆 long 􀃆 float 􀃆 double
  • When it comes to object references you can always cast from a subclass to a superclass because a subclass object is also a superclass object. You can cast an object implicitly to a super class type (i.e. upcasting). If this were not the case polymorphism wouldn’t be possible.
  • You can cast down the hierarchy as well but you must explicitly write the cast and the object must be a legitimate instance of the class you are casting to. The ClassCastException is thrown to indicate that code has attempted to cast an object to a subclass of which it is not an instance. We can deal with the problem of incorrect casting in two ways: Use the exception handling mechanism to catch ClassCastException. Use the instanceof statement to guard against incorrect casting.
  • Design pattern: The “instanceof” and “typecast” constructs are shown for the illustration purpose only. Using these constructs can be unmaintainable due to large if and elseif statements and can affect performance if used in frequently accessed methods or loops. Look at using visitor design pattern to avoid these constructs. (Refer Q11 in How would you go about section…).
  • We can also get a ClassCastException when two different class loaders load the same class because they are treated as two different classes.

Nested Classes

l: TIJ If you don’t need a connection between the inner class object and the outer class object, then you can make the inner class static. This is commonly called a nested class.

Inner classes

l: TIJ The inner class is a valuable feature because it allows you to group classes that logically belong together and to control the visibility of one within the other. However, it’s important to understand that inner classes are distinctly different from composition. the inner class can access methods and fields from the enclosing class as if it owned them. Where should you use inner classes? Code without inner classes is more maintainable and readable. When you access private data members of the outer class, the JDK compiler creates package-access member functions in the outer class for the inner class to access the private members. This leaves a security hole. In general we should avoid using inner classes. Use inner class only when an inner class is only relevant in the context of the outer class and/or inner class can be made private so that only outer class can access it. Inner classes are used primarily to implement helper classes like Iterators, Comparators etc which are used in the context of an outer class.

re-entrant, recursive and idempotent

  • A method in stack is re-entrant allowing multiple concurrent invocations that do not interfere with each other. To be reentrant, a function must hold no static data, must not return a pointer to static data, must work only on the data provided to it by the caller, and must not call non-reentrant functions. http://en.wikipedia.org/wiki/Reentrant
  • A function is recursive if it calls itself. Given enough stack space, recursive method calls are perfectly valid in Java though it is tough to debug. Recursive functions are useful in removing iterations from many sorts of algorithms. All recursive functions are re-entrant but not all re-entrant functions are recursive.
  • Idempotent methods are methods that repeated calls to the same method with the same arguments yield same results. For example clustered EJBs, which are written with idempotent methods, can automatically recover from a server failure as long as it can reach another server.

stack and heap memory

why java is slow http://www.itec.uni-klu.ac.at/~harald/CSE/Content/performance.html http://www.phptr.com/articles/printerfriendly.asp?p=31755&rl=1
  • There are two kinds of memory used in Java. These are called stack memory and heap memory. Stack memory stores primitive types and the addresses of objects. The object values are stored in heap memory.
  • The primitive variables (local method variable) are allocated in the stack, but if they are member variables (i.e. fields of a class), they will be stored in the heap.
  • In Java methods local variables are pushed into stack when a method is invoked and stack pointer is decremented when a method call is completed.
  • In a multi-threaded application each thread will have its own stack but will share the same heap. The stack is threadsafe (each thread will have its own stack) but the heap is not threadsafe unless guarded with synchronisation through your code. This is why care should be taken in your code to avoid any concurrent access issues in the heap space.

final/finally/finalize()

  • final - constant declaration. Refer Q27 in Java section.
  • finally - handles exception. The finally block is optional and provides a mechanism to clean up regardless of what happens within the try block (except System.exit(0) call). Use the finally block to close files or to release other system resources like database connections, statements etc. (Refer Q45 in Enterprise section)
  • finalize() - method helps in garbage collection. A method that is invoked before an object is discarded by the garbage collector, allowing it to clean up its state. Should not be used to release non-memory resources like file handles, sockets, database connections etc because Java has only a finite number of these resources and you do not know when the garbage collection is going to kick in to release these non-memory resources through the finalize() method.

final modifier

A final class can’t be extended i.e. A final class may not be subclassed.
A final method can’t be overridden when its class is inherited.
A final variable can't be changed (i.e. it is a constant).

private constructor

Private constructor is used if you do not want other classes to instantiate the object. The instantiation is done by a public static method within the same class.
Used in the singleton pattern. (Refer Q45 in Java section).
Used in the factory method pattern (Refer Q46 in Java section).
Used in utility classes e.g. StringUtils etc.

static method

Static methods prove useful for creating utility classes, singleton classes and factory methods Utility classes are not meant to be instantiated. Improper coding of utility classes can lead to procedural coding. java.lang.Math, java.util.Collections etc are examples of utility classes in Java.

Static/Instance Variable

Class variables are called static variables. There is only one occurrence of a class variable per JVM per class loader. When a class is loaded the class variables (aka static variables) are initialised.
Instance variables are non-static and there is one occurrence of an instance variable in each class instance (i.e. each object).
A static variable is used in the singleton pattern. (Refer Q45 in Java section). A static variable is used with a final modifier to define constants.

Java Object methods

toString, clone, equals, hashCode, finalize, getClass wait, notify,

shallow/deep clone

Shallow copy: If a shallow copy is performed on object then it is copied but its contained objects are not. The contained objects are affected by changes to cloned object. Java supports shallow cloning of objects by default when a class implements the java.lang.Cloneable interface.
Deep copy: If a deep copy is performed on an object then not only object has been copied but the objects contained within it have been copied as well. Serialization can be used to achieve deep cloning. Deep cloning through serialization is faster to develop and easier to maintain but carries a performance overhead.
 
For example, invoking clone() method on a HashMap returns a shallow copy of HashMap instance, which means the keys and values themselves are not cloned. If you want a deep copy then a simple method is to serialize the HashMap to a ByteArrayOutputSream and then deserialize it. This creates a deep copy but does require that
all keys and values in the HashMap are Serializable. Its primary advantage is that it will deep copy any arbitrary object graph.

Improve IO performance

Minimise accessing the hard disk.
Minimise accessing the underlying operating system.
Minimise processing bytes and characters individually.
1. Use buffering to minimise disk access and underlying operating system. As shown below, with buffering large chunks of a file are read from a disk and then accessed a byte or character at a time. BufferedInputStream, BufferedReader
2. Use the NIO package, if you are using JDK 1.4 or later, which uses performance-enhancing features like buffers to hold data, memory mapping of files, non-blocking I/O operations etc.
3. I/O performance can be improved by minimising the calls to the underlying operating systems. The Java runtime itself cannot know the length of a file, querying the file system for isDirectory(), isFile(), exists() etc must query the underlying operating system.
4. Where applicable caching can be used to improve performance by reading in all the lines of a file into a Java collection class like an ArrayList or a HashMap and subsequently access the data from an in-memory collection instead of the disk.

NIO

The New I/O (NIO): more scalable and better performance Java has long been not suited for developing programs that perform a lot of I/O operations. Furthermore, commonly needed tasks such as file locking, non-blocking and asynchronous I/O operations and ability to map file to memory were not available. Non-blocking I/O operations were achieved through work around such as multithreading or using JNI. The New I/O API (aka NIO) in J2SE 1.4 has changed this situation. A server’s ability to handle several client requests effectively depends on how it uses I/O streams. When a server has to handle hundreds of clients simultaneously, it must be able to use I/O services concurrently. One way to cater for this scenario in Java is to use threads but having almost one-to-one ratio of threads (100 clients will have 100 threads) is prone to enormous thread overhead and can result in performance and scalability problems due to consumption of memory stacks and CPU context switching. To overcome this problem, a new set of non-blocking I/O classes have been introduced to the Java platform in java.nio package.
 
The non-blocking I/O mechanism is built around Selectors and Channels. Channels, Buffers and Selectors are the core of the NIO. A Channel class represents a bi-directional communication channel (similar to InputStrean and OutputStream) between datasources such as a socket, a file, or an application component, which is capable of performing one or more I/O operations such as reading or writing. Channels can be non-blocking, which means, no I/O operation will wait for data to be read or written to the network. The good thing about NIO channels is that they can be asynchronously interrupted and closed. So if a thread is blocked in an I/O operation on a channel, another thread can interrupt that blocked thread. Buffers hold data. Channels can fill and drain Buffers. Buffers replace the need for you to do your own buffer management using byte arrays. There are different types of Buffers like ByteBuffer, CharBuffer, DoubleBuffer, etc. A Selector class is responsible for multiplexing (combining multiple streams into a single stream) by allowing a single thread to service multiple channels. Each Channel registers events with a Selector. When events arrive from clients, the Selector demultiplexes (separating a single stream into multiple streams) them and dispatches the events to corresponding Channels. To achieve non-blocking I/O a Channel class must work in conjunction with a Selector class.

Design pattern: NIO uses a reactor design pattern, which demultiplexes events (separating single stream into multiple streams) and dispatches them to registered object handlers. The reactor pattern is similar to an observer pattern (aka publisher and subscriber design pattern), but an observer pattern handles only a single source of events (i.e. a single publisher with multiple subscribers) where a reactor pattern handles multiple event sources (i.e. multiple publishers with multiple subscribers). The intent of an observer pattern is to define a one-to-many dependency so that when one object (i.e. the publisher) changes its state, all its dependents (i.e. all its
subscribers) are notified and updated correspondingly. Another sought after functionality of NIO is its ability to map a file to memory. There is a specialized form of a
Buffer known as MappedByteBuffer, which represents a buffer of bytes mapped to a file. To map a file to MappedByteBuffer, you must first get a channel for a file. Once you get a channel then you map it to a buffer and subsequently you can access it like any other ByteBuffer. Once you map an input file to a CharBuffer, you can do
pattern matching on the file contents. This is similar to running “grep” on a UNIX file system. Another feature of NIO is its ability to lock and unlock files. Locks can be exclusive or shared and can be held on a contiguous portion of a file. But file locks are subject to the control of the underlying operating system.

Java IO stream

Java input and output is defined in terms of an abstract concept called a “stream”, which is a sequence of data.
There are 2 kinds of streams.
  • Byte streams (8 bit bytes) ?? Abstract classes are: InputStream and OutputStream
  • Character streams (16 bit UNICODE) ?? Abstract classes are: Reader and Writer

Design pattern: java.io.* classes use the decorator design pattern. The decorator design pattern attaches responsibilities to objects at runtime. Decorators are more flexible than inheritance because the inheritance attaches responsibility to classes at compile time. The java.io.* classes use the decorator pattern to construct different combinations of behaviour at runtime based on some basic classes.
File file = new File(“c:/temp”);
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
 
Decorators decorate an object by enhancing or restricting functionality of an object it decorates. The decorators add or restrict functionality to decorated Java objects either before or after forwarding the request. At runtime the BufferedInputStream (bis), which is a decorator (aka a wrapper around decorated object), forwards the method call to its decorated object FileInputStream (fis). The ‘bis’ will apply the additional functionality of buffering around the lower level file (i.e. fis) I/O.

Serialization

Serialization is a process of reading or writing an object. It is a process of saving an object’s state to a sequence of bytes, as well as a process of rebuilding those bytes back into a live object at some future time. An object is marked serializable by implementing the java.io.Serializable interface, which is only a marker interface -- it simply allows the serialization mechanism to verify that the class can be persisted, typically to a file.

Transient variables cannot be serialized. The fields marked transient in a serializable object will not be transmitted in the byte stream. An example would be a file handle or a database connection. Such objects are only meaningful locally. So they should be marked as transient in a serializable class.
Serialization can adversely affect performance since it:
  • Depends on reflection.
  • Has an incredibly verbose data format.
  • Is very easy to send surplus data.

When to use serialization? Do not use serialization if you do not have to. A common use of serialization is to use it to send an object over the network or if the state of an object needs to be persisted to a flat file or a database. (Refer Q57 on Enterprise section). Deep cloning or copy can be achieved through serialization. This may be fast to code but will have performance implications (Refer Q22 in Java section).
The objects stored in an HTTP session should be serializable to support in-memory replication of sessions to achieve scalability (Refer Q20 in Enterprise section). Objects are passed in RMI (Remote Method Invocation) across network using serialization (Refer Q57 in Enterprise section).

pass-by-reference vs pass-by-value

http://jeeinterviews.com/typo/TIJ3/TIJ319.htm#Index2153

This brings up the terminology issue, which always seems good for an argument. The term is “pass by value,” and the meaning depends on how you perceive the operation of the program. The general meaning is that you get a local copy of whatever you’re passing, but the real question is how you think about what you’re passing. When it comes to the meaning of “pass by value,” there are two fairly distinct camps: Feedback

  1. Java passes everything by value. When you’re passing primitives into a method, you get a distinct copy of the primitive. When you’re passing a reference into a method, you get a copy of the reference. Ergo, everything is pass by value. Of course, the assumption is that you’re always thinking (and caring) that references are being passed, but it seems like the Java design has gone a long way toward allowing you to ignore (most of the time) that you’re working with a reference. That is, it seems to allow you to think of the reference as “the object,” since it implicitly dereferences it whenever you make a method call. Feedback
  2. Java passes primitives by value (no argument there), but objects are passed by reference. This is the world view that the reference is an alias for the object, so you don’t think about passing references, but instead say “I’m passing the object.” Since you don’t get a local copy of the object when you pass it into a method, objects are clearly not passed by value. There appears to be some support for this view within Sun, since at one time, one of the “reserved but not implemented” keywords was byvalue (This will probably never be implemented). Feedback

Having given both camps a good airing, and after saying “It depends on how you think of a reference,” I will attempt to sidestep the issue. In the end, it isn’t that important—what is important is that you understand that passing a reference allows the caller’s object to be changed unexpectedly.

 

http://jeeinterviews.com/scjp/source/10909/bbl0037.html

The bottom line on pass-by-value: the called method can't change the caller's variable, although for object reference variables, the called method can change the object the variable referred to. What's the difference between changing the variable and changing the object? For object references, it means the called method can't reassign the caller's original reference variable and make it refer to a different object, or null.

String vs StringBuffer/StringBuilder

String is immutable: you can’t modify a string object but can replace it by creating a new instance. Creating a new instance is rather expensive.
StringBuffer is mutable: use StringBuffer or StringBuilder when you want to modify the contents. StringBuilder was added in Java 5 and it is identical in all respects to StringBuffer except that it is not synchronised, which make it slightly faster at the cost of not being thread-safe.
StringBuffer expands as needed, which is costly however, so it would be better to initilise the StringBuffer with the correct size from the start as shown.
Another important point is that creation of extra strings is not limited to ‘overloaded mathematical operators’ (“+”) but there are several methods like concat(), trim(), substring(), and replace() in String classes that generate new string instances. So use StringBuffer or StringBuilder for computation intensive operations, which offer better performance.

Methods to override for HashMap key

You should override the equals() and hashCode() methods from the Object class. The default implementation of
the equals() and hashcode(), which are inherited from the java.lang.Object uses an object instance’s memory
location (e.g. MyObject@6c60f2ea). This can cause problems when two instances of the car objects have the
same colour but the inherited equals() will return false because it uses the memory location, which is different for
the two instances. Also the toString() method can be overridden to provide a proper string representation of your
object. Points to consider:
• If a class overrides equals(), it must override hashCode().
• If 2 objects are equal, then their hashCode values must be equal as well.
• If a field is not used in equals(), then it must not be used in hashCode().
• If it is accessed often, hashCode() is a candidate for caching to enhance performance.

Initial Capacity & Load Factor

Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.) This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.

This implementation provides constant-time performance for the basic operations (get and put), assuming the hash function disperses the elements properly among the buckets. Iteration over collection views requires time proportional to the "capacity" of the HashMap instance (the number of buckets) plus its size (the number of key-value mappings). Thus, it's very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important.

An instance of HashMap has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the capacity is roughly doubled by calling the rehash method.

As a general rule, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the lookup cost (reflected in most of the operations of the HashMap class, including get and put). The expected number of entries in the map and its load factor should be taken into account when setting its initial capacity, so as to minimize the number of rehash operations. If the initial capacity is greater than the maximum number of entries divided by the load factor, no rehash operations will ever occur.

If many mappings are to be stored in a HashMap instance, creating it with a sufficiently large capacity will allow the mappings to be stored more efficiently than letting it perform automatic rehashing as needed to grow the table.

Note that this implementation is not synchronized. If multiple threads access this map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the map. If no such object exists, the map should be "wrapped" using the Collections.synchronizedMap method. This is best done at creation time, to prevent accidental unsynchronized access to the map:

 Map m = Collections.synchronizedMap(new HashMap(...));  

The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

Best Practices for Collections

l: scjp  Use ArrayLists, HashMap etc as opposed to Vector, Hashtable etc, where possible to avoid any synchronization overhead. Even better is to use just arrays where possible. If multiple threads concurrently access a collection and at least one of the threads either adds or deletes an entry into the collection, then the collection must be externally synchronized. This is achieved by: Map myMap = Collections.synchronizedMap (myMap); List myList = Collections.synchronizedList (myList); // use java.util.concurrent package for J2SE 5.0  Set the initial capacity of a collection appropriately (e.g. ArrayList, HashMap etc). This is because collection classes like ArrayList, HashMap etc must grow periodically to accommodate new elements. But if you have a very large array, and you know the size in advance then you can speed things up by setting the initial size appropriately. For example: HashMaps/Hashtables need to be created with sufficiently large capacity to minimise rehashing (which happens every time the table grows). HashMap has two parameters initial capacity and load factor that affect its performance and space requirements. Higher load factor values (default load factor of 0.75 provides a good trade off between performance and space) will reduce the space cost but will increase the lookup cost of myMap.get(&) and myMap.put(&) methods. When the number of entries in the HashMap exceeds the current capacity * loadfactor then the capacity of the HasMap is roughly doubled by calling the rehash function. It is also very important not to set the initial capacity too high or load factor too low if iteration performance or reduction in space is important.  Program in terms of interface not implementation: For example you might decide a LinkedList is the best choice for some application, but then later decide ArrayList might be a better choice for performance reason. Use: List list = new ArrayList(100); // program in terms of interface & set the initial capacity. Instead of: ArrayList list = new ArrayList();  Return zero length collections or arrays as opposed to returning null: Returning null instead of zero length collection is more error prone, since the programmer writing the calling method might forget to handle a return value of null.  Avoid storing unrelated or different types of objects into same collection: This is analogous to storing items in pigeonholes without any labelling. To store items use value objects or data objects (as opposed to storing every attribute in an ArrayList or HashMap). Provide wrapper classes around your collection API classes like ArrayList, Hashmap etc as shown in better approach column. Also where applicable consider using composite design pattern, where an object may represent a single object or a collection of objects. If you are using J2SE 5.0 then make use of generics.

Collection Framework

Collections come in four basic flavors:

  • Lists Lists of things (classes that implement List).

  • Sets Unique things (classes that implement Set).

  • Maps Things with a unique ID (classes that implement Map).

  • Queues Things arranged by the order in which they are to be processed.

ArrayList, Vector, LinkedList

List Interface

A List cares about the index. The one thing that List has that non-lists don't have is a set of methods related to the index. Those key methods include things like get(int index), indexOf(Object o), add(int index, Object obj), and so on. All three List implementations are ordered by index position—a position that you determine either by setting an object at a specific index or by adding it without specifying position, in which case the object is added to the end. The three List implementations are described in the following sections.

ArrayList Think of this as a growable array. It gives you fast iteration and fast random access. To state the obvious: it is an ordered collection (by index), but not sorted. You might want to know that as of version 1.4, ArrayList now implements the new RandomAccess interface—a marker interface (meaning it has no methods) that says, "this list supports fast (generally constant time) random access." Choose this over a LinkedList when you need fast iteration but aren't as likely to be doing a lot of insertion and deletion.

Vector Vector is a holdover from the earliest days of Java; Vector and Hashtable were the two original collections, the rest were added with Java 2 versions 1.2 and 1.4. A Vector is basically the same as an ArrayList, but Vector methods are synchronized for thread safety. You'll normally want to use ArrayList instead of Vector because the synchronized methods add a performance hit you might not need. And if you do need thread safety, there are utility methods in class Collections (synchronizedList) that can help. Vector is the only class other than ArrayList to implement RandomAccess.

LinkedList A LinkedList is ordered by index position, like ArrayList, except that the elements are doubly-linked to one another. This linkage gives you new methods (beyond what you get from the List interface) for adding and removing from the beginning or end, which makes it an easy choice for implementing a stack or queue. Keep in mind that a LinkedList may iterate more slowly than an ArrayList, but it's a good choice when you need fast insertion and deletion. As of Java 5, the LinkedList class has been enhanced to implement the java.util.Queue interface. As such, it now supports the common queue methods: peek(), poll(), and offer().

l: scjp

Overload

Overloading a method often means you're being a little nicer to those who call your methods, because your code takes on the burden of coping with different argument types rather than forcing the caller to do conversions prior to invoking your method. The rules are simple:
  • Overloaded methods MUST change the argument list.

  • Overloaded methods CAN change the return type.

  • Overloaded methods CAN change the access modifier.

  • Overloaded methods CAN declare new or broader checked exceptions.

  • A method can be overloaded in the same class or in a subclass.

Override

1. The key benefit of overriding is the ability to define behavior that's specific to a particular subclass type. 2. You must implement the abstract method in the subclass unless the subclass is also abstract. 3. The overriding method cannot have a more restrictive access modifier. (Think of polymorphism) 4. The argument list must exactly match 5. The return type must be the same as, or a subtype of, the return type declared in the original overridden method in the superclass. 6. Exception: any unchecked exception (runtime) or narrower and fewer checked exceptions 7. cannot override a method marked static. ???