Preface
I revised it in March 2016 and revisited why I need to optimize the code based on my own work and my daily learning experience. Before I amended, my statement was like this:
Just like a whale eating shrimp, maybe eating one or two shrimps is not very effective for whales, but if you eat too much shrimp, the whales will naturally be full. Code optimization is the same. Perhaps one or two optimizations are of little significance to improving the running efficiency of the code, but as long as you can pay attention to the code optimization everywhere, it is generally very useful for improving the running efficiency of the code.
This view, in the present view, is a reason for code optimization, but it is not completely correct. Today, with the development of mechanical technology, servers often have 8 cores, 16 cores, and 64-bit CPUs, and the code execution efficiency is very high. StringBuilder replaces StringBuffer and ArrayList replaces Vector, which improves the code operation efficiency by minuscule. Even if you notice every point in the project, you can't see any obvious changes in the code operation.
I think the most important role of code optimization should be: to avoid unknown errors. During the code running online, many unexpected errors often occur, because the online environment and the development environment are very different, and error positioning is often a very small reason. However, in order to solve this error, we need to first self-verify, then package out the class file to be replaced, suspend the business and restart. For a mature project, the last one actually has a very big impact, which means that users cannot access the application during this period. Therefore, when writing code, paying attention to various details from the source, weighing and using the best choices will greatly avoid unknown errors and greatly reduce the workload in the long run.
The goals of code optimization are:
1. Reduce the volume of the code
2. Improve the efficiency of code operation
Some of the contents of this article come from the Internet, and some come from daily work and study. Of course, this is not important. What is important is whether the details of these code optimization are truly useful. Then this article will be updated for a long time. As long as you encounter code optimization details worth sharing, this article will be updated from time to time.
Code optimization details
(1) Try to specify the final modifier of the class and method as much as possible
Classes with final modifiers are not deriveable. In the Java core API, there are many examples of applying final, such as java.lang.String, and the entire class is final. Specifying a final modifier for a class can prevent the class from being inherited, and specifying a final modifier for a method can prevent the method from being overridden. If a class is specified as final, all methods of that class are final. The Java compiler will look for opportunities to inline all final methods. Inline is of great significance to improving Java's running efficiency. For details, see Java runtime optimization. This move can improve performance by an average of 50%.
(2) Try to reuse objects
Especially for the use of String objects, StringBuilder/StringBuffer should be used instead when string concatenation occurs. Since Java virtual machines not only need to spend time generating objects, they may also need to spend time garbage collecting and processing these objects in the future, generating too many objects will have a great impact on the performance of the program.
(3) Use local variables as much as possible
The parameters passed when calling the method and the temporary variables created in the call are stored on the stack, which is faster. Other variables, such as static variables, instance variables, etc., are all created in the heap, which is slower. In addition, as the variables created in the stack are finished, these contents are gone and no additional garbage collection is required.
(4) Close the flow in time
During Java programming, be careful when performing database connection and I/O streaming operations. After use, close it in time to release resources. Because the operation of these large objects will cause large system overhead, and if you are not careful, it will lead to serious consequences.
(5) Minimize repeated calculations of variables
To clarify a concept, even if there is only one sentence in the method, it is still consumed, including creating stack frames, protecting the site when calling the method, and restoring the site when calling the method. So for example, the following operation:
for (int i = 0; i < list.size(); i++){...}It is recommended to replace it with:
for (int i = 0, length = list.size(); i < length; i++){...}In this way, when list.size() is very large, it reduces a lot of consumption
(6) Try to adopt a lazy loading strategy, that is, create it when needed
For example:
String str = "aaa";if (i == 1){ list.add(str);}It is recommended to replace it with:
if (i == 1){ String str = "aaa"; list.add(str);}(7) Use abnormalities with caution
Abnormality is not good for performance. To throw an exception, you must first create a new object. The constructor of the Throwable interface calls the local synchronization method named fillInStackTrace(). The fillInStackTrace() method checks the stack and collects call trace information. As long as an exception is thrown, the Java virtual machine must adjust the call stack because a new object is created during processing. Exceptions can only be used for error handling and should not be used to control program flow.
(8) Do not use try...catch... in loop, it should be placed on the outermost layer
According to the opinions put forward by netizens, I think this is worth discussing
(9) If the length of content to be added can be estimated, specify the initial length for the collection and tool classes implemented in an array on the underlying layer.
For example, ArrayList, LinkedLlist, StringBuilder, StringBuffer, HashMap, HashSet, etc. Take StringBuilder as an example:
StringBuilder() // Default allocate 16 characters of space StringBuilder(int size) // Default allocate 16 characters of space StringBuilder(String str) // Default allocate 16 characters + str.length() character space
Its initialization capacity can be set through the constructor of the class (here we refer not only to the StringBuilder above), which can significantly improve performance. For example, StringBuilder, length represents the number of characters that the current StringBuilder can maintain. Because when StringBuilder reaches its maximum capacity, it will increase its own capacity to 2 times and add 2. Whenever StringBuilder reaches its maximum capacity, it will have to create a new character array and copy the old character array content to the new character array - this is a very performance-consuming operation. Just imagine, if you can estimate that 5000 characters are stored in the character array without specifying the length, the power of 2 closest to 5000 is 4096, and the 2 added to each expansion is regardless of the 2, then:
Based on 4096, apply for 8194-sized character arrays, which add up to 12290-sized character arrays at once. If you can specify 5000-sized character arrays at the beginning, you will save more than twice the space to copy the original 4096 characters into the new character array.
This not only wastes memory space but also reduces code operation efficiency. Therefore, it is not wrong to set a reasonable initialization capacity for the collection and tool classes implemented in the underlying array, which will bring immediate results. However, note that collections like HashMap that are implemented in arrays + linked lists should not set the initial size the same as your estimated size, because the possibility of only one object connected to a table is almost 0. It is recommended to set the initial size to N power of 2. If you can estimate that there are 2,000 elements, set it to new HashMap(128) and new HashMap(256).
(10) When copying a large amount of data, use the System.arraycopy() command
(11) Multiplication and division use shift operations
For example:
for (val = 0; val < 100000; val += 5){ a = val * 8; b = val / 2;}Using shift operations can greatly improve performance, because at the bottom of the computer, the positioning operation is the most convenient and fastest, so it is recommended to modify it to:
for (val = 0; val < 100000; val += 5){ a = val << 3; b = val >> 1;}Although the shift operation is fast, it may make the code difficult to understand, so it is best to add corresponding comments.
(12) Do not constantly create object references in the loop
For example:
for (int i = 1; i <= count; i++){ Object obj = new Object(); }This approach will cause the count object object reference to exist in the memory. If the count is large, it will consume memory. It is recommended to change it to:
Object obj = null;for (int i = 0; i <= count; i++){ obj = new Object();}In this way, there is only one Object object reference in memory. Every time new Object() is used, the Object object reference points to a different Object, but there is only one Object in memory, which greatly saves memory space.
(13) Based on the consideration of efficiency and type checking, array should be used as much as possible. ArrayList should be used only if the array size cannot be determined.
(14) Try to use HashMap, ArrayList, and StringBuilder. Unless it is necessary for thread safety, it is not recommended to use Hashtable, Vector, and StringBuffer. The latter three have performance overhead due to the use of synchronization mechanisms.
(15) Do not declare arrays as public static final
Because this is meaningless, it only defines the reference as static final, and the content of the array can still be changed at will. Declaring the array as public is a security vulnerability, which means that the array can be changed by external classes
(16) Try to use single cases in suitable occasions
Using singletons can reduce the load burden, shorten the loading time, and improve loading efficiency, but not all places are suitable for singletons. Simply put, singletons are mainly applicable to the following three aspects:
Control the use of resources, control the generation of concurrent access control instances of resources through thread synchronization, so as to achieve the purpose of saving resources to control the sharing of data, and enable communication between multiple unrelated processes or threads without establishing direct associations.
(17) Try to avoid using static variables at will
You should know that when an object is referenced by a variable defined as a static, gc usually does not recycle the heap memory occupied by the object, such as:
public class A{ private static B b = new B(); }At this time, the life cycle of the static variable b is the same as that of Class A. If Class A is not uninstalled, the B object pointed to by reference B will be residing in memory until the program terminates
(18) Clear the no longer needed sessions in time
In order to clear sessions that are no longer active, many application servers have a default session timeout, usually 30 minutes. When the application server needs to save more sessions, if there is insufficient memory, the operating system will transfer part of the data to disk. The application server may also dump some inactive sessions to disk according to the MRU (most frequently used recently) algorithm, and may even throw insufficient memory exceptions. If the session is to be dumped to disk, it must be serialized first. In large-scale clusters, serializing objects is expensive. Therefore, when the session is no longer needed, the invalidate() method of HttpSession should be called in time to clear the session.
(19) A collection that implements RandomAccess interface, such as ArrayList, should use the most common for loop instead of foreach loop to traverse
This is recommended by JDK to users. The JDK API's explanation of the RandomAccess interface is: implementing the RandomAccess interface is used to indicate that it supports fast random access. The main purpose of this interface is to allow general algorithms to change their behavior, so that it can provide good performance when applied to random or continuous access lists. Practical experience shows that if class instances that implement the RandomAccess interface are randomly accessed, the efficiency of using ordinary for loops will be higher than that of using foreach loops; conversely, if accessed sequentially, it will be more efficient to use Iterator. You can use codes similar to the following to make judgments:
if (list instanceof RandomAccess){ for (int i = 0; i < list.size(); i++){}}else{ Iterator<?> iterator = list.iterable(); while (iterator.hasNext()){iterator.next()}}The underlying implementation principle of foreach loop is the iterator, see Java Syntax Sugar 1: Variable length parameters and foreach loop principle. So the second half of the sentence "Conversely, if it is accessed sequentially, using Iterator will be more efficient" means that the class instances that are accessed sequentially are traversed using a foreach loop.
(20) Use synchronous code blocks instead of synchronization method
This is already clearly stated in the article Synchronized lock method block in multi-threaded module. Unless it can be determined that the entire method needs to be synchronized, try to use synchronized code blocks to avoid synchronizing those codes that do not need to be synchronized, which affects the code execution efficiency.
(21) Declare the constant as static final and name it in capital
This way, these contents can be put into the constant pool during compilation, avoiding the calculation of the generated constant values during runtime. In addition, naming the name of a constant in capital can also facilitate the distinction between constants and variables
(22) Do not create some unused objects, do not import some unused classes
This makes no sense. If "The value of the local variable i is not used" and "The import java.util is never used" appear in the code, then please delete these useless contents
(23) Avoid using reflection during program operation
For more information, see Reflection. Reflection is a very powerful function provided by Java to users. Powerful functions often means low efficiency. It is not recommended to use the reflection mechanism frequently during the program operation, especially the Method's invoke method. If it is indeed necessary, a suggestive approach is to instantiate an object through reflection and put it into memory when the project is started. The user only cares about the fastest response speed when interacting with the peer, and does not care how long it takes for the peer project to start.
(24) Use database connection pool and thread pool
Both pools are used to reuse objects, the former avoids frequent opening and closing of connections, and the latter avoids frequent creation and destruction of threads
(25) Use buffered input and output streams for IO operations
Buffered input and output streams, namely BufferedReader, BufferedWriter, BufferedInputStream, BufferedOutputStream, which can greatly improve IO efficiency
(26) Use ArrayList for scenes with more sequential insertion and random access, and use LinkedList for scenes with more element deletion and intermediate insertion.
This is known by understanding the principles of ArrayList and LinkedList
(27) Don't let too many formal parameters in the public method
The public method is a method provided to the outside world. If you give these methods too many formal parameters, there are two main disadvantages:
Violating the object-oriented programming idea. Java emphasizes that everything is an object. Too many formal parameters and does not match the object-oriented programming idea. Too many parameters will inevitably lead to an increase in the probability of error in method calls.
As for how many "too many" refers to, 3 or 4. For example, we use JDBC to write an insertStudentInfo method. There are 10 student information fields to be inserted into the Student table. These 10 parameters can be encapsulated in an entity class as formal parameters of the insert method.
(28) When string variables and string constants equals are written in front of string constants
This is a relatively common trick, if there is the following code:
String str = "123";if (str.equals("123")){...}It is recommended to modify it to:
String str = "123";if ("123".equals(str)){ ...}This can mainly avoid null pointer exceptions
(29) Please know that in java there is no difference between if (i == 1) and if (1 == i), but from the perspective of reading habits, it is recommended to use the former
Sometimes people ask whether there is any difference between "if (i == 1)" and "if (1== i)"? This starts with C/C++.
In C/C++, the "if (i == 1)" judgment condition is valid, and it is based on 0 and non-0. 0 means false, and non-0 means true. If there is such a piece of code:
int i = 2;if (i == 1){ ...}else{ ...}C/C++ judges that "i==1" is not valid, so it is represented by 0, that is, false. But if:
int i = 2;if (i = 1){ ...}else{ ...}If the programmer accidentally writes "if (i == 1)" as "if (i = 1)", then there will be a problem. Assign i to 1 within if. If you determine that the content in it is not 0, the returned true, but i is obviously 2, and the value compared is 1, the false that should be returned. This situation is very likely to occur in the development of C/C++ and will cause some difficult-to-understand errors. Therefore, in order to avoid developers' incorrect assignment operations in if statements, it is recommended to write the if statement as:
int i = 2;if (1 == i){ ...}else{ ...}In this way, even if the developer accidentally writes "1 = i", the C/C++ compiler can check it out as soon as possible, because we can assign a variable value i to 1, but we cannot assign a constant value 1 to i.
However, in Java, the "if (i = 1)" syntax of C/C++ is impossible to appear, because once this syntax is written, Java will compile and report an error "Type mismatch: cannot convert from int to boolean". However, although there is no semantic difference between "if (i == 1)" and "if (1 == i)" in Java, it would be better to recommend using the former in terms of reading habits.
(30) Do not use the toString() method on the array
Let's take a look at what is printed using toString() for arrays:
int i = 2;if (1 == i){ ...}else{ ...}turn out:
I@18a992f
The original intention is to print out the contents of the array, but it may cause a null pointer exception because the array reference is null. However, although it does not make sense to the array toString(), the contents in the collection can be printed out for the collection toString(), because the parent class of the collection, the Object's toString() method is rewritten.
(31) Do not make downward force transformations on basic data types that are out of range
This will never get the desired result:
public static void main(String[] args){ long l = 12345678901234L; int i = (int)l; System.out.println(i);}We may expect to get some of them, but the result is:
1942892530
Explain it. Long in Java is 8 bytes with 64 bits, so the representation of 12345678901234 in the computer should be:
0000 0000 0000 0000 0000 1011 0011 1010 0111 0011 1100 1110 0010 1111 1111 0010
An int type data is 4 bytes and 32 bits. The first 32 bits of the above string of binary data are taken from the low bit:
0111 0011 1100 1110 0010 1111 1111 0010
This string of binary is represented as decimal 1942892530, so it is the content output on the console above. From this example, we can draw two conclusions:
1. The default data type of integer type is int, long l = 12345678901234L. This number has exceeded the range of int, so there is an L at the end, indicating that this is a long type number. By the way, the default type of floating point type is double, so when defining float, it should be written as ""float f = 3.5f"
2. Next, write another sentence "int ii = l + i;" and will report an error because long + int is a long and cannot be assigned to int
(32) The data not used in the public collection class must be removed in time
If a collection class is public (that is, it is not a property in a method), then the elements in this collection will not be automatically released because there are always references pointing to them. Therefore, if certain data in the public collection is not used and they are not removed, it will cause the public collection to continue to grow, causing the system to have the potential for memory leakage.
(33) Convert a basic data type into a string. The basic data type.toString() is the fastest way, followed by String.valueOf (data), and data +"" is the slowest
There are generally three ways to convert a basic data type to. I have an Integer type data i.e. toString(), String.valueOf(i), and i+"" three ways. How efficient are the three ways? See a test:
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.valueOf():" + (System.currentTimeMillis() - startTime) + "ms"); startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++) { String str = i.toString(); } System.out.println("Integer.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");}The running result is:
String.valueOf(): 11msInteger.toString(): 5ms + "": 25ms
Therefore, when you convert a basic data type to String in the future, you should give priority to using the toString() method. As for why, it's very simple:
String.valueOf() method is called Integer.toString() method, but it will short to judge Integer.toString() method before calling. I will call the i + ""The bottom layer uses the StringBuilder implementation. First use the append method to splice it, and then use the toString() method to get the string.
Compared with the three, it is obviously the fastest, the fastest, the slowest
(34) Use the most efficient way to traverse the Map
There are many ways to traverse the map. Usually, what we need to traverse the Key and Value in the Map. The recommended and most efficient method is:
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()); }} If you just want to iterate over the key value of this Map, it would be more appropriate to use " Set<String> keySet = hm.keySet();"
(35) It is recommended to operate separately for close() of resources
It means, for example, I have a piece of code like this:
try{ XXX.close(); YYY.close();}catch (Exception e){ ...}It is recommended to modify it to:
try{ XXX.close();}catch (Exception e){ ...}try{ YYY.close();}catch (Exception e){ ...}Although it is a bit troublesome, it can avoid resource leakage. We think that if there is no modified code, if XXX.close() throws an exception, it will enter the catch block. YYY.close() will not be executed, and the YYY resource will not be recycled and will be occupied all the time. With more codes like this, it may cause the resource handle to be leaked. After changing to the following writing method, it is guaranteed that XXX and YYY will be closed no matter what.
(36) For ThreadLocal, you must remove before or after use
Currently, basically all projects use thread pooling technology, which is very good. You can dynamically configure the number of threads and reuse threads.
However, if you use ThreadLocal in your project, remember to remove it before or after use. This is because the thread pool technology mentioned above is to reuse a thread, which means that during the code running, a thread will not be destroyed and will be waiting for the next use. Let's take a look at the reference in the Thread class that holds ThreadLocal.ThreadLocalMap:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;
The thread is not destroyed means that the data in the ThreadLocal.ThreadLocalMap of the previous thread set still exists. When the next thread reuses this Thread, it is likely that what you get is the data of the previous thread set rather than the content you want.
This problem is very obscure. Once errors caused by this cause occur, it is very difficult to find this problem without relevant experience or a solid foundation. Therefore, you should pay attention to this when writing code, which will reduce your subsequent workload.
(37) Remember to replace the devil number with constant definition. The existence of devil number will greatly reduce the readability of the code. Whether string constants use constant definitions can depend on the situation.
(38) When the initial assignment of long or Long, use uppercase L instead of lowercase l, because the letter l is very easy to be confused with the number 1, this point is very detailed and worth noting
(39) All rewrite methods must retain the @Override annotation
There are three reasons for doing this:
(1) It is clear that this method is inherited from the parent class
(2) getObject() and get0bject() methods. The fourth letter of the former is "O", and the fourth parent and child of the latter is "0". Adding the @Override annotation can immediately determine whether the rewrite is successful.
(3) Modify the method signature in the abstract class, and the implementation class will immediately report a compilation error.
(40) It is recommended to use the newly introduced Objects tool class in JDK7 to compare objects equals, directly a.equals(b), and there is a risk of null pointer exceptions.
(41) Do not use "+" to splice strings in the loop body, but use StringBuilder to continuously append
Let me talk about the reason why I don't use "+" for string splicing. If I have a method:
public String appendStr(String oriStr, String... appendStrs) { if (appendStrs == null || appendStrs.length == 0) { return oriStr; } for (String appendStr: appendStrs) { oriStr += appendStr; } return oriStr;}After compiling this code, decompile the .class file using javap -c to intercept the key part:
It means that every time the virtual machine encounters the "+" operator to splice the string, it will new a StringBuilder, then call the append method, and finally call the toString() method to convert the string to the oriStr object. That is, how many times the loop will new StringBuilder(), which is a waste of memory.
(42) Do not capture runtime exception class defined in Java class library inherited from RuntimeException
Exception handling efficiency is low, most of the runtime exception classes of RuntimeException can be completely avoided by programmers, such as:
(43) Avoid using Random instances by multiple threads. Although sharing the instance is thread-safe, it will cause performance degradation due to competition for the same seed. After JDK7, you can use ThreadLocalRandom to get random numbers
Explain the reason why competition for the same seed causes performance degradation. For example, take a look at the nextInt() method implementation of the Random class:
public int nextInt() { return next(32); }The next(int bits) method is called, which is a protected method:
protected int next(int bits) { long oldseed, nextseed; AtomicLong seed = this.seed; do { oldseed = seed.get(); nextseed = (oldseed * multiplier + addend) & mask; } while (!seed.compareAndSet(oldseed, nextseed)); return (int)(nextseed >>> (48 - bits));}And the seed here is a global variable:
/** * The internal state associated with this pseudorandom number generator. * (The specs for the methods in this class describe the ongoing * computing of this value.) */ private final AtomicLong seed;
When multiple threads obtain random numbers at the same time, they will compete for the same seed, resulting in a decrease in efficiency.
(44) Static classes, singleton classes, and factory classes set their constructors to private
This is because we don't need to new them outside. After setting the constructor to private, we ensure that these classes will not produce instance objects.
postscript
Excellent code comes from every little optimization. Paying attention to every detail can not only improve the program's running efficiency, but also avoid many unknown problems.
The above are 44 Java code optimization suggestions introduced by the editor. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. Thank you very much for your support to Wulin.com website!