» Primitive Data Types in Java
integers (int, short, byte, long), fractions (float, double), characters (char), boolean (boolean)
String - This is one more non-primitive data type to declare string
» Method Overloading in Java
Inside a class you can have methods with same name as long as they differ in terms of Data Type, Order and Number of Arguments
Example:
public class MethodOverloading { // Inside a class you can have methods with same name as long as they differ in // terms of Data Type, Order and Number of Arguments public void addition(int num1, int num2) { System.out.println(num1 + num2); } public void addition(double num1, double num2) { System.out.println(num1 + num2); } public void addition(int num1, double num2) { System.out.println(num1 + num2); } public void addition(double num1, int num2) { System.out.println(num1 + num2); } public void addition(int num1, int num2, int num3) { System.out.println(num1 + num2 + num3); } } public class MainClass(){ public static void main(String[] args){ MethodOverloading m1 = new MethodOverloading(); m1.addition(10,20); m1.addition(10.10,20.20); m1.addition(10,12.90); m1.addition(90.10,20); m1.addition(1,2,3); } }
» Access Specifiers in Java
- Public Access
- Default Access / No Access Specifier
- Private Access
- Protected Access
» Constructor in Java
A block of code similar to method which gets executed when an instance (object) of the class is created
Generally used to initialize member variables of a class
Constructor name is same as that of class name
Constructor does not have return type (It does not return any value)
Default Constructor - The default constructor is the no-argument constructor created automatically unless you define another constructor
If we don't create any constructor in class then Java assumes it as default constructor class
Constructor is not a member of class which means you cannot call it from another methods like you call any explicit methods in the class
Constructor Overloading: Inside a class you can have multiple constructors as long as they distinguish in terms of arguments either by data type of arguments or number of arguments
Example:
// Class 1 package oopsprogramming public class RoadToll { String type; int tiresCount; public void calculateToll() { if (tiresCount == 2) { System.out.println("Your toll amount is 0"); } else if (tiresCount == 4) { System.out.println("Your toll amount is 10"); } else if (tiresCount > 4) { System.out.println("Your toll amount is 20"); } else { System.out.println("Incorrect tire count"); } } public RoadToll () { System.out.println("Constructor is executed"); } public RoadToll (String type, int tiresCount) { this.type = type; this.tiresCount = tiresCount; } } // Class 2 package oopsprogramming public class RoadToll_Main { public static void main(String[] args) { Road_Toll rt1 = new RoadToll(); rt1.type = "Sadan"; rt1.tiresCount = 4; rt1.calculateToll(); Road_Toll rt2 = new RoadToll("Truck", 6); rt2.calculateToll(); } }
» 4 Pillers of Object Oriented Programming
Data Encapsulation
Polymorphism
Inheritance
Abstraction
» Data Encapsulation or Data Hiding
Data Encapsulation also called as Data Hiding
Hiding the member variables of a class from other classes
So in Data Encapsulation we make member variables as "private" and we create getters/setters of those member variables so that an external class can use getters and setters instead of accessing direct member variable of the class
Example:
// Class 1 package oops_concepts; public class Classroom { private String sub1; private int stdCount1; public String getSub() { return sub1; } public void setSub(String sub) { this.sub1 = sub; } public int getStdCount() { return stdCount1; } public void setStdCount(int stdCount) { this.stdCount1 = stdCount; } public void displayDetails() { System.out.println("This is a " + sub1 + " classroom and it has " + stdCount1 + " students"); } } // Class 2 package oops_concepts; public class Classroom_Main { public static void main(String[] args) { Classroom cs1 = new Classroom(); cs1.setSub("Math"); cs1.setStdCount(50); cs1.displayDetails(); } }
» Static keyword in Java
Static members (variables and methods) belong to a Class (type) and not to Object
The static keyword in Java is used for memory management mainly. We can apply static keyword with variables, methods, blocks and nested classes.
The static variable gets memory only once in the class area at the time of class loading which makes your program memory efficient (i.e. it saves memory)
More about static keyword: https://www.javatpoint.com/static-keyword-in-java
Example:
package oops_concepts; public class Static_Keyword { static String name = "ABC"; byte age = 6; String grade = "1st"; public void displayDetails() { System.out.println("Student " + name + " is " + age + " years old and in " + grade + " grade."); } public static void doThis() { System.out.println("Do This Executed"); } } package oops_concepts; public class Static_Main { public static void main(String[] args) { Static_Keyword sk1 = new Static_Keyword(); Static_Keyword sk2 = new Static_Keyword(); sk1.age = 7; sk1.grade = "2nd"; Static_Keyword.name = "Rob"; Static_Keyword.doThis(); sk2.age = 8; sk2.grade = "3rd"; Static_Keyword.name = "Shyam"; Static_Keyword.doThis(); sk1.displayDetails(); sk2.displayDetails(); } }
» What is JVM?
JVM is Java Virtual Machine
JVM is a baseline program which provide run-time environment to execute the Java code
JVM can be installed on any operating systems
» Main method in Java
public static void main(String[] args){ // your_code }
Whenever we execute a Java application the JVM looks for the main() method and executes it
It is the starting point of Java program
First main() method gets executed then it executes other methods
main() method is void because it doesn't return any value
main() method is scopewise "public" so that JVM can identify it easily
main() method is static so that for JVM it is easy to access it without creating any object of it
What is String[] args?
This is an array of strings
args stands for arguments. Whenever you try to run Java application (ex: from command line) and you want to pass some arguments then you can pass those in form of array of strings which you can utilise in your application
What is NoSuchMethodError:main?
Whenever we run Java program JVM looks for public static void main(string[] args) method and if it doesn't find it in program it throws NoSuchMethodError:main error and terminates the program
» Math Class in Java
Example:
package special_classes; public class MathClass { public static void main(String[] args) { /* * Math Class is provided in java.lang package. java.lang package provides * classes that are fundamental to the design of the Java programming language. * Math class contains methods for performing basic numeric * operations such as the elementary exponential, logarithm, * square root, and trigonometric functions. * * Math.min, Math.max, Math.E, Math.PI, * Math.pow, Math.sqrt, Math.cbrt * Math.ceil, Math.floor, Math.round * Math.sin, Math.cos, Math.tan, Math.log10 * Math.random */ int num1 = 100; int num2 = 200; System.out.println(Math.min(num1, num2)); System.out.println(Math.max(num1, num2)); System.out.println(Math.PI); System.out.println(Math.E); System.out.println(Math.pow(2, 10)); System.out.println(Math.sqrt(200)); System.out.println(Math.cbrt(300)); System.out.println("-------------------------"); double x = 12.98; double y = 10.24; System.out.println(Math.ceil(x)); System.out.println(Math.ceil(y)); System.out.println(Math.floor(x)); System.out.println(Math.floor(y)); System.out.println(Math.round(x)); System.out.println(Math.round(y)); System.out.println("-------------------------"); System.out.println(Math.random()); } }
» StringBuilder Class in Java
Example:
package special_classes; public class StringBuilderClass1 { public static void main(String[] args) { /* * StringBuilder class is used to create mutable (modifiable) string * Some useful methods: * append, insert, replace, delete, reverse */ String str1 = "I"; str1 = str1 + " Like"; str1 = str1 + " Java"; System.out.println(str1); //I / Like / I like / Java / I like Java StringBuilder sb1 = new StringBuilder("I"); sb1.append(" Like"); sb1.append(" Java"); System.out.println(sb1); } } public class StringBuilderClass2 { public static void main(String[] args) { /* * Some useful methods from StringBuilder * insert, replace, delete, reverse */ StringBuilder sb1 = new StringBuilder("Learning is fun"); sb1.insert(9, "Java "); System.out.println(sb1); //Output: Learning Java is fun sb1.replace(9, 13, "Everything"); System.out.println(sb1); //Output: Learning Everything is fun sb1.deleteCharAt(0); System.out.println(sb1); //Output: earning Everything is fun sb1.delete(2, 10); System.out.println(sb1); //Output: eaerything is fun sb1.reverse(); System.out.println(sb1); } }
» Scanner Class in Java
Scanner is a class used to accept the user input during execution of Java Program
Scanner class is from java.util.Scanner package so it requires to import java.util.Scanner to use it
Example:
import java.util.Scanner; public class ScannerClass { public static void main(String[] args) { Scanner s1 = new Scanner(System.in); System.out.println("Please enter a string:"); String str1 = s1.nextLine(); String temp = ""; for (int i = 0; i <= str1.length()-1; i++) { temp = str1.charAt(i) + temp; } System.out.println("Reversed String is: " + temp); } }
» Random Class in Java
To generate random numbers
From java.util.Random
Example:
import java.util.Random; public class RandomClass { public static void main(String[] args) { Random r1 = new Random(); for(int i = 1; i <= 100; i++) { System.out.println(r1.nextInt(100)); // output: 0 to 100 numbers System.out.println((int)(Math.random() * 40000) + 10000); // } } }
» UUID (Universal Unique Identifier) in Java
To generate UUID
Example:
import java.util.UUID; public class UUIDClass { public static void main(String[] args) { for(int i = 1; i <= 10; i++) { System.out.println(UUID.randomUUID()); } } }
» 'var' keyword in Java 10
You can use 'var' keyword in Java 10 to declare a variable
Based on the value assigned to variable Java will decide the type of it
Example:
Public class VarKeyword{ public static void main(String[] args){ var str = "abcd" // type String var num = 10 // type number var sc = new Scanner(System.in) // object } }
Limitations of 'var' in Java 10
You cannot use var keyword to create a variable at Class level
You cannot use var keyword to create a variable at Argument level
Example: public static int add(var a, var b) {} // => INCORRECT
You cannot use var keyword to declare array like this:
var myArr = {10,20,30} => Incorrect
var int [] myArr = {10,20,30} => Correct
» What is Garbage Collector in Java?
In Java Garbage means unreferenced objects
Garbage Collection is a way to destroy unused objects
Garbage collector is a program which run on JVM to get rid of the unused or unreferenced objects
Garbage collection is performed by a daemon thread called Garbage Collector(GC). This thread calls the finalize() method before object is garbage collected.
Java does it automatically unlike C & C++ which uses free() and delete() function respectively to do this task
It makes Java memory efficient
How can an object be unreferenced?
There are several ways like
By nulling the reference
Car c=new Car();= c=null;
By assigning a reference to another
Employee e1=new Employee(); Employee e2=new Employee(); e1=e2;//now the first object referred by e1 is available for garbage collection
By anonymous object
1 | new Department(); |
» What is finalize method in Java
The finalize() method is invoked each time before the object is garbage collected. This method can be used to perform cleanup processing. This method is defined in Object class as:
protected void finalize(){}» What is gc() method in Java
The gc() method is used to invoke the garbage collector to perform cleanup processing. The gc() is found in System and Runtime classes:
1 | public static void gc(){} |
Example of Garbage Collection in Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class TestGarbage{ public void finalize(){System.out.println("object is garbage collected");} public static void main(String args[]){ TestGarbage1 s1=new TestGarbage(); TestGarbage1 s2=new TestGarbage(); s1=null; s2=null; System.gc(); } } // output : // object is garbage collected // object is garbage collected |
» Inheritance in Java
Inheritance is an OOPS concept. In which one object acquires all the properties and behaviors of a parent object.
Ex. Class1 (parent) and Class2 (child)
When you inherit from an existing class, you can reuse methods and fields of the parent class. Moreover, you can add new methods and fields in your current class also
The extends keyword indicates that you are making a new class that derives from an existing class. The meaning of "extends" is to increase the functionality.
» Types of Inheritance in Java
1. Single
2. Multilevel
3. Hierarchical
In java programming, multiple and hybrid inheritance is supported through interface only.
» Single Inheritance
When a class inherits another class, it is known as a single inheritance.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 | class Animal{ void eat(){System.out.println("eating...");} } class Dog extends Animal{ void bark(){System.out.println("barking...");} } class TestInheritance{ public static void main(String args[]){ Dog d=new Dog(); d.bark(); d.eat(); }} |
» Multilevel Inheritance
When there is a chain of inheritance, it is known as multilevel inheritance.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class Animal{ void eat(){System.out.println("eating...");} } class Dog extends Animal{ void bark(){System.out.println("barking...");} } class BabyDog extends Dog{ void weep(){System.out.println("weeping...");} } class TestInheritance2{ public static void main(String args[]){ BabyDog d=new BabyDog(); d.weep(); d.bark(); d.eat(); }} |
» Method Overriding
If the parent class and child class has exact method with same name, same arguments the method from child class overrides the method from parent class. i.e. child overrides the parent
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // PARENT CLASS package inheritance; public class Animal { public void whoAmI() { System.out.println("I am an Animal"); } } // CHILD CLASS 1 package inheritance; public class Dog extends Animal{ public void whoAmI() { System.out.println("I am Dog"); } } // CHILD CLASS 2 package inheritance; public class Cat extends Animal{ public void whoAmI() { System.out.println("I am Cat"); } } // MAIN CLASS package inheritance; public class Animal_Main { public static void main(String[] args) { Dog d = new Dog(); d.whoAmI(); Cat c = new Cat(); c.whoAmI(); } } // OUTPUT: I am Dog I am Cat |
» Polymorphism in Java
Poly means Many
Morphs means Forms
So Polymorphism means Many Forms
So Polymorphism in Java means an ability of Object to get into multiple forms. It is a concept to performs single action in different ways.
There are two types of Polymorphism in Java 1) Compile-time and 2) Runtime
If you overrload a static method in Java, it is example of compile time Polymorphism.
Lets see Run Time polymorphism
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <p>Example:</p> // PARENT CLASS package inheritance; public class Animal { public void whoAmI() { System.out.println("I am an Animal"); } } // CHILD CLASS 1 package inheritance; public class Dog extends Animal{ public void whoAmI() { System.out.println("I am Dog"); } } // CHILD CLASS 2 package inheritance; public class Cat extends Animal{ public void whoAmI() { System.out.println("I am Cat"); } } // MAIN CLASS package inheritance; public class Animal_Main { public static void main(String[] args) { Animal a; a = new Animal(); a.whoAmI(); // output: I am an Animal a = new Dog(); a.whoAmI(); // output: I am Dog a = new Cat(); a.whoAmI() // output: I am Cat } } |
So here object a which we instantiated from Animal class (Parent class) is acting for Dog and Cat classes (Child classes) and calling their method.
Which simply means one object has many forms i.e. Polymorphism
More info on this: https://www.javatpoint.com/runtime-polymorphism-in-java
» Super keyword in Java
The keyword super denotes Super Class
Lets see this by example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package super_keyword; public class Parent { int num1 = 100; public void showInfo() { System.out.println("Show Info of Parent"); } } package super_keyword; public class Child extends Parent{ int num1 = 200; public void showInfo() { System.out.println("Show Info of Child"); } public void getSomeMoreInfo() { System.out.println(num1); // the output will be 200 showInfo(); // the output will be "Show Info of Child" super.showInfo(); // the output will be "Show Info of Parent" System.out.println(super.num1); // the output will be 100 } } |
Here we have same method i.e. showInfo() on Parent class & Child Class.
We have one more method in child class i.e. getSomeMoreInfo() and we want to call showInfo() method of Parent from getSomeMoreInfo()
If we directly call showInfo() then it will print "Show Info of Child" which we doesn't want.
So here come the super keyword, using super.showInfo() we can call the method of Parent Class.
» Super Class Constructor in Java
In Parent class if we have a constructor with argument(s) then it's subclass has to create a constructor with an argument which must call parent's constructor
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package super_keyword; public class Class1 { int num1; public Class1(int a) { num1 = a; } } package super_keyword; public class Class2 extends Class1{ int num2; public Class2(int a, int b) { super(a); num2 = b; } } |
Now if Parent class still provide a no argument constructor then it's not mandatory for subclass to create a constructor
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package super_keyword; public class Class1 { int num1; public Class1(int a) { num1 = a; } public Class1(){ // one more constructor // ... } } package super_keyword; public class Class2 extends Class1{ int num2; // now here subclass doesn't require any constructor } |
» Protected Access in Java
When we create a variable or method as protected then it can be accessed in the same class, in the same package and in the classes of outside package as well when you inherit from it. If you do not inherit then you can't use it.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package package1; public class Car { private String model; int year; protected String make; public String color; } package package1; public class GermanCar extends Car { public void setCarDetails() { model = "X6"; // ERROR: model is private so we cannot use it anywhere expect same class year = 2021; // default is accessible in child make = "BMW"; // protected is accessible in child color = "Red"; // public is accessible anywhere } } package package2; import package1.Car; public class FrenchCar extends Car { public void setCarDetails() { model = "X6"; // ERROR: model is private so we cannot use it anywhere expect same class year = 2021; // ERROR: year is default so we cannot access it outside of package make = "BMW"; // protected is accessible in child because we are inheriting Parent i.e. Car color = "Red"; // public is accessible anywhere } } |
» Abstraction in Java
Abstraction is the process of hiding implementation details from user and provide only the functionalities. Let user know what method they are using and no need to provide the details of it as what code is getting executed as part of those methods.
Abstraction can be done in two ways:
1) By Abstract Classes and Abstract Methods
2) By creating Interfaces
» Abstraction in Java by Abstract Classes and Abstract Methods
Abstract class is a kind of guidline class which provide functional details of class and not the implementation details. The implementation details resides in subclass of it.
Abstract class is a class which cannot be instantiated. You cannot create object of it.
We use keyword 'abstract' to create abstract class.
Example: public abstract class Mobile
Use of abstract class is just to extend it by it's child class and implement an unimplemented methods in the child class.
We can just define methods in abstract class and provide the implementation in subclass.
We can create abstract method in abstract class but subclass must have to provide its implementation.
We can create non-abstract method in abstract class and subclass has no compultion to provide its implementation.
We use keyword 'abstract' to create abstract method.
Example: public abstract void doubleTap()
Example of Abstract class and method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package abstraction; public abstract class Mobile { public abstract void doubleTap(); // its abstract method so you must have to implement it in subclasses public void abc() { System.out.println("I am abc"); } } package abstraction; public class Android extends Mobile{ @Override public void doubleTap() { // implementing parent class method here o/w it will throw an error System.out.println("Android Double Tap"); } } package abstraction; public class IOS extends Mobile{ @Override public void doubleTap() { // implementing parent class method here o/w it will throw an error System.out.println("IOS Double Tap"); } } |
» Abstraction in Java by creating Interfaces
Interface is collection of abstract methods. The method which does not have any implementation
We don't require any abstract keyword to declare class in interface.
Class can implement an Interface
Interface can extend from another Interface
Non abstract class extending from an abstract class needs to implement unimplemented methods
Non abstract class implementing an interface needs to implement unimplemented methods
Interface can not be instantiated
Interface does not have a constructor
All methods in interface are by default abstract (without implementation) except static and default methods. We don't require any abstract keyword for methods in interface.
Interface cannot have data members. All variables by default considered as Static and Final
Everything is public by default
Example:
- Creating Interface
1 2 3 | public interface Interface1{ void doThis(); } |
- Class can implement an Interface
public class Class1 implements Interface1{ // implementing Interface1 in Class1 @Override public void doThis(){ // to do... } }
- Interface can extend from another Interface
public interface Interface2 extends Interface1{ // extending Interface1 in Interface2 void doThat(); }
- Create Class2 which implements Interface2
public class Class2 implements Interface2{ // Here both the methods of Interface1 and Interface2 has to implement in Class because Interface2 is child of Interface1 (or extending Interface1) @Override public void doThis(){ // to do... } @Override public void doThat(){ // to do... } }
- Unlike class we can have Multiple Inheritance is possible in case of interfaces.
public class Class3 implements Int1, Int2 { @Override public void doThat() { // TODO Auto-generated method stub } @Override public void doThis() { // TODO Auto-generated method stub } }
- Abstract class doesn't need to implement interface methods
public class Class2 implements Interface1{ // so here we are not implementing Interface1 methods because we made Class2 an Abstract class }Non abstract class extending from an abstract class needs to implement unimplemented methods
Non abstract class implementing an interface needs to implement unimplemented methods
Example: Take above Class2 and add one abstract method to it
public class Class2 implements Interface2{ public abstract void abc(); }
Lets create Class4
public class Class4 extends Class2{ // because Class2 has method doThat() which has to implement @Override public void doThat() { // TODO Auto-generated method stub } // because Class2 has parent interface which Interface1 which has doThis() so has to implement @Override public void doThis() { // TODO Auto-generated method stub } // because Class2 is abstract class and abstract method so has to implement @Override public void abc() { // TODO Auto-generated method stub } }
- static and default methods in interface (in Java version 8+ only)
Child can override the default methods
Child cannot override the static methods
Lets go to Interface1 and add some static & default methods
public interface Interface1 { static final int a = 100; void doThis(); static void xyz() { System.out.println("Static xyz from Int1"); } default void uvw() { System.out.println("Default uvw from Int1"); } }
Interface cannot have data members (variables)
All the members must be static final
Example:
public interface Interface1{ int num = 100; // NOT ALLOWED this will throw error static final int num = 100; // ALLOWED }
» Final Keyword in Java
Final Field is used to create a Constant.
Final Method is used to avoid Method Overriding. Subclass cannot override it.
Final Class is used to prevent Inheritance. Final class cannot be inherited. You cannot create subclass of final class.
public class Final_Keyword { final int A = 100; // it becomes constant final void abc() { // cannot override System.out.println("Final method abc"); } }
» ArrayList vs LinkedList in Java
ArrayList:
In ArrayList we have index associated with every item so its internally implements dynamic array.
Accessing value is faster than LinkedList because we can use index of an element.
Inserting or deleting something inside the ArrayList is slower becauase when we do that then shift in the memory happens which makes it slower
LinkedList:
In LinkedList its internally implements the doubly linked list so elements are not adjecent to each other they are at random location but they connected to each other using pointers.
Accessing value is slower than ArrayList because always have to start from HEAD and then go to the next element till TAIL hence its time consuming.
Inserting or deleting something inside the LinkedList is faster as compared to ArrayList because of Pointers we can break the current link and add the new link in between of it.
» Hash Set in Java
A Set is a collection (like List) that cannot contain duplicate elements.
HashSet doesn't maintain insertion order.
No positional access of elements.
Example:
import java.util.ArrayList; import java.util.HashSet; public class Hash_Set { public static void main(String[] args) { /* * Set is a Collection that cannot contain duplicate elements. * HashSet does not maintain insertion order * No positional access of elements * Basic Methods: add, remove, clear, contains, size, * Union and Intersection of Sets * Convert set to list */ var set1 = new HashSet<Integer>(); set1.add(10); set1.add(20); set1.add(30); set1.add(40); set1.add(50); set1.add(10); set1.add(20); var set2 = new HashSet<Integer>(); set2.add(10); set2.add(40); set2.add(50); set2.add(70); set2.add(80); //set1.addAll(set2); // UNION //set1.retainAll(set2); // INTERSECTION //System.out.println(set1); //set1.remove(20); // Convert SET to LIST var list1 = new ArrayList<Integer>(); list1.addAll(set1); System.out.println(list1); } }
» Linked Hash Set in Java
A LinkedHashSet is an ordered version of HashSet .
LinkedHashSet maintains a doubly-linked List across all elements.
No positional access of elements.
Example:
import java.util.LinkedHashSet; public class Linked_Hash_Set { public static void main(String[] args) { LinkedHashSet<Character> set1 = new LinkedHashSet<Character>(); set1.add('a'); set1.add('e'); set1.add('i'); set1.add('o'); set1.add('u'); System.out.println(set1); } }
» TreeSet in Java
TreeSet is an important implementation of the SortedSet interface.
Elements are sorted in ascending order by default.
No positional access of elements.
Example:
import java.util.Set; import java.util.TreeSet; public class Tree_Set { public static void main(String[] args) { Set<String> set1 = new TreeSet<String>(); set1.add("Rob"); set1.add("Bob"); set1.add("Andy"); set1.add("Charls"); System.out.println(set1); var set2 = new TreeSet<Integer>(); set2.add(100); set2.add(10); set2.add(50); set2.add(2); set2.add(0); System.out.println(set2); } }
» Iterating over Set in Java
We can use special for loop or an Iterator.
Example:
import java.util.HashSet; import java.util.Iterator; import java.util.TreeSet; public class Iterating_Over_Set { public static void main(String[] args) { /* * Special For Loop * Iterator */ HashSet<Integer> set1 = new HashSet<Integer>(); set1.add(100); set1.add(200); set1.add(300); set1.add(400); set1.add(500); for(int x : set1) { System.out.println(x); } var set2 = new TreeSet<String>(); set2.add("Sun"); set2.add("Mon"); set2.add("Tue"); set2.add("Wed"); set2.add("Thu"); System.out.println("---------------"); Iterator it1 = set2.iterator(); while(it1.hasNext()) { System.out.println(it1.next()); } } }
» HashMap in Java
HashMap provides the basic implementation of Map interface.
It stores data in Key and Value pairs
We can access a value using the corresponding key
Methods: put, get, containsKey, remove, clear, keySet, values, entrySet
Example:
import java.util.HashMap; public class Hash_Map { public static void main(String[] args) { var map = new HashMap<Integer,String>(); map.put(1, "Jan"); map.put(0, "Feb"); map.put(3, "Mar"); map.put(4, "Apr"); map.put(5, "May"); System.out.println(map.get(2)); System.out.println(map.containsKey(3)); map.put(4, "Dec"); //map.remove(4); //map.remove(5, "May1"); //map.clear(); // convert map to set var set1 = map.keySet(); // by key var set2 = map.values(); // by value var set3 = map.entrySet(); // by both key and value System.out.println(set1); System.out.println(set2); System.out.println(set3); System.out.println(map); } }
» TreeMap in Java
TreeMap is implemented based on red-black tree structure.
It is sorted by keys.
Example:
import java.util.TreeMap; public class Tree_Map { public static void main(String[] args) { /* * Methods: put, get, containsKey, containsValue, replace, remove, clear, keySet, values, entrySet * firstEntry,lastEntry,firstKey,lastKey,pollFirstEntry,pollLastEntry */ var map = new TreeMap<Character, Integer>(); map.put('d', 1000); map.put('a', 2000); map.put('c', 3000); map.put('b', 4000); System.out.println(map); // output : {a=2000, b=4000, c=3000, d=1000} //System.out.println(map.containsValue(30000)); //map.put('a', 6000); //map.replace('a', 6000); //System.out.println(map.firstEntry()); //System.out.println(map.lastEntry()); //System.out.println(map.firstKey()); //System.out.println(map.lastKey()); //System.out.println(map.pollFirstEntry()); //System.out.println(map.pollLastEntry()); System.out.println(map); } }
» Iterating over Map
Example:
import java.util.Iterator; import java.util.Map.Entry; import java.util.TreeMap; public class Map_Iterating { public static void main(String[] args) { var map = new TreeMap<Integer,Double>(); map.put(10, Math.sqrt(10)); map.put(30, Math.sqrt(30)); map.put(50, Math.sqrt(50)); map.put(40, Math.sqrt(40)); map.put(20, Math.sqrt(20)); /* for(Entry<Integer, Double> e : map.entrySet()) { System.out.println(e); } */ Iterator it = map.entrySet().iterator(); while(it.hasNext()) { System.out.println(it.next()); } } }
» Exception Handling in Java
What is an Exception: Exceptions are events which occur during the execution of programs that disrupt the normal flow of the application. e.g. String Index Out Of Boundaries, Dividing by Zero, Null Pointer
String x = null; System.out.println(x.length); // exception System.out.println(100/0); // exception
What is Exception Object: An exception object is an instance of an exception class. It gets created and handed over to the Java runtime when an exceptional event occurs. Exception Object contains:
1) Information about the error
2) State of the program
Error vs Exception
Error : A serious problems that a reasonable application should not try to catch e.g. OutOfMemoryError, StackOverflowError, LinkageError
Exception : A conditions that a reasonable application might want to catch e.g. FileNotFoundException, ParseException, SQLException
Throwable : The Throwable class is the superclass of all errors and exceptions
Checked Exceptions vs Unchecked Exceptions
Lets see this by example
import java.io.FileInputStream; public class CheckedVsUnchecked { public static void main(String[] args) { /* * Checked Exceptions : Checked at Compile-Time * e.g. IOException, SQLException, ClassNotFoundException etc. * * Unchecked Exceptions (RunTime Exceptions): Checked at Run-Time * e.g. ArithmeticException, ArrayIndexOutOfBoundsException etc. */ // checked exception FileInputStream x = new FileInputStream("Users/md/Downloads/abc.txt"); // unchecked exception System.out.println(100/0); } }
Try, Catch and Finally by Example:
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class Try_Catch { public static void main(String[] args) { // If there is an exception in test() function still the system.out.println // will execute because we handling an exception there. test(); System.out.println("Hello World"); } public static void test() { try { FileInputStream x = new FileInputStream("Users/md/Downloads/abc.txt"); } catch (FileNotFoundException e) { System.out.println(e.getMessage()); System.out.println(e.toString()); e.printStackTrace(); } catch (IOException e2) { // We can have multiple catch block System.out.println(e2.getMessage()); } catch (Exception e1) { System.out.println(e1.getMessage()); } } }
public class Finally_Block { public static void main(String[] args) { /* * The finally block gets executed after successful run of the * try block or after one of the catch blocks handled an exception */ int num1 = 100; int num2 = 10; try { int num3 = num1 / num2; } finally { System.out.println("Finally Block Code"); } } }
» Design Patterns in Java
Creational Design Pattern: Factory Pattern, Builder Pattern, Singleton Pattern etc..
Structural Design Pattern: Proxy Pattern, Adaptor Pattern etc..
Behavioral Design Pattern: Observer Pattern, State Pattern, Iterator Pattern etc..
» Singleton Design Patterns in Java
The Singleton design pattern ensures that a class has only one instance and provides a global point of access to that instance. In simpler terms, it's like having just one unique object of a particular class throughout the entire application
Imagine a company has a printer. Now, instead of having multiple printers wasting resources, they decide to implement the Singleton pattern. This means they'll have just one printer for the entire office.
Initialization: The first time anyone in the office tries to use the printer, the system initializes it, and from then on, everyone uses the same printer instance.
Access: Whenever an employee wants to print something, they don’t have to get a new printer; they just use the existing one because it’s a Singleton.
public class Printer { // Step 1: Create a private static instance of the class. private static Printer printerInstance; // Step 2: Make the constructor private so that it cannot be instantiated from outside. private Printer() {} // Step 3: Provide a method to access the single instance. public static Printer getInstance() { if (printerInstance == null) { printerInstance = new Printer(); } return printerInstance; } // Other methods of the Printer class go here. public void printDocument(String document) { System.out.println("Printing: " + document); } } // Usage public class Main { public static void main(String[] args) { // Get the singleton instance Printer myPrinter = Printer.getInstance(); // Print using the singleton instance myPrinter.printDocument("Sample Document"); // Try to get another instance, but it's the same one Printer anotherPrinter = Printer.getInstance(); anotherPrinter.printDocument("Another Sample Document"); } }
In this example, no matter how many times you try to get an instance of the Printer class using the getInstance() method, you will always get the same instance. This ensures that there's only one printer object throughout the application.
In this example, the primary goal is to ensure that only one instance of the Printer class exists throughout the application's lifecycle. If you attempt to create another instance of the Printer class using its constructor directly, you'll encounter a compilation error because the constructor is marked as private.
// Step 2: Make the constructor private so that it cannot be instantiated from outside. private Printer() {}
When a constructor is marked as private, it means that you cannot create an instance of that class from outside the class itself. This ensures that other parts of the code cannot instantiate the class using the new keyword.
If you try to do something like this like Attempting to Create Another Instance:
Printer anotherPrinterInstance = new Printer(); // This will give a compilation error.
You'll get a compilation error because the Printer constructor is not accessible from outside the class due to its private access modifier.
To get an instance of the Printer class, you have to use the getInstance() method, which is the only way to access the single instance of the Printer class:
Printer myPrinter = Printer.getInstance(); // This will give you the singleton instance.
In this way, the Singleton pattern ensures that there's only one instance of the Printer class throughout the application, and you cannot create another instance of it using its constructor directly.
The Singleton design pattern is useful in situations where you need to ensure that a class has only one instance and that instance is accessible globally within an application. Here are some common use cases and scenarios where you might use the Singleton pattern:
Logging, Database Management, Caching, Configuration Management
Key Considerations:
While the Singleton pattern provides a convenient way to manage global state and resources, it's essential to use it judiciously. Overuse of the Singleton pattern can lead to tightly coupled code, which can make unit testing difficult and reduce the flexibility of your application.
Additionally, when implementing the Singleton pattern, you should consider thread safety, especially in multi-threaded environments. In Java, you can use various techniques like lazy initialization with double-checked locking or static initialization to ensure thread safety when creating the Singleton instance.
How would you break singleton design pattern?
Reflection: Using Java's reflection API, you can access the private constructor of a Singleton class and create a new instance. Here's a simple code snippet demonstrating this:
public class Singleton { private static Singleton instance; private Singleton() { // Private constructor } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } public class Main { public static void main(String[] args) { try { // Using reflection to create a new instance of Singleton Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); Singleton newInstance = constructor.newInstance(); // Now, you have broken the Singleton pattern } catch (Exception e) { e.printStackTrace(); } } }
» Factory Design Pattern in Java
The Factory design pattern in Java is a creational pattern that provides an interface for creating objects, but allows subclasses to alter the type of objects that will be instantiated. In simpler terms, it's like a factory that produces different types of products based on the request it receives.
Let's take the example of a pizza restaurant. A pizza restaurant can produce different types of pizzas like Margherita, Pepperoni, and Veggie. Instead of having separate methods or classes for creating each type of pizza, the restaurant uses a Pizza Factory.
Factory Interface: Define an interface called Pizza with a method preparePizza.
interface Pizza { void preparePizza(); }
Concrete Implementations: Create concrete classes for different types of pizzas.
class Margherita implements Pizza { @Override public void preparePizza() { System.out.println("Preparing Margherita Pizza..."); } } class Pepperoni implements Pizza { @Override public void preparePizza() { System.out.println("Preparing Pepperoni Pizza..."); } } class Veggie implements Pizza { @Override public void preparePizza() { System.out.println("Preparing Veggie Pizza..."); } }
Pizza Factory: Implement a Pizza Factory that produces different types of pizzas based on the request.
class PizzaFactory { public Pizza createPizza(String type) { if ("Margherita".equalsIgnoreCase(type)) { return new Margherita(); } else if ("Pepperoni".equalsIgnoreCase(type)) { return new Pepperoni(); } else if ("Veggie".equalsIgnoreCase(type)) { return new Veggie(); } return null; } }
Usage: Use the Pizza Factory to create different types of pizzas.
public class Main { public static void main(String[] args) { PizzaFactory pizzaFactory = new PizzaFactory(); Pizza margherita = pizzaFactory.createPizza("Margherita"); margherita.preparePizza(); Pizza pepperoni = pizzaFactory.createPizza("Pepperoni"); pepperoni.preparePizza(); Pizza veggie = pizzaFactory.createPizza("Veggie"); veggie.preparePizza(); } }
In this example, the Pizza Factory acts as a centralized place to create different types of pizzas. Based on the type of pizza requested, the factory produces the corresponding pizza, demonstrating the Factory design pattern in action.
Key Benefits:
Flexibility: The Factory pattern provides flexibility by allowing you to create objects without specifying the exact class of object that will be created.
Encapsulation: It encapsulates the object creation logic, making it easier to manage and extend in the future.
Code Reusability: You can reuse the factory to create multiple instances of objects without duplicating the creation code.
» How do you create microservices in in Spring boot?
In today's digital era, microservices architecture offers scalability and flexibility. Let's explore how to create a simple e-commerce app using Spring Boot with three microservices: Product, Order, and User.
Setup Spring Boot Projects
Generate three Spring Boot projects using Spring Initializr: product-service, order-service, and user-service.
Define Dependencies
Add Spring Web, Spring Data JPA, and H2 Database dependencies to each project's pom.xml.
Create Microservice Components
Product Service
Create a Product entity, repository, and controller for CRUD operations.
@Entity public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String description; private BigDecimal price; // Getters and Setters }
Repository: Create a ProductRepository interface extending JpaRepository
@Repository public interface ProductRepository extends JpaRepository<Product, Long> { }
Controller: Implement a RESTful API to fetch products.
@RestController @RequestMapping("/api/products") public class ProductController { @Autowired private ProductRepository productRepository; @GetMapping public List<Product> getAllProducts() { return productRepository.findAll(); } }
Order Service: Similar to the Product Service, create entities (Order, OrderItem) and corresponding repositories and controllers.
User Service: Define a User entity and implement CRUD operations using repositories and controllers.
Configure Service Communication:
Use Spring Cloud Eureka for service registration and discovery. Each microservice registers itself with the Eureka server and looks up dependencies via service names.
Implement Service Interactions:
In the Order Service, when creating an order, call the Product Service to verify product availability and User Service to validate user details.
Dockerize Microservices
Create Dockerfile for each microservice to containerize them. Use docker-compose to manage multi-container Docker applications, defining services, networks, and volumes.
Implement Circuit Breakers
Use Spring Cloud Circuit Breaker (Hystrix) to handle failures gracefully between service interactions.
Build and Deploy
Build Docker images for each microservice and deploy them to a container orchestration platform like Kubernetes or cloud platforms (AWS, Azure, Google Cloud).» Explain the components (entities, repositories, controllers) for each microservice. Explain how service communication is implemented using Spring Cloud Eureka and service interactions between microservices.
Components of Microservices:
Entities:
Think of entities as the blueprint for your data. In our e-commerce example, each microservice has its data structure:
Product Service: Defines what a product looks like (e.g., name, price).
Order Service: Defines what an order looks like (e.g., items, customer details).
User Service: Defines what a user looks like (e.g., username, email).
Repositories:
Repositories are like specialized libraries where you can save, retrieve, and manage your data.
Product Repository: This is where you'd save, fetch, or delete product information.
Order Repository: This is where you'd handle saving and fetching order details.
User Repository: This manages user-related tasks like saving user details.
Controllers:
Controllers act as traffic controllers for your application, deciding what to do when you receive a request.
Product Controller: When someone wants product info, this controller handles it.
Order Controller: This manages requests related to orders, like placing a new order.
User Controller: When someone needs user details or wants to create a new user, this controller takes care of it.
Service Communication using Spring Cloud Eureka:
Imagine you have three friends, and instead of calling their names whenever you want something, you use a directory or a phonebook to find them easily. Spring Cloud Eureka acts like that phonebook for your microservices.
Registering Services: Each microservice registers itself with Eureka when it starts up. It's like adding your friend's contact details to the phonebook.
Discovering Services: If one microservice needs to talk to another, instead of hardcoding the location, it asks Eureka where the other service is. This way, it can find and communicate with other services without guessing their locations.
Service Interactions Between Microservices:
Imagine you're buying a product online. Multiple things need to happen behind the scenes:
You browse products (Product Service).
You add products to the cart and place an order (Order Service).
You might need to log in or create an account (User Service).
These services don't work in isolation; they talk to each other:
Order Service might ask the Product Service if a product is available when you try to buy it.
When creating an order, the Order Service might check with the User Service to ensure you're a valid user.
This interaction is crucial because each microservice handles its specialized task, but they work together to provide a seamless experience for you, the user. And Spring Cloud Eureka ensures they find and communicate with each other easily.
» Are enums Singleton in Java?
Yes, enums in Java are effectively singletons because they ensure only one instance of each enum constant exists within the JVM. This inherent feature makes enums a recommended choice when you need a singleton instance with a predefined set of values.
When you declare an enum type, Java ensures that only one instance of each enum constant exists within the JVM for that enum type. This makes enums a convenient and safe way to implement the Singleton pattern in Java.
Why Enums are Singleton?
Single Instances: Each enum constant is instantiated once when the enum type is loaded by the JVM. For example, if you have an enum Color with constants RED, GREEN, and BLUE, there will be only one instance of RED, GREEN, and BLUE throughout your application.
Instance Control: You cannot create instances of an enum using new, ensuring that the instances are controlled and limited to those defined by the enum constants.
Thread Safety: Enums are inherently thread-safe due to their static nature. The instances are created statically when the enum class is initialized.
public enum Color { RED, GREEN, BLUE }
Here, Color.RED, Color.GREEN, and Color.BLUE are instances of the Color enum. They are singletons because Java guarantees that each enum constant is instantiated only once.
» Why do we use readResolve in singletons
When you serialize and then deserialize a Singleton object in Java, the default behavior of deserialization is to create a new instance of the class. This breaks the Singleton pattern because now you have multiple instances of what should be a single instance class.
How readResolve() Solves This Problem?
By implementing the readResolve() method in your Singleton class, you can control what object should be returned after deserialization. This method allows you to enforce that only the original Singleton instance is returned, regardless of how many times the Singleton object is serialized and deserialized.
readResolve() is invoked by the serialization mechanism after the object has been deserialized. You can use this method to return the existing Singleton instance, thereby preserving the Singleton pattern.
import java.io.Serializable; public class Singleton implements Serializable { private static final long serialVersionUID = 1L; // Private constructor to prevent instantiation from outside private Singleton() {} private static class SingletonHelper { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonHelper.INSTANCE; } // Method to ensure Singleton integrity during deserialization protected Object readResolve() { return getInstance(); // Returns the existing instance } }
Private Constructor: Prevents instantiation of the Singleton class from outside.
SingletonHelper Class: Nested static class that holds the Singleton instance.
getInstance() Method: Provides access to the Singleton instance.
readResolve() Method: Overrides the default deserialization behavior. It returns the existing Singleton instance instead of allowing the creation of a new one.
» How to create an immutable class in Java?
Creating an immutable class in Java ensures that once an object is created, its state cannot be changed.
package com.example.immutableclass; import java.util.Collections; import java.util.List; import java.util.ArrayList; public final class ImmutableClassExample { private final int id; private final String name; private final List<String> emails; // Mutable field // Constructor initializes all fields public ImmutableClassExample(int id, String name, List<String> emails) { this.id = id; this.name = name; if (emails == null) { this.emails = Collections.emptyList(); // Immutable empty list } else { this.emails = new ArrayList<>(emails); // Defensive copy } } // Getter methods for all fields, no setters public int getId() { return id; } public String getName() { return name; } // Return a defensive copy of mutable field public List<String> getEmails() { return new ArrayList<>(emails); } public static void main(String[] args) { // Example usage List<String> emails = new ArrayList<>(); emails.add("john.doe@example.com"); emails.add("jane.smith@example.com"); ImmutableClassExample immutableObj = new ImmutableClassExample(1, "John Doe", emails); System.out.println("ID: " + immutableObj.getId()); System.out.println("Name: " + immutableObj.getName()); System.out.println("Emails: " + immutableObj.getEmails()); } }
final Class: Prevents subclassing.
private final Fields: Ensures fields cannot be changed after object creation.
Constructor: Initializes all fields, including making a defensive copy of any mutable objects passed to it.
No Setter Methods: No methods that modify the state of the object.
Getter Methods: Provide read-only access to fields, with defensive copies of any mutable fields.
Creating immutable classes in Java involves making the class final, using private final fields, initializing all fields via the constructor, and ensuring no setter methods are provided. This guarantees that objects of the class cannot be modified after creation, promoting better code reliability and thread safety in Java applications.
No comments:
Post a Comment