Showing posts with label Java Antipatterns. Show all posts
Showing posts with label Java Antipatterns. Show all posts

Monday, July 4, 2011

Java Common Mistakes (Part 3)

1. Accessing non-static member variables from static methods (such as main)
Particularly when first introduced to Java, Many programmers have problems with accessing member variables from their main method. The method signature for main is marked static - meaning that we don't need to create an instance of the class to invoke the main method. For example, a Java Virtual Machine (JVM) could call the class MyApplication like this :-

MyApplication.main ( command_line_args );

This means, however, that there isn't an instance of MyApplication - it doesn't have any member variables to access! Take for example the following application, which will generate a compiler error message.

public class MyApplication {
public String my_member_variable = "somedata";
public static void main (String args[]) {
// Access a non-static member from static method
System.out.println ("This generates a compiler error" + my_member_variable );
}
}

If you want to access its member variables from a non-static method (like main), you must create an instance of the object. Here's a simple example of how to correctly write code to access non-static member variables, by first creating an instance of the object.

public class MyApplication {
public String my_member_variable = "somedata";
public static void main (String args[]) {
MyApplication myApplication = new MyApplication();
// Access member variable of myApplication
System.out.println ("This WON'T generate a compilation error" + myApplication.my_member_variable);
}
}


2. Null pointers
Null pointers are one of the most common errors that Java programmers make. Compilers can't check this one for you - it will only surface at runtime, and if you don't discover it, your users certainly will.

When an attempt to access an object is made, and the reference to that object is null, a NullPointerException will be thrown. The cause of null pointers can be varied, but generally it means that either you haven't initialized an object, or you haven't checked the return value of a function.

Many functions return null to indicate an error condition - but unless you check your return values, you'll never know what's happening. Since the cause is an error condition, normal testing may not pick it up - which means that your users will end up discovering the problem for you. If the API function indicates that null may be returned, be sure to check this before using the object reference!

Another cause is where your initialization has been sloppy, or where it is conditional.

Sometimes your variables (the array of strings) will be initialized, and other times they won't. One easy solution is to check BEFORE you attempt to access a variable in an array that it is not equal to null.

3. Confusion over passing by value, and passing by reference
This can be a frustrating problem to diagnose, because when you look at the code, you might be sure that its passing by reference, but find that its actually being passed by value. Java uses both, so you need to understand when you're passing by value, and when you're passing by reference.

When you pass a primitive data type, such as a char, int, float, or double, to a function then you are passing by value. That means that a copy of the data type is duplicated, and passed to the function.
If the function chooses to modify that value, it will be modifying the copy only. Once the function finishes, and control is returned to the returning function, the "real" variable will be untouched, and no changes will have been saved. If you need to modify a primitive data type, make it a return value for a function, or wrap it inside an object

When you pass a Java object, such as an array, a vector, or a string, to a function then you are passing by reference. Yes - a String is actually an object, not a primitive data type. So that means that if you pass an object to a function, you are passing a reference to it, not a duplicate. Any changes you make to the object's member variables will be permanent - which can be either good or bad, depending on whether this was what you intended.

On a side note, since String contains no methods to modify its contents, you might as well be passing by value.


4. In Java, a single equal sign ( = ) is an entirely different operator than a double equal sign ( == ). In most cases, use the double equal sign when creating a loop or conditional statement and use the single equal sign everywhere else. For example:
To compare a and b for equality, use a==b; (note the double equal sign).
Where b has the same value as a, use a=b; (note the single equal sign).

5. Forget to initialize Object Arrays
A Java array declared for an object type is really an array of object references. Creating the array, with new, simply creates empty references. The elements of the array are all set to null. You have to set each element to a real object reference in order to fill the array. Many students misunderstand this and they think that creating an object array creates all the actual objects. (In many cases, this is a concept that the student carries over from C++, where creating an array of objects does create all the objects by calling their default constructor.)

In the following example, the student wants to create three StringBuffer objects to hold some data. This code will compile, but will throw a NullPointerException on the last line where one of the (non-existent) objects is used.

Mistake Example:

// Create an array of data buffers
StringBuffer[] myTempBuffers;
myTempBuffers = new StringBuffer[3];
myTempBuffers[0].append(data);

To correct this problem, it is important to remember to initialize the elements of any Java array.

// Create an array of data buffers and initialize it.
StringBuffer[] myTempBuffers;
myTempBuffers = new StringBuffer[3];
for (int ix = 0; ix < myTempBuffers.length; ix++)
myTempBuffers[ix] = new StringBuffer();
myTempBuffers[0].append(data);

6. Putting several public classes in one file
Java source code files have a special relationship to the classes in those files.
The rules can be summarized as follows:

1. Any Java class is defined in exactly one source file
2. At most one public class may be defined by any one source file
3. If a public class appears in a source file, the name of the file and the name of the class must be the same (case sensitive).

Sometimes, a student will forget the second rule in their rush to code up a lab exercise or project. They'll put two or more public classes into a source file.

7. Forgetting to make sure that system resources (network or database connections, streams) are freed by calling close() in a finally block. Assuming that you can't produce a memory leak in Java because it has garbage collection

8. Using the Singleton design pattern in an environment with multiple class loaders

9. Omitting break from case statements

10. Using a variable before it is given a value

11. Forgetting that arguments are passed by reference to methods if they are objects

12. Mistyping the name of a method when overriding

13. Forgetting that Java is zero-indexed

14. Capitalization errors
This is one of the most frequent errors that we all make. It's so simple to do, and sometimes one can look at an uncapitalized variable or method and still not spot the problem. You can easily train yourself to make less of them. There's a very simple trick you can learn :-

1. all methods and member variables in the Java API begin with lowercase letters
2. all methods and member variables use capitalization where a new word begins e.g - getDoubleValue()

15. Hashtable, HashMap and HashSet are overrated
These classes are extremely popular. Because they have great usability for the developer. Unfortunately they are also horribly inefficient. Hashtable and HashMap wrap every key/value pair into an Entry wrapper object. An Entry object is surprisingly large. Not only does it hold a reference to key and value, but also stores the hash code and a forward reference to the next Entry of the hash bucket. When you look at heap dumps with a memory analyzer you will be shocked by how much space is wasted by them in large applications like an application server. When you look at the source code of HashSet you will see that the developers were extremely lazy and just used a HashMap in the backend!

Before using any of these classes, think again. IdentityHashMap can be a viable alternative. But be careful, it intentionally breaks the Map interface. It is much more memory efficient by implementing an open hashtable (no buckets), doesn't need an Entry wrapper and uses a simple Object[] as its backend. Instead of a HashSet a simple ArrayList may do similarly well (you can use contains(Object)) as long as it's small and lookups are rare.
For Sets that contain only a handful of entries the whole hashing is overkill and the memory wasted for the HashMap backend plus the wrapper objects is just nuts. Just use an ArrayList or even an array.

Friday, August 20, 2010

Java Common Mistakes (Part 1)

1. Returning Null, a method which returns a collection or an array,
Null should never be returned from a method returning a collection or an array. Instead return a empty array (a static final empty array) or one of the empty collections (e.g. Collections.EMPTY_LIST assuming the client should not be modifying the collection).

2. Unnecessary use of thread safety of StringBuffer
Use StringBuilder rather then StringBuffer, unless synchronization is required. StringBuilder is not thread safe, and therefore avoids the synchronization overhead of StringBuffer.

3. Not taking advantage of ‘toString()’
Overriding toString() method gives you a cheap way to provide human-readable labels for objects in output.

Bad:
System.out.println("Author: "+person.getName());

Good:
class Person {
...
public String toString() {
return this.getName();
}
}
...
System.out.println("Author: "+person);

4. Instatiation of immutable objects
Creating new instances of immutable primitive type wrappers (such as Number subclasses and Booleans) wastes the memory and time needed for allocating new objects. Static valueOf() method works much faster than a constructor and saves the memory, as it caches frequently used instances.

Bad:
Integer i = new Integer(0);
Boolean b = new Boolean(true);

Good:
Integer i = Integer.valueOf(0);
Boolean b = Boolean.valueOf(true); // or Boolean.TRUE

5. Unbuffered I/O
Reading and writing I/O streams byte-by-byte is too expensive, as every read()/write() call refers to the underlying native (JNI) I/O subsystem. Usage of buffered streams reduces the number of native calls and improves I/O performance considerably.

Bad:
InputStream in = new FileInputStream(f); int b; while ((b = in.read()) != -1) {     ... }
Good:
InputStream in = new BufferedInputStream(new FileInputStream(f));
...


6. ‘equals()’ does not check for null argument

If you override equals() method in your class, always check if an argument is null. If a null value is passed, equals() must unconditionally return false (no NullPointerException should be thrown!).

Bad:
class Person {
...
public boolean equals(Object o) {
return (o instanceof Person) &&
this.getName().equals(((Person)o).getName());
}
...
}

Good:
class Person {
...
public boolean equals(Object o) {
return (o != null) &&
(o instanceof Person) &&
this.getName().equals(((Person)o).getName());
}
...
}

class Person {



public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Person))
return false;

Person that = (Person)o;
….

7. ‘equals()’ is overridden while ‘hashCode()’ is not
If you override equals() in your class, always provide a custom implementation of hashCode() that returns the same hash code value for two or more equal objects:

a.equals(b) ? a.hashCode() == b.hashCode()

This is, in fact, a general contract defined by Java API Specification. Violation of this rule (which is likely the case if equals() method is overridden while hashCode() is inherited from Object) may cause numerous bugs and unexpected behaviours.

Note that the reverse rule: two or more unequal objects must have different hash codes, or

!a.equals(b) ? a.hashCode() != b.hashCode()


8. Difference between == and .Equals() Method

Value Type: == and .Equals() methods compare two objects by value.

Ex: int a = 5; int b = 5;

Console.WriteLine(a==b); Output: true
Console.writeLine(a.Equals(b)); Output: true

Reference Type: == performs an identity comparison, i.e. it will only return true if both references point to the same object.

While Equals() method is expected to perform a value comparison.

Ex: StringBuilder s1 = new StringBuilder(“Yup”);

StringBuilder s2 = new StringBuilder(“Yup”);

Console.WriteLine(s1==s2); Output : false

Console.WriteLine(s1.Equals(s2)); Output: true

In above example, s1 and s2 are different objects hence “==” returns false, but they are equivalent hence “Equals()” method returns true.

Note: There is an exception of this rule, i.e. when you use “==” operator with string class it compares value rather than identity.

When To use:

For value comparison, with Value Tyep use “==” operator and use “Equals()” method while performing value comparison with Reference Type.