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. ???