Java Fundamentals: Part 2

Vijay
10 min readJan 7, 2019

--

This post highlights some of the most important aspects of Java programming. The keywords and concepts covered in this blog are not-only important to understand but practical and routinely appear in some form during interviews for software engineering positions.

Java Keywords and Concepts

final, finally, finalize

final

  • When you assign the final reserved keyword to a variable in Java, this prevents modification of its’ state once initialized. If a variable is declared as final, you cannot modify its’ value — the result if we were to try is an immediate Compile Time Error. When you assign the final keyword to a class, this disallows the class from being extended. When you assign the final keyword to a method, then it cannot be overridden.It is important to note that in the case that a class is declared as final, then by default all methods in the class are also final… variables however are not.

finally

  • The finally keyword is a reserved keyword that really functions exactly as it’s named. It is used in association with the try/catch block, and basically identifies a piece of code that always executes when the try block exists. It is code that is executed even if an unexpected exception (checked or unchecked) occurs. Common practice is to put cleanup code (i.e. close a file), or in a bigger picture — code that prevents resource leaks — in a finally block because the run-time system always executes the statements regardless of what happens in the try block.
try
{
int i = arr[4];

// this statement will never execute
// as exception is raised by above statement
System.out.println("Inside try block");
}

catch(ArrayIndexOutOfBoundsException ex)
{
System.out.println("Exception caught in catch block");
}

finally
{
System.out.println("finally block executed");
}

finalize

  • The root of all classes — Object. This class contains a finalize method, which gets invoked when the lifecycle of an object is coming to an end and the JVM figures out that a particular object should be garbage collected. The main purpose of a finalize() method is provide a piece of code which allows for the release of resources used by objects BEFORE they’re removed from memory. This method can serve as a safety net when other methods fail. However, there are several problems that exist when using the reserved keyword finalizer, without getting into the implementation details, just realize a few things…
  • No timing guarantee of method execution since garbage collection can occur at anytime
  • System resources are limited, not all code may be executed prior to a system crash
  • Performance hit due to the JVM having to work extra hard when dealing with objects containing a non-empty finalize()

What is meant by immutability and why is it important?

  • In Java, an object is immutable if its state cannot change after construction. Immutable objects provide the benefit of being Thread safe — they do not permit concurrent modifications of the same instance across several threads. Immutable objects can be passed around without fear they will be modified, and in the case that their is a modification in state, this results in a new instance.

Example of an Immutable Class

  • The most straightforward example of an immutable class in Java is the String. When you initialize a variable of the String type, that value is final by definition, and it cannot be modified. Operations such as trim(), substring(), and replace() ALWAYS return a new instance, and don’t affect the current instance.
String test = "test";
test = "changed"; // Correct - reference points to new value
String test = "test";
String testTrim = test.trim(); // Correct - new instance returned
  • Other examples from the JDK include the wrapper classes: Integer, Float, and Boolean — these classes create a new instance every time you attempt to modify them.

How to create an Immutable Class?

There are a few rules that must be adhered to in order to create an immutable class…

The whole point of an immutable class is to create an object that is safe from modification. Marking all fields as final means that they cannot be changed after initial construction.

  • Mark the class as final — no classes can extend it
  • Mark the fields as final — one-time initialization

Don’t expose setter methods…

  • If you would like to expose methods which allow modification of an object’s state, you must always return a new instance of the class.

Immutable classes can include mutable object references

  • If the class holds a mutable object, make sure to use a clone copy when modifying fields. This is simple. When we have an immutable class with a mutable object as a member variable, we will simply pass it into the constructor, the same as we do for all objects in OOP and additionally we will also create a new instance of that class. It is on the new instance that we will extract the parameters from the object provided as input, and set on the new instance. This is the clone copy method. Upon setting all fields of the cloned copy, we will than assign the new object to the instance field for the class, ensuring our entire class remains immutable.
public class ImmutableObject {
private final int primitiveField;
private final Object objectField;
public ImmutableObject(int p, Object o) {
primitiveField = p;
objectField = new Object(); // Clone
objectField.setField(o.getField()); // Copy
}
}

Object-Oriented Programming: Abstract classes vs. Interfaces

Abstract classes

  • An abstract class is a class which MUST contain one or more abstract methods. All this really means is that a class marked as “abstract” can contain “abstract” methods (methods with no implementation), and/or regular methods which do contain implementation details. The idea behind an abstract class is to provide a basic level of functionality across your class hierachy while also keeping in mind that operations may be performed differently for various classes. An abstract method is useful when we want to force subclasses to provide a custom implementation for it.

Interfaces

  • An interface is just like a class except that it can only contain method signatures and fields. A Java interface cannot contain implementation details of the method, only the signature (name, parameters, exceptions, return type). A class that implements an interface is required to provide implementation details for all methods of the interface. A Java class can implement multiple Java interfaces but remember this requires that the class implement all associated methods. Interfaces are a way of achieving polymorphism, a concept which implies that instances of a class (objects) can be used as if it were of different types.

Main idea: An abstract class can contain unimplemented (abstract) and implemented methods which can be inherited from. An abstract class is extended. An interface simply serves as a contract, and cannot provide any method implementations. An interface is implemented.

Data Structures in Java: Arrays vs. LinkedLists

Arrays —

  • An array is an object that contains a number of similarly typed variables. When we create an array, the JVM creates a contiguous block of memory with spaces in which the address of each member object may be stored. In the case of an array of objects, the heap contains an linear data structure which contains references to the values stored in the array. The array class is a member of the Java Collections Framework.

Big-O Worst-Case Performance

Access: O(1), Search: O(n), Insertion: O(n), Deletion: O(n)

LinkedLists —

  • Similar to an array, a linked list is a linear data structure but elements are not stored in contiguous locations and every element is a separate object with a value and an address. Each object is known as a node, and elements are linked using pointers and addresses. It provides advantages for insertions and deletions because of its’ dynamic nature which allows for updates to the head and tail of the list, while it has a few disadvantages with the major one being that nodes cannot be accessed directly. Access into the list requires that we need to start from one end of the list and traverse each link to reach our target node. A LinkedList is a member of the Java Collections Framework.

Big-O Worst-Case Run-Time Performance

Access: O(n), Search: O(n), Insertion: O(1), Deletion: O(1)

Sets —

Just some things to remember:

  • Does not allow duplicate values
  • Has no guaranteed internal order
  • Interface in the collections package
  • Concrete implementations include: HashSet, TreeSet, LinkedHashSet

Big-O Worst-Case Run-Time Performance

Access: O(1), Search: O(n), Insertion: O(1), Deletion: O(1)

Data Structures in Java: HashMaps

One of the most useful data structures in all of programming is the HashMap. A HashMap implements the Map interface in the JDK and is used to store data in “key/value” pairs, in which one one object, a key (index) is used to access another object (a value). In contrast to an array which stores items in an ordered collection in which items are accessed with an index number, a HashMap allows for indexing via any class type.

Here is everything you need to know about a HashMap.

  • Internally, a HashMap is just an array of single linked lists as elements.
  • A HashMap cannot contain duplicate keys
  • A HashMap can contain a null key and/or null value

Hash Functions and Hash Collisions

When a user calls put(K key, V value) or get(Object key), the function computes the index of the bucket in which the entry should be. All this means is that the input key is passed to a function which computes a hashcode. This hashcode tells us where to place the value or where it is located. A hash collision occurs when a hash function returns the same bucket location for two different keys. Two different keys can be found to have the same hashCode in the case of two unequal objects in Java. HashMap uses the equals() and hashCode() methods to find the bucket location for a given input key. Remember that Java does guarantee that the same hash code will result for equal objects but not vice versa. Prior to Java 8, HashMaps handled collisions by using a linked list to store map entries which end up in the same array location. From Java 8 onwards, balanced trees are used in-place of linked lists when the size of the bucket grows beyond a certain threshold. Due to the tree properties, the worst case performance of retrieval operations improves from O(n) to O(logn).

Step by step:

put(K key, Value v)
hash(key) → hashcode
index = hash & (n-1) → index value

When there is a collision, entries are appended to a linked list or balanced tree.

get(Object key)
hash(key) → hashcode
index = hash & (n-1) → index value

use equals() to compare the hashcode of the given key with the hashcode of the key at the index value. If it matches then return the value, if it doesn’t then traverse the linked list or balanced tree at the index location until there is a match.

Big-O Worst-Case Run-Time Performance

Access: O(1), Search: O(1), Insertion: O(1), Deletion: O(1)

Big-O Runtime Cheat Sheet

New Feature Introduced in Java 8

What is a default method and when should it be used?

  • In Java 8, interfaces support the addition of a “default” identifier on method declarations. Marking a method as default simply means that an implementation (filled in method body). This specifies a piece of code to be executed as the default in a situation where a concrete class fails to provide an implementation for that method.
  • The benefit of using a default methods is backwards compatibility. Default methods introduce a mechanism for adding functionality to an interface without breaking all classes that extend the interface. Think about it, when you add a method to an interface, all subclasses require an implementation for that method. when you add a method to an interface and identify it as default, subclasses CAN override the default implementation to extend the functionality or unconcerned classes can leave the method out and simply use the default behavior.

Why not use an abstract class?

  • Although this update in Java 8 does make it seem as though interfaces and abstract classes are the same… that is not the case. An abstract class can define a constructor. They can be objects with a state associated with them, in contrast to an interface which simply defines a contract. Methods in an abstract class can modify both method arguments as well as fields of their class, whereas default methods in an interface can only access its arguments because interfaces do not have any state. Both are really used for different purposes.

If you’ve made it this far, congrats! In summary the purpose of this post was to highlight some of the more popular (and practical) aspects of the Java programming language.

In this post we —

  • Defined some Java keywords and capabilities related to immutability
  • Compared abstract classes and interfaces
  • Highlighted key data structures including arrays, lists, sets, and maps
  • Touched on the concept of “default” methods introduced in Java 8

Check out the links below to continue learning!

And if you’d like, follow me here! I’ll be continuing to add content related to software engineering concepts. Soon-to-come is an overview of multithreading — a Java feature which allows concurrent execution of a program for maximum utilization of the CPU which is extremely beneficial in increasing performance for resource intensive applications.

--

--