Best practices in Java

  • 143 views
Uploaded on

- Best Practices in Java …

- Best Practices in Java
- Avoiding garbage collection
- Loop Optimizaiton
- Exception Handling
- Object Oriented Design Principles
- Avoiding null pointer exception

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
143
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
0
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Best Practices in Java 1. Avoid creating unnecessary objects and always prefer to do Lazy Initialization public class Countries { private List countries; public List getCountries() { //initialize only when required if(null == countries) { countries = new ArrayList(); } return countries; } }
  • 2. 2. Never make an instance fields of class public public class MyCalender { public String[] weekdays = {"Sun", "Mon", "Tue", "Thu", "Fri", "Sat", "Sun"}; //some code } ● Maybe accessed by anyone who changes value or insert a bug ● SOLUTION: Private Field + Getter_Func( ) private String[] weekdays = {"Sun", "Mon", "Tue", "Thu", "Fri", "Sat", "Sun"}; public String[] getWeekdays() { return weekdays; }
  • 3. 3. Minimize Mutability of a class ● Immutable classes are simple, they are easy to manage. They are thread safe. ● To make a class immutable you can define its all constructors private and then create a public static method to initialize and object and return it public class Employee { private String firstName; private String lastName; //private default constructor private Employee(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public static Employee valueOf (String firstName, String lastName) { return new Employee(firstName, lastName); } }
  • 4. 4. Prefer Interfaces instead of Abstract classes ● First you can not inherit multiple classes in Java but you can definitely implements multiple interfaces. ● Its very easy to change the implementation of an existing class and add implementation of one more interface rather then changing full hierarchy of class. 5. Try to limit the scope of Local variable ● Minimizing the scope of a local variable makes code more readable, less error prone and also improves the maintainability of the code. Thus, declare a variable only when needed just before its use. Always initialize a local variable upon its declaration. If not possible at least make the local instance assigned null value. 6. Use standard library instead of writing your own from scratch ● It is very advisable to use an existing standard library which is already tested, debugged and used by others. This not only improves the efficiency of programmer but also reduces chances of adding new bugs in your code. ● Also using a standard library makes code readable and maintainable. .
  • 5. 7. Try to use Primitive types instead of Wrapper class ● Wrapper classes are great. But at same time they are slow. ● Primitive types are just values, whereas Wrapper classes are stores information about complete class. int x = 10; int y = 10; Integer x1 = new Integer(10); Integer y1 = new Integer(10); System.out.println(x == y); System.out.println(x1 == y1); ● Also if you are using a wrapper class object then never forget to initialize it to a default value.
  • 6. Avoiding Garbage Collection ● The process of disposing of Java objects is called garbage collection (GC). GC is automatic in Java programs. When an object is no longer referenceable, it is available for GC. This occurs at the end of the block of code that references that object. GC is a low-priority JVM task that runs when there are CPU cycles available or the JVM runs short on memory. If the JVM is short on memory, GC will run continually until memory is available to run the system. This thread typically uses 5-15 percent of the CPU. There are two techniques that can be used to reduce GC. The first is to write applications that reuse existing objects; this eliminates the overhead of creating and destroying the object, thus reducing GC JVM overhead, but it does require extra work on the programmer because values will need to be reinitialized prior to reuse. ● The second technique is to use the appropriate objects that can meet the requirements. It is a well-known fact that string concatenation is an expensive operation in Java applications. This is because strings are immutable, meaning that the value of a string can never be changed. Consequently, every time two strings are concatenated, a new string must be created.The recommendation is to use a string buffer object instead. In the example below, several intermediate string objects are created as a result of the eight concatenations. Each of these intermediate string objects will need to be GC System.out.println(person.getLastName() + “, “ + person.getFirstName() + “ lives at “ + person.getAddress().getStreet1() + “; “ + person.getAddress().getCity() + “, “ + person.getAddress().getState() ); However, using a string buffer, the intermediate objects are not created requiring less GC while producing the same result. StringBuffer buf = new StringBuffer; buf.append(person.getLastName()); buf.append(“, “); buf.append(person.getFirstName()); buf.append(“ lives at “); buf.append(person.getAddress().getStreet1()); buf.append(“; “); buf.append(person.getAddress().getCity()); buf.append(“, “ ); buf.append(person.getAddress().getState()); System.out.println(buf.toString());
  • 7. Loop Optimization ● Java programs spend most of their time in loops. There are several techniques that can be used to optimize loops. Here are two examples that can be implemented easily to reduce a program’s overhead Avoid using a method call as the termination criteria for a loop. For example: The code on the right will perform an average of 150 percent faster with the same results. This is because the length of the string is not calculated for each iteration through the loop. The method call to str.length() is avoided by setting the results to an integer prior to entering the loop. ● The loop on the right can be improved even more by changing the loop to count backwards. The JVM is optimized to compare to integers between -1 and +5. So rewriting a loop to compare against 0 will produce faster loops. So for example, the loop on the right could be changed to use the following for loop instruction: for (int j = len-1; j >= 0; j—) There are other performance considerations when coding loops in Java. For example, once the processing requirement of a loop has been completed, use the break statement to terminate the loop. This saves the JVM from iterating through the loop doing the evaluations of the termination criteria. Using local variables within a loop requires less processing than instance variables. Local variables are defined within the scope of the method prior to the loop. Instance variables are part of a class object. Instance variables are looked up by the JVM, which can be expensive. For example, a class has an integer instance variable that needs to be set to an index value into an array. Within the method prior to the “for” loop, a local variable is defined. The local variable is used within the loop doing comparisons. After successfully locating the index, the loop is exited and the instance variable is set to the value of the local variable. In some cases, if the array is part of the object where the integer that needs to be set is located, it would be faster to create a local copy of the array and use that to determine the index within the loop. Simple tests can be run by wrapping the code with System.currentTimeMillis() method calls to help you make the right choice. Performance testing may take a little extra time, but it is important to remember the life expectancy of an application is years. Therefore, investing in performance testing early in the life cycle drives significant return oninvestment throughout the application life cycle.
  • 8. public loop control() public loop control() { { String str = “abcdefghijklmnopqurstuvwxyz”; String str =“abcdefghijklmnopqurstuvwxyz”; for (int j = 0; j < str.length(); j++) int len = str.length(); { for (int j = 0; j < len; j++) // code doesn’t change the length of the string. { } // code doesn’t change the length of the string }
  • 9. Data Structures ● The data structure most easily modified for performance is collections. A collection is used as a generic term for any object that represents a set of items grouped together. There are three types of collections: sets, lists and maps. A set is a collection of objects that in the purest sense has no particular order. You can think of that as similar to having a collection of items stored in a shoebox. A list has the characteristics that objects are stored in a linear manner. An array is an example of a list where objects are stored into the list based upon the data structure’s index. In most cases, order is not as impo tant when using arrays. Maps involve pairs of objects, a key and the object itself. Objects are stored and retrieved using the keys. There are nine classes defined within the Java utility libraries that can be used to manage collections. There are two classes that provide set collections: HashSet and TreeSet. HashSet is faster than TreeSet because TreeSet provides iteration of the keys in order. The implementations of sets are slower than most other collection objects and,therefore, careful consideration should be given prior to using this functionality. ● There are three classes included in a map collection: HashMap, HashTable and TreeMap. A map is used to store data to minimize the need for searching when you want to retrieve an object. Both HashTables and HashMaps are fast, providing adequate performance. A TreeMap is slower than a HashMap for the same reason as outlined above regarding TreeSets and HastSets. The keys used in map collections have to be unique. There are four classes included in a list: ArrayList, Vector, Stack and LinkedList. The ArrayList is the fastest of the list classes with Vector and Stack being about equal. Vector is slower than an ArrayList because of synchronization. Stack is implemented using the Vector class, but offers additional methods to push and pop entries. ● Simple arrays provide the fastest data structure for storing data. The real advantage of Java is the libraries that have implemented advanced algorithms to make the management of data easier. These algorithms are optimized to bring better performance than could be implemented by the average application developer. And the code in these libraries is stable. So what is the bottom line on collection performance? Proper sizing of the collection object is one of the most important considerations. For example, if the number of elements exceed the capacity of a HashTable, the program will end up with multiple nodes. Multiple nodes reduce a HashTable’s efficiency. In addition, a larger hash code will ensure more even distribution within a HashTable, which improves performance. Vectors should be sized properly to avoid expansion. Adding and removing items from the end of a Vector improves performance because this avoids the shifting of existing elements. Unlike the Vector collection class, ArrayLists and HashMaps are not synchronized classes. When usin multithreaded applications use ArrayLists and HashMaps to improve performance when synchronized access to the data is not a concern.
  • 10. Exception Handling ● Exceptions are used in Java programs to signal an error or problem during the execution of a program. Exceptions are used extensively by the standard Java libraries. It is an indication that an operation within your application needs special attention. There are several different types of exceptions by coding errors, standard library methods, programmer selfdefined and Java JVM. For example, a standard library method exception would be IndexOutOfBoundsException, thrown when a program uses an index outside the bounds of the object for an array. For almost all the exceptions represented by subclasses of the exception class, you must include code in your program to deal with them. A try-catch block is used to handle the processing of exceptions. Exceptions must be handled by the calling method in a Java catch-code block. That calling method has an option to register that it will throw an exception in its method definition to pass the error to the next higher point in the calling sequence. Your program will not compile if exceptions are not handled. ● A simple performance improvement can be achieved by placing the try-catch block outside any loops. On some JVMs, this can amount to as much as a 10-percent increase in performance. In the figure below, example one will run faster on most JVMs than example two. In example one, the try-catch block is outside the “for” loop. In this example, an exception is not thrown. If the evaluation in the “for” loop expression was changed to j>= -1, the ArthmeticException would be thrown.
  • 11. ● Whenever an exception is thrown, the JVM must execute several hundred lines of code to handle the exception. A lot of this overhead is due to getting a snapshot of, and unwinding, the stack when the exception occurs. So exceptions should be used only for extreme conditions. ● There are times when you need to have an exception thrown despite the overhead associated with exception processing. ● This is the case where you are going to throw a self-defined exception. The overhead can be reduced 50-100 times by defining the exception object and reusing it. Below is an example where we define the exception object in the init( ) method. Then when the exception is thrown it is reused. // Reuse of Exception Object public static Exception REUSABLE_EX = new Exception(); public void method1() throws EXCEPTION { if (I == 1)1111 throw REUSABLE_EX; } ● The alternative would be to code “throw new Exception(); // 50 –100 times slower” – replacing the previous throw instruction.
  • 12. Object Oriented Design Principles ● DRY (Don't repeat yourself) Our first object oriented design principle is DRY, as name suggest DRY (don't repeat yourself) means don't write duplicate code, instead use Abstraction to abstract common things in one place. If you have block of code in more than two place consider making it a separate method. ● Open Closed Design Principle Classes, methods or functions should be Open for extension (new functionality) and Closed for modification. ● Favor Composition over Inheritance Composition allows to change behavior of a class at runtime by setting property during runtime and by using Interfaces to compose a class we use polymorphism which provides flexibility of to replace with better implementation any time.
  • 13. ● Interface Segregation principle (ISP) Interface Segregation Principle stats that, a client should not implement an interface, if it doesn't use that. This happens mostly when one interface contains more than one functionality, and client only need one functionality and not other. ● Programming for Interface not implementation Always program for interface and not for implementation this will lead to flexible code which can work with any new implementation of interface. So use interface type on variables, return types of method or argument type of methods in Java.
  • 14. Best Practices to follow while writing Code 1) Focus on readability of code; assume that you don't have comments to explain the code. Give your method, variables and class meaningful name. 2) Don't write what code is doing, this should be left for the code to explain and can be easily done by giving class, variable and method meaningful name. For example: //calculates square root of given number //using Newton-Raphson method public void abc(int a){ r = a / 2; while ( abs( r - (a/r) ) > t ) { r = 0.5 * ( r + (a/r) ); } System.out.println( "r = " + r ); } public void squareRoot(int num){ root = num/ 2; while ( abs(root - (num/ root) ) > t ) { r = 0.5 * (root + (num/ root)); } System.out.println( " root = " + root ); }
  • 15. 3) Always write why you are writing this piece of code, why you are writing this piece of code because this information is not visible until you write them in comments and this is critical to identify any bug or behavior with changing business environment. 4) If you are writing core libraries which will be used by different project and with different teams. Follow javadoc comment style and document all assumption and precondition for using your API. 5) Always try to finish your comment in as few words as possible, one liner comment is best until its explaining "Why" part and can't be replaced by code itself. No body likes or has enough time to read longer comment. 6) Last but not the least give your code to fellow developer to understand as part of code review and ask him how much he understands it.
  • 16. Best Practices to avoid NullPointerException in Java 1) Call equals() and equalsIgnoreCase() method on known String literal rather unknown object Always call equals() method on known String which is not null. Since equals()method is symmetric, calling a.equals(b) is same as calling b.equals(a), and that’s why many programmer don’t pay attention on object a and b. One side effect of this call can result in NullPointerException, if caller is null. Object unknownObject = null; //wrong way - may cause NullPointerException if(unknownObject.equals("knownObject")) { System.err.println("This may result in NullPointerException if unknownObject is null"); } //right way - avoid NullPointerException even if unknownObject is null if("knownObject".equals(unknownObject)) { System.err.println("better coding avoided NullPointerException"); }
  • 17. 2) Prefer valueOf() over toString() where both return same result Since calling toString() on null object throws NullPointerException, if we can get same value by calling valueOf() then prefer that, as passing null to valueOf() returns "null", specially in case of wrapper classes like Integer, Float, Double or BigDecimal. BigDecimal bd = getPrice(); System.out.println(String.valueOf(bd)); //doesn’t throw NPE System.out.println(bd.toString()); //throws "Exception in thread "main" java.lang.NullPointerException" 3) Using null safe methods and libraries There are lot of open source library out there, which does the heavy lifting of checking null for you. One of the most common one is StringUtils from Apache commons. You can use StringUtils.isBlank(), isNumeric(), isWhiteSpace() and other utility methods without worrying of NullPointerException. //StringUtils methods are null safe, they don't throw NullPointerException System.out.println(StringUtils.isEmpty(null)); System.out.println(StringUtils.isBlank(null)); System.out.println(StringUtils.isNumeric(null)); System.out.println(StringUtils.isAllUpperCase(null)); Output: true true false false
  • 18. 4) Avoid returning null from method, instead return empty collection or empty array. By returning empty collection or empty array you make sure that basic calls like size(), length() doesn't fail with NullPointerException. Collections class provides convenient empty List, Set and Map as Collections.EMPTY_LIST, Collections.EMPTY_SET and Collections.EMPTY_MAP which can be used accordingly. Here is code example public List getOrders(Customer customer){ List result = Collections.EMPTY_LIST; return result; } ● Similarly you can use Collections.EMPTY_SET and Collections.EMPTY_MAP instead of returning null. 5) Use of annotation @NotNull and @Nullable While writing method you can define contracts about nullability, by declaring whether a method is null safe or not, by using annotations like @NotNull and @Nullable. Modern days compiler, IDE or tool can read this annotation and assist you to put a missing null check, or may inform you about an unnecessary null check, which is cluttering your code. IntelliJ IDE and findbugs already supports such annotation. These annotations are also part of JSR 305, but even in the absence of any tool or IDE support, this annotation itself work as documentation. By looking @NotNull and @Nullable, programmer can himself decide whether to check for null or not. By the way ,this is relatively new best practice for Java programmers and it will take some time to get adopted. 6) Avoid unnecessary autoboxing and unboxing in your code Despite of other disadvantages like creating temporary object, autoboxing are also prone to NullPointerException, if the wrapper class object is null. For example, following code will fail with NullPointerException if person doesn't have phone number and instead return null. Person ram = new Person("ram"); int phone = ram.getPhone(); ● Not just equality but < , > can also throw NullPointerException if used along autoboxing and unboxing. See this article to learn more pitfalls of autoboxing and unboxing in Java.
  • 19. 7) Follow Contract and define reasonable default value One of the best way to avoid NullPointerException in Java is as simple as defining contracts and following them. Most of the NullPointerException occurs because Object is created with incomplete information or all required dependency is not provided. If you don't allow to create incomplete object and gracefully deny any such request you can prevent lots of NullPointerException down the road. Similarly if Object is allowed to be created, than you should work with reasonable default value. for example an Employee object can not be created without id and name, but can have an optional phone number. Now if Employee doesn't have phone number than instead of returning null, return default value e.g. zero, but that choice has to be carefully taken sometime checking for null is easy rather than calling an invalid number. One same note, by defining what can be null and what can not be null, caller can make an informed decision. Choice of failing fast or accepting null is also an important design decision you need to take and adhere consistently. 8) If you are using database for storing your domain object such as Customers, Orders etc than you should define your null-ability constraints on database itself. Since database can acquire data from multiple sources, having null-ability check in DB will ensure data integrity. Maintaining null constraints on database will also help in reducing null check in Java code. While loading objects from database you will be sure, which field can be null and which field is not null, this will minimize unnecessary != null check in code. 9) Use Null Object Pattern This is another way of avoiding NullPointerExcpetion in Java. If a method returns an object, on which caller, perform some operations e.g. Collection.iterator() method returns Iterator, on which caller performs traversal. Suppose if a caller doesn’t have any Iterator, it can return Null object instead of null. Null object is a special object, which has different meaning in different context, for example, here an empty Iterator, calling hasNext() on which returns false, can be a null object. Similarly in case of method, which returns Container or Collection types, empty object should be used instead of returning null.