Monday, November 19, 2012

Part VI - Effective Java - General Programming Pracitces

This is the sixth post based on the book Effective Java by Joshua Bloch. In the previous post we read about best practices to write good methods in Java.

This post covers General Programming practices which will help writing good quality code.

Minimize the scope of the local variables

Declare variables at the last possible moment. Defining variables before they are really required can mean that the scope of the variable may turn out to be more than necessary, it may inadvertently be used before it is logically initialized, it can be used accidently after its scope has finished. All of these can have disastrous impact on the application. Also somebody reading the code can tend to forget the type of the variable if it is used far away from where it is declared.
Every local variable should contain an initializer. The only exceptions can be variables which are required in the finally block when exceptions are handled.

Know and use the libraries

It is important to learn the libraries available and leverage them. Libraries including standard Java library are changing and becoming more powerful as time progresses and unless one keeps track of the changes one would end up performing unnecessary coding to achieve something which can be better done by some library method.
Understand and then keep abreast of changes to at least java.lang, java.util and java.io.

Avoid float and double if exact answers are required

The float and double types will not give precise values. They are not suited for monetary calculations. Rounding off will also not help. Use BigDecimal or int or long for monetary calculations.
Also when using BigDecimal ensure to instantiate as follows:
BigDecimal tenPaise = new BigDecimal(“0.1”);
not
BigDecimal tenPaise = new BigDecimal(0.1);
Note that in the second case we end up using a float value inadvertently and this will result in the BigDecimal getting a value close to 0.1 and not exactly 0.1, whereas in the first case since we have used a String as the parameter it will give a precise value of 0.1.

Avoid Strings where other types are more appropriate

Do not use String type variables to represent data which is actually of a different data type. E.g. when data is received from a browser or read from a file it typically is in the form of a string and one tends to carry forward the same till it is necessary to deal with the actual data type. Instead convert the strings to the correct data type at the first opportunity and convert them back into strings only at the last possible moment.
Do not concatenate string representation of strings to form a key for a Map. Instead use a helper class which implements the correct equals and hashCode method to contain the fields that are required for the key.

Beware the Performance of String Concatenation

Using the “+” operator to concatenate strings is a big overhead. Instead use a StringBuffer if one needs the variable to be thread safe or better still use StringBuilder if one is sure that it is not necessary for the variable to be thread safe.
Additionally prime the StringBuffer or StringBuilder with the expected size it is expected to grow to if it is possible.

Prefer Interfaces to Reflection

Reflection in Java is a very powerful feature which allows one to interact with the objects at runtime by discovering what the object supports and using them.
But this comes at a cost and should be avoided as far as possible. Instead of reflection one should consider use of Interfaces.

Reflection comes with the following disadvantages:
1.       All benefits of compile time checking is lost.
2.       The code required to perform reflective access is clumsy and verbose.
3.       Performance of code using reflection will be slower than code that does not use reflection
If the issue to determine the class at runtime then one should define an interface, use the interface in the code and at run time use reflection to instantiate the class (factory pattern). This way we can keep reflection to the minimum while retaining benefits that accrue with usage of interfaces.
Note that all Object Relation Mapping libraries use reflection.

Use Native Methods Judiciously

Java provides a way to execute native code. This was used to get better performance and/or to use some native library functions. But given the progress that JVM has made one should use native calls if and only if that is the last resort to use a library that is available only in the native format. It is not worth the trouble of using native calls for improved performance.

Optimize Judiciously

Many a times the developers have the tendency to optimize as they are writing the code. Here are some aphorisms about optimization that should be known and adhered to by all:
1.       More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason – including blind stupidity. William A Wulf
2.       We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Donald E. Knuth
3.       We follow two rules in matter of optimization
a.       Rule 1: Don’t do it
b.      Rule 2 (for experts only): Don’t do it yet – that is not until you have a perfectly clear and unoptimized solution.
M. A. Jackson
Corollary is “Strive to write good programs rather than fast ones”.
But the same time
1.       Strive to avoid design decisions that limit performance. It is difficult to change APIs, wire-level protocols, and persistent data formats. These should be considered carefully at design time to provide good performance right at the start.
2.       Consider performance consequences of your API decisions.
3.       Measure performance before and after each attempted optimization.

Adhere to generally accepted naming convention

There is a generally accepted naming convention for the Java language. Adhere to this naming conventions and avoid violating these unless there is real reason to do so. It will make the program understandable and maintainable by a larger audience. Some conventions are
1.       Package names should be in lower case
2.       Package names can have abbreviated words
3.       Class names should start with a Capital letter
4.       Method, Attribute and Variable names should start with lower case
5.       Typically Acronyms used in Class, Method, Attribute or Variable names are capitalized although there is no general agreement on this
6.       Generally Class, Method, Attribute and Variable names should have full words
7.       Use a Camel Hump notation to separate words in classname, methods, attributes and variables.
8.       Underscores are used only for constants
9.       Constants are defined with a set of Capital letters and Underscores
10.   Interfaces are named as classes or as adjectives ending with “able” or “ible”
11.   One could follow the convention of naming all interfaces starting with an “I” although this is not a convention that is followed by all. This avoids the necessity of adding an “Impl” to indicate implementation of the interface.
12.   All methods that return a Boolean value start with “is”.

In the next post we will read about best practices in Exception Handling in Java.

No comments: