Java Code Optimization Tips and Best Practices
This article presents a comprehensive collection of Java code optimization techniques, covering topics such as using final modifiers, object reuse, local variables, proper resource management, loop improvements, lazy loading, exception handling, collection sizing, and efficient map traversal to enhance performance and reduce memory usage.
Preface
Code optimization is an important topic; even seemingly insignificant changes can accumulate to a noticeable performance boost, much like a whale gaining weight by eating many tiny shrimp.
Goals of Code Optimization
1. Reduce the size of the code. 2. Improve the runtime efficiency of the code.
Optimization Details
1. Use the final modifier for classes and methods
Marking a class as final prevents it from being subclassed. Declaring a method as final allows the JVM to inline it, which can improve performance by up to 50%.
2. Reuse objects whenever possible
Especially for String objects, use StringBuilder or StringBuffer for concatenation to avoid creating many temporary objects that increase garbage‑collection overhead.
3. Prefer local variables
Method parameters and temporary variables are stored on the stack, which is faster than heap‑allocated static or instance variables and does not require extra garbage collection.
4. Close streams promptly
When working with database connections or I/O streams, always close them after use to release resources and avoid large system overhead.
5. Avoid repeated calculations of the same variable
Cache the result of expensive calls, e.g., replace repeated list.size() calls with a cached length:
for (int i = 0, length = list.size(); i < length; i++) {
// ...
}6. Apply lazy loading
Create objects only when they are needed. Example:
String str = "aaa";
if (i == 1) {
list.add(str);
}After refactoring, the object creation is moved inside the conditional block.
7. Use exceptions sparingly
Throwing an exception creates a new object and captures a stack trace, which is costly. Exceptions should be used for error handling, not for controlling program flow.
8. Place try…catch blocks at the outermost level when possible
Wrapping inner loops with try…catch can degrade performance and make the code harder to read.
9. Pre‑size collections when the expected size is known
For array‑based collections such as ArrayList, StringBuilder, HashMap, etc., specify an initial capacity to avoid repeated resizing and copying.
10. Use System.arraycopy() for bulk copying
When copying large arrays, System.arraycopy() is much faster than manual element‑by‑element copying.
11. Replace multiplication/division with bit‑shift operations when appropriate
for (int val = 0; val < 100000; val += 5) {
a = val << 3; // val * 8
b = val >> 1; // val / 2
}Bit‑shifts are faster but should be documented for readability.
12. Avoid creating objects inside tight loops
for (int i = 1; i <= count; i++) {
Object obj = new Object(); // creates many objects
}Instead, reuse a single reference or move object creation outside the loop.
13. Prefer arrays over ArrayList when the size is fixed
Arrays have less overhead and better cache locality.
14. Prefer non‑synchronized collections unless thread safety is required
Use HashMap, ArrayList, StringBuilder instead of their synchronized counterparts ( Hashtable, Vector, StringBuffer) to avoid unnecessary locking.
15. Do not declare arrays as public static final
While the reference is constant, the array contents can still be modified, which may lead to security issues.
16. Use the Singleton pattern judiciously
Singletons can reduce loading time and resource usage but should only be applied when a single shared instance truly makes sense (resource control, instance control, data sharing).
17. Avoid overusing static variables
Static references prevent the garbage collector from reclaiming the objects they point to, potentially causing memory leaks.
18. Explicitly invalidate unused HTTP sessions
Calling HttpSession.invalidate() frees session resources and prevents unnecessary serialization in clustered environments.
19. Use a classic for loop for RandomAccess collections
if (list instanceof RandomAccess) {
for (int i = 0; i < list.size(); i++) {
// fast random access
}
} else {
for (Iterator<?> it = list.iterator(); it.hasNext(); ) {
it.next();
}
}20. Prefer synchronized blocks over synchronized methods
Synchronize only the critical section to avoid locking code that does not need protection.
21. Declare constants as static final and use uppercase names
This allows the compiler to place them in the constant pool and makes them easy to identify.
22. Remove unused variables and imports
Eliminate warnings such as "The value of the local variable i is not used" and "The import java.util is never used".
23. Minimize the use of reflection at runtime
Reflection is powerful but incurs a performance penalty; cache reflective objects when they must be used.
24. Use connection pools and thread pools
Pooling reuses expensive resources like database connections and threads, reducing creation overhead.
25. Use buffered I/O streams
Classes such as BufferedReader, BufferedWriter, BufferedInputStream, and BufferedOutputStream greatly improve I/O efficiency.
26. Choose ArrayList for random access and LinkedList for frequent insertions/removals
Understanding their internal structures helps select the right implementation.
27. Keep the number of method parameters reasonable
Too many parameters (more than 3‑4) violate object‑oriented principles and increase the chance of errors; consider encapsulating parameters in a DTO.
28. Write constant strings first in equals checks
if ("123".equals(str)) {
// avoid NullPointerException
}29. Prefer i == 1 over 1 == i in Java for readability
Both are equivalent, but the former is more idiomatic in Java.
30. Do not use toString() on arrays
Calling array.toString() prints the object’s hash code (e.g., [I@18a992f) rather than its contents. Use Arrays.toString(array) instead.
31. Avoid unsafe down‑casting of primitive types
Casting a large long to int truncates the high bits and yields unexpected values.
32. Remove unused elements from shared collections
Failing to clean up unused entries can cause memory leaks in long‑lived collections.
33. Convert primitive types to String efficiently
Use primitive.toString() (fastest), then String.valueOf(primitive), and avoid primitive + "" (slowest).
34. Iterate over Map efficiently
Set<Map.Entry<String, String>> entrySet = map.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());
}For only keys, use map.keySet().
35. Close resources separately to avoid loss on exception
try { xxx.close(); } catch (Exception e) { /* handle */ }
try { yyy.close(); } catch (Exception e) { /* handle */ }This guarantees that each resource is closed even if the previous close() throws.
Conclusion
Applying these 35 practical tips can significantly improve Java application performance, reduce memory consumption, and prevent common pitfalls such as resource leaks and unnecessary object creation.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
