Mastering Java Pitfalls: NPE, Thread Safety, and Performance Best Practices
This article compiles common Java bugs such as NullPointerException, thread‑safety issues, improper exception handling, and performance traps, and provides practical solutions including annotations, Optional, MapStruct, concurrent utilities, immutable objects, proper resource management, and Spring transaction safeguards.
Problem List
NullPointerException
NPE is perhaps the most common programming error; Tony Hoare called it the "billion‑dollar mistake". Java lacks built‑in null handling, but annotations and Optional can help avoid it.
Use JSR‑305 / JetBrains annotations
NotNull
Nullable
By explicitly marking method parameters, return values, and fields with these annotations and using static analysis tools, most NPEs can be caught at compile time.
Use Optional for chain calls
Optional, introduced in Guava and later added to Java 8, forces callers to handle possible absence of a value and enables elegant chain calls.
<span>public class OptionalExample {</span>
<span> public static void main(String[] args) {</span>
<span> // traditional null handling</span>
<span> User user = getUser();</span>
<span> String city = "DEFAULT";</span>
<span> if (user != null && user.isValid()) {</span>
<span> Address address = user.getAddress();</span>
<span> if (address != null) {</span>
<span> city = address.getCity();</span>
<span> }</span>
<span> }</span>
<span> System.out.println(city);
<span>
<span> // using Optional</span>
<span> Optional<User> optional = getUserOptional();</span>
<span> city = optional.filter(User::isValid)</span>
<span> .map(User::getAddress)</span>
<span> .map(Address::getCity)</span>
<span> .orElse("DEFAULT");</span>
<span> System.out.println(city);
<span> }
<span>
<span> @Nullable
<span> public static User getUser() { return null; }
<span>
<span> public static Optional<User> getUserOptional() { return Optional.empty(); }
<span>
<span> @Data
<span> public static class User { private Address address; private boolean valid; }
<span>
<span> @Data
<span> public static class Address { private String city; }
<span>}Objects.equals instead of a.equals(b)
Using Objects.equals(a, b) prevents NPE when either argument may be null.
Empty Object Pattern
Replace null collections or objects with immutable empty instances to avoid null checks.
<span>public class EmptyListExample {</span>
<span> public static void main(String[] args) {</span>
<span> List<String> listNullable = getListNullable();</span>
<span> if (listNullable != null) {</span>
<span> for (String s : listNullable) { System.out.println(s); }</span>
<span> }
<span>
<span> List<String> listNotNull = getListNotNull();</span>
<span> for (String s : listNotNull) { System.out.println(s); }
<span> }
<span>
<span> @Nullable
<span> public static List<String> getListNullable() { return null; }
<span>
<span> @NotNull
<span> public static List<String> getListNotNull() { return Collections.emptyList(); }
<span>}Null Strategy
<span>public class NullStrategyExample {</span>
<span> private static final Map<String, Strategy> strategyMap = new HashMap<>();
<span>
<span> public static void handle(String strategy, String content) {</span>
<span> findStrategy(strategy).handle(content);
<span> }
<span>
<span> @NotNull
<span> private static Strategy findStrategy(String key) {</span>
<span> return strategyMap.getOrDefault(key, new DoNothing());
<span> }
<span>
<span> public interface Strategy { void handle(String s); }
<span>
<span> public static class DoNothing implements Strategy { @Override public void handle(String s) {} }
<span>}Object Conversion
Manual DTO conversion is error‑prone; MapStruct generates compile‑time mapper code, catching unmapped fields early.
<span>@Mapper(componentModel = "spring", unmappedSourcePolicy = ReportingPolicy.ERROR, unmappedTargetPolicy = ReportingPolicy.ERROR, uses = DateUtil.class)</span>
<span>public interface UserConvertor {</span>
<span> UserDTO toUserDTO(UserDO userDO);
<span>
<span> @Data class UserDO { private String name; private Integer age; private Date birthDay; }
<span>
<span> @Data class UserDTO { private String name; private Integer age; private String birthDay; }
<span>}Usage in a service:
<span>@RequiredArgsConstructor
<span>@Component
<span>public class UserService {</span>
<span> private final UserDao userDao;
<span> private final UserConvertor userConvertor;
<span>
<span> public UserDTO getUser(String userId) {</span>
<span> UserDO userDO = userDao.getById(userId);
<span> return userConvertor.toUserDTO(userDO);
<span> }
<span>}Thread‑Safety Issues
The JVM memory model is complex; follow basic Java concurrency rules and use thread‑safe classes.
Use thread‑safe classes
Example of non‑atomic Map.get/put leading to inconsistency:
<span>public class ConcurrentHashMapExample {</span>
<span> private Map<String, String> map = new ConcurrentHashMap<>();
<span>
<span> public void appendIfExists(String key, String suffix) {</span>
<span> String value = map.get(key);
<span> if (value != null) { map.put(key, value + suffix); }
<span> }
<span>}Correct atomic version using computeIfPresent:
<span>public class ConcurrentHashMapExample {</span>
<span> private Map<String, String> map = new ConcurrentHashMap<>();
<span>
<span> public void append(String key, String suffix) {</span>
<span> map.computeIfPresent(key, (k, v) -> v + suffix);
<span> }
<span>}Ensuring Atomic Changes
Use volatile or atomic wrappers; avoid double‑checked locking without volatile.
<span>class Foo {</span>
<span> private volatile Helper helper = null;</span>
<span> public Helper getHelper() {</span>
<span> if (helper == null) { synchronized (this) { if (helper == null) { helper = new Helper(); } } }
<span> return helper;
<span> }
<span>}Immutable Objects
Immutable objects (e.g., String, BigDecimal, custom value objects) are naturally thread‑safe.
<span>@Getter
<span>public class AtomicDiamondParser {</span>
<span> private volatile Range range;</span>
<span> public AtomicDiamondParser() { /* listener sets range */ }
<span>
<span> @Value
<span> public static class Range { private int start; private int end; }
<span>}Exception Handling
Avoid duplicated try‑catch blocks, swallowing exceptions, or exposing unknown exceptions.
<span>@Slf4j
<span>public class DuplicatedExceptionHandlerExample {</span>
<span> private UserService userService;
<span>
<span> public User query(String id) {</span>
<span> try { return userService.query(id); }
<span> catch (Exception e) { log.error("query error, userId: {}", id, e); return null; }
<span> }
<span>
<span> public User create(String id) {</span>
<span> try { return userService.create(id); }
<span> catch (Exception e) { log.error("create error, userId: {}", id, e); return null; }
<span> }
<span>}Use AOP to centralise handling, convert unknown exceptions to a common result type, and log consistently.
Checked Exceptions
Checked exceptions add boiler‑plate; Lombok's @SneakyThrows can hide them when appropriate.
Thread‑Pool Misuse
Unbounded cached thread pools can exhaust resources under load; prefer bounded ThreadPoolExecutor.
<span>public class ManualCreateThreadPool {</span>
<span> private Executor executor = new ThreadPoolExecutor(10, 10, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("work-%d").build());
<span>}Spring Bean Implicit Dependencies
Static access to beans or switches can break initialization order; use BeanFactoryPostProcessor or explicit injection to guarantee ordering.
<span>@Component
<span>public class PreInitializer implements BeanFactoryPostProcessor, PriorityOrdered {</span>
<span> public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; }
<span> public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) throws BeansException { SwitchManager.init(appName, SwitchClass.class); }
<span>}Memory / Resource Leaks
Global maps retain objects; replace with Guava LoadingCache with size/expiry limits.
<span>@Service
<span>public class MetaInfoManager {</span>
<span> private LoadingCache<String, MetaInfo> loadingCache = CacheBuilder.newBuilder().maximumSize(1000).build(new CacheLoader<String, MetaInfo>() { public MetaInfo load(String key) { return loadFromRemote(key); } });
<span> public MetaInfo getMetaInfo(String id) { return loadingCache.getUnchecked(id); }
<span> private MetaInfo loadFromRemote(String id) { return null; }
<span> @Data static class MetaInfo { private String id; private String name; }
<span>}Runtime Class Generation
Libraries like CGLIB or Javassist generate classes that are hard for the JVM to reclaim; use them sparingly.
Try‑With‑Resources
<span>public class TryWithResourceExample {</span>
<span> public static void main(String[] args) throws IOException {</span>
<span> try (InputStream in = Files.newInputStream(Paths.get(""))) { /* read */ }
<span> }
<span>}Performance Issues
Common bottlenecks are excessive logging, large object serialization, remote calls, and database access. Use professional tools like JMH, Arthas flame graphs, or profilers instead of ad‑hoc loops.
<span>@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
<span>@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
<span>@Fork(3)
<span>@BenchmarkMode(Mode.AverageTime)
<span>@OutputTimeUnit(TimeUnit.NANOSECONDS)
<span>public class JMHExample {</span>
<span> @Benchmark public void testPerformance(Blackhole bh) { bh.consume(multiply(10, 10)); }
<span> private int multiply(int a, int b) { return a * b; }
<span>}URL equals/hashCode
Using URL.equals triggers DNS lookups; prefer URI for pure string comparison.
Spring Transaction Pitfalls
Transactional proxies are bypassed when a bean calls its own @Transactional method; extract the method to another bean or use self‑injection.
<span>@Component
<span>public class TransactionNotWork {</span>
<span> public void doTheThing() { actuallyDoTheThing(); }
<span> @Transactional public void actuallyDoTheThing() { /* ... */ }
<span>}References
Null: The Billion‑Dollar Mistake – https://www.infoq.cn/article/uyyos0vgetwcgmo1ph07
Double‑Checked Locking – https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
Latency Numbers Every Programmer Should Know – https://colin-scott.github.io/personal_website/research/interactive_latency.html
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.
Alibaba Cloud Developer
Alibaba's official tech channel, featuring all of its technology innovations.
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.
