Brief Notes From Effective Java

Batuhan Kadıoğlu
Sahibinden Technology
6 min readSep 1, 2021

This blog post consists of brief notes from Effective Java(3rd edition) by Joshua Bloch and my thoughts. Especially to improve the post’s readability, I chose some items that I want to emphasize. This book includes very valuable experience in java and software engineering. You should add it to your reading list. Let’s start!

Photo by Michiel Leunens on Unsplash

Eliminate obsolete object references

As is known java has an automatic memory management system. This system protects developers from the complexity of explicit memory management. But there is no free lunch. Some code habits can block GC, thus memory leaks and other memory problems can happen.

Let’s think we design a stack that uses an array in the background.

private int size = 0;public Object pop() {  return elements[ — size];}

If implements a pop method as above, GC can not deallocate unused parts of the array. And the devil shows itself slowly, “memory leak”.

public Object pop() {  Object result = elements[ — size];  elements[size] = null;  return result;}

If a class manages its memory, the programmer should be alert for memory leaks. Whenever an element is freed, any object references contained in the element should be nulled out. But nulling out object references should be the exception rather than the norm.

This occurs naturally if you define each variable in the narrowest possible scope.

Try-With-Resources Better Than Try-Finally

The libraries may include resources that must be closed manually by invoking a close method. File operations, DB connections, etc. Sometimes resource closing is missed by clients and it can cause bad consequences.

Okay, we decided to close resources. But why do we not use an old good friend “Try-Finally”?

Prone to error syntax

If we use inner try-finally blocks, the readability of the code decreases dramatically.

Exception obligating

The code in both the “try” block and the “finally” block is capable of throwing exceptions. In this scenario, the first exception was obligated by the second(when invoking the close method). In this manner, root cause of the problem can not be viewed in the exception stack trace.

Try-Finally

static void copy(String src, String dst) throws IOException {  InputStream in = new FileInputStream(src);  try {    OutputStream out = new FileOutputStream(dst);    try {    ……    } finally {      out.close();    }  } finally {  in.close();  }}

Try-With-Resources

static void copy(String src, String dst) throws IOException {  try (InputStream   in = new FileInputStream(src);  OutputStream out = new FileOutputStream(dst)) {   ...  }}

Less prone to error, clean syntax, what else does a developer want?

Minimize The Accessibility Of Classes And Members

“The single most important factor that distinguishes a well-designed component from a poorly designed one is the degree to which the component hides its internal data and other implementation details from other components”

Encapsulation is one of the most important pillars for object-oriented paradigm.

  • It can not cause good performance but enables effective performance tuning. (Decoupled modules).
  • Increase reuse. (Because the components not strong tied can reuse different contexts).
  • Boost development speed.

Private fields and public accessors are widely used “best practices”. In the level of class and method same tendency. If you have not any acceptable cause for choice to access modifiers, the order must be private, package-private, protected, public.

Minimize Mutability

James Gosling(the creator of Java), asked him in an interview when should one use immutables, he answered:

“I would use an immutable whenever I can.”

Main advantages of using immutability;

  • Simple. An immutable object can be in exactly one state.
  • Thread-safe, they do not require synchronization.
  • Ease of caching.

And on the other hand, this is the way goes Java* and other new programming languages*.

Don’t Use Raw Types

Generics were added to Java programming language in 2004 with version 5. But because of requirements known as “migration compatibility” java still supports raw types. First of all, make a quick look at raw types and other alternatives.

List<String> strList = … → list of stringList strList = … → raw

The first instance provides a strong type-safe on the compiler phase. Do not make any surprise on runtime like “Hey this reference is not a string, I have little sweat ClassCastException for you”.

But the second one creates an error-prone environment. You can add any kind of object to this list without a compiler error(with compiler warning). Exactly this situation delays error to runtime.

On the other hand, if I want to create a method that works without care about object type, can I use raw type? Unbound wildcard was more proper/safe.

int numElementsInCommon(Set s1, Set s2) → raw typeint numElementsInCommon(Set<?> s1, Set<?> s2) → unbound wildcard

Briefly raw types are provided only for compatibility and interoperability with legacy code that predates the introduction of generics. Don’t use them.

Photo by Munro Studio on Unsplash

Return Empty Collections Or Arrays, Not Nulls

Pretty simple but effective. If not used, every call must write a terrible if statement. Otherwise, encounter the null pointer exception will be cheesy.

Java presents some immutable empty collections; Collections.emptyList(), Collections.emptySet(), Collections.emptyMap(). As immutable objects may be shared freely, using these collections can be more convenient.

public List<Item> getItems() {  return itemList.isEmpty() ? Collections.emptyList()  : new ArrayList<>(itemList);}

Design Method Signatures Carefully

Name

Choose method names carefully. Names should always obey the standard naming conventions and should be understandable and consistent. Especially using different names for the same operation is a bad habit. For example;

List<User> getAllUsers() {  // get all user from the database (with hibernate or any kind of    ORM)}List<Store> loadAllStores() {  // get all store from the database (with hibernate or any kind of ORM)}

Parameter List

Although most modern IDE’s helps to understand method signature long method list always error-prone. Gather them around a concept. For example;

addMarker(String title, String colorCode, String url, long latitude, long longitude)addMarker(Marker marker)

Another alternative technique could be to implement the builder pattern.

Don’t Reinvent the Wheel

Occasionally a library can fail your meets. But most of the time there is at least one library that presents the functionality you require. This library can be in java sub-packages or a third party(actively maintained, secure, reputable, etc.) In this context to mention some advantage of the using library can be beneficial;

  • You take advantage of the knowledge of the experts who wrote it and the experience of those who used it before you.
  • Saving on time. There is more time for application rather than infrastructure.
  • Make your code more readable, maintainable, reusable by the multitude of developers.
  • You do not effort to gain the new functionality, performance improvements, etc.

Avoid Float And Double If Exact Answers Are Required

The float and double types are designed primarily for scientific and engineering calculations. They perform binary floating-point arithmetic* which designed for approximations quickly over a broad range of magnitudes. They do not provide exact results and should not be used where exact results are required.

BigDecimal x = new BigDecimal(“1.89”);BigDecimal y = new BigDecimal(“1.03”);System.out.println(x.subtract(y));System.out.println(1.89–1.03);0.860.8599999999999999

If you require an exact result(for example monetary calculations), should use BigDecimal or other specific implementation.

*IEEE 754

Refer To Objects By Their Interfaces

Using interfaces as types make your program will be much more flexible. If you decide the change implementation for any reason(for example performance). All you have to do change the class name without compile error.

Map<String,String> map = new HashTable<>(); — — after a while, when I don’t need thread-safetyMap<String,String> map = new HashMap<>();

If there is no suitable interface, you can use the least specific class in the class hierarchy that provides the required functionality.

Favor The Use Of Standard Exceptions

“An attribute that distinguishes expert programmers from less experienced ones is that experts strive for and usually achieve a high degree of code reuse.”

The Java libraries provide a set of exceptions that cover most of the exception-throwing needs of most APIs. Benefits of reusing exception;

  • Easier to learn and use. (conventions that programmers are already familiar with)
  • Small memory print.
  • Less time for class loading.

IllegalArgumentException, IllegalStateException, NullPointerException, IndexOutOfBoundsException, UnsupportedOperationException, ConcurrentModificationException some of those.

If an exception fits your needs, use it. On the other hand, feel free to subclass a standard exception if you want to add more detail.

--

--

Batuhan Kadıoğlu
Sahibinden Technology

I’m writing about distributed systems, concurrency programming Java, Go, and more.