Learn Java in one post (30 minutes) with code examples

In this article, we have covered all important ideas in Java Programming Language and presented each idea along with code examples. You have provided easy to follow implementations of standard Data Structures like Trie and Binary Tree to give you good hold on Implementing ideas.

This is a must read to revise Java in 30 minutes just before an examination or an Interview.

Table of contents:

  1. Hello World in Java
  2. Data types in Java
  3. Variables in Java
  4. OOP in Java
  5. Inheritance in Java
  6. Interface in Java
  7. Take Input in Java
  8. Node for Binary Tree in Java
  9. Binary Tree in Java using above node
  10. Trie class structure in Java
  11. Hash Map structure in Java
  12. Dynamic Array Structure in Java
  13. Linked List class Structure in Java
  14. Data Structures in Java Collections (java.util)
  15. Comparator for custom ordering in Java
  16. Throw vs Throws
  17. String vs StringBuffer vs StringBuilder
  18. Singleton class in Java
  19. Serializable & transient in Java
  20. Multiple threads in Java
  21. Fundamental ideas in Java

Let us get started with mastering Java Programming Language in one go.

Hello World in Java

This is the simpliest code in Java which creates a class named PrintWord and has the main() function which prints a single word that is "opengenus". You must write a simpler structure before writing a complete implementation of a problem.

// This code prints "opengenus".
public class PrintWord {
  public static void main(final String[] args) {
    System.out.println("opengenus");
  }
}

Output:

opengenus

String[] args is an array of command line arguments and these are made final that is the values cannot be changed as this is a good practice and avoid runtime bugs. You can skip this as well and the program will run correctly.

Data types in Java

This code example creates 3 variables of Integer data type (which is a primitive data type in Java). We add the first two variables and store the result in the third variable.

// This code add two variables.
public class PrintWord {
  public static void main(final String[] args) {
    int A = 10;
    int B = 21;
    int C; // Default value of C is 0
    C = A + B;
    System.out.println(C);
  }
}

Output:

31

Following are the primitive data types:

Primitive data type Default value Size
boolean false 1 byte
char '\u0000' 2 byte
byte 0 1 byte
short 0 2 byte
int 0 4 byte
long 0L 8 byte
float 0.0f 4 byte
double 0.0d 8 byte

Note: boolean needs only 1 bit but is assigned 1 byte because 1 byte is the smallest amount of memory that can be allocated.

You can define user-defined data types when we learn how to implement custom class and use them as objects in Java.

Variables in Java

You know how to define and use variables from the previous code examples but there are three types:

  • Static Variable: This is a variable that is common to the entire code. You can define head of a Linked List as a static variable as the head is needed to access it and can be used in all methods.
  • Local variable: These are variables that are defined within a function and hence, can be used within the function only.
  • Instance variable: These are variables that have been defined in main() function.
// This code prints "opengenus".
public class PrintWord {

  // Static variable
  static float check_variable1 = 1.02;
  
  void function_check() {
    // Local variable
    double check_variable2 = 2.931271;
  }
  
  public static void main(final String[] args) {
    // Instance variable
    int check_variable3 = 2;
  }
}

Note: static keyword is used when we want to keep one common copy across all instances of a class and different functions. Note main() is used with static as there is only one main().

Go through this article to understand scope of variables deeper.

OOP in Java

This section is based on Singly Linked List. We create two classes:

  • An internal node
  • A Singly Linked List using the above node.

This sample Java code will give you the complete idea of creating custom class, defining member variables, defining a constructor and creating objects of our class in main().

// Singly Linked List implementation in Java
// iq.opengenus.org

// Class for Internal Node
public class Node {
  	public int item;
  	public Node next;
  }

// Class for Singly Linked List
public class SinglyLinkedList {
    // Two private data members
    private int size;
    private Node head;

    // Constructor of Linked List class
	public SinglyLinkedList() {
		this.size = 0;
		this.head = null;
	}

    // Internal Function to insert Node in SinglyLinkedList
	public void insertNode(int item) {
		Node node = new Node();
		node.item = item;
		Node current = this.head;

		if (this.head == null) {
			this.head = node;
			this.head.next = null;
			this.size = 1;
			System.out.println(this.head.toString());
		} else {

			while (current.next != null) {
				current = current.next;
			}
			current.next = node;
			node.next = null;
			this.size += 1;
		}
	}
}

// Create a main class to use SinglyLinkedList class
public class Test_SinglyLinkedList {

	public static void main(String[] args) {
        // Create an object of 
		SinglyLinkedList list = new SinglyLinkedList();
		list.insertNode("2");
		list.insertNode("9");
    }
}

Go through the above example carefully as it is the fundamental code you will need to implement other ideas and data structures like Binary Tree, Trie and others.

To have a good hold on OOP in Java, go through this article.

Inheritance in Java

In this example, we class B inherits class A. The object of class B will have access to member variables and functions in class A depending on access specifiers.

// A is base class
// B is child class
// B inherets A
Class A
{
   public void methodA()
   {
     System.out.println("Base class method");
   }
}

// B is inheriting A
Class B extends A
{
   public void methodB()
   {
     System.out.println("Child class method");
   }
   public static void main(String args[])
   {
     B obj = new B();
     obj.methodA(); //calling super class method
     obj.methodB(); //calling local method
  }
}

There are different types of Inheritance but not all types are permitted in Java. You can learn about types of Inheritance here.

For example, Multiple Inheritance is not permitted in Java but it can be implemented using Interface. You can learn about this technique here.

Interface in Java

We can inherit only one class but we can inherit multiple interface.

Interface is like a class but it has only function definitions. No function has a real code as Interface only provides the structure that needs to be followed and the actual code is the responsibility of the class using the interface.

This is an example of an Interface:

public interface ContacInfo{
    //Abstract method declarations
    void addAddress(Address address);// Passing object of type Address
    void addEmail(String string);
    void addPhoneNo(long phoneNo);
}

Go through this Student class which uses the above interface:

public class Student implements ContactInfo{
    int rollNumber;
    String name;
    int age;
    Address address;
    String email;
    long phoneNo;
    
    public void addAddress(Address address)
    {
        this.address = address;
    }
    
    public void addEmail(String email){
        this.email = email;
    }
    
    public void addPhoneNo(long phoneNo){
        this.phoneNo = phoneNo;
    }
    
}

To learn more about Interface in Java, go through this article.

Take Input in Java

Go through this Java code to understand how to take input using Scanner.

import java.util.Scanner;

public class Input {
    public static void main(String[] args) {
        // create a scancer object to take user input
        Scanner in = new Scanner(System.in);
        String userInput;
        // nextLine(): complete line till \n
        userInput = in.nextLine();
        // next(): word till whitespace
        userInput = in.next();
        // nextInt(), nextDouble(): for numeric data
        int data = in.nextInt();
        int data = in.nextDouble();
        
        // Check if there is input left
        boolean check = in.hasNextInt();
        boolean check = in.hasNextLine()
    }
}

To get a complete list of Scanner methods, go through this list.

Node for Binary Tree in Java

// Binary Tree Node
// Part of iq.opengenus.org

public class BNode {

    private int key;
    private String name;

    private BNode leftChild;
    private BNode rightChild;

    /**
     * Constructors
     * @param key
     * @param name
     */
    BNode(int key, String name) {
        this.key = key;
        this.name = name;
    }

    /**
     * Getter
     * @return Bnode
     */
    public BNode getLeftChild() { 
        return leftChild; 
    }

    /**
     * Setter for the left child node
     * @param pLeftChild
     */
    public void setLeftChild(BNode pLeftChild) {
        leftChild = pLeftChild;
    }

    /**
     * Setter for the righ child node
     * @param pRightChild
     */
    public void setRightChild(BNode pRightChild) {
        rightChild = pRightChild;
    }

    /**
     * Getter
     * @return BNode
     */
    public BNode getRightChild() { return rightChild; }

    /**
     * Getter
     * @return key
     */
    public int getKey() { 
        return key; 
    }

    /**
     * setter
     * @param data
     */
    public void setKey(int data) { 
        key = data; 
    }

    /**
     * Getter
     * @return name
     */
    public String getName() { 
        return name; 
    }
    
    /**
     *
     * @return
     */
    public String toString() {
        StringBuffer b = new StringBuffer();
        b.append("[Key="); b.append(key);
        if (leftChild == null) {
            b.append(";leftChild=null");
        } else {
            b.append(";leftChild=" + leftChild.getKey());
        }
        if (rightChild== null) {
            b.append(";rightChild=null");
        } else {
            b.append(";rightChild=" + rightChild.getKey());
        }
        b.append("]");
        return b.toString();
    }
}

Binary Tree in Java using above node

// Binary Tree implementation in Java
// Part of iq.opengenus.org
// Go through the comments carefully
// @something are annotations (metadata)
public class BinaryTree {

    // BNode defined in previous section
    // BNode can be included here alternatively
    private BNode root;
    private int numOfNodes = 0;

    /**
     * Constructor
     */
    public BinaryTree() {
        root = null;
    }

    /**
     * increment the number of items in the tree
     */
    public void inc() { 
        numOfNodes++;
    }

    /**
     * decrement the number of items in the tree
     */
    public void dec() { 
        numOfNodes--;
    }

    /** getter
     *
     * @return number of nodes in the tree
     */
    public int getNumOfNodes() { 
        return numOfNodes; 
    }

    public BNode getRoot() { 
        return root;
    }
    
    /**
     * Add node traversing the tree until you find the right place for insertion.
     * @param key
     * @param name
     */
    public void addNode(int key, String name) {
        // Create a new Node and initialize it
        BNode newNode = new BNode(key, name);
        // If there is no root this becomes root
        if (root == null) {
            root = newNode;
            inc();
        } else {
            // Set root as the Node we will start
            // with as we traverse the tree
            BNode focusNode = root;
            // Future parent for our new Node
            BNode parent;
            while (true) {
                // root is the top parent so we start
                // there
                parent = focusNode;
                // Check if the new node should go on
                // the left side of the parent node
                if (key < focusNode.getKey()) {
                    // Switch focus to the left child
                    focusNode = focusNode.getLeftChild();
                    // If the left child has no children
                    if (focusNode == null) {
                        // then place the new node on the left of it
                        parent.setLeftChild(newNode);
                        inc();
                        return; // All Done
                    }
                } else { // If we get here put the node on the right
                    focusNode = focusNode.getRightChild();
                    // If the right child has no children
                    if (focusNode == null) {
                        // then place the new node on the right of it
                        parent.setRightChild(newNode);
                        inc();
                        return; // All Done
                    }
                }
            }
        }
    }

    /**
     * Traverse in the inorder. This is the sorted order.
     * 1. Go down the left's nodes' children recursively until no children
     * 2. Print Node's key
     * 3. Go down the right nodes' children recursively until no children
     * Recursion is used to go to one node and
     * then go to its child nodes and so forth
     * @param focusNode, the root node sent to it
     */
    public void inOrderTraverseTree(BNode focusNode) {
        if (focusNode != null) {
            // Traverse the left node's children recursively
            inOrderTraverseTree(focusNode.getLeftChild());
            // Visit the currently focused on node
            System.out.println(focusNode);
            // Traverse the right node
            inOrderTraverseTree(focusNode.getRightChild());
        }
    }

    /**
     * Travere in the preorder
     * @param focusNode
     */
    public void preorderTraverseTree(BNode focusNode) {
        if (focusNode != null) {
            System.out.println(focusNode);
            preorderTraverseTree(focusNode.getLeftChild());
            preorderTraverseTree(focusNode.getRightChild());
        }
    }

    /**
     * traverse postorder
     * @param focusNode
     * @param focusNode
     */
    public void postOrderTraverseTree(BNode focusNode) {
        if (focusNode != null) {
            postOrderTraverseTree(focusNode.getLeftChild());
            postOrderTraverseTree(focusNode.getRightChild());
            System.out.println(focusNode);
        }
    }

    /**
    * Find height
     * @param root
     * @return
     */
    public int findHeight(BNode root) {
        if (root == null) {
            return -1;
        } else
            return Math.max(findHeight(root.getLeftChild()), findHeight(root.getRightChild())) + 1;
    }

    /**
     * Find Mininum value in the tree
     * @param root
     * @return value or -1 if tree is empty
     */
    public int findMinValue(BNode root) {
        if (root == null) {
            return -1;
        } else if (root.getLeftChild() == null) {
            return root.getKey();
        } else {
            return findMinValue(root.getLeftChild());
        }
    }

    /**
     * Find the Maximum value in the tree
     * @param root
     * @return
     */
    public int findMaxValue(BNode root) {
        if (root == null) {
            return -1;
        } else if (root.getRightChild() == null) {
            return root.getKey();
        } else {
            return findMaxValue(root.getRightChild());
        }
    }

    /**
     * Find if the key exists in the tree
     * @param key
     * @return return Bnode if found otherwise null
     */
    public BNode findNode(int key) {
        // Start at the top of the tree
        BNode focusNode = root;
        // check if empty tree
        if (root == null) {
            return null;
        }
        // If Node is not found
        // keep looking
        while ( focusNode.getKey() != key)  {
            // If we should search to the left
            if (key < focusNode.getKey()) {
                // Shift the focus Node to the left child
                focusNode = focusNode.getLeftChild();
            } else {
                // Shift the focus Node to the right child
                focusNode = focusNode.getRightChild();
            }
            // node not found
            if (focusNode == null)
                return null;
        }
        return focusNode;
    }

    /**
     * Delete a key
     * @param key
     * @return true if deleted, false otherwise
     */
    public boolean remove(int key) {
        // Start at the top of the tree
        BNode focusNode = root;
        BNode parent = root;

        // special case if the tree is empty
        if (focusNode == null) {
            return false;
        }
        // When searching for a Node this will
        // tell us whether to search to the
        // right or left
        boolean isItALeftChild = true;
        // While we haven't found the Node
        // keep looking
        while (focusNode.getKey() != key) {
            parent = focusNode;
            // If we should search to the left
            if (key < focusNode.getKey()) {
                isItALeftChild = true;
                // Shift the focus Node to the left child
                focusNode = focusNode.getLeftChild();
            } else {
                // Greater than focus node so go to the right
                isItALeftChild = false;
                // Shift the focus Node to the right child
                focusNode = focusNode.getRightChild();
            }
            // The node wasn't found
            if (focusNode == null)
                return false;
        }
        // If Node doesn't have children delete it
        if (focusNode.getLeftChild() == null && focusNode.getRightChild() == null) {
            // If root delete it
            if (focusNode == root)
                root = null;
                // If it was marked as a left child
                // of the parent delete it in its parent
            else if (isItALeftChild)
                parent.setLeftChild(null);
                // Vice versa for the right child
            else
                parent.setRightChild(null);
        }
        // If no right child
        else if (focusNode.getRightChild() == null) {
            if (focusNode == root)
                root = focusNode.getLeftChild();
                // If focus Node was on the left of parent
                // move the focus Nodes left child up to the
                // parent node
            else if (isItALeftChild)
                parent.setLeftChild(focusNode.getLeftChild());
                // Vice versa for the right child
            else
                parent.setRightChild(focusNode.getLeftChild());
        }
        // If no left child
        else if (focusNode.getLeftChild() == null) {
            if (focusNode == root)
                root = focusNode.getRightChild();
                // If focus Node was on the left of parent
                // move the focus Nodes right child up to the
                // parent node
            else if (isItALeftChild)
                parent.setLeftChild(focusNode.getRightChild());
                // Vice versa for the left child
            else
                parent.setRightChild(focusNode.getRightChild());

        }
        // Two children so I need to find the targeted deleted nodes'
        // replacement
        else {
            BNode replacement = getReplacementNode(focusNode);
            // If the focusNode is root replace root
            // with the replacement
            if (focusNode == root)
                root = replacement;
                // If the deleted node was a left child
                // make the replacement the left child
            else if (isItALeftChild)
                parent.setLeftChild(replacement);
                // Vice versa if it was a right child
            else
                parent.setRightChild(replacement);
            replacement.setLeftChild(focusNode.getLeftChild());
        }
        dec();
        return true;
    }

    /**
     * Find the replacement node. Used in delete function.
     * @param replacedNode
     * @return BNode or null;
     */
    public BNode getReplacementNode(BNode replacedNode) {
        BNode replacementParent = replacedNode;
        BNode replacement = replacedNode;

        BNode focusNode = replacedNode.getRightChild();
        // While there are no more left children
        while (focusNode != null) {
            replacementParent = replacement;
            replacement = focusNode;
            focusNode = focusNode.getLeftChild();
        }
        // If the replacement isn't the right child
        // move the replacement into the parent's
        // leftChild slot and move the replaced nodes
        // right child into the replacements rightChild
        if (replacement != replacedNode.getRightChild()) {
            replacementParent.setLeftChild(replacement.getRightChild());
            replacement.setRightChild(replacedNode.getRightChild());
        }
        return replacement;
    }


    /**
     * Given root of the tree or subtree, find its minimum.
     * @param root
     * @return
     */
    public BNode findMin(BNode root)
    {
        BNode focusNode = root;
        while (focusNode.getLeftChild() != null) {
            focusNode = focusNode.getLeftChild();
        }
        return focusNode;
    }

    /**
     * Recursive version of the deletion.
     * @param root
     * @param data
     * @return the deleted node
     */
    public BNode delete(BNode root, int data) {
        if (root == null)  {
            return root;
        } else if(data < root.getKey()) {
            // traverse recursively the left side
            root.setLeftChild(delete(root.getLeftChild(), data));
            // travers recursively the right side
        } else if (data > root.getKey())  {
            root.setRightChild(delete(root.getRightChild(), data));
        } else {
            // Wohoo... I found you, Get ready to be deleted
            // check the three cases
            // Case 1:  No child, the easy case
            if(root.getLeftChild() == null && root.getRightChild() == null) {
                root = null;
            }
            //Case 2: One child
            else if(root.getLeftChild() == null) {
                BNode temp = root;
                root = root.getRightChild();
            } else if(root.getRightChild() == null) {
                BNode temp = root;
                root = root.getLeftChild();
            }
            // case 3: 2 children
            else {
                BNode temp = findMin(root.getRightChild());
                root.setKey(temp.getKey());
                root.setRightChild(delete(root.getRightChild(), temp.getKey()));
            }
        }
        return root;
    }

    /**
     * Check if this tree an a binary tree
     * @param root
     * @return true or false
     */
    boolean isBinarySearchTree(BNode root) {
        int rootKey = root.getKey();
        if (root == null) {
            return true;
        } else if (findMinValue(root.getLeftChild()) < rootKey && findMaxValue(root.getRightChild()) > rootKey) {
            return true & isBinarySearchTree(root.getLeftChild()) & isBinarySearchTree(root.getRightChild());
        } else return false;
    }

    /**
     * Driver for the program
     * @param args
     */
    public static void main(String[] args) {

        BinaryTree bTTree = new BinaryTree();
        bTTree.addNode(4, Integer.toString(4));
    }
}

Defining the class definition is important for various data structures. Once you have the correct definition, you can easily implement the functions you need.

Trie class structure in Java

Class structure of Trie in Java:

public class Trie {

    private class TrieNode {
        Map<Character, TrieNode> children;
        boolean endOfWord;

        public TrieNode() {
            children = new HashMap<>();
            endOfWord = false;
        }
    }

    private final TrieNode root;
    
    public Trie() {
        root = new TrieNode();
    }
    
    // Sample insert method
    public void insert(String word) {
        TrieNode current = root;
        for (int i = 0; i < word.length(); i++) {
            char ch = word.charAt(i);
            TrieNode node = current.children.get(ch);
            if (node == null) {
                node = new TrieNode();
                current.children.put(ch, node);
            }
            current = node;
        }
        //mark the current nodes endOfWord as true
        current.endOfWord = true;
    }
}

Hash Map structure in Java

Class structure of Hash Map in Java:

package java.util;
import java.io.*;

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
{
	static final int DEFAULT_INITIAL_CAPACITY = 16;
	static final int MAXIMUM_CAPACITY = 1 << 30;
	static final float DEFAULT_LOAD_FACTOR = 0.75f;

	transient Entry[] table;
	transient int size;
	int threshold;
	final float loadFactor;

	public HashMap() {
	    this.loadFactor = DEFAULT_LOAD_FACTOR;
	    threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
	    table = new Entry[DEFAULT_INITIAL_CAPACITY];
	    init();
	}
}

Dynamic Array Structure in Java

Class structure of Dynamic Array in Java:

public class DynamicArray<E> implements Iterable<E>, RandomAccess {

    private static final int INITIAL_CAPACITY = 10;
    private int size;
    private int capacity = INITIAL_CAPACITY;

    private Object[] array;

    public DynamicArray() {
        array = new Object[INITIAL_CAPACITY];
    }
}

Linked List class Structure in Java

Class structure of Linked List in Java:

public class LinkedList {
    
    private class Node
	{
		private Node next;
		private Object data;
		
		public Node(Object dat)
		{
			data = dat;
		}
		
		public Object getData()
		{
			return data;
		}
	}

    private int size;
	private Node head;

	public LinkedList() {
		this.size = 0;
		this.head = null;
	}
}

Data Structures in Java Collections (java.util)

  • HashMap
// Hash Map usage in Java
// Part of iq.opengenus.org
// Inherits AbstractMap; not syncronized; allow null key
// Interface: Map, Cloneable, Serializable
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Iterator;
public class Test {
    public static void main(String[] args) {
        
        // initialize a HashMap
        Map<String, Integer> map = new HashMap<String, Integer>();

        // To make HashMap thread safe
        Map<String, Integer> map = Collections.synchronizedMap(
        					new HashMap<String, Integer>());
 
        // Add elements using put method
        map.put("opengenus", 1);
 		// Remove using key
 		map.remove("opengenus");
 		map.containsKey("opengenus");
 		map.containsValue("1");
 		// get with key
 		map.get("opengenus");
 		map.size();

        // Iterate the map using for-each loop
        for (Map.Entry<String, Integer> e : map.entrySet()) {
            System.out.println("Key: " + e.getKey() + " Value: " + e.getValue());
        }

        // Get a set of the entries
        Set set = map.entrySet();
        // Get an iterator
        Iterator i = set.iterator();
        // Display elements
        while(i.hasNext()) {
           Map.Entry me = (Map.Entry)i.next();
           System.out.print(me.getKey() + ": ");
           System.out.println(me.getValue());
        }
    }
}

Similar to HashMap

// LinkedHashMap
// Maintains order of iteration
LinkedHashMap<Integer, String> lhmap = 
                 new LinkedHashMap<Integer, String>();

// HashTree
// Does not allow null tree
// only homogeneous keys; supports sorting
TreeMap<Integer, String> tmap =
                 new TreeMap<Integer, String>();

// Syncronized; slow; legacy; inherits dictionary
Hashtable<Integer,String> hm = new Hashtable<Integer,String>();  
  • ArrayList
// ArrayList usage in Java
// Part of iq.opengenus.org
// Iterable -> Collection -> List -> 
// AbstractList (Interface) -> ArrayList
// No duplicate, maintains order, not synchronized
import java.util.*;  
public class Test {  
	public static void main(String args[]) {  
		ArrayList<String> list = new ArrayList<String>(); 
		// Add
		list.add("opengenus");
		// Remove
		list.remove("element");
		// Get i-th element
		list.get(i-1);

		//Printing the arraylist object   
		System.out.println(list);
		// Sort
		Collections.sort(list);

		// Traverse
		Iterator itr = list.iterator();
		while(itr.hasNext()) {
			System.out.println(itr.next());
		}
	}
}
  • LinkedList
// ArrayList usage in Java
// Part of iq.opengenus.org
// Iterable -> Collection -> Queue -> Deque(interface) -> LinkedList
// Iterable -> Collection -> List(interface) -> AbstractSequentialL) -> LL
import java.util.*;  
public class Test {  
	public static void main(String args[]) {
		LinkedList<String> ll = new LinkedList<String>();  
		ll.add("opengenus");
		ll.addFirst("open");
		ll.addLast("opengenus");
		ll.remove("open");

		Iterator<String> itr = ll.iterator();  
		while(itr.hasNext()) {  
			System.out.println(itr.next());  
		}
	}
}
  • Stack
// Stack usage in Java
// Part of iq.opengenus.org
// Iterable -> Collection -> List ---> Vector -> Stack
import java.util.Iterator;  
import java.util.Stack;  
public class StackIterationExample1   
{     
	public static void main (String[] args)   
	{   
		//creating an object of Stack class  
		Stack<String> stk = new Stack<String>(); 

		//pushing elements into stack  
		stk.push("opengenus");
		stk.pop();
		stk.peek();
		stk.search("opengenus");
		stk.empty();

		//iteration over the stack  
		Iterator iterator = stk.iterator();  
		while(iterator.hasNext())  
		{  
			Object values = iterator.next();  
			System.out.println(values);   
		}     
	}  
}
  • Stack
// Queue usage in Java
// Part of iq.opengenus.org
// Iterable -> Collection -> List ---> Vector -> Stack
import java.util.Iterator;  
import java.util.Queue;  
public class Test {  
public static void main(String[] args) {
    Queue<String> queue=new PriorityQueue<String>();  
    
    // add
    queue.add("opengenus"); 
    // remove
    queue.remove();
    // remove; null if empty
    queue.poll();
    // get
    queue.peek();

    //Traversing queue elements  
    for(String str: queue) {  
    	System.out.println(str);  
    }
}  
}

Types:

  • PriorityQueue (add Comparable operator)
  • BlockingQueue
  • LinkedBlockingQueue
  • ArrayBlockingQueue
  • PriorityBlockingQueue
  • DelayQueue
  • SynchronousQueue

Comparator for custom ordering in Java

Priority Queue + Comparable Interface:

PriorityQueue<Student> pq = new 
             PriorityQueue<Student>(5, new StudentComparator());


class Student implements Comparator {
	// For a custom class
	public int compare(Student a, Student b)
	{
	    return a.id - b.id;
	}
}

// -1, 0, 1
class StudentComparator implements Comparator<Student> {
              
    // Overriding compare() of Comparator 
    public int compare(Student s1, Student s2) {
        if (s1.score < s2.score)
            return 1;
        else if (s1.score > s2.score)
            return -1;
        return 0;
        }
}

Runnable is an interface that is to be implemented by a class whose objects should be executed by a thread.

class Consumer implements Runnable {
   private final BlockingQueue queue;
}
  • Deque

Implementation is similar to previous examples.

Types include:

  • Deque
  • ArrayDeque
  • ConcurrentLinkedDeque
  • LinkedBlockingDeque
  • LinkedList

With this, you have the knowledge of different Java Collection data structure available. You can use these in your code quickly and are present in java.util package.

Throw vs Throws

  • Throw is used within a function to raise an error
  • Throws is used in function definition to suggest it may throw an error
public static void checkNum(int num) {  
	if (num < 1) {  
	    throw new ArithmeticException("\nNumber is negative, cannot calculate square");  
}  

public static int divideNum(int m, int n) throws ArithmeticException {  
    int div = m / n;  
    return div;  
}  

String vs StringBuffer vs StringBuilder

This code summarizes the main points:

String str = new String("open");
// str is modified and new object is created
// more memory and time
str = str + "genus";

// StringBuilder: not threadsafe or synchronized
// but does not create a new object
StringBuilder str = new StringBuilder("open");
// StringBuffer: threadsafe and synchronized
// does not create a new object
StringBuffer str = new StringBuffer("Geeks");

s2.append("genus");

Singleton class in Java

Singleton class is a class whose object can be created only once and hence, only one object is used.

// Singleton class in Java
// Part of iq.opengenus.org
public final class ClassSingleton {
    private static ClassSingleton INSTANCE;
    private String info = "Initial info class";
    
    private ClassSingleton() {
    }
    
    public synchronized static ClassSingleton getInstance() {
        if(INSTANCE == null) {
            INSTANCE = new ClassSingleton();
        }
        return INSTANCE;
    }
}

Serializable & transient in Java

transient keyword is used to avoid converting an object to a stream of bytes during serialization.

public class Student implements Serializable {
    String name;
    transient int age;
}

To save the object, simply write it to a file:

Student student1 =new Student("OpenGenus");

FileOutputStream f = new FileOutputStream("sample.txt");    
ObjectOutputStream out = new ObjectOutputStream(f);    
out.writeObject(student1);
out.flush();
out.close();
f.close();

Multiple threads in Java

A class can support threads by extending the Thread class or by implementing the Runnable interface. In both cases, we need to override the run() method which is executed by the threads.

Following is a simple example with Runnable interface:

class Test implements Runnable {
    public void run()
    {
    	// add some code
    }
}

class Test2 {
    public static void main(String[] args)
    {
    	// Number of threads
        int n = 8;
        for (int i = 0; i < n; i++) {
            Thread object = new Thread(new Test());
            object.start();
        }
        // Threads run in parallel
    }
}

A test case should check if the code runs correctly using multiple threads.

Thread uses shared address space but different program count and stack memory. Process uses different address space.

Fundamental ideas in Java

To run a code file named "code.java", use the following commands:

javac code.java
java code

Garbage Collection in Java:

  • Memory is divided into two major parts: Young (Eden, Survivor 1, Survivor 2) and Old generation
  • There are different garbage collection algorithms (like Serial Garbage Collection, Parallel GC, New GC, Concurrent Mark and Sweep) used in Java for different use case. Learn More.
  • There are techniques like Mark-copy and Mark-sweep-compact.
  • GC is done is 3 phases: Minor, Major and Full.

We can call GC explicitly in code:

// Explicit call of the Garbage Collector
System.gc();

To exit the code, use:

System.exit(1);

Memory management in Java:

  • A new variable is allocated space in Eden space.
  • memory defrag process is used to avoid issues due to Fragmentation.
  • Learn more about Memory model in Java
  • class structure (runtime constants and static variables) and code for methods and constructors are stored in Permanent Generation Memory Space.
  • Memory pools are created for immutable objects like String pools.
  • Runtime constant pool (for run-time constants) is the part of the method area.
  • Stack memory is used for execution of a thread.

The reference to an object determines when the object will be removed in Garbage Collection. Types of references in Java:

// Creates a strong reference
Object obj = new Object();

// New Soft Reference to obj object
import java.lang.ref.*
SofReference<Object> softRef = new SoftReference<Object>(obj);

// New Weak Reference to obj object
WeakReference<Object> softRef = new WeakReference<Object>(obj);
// Returns the object reference if it remains in memory, 
// otherwise returns null
obj = softRef.get();

// Creating a reference queue
ReferenceQueue<obj> refQueue = new ReferenceQueue<obj>();
// New Phantom Reference to obj object in a queue
PhantomReference<Object> phantomRef = new PhantomReference<Object>(obj, refQueue);

With this, you have a strong idea of implementing any Data Structure in Java along with core design ideas. Best of luck with your Test or Coding Interview.