Class Object

java.lang.Object

Every class in java extends by default from the Object class.

The class Object is the ghost super class of all classes. 

Built-in classes, including arrays, override methods of the Object class.

  • hashCode()
  • toString()
  • equals(Object o)
  • clone()

The remaining methods are related to multi-threading. 

hashCode()

Returns an value based on a hashing algorithm. 

It is used to compare two objects for equality. It is also used for storing keys in a hash table.

toString()

Calling toString() on an object returns its identity by default: the hash code of the object in hexadecimal. 

The class may override this method to return a meaningful text to its caller.

equals(Object o)

The equality of two objects is based on their hash codes.

If the hash code of the comparing object is the same as that of the argument object, then the method returns true; false otherwise.

clone()

Returns a copy of an object that implements the Cloneable interface.

A cloned copy is used whenever it is required to keep the original object unchanged.

An array also supports cloning. However, the cloning produces a shallow copy; that is, the elements are merely copied and they themselves are not cloned.

// Box.java

public class Box
{
    private double length;
    private double breadth;
    private double height;

    public Box(double length, double breadth, double height)
    {
        this.length = length;
        this.breadth = breadth;
        this.height = height;
    }

    public void setLength(double length)
    {
        this.length = length;
    }

    public double getLength()
    {
        return length;
    }

    public void setBreadth(double breadth)
    {
        this.breadth = breadth;
    }

    public double getBreadth()
    {
        return breadth;
    }

    public void setHeight(double height)
    {
        this.height = height;
    }

    public double getHeight()
    {
        return height;
    }

    private double volume() {
        return length * breadth * height;
    }

    @Override
    public boolean equals(Object obj)
    {
        Box carton = (Box)obj; // cast Object type to Box type
        return this.volume() == carton.volume();
    }

    @Override
    public String toString()
    {
        return "dimensions: "+ length + "x" +breadth + "x" +height;
    }

    public static void main(String[] args) {
        Box trunk = new Box(20,30,40);
        Box box = new Box(10,30,80);

        if(trunk.equals(box)) {
            System.out.println("the 2 boxes have equal volumes");
        }
        else {
            System.out.println("the 2 boxes have unequal volumes");
        }
        System.out.println("trunk "+trunk);
        System.out.println("box "+box);
    }
}

Thread, Runnable

java.lang.Thread implements Runnable

A thread is an independently executing block of code within a program.

For example, an IDE like Eclipse or Visual Studio allows user to edit code, even as it is compiling the code in the background and showing error notifications at the same time.

A thread takes control of execution. When interrupted, it gives up control to the calling thread. When resumed, it regains control.

Multiple threads can run concurrently, and may or may not share data.

Programs achieve multitasking capability from multi-threading. 

A Thread can have a name, priority and created as a daemon thread.

A program in Java runs inside the main thread. The Java Virtual Machine (JVM) creates the main thread as soon as the program is launched. Other user-defined threads may be spawned by the program as requured. 

To create a thread, a class must extend the Thread class.  The task to be performed by the thread is enclosed in the run method. 

If a class already extends another class, then, in order to become a thread, that class must implement the Runnable interface and provide implementation for the run method.

A thread has a life cycle: its state changes in a definite way.

A complete example

After creation, a thread is launched by calling the start() method.


//Fibonacci.java
package threads;

public class Fibonacci extends Thread
{
	private int number;
	public Fibonacci(int number) {
		this.number = number;
	}
	

	@Override
	public void run()
	{
		printSeries();
		System.out.println();
	}
	
	private void printSeries() {
		int prev1=0,prev2 = 1;
		int sum = prev1+prev2;
		
		System.out.print(prev1+"\t"+prev2+"\t"+sum);
		for(int i=0; i<number; i++) {
			prev1 = prev2;
			prev2 = sum;
			sum = prev1+prev2;
			System.out.print("\t"+sum);
		}
	}
}

// FibonacciRecursive.java
package threads;

public class FibonacciRecursive implements Runnable
{
	private int number;
	private int prev1=0,prev2=1,sum=0;
	public FibonacciRecursive(int number) {
		this.number = number;
	}

	@Override
	public void run()
	{
		sum = prev1+prev2;
		System.out.print(prev1+"\t"+prev2);
		fibber();
		System.out.println();
	}
	
	private void fibber() {
		sum = prev1+prev2;
		System.out.print("\t"+sum);	
		while(number-- > 0) {
			prev1 = prev2;
			prev2 = sum;
			fibber();
		}
	}
}

// SeriesPrinter.java
package threads;

public class SeriesPrinter
{
	public static void runSeries(Thread t) {
		long start = System.nanoTime();
		t.start();
		long end = System.nanoTime();
		System.out.println(t.getName()+"->"+ (end-start)+"ns");
	}
	public static void main(String[] args) {
		Thread fibonacci = new Fibonacci(10);
		fibonacci.setName("loop");
		runSeries(fibonacci);
		
		System.out.println("............");
		Thread fibber = new Thread(new FibonacciRecursive(10));
		fibber.setName("recursion");
		runSeries(fibber);
		
	}
}

As exercise, set thread priority, and re-run the above example. Observe the output. 

A thread may be interrupted to allow another thread to execute its task. 

More on threads in another post.

Errors & Exceptions

java.lang.Exception

Programs react to user input in different ways, sometimes with expected outcomes and at other times with unexpected behaviour leading to abrupt termination of the program.

An error or an exception arises when the program is unable to process the data received and behaves in unexpected ways.

Throwable is the top level class of Exception and Error classes. All exceptions are derived from the Exception class.

Error

For example, when a section of the code goes into an unterminated recursion, the memory allocated for the program will soon be exhausted. The program eventually terminates with a stack overflow error. It can also occur due to cyclic relationship between classes.

Exception

When the program tries to access an array element that does not exist, or computes a division by zero, an exceptional condition arises. The program cannot continue forward and therefore terminates abruptly.

Java provides an API for handling exceptional cases. The exception handling mechanism is object-oriented and ensures a uniform way to handle exceptions. Methods may be declared to throw exceptions under certain conditions. Exception handling allows a program to recover from an exception event and continue without abrupt termination. 

Types of exceptions

There are two kinds of exceptions:

  • checked exceptions

  • unchecked exceptions

The compiler forces the developer to handle checked exceptions. Unchecked exceptions are encountered only at runtime.

Exception handling

  1. Create an exception class.

  2. Declare a method to throw exception

  3. Throw that exception under a certain condition

  4. In the method call, try to execute the method inside a try-catch block.

  5. Handle the exception in the catch block

  6. A finally block is provided at the end to release any system resources used by the method, such as open files or database connections. The finally block executes whether there has been an exception of not. The finally block is optional. The try-with-resources block ensures automatic release of resources.

A few unchecked or runtime exceptions are:

  • NullPointerException, occurs when an object reference is used before it is initialised

  • ArrayIndexOutOfBoundsException, occurs when an invalid index is used to access an array element

  • DivideByZeroException, occurs when the denominator in a division is zero

A few checked exceptions include

  • FileNotFoundException, occurs when the program is found to access a non-existent file

  • ClassNotFoundException, occurs when the runtime tries to load a non-existent class

An example of user defined exception

A user defined exception is derived from the Exception base class.

// Authenticator.java
package auth;

public class Authenticator
{
	public static void main(String[] args) {
		Login logon = new Login("anand", "jinx8Sphinx");
		logon.isValid();
	}
}

// Login.java
package auth;

public class Login
{
	private String username;
	private String password;

	public Login(String username, String password)
	{
		this.username = username;
		this.password = password;
	}

	public void setUsername(String username)
	{
		this.username = username;
	}

	public String getUsername()
	{
		return username;
	}

	public void setPassword(String password)
	{
		this.password = password;
	}

	public String getPassword()
	{
		return password;
	}
	
	public boolean isValid() {
		boolean logon = false;
		try {
			PasswordValidator pwd = new PasswordValidator(password);
			logon = pwd.validate();
			System.out.println("your password is valid");
		} catch(PasswordException pex) {
			System.out.println(pex.getMessage());
		}
		return logon;
	}
	
	public boolean verify() {
		return false;
	}
}

// PasswordValidator.java
package auth;

public class PasswordValidator
{
	private String password;

	public PasswordValidator(String password)
	{
		this.password = password;
	}

	public void setPassword(String password)
	{
		this.password = password;
	}

	public String getPassword()
	{
		return password;
	}
	
	public boolean validate() throws PasswordException {
		boolean verified = new PasswordRules().checkRules();
		if(!verified) {
			throw new PasswordException("Invalid password");
		}
		return false;
	}
	
	class PasswordRules {
		private static final int MINIMUM = 8;
		private boolean hasUpper;
		private boolean hasDigit;
		private boolean hasMinimum;
		
		boolean checkRules() {
			hasMinimum = password.length() >= MINIMUM;
			hasDigit = check(true);
			hasUpper = check(false);
			return hasMinimum && hasDigit && hasUpper;
		}
		
		boolean check(boolean checkForDigit) {
			for(char c : password.toCharArray()) {
				if(checkForDigit) {
					if(Character.isDigit(c)) {
						 return true;
					}
				} else {
					if(Character.isUpperCase(c)) {
						return true;
					}
				}
			}
			return false;
		}
	}
	
}

//PasswordException.java
package auth;

public class PasswordException extends Exception
{
	public PasswordException(String message) {
		super(message);
	}
}

As exercise, the verify method may be implemented for authentication.

Interface

An interface, like a class, specifies a type.

Unlike a class, however, an interface cannot be instantiated. Again, unlike a class, an interface can extend several interfaces.

An interface may consist of abstract methods, static methods, default methods or one or more constants.

Note: All methods are by default public and abstract. Constants are by default public, static and final. The interace itself can be declared public for access across all packages, or the public keyword may be omitted to make it package-private.

public interface UniversalTVRemote {
	boolean toggleOnOff();
	void volumeUp();
	void channelNext();
}

The body of an interface is left out: the class that implements this interface will provide the functionality.

An interface, therefore, exposes an object’s behaviour, like the specification of a contract.

public class SamsungTV implements UniversalTVRemote {
	private int volume, channel;
	private static final int volumeLimit = 5;
	private static final int channelLimit = 200;
	boolean toggleOnOff() {
		// code to implement power on/off
	}
	void volumeUp() {
		if(volume < volumeLimit)
			volume++;
	}
	void channelNext() {
		if(channel < channelLimit)
			channel++;
	}
}

public class SonyTV implements UniversalTVRemote {
	private int sound, surf;
	private static final int VOLUME = 10;
	private static final int CHANNEL = 300;
	boolean toggleOnOff() {
		// code to implement
	}
	void volumeUp() {
		if(sound < VOLUME)
			sound++;
	}
	void channelNext() {
		if(surf < CHANNEL)
			surf++;
	}
}

A class that implements the interface provides its own implementation, while adhering to the published behaviour.

A popular software adage prescribes: code to the interface, not to the implementation.

An interface helps build a common behaviour across multiple implementations. Application Programming Interfaces (APIs) are usually built for propreitary systems using interfaces.

Interfaces do not belong to a class hierarchy. Interfaces help decouple dependent classes in class compositions.

A complete example

// Exam.java
package student;

public class Exam
{
	private String subject;

	public void setSubject(String subject)
	{
		this.subject = subject;
	}

	public String getSubject()
	{
		return subject;
	}
}

// Student.java
package student;

public class Student
{
	private String name;

	public void setName(String name)
	{
		this.name = name;
	}

	public String getName()
	{
		return name;
	}
	
	public void takesExam(Exam test) {
		System.out.println(name +" attended "+test.getSubject()+" exam");
	}
}

// Attendance.java
package student;

public class Attendance
{
	public static final int COUNT = 100;
	private int present=0;
	MarkAttendance[] attendants = new MarkAttendance[COUNT];
	
	public void mark(Exam subject, Student student) {
		MarkAttendance mark = new MarkAttendance(subject, student);
		attendants[present++] = mark;
	}
	
	public void showAttendance() {
		for(MarkAttendance mark : attendants) {
			if(mark != null) {
				System.out.println(mark);
			}
		}
	}
	
	class MarkAttendance {
		Exam subject;
		Student student;
		
		MarkAttendance(Exam subject, Student student) {
			this.subject = subject;
			this.student = student;
		}

		@Override
		public String toString()
		{
			return student.getName()+" takes "+ subject.getSubject();
		}
		
	}
}

// ExamBoard.java
package student;

public class ExamBoard
{
	private Attendance present;
	
	public ExamBoard(Attendance present) {
		this.present = present;
	}
	
	public void exam(Exam subject, Student student) {
		student.takesExam(subject);
		present.mark(subject, student);
	}
	
	public void listAttendees() {
		present.showAttendance();
	}
}

// ExamVenue.java
package student;

public class ExamVenue
{
	public static void main(String[] args) {
		Exam physics = new Exam();
		physics.setSubject("Physics");
		
		Exam chem = new Exam();
		chem.setSubject("Chemistry");
		
		Student atul = new Student();
		atul.setName("Atul Sharma");
		
		Student asha = new Student();
		asha.setName("Asha Parekh");
		
		Attendance marker = new Attendance();
		ExamBoard board = new ExamBoard(marker);
		board.exam(physics,atul);
		board.exam(chem,asha);
		board.exam(chem,atul);
		System.out.println("--------------");
		board.listAttendees();
	}
	
}

This example presents some interesting features.

  • The Attendance class maintains an inner class to keep track of students and the exams they take.
  • The ExamBoard holds an instance of Attendance via its constructor. The two classes are tightly coupled through direct association (composition)
  • The Exam and the Student classes are used by the classes described above.
  • The ExamVenue brings it all together.

The code below uses interface to decouple the Attendance from the ExamBoard class. Following the good old principle to code to the interface and not to implementation, the NewExamBoard class now holds a reference to the interface AttendanceMarker. The implementation class AttendanceKeeper is supplied at runtime.


This allows any class that implements the interface to be passed at runtime. The old Attendance, if it implements the AttendanceMarker interface, may also be passed instead.

This polymorphic behaviour (that is, using any subtype of interface) helps to swap dependent classes with superior implementations. The AttendanceKeeper uses a Map from the java.util package to mark attendance.

Note: The util package will be covered in subsequent posts.

Here’s the modified code. Only the new classes are shown.

// AttendanceMarker.java
package student;

public interface AttendanceMarker
{
	void mark(Exam subject, Student student);
	void showAttendance();
}

// AttendanceKeeper.java
package student;
import java.util.*;

public class AttendanceKeeper implements AttendanceMarker
{
	private Map<Student,Exam> attendants = new HashMap<>();
	
	@Override 
	public void mark(Exam subject, Student student) {
		if(!attendants.containsKey(student)) {
			attendants.put(student,subject);
		} else {
			Exam topic = attendants.get(student);
			topic.setSubject(topic.getSubject()+","+subject.getSubject());
			attendants.put(student,topic);
		}
	}
	
	@Override
	public void showAttendance() {
		Set<Student> keys = attendants.keySet();
		Iterator<Student> loop = keys.iterator();
		while(loop.hasNext()) {
			Student student = loop.next();
			Exam subject = attendants.get(student);
			System.out.println(student.getName()+" takes "+subject.getSubject());
		}
	}
}

// NewExamBoard.java
package student;

public class NewExamBoard
{
	private AttendanceMarker present;

	public NewExamBoard(AttendanceMarker present) {
		this.present = present;
	}

	public void exam(Exam subject, Student student) {
		student.takesExam(subject);
		present.mark(subject, student);
	}

	public void listAttendees() {
		present.showAttendance();
	}
}

// NewExamVenue.java
package student;

public class NewExamVenue
{
	public static void main(String[] args) {
		Exam physics = new Exam();
		physics.setSubject("Physics");

		Exam chem = new Exam();
		chem.setSubject("Chemistry");

		Student atul = new Student();
		atul.setName("Atul Sharma");

		Student asha = new Student();
		asha.setName("Asha Parekh");

		AttendanceMarker marker = new AttendanceKeeper();
		NewExamBoard board = new NewExamBoard(marker);
		board.exam(physics,atul);
		board.exam(chem,asha);
		board.exam(chem,atul);
		System.out.println("--------------");
		board.listAttendees();
	}
}

Class Abstract

Abstraction is a powerful feature in object-oriented programming. It provides a conceptual modelling of a generic thing or idea.

Generic things like shape, animal, or a bank account may be modelled as an abstract class, because they don’t point to a specific thing in the world. Concrete or specific instances could be a rectangle, a tiger or a savings account.

In other words, when an instance of a class does not point to a real world object or a concept, it is best declared abstract.

An abstract class may provide a partial implementation.

When a class declares atleast one abstract method, then it must be declared abstract.

In inheritance, the top level classes are usually abstract. The subclasses provide the concrete Implementations​.

An example

//Book.java
package book;

public abstract class Book {
	protected String title;
	protected int pages;
	protected Cover cover;
	protected int bookmark;
	protected String author;
	
	public Book(String title, int pages, Cover cover, String author)
	{
		this.title = title;
		this.pages = pages;
		this.cover = cover;
		this.author = author;
	}

	public int getBookmark()
	{
		return bookmark;
	}

	public String getAuthor()
	{
		return author;
	}

	public String getTitle()
	{
		return title;
	}

	public int getPages()
	{
		return pages;
	}

	public Cover getCover()
	{
		return cover;
	}
	
	enum Cover{
		BOUND, SPIRAL
	}
	
	public abstract void setBookmark(int page);
	
	public abstract void read();

	@Override
	public String toString()
	{
		return title+" by "+author +", cover is "+cover+" with "+pages+" pages";
	}
	
}

// PrintedBook.java
package book;

public class PrintedBook extends Book
{
	public PrintedBook(String title, int pages, Cover cover, String author) {
		super(title,pages,cover,author);
	}

	@Override
	public void setBookmark(int page)
	{
		if(page > 0 && page < pages) {
			bookmark = page;
		} else {
			System.out.println("Invalid page number");
		}
	}

	@Override
	public void read()
	{
		System.out.println("Reading "+title+" from page "+bookmark);
	}

}

// NoteBook.java
package book;

public class NoteBook extends Book
{
	public NoteBook(String title, int pages, Cover cover, String author) {
		super(title,pages,cover,author);
	}

	@Override
	public void setBookmark(int page)
	{
		if(page > 0 && page < pages) {
			bookmark = page;
		} else {
			System.out.println("Invalid page number");
		}
	}

	@Override
	public void read()
	{
		System.out.println("Reading "+title+" from page "+bookmark);
	}
	
	public void write()
	{
		System.out.println("Writing in "+title+" on page "+bookmark);
	}

}

// BookRack.java
package book;

public class BookRack
{
	public static final int BOOKCOUNT = 100;
	private Book[] books;
	private int count=0;
	
	public BookRack() {
		books = new Book[BOOKCOUNT];
	}
	
	public void add(Book book) {
		if(count < BOOKCOUNT)
			books[count++] = book;
	}
	
	public void list() {
		for(Book book : books) {
			if(book != null)
				System.out.println(book);
		}
	}
	
	public static void main(String[] args){
		Book java = new PrintedBook("Javalore",185,Book.Cover.BOUND, "Anand B");
		java.setBookmark(45);
		java.read();
		
		BookRack rack = new BookRack();
		rack.add(java);
		rack.list();
	}
}

Class Inheritance

Inheritance builds a parent-child relationship between two classes.

The child class extends the base functionality of the parent class.

Inheritance promotes extensibility in a program.

The functionality available in the patent class can be reused in the child class. The child class may extend that functionality or override it by providing its own. 

Java supports single inheritance: a class can extend only one other class. Every child class can have only one parent.

Inheritance enables polymorphism: the parent class reference can hold an instance of the child class.

// Document.java
package document;

public class Document
{
  protected String fileName;
  protected String ext;	// file extension
  protected String content;

  public Document(String fileName, String content) {
    this.fileName = fileName;
    ext = ".txt";
    this.content = content;
  }

  public void read() {
    System.out.println(content);
  }

  public void edit(String text) {
    content += "\n"+text;
  }

  public void rename(String name) {
    fileName = name;
  }

  @Override
  public String toString()
  {
    return fileName+ext;
  }

}

// WordDocument.java

package document;

public class WordDocument extends Document
{
  public WordDocument(String fileName, String text) {
    super(fileName, text);
    ext = ".doc";
  }

  @Override
  public void read()
  {
    System.out.println("Reading file "+fileName+ext);
    super.read();
  }
}

// PDFDocument.java

public class PDFDocument extends Document
{
  public PDFDocument(String fileName, String text) {
    super(fileName, text);
    ext = ".pdf";
  }

  @Override
  public void read()
  {
    System.out.println("Reading file "+fileName+ext);
    super.read();
  }

}

// DocumentMaker.java

package document;

public class DocumentMaker
{
  // polymorphic
  public static void edit(Document doc, String content) {
    doc.edit(content);
  }
  // polymorphic
  public static void read(Document doc) {
    doc.read();
  }

  public static void main(String[] args) {
    // polymorphic
    Document word = new WordDocument("profile", "Profiles in Courage");
    edit(word, "A life story of JFK");
    read(word);

    System.out.println();

    PDFDocument pdf = new PDFDocument("resume","My Resume");
    edit(pdf, "My personal portfolio");
    read(pdf);

    System.out.println();

    Document note = new Document("My Notes", "Memo");
    System.out.println("Reading "+note);
    edit(note, "Just a quick note.");
    read(note);
  }
}

Class Composition

Classes relate to one another in two fundamental ways:
– Composition (HAS-A relationship)
Complex classes are composed of simple classes.
– Inheritance (IS-A relationship)
Classes inherit from other classes to provide extended functionality.

Composition promotes dependency between classes. This limits independent growth of the dependent classes.

Inheritance extends the base functionality of the parent class in the child class. This promotes extensibility.

The choice is not between composition and inheritance, but rather the decision shall be based on the desired functionality.


A composition example

// ToDo.java
package task;
public class ToDo {
  private String description;
  private boolean status;

  public ToDo(String description) {
    this.description = description;
  }

  public void setDescription(String describe){
    description = describe;
  }

  public String getDescription() {
    return description;
  }

  public void setStatus(boolean done) {
    status = done;
  }

  public boolean isDone() {
    return status;
  }

  public String toString() {
    return description + " -> "+(status?"done":"to do");
  }
}

// Task.java
package task;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.ParseException;
public class Task {
  private ToDo[] todos;
  private int count = 0;
  private String title;
  private Date due;
  private boolean status;

  public Task(String title) {
    this.title = title;
    todos = new ToDo[Tasker.TODOCOUNT];
  }
  public Task(String title, String dateStr) {
    this(title);
    try {
      addReminder(dateStr);
    } catch(ParseException pex) {
        System.out.println(pex.getMessage());
    }
}

public void setStatus() {
  for(ToDo todo : todos) {
    if(todo != null) {
      if(!todo.isDone()) {
        status = false;
        return;
      }
    }
  }
  status = true;
}

public boolean getStatus() {
  setStatus();
  return status;
}

public void setDue(Date date) {
  due = date;
}

public void addReminder(String str) throws ParseException {
  DateFormat format = new SimpleDateFormat("MMM d, yyyy h:m");
//DateFormat.getDateTimeInstance(
//DateFormat.MEDIUM, DateFormat.SHORT);

  due = format.parse(str);
}

public Date getDue() {
  return due;
}
public String getTitle() {
  return title;
}
public void add(ToDo todo) {
  if(count < Tasker.TODOCOUNT) {
    todos[count++] = todo;
  }
}

  public ToDo[] getTodos() {
    return todos;
  }
}

// Tasker.java
package task;
import java.util.Date;
import java.text.DateFormat;
import java.text.ParseException;
public class Tasker {
  private Task[] tasks;
  private int count = 0;
  public static final int TASKCOUNT = 100;
  public static final int TODOCOUNT = 10;

  enum Mode {
    ALL, PENDING, DONE
  }

  public Tasker() {
    tasks = new Task[TASKCOUNT];
  }

  public Task[] getTasks() {
    return tasks;
  }

  public void add(Task task) {
    if(count < TASKCOUNT) {
      tasks[count++] = task;
    }
  }

public void addReminder(Task task, String date) {
  try {
    task.addReminder(date);
  } catch(ParseException pex) {
    System.out.println(pex.getMessage());
  }
}

public void postpone(Task task, int days) {
  Date date = task.getDue();
  date.setDate(date.getDate()+days);
  task.setDue(date);
}

public void show(Task tsk) {
  String dueBy = ", due by ";
  Date due = tsk.getDue();
  if(due != null) {
    dueBy += due.toString();
    System.out.println(tsk.getTitle()+dueBy);
  } else {
    System.out.println(tsk.getTitle());
  }
  int n=1;
  for(ToDo tod : tsk.getTodos()) {
    if(tod != null) {
      System.out.println("\t"+n+". "+tod);
      n = n+1;
    }
  }
}

public void show(Mode mode) {
  for(Task tsk : getTasks()) {
     if(tsk != null) {
      if(mode == Mode.ALL)
        show(tsk);
      if(mode == Mode.PENDING)
        if(!tsk.getStatus())
          show(tsk);
      if(mode == Mode.DONE)
        if(tsk.getStatus())
          show(tsk);
    }
  }
}

public static void main(String[] argsv) {
  Tasker tasker = new Tasker();
  Task task = new Task("Program", "Nov 14, 2017 5:14 PM");
  tasker.postpone(task,3);
  ToDo todo = new ToDo("complete the program");
  ToDo next = new ToDo("test it a lot");
  todo.setStatus(true);
  next.setStatus(true);
  task.add(todo);
  task.add(next);
  Task another = new Task("Blog");
  tasker.addReminder(another,"Nov 14, 2017 5:14 PM");
  ToDo todo1 = new ToDo("Post to javalore");
  ToDo next1 = new ToDo("check on mobile");
  another.add(todo1);
  another.add(next1);
  tasker.add(task);
  tasker.add(another);
   tasker.show(Mode.ALL);
}
}