preface

Here are some of the coding lessons I learned in my day job, along with some tips to improve the efficiency of my application, and let’s write better code together.

Minimize double counting of variables

Make it clear that calls to a method, even if there is only one statement in the method, have costs, including creating stack frames, protecting the scene when the method is called, and restoring the scene when the method is finished. So for example:

for (int i = 0; i < list.size(); i++) {... }Copy the code

You are advised to replace it with:

for (int i = 0, length = list.size(); i < length; i++) {... }Copy the code

This saves a lot of overhead when list.size() is large

String concatenation

Instead of using “+” for string concatenation inside the loop, use StringBuilder to append the string

When the virtual machine encounters the “+” operator to concatenate strings, it creates a StringBuilder and calls the Append method.

Finally, the toString() method is called to convert the string assignment to the oriStr object, that is, to loop through as many New StringBuilder() as possible, which is a waste of memory.

Specifies that classes and methods are final

Classes with the final modifier are not derivable. In the Java core API, there are many examples of final applications, such as java.lang.String, where entire classes are final.

Specifying a final modifier for a class makes it uninheritable, and specifying a final modifier for a method makes it unoverridden. If a class is specified as final, all methods of that class are final.

The Java compiler looks for opportunities to inline all final methods. Inlining is important for improving Java runtime efficiency. See Java Runtime Optimization. This can improve performance by an average of 50%.

Use local variables whenever possible

Parameters passed when a method is called, as well as temporary variables created during the call, are kept in the stack faster, while other variables, such as static variables, instance variables, and so on, are created in the heap slower.

In addition, variables created in the stack are removed as the method completes its run, requiring no additional garbage collection.

Turn off the flow in time

During Java programming, be careful when connecting to the database and performing I/O flow operations. After using the database, close the database to release resources. Because the operation of these large objects will cause a large overhead of the system, a slight mistake will lead to serious consequences.

Try to use a lazy loading strategy

Drawing on this idea is to create an object when it needs to be earned

Such as:

String str = "aaa";
if (i == 1)
{
  list.add(str);
}
Copy the code

You are advised to replace it with:

if (i == 1)
{
  String str = "aaa";
  list.add(str);
}
Copy the code

Careful with abnormal

Exceptions are detrimental to performance. To throw an exception, a new object is created. The constructor of the Throwable interface calls a locally synchronized method called fillInStackTrace(), which checks the stack to collect the call trace information.

Whenever an exception is thrown, the Java virtual machine must adjust the call stack because a new object is created during processing. Exceptions should only be used for error handling and should not be used to control program flow.

try/catch

Do not use try… in loops. The catch… You should put it in the outermost layer

Specify the size when using a collection

Examples include ArrayList, LinkedLlist, StringBuilder, StringBuffer, HashMap, HashSet, etc. Take StringBuilder for example:

  • StringBuilder() // Allocates 16 character space by default
  • StringBuilder(int size) // The default allocation of size character space
  • StringBuilder(String STR) // Default allocation of 16 characters +str.length() character space

You can set the initialization capacity of a class (and not just the StringBuilder above) through its constructor, which can significantly improve performance. For example, StringBuilder, length is the number of characters that the current StringBuilder can hold.

Because when StringBuilder reaches its maximum capacity, it increases its current capacity by two plus two, and whenever StringBuilder reaches its maximum capacity, It has to create a new character array and copy the contents of the old character array into the new character array —- which is a very performance expensive operation. Imagine that the array contains about 5000 characters without specifying the length, and the nearest power of 5000 is 4096, with each increment incrementing by 2:

Applying for 8194 character arrays on top of 4096 adds up to applying for 12290 character arrays at once, more than doubling the space saved if you could have specified 5000 character arrays initially

Copy the original 4096 characters into the new character array

In this way, it wastes memory space and reduces the efficiency of the code. Therefore, you can’t go wrong with setting a reasonable initialization capacity for collections and utility classes that are implemented in arrays at the bottom. This will bring immediate results. Note, however, that for collections such as a HashMap, which is implemented as an array + linked list, do not set the initial size to the size you estimated, because the probability of joining only one object on a table is almost zero. The recommended initial size is 2 to the NTH power, or new HashMap(128) or new HashMap(256) if 2000 elements are expected.

When copying large amounts of data

Use the system.arraycopy () command

Multiplication and division use shift operations

Such as:

for (val = 0; val < 100000; val += 5)
{
  a = val * 8;
  b = val / 2;
}
Copy the code

Using shift operation can greatly improve performance, because at the bottom of the computer, the counterpoint operation is the most convenient and fastest, so it is recommended to change to:

for (val = 0; val < 100000; val += 5)
{
    a = val << 3;
    b = val >> 1;
}
Copy the code

The shift operation, while fast, can make the code difficult to understand, so it is best to comment accordingly.

Do not keep creating object references within the loop

Such as:

for (int i = 1; i <= count; i++)
{
    Object obj = new Object();    
}
Copy the code

If count is too large, the memory will be used. We recommend changing it to:

Object obj = null;
for (int i = 0; i <= count; i++)
{
    obj = new Object();
}
Copy the code

Each time a new Object() is called, the Object reference refers to a different Object, but there is only one Object in memory. This saves a lot of memory.

Do not declare arrays to be public static final

Declaring an array public is a security loophole, which means that the array can be changed by an external class

Use singletons when appropriate

Singletons can reduce the load burden, shorten the load time, and improve the load efficiency. However, they are not applicable to all places. In brief, singletons are mainly applicable to the following three aspects:

  • Control the use of resources, through thread synchronization to control concurrent access to resources
  • Control the generation of instances to achieve the purpose of saving resources
  • Control the sharing of data and allow communication between unrelated processes or threads without establishing a direct correlation

Try to avoid arbitrary use of static variables

Remember that when an object is referenced by a variable defined as static, gc usually does not reclaim the heap memory occupied by the object, as in:

public class A
{
    private static B b = new B();  
}
Copy the code

The lifetime of the static variable B is the same as that of class A. If class A is not unloaded, the object referred to by b will stay in memory until the program terminates

Clear sessions that are no longer needed in a timely manner

To clear out inactive sessions, many application servers have a default session timeout, typically 30 minutes. When the application server needs to hold more sessions, if the memory is low, the operating system will move some of the data to disk, the application server may dump some of the inactive sessions to disk according to the MRU (most frequently used recently) algorithm, or even throw an out-of-memory exception.

If a session is to be dumped to disk, it must first be serialized, and serializing objects can be expensive in a large cluster. Therefore, when a session is no longer needed, it should be immediately cleared by calling the invalidate() method of HttpSession.

The for and foreach

Collections that implement the RandomAccess interface, such as ArrayList, should be traversed using the most common for loop rather than the foreach loop

This is what the JDK recommends to users. The RandomAccess interface is implemented to show that it supports fast RandomAccess. The main purpose of this interface is to allow general algorithms to change their behavior to provide good performance when applied to random or continuously accessed lists.

Practical experience shows that the efficiency of using ordinary for loop is higher than foreach loop if the class instance implementing RandomAccess interface is accessed randomly. Conversely, if the access is sequential, it is more efficient to use Iterator. This can be done with code like the following:

if (list instanceof RandomAccess)
{
    for (int i = 0; i < list.size(); i++){}
}
else{ Iterator<? > iterator = list.iterable(); while (iterator.hasNext()){iterator.next()} }Copy the code

The underlying implementation principle for foreach loops is the Iterator. See Java Syntax Sugar 1: Variable-length Arguments and Foreach loops. Instead, it is more efficient to use Iterator if the class is accessed sequentially.

Use synchronized code blocks instead of synchronized methods

Unless it is clear that an entire method needs to be synchronized, try to use synchronized code blocks to avoid synchronizing code that does not need to be synchronized, affecting the efficiency of code execution.

Declare the constant static final and name it in uppercase

This allows you to put the content into the constant pool at compile time, avoiding the need to evaluate the value of the generated constant at run time. In addition, it is easy to distinguish constants from variables by naming them in uppercase

Avoid using reflection during program execution

Reflection is a powerful feature that Java provides to users, and powerful features often mean inefficient.

It is not recommended to use invoke methods that use reflection frequently, especially Method, during program execution.

If necessary, it is recommended that classes that need to be loaded by reflection instantiate an object and put it into memory at project startup —- The user is only interested in getting the fastest response when interacting with the other end, not how long it takes the other end to start the project.

Use database connection pools and thread pools

Both pools are designed to reuse objects, the former to avoid frequent opening and closing of connections, and the latter to avoid frequent thread creation and destruction

IO operations are performed using buffered I/O streams

Buffered input and output streams (BufferedReader, BufferedWriter, BufferedInputStream, BufferedOutputStream) can greatly improve I/O efficiency

ArrayList, and LinkedList

ArrayList is used for scenarios with high sequential inserts and random access, and LinkedList is used for scenarios with high element deletion and intermediate inserts

equals

String variables and string constants are preceded by equals to avoid null-pointer exceptions

if ("abc".equals(str)) {

}
Copy the code

Do not use the toString() method on arrays

Take a look at what is printed using toString() on an array:

public static void main(String[] args)
{
    int[] is = new int[] {1.2.3};
    System.out.println(is.toString());
    // Output: [I@156643d4
}
Copy the code

The null pointer exception is possible because the array reference is null. AbstractCollections overrides the toString() method of Object, but it doesn’t make sense to print the contents of the collection toString().

Data that is not used in public collection classes must be removed promptly

If a collection class is public (that is, not an attribute in a method), the elements in the collection are not automatically freed because there are always references to them. Therefore, if some data in the public set is not used but not removed, it will cause the public set to increase, so that the system has the potential of memory leakage.

Conversion efficiency of basic data types to strings

ToString () is the fastest way to convert a primitive data type to a String, followed by string.valueof (data) and data +””

Test speed:

public static void main(String[] args)
{
    int loopTime = 50000;
    Integer i = 0;
    long startTime = System.currentTimeMillis();
    for (int j = 0; j < loopTime; j++)
    {
        String str = String.valueOf(i);
    }    
    System.out.println("String. The valueOf () :" + (System.currentTimeMillis() - startTime) + "ms");
    startTime = System.currentTimeMillis();
    for (int j = 0; j < loopTime; j++)
    {
        String str = i.toString();
    }    
    System.out.println("Is an Integer. The toString () :" + (System.currentTimeMillis() - startTime) + "ms");
    startTime = System.currentTimeMillis();
    for (int j = 0; j < loopTime; j++)
    {
        String str = i + "";
    }    
    System.out.println("I + \"\" :" + (System.currentTimeMillis() - startTime) + "ms");
}
Copy the code

The running results are as follows:

String. The valueOf () :11Ms Integer. The toString () :5ms
i + "":25ms
Copy the code

The reason:

  • The string.valueof () method calls integer.tostring () underneath, but shorts the judgment before calling it
  • Leave out the integer.toString () method
  • I + “” is implemented using StringBuilder, which uses append method to concatenate the string, and toString() method to retrieve the string

The most efficient way to traverse a Map

There are many ways to traverse a Map. Generally, we need to traverse keys and values in a Map. The most efficient way is recommended as follows:

public static void main(String[] args)
{
    HashMap<String, String> hm = new HashMap<String, String>();
    hm.put("111"."222");

    Set<Map.Entry<String, String>> entrySet = hm.entrySet();
    Iterator<Map.Entry<String, String>> iter = entrySet.iterator();
    while (iter.hasNext())
    {
        Map.Entry<String, String> entry = iter.next();
        System.out.println(entry.getKey() + "\t"+ entry.getValue()); }}Copy the code

If you just want to iterate over the Map’s keys, use Set keySet = hm.keyset (); Would be more appropriate

You are advised to separate close() for resources

Such as:

try
{
    XXX.close(a); YYY.close(a); } catch (Exception e) { ... }Copy the code

You are advised to change the value to:

try
{
    XXX.close(a); } catch (Exception e) { ... } try { YYY.close(a); } catch (Exception e) { ... }Copy the code

It’s a hassle, but it avoids resource leaks. Yyy. close() will not be executed, and YYY will not be recycled. It has been used for a long time. This code may cause resource handle leakage. XXX and YYY will be closed in any case

ThreadLocal must be removed before or after use

Thread pooling technology does a thread reuse, which means that while code is running, a thread that has been used is not destroyed but is waiting for another use. We have a look at the Thread class, holding a ThreadLocal. ThreadLocalMap references:

/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
Copy the code

Article on the Thread does not destroy means the Thread set ThreadLocal. Data in a ThreadLocalMap still exists, then the next a Thread to reuse the Thread, is likely to get to is a Thread on the set of data rather than the content they want.

This is a very subtle problem, and it is very difficult to find errors due to this cause without experience or solid foundation, so pay attention to this when writing code, which will save you a lot of work later on.

Override methods must retain the @override annotation

  • It is clear that this method is inherited from the parent class
  • The getObject() and get0bject() methods, whose fourth letter is “O” and whose fourth child is “0”, add the @override annotation to immediately determine whether the Override succeeded
  • Change a method signature in an abstract class, and the implementation class immediately reports a compilation error

Follow wechat public account: IT elder brother

Java actual combat project video tutorial: you can get 200G, 27 sets of actual combat project video tutorial

Reply: Java learning route, you can get the latest and most complete a learning roadmap

Re: Java ebooks, get 13 must-read books for top programmers

Java foundation, Java Web, JavaEE all tutorials, including Spring Boot, etc

Reply: Resume template, you can get 100 beautiful resumes