Top 10 JAVA programming tricks

Back Top 10 JAVA programming tricks

After a great response to our last blog on "TOP 10 MISTAKES THAT JAVA DEVELOPERS SHOULD AVOID", we are back with a some new Java Programming tricks this time. These are:

  1. Never use keys with mutable hashcode

Hashcode is used by hash based collections like HashMap and HashSet to support fast key lookups. But therein lies a subtle problem, i.e. Keys that constituted of mutable fields result in undesirable behavior.

Assuming the Name object in the following listing has a typical hashCode() (which uses fname and lname fields, both mutable to calculate the hashCode()), the get() call to Map will fail and return null

public class Name {

   private String fname;

   private String lname;

   public Name(String fname, String lname) {

       this.fname = fname;

       this.lname = lname;

   }

   public void setFname(String fname) {

       this.fname = fname;

   }

   public void setLname(String lname) {

       this.lname = lname;

   }

   public int hashCode() {

       return fname.hashCode() & lname.hashCode();

   }

   // ...

   public static void main(String[] args) {

       Name name1 = new Name("Sunny", "Rana");

       Name name2 = new Name("Sunil", "Kumar");

       StudentDetail studentDetail1 = new StudentDetail(18, "ABC block, jaipur", "Mechanical");

       StudentDetail studentDetail2 = new StudentDetail(19, "Charms India, delhi", "Computer Science");

       Map<Name, StudentDetail> map = new HashMap<>();

       map.put(name1, studentDetail1);

       map.put(name2, studentDetail2);

       name1.setLname("Singh");

       System.out.println(map.get(name1));

   }

}

class StudentDetail {

   private int age;

   private String address;

   private String stream;

   public StudentDetail(int age, String address, String stream) {

       this.age = age;

       this.address = address;

       this.stream = stream;

   }

}

For the same reason String objects being immutable are best suited for HashMap keys.

  1. Double brace initialization of collections

A convinient way to create and populate an ArrayList is

List<String> names = Arrays.asList("name1","name2", "name3");

but this creates an immutable list, so any further add operations results in UnsupportedOperationException

//This throws java.lang.UnsupportedOperationException

names.add("names4");

Double Brace initialization allows us the further add operation during the lagter stage

List<String> names1 = new ArrayList<String>(){{

   add("abc");

   add("xyz");

   add("pqr");

}};

//this will work fine here

names1.add("uvw");

  1. Using initializer code blocks

With a static initializer block, we can execute code when the class is loaded and with an instance initializer block, we can execute code before the constructor is executed. static initializer block can be useful when we need to auto-execute some code only once that that too before the construction of first object of that class. Instance initializer can be useful in case of anonymous classes implementing the interfaces which have no constructors to execute any expression during object construction.

public class InitializersTest {

  String name = "member variable";

  public InitializersTest() {

      System.out.println("constructor");

  }

  //static initializer

  static {

      System.out.println("static initializer");

  }

  //instance initializer

  {

      System.out.println("instance initializer");

  }

  public static void main(String[] args) {

      InitializersTest one = new InitializersTest();

      InitializersTest  two = new InitializersTest();

     }

}

The output will be

static initializer called

instance initializer called

constructor called

instance initializer called

constructor called

  1. Prefer Primitives over Wrapper Class

Wrapper classes are are often slower than primitive classes. Primitives contain only values while the wrapper class stores information about the entire class. Further, as wrapper classes deal with objects, comparing them like the primitives does not give desired results.

int numeral1 = 5;

int numeral2 = 5;

Integer numObject1 = new Integer(5);

Integer numObject2 = new Integer(5);

Here

numeral2 == numeral2 returns true

where as

numObject1 == numObject2 returns false

  1. Create Strings as literals instead of using 'new' keyword

Create string constants like String message = "Error message" because String implements Fly Weight design pattern, which maintains a pool, if "Error message" string is already present in the pool then it will return the reference from pool, otherwise it will create a new String object, put it into pool and returns the reference of newly created String object.

While if String message = new String("Error message") is used to create a string, then it does not search string pool and will create a new String every time meaning if we have new String("Error message") 10 places in the code, it will create 10 new String objects.

  1. Use String.valueOf() instead of toString()

String.valueOf() is null safe and will not throw NullPointerException

Student student = null;

String value  = String.valueOf(student);//will result in value = “null”

String string = student.toString(); //will result in  NullPointerException

If student is initilized to a valid object then both value and string will be assigined the same string value

  1. Use String.format() static method to format the strings

String.format() method can be used to get a formatted string using the specified format string and arguments

e.g.

String message = String.format("Circumference of a circle with radius %02d is 2* %f*%d", r, pi, r);

will return message = “Circumference of a circle with radius 06 is 2*3.141593*6”

  1. Determining the method name of class at runtime

We can determine the method name being executed at the runtime as follows

String methodName = Thread.currentThread().getStackTrace()[3].getMethodName();

methodName will contain current method name

This can be very helpful during debugging if we have current method name in the log file along with log messages

So we can have follwing method in some Utility class

public String getCurrentMethodName() {

    return Thread.currentThread().getStackTrace()[3].getMethodName();

}

public void someMethod() {

    System.out.println(Utility.getCurrentMethodName() + ": message");

}

  1. Capture the stack trace of an exception

Checking the code for errors is one of the most painstaking parts of the development process. It is also very time-consuming, if  we encounter an error whose source isn’t immediately clear. Using this code, we can more easily track down where an exception is occurring.

Exception e = …;

java.io.StringWriter sw = new java.io.StringWriter();

e.printStackTrace(new java.io.PrintWriter(sw));

String trace = sw.getBuffer().toString();

  1. Prefer internal iteration over external

Java 8 has introduced internal iteration. forEach can iterate the collection internally. Before java 8, there was no such way to iterate collection internally and to iterate the collection we required to use for loop or while loop. Using forEach, now iteration is possible in one line. To understand internal and external iteration go through the following example

List<String> countries = Arrays.asList("Australia", "Bangladesh", "England", "India", "Pakistan", "Sri Lanka", "South Africa");

//External iteration

for (String country : countries) {

   System.out.println(country);

}

//Internal iteration using lambda

countries.stream().forEach(country -> System.out.println(country));

//Internal iteration using method reference

countries.stream().forEach(System.out::println);