Skip to content

in28minutes/java-cheat-sheet

master
Switch branches/tags
Code

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 

README.md

Java Tutorial For Beginners - A Cheat Sheet

Review Java 9 Concepts at Jet Speed.

Complete Java Course

Image

Image

Image

Introduction

Background

Popularity of Java

  • Platform Independent or Portable
  • Object Oriented Language
  • Security
  • Rich API
  • Great IDE's
  • Omnipresent
    • Web Applications (Java EE (JSP, Servlets), Spring, Struts..)
    • Mobile Apps(Android)
    • Microservices (Spring Boot)

Platform Independence

  • Build once, run anywhere alt text
  • Java bytecode is the instruction set of the Java virtual machine
graph TD
A[Java Code] -->|Compiled| B(Bytecode)
B --> C{Run}
C -->|bytecode| D[Windows JVM] 
D --> K[Windows Instructions]
C -->|bytecode| E[Unix  JVM]
E --> L[Unix Instructions]
C -->|bytecode| F[Linux  JVM]
F --> M[Linux Instructions]
C -->|bytecode| G[Any other platform  JVM]
G --> N[Linux Instructions]

JDK vs JVM VS JRE

  • JVM (Java Virtual Machine)
    • runs the Java bytecode.
  • JRE
    • JVM + Libraries + Other Components (to run applets and other java applications)
  • JDK
    • JRE + Compilers + Debuggers

ClassLoader

  • Find and Loads Java Classes!

Three Types

  • System Class Loader - Loads all application classes from CLASSPATH
  • Extension Class Loader - Loads all classes from extension directory
  • Bootstrap Class Loader - Loads all the Java core files

Order of execution of ClassLoaders

  • JVM needs to find a class, it starts with System Class Loader.
  • If it is not found, it checks with Extension Class Loader.
  • If it not found, it goes to the Bootstrap Class Loader.
  • If a class is still not found, a ClassNotFoundException is thrown.

First Java Program

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }

}

Notes

  • Every line of code we write in Java is part of something called Class. We will talk about Class later.
  • First line defines a public class called HelloWorld. All the code in a class is between { and }.
  • When a program runs, Java should know which line of code has to be run first. public static void main(String[] args) is the first method that is run when a program is executed.

Java, like any other programming language, is particular about syntax!!

Using Java and JavaC

There are two steps involved in running a Java Program

  • Compilation
  • Execution

Compilation

We use javac to compile java code.

  • Open CommandPrompt/Terminal and cd to the folder where HelloWorld.java file is present
  • execute the command below
javac HelloWorld.java
  • You should see two files HelloWorld.java and HelloWorld.class in the folder.
  • HelloWorld.class contains the java bytecode

Execution

  • Now we can run the program using JVM
  • execute the command below
java HelloWorld
  • You should see the output "Hello World" printed in the console.

Class and Object

  • What is a class?
  • Definining an instance of a class - an object
  • Invoking a method on the object

Variables

  • Value of a variable changes during the course of a program execution.
int number;
number = 5;
System.out.println(number);//5
number = number + 2;
System.out.println(number);//7
number = number + 2;
System.out.println(number);//9

Declaring and Initializing Variables

  • Declaration is give a variable a name and type
TYPE variableName;

Tips

  • Two or more variables of single type can be declared together.
  • Variable can be local or global. The local variables can be referenced (ie, are valid) only within the scope of their method (or function).
  • All six numeric types in Java are signed.

Primitive Variables

Variables that store value.

Java defines few types like int (numbers), float(floating point numbers), char (characters). Variables of these types store the value of the variable directly. These are not objects. These are called primitive variables.

An example is shown below: Primitive Variables contains bits representing the value of the variable.

int value = 5;

Different primitive types in java are char, boolean, byte, short, int, long, double, or float. Because of these primitive types, Java is NOT considered to be a pure objected oriented language.

Numeric Data Types

  • Types : byte, short, int, long, float, double
  • Number of bits : 8, 16, 32, 64, 32, 64
  • Range : -x to x-1 where x = Power(2, number of bits -1)

char Data Type

  • Used to store characters. Size of character is 16 bits.

Examples

int i = 15;
long longValue = 1000000000000l;
byte b = (byte)254;

float f = 26.012f;
double d = 123.567;
boolean isDone = true;
boolean isGood = false;
char ch = 'a';
char ch2 = ';';

Reference Variables

Animal dog = new Animal();

The instance of new Animal - Animal object - is created in memory. The memory address of the object created is stored in the dog reference variable.

Reference Variables contains a reference or a guide to get to the actual object in memory.

Puzzles

Animal dog1 = new Animal();
dog1 = new Animal();

What will happen? Two objects of type Animal are created. Only one reference variable is created.

Animal animal1 = new Animal();
Animal animal2 = new Animal();
animal1 = animal2;

What will happen? What would happen if the same was done with primitive variables?

Identifiers

Names given to a class, method, interface, variables are called identifiers.

Legal Identifier Names

  • Combination of letters, numbers, $ and under-score(_)
  • Cannot start with a number
  • Cannot be a keyword
  • No limit on length of identifier

Java Keywords

List of Java Keywords

  • Primitives DataTypes : byte,short,int,long,float,double,char,boolean
  • Flow Control : if, else,for,do, while, switch, case, default, break, continue,return
  • Exception Handling : try, catch, finally,throw,throws,assert
  • Modifiers : public,private,protected,final,static,native,abstract, synchronized,transient,volatile,strictfp
  • Class Related : class,interface,package,extends,implements,import
  • Object Related : new, instanceof,super,this
  • Literals : true, false, null
  • Others : void, enum
  • Unused : goto,const

Literals

Any primitive data type value in source code is called Literal.

There are four types of literals:

  • Integer & Long
  • Floating Point
  • Boolean
  • Double

Literals

Integer Literals

  • There are 3 ways of representing an Integer Literal.
    • Decimal. Examples: 343, 545
    • Octal. Digits 0 to 7. Place 0 before a number. Examples : 070,011
    • Hexadecimal. Digits 0 to 9 and alphabets A to F (10-15). Case insensitive.
  • An integer literal by default is int.

Long Literals

  • All 3 integer formats: Decimal, Octal and Hexadecimal can be used to represent long by appending with L or l.

Floating point Literals

  • Numbers with decimal points. Example: double d = 123.456;
  • To declare a float, append f. Example: float f = 123.456f;
  • Floating point literals are double by default.
  • Appending d or D at end of double literal is optional Example: double d = 123.456D;

Boolean Literals

  • Valid boolean values are true and false.
  • TRUE, FALSE or True, False are invalid.

Character Literals

  • Represented by single character between single quotes Example: char a = 'a'
  • Unicode Representation also can be used. Prefix with \u. Example: char letterA = '\u0041';
  • A number value can also be assigned to character. Example: char letterB = 66; Numeric value can be from 0 to 65535;
  • Escape code can be used to represent a character that cannot be typed as literal. Example: char newLine = '\n';

Puzzles

int eight = 010; 
int nine=011;  
int invalid = 089;//COMPILER ERROR! 8 and 9 are invalid in Octal
int sixteen = 0x10; 
int fifteen = 0XF; 
int fourteen = 0xe;
int x = 23,000;
long a = 123456789l; 
long b = 0x9ABCDEFGHL; 
long c = 0123456789L;

float f = 123.456;//COMPILER ERROR! A double value cannot be assigned to a float.

boolean b = true; boolean b=false;
boolean b = TRUE;//COMPILATION ERROR
boolean b = 0; //COMPILER ERROR. This is not C Language

char ch = a;
char a = 97;
char ch1 = 66000; //COMPILER ERROR!

Tip - Assignment Operator

Assignment operator evaluates the expression on the right hand side and copies the value into the variable on the left hand side.

Basic Examples

int value = 35;//35 is copied into 35

int squareOfValue = value * value;//value * value = 35 * 35 is stored into squareOfValue

int twiceOfValue = value * 2;

Puzzles

int a1 = 5;
int b1 = 6;
b1 = a1; // value of a1 is copied into b1
a1 = 10; // If we change a1 or b1 after this, it would not change the other variable.. b1 will remain 6

Actor actor1 = new Actor();
actor1.setName("Actor1");
//This creates new reference variable actor1 of type Actor  new Actor() on the heap assigns the new Actor on the heap to reference variable

Actor actor2 = actor1;
actor2.setName("Actor2");
System.out.println(actor1.getName());//Actor2

Casting - Implicit and Explicit

Casting is used when we want to convert one data type to another.

  • A literal integer is by default int. Operation involving int-sized or less always result in int.
  • Floating point literals are by default double

Implicit Casting

  • Implicit Casting is done directly by the compiler.
    • Example : Widening Conversions i.e. storing smaller values in larger variable types.
byte b = 10; //byte b = (int) 10; Example below compiles because compiler introduces an implicit cast.

short n1 = 5;
short n2 = 6;
//short sum = n1 + n2;//COMPILER ERROR
short sum = (short)(n1 + n2);//Needs an explicit cast

byte b = 5;
b += 5; //Compiles because of implicit conversion

int value = 100;
long number = value; //Implicit Casting
float f = 100; //Implicit Casting 

Explicit Casting

  • Explicit Casting needs to be specified by programmer in code.
    • Example: Narrowing Conversions. Storing larger values into smaller variable types;
  • Explicit casting would cause truncation of value if the value stored is greater than the size of the variable.
long number1 = 25678;
int number2 = (int)number1;//Explicit Casting
//int x = 35.35;//COMPILER ERROR
int x = (int)35.35;//Explicit Casting

int bigValue = 280;
byte small = (byte) bigValue;
System.out.println(small);//output 24. Only 8 bits remain.

//float avg = 36.01;//COMPILER ERROR. Default Double
float avg = (float) 36.01;//Explicit Casting
float avg1 = 36.01f;
float avg2 = 36.01F; //f or F is fine

//byte large = 128; //Literal value bigger than range of variable type causes compilation error
byte large = (byte) 128;//Causes Truncation!

Compound Assignment Operators

  • Examples : +=, -=, *=
int a = 5;
a += 5; //similar to a = a + 5;
a *= 10;//similar to a = a * 10;
a -= 5;//similar to a = a - 5;
a /= 5;//similar to a = a / 5;

Other Operators

Remainder(%) Operator

  • Remainder when one number is divided by another.
System.out.println(10 % 4);//2
System.out.println(15 % 4);//3
System.out.println(-15 % 4);//-3

Conditional Operator

  • Conditional Operator is a Ternary Operator (3 Operands)
  • syntax : booleanCondition ? ResultIfTrue: ResultIfFalse;
int age = 18;

System.out.println(
age >= 18 ? "Can Vote": "Cannot Vote");//Can Vote

age = 15;

System.out.println(
age >= 18 ? "Can Vote": "Cannot Vote");//Cannot Vote

Bitwise Operators

  • You can work at bit level with these operators.
  • & is bitwise AND, | is bitwise OR, ~ is bitwise complement (negation), ^ is bitwise XOR, << is left shift bitwise operator and >> is right shift bitwise operator.
System.out.println(25|12);//output will be 29
/*convert to binary and calculate: 
00001100 (12 in decimal)
00011001 (25 in decimal)
________
00011101 (29 in decimal) */
System.out.println(25&12);//output will be 8
System.out.println(25^12);//output will be 21

Passing Variables to Methods

  • All variables , primitives and references , in Java, are passed to functions using copy-of-variable-value.

Passing Variables to Methods : Example

  • Passing a primitive variable and modifying the value in a method
  • Passing a reference variable and modifying the value in a method

Returning a Value From Method

  • null is a valid return value for an object.
  • You can return andy type that can be implicitly coverted to return type.
  • You cannot return anything from a void method.

Types of Variables

  • Different Types of Variables: Static, Member (or instance), Local, Block

Instance Variables

  • Declared inside a class outside any method.
  • Each instance of the class would have its own values.
  • Also called member value, field or property.

Local Variables

  • Variables declared in a method
  • Local Variables can only be marked with final modifier
  • If the name of a Local Variable is same as the name of an instance variable, it results in shadowing.

Member Variables

  • Defined at class level and without keyword static.

Static Variable

  • Defined at class level and using keyword static.

Member Variable and Static Variable

  • Member Variables can be accessed only through object references.
  • Static Variables can be accessed through a. Class Name and b. Object Reference. It is NOT recommended to use object reference to refer to static variables.

Example Static and Member Variables

public class StaticAndMemberVariables {
    public static void main(String[] args) {
	Actor actor1 = new Actor();
	actor1.name = "ACTOR1";
	//Actor.name //Compiler Error

	//Below statement can be written as actor1.count++
	//But NOT recommended.
	Actor.count++;

	Actor actor2 = new Actor();
	actor2.name = "ACTOR2";

	//Below statement can be written as actor2.count++
	//But NOT recommended.
	Actor.count++;

	System.out.println(actor1.name);//ACTOR1
	System.out.println(actor2.name);//ACTOR2

	//Next 3 statements refer to same variable
	System.out.println(actor1.count);//2
	System.out.println(actor2.count);//2
	System.out.println(Actor.count);//2
    }
}

class Actor {
    //RULE 1: Member Variables can be accessed 
    //only through object references
    String name;
    
    //RULE 2:Static Variables can be accessed 
    //through a.Class Name and b.Object Reference
    //It is NOT recommended to use object reference 
    //to refer to static variables.
    static int count;    
}

Scope of a Variable

  • Scope of a variable defines where (which part of code) a variable can be accessed.

Important Rules

  • Static Variable can be used anywhere in the class.
  • Member Variable can be used in any non-static method.
  • Local Variable can be used only in the method where it is declared.
  • Block Variable can be used only in the block (code between { and }) where it is declared.

Variable Scope Examples

Below code shows all these Rules in action:

public class VariablesExample {
    //RULE 1:Static Variable can be used anywhere in the class. 
    static int staticVariable;
    
    //RULE 2:Member Variable can be used in any non-static method. 
    int memberVariable;
    
    void method1() {
		//RULE 3: method1LocalVariable can be used only in method1.
		int method1LocalVariable;

		memberVariable = 5;//RULE 2
		staticVariable = 5;//RULE 1

		//Some Code
		{
		    //RULE 4:blockVariable can be used only in this block.
		    int blockVariable;
		    //Some Code
		}

		//blockVariable = 5;//COMPILER ERROR - RULE 4
    }
    
    void method2() {
		//method1LocalVariable = 5; //COMPILER ERROR - RULE3
    }
    
    static void staticMethod() {
		staticVariable = 5;//RULE 1
		//memberVariable = 5; //COMPILER ERROR - RULE 2
    }
}

Scope Example 1

  • staticVariable is declared using keyword static.
  • It is available in the instance method method1 and static method named staticMethod.

Scope Example 2

  • memberVariable is declared directly in the class and does NOT use keyword static. So, it is an instance variable.
  • It is available in the instance method method1 but not accessible in the static method named staticMethod.

Scope Example 3

  • method1LocalVariable is declared in the method method1. So, it is a local variable.
  • It is available in the instance method method1 but available in any other instance or static methods.

Scope Example 4

  • blockVariable is declared in a block in method1. So, it is a block variable.
  • It is available only in the block where it is defined.
  • It is not accessible any where out side the block , even in the same method.

Variable Initialization

  • Initialization defines the default value assigned to a variable if it is not initialized.

Important Rules

  • Member/Static variables are alway initialized with default values.
  • Default values for numeric types is 0, floating point types is 0.0, boolean is false, char is '\u0000' and for a object reference variable is null.
  • Local variables are not initialized by default by compiler.
  • Using a local variable before initialization results in a compilation error.
  • Assigning a null value is a valid initialization for reference variables.

Variable Initialization Examples

Lets look at an example program to understand all the rules regarding variable initialization.

package com.in28minutes.variables;

//RULE1:Member/Static variables are alway initialized with 
//default values.Default values for numeric types is 0, 
//floating point types is 0.0, boolean is false, 
//char  is '\u0000' and object reference variable is null.

//RULE2:Local/block variables are NOT initialized by compiler. 

//RULE3    :If local variables are used before initialization, 
//it would result in Compilation Error

public class VariableInitialization {
    public static void main(String[] args) {
		Player player = new Player();

		//score is an int member variable - default 0
		System.out.println(player.score);//0 - RULE1

		//name is a member reference variable - default null
		System.out.println(player.name);//null - RULE1

		int local; //not initialized
		//System.out.println(local);//COMPILER ERROR! RULE3

		String value1;//not initialized
		//System.out.println(value1);//COMPILER ERROR! RULE3

		String value2 = null;//initialized
		System.out.println(value2);//null - NO PROBLEM.
    }
}


class Player{
    String name;
    int score;
}

Initialization Example 1

  • player is an instance of the class Player. It contains member variables named name and score.
  • All member variables are initialized by default. Since name refers to a String i.e a reference variable it is initialized to null. score is an int variable and hence initialized to 0.

Initialization Example 2

  • local is a local variable defined in the main method.
  • An attempt to access a local variable without initialization would result in a compilation error.
  • Same is the case with value1 which is a String local variable.
  • If null is assigned to a reference variable, reference variable is considered to be assigned.

Wrapper Classes

  • Example 1

  • A wrapper class wraps (encloses) around a data type and gives it an object appearance

  • Wrapper: Boolean,Byte,Character,Double,Float,Integer,Long,Short

  • Primitive: boolean,byte,char ,double, float, int , long,short

  • Examples of creating wrapper classes are listed below.

    • Integer number = new Integer(55);//int;
    • Integer number2 = new Integer("55");//String
    • Float number3 = new Float(55.0);//double argument
    • Float number4 = new Float(55.0f);//float argument
    • Float number5 = new Float("55.0f");//String
    • Character c1 = new Character('C');//Only char constructor
    • Boolean b = new Boolean(true);
  • Reasons

    • null is a possible value
    • use it in a Collection
    • Object like creation from other types.. like String
  • A primitive wrapper class in the Java programming language is one of eight classes provided in the java.lang package to provide object methods for the eight primitive types. All of the primitive wrapper classes in Java are immutable.

Wrapper classes are final and immutable.

Creating Wrapper Classes

Integer number = new Integer(55);//int
Integer number2 = new Integer("55");//String

Float number3 = new Float(55.0);//double argument
Float number4 = new Float(55.0f);//float argument
Float number5 = new Float("55.0f");//String

Character c1 = new Character('C');//Only char constructor
//Character c2 = new Character(124);//COMPILER ERROR

Boolean b = new Boolean(true);

//"true" "True" "tRUe" - all String Values give True
//Anything else gives false
Boolean b1 = new Boolean("true");//value stored - true
Boolean b2 = new Boolean("True");//value stored - true
Boolean b3 = new Boolean("False");//value stored - false
Boolean b4 = new Boolean("SomeString");//value stored - false

b = false;

Wrapper Class Utility Methods

  • A number of utility methods are defined in wrapper classes to create and convert them.

valueOf Methods

Provide another way of creating a Wrapper Object

Integer seven = 
    Integer.valueOf("111", 2);//binary 111 is converted to 7

Integer hundred = 
    Integer.valueOf("100");//100 is stored in variable

xxxValue methods

xxxValue methods help in creating primitives

Integer integer = Integer.valueOf(57);
int primitive = integer.intValue();//57
float primitiveFloat = integer.floatValue();//57.0f

Float floatWrapper = Float.valueOf(57.0f);
int floatToInt = floatWrapper.intValue();//57
float floatToFloat = floatWrapper.floatValue();//57.0f

parseXxx methods

parseXxx methods are similar to valueOf but they return primitive values

int sevenPrimitive = 
    Integer.parseInt("111", 2);//binary 111 is converted to 7

int hundredPrimitive = 
    Integer.parseInt("100");//100 is stored in variable

static toString method

Look at the example of the toString static method below.

Integer wrapperEight = new Integer(8);
System.out.println(Integer.
toString(wrapperEight));//String Output: 8

Overloaded static toString method

2nd parameter: radix

System.out.println(Integer
.toString(wrapperEight, 2));//String Output: 1000

static toYyyyString methods.

Yyyy can be Hex,Binary,Octal

System.out.println(Integer
.toHexString(wrapperEight));//String Output:8 
System.out.println(Integer
.toBinaryString(wrapperEight));//String Output:1000
System.out.println(Integer
.toOctalString(wrapperEight));//String Output:10

Wrapper Class , Auto Boxing

Integer ten = new Integer(10);
ten++;//allowed. Java does the work behind the screen for us

Boxing and new instances

  • Auto Boxing helps in saving memory by reusing already created Wrapper objects. However wrapper classes created using new are not reused.
  • Two wrapper objects created using new are not same object.
Integer nineA = new Integer(9);
Integer nineB = new Integer(9);
System.out.println(nineA == nineB);//false
System.out.println(nineA.equals(nineB));//true
  • Two wrapper objects created using boxing are same object.
Integer nineC = 9;
Integer nineD = 9;
System.out.println(nineC == nineD);//true
System.out.println(nineC.equals(nineD));//true

String Class

  • A String class can store a sequence of characters. String is not a primitive in Java but a Class in its own right.

Strings are immutable

  • Value of a String Object once created cannot be modified. Any modification on a String object creates a new String object.
String str3 = "value1";
str3.concat("value2");
System.out.println(str3); //value1

Note that the value of str3 is not modified in the above example. The result should be assigned to a new reference variable (or same variable can be reused).

String concat = str3.concat("value2");
System.out.println(concat); //value1value2

Where are string literals stored in memory?

All strings literals are stored in "String constant pool". If compiler finds a String literal,it checks if it exists in the pool. If it exists, it is reused. Following statement creates 1 string object (created on the pool) and 1 reference variable.

String str1 = "value"; 

However, if new operator is used to create string object, the new object is created on the heap. Following piece of code create 2 objects.

//1. String Literal "value" - created in the "String constant pool"
//2. String Object - created on the heap
String str2 = new String("value");

String vs StringBuffer vs StringBuilder

  • Immutability : String
  • Thread Safety : String(immutable), StringBuffer
  • Performance : StringBuilder (especially when a number of modifications are made.)
  • Example 1

String Constant Pool

  • All strings literals are stored in "String constant pool". If compiler finds a String literal,it checks if it exists in the pool. If it exists, it is reused.

  • Following statement creates 1 string object (created on the pool) and 1 reference variable.

String str1 = "value"; 
  • However, if new operator is used to create string object, the new object is created on the heap. Following piece of code create 2 objects.
//1. String Literal "value" - created in the "String constant pool"
//2. String Object - created on the heap
String str2 = new String("value");

String Method Examples

String class defines a number of methods to get information about the string content.

String str = "abcdefghijk";
Get information from String

Following methods help to get information from a String.

//char charAt(int paramInt)
System.out.println(str.charAt(2)); //prints a char - c
System.out.println("ABCDEFGH".length());//8
System.out.println("abcdefghij".toString()); //abcdefghij
System.out.println("ABC".equalsIgnoreCase("abc"));//true

//Get All characters from index paramInt
//String substring(int paramInt)
System.out.println("abcdefghij".substring(3)); //defghij

//All characters from index 3 to 6
System.out.println("abcdefghij".substring(3,7)); //defg

String s1 = new String("HELLO"); 
String s2 = new String("HELLO"); 
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true

String Manipulation methods

Most important thing to remember is a String object cannot be modified. When any of these methods are called, they return a new String with the modified value. The original String remains unchanged.

//String concat(String paramString)
System.out.println(str.concat("lmn"));//abcdefghijklmn

//String replace(char paramChar1, char paramChar2)
System.out.println("012301230123".replace('0', '4'));//412341234123

//String replace(CharSequence paramCharSequence1, CharSequence paramCharSequence2)
System.out.println("012301230123".replace("01", "45"));//452345234523

System.out.println("ABCDEFGHIJ".toLowerCase()); //abcdefghij

System.out.println("abcdefghij".toUpperCase()); //ABCDEFGHIJ

//trim removes leading and trailings spaces
System.out.println(" abcd  ".trim()); //abcd

String Concatenation Operator

Three Rules of String Concatenation

  • RULE1: Expressions are evaluated from left to right.Except if there are parenthesis.
  • RULE2: number + number = number
  • RULE3: number + String = String
System.out.println(5 + "Test" + 5); //5Test5
System.out.println(5 + 5 + "Test"); //10Test
System.out.println("5" + 5 + "Test"); //55Test
System.out.println("5" + "5" + "25"); //5525
System.out.println(5 + 5 + "25"); //1025
System.out.println("" + 5 + 5 + "25"); //5525
System.out.println(5 + (5 + "25")); //5525
System.out.println(5 + 5 + 25); //35

Increment and Decrement Operators

  • Lets learn about the increment and decrement operators in Java.

Basics of Increment and Decrement Operators

Except for a minor difference ++i,i++ is similar to i = i+1 and --i,i-- is similar to i = i-1

++i is called pre-increment and i++ post increment

Increment Operators

Pre increment statement returns value after increment. Post increment statement returns value before increment

int i = 25;
int j = ++i;//i is incremented to 26, assigned to j
System.out.println(i + " " + j);//26 26

i = 25;
j = i++;//i value(25) is assigned to j, then incremented to 26
System.out.println(i + " " + j);//26 25

Decrement Operators

Decrement Operators are similar to increment operators.

i = 25;
j = --i;//i is decremented to 24, assigned to j
System.out.println(i + " " + j);//24 24

i = 25;
j = i--;//i value(25) is assigned to j, then decremented to 24
System.out.println(i + " " + j);//24 25

Relational Operators

  • Relation Operators are used to compare operands. They a always return true or false. List of Relation Operators include <, <=, >, >=, ==, and !=.

Relation Operators Examples

Let's consider a few examples of relational operators. Let's assume a int variable named number with a value 7.

int number = 7;

greater than operator

System.out.println(number > 5);//true
System.out.println(number > 7);//false

greater than equal to operator

System.out.println(number >= 7);//true

less than operator

System.out.println(number < 9);//true
System.out.println(number < 7);//false

less than equal to operator

System.out.println(number <= 7);//true

is equal to operator

System.out.println(number == 7);//true
System.out.println(number == 9);//false

NOT equal to operator

System.out.println(number != 9);//true
System.out.println(number != 7);//false

single = is assignment operator and == is comparison. Below statement uses =.

System.out.println(number = 7);//7

== (equals) operator

Let's look at how == equals operator works with primitives and reference variables.

Primitive Variables

  • Equality for Primitives only compares values
int a = 5;
int b = 5;

Below statement compares if a and b have same value.

System.out.println(a == b);//true

Reference Variables

Integer aReference = new Integer(5);
Integer bReference = new Integer(5);

For reference variables, == compares if they are referring to the same object.

System.out.println(aReference == bReference);//false

bReference = aReference;

//Now both are referring to same object
System.out.println(aReference == bReference);//true

Bitwise operators

  • Bitwise Operators are |,&,^ and ~
int a = 5; 
int b = 7; 

// bitwise and 
// 0101 & 0111=0101 = 5 
System.out.println("a&b = " + (a & b)); 

// bitwise or 
// 0101 | 0111=0111 = 7 
System.out.println("a|b = " + (a | b)); 

// bitwise xor 
// 0101 ^ 0111=0010 = 2 
System.out.println("a^b = " + (a ^ b)); 

// bitwise and 
// ~0101=1010 
// will give 2's complement of 1010 = -6 
System.out.println("~a = " + ~a); 

// can also be combined with 
// assignment operator to provide shorthand 
// assignment 
// a=a&b 
a &= b; 
System.out.println("a= " + a); // a = 5

Logical Operators

  • Logical Operators are &&, ||, |, &, ! and ^.

Short Circuit And Operator - &&

  • True when both operands are true.
System.out.println(true && true);//true
System.out.println(true && false);//false
System.out.println(false && true);//false
System.out.println(false && false);//false

Short Circuit Or Operator - ||

True when atleast one of operands are true.

System.out.println(true || true);//true
System.out.println(true || false);//true
System.out.println(false || true);//true
System.out.println(false || false);//false

Certification Tip : Logical Operators work with boolean values but not numbers.

//System.out.println(5 || 6);//COMPILER ERROR

Short circuit operators are Lazy

  • They stop execution the moment result is clear.
    • For &&, if first expression is false,result is false.
    • For ||, if first expression is true, the result is true.
    • In above 2 situations, second expressions are not executed.
int i = 10;
System.out.println(true || ++i==11);//true
System.out.println(false && ++i==11);//false
System.out.println(i);//i remains 10, as ++i expressions are not executed.

Operator & and |

  • Logical Operators &, | are similar to &&, || except that they don't short ciruit.
  • They execute the second expression even if the result is decided.

Certification Tip : While & and | are very rarely used, it is important to understand them from a certification perspective.

int j = 10;
System.out.println(true | ++j==11);//true
System.out.println(false & ++j==12);//false
System.out.println(j);//j becomes 12, as both ++j expressions are executed

Operator exclusive-OR (^)

  • Result is true only if one of the operands is true.
System.out.println(true ^ false);//true
System.out.println(false ^ true);//true
System.out.println(true ^ true);//false
System.out.println(false ^ false);//false

Not Operator (!)

Result is the negation of the expression.

System.out.println(!false);//true
System.out.println(!true);//false

Arrays

  • TODO : Why do we need arrays?
//Declaring an Array
int[] marks;

// Creating an array
marks = new int[5]; // 5 is size of array

int marks2[] = new int[5];//Declaring and creating an array in same line.

System.out.println(marks2[0]);//New Arrays are always initialized with default values - 0

//Index of elements in an array runs from 0 to length - 1
marks[0] = 25;
marks[1] = 30;
marks[2] = 50;
marks[3] = 10;
marks[4] = 5;

System.out.println(marks[2]);//Printing a value from array

//Printing a 1D Array
int marks5[] = { 25, 30, 50, 10, 5 };
System.out.println(marks5); //[I@6db3f829
System.out.println(
    Arrays.toString(marks5));//[25, 30, 50, 10, 5]

int length = marks.length;//Length of an array: Property length

//Enhanced For Loop
for (int mark: marks) {
    System.out.println(mark);
}

//Fill array with a value
Arrays.fill(marks, 100); //All array values will be 100

//String Arrays
String[] daysOfWeek = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };

2D Arrays

Best way to visualize a 2D array is as an array of arrays.

int[][] matrix = { { 1, 2, 3 }, { 4, 5, 6 } };

int[][] matrixA = new int[5][6]; 

//Accessing elements from 2D array:
System.out.println(matrix[0][0]); //1
System.out.println(matrix[1][2]); //6

//Looping a 2D array
for (int[] array: matrix) {
    for (int number: array) {
         System.out.println(number);
    }
}

// Printing a 2D Array
int[][] matrix3 = { { 1, 2, 3 }, { 4, 5, 6 } };
System.out.println(matrix3); //[[I@1d5a0305
System.out.println(

Arrays.toString(matrix3)); 
//[[I@6db3f829, [I@42698403]

System.out.println(Arrays.deepToString(matrix3)); 
//[[1, 2, 3], [4, 5, 6]]

System.out.println(matrix3[0]);//[I@86c347 - matrix3[0] is a 1D Array
System.out.println(Arrays.toString(matrix3[0]));//[1, 2, 3]

Other Array Operations

//Comparing Arrays
int[] numbers1 = { 1, 2, 3 };
int[] numbers2 = { 4, 5, 6 };

System.out.println(Arrays
.equals(numbers1, numbers2)); //false

int[] numbers3 = { 1, 2, 3 };

System.out.println(Arrays
.equals(numbers1, numbers3)); //true

// Sorting an Array

int rollNos[] = { 12, 5, 7, 9 };
Arrays.sort(rollNos);
System.out.println(Arrays.toString(rollNos));//[5, 7, 9, 12]

Array of Objects

Person[] persons = new Person[3];

//By default, an array of 3 reference variables is created.
//The person objects are not created
System.out.println(persons[0]);//null

//Let's create the new objects
persons[0] = new Person();
persons[1] = new Person();
persons[2] = new Person();

//Creating and initializing person array in one statement
Person[] personsAgain = { new Person(),new Person(),new Person()};

//Another example
Person[][] persons2D = 
    {
		{ new Person(),new Person(),new Person()},
		{ new Person(),new Person()}
    };

Array Certification Tips and Puzzles

//You can Declare, Create and Initialize Array on same line.
int marks3[] = { 25, 30, 50, 10, 5 };

//Leaving additional comma is not a problem. (note that comma after 5)
int marks4[] = { 25, 30, 50, 10, 5, };


int marks[]; //Not Readable
int[] runs; //Readable


//int values[5];//Compilation Error!Declaration of an Array should not include size. 

//marks = new int[];//COMPILER ERROR! Size of an array is mandatory to create an array.


//Declaring 2D Array Examples:
int[][] matrix1; //Recommended
int[] matrix2[]; //Legal but not readable. Avoid.

//Access 10th element when array has only length 5
//Runtime Exception: ArrayIndexOutOfBoundsException
//System.out.println(marks[10]);

//Array can contain only values of same type.

//COMPILE ERROR!!
//int marks4[] = {10,15.0}; //10 is int 15.0 is float

//Cross assigment of primitive arrays is ILLEGAL
int[] ints = new int[5];
short[] shorts = new short[5];
//ints = shorts;//COMPILER ERROR
//ints = (int[])shorts;//COMPILER ERROR


//The first dimension of a 2D array is mandatory
matrixA = new int[3][];//FINE
//matrixA = new int[][5];//COMPILER ERROR
//matrixA = new int[][];//COMPILER ERROR

//Each row in a 2D Array can have a different size. This is called a Ragged Array.
matrixA = new int[3][];//FINE
matrixA[0] = new int[3];
matrixA[0] = new int[4];
matrixA[0] = new int[5];

If Else Condition

  • Conditionally execute code!
  • Code inside If is executed only if the condition is true.

// Basic Example

if(true){
    System.out.println("Will be printed");
}

if(false){
    System.out.println("Will NOT be printed");//Not executed
}

//Example 1
int x = 5;

if(x==5){
    System.out.println("x is 5");//executed since x==5 is true
}

//Example 2
x = 6;
if(x==5){
    System.out.println("x is 5");//Not executed since x==5 is false
}

//Example 3
int y = 10;

if(y==10){
    System.out.println("Y is 10");//executed-condn y==10 is true
} else {
    System.out.println("Y is Not 10");
}

//Example 4
y = 11;

if(y==10){
    System.out.println("Y is 10");//NOT executed
} else {
    System.out.println("Y is Not 10");//executed
}

//Example 5
int z = 15;
//Only one condition is executed. Rest of the conditions are skipped.
if(z==10){
    System.out.println("Z is 10");//NOT executed
} else if(z==12){
    System.out.println("Z is 12");//NOT executed
} else if(z==15){
    System.out.println("Z is 15");//executed. 
} else {
    System.out.println("Z is Something Else.");//NOT executed
}

z = 18;
if(z==10){
    System.out.println("Z is 10");//NOT executed
} else if(z==12){
    System.out.println("Z is 12");//NOT executed
} else if(z==15){
    System.out.println("Z is 15");//NOT executed
} else {
    System.out.println("Z is Something Else.");//executed
}

//If else Example: without Blocks
int number = 5;
if(number < 0) 
    number = number + 10; //Not executed
    number++; //This statement is not part of if. Executed.
System.out.println(number);//prints 6

If else Puzzles

//Puzzle 1
int k = 15;
if (k > 20) {
    System.out.println(1);
} else if (k > 10) {
    System.out.println(2);
} else if (k < 20) {
    System.out.println(3);
} else {
    System.out.println(4);
}

//Output is 2. 
//Once a condition in nested-if-else is true the rest of the code is not executed.  


//Puzzle 2
int l = 15;

if(l<20)
    System.out.println("l<20");
if(l>20)
    System.out.println("l>20");
else
    System.out.println("Who am I?");

//Output is "l<20" followed by "Who am I?" on next line. //else belong to the last if before it unless brackets ({}) are used.

//Puzzle 3

int m = 15;

if(m>20)
    if(m<20)
System.out.println("m>20");
    else
System.out.println("Who am I?");

//Nothing is printed to output. 

//Code above is similar to the code snippet shown below

if(m>20) {//Condn is false. So, code in if is not executed
    if(m<20)
System.out.println("m>20");
    else
System.out.println("Who am I?");
}

Puzzles Continued

//Puzzle 4

int x1 = 0;
//Condition in if should always be boolean
//if(x1) {} //COMPILER ERROR
//if(x1=0) {}//COMPILER ERROR. Using = instead of ==
//If else condition should be boolean

//Puzzle 5

boolean isTrue = false;

if(isTrue==true){
    System.out.println("TRUE TRUE");//Will not be printed
}

if(isTrue=true){
    System.out.println("TRUE");//Will be printed.
}

//Condition is isTrue=true. This is assignment. Returns true. So, code in if is executed.

Switch Statement

  • Choose between a set of options.
  • From Java 6, String can be used as the switch argument.
//Example 1

int number = 2;
switch (number) {
case 1:
    System.out.println(1);
    break;
case 2:
    System.out.println(2);//PRINTED
    break;
case 3:
    System.out.println(3);
    break;
default:
    System.out.println("Default");
    break;
}
// Output of above example is 2.The case which is matched is executed.

Important Tips

  • There is a break statement in every case. If there is no break statement, switch continues to execute other cases.
  • There is a case named default. If none of the cases match default case is executed.
//Switch Statement Example 2 , No Breaks
number = 2;
switch (number) {
case 1:
    System.out.println(1);
case 2:
    System.out.println(2);
case 3:
    System.out.println(3);
default:
    System.out.println("Default");
}

Output of above switch

2
3
Default

Since there is no break after case 2, execution falls through to case 3. There is no break in case 3 as well. So, execution falls through to default.

Code in switch is executed from a matching case until a break or end of switch statement is encountered.

Switch Statement Example 3 , Few Break's

number = 2;
switch (number) {
case 1:
    System.out.println(1);
    break;
case 2:
case 3:
    System.out.println("Number is 2 or 3");
    break;
default:
    System.out.println("Default");
    break;
}

Program Output

number is 2 or 3.

Case 2 matches. Since there is no code in case 2, execution falls through to case 3, executes the println. Break statement takes execution out of the switch.

Switch Statement Example 4 , Let's Default

  • default is executed if none of the case's match.
number = 10;
switch (number) {
case 1:
    System.out.println(1);
    break;
case 2:
    System.out.println(2);
    break;
case 3:
    System.out.println(3);
    break;
default:
    System.out.println("Default");
    break;
}

Code Output

Default

Switch Statement Example 5 - Default need not be Last

number = 10;
switch (number) {
default:
    System.out.println("Default");
    break;
case 1:
    System.out.println(1);
    break;
case 2:
    System.out.println(2);
    break;
case 3:
    System.out.println(3);
    break;
}

Output

Default

Switch statement Example 6

Switch can be used only with char, byte, short, int, String or enum

long l = 15;
/*switch(l){//COMPILER ERROR. Not allowed.
}*/

Case value should be a compile time constant.

number = 10;
switch (number) {
//case number>5://COMPILER ERROR. Cannot have a condition
//case number://COMPILER ERROR. Should be constant.
}    

Loops

A loop is used to run same code again and again.

While Loop

int count = 0;

while(count < 5){//while this condn is true, loop is executed.
    System.out.print(count);
    count++;
}
//Output - 01234

While loop Example 2

count = 5;
while(count < 5){//condn is false. So, code in while is not executed.
    System.out.print(count);
    count++;
}//Nothing is printed to output

Do While Loop

  • The difference between a while and a do while is that the code in do while is executed at least once.
  • In a do while loop, condition check occurs after the code in loop is executed once.

Do While loop Example 1

int count = 0;
do{
    System.out.print(count);
    count++;
}while(count < 5);//while this condn is true, loop is executed.
//output is 01234

Do While loop Example 2

count = 5;
do{
    System.out.print(count);
    count++;
}while(count < 5);
//output is 5

For Loop

For loop is used to loop code specified number of times.

For Loop Example 1

for (int i = 0; i < 10; i++) {
    System.out.print(i);
}
//Output - 0123456789

Syntax - For loop statement has 3 parts

  • Initialization => int i=0. Initialization happens the first time a for loop is run.
  • Condition => i<10. Condition is checked every time before the loop is executed.
  • Operation (Increment or Decrement usually) => i++. Operation is invoked at the start of every loop (except for first time).

For Loop Example 2: There can be multiple statements in Initialization or Operation separated by commas

for (int i = 0,j = 0; i < 10; i++,j--) {
    System.out.print(j);
}
//Output - 0123456789

Enhanced For Loop

Enhanced for loop can be used to loop around array's or List's.

int[] numbers = {1,2,3,4,5};

for(int number:numbers){
    System.out.print(number);
}
//Output - 12345

Any of 3 parts in a for loop can be empty.

for (;;) {
    System.out.print("I will be looping for ever..");
}
//Infinite loop => Loop executes until the program is terminated.

Break Statement

Break statement breaks out of a loop

Example 1

for (int i = 0; i < 10; i++) {
    System.out.print(i);
    if (i == 5) {
         break;
    }
}
//Output - 012345
//Even though the for loop runs from 0 to 10, execution stops at i==5 because of the break statement. ÒBreak statementÓ stops the execution of the loop and takes execution to the first statement after the loop.

Break can be used in a while also.

int i = 0;
while (i < 10) {
    System.out.print(i);
    if (i == 5) {
break;
    }
    i++;
}
//Output - 012345

Break statement takes execution out of inner most loop.

for (int j = 0; j < 2; j++) {
    for (int k = 0; k < 10; k++) {
System.out.print(j + "" + k);
if (k == 5) {
    break;//Takes out of loop using k
}
    }
}
//Output - 000102030405101112131415
//Each time the value of k is 5 the break statement is executed. 
//The break statement takes execution out of the k loop and proceeds to the next value of j.

Labels can be used to label and refer to specific for loop in a nested for loop.

outer:
    for (int j = 0; j < 2; j++) {
       for (int k = 0; k < 10; k++) {
             System.out.print(j + "" + k);
             if (k == 5) {
               break outer;//Takes out of loop using j
             }
        }
    }
//Output - 000102030405

Continue Statement

  • Continue statement skips rest of the statements in the loop and starts next iteration
for (int i = 0; i < 10; i++) {
    if (i == 5) {
      continue;
    }
    System.out.print(i);
}

//Output => 012346789

//Note that the output does not contain 5. 
//When i==5 continue is executed. Continue skips rest of the code and goes to next loop iteration. 
//So, the print statement is not executed when i==5.

Continue can be used in a while also

int i = 0;
while (i < 10) {
    i++;
    if (i == 5) {
        continue;
    }
    System.out.print(i);
}
//Output - 1234678910

Continue statement takes execution to next iteration of inner most loop.

for (int j = 0; j < 2; j++) {
    for (int k = 0; k < 10; k++) {
       if (k == 5) {
          continue;//skips to next iteration of k loop
        }
        System.out.print(j + "" + k);
    }
}
//Output - 000102030406070809101112131416171819
//When k==5 the print statement in the loop is skipped due to continue. 
//So 05 and 05 are not printed to the console.

Label Example

outer:
    for (int j = 0; j < 2; j++) {
		for (int k = 0; k < 10; k++) {
    		if (k == 5) {
				continue outer;//skips to next iteration of j loop
    		}
    		System.out.print(j + "" + k);
		}
    }
//Output - 00010203041011121314
//When k==5 is true, continue outer is called. 
//So, when value of k is 5, the loop skips to the next iteration of j. 

Enum

  • Enum allows specifying a list of valid values (or allowed values) for a Type.

Enum Declaration

Consider the example below. It declares an enum Season with 4 possible values.

    enum Season {
       WINTER, SPRING, SUMMER, FALL
    };

Enum Example 1

//Enum can be declared outside a class
enum SeasonOutsideClass {
	WINTER, SPRING, SUMMER, FALL
};

public class Enum {
	// Enum can be declared inside a class
	enum Season {
		WINTER, SPRING, SUMMER, FALL
	};

	public static void main(String[] args) {
		/*
		 * //Uncommenting gives compilation error //enum cannot be created in a
		 * <main></main>ethod enum InsideMethodNotAllowed { WINTER, SPRING, SUMMER, FALL };
		 */

		// Converting String to Enum
		Season season = Season.valueOf("FALL");

		// Converting Enum to String
		System.out.println(season.name());// FALL

		// Default ordinals of enum
		// By default java assigns ordinals in order
		System.out.println(Season.WINTER.ordinal());// 0
		System.out.println(Season.SPRING.ordinal());// 1
		System.out.println(Season.SUMMER.ordinal());// 2
		System.out.println(Season.FALL.ordinal());// 3

		// Looping an enum => We use method values
		for (Season season1 : Season.values()) {
			System.out.println(season1.name());
			// WINTER SPRING SUMMER FALL (separate lines)
		}

		// Comparing two Enums
		Season season1 = Season.FALL;
		Season season2 = Season.FALL;
		System.out.println(season1 == season2);// true
		System.out.println(season1.equals(season2));// true
	}
}

Enum Rules

  • Enums can be declared in a separate class(SeasonOutsideClass) or as member of a class(Season). Enums cannot be declared in a method.

Conversion of Enum : Function valueOf(String) is used to convert a string to enum.

//Converting String to Enum
Season season = Season.valueOf("FALL");

Function name() is used to find String value of an enum.

//Converting Enum to String
System.out.println(season.name());//FALL

Java assigns default ordinals to an enum in order. However, it is not recommended to use ordinals to perform logic.

//Default ordinals of enum
// By default java assigns ordinals in order
System.out.println(Season.WINTER.ordinal());//0
System.out.println(Season.SPRING.ordinal());//1
System.out.println(Season.SUMMER.ordinal());//2
System.out.println(Season.FALL.ordinal());//3

Looping around an Enum - List of values allowed for an Enum can be obtained by invoking the function values().

//Looping an enum => We use method values
for (Season season1: Season.values()) {
    System.out.println(season1.name());
    //WINTER SPRING SUMMER FALL (separate lines)
}

Comparing two Enums

//Comparing two Enums
Season season1 = Season.FALL;
Season season2 = Season.FALL;
System.out.println(season1 == season2);//true
System.out.println(season1.equals(season2));//true

Enum Example 2

package com.in28minutes.java.beginners.concept.examples.enums;

public class EnumAdvanced {

	// Enum with a variable,method and constructor
	enum SeasonCustomized {
		WINTER(1), SPRING(2), SUMMER(3), FALL(4);

		// variable
		private int code;

		// method
		public int getCode() {
			return code;
		}

		// Constructor-only private or (default)
		// modifiers are allowed
		SeasonCustomized(int code) {
			this.code = code;
		}

		// Getting value of enum from code
		public static SeasonCustomized valueOf(int code) {
			for (SeasonCustomized season : SeasonCustomized.values()) {
				if (season.getCode() == code)
					return season;
			}
			throw new RuntimeException("value not found");// Just for kicks
		}

		// Using switch statement on an enum
		public int getExpectedMaxTemperature() {
			switch (this) {
			case WINTER:
				return 5;
			case SPRING:
			case FALL:
				return 10;
			case SUMMER:
				return 20;
			}
			return -1;// Dummy since Java does not recognize this is possible :)
		}

	};

	public static void main(String[] args) {
		SeasonCustomized season = SeasonCustomized.WINTER;

		/*
		 * //Enum constructor cannot be invoked directly //Below line would
		 * cause COMPILER ERROR SeasonCustomized season2 = new
		 * SeasonCustomized(1);
		 */

		System.out.println(season.getCode());// 1

		System.out.println(season.getExpectedMaxTemperature());// 5

		System.out.println(SeasonCustomized.valueOf(4));// FALL

	}

}

More Enum Basics

  • Enums can contain variables, methods, constructors. In example 2, we created a local variable called code with a getter.
  • We also created a constructor with code as a parameter.
//variable
private int code;

//method
public int getCode() {
    return code;
}

//Constructor-only private or (default) 
//modifiers are allowed
SeasonCustomized(int code) {
    this.code = code;
}

Each of the Season Type's is created by assigning a value for code.

WINTER(1), SPRING(2), SUMMER(3), FALL(4);

Enum constructors can only be (default) or (private) access. Enum constructors cannot be directly invoked.

/*//Enum constructor cannot be invoked directly
  //Below line would cause COMPILER ERROR
SeasonCustomized season2 = new SeasonCustomized(1);
*/

Example below shows how we can use a switch around an enum.

// Using switch statement on an enum
public int getExpectedMaxTemperature() {
	switch (this) {
	case WINTER:
		return 5;
	case SPRING:
	case FALL:
		return 10;
	case SUMMER:
		return 20;
	}
	return -1;
}

Enum Example 3

package com.in28minutes.java.beginners.concept.examples.enums;

public class EnumAdvanced2 {

	// Enum with a variable,method and constructor
	enum SeasonCustomized {
		WINTER(1) {
			public int getExpectedMaxTemperature() {
				return 5;
			}
		},
		SPRING(2), SUMMER(3) {
			public int getExpectedMaxTemperature() {
				return 20;
			}
		},
		FALL(4);

		// variable
		private int code;

		// method
		public int getCode() {
			return code;
		}

		// Constructor-only private or (default)
		// modifiers are allowed
		SeasonCustomized(int code) {
			this.code = code;
		}

		public int getExpectedMaxTemperature() {
			return 10;
		}

	};

	public static void main(String[] args) {
		SeasonCustomized season = SeasonCustomized.WINTER;

		System.out.println(season.getExpectedMaxTemperature());// 5

		System.out.println(SeasonCustomized.FALL.getExpectedMaxTemperature());// 10

	}

}

Enum Constant Class - In the example above, take a look at how the Winter Type is declared: It provides an overriding implementation for the getExpectedMaxTemperature method already declared in the Enum. This feature in an Enum is called a Constant Class.

WINTER(1) {
    public int getExpectedMaxTemperature() {
         return 5;
    }
}

Inheritance

Inheritance allows extending a functionality of a class and also promotes reuse of existing code.

Every Class extends Object class

  • Every class in Java is a sub class of the class Object.
  • When we create a class in Java, we inherit all the methods and properties of Object class.
String str = "Testing";
System.out.println(str.toString());
System.out.println(str.hashCode());
System.out.println(str.clone());

if(str instanceof Object){
    System.out.println("I extend Object");//Will be printed
}

Create a class Actor

public class Actor {
    public void act(){
        System.out.println("Act");
    };
}

We can extend this class by using the keyword extends. Hero class extends Actor

//IS-A relationship. Hero is-a Actor
public class Hero extends Actor {
    public void fight(){
       System.out.println("fight");
    };
}

Since Hero extends Actor, the methods defined in Actor are also available through an instance of Hero class.

Hero hero = new Hero();
//act method inherited from Actor
hero.act();//Act
hero.fight();//fight

Let's look at another class extending Actor class - Comedian.

//IS-A relationship. Comedian is-a Actor
public class Comedian extends Actor {
    public void performComedy(){
       System.out.println("Comedy");
    };
}

Methods in Animal class can be executed from an instance of Comedian class.

Comedian comedian = new Comedian();
//act method inherited from Actor
comedian.act();//Act
comedian.performComedy();//Comedy

Super class reference variable can hold an object of sub class

Actor actor1 = new Comedian();
Actor actor2 = new Hero();

Object is super class of all classes. So, an Object reference variable can hold an instance of any class.

//Object is super class of all java classes
Object object = new Hero(); 

Inheritance: IS-A Relationship

We should use inheritance only when there is an IS-A relationship between classes. For example, Comedian IS-A Actor, Hero IS-A Actor are both true. So, inheritance is correct relationship between classes.

  • Comedian is called a Sub Class. Actor is Super Class.

Multiple Inheritance results in a number of complexities. Java does not support Multiple Inheritance.

class Dog extends Animal, Pet { //COMPILER ERROR
}

We can create an inheritance chain.

class Pet extends Animal {
}

class Dog extends Pet {
}

Inheritance and Polymorphism

Polymorphism is defined as "Same Code" having "Different Behavior".

Example

public class Animal {
    public String shout() {
        return "Don't Know!";
    }
}

class Cat extends Animal {
    public String shout() {
        return "Meow Meow";
    }
}

class Dog extends Animal {
    public String shout() {
        return "BOW BOW";
    }

    public void run(){

    }
}

Execution

Animal animal1 = new Animal();
System.out.println(animal1.shout()); //Don't Know!

Animal animal2 = new Dog();//Animal reference used to store Dog object

//Reference variable type => Animal
//Object referred to => Dog
//Dog's bark method is called.
System.out.println(animal2.shout()); //BOW BOW

//Cannot invoke sub class method with super class reference variable.
//animal2.run();//COMPILE ERROR

Puzzle and Tips - instanceof Operator in depth

instanceof operator checks if an object is of a particular type.

class SuperClass {
};

class SubClass extends SuperClass {
};

interface Interface {
};

class SuperClassImplementingInteface implements Interface {
};

class SubClass2 extends SuperClassImplementingInteface {
};

class SomeOtherClass {
};

SubClass subClass = new SubClass();
Object subClassObj = new SubClass();

SubClass2 subClass2 = new SubClass2();
SomeOtherClass someOtherClass = new SomeOtherClass();

//We can run instanceof operator on the different instances created earlier.
System.out.println(subClass instanceof SubClass);//true
System.out.println(subClass instanceof SuperClass);//true
System.out.println(subClassObj instanceof SuperClass);//true

System.out.println(subClass2 instanceof SuperClassImplementingInteface);//true

//instanceof can be used with interfaces as well. 
//Since Super Class implements the interface, below code prints true.
System.out.println(subClass2 
instanceof Interface);//true

//If the type compared is unrelated to the object, a compilation error occurs.
//System.out.println(subClass 
//    instanceof SomeOtherClass);//Compiler Error

//Object referred by subClassObj(SubClass)- NOT of type SomeOtherClass
System.out.println(subClassObj instanceof SomeOtherClass);//false

Class, Object, State and Behavior

  • In this tutorial, lets look at a few important object oriented concepts.

Class, Object, State and Behavior Example

package com.in28minutes;

public class CricketScorer {
    //Instance Variables - constitute the state of an object
    private int score;

    //Behavior - all the methods that are part of the class
    //An object of this type has behavior based on the 
    //methods four, six and getScore
    public void four(){
score = score + 4;
    }
    
    public void six(){
score = score + 6;
    }
    
    public int getScore() {
return score;
    }
    
    public static void main(String[] args) {
CricketScorer scorer = new CricketScorer();
scorer.six();
//State of scorer is (score => 6)
scorer.four();
//State of scorer is (score => 10)
System.out.println(scorer.getScore());
    }
}

Class

A class is a Template.

  • In above example, class CricketScorer is the template for creating multiple objects.

A class defines state and behavior that an object can exhibit.

Object

An instance of a class.

  • In the above example, we create an object using new CricketScorer().
  • The reference of the created object is stored in scorer variable.
  • We can create multiple objects of the same class.

State

State represents the values assigned to instance variables of an object at a specific time.

Consider following code snippets from the above example.

  • The value in score variable is initially 0.
  • It changes to 6 and then 10.

State of an object might change with time.

scorer.six();
//State of scorer is (score => 6)

scorer.four();
//State of scorer is (score => 10)

Behavior

Behaviour of an object represents the different methods that are supported by it.

  • Above example the behavior supported is six(), four() and getScore().

toString method

toString() method in Java is used to print the content of an object.

Example

class Animal {

    public Animal(String name, String type) {
      this.name = name;
      this.type = type;
    }

    String name;
    String type;

}



Animal animal = new Animal("Tommy", "Dog");

//Output does not show the content of animal (what name? and what type?).
System.out.println(animal);//com.in28minutes.Animal@f7e6a96

To show the content of the animal object, we can override the default implementation of toString method provided by Object class.

//Adding toString to Animal class
class Animal {
    
    public Animal(String name, String type) {
        this.name = name;
        this.type = type;
    }

    String name;
    String type;

    public String toString() {
        return "Animal [name=" + name + ", type=" + type + "]";
    }

}

Animal animal = new Animal("Tommy","Dog");

//Output now shows the content of the animal object. 
System.out.println(animal);//Animal [name=Tommy, type=Dog]

equals method

equals method is used to compare if two objects are having the same content.

  • Default implementation of equals method is defined in Object class. The implementation is similar to == operator.
  • By default, two object references are equal only if they are pointing to the same object.
  • However, we can override equals method and provide a custom implementation to compare the contents for an object.

Example

class Client {
    private int id;

    public Client(int id) {
      this.id = id;
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + id;
      return result;
    }
}

// == comparison operator checks if the object references are pointing to the same object. 
// It does NOT look at the content of the object.
Client client1 = new Client(25);
Client client2 = new Client(25);
Client client3 = client1;

//client1 and client2 are pointing to different client objects.
System.out.println(client1 == client2);//false

//client3 and client1 refer to the same client objects.
System.out.println(client1 == client3);//true

//similar output to ==
System.out.println(client1.equals(client2));//false
System.out.println(client1.equals(client3));//true

//overriding equals method
class Client {
    private int id;

    public Client(int id) {
      this.id = id;
    }

    @Override
    public boolean equals(Object obj) {
      Client other = (Client) obj;
      if (id != other.id)
          return false;
      return true;
    }
}

Signature of the equals method is "public boolean equals(Object obj) ".

  • Note that "public boolean equals(Client client)" will not override the equals method defined in Object. Parameter should be of type Object.
  • The implementation of equals method checks if the id's of both objects are equal. If so, it returns true.
  • Note that this is a basic implementation of equals.

Example

Client client1 = new Client(25);
Client client2 = new Client(25);
Client client3 = client1;

//both id's are 25
System.out.println(client1.equals(client2));//true

//both id's are 25
System.out.println(client1.equals(client3));//true

Any equals implementation should satisfy these properties:

  • Reflexive. For any reference value x, x.equals(x) returns true.
  • Symmetric. For any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • Transitive. For any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) must return true.
  • Consistent. For any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, if no information used in equals is modified.
  • For any non-null reference value x, x.equals(null) should return false.

Let's now provide an implementation of equals which satisfy these properties:

//Client class
@Override
public boolean equals(Object obj) {
    if (this == obj)
return true;
    if (obj == null)
return false;
    if (getClass() != obj.getClass())
return false;
    Client other = (Client) obj;
    if (id != other.id)
return false;
    return true;
}

hashCode method

  • HashCode's are used in hashing to decide which group (or bucket) an object should be placed into.
    • A group of object's might share the same hashcode.
    • The implementation of hash code decides effectiveness of Hashing.
    • A good hashing function evenly distributes object's into different groups (or buckets).

hashCode method properties

  • If obj1.equals(obj2) is true, then obj1.hashCode() should be equal to obj2.hashCode()
  • obj.hashCode() should return the same value when run multiple times, if values of obj used in equals() have not changed.
  • If obj1.equals(obj2) is false, it is NOT required that obj1.hashCode() is not equal to obj2.hashCode(). Two unequal objects MIGHT have the same hashCode.

Example

//Client class
@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + id;
    return result;
}

Abstract Class

An abstract class cannot be instantiated.

public abstract class AbstractClassExample {
    public static void main(String[] args) {
      //An abstract class cannot be instantiated
      //Below line gives compilation error if uncommented
      //AbstractClassExample ex = new AbstractClassExample();
    }
}

//Abstract class can contain instance and static variables

public abstract class AbstractClassExample {
    
    //Abstract class can contain instance and static variables
    public int publicVariable;
    private int privateVariable;
    static int staticVariable;

}

//An Abstract method does not contain body.
//Abstract Class can contain 0 or more abstract methods
//Abstract method does not have a body
abstract void abstractMethod1();
abstract void abstractMethod2();

//Abstract method can be declared only in Abstract Class. 
class NormalClass{
    abstract void abstractMethod();//COMPILER ERROR
}

// Abstract class can contain fully defined non-abstract methods. 
public abstract class AbstractClassExample {
    
    //Abstract class can contain instance and static variables
    public int publicVariable;
    private int privateVariable;
    static int staticVariable;
    
    //Abstract Class can contain 0 or more abstract methods
    //Abstract method does not have a body
    abstract void abstractMethod1();
    abstract void abstractMethod2();
    
    //Abstract Class can contain 0 or more non-abstract methods
    public void nonAbstractMethod(){
      System.out.println("Non Abstract Method");
    }    
    
    public static void main(String[] args) {
      //An abstract class cannot be instantiated
      //Below line gives compilation error if uncommented
      //AbstractClassExample ex = new AbstractClassExample();
    }
}

//Extending an abstract class
class SubClass2 extends AbstractClassExample {
    void abstractMethod1() {
      System.out.println("Abstract Method1");
    }

    void abstractMethod2() {
      System.out.println("Abstract Method2");
    }
}

// A concrete sub class should implement all abstract methods.
// Below class gives compilation error if uncommented
/*
class SubClass extends AbstractClassExample {
    
}
*/

//An abstract sub class need not implement all abstract methods.
abstract class AbstractSubClass extends AbstractClassExample {
    void abstractMethod1() {
      System.out.println("Abstract Method1");
    }
    //abstractMethod2 is not defined.
}

Tips

  • Abstract Methods cannot be paired with final or private access modifiers.
  • A variable cannot be abstract.

Constructors

  • Constructor is invoked whenever we create an instance(object) of a Class. We cannot create an object without a constructor. If we do not provide a constructor, compiler provides a default no-argument constructor.

Constructor Example 1: Default Constructor

In the example below, there are no Constructors defined in the Animal class. Compiler provides us with a default constructor, which helps us create an instance of animal class.

public class Animal {
    String name;

    public static void main(String[] args) {
// Compiler provides this class with a default no-argument constructor.
// This allows us to create an instance of Animal class.
Animal animal = new Animal();
    }
}

Constructor Example 2: Creating a Constructor

If we provide a constructor in the class, compiler will NOT provide a default constructor. In the example below we provided a constructor "public Animal(String name)". So, compiler will not provide the default constructor.

Constructor has the same name as the class and no return type. It can accept any number of parameters.

class Animal {
    String name;

    // This is called a one argument constructor.
    public Animal(String name) {
this.name = name;
    }

    public static void main(String[] args) {
// Since we provided a constructor, compiler does not
// provide a default constructor.
// Animal animal = new Animal();//COMPILER ERROR!

// The only way we can create Animal1 object is by using
Animal animal = new Animal("Tommy");
    }
}

Constructor Example 3: Provide No Argument Constructor

If we want to allow creation of an object with no constructor arguments, we can provide a no argument constructor as well.

class Animal {
    String name;

    public Animal() {
this.name = "Default Name";
    }

    // This is called a one argument constructor.
    public Animal(String name) {
this.name = name;
    }

    public static void main(String[] args) {
// Since we provided a constructor, compiler does not
// provide a default constructor.
// Animal animal = new Animal();//COMPILER ERROR!

// The only way we can create Animal1 object is by using
Animal animal = new Animal("Tommy");
    }
}

Constructor Example 4: Calling a Super Class Constructor

A constructor can invoke another constructor, or a super class constructor, but only as first statement in the constructor. Another constructor in the same class can be invoked from a constructor, using this({parameters}) method call. To call a super class constructor, super({parameters}) can be used.

Both example constructors below can replace the no argument "public Animal() " constructor in Example 3.

public Animal() {
    super();
    this.name = "Default Name";
}

public Animal() {
    this("Default Name");
}

super() or this() should be first statements in a Constructor.

Below examples will throw a compilation error if the super or this calls are uncommented.

public Animal() {
    this.name = "Default Name";
    //super(), if called, should always the first statement in a constructor.
    //super(); //COMPILER ERROR
}

public Animal() {
    System.out.println("Creating an Animal");
    //this(string), if called, should always the first statement in a constructor.
    //this("Default Name");//COMPILER ERROR
}

Constructor Example 5

Member variables/methods should not be used in constructor calls (super or this). Static variables or methods can be used.

public Animal() {
    //member variable cannot be used in a constructor call
    this(name);//COMPILER ERROR since name is member variable
}

Constructor Example 6: Constructor cannot be directly called

A constructor cannot be explicitly called from any method except another constructor.

class Animal {
    String name;

    public Animal() {
    }

    public method() {
Animal();// Compiler error
    }
}

Constructor Example 7: Super Class Constructor is invoked automatically

If a super class constructor is not explicitly called from a sub class constructor, super class (no argument) constructor is automatically invoked (as first line) from a sub class constructor.

Consider the example below:

class Animal {
    public Animal() {
System.out.println("Animal Constructor");
    }
}

class Dog extends Animal {
    public Dog() {
System.out.println("Dog Constructor");
    }
}

class Labrador extends Dog {
    public Labrador() {
System.out.println("Labrador Constructor");
    }
}

public class ConstructorExamples {
    public static void main(String[] args) {
Labrador labrador = new Labrador();
    }
}
//Output - Animal Constructor
Dog Constructor
Labrador Constructor

It is almost as if super() method is invoked as the first line of every constructor. The example code below shows how the code above behaves.

class Animal {
    public Animal() {
super();// IMPLICIT CALL
System.out.println("Animal Constructor");
    }
}

class Dog extends Animal {
    public Dog() {
super();// IMPLICIT CALL
System.out.println("Dog Constructor");
    }
}

class Labrador extends Dog {
    public Labrador() {
super();// IMPLICIT CALL
System.out.println("Labrador Constructor");
    }
}

Constructor Example 8

Since a subclass constructor explicitly calls a super class constructor with no arguments, this can cause a few compiler errors.

class Animal {
    String name;

    public Animal(String name) {
this.name = name;
System.out.println("Animal Constructor");
    }
}

class Dog extends Animal {
    public Dog() { // COMPILER ERROR! No constructor for Animal()
System.out.println("Dog Constructor");
    }
}

public Dog() makes an implicit super() call i.e. a call to Animal() (no argument) constructor. But no such constructor is defined in Animal class.

Constructor Example 9

Similar example below except that the Dog no argument constructor is not provided by programmer. However, the compiler would give the no argument constructor, which would invoke super() method. This would again result in a compilation error.

class Animal {
    String name;

    public Animal(String name) {
this.name = name;
System.out.println("Animal Constructor");
    }
}

class Dog extends Animal {// COMPILER ERROR! No constructor for Animal()
}

Two ways to fix above errors. 1.Create a no arguments constructor in Animal class. 2.Make a explicit super("Default Dog Name") call in the Dog() constructor.

Creating a super class no argument constructor

class Animal {
    String name;

    public Animal() {
    }

    public Animal(String name) {
this.name = name;
System.out.println("Animal Constructor");
    }
}

Making an explicity super call

class Dog extends Animal {
    public Dog() { // COMPILER ERROR! No constructor for Animal()
super("Default Dog Name");
System.out.println("Dog Constructor");
    }
}

Constructors are NOT inherited.

class Animal {
    String name;

    public Animal(String name) {
this.name = name;
System.out.println("Animal Constructor with name");
    }
}

class Dog extends Animal {
}

public class ConstructorExamples {
    public static void main(String[] args) {
// Dog dog = new Dog("Terry");//COMPILER ERROR
    }
}

new Dog("Terry") is not allowed even though there is a constructor in the super class Animal with signature public Animal(String name).

Solution is to create an explicit constructor in sub class invoking the super class constructor. Add below constructor to Dog class.

class Dog extends Animal {
    public Dog() {
super("Default Dog Name");
    }
}

Coupling

  • Coupling is a measure of how much a class is dependent on other classes. There should minimal dependencies between classes. So, we should always aim for low coupling between classes.

Coupling Example Problem

Consider the example below:

class ShoppingCartEntry {
    public float price;
    public int quantity;
}

class ShoppingCart {
    public ShoppingCartEntry[] items;
}

class Order {
    private ShoppingCart cart;
    private float salesTax;

    public Order(ShoppingCart cart, float salesTax) {
this.cart = cart;
this.salesTax = salesTax;
    }

    // This method know the internal details of ShoppingCartEntry and
    // ShoppingCart classes. If there is any change in any of those
    // classes, this method also needs to change.
    public float orderTotalPrice() {
float cartTotalPrice = 0;
for (int i = 0; i < cart.items.length; i++) {
    cartTotalPrice += cart.items[i].price
    * cart.items[i].quantity;
}
cartTotalPrice += cartTotalPrice * salesTax;
return cartTotalPrice;
    }
}

Method orderTotalPrice in Order class is coupled heavily with ShoppingCartEntry and ShoppingCart classes. It uses different properties (items, price, quantity) from these classes. If any of these properties change, orderTotalPrice will also change. This is not good for Maintenance.

Coupling Example Solution

Consider a better implementation with lesser coupling between classes below: In this implementation, changes in ShoppingCartEntry or CartContents might not affect Order class at all.

class ShoppingCartEntry
{
    float price;
    int quantity;

    public float getTotalPrice()
    {
return price * quantity;
    }
}

class CartContents
{
    ShoppingCartEntry[] items;

    public float getTotalPrice()
    {
float totalPrice = 0;
for (ShoppingCartEntry item:items)
{
    totalPrice += item.getTotalPrice();
}
return totalPrice;
    }
}

class Order
{
    private CartContents cart;
    private float salesTax;

    public Order(CartContents cart, float salesTax)
    {
this.cart = cart;
this.salesTax = salesTax;
    }

    public float totalPrice()
    {
return cart.getTotalPrice() * (1.0f + salesTax);
    }
}

Cohesion

  • Cohesion is a measure of how related the responsibilities of a class are. A class must be highly cohesive i.e. its responsibilities (methods) should be highly related to one another.

Cohesion Example Problem

Example class below is downloading from internet, parsing data and storing data to database. The responsibilities of this class are not really related. This is not cohesive class.

class DownloadAndStore{
    void downloadFromInternet(){
    }
    
    void parseData(){
    }
    
    void storeIntoDatabase(){
    }
    
    void doEverything(){
downloadFromInternet();
parseData();
storeIntoDatabase();
    }
}

Cohesion Example Solution

This is a better way of approaching the problem. Different classes have their own responsibilities.

class InternetDownloader {
    public void downloadFromInternet() {
    }
}

class DataParser {
    public void parseData() {
    }
}

class DatabaseStorer {
    public void storeIntoDatabase() {
    }
}

class DownloadAndStore {
    void doEverything() {
new InternetDownloader().downloadFromInternet();
new DataParser().parseData();
new DatabaseStorer().storeIntoDatabase();
    }
}

Encapsulation

  • Encapsulation is hiding the implementation of a Class behind a well defined interfaceÓ. Encapsulation helps us to change implementation of a class without breaking other code.

Encapsulation Approach 1

In this approach we create a public variable score. The main method directly accesses the score variable, updates it.

Example Class

public class CricketScorer {
    public int score;
}

Let's use the CricketScorer class.

public static void main(String[] args) {
CricketScorer scorer = new CricketScorer();
scorer.score = scorer.score + 4;
}

Encapsulation Approach 2

In this approach, we make score as private and access value through get and set methods. However, the logic of adding 4 to the score is performed in the main method.

Example Class

public class CricketScorer {
    private int score;

    public int getScore() {
return score;
    }

    public void setScore(int score) {
this.score = score;
    }
}

Let's use the CricketScorer class.

public static void main(String[] args) {
CricketScorer scorer = new CricketScorer();

int score = scorer.getScore();
scorer.setScore(score + 4);
}

Encapsulation Approach 3

In this approach - For better encapsulation, the logic of doing the four operation also is moved to the CricketScorer class.

Example Class

public class CricketScorer {
    private int score;
    
    public void four() {
score += 4;
    }

}

Let's use the CricketScorer class.

public static void main(String[] args) {
  CricketScorer scorer = new CricketScorer();
  scorer.four();
}

Encapsulation Example

In terms of encapsulation Approach 3 > Approach 2 > Approach 1. In Approach 3, the user of scorer class does not even know that there is a variable called score. Implementation of Scorer can change without changing other classes using Scorer.

Interface

  • An interface defines a contract for responsibilities (methods) of a class. Let's look at a few examples of interfaces.

Defining an Interface

An interface is declared by using the keyword interface. Look at the example below: Flyable is an interface.

//public abstract are not necessary
public abstract interface Flyable {
    //public abstract are not necessary
    public abstract void fly();
}

An interface can contain abstract methods -- NOT TRUE ANY MORE

In the above example, fly method is abstract since it is only declared (No definition is provided).

Implementing an Interface

We can define a class implementing the interface by using the implements keyword. Let us look at a couple of examples:

Example 1

Class Aeroplane implements Flyable and implements the abstract method fly().

public class Aeroplane implements Flyable{
    @Override
    public void fly() {
System.out.println("Aeroplane is flying");
    }
}

Example 2

public class Bird implements Flyable{
    @Override
    public void fly() {
System.out.println("Bird is flying");
    }
}

Using the Interface and Implementation

The interface classes can directly be instantiated and stored in the class reference variables

Bird bird = new Bird();
bird.fly();//Bird is flying

Aeroplane aeroplane = new Aeroplane();
aeroplane.fly();//Aeroplane is flying

An interface reference variable can hold objects of any implementation of interface.

Flyable flyable1 = new Bird();
Flyable flyable2 = new Aeroplane();

Variables in an interface

Variables in an interface are always public, static, final. Variables in an interface cannot be declared private.

interface ExampleInterface1 {
    //By default - public static final. No other modifier allowed
    //value1,value2,value3,value4 all are - public static final
    int value1 = 10;
    public int value2 = 15;
    public static int value3 = 20;
    public static final int value4 = 25;
    //private int value5 = 10;//COMPILER ERROR
}

Methods in an interface

Interface methods are by default public and abstract. A concrete default method (fully defined method) can be created in an interface. Consider the example below:

interface ExampleInterface1 {
    //By default - public abstract. No other modifier allowed
    void method1();//method1 is public and abstract
    //private void method6();//COMPILER ERROR!
    
}

Extending an Interface

An interface can extend another interface. Consider the example below:

interface SubInterface1 extends ExampleInterface1{
    void method3();
}

Class implementing SubInterface1 should implement both methods - method3 and method1(from ExampleInterface1) An interface cannot extend a class.

/* //COMPILE ERROR IF UnCommented
   //Interface cannot extend a Class
interface SubInterface2 extends Integer{
    void method3();
}
*/

A class can implement multiple interfaces. It should implement all the method declared in all Interfaces being implemented.

interface ExampleInterface2 {
    void method2();
}

class SampleImpl implements ExampleInterface1,ExampleInterface2{
    /* A class should implement all the methods in an interface.
       If either of method1 or method2 is commented, it would 
       result in compilation error. 
     */
    public void method2() {
System.out.println("Sample Implementation for Method2");
    }

    public void method1() {
System.out.println("Sample Implementation for Method1");
    }
    
}

Interface , Things to Remember

A class should implement all the methods in an interface, unless it is declared abstract. A Class can implement multiple interfaces. No new checked exceptions can be thrown by implementations of methods in an interface.

Method Overloading

  • A method having the same name as another method (in same class or a sub class) but having different parameters is called an Overloaded Method.

Method Overloading Example 1

doIt method is overloaded in the below example:

class Foo{
    public void doIt(int number){

    }
    public void doIt(String string){

    }
}

Method Overloading Example 2

Overloading can also be done from a sub class.

class Bar extends Foo{
    public void doIt(float number){

    }
}

Overloading - Other Rules

An overloaded method should have different arguments than the original method. It can also have a different return type. A method cannot be overloaded just by only changing the return type. Overloaded methods are always treated as if they are different methods altogether. Overloading does not put any restrictions on access modifiers or exceptions thrown from the method. Overloaded method invocation is based on the Type of the Reference variable. It is NOT based on the object it refers to.

  • Java Example
    • Constructors
    • public HashMap(int initialCapacity, float loadFactor)
    • public HashMap() {
    • public HashMap(int initialCapacity)
    • Methods
    • public boolean addAll(Collection<? extends E> c)
    • public boolean addAll(int index, Collection<? extends E> c)
  • Rules

Method Overriding

  • Creating a Sub Class Method with same signature as that of a method in SuperClass is called Method Overriding.

Method Overriding Example 1:

Let's define an Animal class with a method shout.

public class Animal {
    public String bark() {
return "Don't Know!";
    }
}

Let's create a sub class of Animal , Cat - overriding the existing shout method in Animal.

class Cat extends Animal {
    public String bark() {
return "Meow Meow";
    }
}

bark method in Cat class is overriding the bark method in Animal class.

  • Java Example
    • HashMap public int size() overrides AbstractMap public int size()
  • Example

Overriding Method Cannot have lesser visibility

Overriding method cannot have lesser visibility than the Super Class method. Consider these two examples

Example 1

class SuperClass{
    public void publicMethod(){

    }
}

class SubClass{
    //Cannot reduce visibility of SuperClass Method
    //So, only option is public
    public void publicMethod() {

    }    
}

publicMethod in SubClass can only be declared as public. Keyword protected, private or (default) instead of public would result in Compilation Error.

Example 2

class SuperClass{
    void defaultMethod(){

    }
}

class SubClass{
    //Can be overridden with public,(default) or protected
    //private would give COMPILE ERROR!
    public void defaultMethod(){

    }
    
}

defaultMethod in SuperClass is declared with default access. Any method overriding it can have access default or greater. So default, protected and public are fine. Overriding method cannot be private.

Overriding method cannot throw new Checked Exceptions

Consider the example below:

class SuperClass{
    public void publicMethod() throws FileNotFoundException{

    }
}

class SubClass{
    //Cannot throw bigger exceptions than Super Class
    public void publicMethod() /*throws IOException*/ {

    }
}

publicMethod() in SuperClass throws FileNotFoundException. So, the SubClass publicMethod() can throw FileNotFoundException or any sub class of FileNotFoundException. It can also not throw an Exception (as in the example). But, it cannot throw any new Exception. For example, Òpublic void publicMethod() throws IOExceptionÓ would cause compilation error.

Other Overriding Rules

A Sub Class can override only those methods that are visible to it. Methods marked as static or final cannot be overridden. You can call the super class method from the overriding method using keyword super.

Overriding and Polymorphism Example

Overridden method invocation is based on the object referred to. It is not based on the Type of the Reference variable. This is called Polymorphism. Consider the example below:

class Animal{
    public void bark(){
System.out.println("Animal Bark");
    }
}

class Dog extends Animal{
    public void bark(){
System.out.println("Dog Bark");
    }
}

public class PolymorphismExample {
    public static void main(String[] args) {

Animal[] animals = {new Dog(),new Animal()};

animals[0].bark();//Dog bark
animals[1].bark();//Animal bark
    }

}

animals[0] contains a reference to Dog Object. When animals[0].bark() method is called method in Dog class is invoked even though the type of reference variable is Animal. animals[1] contains a reference to Animal Object. When animals[1].bark() method is called method in Animal class is invoked.

Covariant Returns

A sub class is considered to be of same type as its super class. So, in interfaces or abstract class, it is fine to provide implementations using the Sub Class Types as Return Types.(com.in28minutes.SameType)

Class Modifiers

  • Let us learn about a few Java Class Modifiers.

Access Modifiers

Access modifier for a class can be public or (default), It cannot be private or protected.

public class PublicClass{
    
}

class DefaultClass{
    
}

protected class Error{//COMPILER ERROR
    
}

private class Error{//COMPILER ERROR
    
}

Non-access modifiers

strictfp, final, abstract modifiers are valid on a class.

Class Access Modifiers

  • Lets learn about a few Java Class Access Modifiers.

public class modifier

A public class is visible to all other classes.

default class modifier

A class is called a Default Class is when there is no access modifier specified on a class. Default classes are visible inside the same package only. Default access is also called Package access.

Default Class Modifier Examples

Default Access Class Example

package com.in28minutes.classmodifiers.defaultaccess.a;

/* No public before class. So this class has default access*/
class DefaultAccessClass {
//Default access is also called package access    
}

Another Class in Same Package: Has access to default class

package com.in28minutes.classmodifiers.defaultaccess.a;

public class AnotherClassInSamePackage {
    //DefaultAccessClass and AnotherClassInSamePackage 
    //are in same package.
    //So, DefaultAccessClass is visible.
    //An instance of the class can be created.    
    DefaultAccessClass defaultAccess;
}

Class in Different Package: NO access to default class

package com.in28minutes.classmodifiers.defaultaccess.b;

public class ClassInDifferentPackage {
    //Class DefaultAccessClass and Class ClassInDifferentPackage
    //are in different packages (*.a and *.b)
    //So, DefaultAccessClass is not visible to ClassInDifferentPackage
    
    //Below line of code will cause compilation error if uncommented
    //DefaultAccessClass defaultAccess; //COMPILE ERROR!!    
}

Method and Variable Access Modifiers

  • Method and variable access modifiers can be public, protected, private or (default)

Two Access Modifier Questions

When we talk about access modifiers, we would discuss two important questions

Is Accessible through reference/instance variable?

We create an instance of the class and try to access the variables and methods declared in the class.

ExampleClass example = new ExampleClass();

example.publicVariable = 5;
example.publicMethod();

Is Accessible through Inheritance?

Can we access the super class variables and methods from a Sub Class?

public class SubClass extends ExampleClass {    
    void subClassMethod(){
publicVariable = 5;
protectedVariable = 5;
    }    
}

Important Access Things to Remember

A sub class trying to access through reference/instance variables, will have the same access as a normal class (non sub class). Access modifiers cannot be applied to local variables

Access Modifiers Example

Let's consider the following class with variables and methods declared with all 4 access modifiers:

package com.in28minutes.membermodifiers.access;

public class ExampleClass {
    int defaultVariable;
    public int publicVariable;
    private int privateVariable;
    protected int protectedVariable;
    
    void defaultMethod(){

    }
    
    public void publicMethod(){

    }
    
    private void privateMethod(){

    }
    
    protected void protectedMethod(){

    }
}

Method Access Modifiers

Let's discuss about access modifiers in order of increasing access.

private

a. Private variables and methods can be accessed only in the class they are declared. b. Private variables and methods from SuperClass are NOT available in SubClass.

default or package

a. Default variables and methods can be accessed in the same package Classes. b. Default variables and methods from SuperClass are available only to SubClasses in same package.

protected

a. Protected variables and methods can be accessed in the same package Classes. b. Protected variables and methods from SuperClass are available to SubClass in any package

public

a. Public variables and methods can be accessed from every other Java classes. b. Public variables and methods from SuperClass are all available directly in the SubClass

Access Modifier Example: Class in Same Package

Look at the code below to understand what can be accessed and what cannot be.

package com.in28minutes.membermodifiers.access;

public class TestClassInSamePackage {
    public static void main(String[] args) {
ExampleClass example = new ExampleClass();

example.publicVariable = 5;
example.publicMethod();

//privateVariable is not visible
//Below Line, uncommented, would give compiler error
//example.privateVariable=5; //COMPILE ERROR
//example.privateMethod();

example.protectedVariable = 5;
example.protectedMethod();

example.defaultVariable = 5;
example.defaultMethod();
    }
}

Access Modifier Example: Class in Different Package

Look at the code below to understand what can be accessed and what cannot be.

package com.in28minutes.membermodifiers.access.different;

import com.in28minutes.membermodifiers.access.ExampleClass;

public class TestClassInDifferentPackage {
    public static void main(String[] args) {
ExampleClass example = new ExampleClass();

example.publicVariable = 5;
example.publicMethod();

//privateVariable,privateMethod are not visible
//Below Lines, uncommented, would give compiler error
//example.privateVariable=5; //COMPILE ERROR
//example.privateMethod();//COMPILE ERROR

//protectedVariable,protectedMethod are not visible
//Below Lines, uncommented, would give compiler error
//example.protectedVariable = 5; //COMPILE ERROR
//example.protectedMethod();//COMPILE ERROR

//defaultVariable,defaultMethod are not visible
//Below Lines, uncommented, would give compiler error
//example.defaultVariable = 5;//COMPILE ERROR
//example.defaultMethod();//COMPILE ERROR
    }
}

Access Modifier Example: Sub Class in Same Package

Look at the code below to understand what can be accessed and what cannot be.

package com.in28minutes.membermodifiers.access;

public class SubClassInSamePackage extends ExampleClass {
    
    void subClassMethod(){
publicVariable = 5;
publicMethod();

//privateVariable is not visible to SubClass
//Below Line, uncommented, would give compiler error
//privateVariable=5; //COMPILE ERROR
//privateMethod();

protectedVariable = 5;
protectedMethod();

defaultVariable = 5;
defaultMethod();
    }    
}

Access Modifier Example: Sub Class in Different Package

Look at the code below to understand what can be accessed and what cannot be.

package com.in28minutes.membermodifiers.access.different;

import com.in28minutes.membermodifiers.access.ExampleClass;

public class SubClassInDifferentPackage extends ExampleClass {
    
    void subClassMethod(){
publicVariable = 5;
publicMethod();

//privateVariable is not visible to SubClass
//Below Line, uncommented, would give compiler error
//privateVariable=5; //COMPILE ERROR
//privateMethod();

protectedVariable = 5;
protectedMethod();

//privateVariable is not visible to SubClass
//Below Line, uncommented, would give compiler error
//defaultVariable = 5; //COMPILE ERROR
//defaultMethod();
    }    
}

Final modifier

  • Let's discuss about Final modifier in Java.

Final class cannot be extended

Consider the class below which is declared as final.

final public class FinalClass {
}

Below class will not compile if uncommented. FinalClass cannot be extended.

/*
class ExtendingFinalClass extends FinalClass{ //COMPILER ERROR
    
}
*/

Example of Final class in Java is the String class. Final is used very rarely as it prevents re-use of the class.

Final methods cannot be overriden.

Consider the class FinalMemberModifiersExample with method finalMethod which is declared as final.

public class FinalMemberModifiersExample {
    final void finalMethod(){
    }
}

Any SubClass extending above class cannot override the finalMethod().

class SubClass extends FinalMemberModifiersExample {
    //final method cannot be over-riddent
    //Below method, uncommented, causes compilation Error
    /*
    final void finalMethod(){

    }
    */
}

Final variable values cannot be changed.

Once initialized, the value of a final variable cannot be changed.

final int finalValue = 5;
//finalValue = 10; //COMPILER ERROR

Final arguments value cannot be modified.

Consider the example below:

void testMethod(final int finalArgument){
    //final argument cannot be modified
    //Below line, uncommented, causes compilation Error
    //finalArgument = 5;//COMPILER ERROR
}

Other Non access Modifiers

  • A class cannot be both abstract and final

strictfp

This modifier can be used on a class and a method. This (strictfp) cannot be used on a variable. IEEE standard for floating points would be followed in the method or class where strictfp modifier is specified.

volatile

Volatile can only be applied to instance variables. A volatile variable is one whose value is always written to and read from "main memory". Each thread has its own cache in Java. The volatile variable will not be stored on a Thread cache.

native

Can be applied only to methods. These methods are implemented in native languages (like C)

Static Variables and Methods

  • Static variables and methods are class level variables and methods. There is only one copy of the static variable for the entire Class. Each instance of the Class (object) will NOT have a unique copy of a static variable. Let's start with a real world example of a Class with static variable and methods.

Static Variable/Method , Example

count variable in Cricketer class is static. The method to get the count value getCount() is also a static method.

public class Cricketer {
    private static int count;

    public Cricketer() {
count++;
    }

    static int getCount() {
return count;
    }

    public static void main(String[] args) {

Cricketer cricketer1 = new Cricketer();
Cricketer cricketer2 = new Cricketer();
Cricketer cricketer3 = new Cricketer();
Cricketer cricketer4 = new Cricketer();

System.out.println(Cricketer.getCount());//4
    }
}

4 instances of the Cricketer class are created. Variable count is incremented with every instance created in the constructor.

Static Variables and Methods Example 2

Example class below explains all the rules associated with access static variables and static methods.

public class StaticModifierExamples {
    private static int staticVariable;
    private int instanceVariable;

    public static void staticMethod() {
//instance variables are not accessible in static methods
//instanceVariable = 10; //COMPILER ERROR

//Also, this Keyword is not accessible. this refers to object.
//static methods are class methods

//static variables are accessible in static methods
staticVariable = 10;
    }

    public void instanceMethod() {
//static and instance variables are accessible in instance methods
instanceVariable = 10;
staticVariable = 10;
    }

    public static void main(String[] args) {
//static int i =5; //COMPILER ERROR
StaticModifierExamples example = new StaticModifierExamples();

//instance variables and methods are only accessible through object references
example.instanceVariable = 10;
example.instanceMethod();
//StaticModifierExamples.instanceVariable = 10;//COMPILER ERROR
//StaticModifierExamples.instanceMethod();//COMPILER ERROR

//static variables and methods are accessible through object references and Class Name.
example.staticVariable = 10;
example.staticMethod();
StaticModifierExamples.staticVariable = 10;
StaticModifierExamples.staticMethod();

    }
}

In a static method, instance variables are not accessible. Keyword this is also not accessible. However static variables are accessible.

    public static void staticMethod() {
//instance variables are not accessible in static methods
//instanceVariable = 10; //COMPILER ERROR

//Also, this Keyword is not accessible. this refers to object.
//static methods are class methods

//static variables are accessible in static methods
staticVariable = 10;
    }

In instance methods, both static and instance variables are accessible.

    public void instanceMethod() {
instanceVariable = 10;
staticVariable = 10;
    }

Instance variables and methods are only accessible through object references.

example.instanceVariable = 10;
example.instanceMethod();
//StaticModifierExamples.instanceVariable = 10;//COMPILER ERROR
//StaticModifierExamples.instanceMethod();//COMPILER ERROR

Static variables and methods are accessible through object references and Class Name.

example.staticVariable = 10;
example.staticMethod();
StaticModifierExamples.staticVariable = 10;
StaticModifierExamples.staticMethod();

It is always recommended to use Class Name to access a static variable or method. This is because static methods are class level methods. It is not appropriate to use instance references to call static methods (even though it compiles and works).

Static methods cannot be overridden

Consider the example below:

class Animal{
    static void StaticMethod(){
System.out.println("Animal Static Method");
    }
}

class Dog extends Animal{
    static void StaticMethod(){
System.out.println("Dog Static Method");
    }
}

When code below is run, static method in Animal is executed. Static method invocation is based on the type of reference variable. It does not depend on the type of object referred to.

Animal animal = new Dog();
animal.StaticMethod();//Animal Static Method

Local variables cannot be declared as static

Example below:

public static void main(String[] args) {
    //static int i =5; //COMPILER ERROR
}

Class Contents

  • Let's discuss what a Java class can contain and what it cannot.

#Section Java Source File Rules A Java Source File can contain a.0 or 1 Public Classes b.0 or 1 or More Non Public Classes

Order should be

  1. Package Statement
  2. Imports
  3. Class Declarations

Comments can be anywhere in the file. If there is a public class, file name should the (name of public class) + ".java". If name of public class is Scorer, name of file should be Scorer.java. If there is no public class, there are no restrictions on file name.

Example Class

/* Comments Anywhere*/
package com.in28minutes.classcontent;
class DefaultClass1{
	
}
/* Comments Anywhere*/
class DefaultClass2{
	
}
/* Comments Anywhere*/
public class PublicClass1 {
}

/* Cannot have another Public Class. 
public class PublicClass2 {
}
*/

Nested Class

  • Nested Classes are classes which are declared inside other classes.

Nested Class Example

Consider the following example:

class OuterClass {

    public class InnerClass {
    }

    public static class StaticNestedClass {
    }

    public void exampleMethod() {
class MethodLocalInnerClass {
};
    }

}

Inner Class

Generally the term inner class is used to refer to a non-static class declared directly inside another class. Consider the example of class named InnerClass.

Static Inner Class

A class declared directly inside another class and declared as static. In the example above, class name StaticNestedClass is a static inner class.

Method Inner Class

A class declared directly inside a method. In the example above, class name MethodLocalInnerClass is a method inner class.

Inner Class

  • Inner Class is a very important Java Concept. Let's learn about it in this tutorial.

Inner Class Example

Consider the following Example:

class OuterClass {
    private int outerClassInstanceVariable;

    public class InnerClass {
private int innerClassVariable;

public int getInnerClassVariable() {
    return innerClassVariable;
}

public void setInnerClassVariable(
int innerClassVariable) {
    this.innerClassVariable = innerClassVariable;
}

public void privateVariablesOfOuterClassAreAvailable() {
    outerClassInstanceVariable = 5; // we can access the value
    System.out.println("Inner class ref is " + this); 
    
    //Accessing outer class reference variable
    System.out.println("Outer class ref is " + OuterClass.this);
}
    }

    public void createInnerClass(){
//Just use the inner class name to create it
InnerClass inner = new InnerClass();
    }
    
}

public class InnerClassExamples {
    public static void main(String[] args) {

// COMPILER ERROR! You cannot create an inner class on its own.
// InnerClass innerClass = new InnerClass();
OuterClass example = new OuterClass();

// To create an Inner Class you need an instance of Outer Class
OuterClass.InnerClass innerClass = example.new InnerClass();

    }
}

Inner class cannot be directly instantiated.

// InnerClass innerClass = new InnerClass(); //Compiler Error

To create an Inner Class you need an instance of Outer Class.

OuterClass example = new OuterClass();
OuterClass.InnerClass innerClass = example.new InnerClass();

Creating an Inner Class instance in outer class

Consider the method createInnerClass from the example above: This method shows how to create an inner class instance.

public void createInnerClass(){
    //Just use the inner class name to create it
    InnerClass inner = new InnerClass();
}

Instance variables of Outer Class are available in inner class

Consider the method privateVariablesOfOuterClassAreAvailable from InnerClass declared above:

public void privateVariablesOfOuterClassAreAvailable() {
    outerClassInstanceVariable = 5; // we can access the value
    System.out.println("Inner class ref is " + this); 
    
    //Accessing outer class reference variable
    System.out.println("Outer class ref is " + OuterClass.this);
}

Static Inner Nested Class

  • Let's learn about Static Inner Nested Class in this Java tutorial.

Static Inner Nested Class Example

Consider the example below:

class OuterClass {
    private int outerClassInstanceVariable;

    public static class StaticNestedClass {
private int staticNestedClassVariable;

public int getStaticNestedClassVariable() {
    return staticNestedClassVariable;
}

public void setStaticNestedClassVariable(
int staticNestedClassVariable) {
    this.staticNestedClassVariable = staticNestedClassVariable;
}

public void privateVariablesOfOuterClassAreNOTAvailable() {
    // outerClassInstanceVariable = 5; //COMPILE ERROR
}
    }

}

public class InnerClassExamples {
    public static void main(String[] args) {
// Static Nested Class can be created without needing to create its
// parent. Without creating NestedClassesExample, we created
// StaticNestedClass
OuterClass.StaticNestedClass staticNestedClass1 = new OuterClass.StaticNestedClass();
staticNestedClass1.setStaticNestedClassVariable(5);

OuterClass.StaticNestedClass staticNestedClass2 = new OuterClass.StaticNestedClass();
staticNestedClass2.setStaticNestedClassVariable(10);

// Static Nested Class member variables are not static. They can have
// different values.
System.out.println(staticNestedClass1
.getStaticNestedClassVariable()); //5
System.out.println(staticNestedClass2
.getStaticNestedClassVariable()); //10
    }
} 

Creating Static Nested Class

Static Nested Class can be created without needing to create its parent. Without creating NestedClassesExample, we createdStaticNestedClass.

OuterClass.StaticNestedClass staticNestedClass1 = new OuterClass.StaticNestedClass();

Member variables are not static

Static Nested Class member variables are not static. They can have different values.

System.out.println(staticNestedClass1
.getStaticNestedClassVariable()); //5
System.out.println(staticNestedClass2
.getStaticNestedClassVariable()); //10

Outer class instance variables are not accessible.

Instance variables of outer class are not available in the Static Class.

public void privateVariablesOfOuterClassAreNOTAvailable() {
    // outerClassInstanceVariable = 5; //COMPILE ERROR
}

Method Inner Class

  • Let us learn about Method Inner Class in this tutorial.

Method Inner Class Example

Consider the example below: MethodLocalInnerClass is declared in exampleMethod();

class OuterClass {
    private int outerClassInstanceVariable;

    public void exampleMethod() {
int localVariable;
final int finalVariable = 5;
class MethodLocalInnerClass {
    public void method() {
//Can access class instance variables
System.out
.println(outerClassInstanceVariable);

//Cannot access method's non-final local variables
//localVariable = 5;//Compiler Error
System.out.println(finalVariable);//Final variable is fine..
    }
}

//MethodLocalInnerClass can be instantiated only in this method
MethodLocalInnerClass m1 = new MethodLocalInnerClass();
m1.method();
    }

    //MethodLocalInnerClass can be instantiated only in the method where it is declared
    //MethodLocalInnerClass m1 = new MethodLocalInnerClass();//COMPILER ERROR

}

Method inner class is not accessible outside the method

Look at the commented code below exampleMethod. MethodLocalInnerClass can be instantiated only in the method where it is declared.

Method inner class can access class instance variables

//Can access class instance variables
System.out.println(outerClassInstanceVariable);

Method inner class cannot access method's non-final local variables

//Cannot access method's non-final local variables
//localVariable = 5;//Compiler Error
System.out.println(finalVariable);//Final variable is fine..

Variable Arguments

  • Variable Arguments allow calling a method with different number of parameters.

Variable Arguments Example

    //int(type) followed ... (three dot's) is syntax of a variable argument. 
    public int sum(int... numbers) {
//inside the method a variable argument is similar to an array.
//number can be treated as if it is declared as int[] numbers;
int sum = 0;
for (int number: numbers) {
    sum += number;
}
return sum;
    }

    public static void main(String[] args) {
VariableArgumentExamples example = new VariableArgumentExamples();
//3 Arguments
System.out.println(example.sum(1, 4, 5));//10
//4 Arguments
System.out.println(example.sum(1, 4, 5, 20));//30
//0 Arguments
System.out.println(example.sum());//0
    }

Variable Arguments Syntax

Data Type followed ... (three dot's) is syntax of a variable argument.

public int sum(int... numbers) {

Inside the method a variable argument is similar to an array. For Example: number can be treated in below method as if it is declared as int[] numbers;

    public int sum(int... numbers) {
int sum = 0;
for (int number: numbers) {
    sum += number;
}
return sum;
    }

Variable Argument: only Last Parameter

Variable Argument should be always the last parameter (or only parameter) of a method. Below example gives a compilation error

    public int sum(int... numbers, float value) {//COMPILER ERROR
    }

Variable Argument of Type Custom Class

Even a class can be used a variable argument. In the example below, bark method is overloaded with a variable argument method.

    class Animal {
void bark() {
    System.out.println("Bark");
}
void bark(Animal... animals) {
    for (Animal animal: animals) {
animal.bark();
    }
}
    }

Exception Handling

  • In this tutorial, let's understand the need for exception handling and learn how to handle exceptions.

Example without Exception Handling

Let's first look an example without exception handling. Method main throws an exception because toString method is invoked on a null object.

    public static void main(String[] args) {
String str = null;
str.toString();
    }

Output of above program is

Exception in thread "main" java.lang.NullPointerException at
com.in28minutes.exceptionhandling.ExceptionHandlingExample1.main(ExceptionHandlingExample1.java:6)

Exception Example 2 , Propagation of an Exception

In this example, main invokes method1, which invokes method2 which throws a NullPointerException. Check the output of this program.

    public static void main(String[] args) {
method1();
    }

    private static void method1() {
method2();
    }

    private static void method2() {
String str = null;
str.toString();
    }
//Output - Exception in thread "main" java.lang.NullPointerException at
com.in28minutes.exceptionhandling.ExceptionHandlingExample1.method2(ExceptionHandlingExample1.java:15)
at com.in28minutes.exceptionhandling.ExceptionHandlingExample1.method1(ExceptionHandlingExample1.java:10)
at com.in28minutes.exceptionhandling.ExceptionHandlingExample1.main(ExceptionHandlingExample1.java:6)

Look at the stack trace. Exception which is thrown in method2 is propagating to method1 and then to main. This is because there is no exception handling in all 3 methods - main, method1 and method2

Exception Example 3: Execution of method stopped

Look at the example below: A println method call is added after every method call.

    public static void main(String[] args) {
method1();
System.out.println("Line after Exception - Main");
    }

    private static void method1() {
method2();
System.out.println("Line after Exception - Method 1");
    }

    private static void method2() {
String str = null;
str.toString();
System.out.println("Line after Exception - Method 2");
    }
//Output - Exception in thread "main" java.lang.NullPointerException
at com.in28minutes.exceptionhandling.ExceptionHandlingExample1.method2(ExceptionHandlingExample1.java:18)
at com.in28minutes.exceptionhandling.ExceptionHandlingExample1.method1(ExceptionHandlingExample1.java:12)
at com.in28minutes.exceptionhandling.ExceptionHandlingExample1.main(ExceptionHandlingExample1.java:7)

Note that none of the lines with text "Line after Exception - ****" are executed. If an exception occurs, lines after the line where exception occurred are not executed. Since all three methods main, method1() and method2() do not have any Exception Handling, exception propagates from method2 to method1 to main.

Exception Handling Example 4: Try catch block

Let's add a try catch block in method2

    public static void main(String[] args) {
method1();
System.out.println("Line after Exception - Main");
    }

    private static void method1() {
method2();
System.out.println("Line after Exception - Method 1");
    }

    private static void method2() {
try {
    String str = null;
    str.toString();
    System.out.println("Line after Exception - Method 2");
} catch (Exception e) {
    // NOT PRINTING EXCEPTION TRACE- BAD PRACTICE
    System.out.println("Exception Handled - Method 2");
}
    }

Output

Exception Handled - Method 2
Line after Exception - Method 1
Line after Exception - Main

Since Exception Handling is added in the method method2, the exception did not propogate to method1. You can see the "Line after Exception - **" in the output for main, method1 since they are not affected by the exception thrown. Since the exception was handled in method2, method1 and main are not affected by it. This is the main essence of exception handling. However, note that the line after the line throwing exception in method2 is not executed.

Few important things to remember from this example. 1.If exception is handled, it does not propogate further. 2.In a try block, the lines after the line throwing the exception are not executed.

Exception Handling Example 5: Need for Finally

Consider the example below: In method2, a connection is opened. However, because of the exception thrown, connection is not closed. This results in unclosed connections.

package com.in28minutes.exceptionhandling;

class Connection {
    void open() {
System.out.println("Connection Opened");
    }

    void close() {
System.out.println("Connection Closed");
    }
}

public class ExceptionHandlingExample1 {

    // Exception Handling Example 1
    // Let's add a try catch block in method2
    public static void main(String[] args) {
method1();
System.out.println("Line after Exception - Main");
    }

    private static void method1() {
method2();
System.out.println("Line after Exception - Method 1");
    }

    private static void method2() {
try {
    Connection connection = new Connection();
    connection.open();

    // LOGIC
    String str = null;
    str.toString();

    connection.close();
} catch (Exception e) {
    // NOT PRINTING EXCEPTION TRACE- BAD PRACTICE
    System.out.println("Exception Handled - Method 2");
}
    }
}

Output

Connection Opened
Exception Handled - Method 2
Line after Exception - Method 1
Line after Exception - Main

Connection that is opened is not closed. Because an exception has occurred in method2, connection.close() is not run. This results in a dangling (un-closed) connection.

Exception Handling Example 6 - Finally

Finally block is used when code needs to be executed irrespective of whether an exception is thrown. Let us now move connection.close(); into a finally block. Also connection declaration is moved out of the try block to make it visible in the finally block.

    private static void method2() {
Connection connection = new Connection();
connection.open();
try {
    // LOGIC
    String str = null;
    str.toString();

} catch (Exception e) {
    // NOT PRINTING EXCEPTION TRACE - BAD PRACTICE
    System.out.println("Exception Handled - Method 2");
} finally {
    connection.close();
}
    }

Output

Connection Opened
Exception Handled - Method 2
Connection Closed
Line after Exception - Method 1
Line after Exception - Main

Connection is closed even when exception is thrown. This is because connection.close() is called in the finally block. Finally block is always executed (even when an exception is thrown). So, if we want some code to be always executed we can move it to finally block.

Code in finally is NOT executed only in two situations. If exception is thrown in finally. If JVM Crashes in between (for example, System.exit()).

finally is executed even if there is a return statement in catch or try

private static void method2() {
Connection connection = new Connection();
connection.open();
try {
    // LOGIC    
    String str = null;
    str.toString();
    return;
} catch (Exception e) {
    // NOT PRINTING EXCEPTION TRACE - BAD PRACTICE
    System.out.println("Exception Handled - Method 2");
    return;
} finally {
    connection.close();
}
    }

Exception Handling Syntax

Let's look at a few quirks about Exception Handling syntax.

try without a catch is allowed

private static void method2() {
Connection connection = new Connection();
connection.open();
try {
    // LOGIC
    String str = null;
    str.toString();
} finally {
    connection.close();
}
    }

Output:

Connection Opened
Connection Closed
Exception in thread "main" java.lang.NullPointerException at com.in28minutes.exceptionhandling.ExceptionHandlingExample1.method2(ExceptionHandlingExample1.java:33) at com.in28minutes.exceptionhandling.ExceptionHandlingExample1.method1(ExceptionHandlingExample1.java:22) at com.in28minutes.exceptionhandling.ExceptionHandlingExample1.main(ExceptionHandlingExample1.java:17)

Try without a catch is useful when you would want to do something (close a connection) even if an exception occurred without handling the exception.

Try without both catch and finally is not allowed.

Below method would give a Compilation Error!! (End of try block)

    private static void method2() {
Connection connection = new Connection();
connection.open();
try {
    // LOGIC
    String str = null;
    str.toString();
}//COMPILER ERROR!!
    }

Exception Handling Hierarchy

Throwable is the highest level of Error Handling classes.

Below class definitions show the pre-defined exception hierarchy in Java.

//Pre-defined Java Classes
class Error extends Throwable{}
class Exception extends Throwable{}
class RuntimeException extends Exception{}

Below class definitions show creation of a programmer defined exception in Java.

//Programmer defined classes
class CheckedException1 extends Exception{}
class CheckedException2 extends CheckedException1{}

class UnCheckedException extends RuntimeException{}
class UnCheckedException2 extends UnCheckedException{}

Errors

Error is used in situations when there is nothing a programmer can do about an error. Ex: StackOverflowError, OutOfMemoryError.

Exception

Exception is used when a programmer can handle the exception.

Un-Checked Exception

RuntimeException and classes that extend RuntimeException are called unchecked exceptions. For Example: RuntimeException,UnCheckedException,UnCheckedException2 are unchecked or RunTime Exceptions. There are subclasses of RuntimeException (which means they are subclasses of Exception also.)

Checked Exception

Other Exception Classes (which don't fit the earlier definition). These are also called Checked Exceptions. Exception, CheckedException1,CheckedException2 are checked exceptions. They are subclasses of Exception which are not subclasses of RuntimeException.

Throwing RuntimeException in method

Method addAmounts in Class AmountAdder adds amounts. If amounts are of different currencies it throws an exception.

class Amount {
    public Amount(String currency, int amount) {
this.currency = currency;
this.amount = amount;
    }

    String currency;// Should be an Enum
    int amount;// Should ideally use BigDecimal
}

// AmountAdder class has method addAmounts which is throwing a RuntimeException
class AmountAdder {
    static Amount addAmounts(Amount amount1, Amount amount2) {
if (!amount1.currency.equals(amount2.currency)) {
    throw new RuntimeException("Currencies don't match");
}
return new Amount(amount1.currency, amount1.amount + amount2.amount);
    }
}

public class ExceptionHandlingExample2 {

    public static void main(String[] args) {
AmountAdder.addAmounts(new Amount("RUPEE", 5), new Amount("DOLLAR", 5));
    }

}

Output

Exception in thread "main" java.lang.RuntimeException: Currencies don't match
at com.in28minutes.exceptionhandling.AmountAdder.addAmounts(ExceptionHandlingExample2.java:17)
at com.in28minutes.exceptionhandling.ExceptionHandlingExample2.main(ExceptionHandlingExample2.java:28)

Exception message shows the type of exception(java.lang.RuntimeException) and the string message passed to the RuntimeException constructor("Currencies don't match");

Throwing Exception (Checked Exception) in method

Let us now try to change the method addAmounts to throw an Exception instead of RuntimeException. It gives us a compilation error.

class AmountAdder {
    static Amount addAmounts(Amount amount1, Amount amount2) {
if (!amount1.currency.equals(amount2.currency)) {
    throw new Exception("Currencies don't match");// COMPILER ERROR!// Unhandled exception type Exception
}
return new Amount(amount1.currency, amount1.amount + amount2.amount);
    }
}

All classes that are not RuntimeException or subclasses of RuntimeException but extend Exception are called CheckedExceptions. The rule for CheckedExceptions is that they should be handled or thrown. Handled means it should be completed handled - i.e. not throw out of the method. Thrown means the method should declare that it throws the exception

Throws Exception Example

Let's look at how to declare throwing an exception from a method.

class AmountAdder {
    static Amount addAmounts(Amount amount1, Amount amount2) throws Exception {
if (!amount1.currency.equals(amount2.currency)) {
    throw new Exception("Currencies don't match");
}
return new Amount(amount1.currency, amount1.amount + amount2.amount);
    }
}

Look at the line "static Amount addAmounts(Amount amount1, Amount amount2) throws Exception". This is how we declare that a method throws Exception. This results in compilation error in main method. This is because Main method is calling a method which is declaring that it might throw Exception. Main method again has two options a. Throw b. Handle

Code with main method throwing the exception below

    public static void main(String[] args) throws Exception {
AmountAdder.addAmounts(new Amount("RUPEE", 5), new Amount("DOLLAR", 5));
    }

Output

Exception in thread "main" java.lang.Exception: Currencies don't match
at com.in28minutes.exceptionhandling.AmountAdder.addAmounts(ExceptionHandlingExample2.java:17)
at com.in28minutes.exceptionhandling.ExceptionHandlingExample2.main(ExceptionHandlingExample2.java:28)

Handling an Exception

main can also handle the exception instead of declaring throws. Code for it below.

public static void main(String[] args) {
try {
    AmountAdder.addAmounts(new Amount("RUPEE", 5), new Amount("DOLLAR",5));
} catch (Exception e) {
    System.out.println("Exception Handled in Main");
}
    }

Output

Exception Handled in Main

Custom Defined Exception Classes

For the scenario above we can create a customized exception, CurrenciesDoNotMatchException. If we want to make it a Checked Exception, we can make it extend Exception class. Otherwise, we can extend RuntimeException class.

Extending Exception Class