40 Essential Java Code Optimization Tips for Faster, Safer Applications
This article explains why code optimization is crucial for preventing unexpected runtime errors, outlines the main goals of reducing code size and improving execution efficiency, and provides a comprehensive list of practical Java optimization techniques—from using final modifiers and reusing objects to proper resource handling and avoiding common pitfalls—complete with code examples and performance explanations.
In my opinion, the most important purpose of code optimization is to avoid unknown errors. During the deployment and execution of code in production, many unexpected errors can occur because the production environment differs significantly from the development environment; locating these errors often boils down to a very small cause.
To solve such errors, we first need to self‑validate, then package the class files to be replaced, pause the business, and restart. For a mature project, the last step can have a huge impact, meaning users cannot access the application during that time. Therefore, paying attention to details from the source and making optimal choices can greatly reduce unknown errors and, in the long run, lower the amount of work required.
The goals of code optimization are:
Reduce the size of the code.
Improve the runtime efficiency of the code.
The content of this article comes partly from the Internet and partly from my own work and study. The important thing is whether the detailed optimization tips are truly useful. This article will be updated continuously whenever I encounter worthwhile optimization details.
Code Optimization Details
1. Use the final modifier for classes and methods whenever possible
Classes marked as final cannot be subclassed. Many core Java APIs, such as java.lang.String, are final. Declaring a class as final prevents inheritance, and declaring a method as final prevents overriding. The Java compiler can inline final methods, which can improve performance by up to 50%.
2. Reuse objects whenever possible
Especially for String objects, use StringBuilder or StringBuffer instead of repeated string concatenation. Creating many objects incurs allocation and garbage‑collection overhead, which can significantly affect performance.
3. Prefer local variables
Method parameters and temporary variables created during a call are stored on the stack, which is fast. Static or instance variables reside on the heap and are slower. Stack variables are automatically reclaimed when the method finishes, avoiding extra garbage‑collection work.
4. Close streams promptly
When working with database connections or I/O streams in Java, always close them after use to release resources. Failing to do so can cause large system overhead and severe consequences.
5. Avoid repeated calculations of the same variable
Even a single‑statement method call has overhead (stack‑frame creation, state saving, etc.). For example, replace: for (int i = 0; i < list.size(); i++) { ... } with:
for (int i = 0, length = list.size(); i < length; i++) { ... }This reduces the cost when list.size() is large.
6. Use lazy‑initialization
Create objects only when they are needed. Example:
String str = "aaa";
if (i == 1) {
list.add(str);
}Replace with:
if (i == 1) {
String str = "aaa";
list.add(str);
}7. Use exceptions sparingly
Throwing an exception creates a new object and invokes fillInStackTrace(), which is costly. Exceptions should be used for error handling, not for controlling program flow.
8. Avoid try…catch inside loops; place it outside the loop
This suggestion is open to discussion.
9. Pre‑allocate capacity for collections that use arrays internally
For ArrayList, StringBuilder, etc., specify an initial capacity when possible. For example, new StringBuilder(5000) avoids repeated resizing.
10. Use System.arraycopy() for bulk copying
When copying large amounts of data, System.arraycopy() is the most efficient method.
11. Use bit‑shift operations instead of multiplication/division
Replace:
a = val * 8;
b = val / 2;with:
a = val << 3;
b = val >> 1;Bit‑shifts are much faster, though you should add comments for readability.
12. Do not create new object references inside a loop
Instead of creating a new object on each iteration, reuse a single reference:
Object obj = null;
for (int i = 0; i <= count; i++) {
obj = new Object();
}13. Prefer arrays over ArrayList when random access is frequent
Use ArrayList for many insertions/deletions; use LinkedList when deletions and middle insertions are common.
14. Prefer HashMap , ArrayList , StringBuilder over their synchronized counterparts
Unless thread safety is required, avoid Hashtable, Vector, and StringBuffer because synchronization adds overhead.
15. Do not declare arrays as public static final
Such a declaration does not make the array immutable; the contents can still be modified, and exposing the array publicly is a security risk.
16. Use the Singleton pattern where appropriate
Singletons can reduce loading overhead, shorten load time, and improve efficiency, but they should only be used when the three typical use‑cases apply: controlling resource usage, limiting instance creation, and sharing data without direct coupling.
17. Avoid arbitrary static variables
Static references prevent garbage collection of the referenced objects, potentially causing memory leaks.
18. Invalidate sessions promptly
Call HttpSession.invalidate() when a session is no longer needed to free resources.
19. Iterate RandomAccess collections with a classic for loop
For collections that implement RandomAccess (e.g., ArrayList), a plain for loop is faster than a foreach loop.
20. Prefer synchronized blocks over synchronized methods
Use synchronized blocks only around the code that truly needs synchronization to avoid unnecessary performance penalties.
21. Declare constants as static final and use uppercase naming
This allows the compiler to place them in the constant pool and makes them easy to distinguish from variables.
22. Remove unused objects 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. Avoid reflection in performance‑critical code
Reflection is powerful but slow; avoid it, especially frequent Method.invoke() calls. If necessary, instantiate reflective classes at startup.
24. Use connection pools and thread pools
Both pools reuse objects, reducing the overhead of repeatedly opening connections or creating threads.
25. Use buffered I/O streams
BufferedReader, BufferedWriter, BufferedInputStream, and BufferedOutputStream greatly improve I/O efficiency.
26. Choose ArrayList for frequent sequential access and LinkedList for many deletions/insertions
Understanding the underlying implementation helps you pick the right collection.
27. Keep the number of parameters in public methods small (3‑4 max)
Too many parameters violate object‑oriented principles and increase the chance of errors. Wrap related parameters in a DTO when necessary.
28. Write string literals first in equals comparisons
Use "123".equals(str) instead of str.equals("123") to avoid possible NullPointerException.
29. Prefer if (i == 1) over if (1 == i) for readability
Both are equivalent in Java, but the former is more natural to read.
30. Do not call toString() on arrays
Calling toString() on an array prints a hash code like [I@18a992f. Use Arrays.toString() instead, or rely on collection toString() implementations.
31. Avoid down‑casting primitive types beyond their range
Casting a large long to int truncates high bits, producing unexpected results.
32. Remove unused elements from shared collections
Publicly shared collections that retain unused data can cause memory leaks.
33. Convert primitive types to String efficiently
Use Integer.toString(i) (fastest), then String.valueOf(i), and avoid i + "" (slowest).
34. Iterate Map efficiently
Use entrySet() with an iterator:
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 only keys are needed, use hm.keySet().
35. Close resources in separate try blocks
Closing XXX and YYY in the same try can cause the second close to be skipped if the first throws. Use two independent try / catch blocks to guarantee both are closed.
36. Remove ThreadLocal values after use
Because thread pools reuse threads, leftover ThreadLocal data can be accessed by subsequent tasks, leading to subtle bugs.
37. Replace magic numbers with named constants
Constants improve readability; string constants may be defined as needed.
38. Use uppercase L for long literals
Lowercase l can be confused with the digit 1.
39. Annotate overridden methods with @Override
This makes inheritance explicit, helps catch signature mismatches, and prevents accidental overloading.
40. Use Objects.equals(a, b) (Java 7+) to avoid NullPointerException
It safely handles nulls.
41. Avoid string concatenation with + inside loops; use StringBuilder
Each + creates a new StringBuilder, leading to unnecessary object creation.
42. Do not catch runtime exceptions that extend RuntimeException
Most can be avoided by proper checks (e.g., avoid ArithmeticException by checking divisor, NullPointerException by null checks, etc.).
43. Avoid sharing a single Random instance across threads
Contention on the internal seed degrades performance. Use ThreadLocalRandom (Java 7+) instead.
44. Make constructors of utility, singleton, and factory classes private
This prevents accidental instantiation.
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.
Open Source Linux
Focused on sharing Linux/Unix content, covering fundamentals, system development, network programming, automation/operations, cloud computing, and related professional knowledge.
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.
