You are on page 1of 172

What will be the output of the following Java code?

class Output
{
public static void main(String args[])
{
Integer i = new Integer(257);
byte x = i.byteValue();
System.out.print(x);
}
}

a.
1

b.
256

c.
0

d.
257

ChatGPT
The correct answer is:

a. 1

Explanation:

In the given Java code:

An Integer object i is created with a value of 257.


i.byteValue() converts the Integer to a byte value. The result is 1 because the
byte range in Java is from -128 to 127, and 257 modulo 256 is 1.
Therefore, the output of the code is "1" (option a).

What type of members are not serialized?

a.
Private

b.
Throwable

c.
Static

d.
Protected

ChatGPT
c. Static

Explanation:

In Java, during the serialization process, static members (fields or methods) are
not serialized. The transient keyword is used to indicate that a field should not
be serialized, and it is another way to exclude members from the serialization
process. Private, protected, and public members can all be serialized, but their
behavior may depend on other factors such as the transient modifier. Throwable is a
class in Java, and the question seems to be about class members rather than classes
themselves.

Q16)What will be the output of the following program?

public class Test1 {


static{
System.out.println("Static");
}
{
System.out.println("Instance");
}
public void Test1(){
System.out.println("Constructor");
}
public static void main(String[] args) {
Test1 t = null;
}
}
A1 Instance Static
A2 Static Instance
A3 Static
A4 Static Instance Constructor

ChatGPT
The correct output will be:

A3 Static

Explanation:

The static block (static{}) is executed when the class is loaded. So, "Static" will
be printed first.
The instance block ({}) is executed when an instance of the class is created.
However, in the main method, an instance of the class is not created (Test1 t =
null;), so the instance block will not be executed in this context.
The constructor public void Test1() is not a valid constructor because the class
name and constructor name should match. The correct constructor should be public
Test1(). Since the constructor is not invoked in the main method, "Constructor"
will not be printed.
Therefore, the correct output is:

A2 Static

java
Copy code
String s1 = new String("hi");
String s2 = "hi";
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
In this code:
s1 == s2: This checks if s1 and s2 refer to the same object. Even though both
contain the same sequence of characters, s1 is created using the new keyword,
creating a new object in the heap, while s2 is a string literal, and string
literals are interned in Java. Therefore, s1 == s2 evaluates to false.

s1.equals(s2): This checks if the content of the two strings is the same. Both
strings contain the characters "hi," so s1.equals(s2) evaluates to true.

So, the correct output is:

A1 false true

Explanation:

The code will result in a compiler error because the == operator cannot be used to
compare objects of different types. In this case, i is an Integer object, and f is
a Float object. The comparison i == f is not valid, leading to a compilation error.
Explanation:

A) The File class in Java can indeed be used to represent both files and
directories. It is a versatile class for file-related operations.

B) The mkdir() method in the File class is used to create a directory.

C) The mkdirs() method in the File class is used to create a directory and its
parent directories if they do not exist.

Session 1 : Introduction to Java


--------------------------------

#### Features of java


=====================

1. Lambda Expressions:
----------------------
- Lamda Expression is an Anonymous Function
- not having any modifier
- not having any name
- not having any return

- Steps to make any function Lambda Expression


----------------------------------------------
- Remove modifier
- Remove method name
- Remove return type
- Place arrow
- Example 1
-----------

Normal Function
---------------

private void sayHello(){


System.out.println("Hello World!");
}

Lambda Expression
-----------------

() -> { System.out.println("Hello World"); }

- Example 2
-----------
private int getStringLength(String str){
return str.length;
}

(String str) -> { return str.length(); }

## Benefits of Lambda Expression


--------------------------------
1) To make code more readable and maintainable

2) file size reduction

# Beautifying Lambda Expression


-------------------------------

1) if the function body has only one statement then no need to include
{}
--------------------------------------------------------------
private int getStringLength(String str){
return str.length();
}

(String str) -> return str.length();

2) Use type inference, compiler guess the situation or context


--------------------------------------------------------------
private void add(int a, int b){
System.out.println(a + b);
}

(int a, int b) -> System.out.println(a + b);

(a, b) -> System.out.println(a + b);

3) No return type
-----------------
private int getStringLength(String str){
return str.length();
}

(String str) -> return str.length()

(str) -> str.length();

4) If only one param remove small brackets


------------------------------------------

(str) -> str.length();

str -> str.length();

2. Functional Interface
-----------------------
- Interface which is having only one abstract method and any number of
default and static methods

- Before 1.8, only public abstract methods were only allowed inside the
iterfaces

- We can invoke lambda expression by using functional interface

@FunctionalInterface
public interface MyInterface{ // ok
public void sayHello();
}

@FunctionalInterface
public interface MyInterface{ // error
public void sayHello();
public void sayBye();
}

@FunctionalInterface
public interface MyInterface{ // error

@FunctionalInterface
public interface MyInterface{ // ok
public void sayHello();

default void sayBye();


}

@FunctionalInterface
public interface MyInterface{ // ok
public void sayHello();

default void sayBye();

static void sayOk();


}

- @FunctionalInterface : is the annotation, which is used to tell the


compiler that the given interface is functional interface

3. Stream API
=============

- It is introduced in JDK 1.8 version


- Stream api is mainly used to process the data.

- Example :
- I stored data inside the collection

- Now if I want to sort, find min, find max, find avg, etc.

- This processing can be done with the help of stream api

- 2 types of stream in java


- java.util stream : used to deal with raw data
- java.io stream : used to deal with file

# java.util stream api


======================
- Example
==========
1) Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5);

2)
ArrayList<Integer> a1 = new ArrayListL<>();
a1.add(10);
a1.add(20);
a1.add(30);

Stream<Integer> s2 = a1.stream();

# 2 different varieties of stream api methods();


================================================
1) Intermediate methods : which will return new stream
------------------------------------------------------
- filter()

- map()
flatmap()

- skip()
limit()
distinct()

2) Terminal methods : which will return result


-----------------------------------------------
- count()
- collect()

## Examples of Stream API


=========================

Stream<Integer> s1= Stream.of(1, 2, 3, 4, 5, 6);

s1 = s1.filter(i -> i > 2);

s1.forEach(i -> System.out.println(i));

## Example using collection


===========================

ArrayList<Integer> a1 = new ArrayList<>();


a1.add(10);
a1.add(20);
a1.add(30);

Stream<Integer> s2 = a1.stream();

s2 = s2.filter(i -> i > 10);


s2.forEach(i -> System.out.println(i));
## How to perform mapping operation using stream
=================================================
- converting the letters into uppercase

ArrayList<String> a1 = new ArrayList<>();


a1.add("Raw");
a1.add("Rat");
a1.add("Pankaj");

Stream<String> s2 = a1.stream();

s2 = s2.map(i -> i.toUpperCase());


s2.forEach(i -> System.out.println(i));

// filter is used to filter the data


// map is used to transform the data

Q) Print name of emp having salary > 5000 using stream api
===========================================================

import java.util.ArrayList;
import java.util.List;

public class EmployeeStreamExample {


public static void main(String[] args) {
// Create a list of employees
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("John", 6000));
employees.add(new Employee("Alice", 4500));
employees.add(new Employee("Bob", 7000));
employees.add(new Employee("Eve", 5500));

// Create a stream and filter employees with salary > 5000


employees.stream()
.filter(employee -> employee.getSalary() > 5000)
.forEach(employee -> System.out.println("Employee
name: " + employee.getName()));
}

static class Employee {


private String name;
private int salary;

public Employee(String name, int salary) {


this.name = name;
this.salary = salary;
}

public String getName() {


return name;
}

public int getSalary() {


return salary;
}
}
}

4. Java Time API


================
- LocalDate today = LocalDate.now();

- This API helps to get the date

- Ex :
------

import java.time.LocalDate;

public class Main {


public static void main(String[] args) {
LocalDate today = LocalDate.now();
System.out.println(today);
}
}

// 2024-01-01

5. Optional Class:
==================
- The Optional class helps in avoiding null pointer exceptions.

- Thus, it tackles the scenario where the value maybe null

- Optional<String> nameOptional = Optional.ofNullable(getName());


##### JVM, JRE, JDK
===================

JVM is the virtual machine responsible for executing Java bytecode.

JDK is a development kit containing tools for compiling, debugging, and developing
Java applications.

Key Components:
---------------
Java Compiler (javac):
Translates Java source code into bytecode.

Java Interpreter (java):


Executes Java bytecode on the JVM.

Debugger (jdb):
Helps in debugging Java applications.

JavaDoc:
Generates documentation from Java source code comments.

Java Archive (jar):


Package of Java applications and libraries into a single file.

JRE is a runtime environment containing the JVM and libraries necessary for running
Java applications.

Key Components:
---------------
Java Virtual Machine (JVM):
Executes Java bytecode.

Java Standard Libraries:


A set of class libraries and APIs for common programming tasks.

Additional Libraries:
Additional libraries and files necessary for Java applications to run.
##### Structure of java class
=============================

[access modifier] [class keyword] ClassName [extends ParentClass] [implements


Interface1, Interface2, ...] {
// Class Body
}

##### Primitive and non-primitive data types


============================================

Primitive Data Types


--------------------
Primitive data types are those which have predefined way of storing data by
the system.

Primitive data structures are char, int, float, double, byte, boolean, long,
short int

The predefined operations are addition, subtraction, etc.

Non-Primitive Data Types


------------------------
But there are certain situations when primitive data types are not sufficient
for our job.

They are also called as derived or userdefined data types


There comes derived data types
- Array
- Enum
- Interface
- Class
- Collection

Session 2 : Basic Programming Concepts


======================================

##### Java Tokens


-----------------
Tokens in Java are the smallest units of a Java program and are the building
blocks of the language.

Java source code is converted into a sequence of tokens during the


compilation process.

- Examples :
------------
1. Keywords
- class, public, static, etc..

2. Identifiers
- variable name

3. Literals
- 123, 123.0, "Hello"

4. Operators
- +, -, /, %

5. Separators
- ,, ;

6. Comments
- //

7. Modifiers
- private, protected
8. Special Symbols
- @

9 Whitespace
-

##### OOPs
==========

OOPs
====
- Smalltalk is the first purely OOPs Language

- Further java, C#, C++, Python

- Object Oriented Programming System

- OOP is a programming paradigm

- programming paradigm : means way to the programming

- Other ways :
- POP : Procedural Oriented Programming Paradigm
- Functional Paradigm
- Logical Paradigm
- Structured Paradigm

// fake
-------
- There are 4 major pillar and 3 minor pillar of OOPs

- 4 Major Pillars of OOPs

- Abstraction
- Encapsulation
- Modularity
- Hierarchy

- 3 Minor Pillars of OOps

- Polymorphism
- Concurrency
- Persistance

OOPs
====
- In OOPs pillars are divided into 2 parts

1) For code security


--------------------
- Abstraction
-------------
- Data Hiding

- Encapsulation
---------------
- Data Security
- Tightly Coupled Classes

2) For code useability


----------------------
- Polymorphism
--------------
- Runtime polymorphism
- Compile time polymorphism

- Inheritance
-------------
- IS-A
- HAS-A

# Class
=======
- Example : Animal, Birds, Cars

- Class is the collection of objects (car -> tata, honda, etc.)


- Class is not a real world entity, it is just a prototype
- Class does not occupy memory
- Size of empty class is 1 byte

- Syntax
---------
- access-modifier class Class-Name{
- fields
- constructors
- methods
- blocks
- nested class

# Object
========
- Example : Elephant, Lion, Eagle, Falcon, Buggati, Rolls Royce

- Object is an instance of class


- Object is a real world entity
- It occupies memory

- Object consists of
--------------------
- Identity : name
- State / Attribute : color, breed, age
- Behavior : eat, run (it represents
methods)

- Objects can be created by using


---------------------------------
1) new keyword

2) newInstance() method

3) clone() method

4) deserialization

5) factory method

- Steps to create an object


---------------------------
- Declaration : we declare variable of the object
Animal bullDog;

- Instantiation : we use new keyword here


Animal bullDog = new

- Initialization : initialize by using constructor


Animal bullDog = new Animal();

- We can call method using object reference variable


----------------------------------------------------
bullDog.run();
# Method
========
- Any task which is performed

- Example : Animal eats, runs, attacks, etc

: Bird flies

: Car drives, beeps

- A set of codes which perform a particular task


- Advantages :
- Code reusability
- Code optimisation

- Syntax
--------
access-modifier return-type method-Name(list of parameters){

## Note :
---------
- Default access modifier of class and methods in java is default

#####
1)

class Animal{
public void eat(){
System.out.println("I am eating");
}

public static void main(String[] args){


System.out.println(1);
}
}

output
------
1

2)
class Animal{
public void eat(){
System.out.println("I am eating");
}

public static void main(String[] args){


System.out.println(1);

Animal buzo = new Animal();


buzo.eat();
}
}

output
------
1
I am eating

##### How to initialize an object ?


- Three ways
------------
- By reference variable
- By using methods
- By using constructor

1) By using reference variable


------------------------------

class Animal{
String color;
int age;

public static void main(String[] args)


{
Animal buzo = new Animal();
buzo.color = "Black";
buzo.age = 10;

System.out.println(buzo.color + " " + buzo.color);


}
}

2) By using method
------------------

class Animal{
String color;
int age;

void initObj(String s, int a){


color = c;
age = a;
}

void display(){
System.out.println(color + " " + age);
}

public static void main(String[] args)


{
Animal buzo = new Animal();
buzo.initObj("Black", 10);
buzo.display();

}
}

##### Constructor
=================
- Contructor is a block similar to method having same name as that of
ClassName

- Constructor does not have any return type


- not even void

- The only access modifiers applicable for constructor is private, default,


protected and public

- Constructor executes automatically when we create an object

- Constructor is used to initialize an object

- Why does constructors came into picture ?


-------------------------------------------
1) Because if we do not defined constructor then, the compiler will end
up taking default values of data types respective to fields.

- Example :

- Like for string : null


- for int : 0
- for double : 0.0
- for float : 0.0f

- And this will initialize every object with same values and is not a
good practice and ends up creating a problem.
2) We can initialize every object with unique values, but that will end
up taking more line of codes as well as more time

- To overcome these problems, constructor (parameterized) came into


picture, which allows us to initialize the object during it's declaration itself

- Syntax
--------

class Employee{
String name;
int age;

public Employee(String name, int age){


this.name = name;
this.age = age;
}
}

public class Solution{


public static void main(String[] args){
Employee e1 = new Employee("Ram", 21);
Employee e2 = new Employee("Shyam", 23);
}
}

# 3 types of constructor
========================

1) Default Contructor
---------------------

- A constructor which is created by compiler on its own

- It is a no argument constructor

- This default constructor consist of a single line,


- super();

- Compiler calls default constructor only when programmer do not define any
constructor inside a class

- Default Constructor
---------------------

class Test{

String name;

Test(){
super();
}
}

2) No argument constructor (User-defined)


-----------------------------------------

- It is a no argument constructor created by the programmer

- No argument Constructor
-------------------------

class Test{

String name;

Test(){

}
}

3) Parameterized constructor (User-defined)


-------------------------------------------

- It is a constructor which is created by the programmer


- This contructor consists of parameters

- Parameterized Constructor
---------------------------

class Test{

String name;

Test(String name){
this.name = name;
}
}

##### Inheritance (IS-A Relationship)


=====================================
- Process of inheriting the properties of parent class into child class

- We can achieve inheritence using extends keyword

- Example
---------
class Animal{
void eat(){
System.out.println("I am eating");
}
}

class Dog extends Animal{ // Inheriting


public static void main(String[] args){
Dog d = new Dog();
d.eat();
}
}

- Conclusion
------------
- Dog IS-A Animal
- Eagle IS-A Bird

- Inheritance is also called as IS-A relationship

- Advantage
-----------
- Code reusability
- We can achieve polymorphism(method overriding) as well using
inheritance

- Disadvantge
-------------
- Both parent and child class gets tightly coupled
- So if we make any changes inside the parent class then that
will be reflected inside the child class as well

##### 5 Types of inheritance


-----------------------------
- Single
- Multiple (not supported in java)
- Multilevel
- Hierarchical
- Hybrid

1) Single Level Inheritance


----------------------------
- Suppose we have 2 classes A and B
- Single Inheritance : class B is extending class A

class A{
void showA(){
System.out.println("A class method()");
}
}

class B extends A{
void showB(){
System.out.println("B class method()");
}

public static void main(String[] args){


A objA = new A();
objA.showA(); // ok
objA.showB(); // error

B objB = new B();


objB.showA(); // ok
objB.showB(); // ok
}
}

2) Multilevel Inheritance
-------------------------
- Suppose we have more than 2 classes, say A, B, and C
- Multilevel Inheritance : class C is extending class B and class B is
extending class A

- So indirectly class C is extending class A

class A{
void showA(){
System.out.println("A class method()");
}
}

class B extends A{
void showB(){
System.out.println("B class method()");
}
}

class C extends B{
void showC(){
System.out.println("C class method()");
}

public static void main(String[] args){


A objA = new A();
objA.showA(); // ok
objA.showB(); // error
objA.showC(); // error

B objB = new B();


objB.showA(); // ok
objB.showB(); // ok
objB.showC(); // error

C objC = new C();


objC.showA(); // ok
objC.showB(); // ok
objC.showC(); // ok
}
}

3) Hierarchical Inheritance
----------------------------
- Suppose we have more than 2 classes, say A, B, and C
- Hierarchical Inheritance : class C is extending class A and class B is also
extending class A

class A{
void showA(){
System.out.println("A class method()");
}
}

class B extends A{
void showB(){
System.out.println("B class method()");
}
}

class C extends A{
void showC(){
System.out.println("C class method()");
}

public static void main(String[] args){


A objA = new A();
objA.showA(); // ok
objA.showB(); // error
objA.showC(); // error

B objB = new B();


objB.showA(); // ok
objB.showB(); // ok
objB.showC(); // error

C objC = new C();


objC.showA(); // ok
objC.showB(); // error
objC.showC(); // ok
}
}

4) Multiple Inheritance (Not possible in java)


-----------------------
- Suppose we have more than 2 classes, say A, B, and C
- Multiple Inheritance : class C is extending class A and class C is also
extending class B

- Not possible in java because of ambiguity error

- class C extends A, B{

- suppose class A and B contains method with same name

- At this situation compiler gets confused, which show method to


execute

5) Hybrid Inheritance (Not possible java)


---------------------
- It is the combination of any 2 inheritances

- Suppose we have more than 2 classes, say A, B, and C


- Hybrid Inheritance : class C is extending class A and class B is also
extending class A

class D is extending class B and class D is also extending class C

- Diamaond Problem occurs in Hybrid Inheritance

## Note
=======
- We cannot inherit everything from the parent class to child class

- ex:
- We cannot inherit constructor and private member of parent class into
child class

##### Different types of relationships in java


==============================================

Inheritance (IS-A), Association (HAS-A), Aggregation & Composition


-------------------------------------------------------------------

- There are 2 types of relationship in java


--------------------------------------------
- Inheritance (IS-A)

- Association (HAS-A)
---------------------
- Association has 2 forms:
- Aggregaton
- Comnposition

Advantage :
-----------
- Code reusability (same class can be extended
- Cost cutting
- Reduce redundancy (un-necessary code is removed)

##### Inheritance (IS-A)


========================
- Process of inheriting the properties of parent class into child class

- It shows IS-A relationship

- We can achieve inheritence using extends keyword

- If we are using inheritance between the classes, then classes gets tightly
coupled, means if we make changes onto the property of the parent classes than it
will affect the property of child class

- Main use of inheritance : code reuseability

- Bottom to top

##### Association (HAS-A)


=========================
class Student{
String name;
int roll_no;
}

- The above example shows association


- Student HAS-A name
- Student HAS-A roll_no

- We can achieve association directly, as well as using new keyword


- Example
---------

class Engine{

}
class Car{
Engine e = new Engine();
}

- the above example denotes : car HAS-A engine

##### Difference between Inheritance and Association


----------------------------------------------------
- We do not use extends keyword in association, instead we use
reference variable and new keyword

- In association, we can call only those properties from parent class


which are required inside child class

- In association the classes do not get tightly coupled, means changes


inside the property of parent class will not affect the property of child class

- Inheritance : in inheritance blood relation is formed between the


classes

- Asscoiation : in association there doesn't form a blood relation


between the classes

- Inheritance : Bottom to top relationship


: Tata Nano IS A Car

- Association : Top to bottom relationship


: Car HAS A Music Player (Aggregation)
: Car HAS A Engine (Composition)

## 2 forms of Association
==========================
- Aggregation : is a weak bonding

- Composition : is strong bonding

- Example
---------
Weak bonding
------------
class Car{ // container object

}
class Music_Player{ // contained object

- without Music_Player as well Car will do it's job

Strong Bonding
--------------
class Car{ // container object

}
class Engine{ // contained object
}

- without engine car cannot do it's job

- Example 2 :
------------
Weak bonding : College <-> Professor

Strong bonding : College <-> Branches (CS/ IT/ MECH)

##### Polymorphism (Many Forms)


===============================
- Real World Examples
---------------------
- Water -> solid, liquid, gas

- Shape -> triangle, rectange, square, circle

- Sound -> bark, roar

- Polymorphism -> same thing with different forms

-> in java, same method with different forms

- 2 types of polymorphism
--------------------------
- Compile Time Polymorphism / Static Polymorphism

- We can achieve CTP using Method Overloading

- CTP is handled by compiler

- Run Time Polymorphism / Dynamic Polymorphism

- We can achieve RTP using Method Overriding

- RTP is handled by JVM

##### Conditions for Method Overloading


---------------------------------------
- same name
- same class
- different arguments
- number of arguments
- type of arguments
- order of arguments

##### Conditions for Method Overriding


--------------------------------------

- same name
- different class
- same arguments
- number of arguments
- type of arguments
- order of arguments
- Inheritance (IS-A) *** this is mandatory for method overriding

## Method Overloading
=====================

1)
class Test{
void show(){
System.out.println(1);
}

void show(){
System.out.println(2);
}

public static void main(String[] args){


Test t = new Test();
t.show();
}
}

output
------
- ambiguity error : show() already defined

2)
class Test{
void show(){
System.out.println(1);
}

void show(int a){


System.out.println(a);
}
public static void main(String[] args){
Test t = new Test();
t.show();

t.show(100);
}
}

output
------
1
100

##### Special Cases Of Method Overloading


-----------------------------------------
1) Can we achieve Method Overloading by changing the return type of method
only?
- No

2) Can we overload java main() method?


- Yes. Because JVM always calls main() method which receives String
array as arguments only.

3) Method Overloading and Type Promotion


class Test{
void show(int a){
System.out.println(a);
}

void show(String a){


System.out.println(a);
}

public static void main(String[] args){


Test t = new Test();
t.show("abc");
}
}

output :
--------
abc

class Test{
void show(int a){
System.out.println(a);
}

void show(String a){


System.out.println(a);
}

public static void main(String[] args){


Test t = new Test();
t.show(10);
}
}

output :
--------
10

// automatic promotion
----------------------
class Test{
void show(int a){ // this is called
System.out.println("hello");
}

void show(String a){


System.out.println("hi");
}

public static void main(String[] args){


Test t = new Test();
t.show('a'); // character as argument
}
}

output :
--------
hello

### Automatic Promotion


-----------------------
One data type is promoted to another implicitly if no matching datatype
is found.

byte -> short -> int -> float


-> long

char -> int

-> float -> double

-> long -> float


-> double

- In method overloading, automatic promotion concept is used


------------------------------------------------------------

*****
-----
class Test{
void show(Object a){
System.out.println("object");
}

void show(String a){ // this is called


System.out.println("string");
}

public static void main(String[] args){


Test t = new Test();
t.show("abc");
}
}

output :
--------
string

*****
-----
class Test{
void show(StringBuffer a){
System.out.println("StringBuffer");
}

void show(String a){ // this is called


System.out.println("String");
}

public static void main(String[] args){


Test t = new Test();
t.show(null);
}
}

output :
--------
ERROR : ambiguous

*****
-----
class Test{
void show(int a, float b){
System.out.println("int float method");
}

void show(float a, int b){ // this is called


System.out.println("float int method");
}

public static void main(String[] args){


Test t = new Test();
t.show(10, 20.5f);
}
}

output :
--------
int float method

- Because of automatic promotion : int is promoted to float

// varargs

- Variable arguments : we can provide any number of arguments


- int...a

*****
-----
class Test{
void show(int a){
System.out.println("int method");
}

void show(int...a){
System.out.println("varargs method");
}

public static void main(String[] args){


Test t = new Test();
t.show(10);
}
}

output :
--------
int method

*****
-----
class Test{
void show(int a){
System.out.println("int method");
}

void show(int...a){
System.out.println("varargs method");
}

public static void main(String[] args){


Test t = new Test();
t.show(10, 20, 30);
t.show();
}
}

output :
--------
varargs method
varargs method

##### Method Overriding


=======================
-

class Test{
void show(){
System.out.println(1);
}
}

class XYZ extends Test { // compulsory


void show(){
System.out.println(2);
}
public static void main(String[] args){
Test t = new Test();
t.show(); // 1

Xyz x = new Xyz();


x.show(); // 2
}
}

x se Test class ka show bhi call kar sakte hai, using super() keyword

- Using method overriding we can acheive run time polymorphism, because we


can override the method of parent class into it's sub-class and if required I can
change I can change implementation of that method as well inside the child class
Q) Do overriding method must have same return type ?
----------------------------------------------------
- Before version 1.4, it was mandatory to have the same return type

- After version 1.4, we can use covariant return type

- Means if we have parent return type in the parent class then we can
have same return type or child return type inside the child class

Object -> String


-> StringBuffer
-> StringBuilder
-> Number
-> Character
-> Boolean
-> Void
-> ...

Number -> Byte


-> Short
-> Integer
-> Long
-> Float
-> Double

Example
-------
class Test{
Object show(){
System.out.println("Hello");
return null;
}
}

class XYZ extends Test { // compulsory


String show(){
System.out.println("Hi");
return null;
}
}

- this is ok
- because String is the child class of Object Class

Q) Overriding and Access-Modifiers ?


------------------------------------
- Access modifier inside the child class method must be of same or greater
accessiblity than the access modifier inside the parent class
Q) Overriding and Exception Handling
-------------------------------------
1) If parent class not throwing any exception then child class can only
through unchecked exception

1)
class Test{
void show(){
System.out.println("1");
}
}

class Xyz extends Test{


void show() throws RuntimeException{ // unchecked exception
System.out.println("2");
}
}

output
------
ok

2)
class Test{
void show(){
System.out.println("1");
}
}

class Xyz extends Test{


void show() throws Exception{ // checked exception
System.out.println("2");
}
}

output
------
error

2) If the parent class is throwing any exception than child class cannot
through any exception or it can through exception of same type or any other
exception of same level

1)
class Test{
void show() throws Exception{ // checked exception
System.out.println("1");
}
}
class Xyz extends Test{
void show() throws Exception{ // checked exception
System.out.println("2");
}
}

output
------
ok

2)
class Test{
void show() throws RuntimeException{ //unchecked exception
System.out.println("1");
}
}

class Xyz extends Test{


void show() throws ArrayIndexOutOfBoundException{ // child unchecked
exception
System.out.println("2");
}
}

output
------
ok

3)
class Test{
void show() throws RuntimeException{ //unchecked exception
System.out.println("1");
}
}

class Xyz extends Test{


void show(){ // no exception
System.out.println("2");
}
}

output
------
ok
##### Relationship between Overriding and Abstract Method
----------------------------------------------------------

- If any class is extending or implementing some other abstract class or


interface respectively. Then that class must override the abstract methods of
abstract class or interface respectively

- We cannot create object of abstract class *****


- We cannot create object of interface *****

- Interface consist of abstract methods bydefault

- Conclusion : Abstract method should always override

Example
-------

abstract class Test{


public abstract void display();
}

class Xyz extends Test{


void display(){
System.out.println("Hello");
}

public static void main(String[] args){


// Test t = new Test(); // we cannot create object of abstract
method

Xyz x = new Xyz();


}
}

-
Interface I{
public abstract void display();
}

class Xyz implements I{


void display()
--------------
{

public static void main(String[] args){


// I i = new I(); // we cannot create object of interface
Xyz x = new Xyz();
}
}

output :
--------
ERROR : attempting to access weaker access privileges at void diplay()

-
Solution : make it public
--------------------------

Interface I{
public abstract void display();
}

class Xyz implements I{


public void display()
---------------------
{

public static void main(String[] args){


// I i = new I(); // we cannot create object of interface

Xyz x = new Xyz();


}
}

output :
--------
ok

Q) Invoking method of parent class from child class


---------------------------------------------------
- super keyword

class Test{
void display(){
System.out.println("1");
}
}

class Xyz extends Test{


void display(){
super.show();
--------
System.out.println("2");
}

public static void main(String[] args){


Xyz x = new Xyz();

x.display();
}
}

output :
--------
1
2

Q) Which methods cannot override?


----------------------------------
- Final Methods cannot be overridden
- Final Methods cannot be overridden

- Static Methods cannot be overriden


- Method Hiding

- Private Methods cannot be overridden


- Cannot access private methods outside the class in which it is
declared

Q) Overridding and synchronized / strictfp method


-------------------------------------------------

- Yes, synchronized / strictfp keywords with methods then also we can


override them inside child class

- Example
---------
1)
class Test{
synchronized void display(){
System.out.println("1");
}
}

class Xyz extends Test{


synchronized void display(){
System.out.println("2");
}

public static void main(String[] args){


Xyz x = new Xyz();
x.display();

Test t = new Test();


t.display();
}
}

output :
--------
2
1

2)
class Test{
strictfp void display(){
System.out.println("1");
}
}

class Xyz extends Test{


strictfp void display(){
System.out.println("2");
}

public static void main(String[] args){


Xyz x = new Xyz();
x.display();

Test t = new Test();


t.display();
}
}

output :
--------
2
1
##### Abstraction
=================

OOPs
====
- In OOPs pillars are divided into 2 parts

1) For code useability


- Polymorphism
- Runtime polymorphism
- Compile time polymorphism
- Inheritance
- IS-A
- HAS-A

2) For code security


- Abstraction
- Data Hiding
- Encapsulation
- Tightly Coupled Classes

- Abstraction
-------------
- Abstraction means Implementation Hiding (How is Vehicle Made?)
- It is used to achieve data security

- Real World Example


--------------------

- Car : Manufacturer shows us the steering, brake, etc


: But doesn't tell about the internal implemention of steering
and brake

- Brake and steering are abstract

- How can we achieve abstraction in java ?


------------------------------------------
- 2 ways
--------
- By using abstract class (0 - 100% abstraction)
- By using interface (100% abstraction)

### 1) using abstract class (0 - 100% abstraction)


==================================================
Car
---
Car{
no_of_tyres = 4
start(){
Sop("starts with key");
}
}

Scooter
-------
Scooter{
no_of_tyres = 2
start(){
Sop("starts with kick");
}
}

- Since the variable name and method name in both the above object are same,
we can declare one abstract method for code reusability and security

class Vehicle{ // declare the class abstract


no_of_tyres;
start(); // public abstract method
}

error : missing method body or declare abstract

Solution ; abstract class Vehicle... will solve the error

Code :
------

// 100% abstraction
-------------------
abstract class Vehicle{
int no_of_tyres; // we are not telling the no of tyres.. thus
we are hiding the detail

public abstract void start();


}

class Car extends Vehicle{


void start(){
System.out.println("Starts with key!");
}
}

class Scooter extends Vehicle{


void start(){
System.out.println("Starts with kick!");
}
}
# Conclusion
-------------
- Method Overridding

- we cannot create object of abstract class


- because abstract class consists of abstract methods, and since
the abstract method doesn't have a body.. there no point of creating the object of
it's class

- But we can create reference of abstract class

## Abstract Method
-------------------
- abstract method is the method with no implementation, means it doesn't have
a body

- if there exists a abstract method inside a class than it is mandatory to


declare that class abstract

- if class is abstract it is not mandatory that it will contain a abstract


method

- if any class extends the abstract class then it mandatory for that class to
implement the abstract methods of abstract class

- We cannot create object of abstract class but we can create the reference
of abstract class

- We can achieve method overriding using abstract class and methods

// 0% abstraction
-----------------
abstract class Vehicle{
int no_of_tyres; // we are not telling the no of tyres.. thus we are
hiding the detail

public abstract void start(){

}
}
### full code
=============
=============

abstract class Vehicle{


public abstract void start();
}

class Car extends Vehicle{


public void start(){
System.out.println("cars starts with key!");
}
}

class Scooter extends Vehicle{


public void start(){
System.out.println("scooter starts with kick!");
}

public static void main(String[] args){


// Vehicle v = new Vehicle();
// ERROR : vehicle is abstract cannot be instantiated

Car c = new Car();


c.start(); // cars starts with key!

Scooter s = new Scooter();


s.start(); // scooter starts with kick!
}
}

### 2) using interface (100% abstraction)


=========================================

- Interface is similar to abstract class but it can have only abstract


methods

- Abstract class is a class which can or cannot have abstract methods

is the blueprint of the class which tells class what to do


- We cannot create object of interface class

- Why do we need Interface ?


----------------------------
- Interface is used to achieve 100% abstraction because interface can
contain only abstract methods

- It is used to achieve multiple inheritance, which is not possible in


java using classes

- It can be used to achieve loose coupling


- Means if introduce any new public abstract method inside
interface that will not affect any of it's implementing classes

- Syntax :
----------

interface Interface_Name{

methods // public abstract type

fields // public static final

After java 8 :
--------------
default methods //

static methods // public

After 9th :
-----------
private methods

- Program
=========

- Method Overridding
--------------------

interface I1{
public abstract void show();
}
class Test implements I1{
void show()
-----------
{

}
}

// ERROR : void show() of implementing must be public

interface I1{
public abstract void show();
}

class Test implements I1{


public void show()
------------------
{

public static void mian(String[] args){


Test t = new Test();
}
}

// actual code
--------------

interface I1{
protected abstract void show(); // error
}

interface I1{
default abstract void show(); // error
}

interface I1{
void abstract void show();

void display(){ // error


}
}

interface I1{

public static final int a = 10;

void abstract void show();

static void display(){ // ok

default void display(){ // ok

}
}

interface I1{
void show();
}

class Test implements I1{


void show(){ // ERROR

}
}

interface I1{
void show(); // public abstract
}

class Test implements I1{


public void show(){ // ok

}
}

// multiple inheritance using interface


---------------------------------------

interface I1{
public abstract void show();
}

interface I2{
public abstract void display();
}

class Test implements I1, I2{


public void show(){

public void display(){

public static void main(String[] args){


Test t = new Test();
t.show();
t.display();
}
}

##### Encapsulation
-------------------
- It is used to achieve data security
- Encapsulation means hidding the data
- Plus encapsulation is also a mechanism to wrap class variables and methods
together

- Steps to achieve encapsulation


--------------------------------
- Declare all the class variables as private

- Create getter, setter methods for modifying and viewing those private
class variables
- Example :
-----------

class Employee{
private int empId; // Data Hidding

public void setempId(int empId1){ // for modifying the data


empId = empId1;
}

public int getempId(){ // for viewing the data outside the


class
return empId;
}
}

- This entire process is called Encapsulation

## Full Code
============

class Employee{
private int empId; // data hidding

public void setEmpId(int empId1){


empId = empId1;
}

public int getEmpId(){


return empId;
}
}

public class Solution{


public static void main(String[] args){
Employee e = new Employee;
e.setEmpId(10);

System.out.println(e.getEmpId());
}
}
##### this keyword
==================
==================
- this keyword is the reference variable which refers to the object of
current class

- Example
----------
- if we have same name for local variable and for instance variable
- then there occurs a problem (shadowing)
- and to overcome this problem, this keyword comes into picture

class Test{
int i;

void setValues(int i){


i = i; // --> these both are local variable
}

void show(){
System.out.println(i);
}
}

public class Solution{


public static void main(String[] args){
Test t = new Test();

t.setValues(10);

t.show(); // 0 --> Problem (default value of int)


}
}

to overcome the above problem


-----------------------------

class Test{
int i;

void setValues(int i){


this.i = i;
}

void show(){
System.out.println(i);
}
}

public class Solution{


public static void main(String[] args){
Test t = new Test();

t.setValues(10);

t.show(); // 10
}
}

- this keyword differentiates instance variable from the current local


variable
- we can also say it is used to overcome shadowing effect

##### Uses of "this" keyword


============================

1) this keyword can be used to refer current class instance variable

class Test{
int x;

void setValue(int x){


this.x = x;
}
}

2) this keyword can be used to invoke current class method (implicitly)

class Test{

void display(){
System.out.println("Hello");
}

void show(){
display(); // this.display(); *****
}

public static void main(String[] args){


Test t = new Test();
t.show();
}
}

// Hello

3) this() keyword can be used to invoke current class constructor

class Test{
Test(){
System.out.println("no arg constructor");
}

Test(int a){
this();
System.out.println("parameterised constructor");
}

public static void main(String[] args){


Test t = new Test();
}
}

//
no arg constructor
parameterised constructor

class Test{
Test(){
this(10);
System.out.println("no arg constructor");
}

Test(int a){
System.out.println("parameterised constructor");
}

public static void main(String[] args){


Test t = new Test();
}
}

//
parameterised constructor
no arg constructor

4) this keyword can be used to pass as an argument in the method call

class Test{
void m1(Test t){
System.out.println("Hi");
}

void m2(){
m1(this);
}

public static void main(String[] args){


Test t = new Test();
t.m2();
}
}

//
Hi

5) this keyword can be used to pass as an argument in the constructor call

class Test{
Test()
{
System.out.println("test class constructor");
}
}

class Test2{
void m1(Test2 t2){
Test t = new Test(this);
}

public static void main(String[] args){


Test2 t2 = new Test2();
t2.m1();
}
}

6) this keyword can be used to return the current class instance from the
method

class Test2{
Test2 m1(Test2 t2){
return this;
}

public static void main(String[] args){


Test2 t2 = new Test2();
t2.m1();
}
}
##### super keyword
===================

super = parent

- super is a reference variable which refers to the object of parent class

class A{
int a = 10;
}

class B extends A{
int a = 20;

void show(int a){


System.out.println(a); // 30

System.out.println(this.a); // 20 (current class


reference variable)

System.out.println(super.a); // 10 (parent class


reference variable)
}

public static void main(String[] args){


B objB = new B();
objB.show(30);
}
}

##### super keyword


===================
1) super keyword can be used to refer immediate parent class instance variable

class A{
int i = 10;
}

class B extends A{
int i = 20;
void show(int i){
System.out.println(i); // 30

System.out.println(this.i); // 20

System.out.println(super.i); // 10
}

public static void main(String[] args){


B objB = new B();
objB.show(30);
}
}

2) super keyword can be used to invoke immediate parent class method

class A{
void m1(){
System.out.println("m1() method");
}
}

class B extends A{
void show(){
super.m1();
}

public static void main(String[] args){


B objB = new B();
objB.show();
}
}

//
m1() method

class A{
void m1(){
System.out.println("in class A");
}
}
class B extends A{
void m1(){
System.out.println("in class B");
}

void show(){
m1();
super.m1();
}

public static void main(String[] args){


B objB = new B();
objB.show();
}
}

//
in class B
in class A

3) super() can be used to invoke immediate parent class constructor

class A{
A(){
System.out.println("in class A");
}
}

class B extends A{
B(){
// super(); //
internally compiler
System.out.println("in class B");
}

public static void main(String[] args){


B objB = new B();
}
}

//
in class A *****
in class B

super() vs super ****


================
super : keyword
super() : it is used in constructor

###### final (variable, method, class)


======================================
1) variable : such that variable remain constant

2) method : such that we cannot override method

3) class : such that we cannot inherit class

##### static keyword (- static is used for memory management)


====================

Access Modifiers : public, private, protected, default

Non-Access Modifiers : static, final, abstract, synchronized, transient, volatile,


strictfp

## static :
-----------
- static is used for memory management

- can be used with class level variable

- cannot be used with local variable

- can be used with methods


- can be used with blocks

- can be used with inner class

- cannot be used with outer class

1)
class Test{
static int a = 10; // ok
void m1(){
static int b = 10; // error
}

psvm(S[] a){
Syso(Test.a); // 10
}
}

// why we need static? (- static is used for memeory management)


----------------------
example we have class where we have employee details say : name, age and
company_name

everytime I create an object of new employee, name, age and company_name will
get space everytime...

problem is company name is same for all employee and calling it for every
employee object doesn't makes a sense and it will lead to memory problem that's why
what I will do is.. declare company name instance variable as static

such that it will become class level variable and be available for each
instances created for the class

class Employee{
int empid;

String name;

static String company = "Xyz"; // static variable must be initialized

Employee(int empid, String name){


this.empid = empid;
this.name = name;
}

void display(){
Sop(empid + " " + name + " " + company);
}

psvm(S[] a){
Employee e1 = new Employee(100, "Rajiv"); // bas do variable
e1.display(); // 100 Rajiv XYZ
...
}
}

// static variable ke wajje se memory management hoti hai

// ek hi time memory allocate hoti hai static variables ko while class loading

######### COUNTER DEMO ###########

class CounterDemo{
int count=0;

CounterDemo(){
count++;
System.out.println(count);
}

public static void main(String[] args){


CounterDemo c1 = new CounterDemo();
CounterDemo c2 = new CounterDemo();
CounterDemo c3 = new CounterDemo();
}
}

//
1
1
1

class CounterDemo{
static int count=0;

CounterDemo(){
count++;
System.out.println(count);
}

public static void main(String[] args){


CounterDemo c1 = new CounterDemo();
CounterDemo c2 = new CounterDemo();
CounterDemo c3 = new CounterDemo();
}
}
//
1
2
3

// c1, c2, c3 ek ek baar apen liye constructor call karenge aur uske waje se
count 1, 2, 3

##### static method


===================
- static method belongs to the class

- you can call static method using class

- static is used for memory management

class Test{
static void show(){

psvm(S[] a){
Test.show();
}
}

class Test{
void show(){

psvm(S[] a){
Test t = new Test();
t.show();
}
}
## static method directly call kar sakta hu but within a class ****

class Tset{
static void display(){
System.out.println("1");
}

public static void main(String[] args){


display(); // 1
}
}

class Tset{
static void display(){
System.out.println("1");
}

public static void main(String[] args){


display(); // 1

// show(); // error
Test.show();
}
}

class Test{
static void show(){
System.out.println("2");
}
}

## Rules for static methods


===========================

- static methods belongs to the class, not the object

- staic methods can be accessed directly by class name and doesn't need any
object

- A static method can access only static data. It cannot access non-static
data

- A static method can call only static methods and cannot call a non-static
method

- A static method method cannot refer to this or super keyword in any way
##### static blocks
===================

- if we use static keyword with any block then it becomes a static block

static
{

1)
class Test{
void show(){

psvm(S[] a){
Test t = new Test();
t.show();
}
}

- there is no need to call static block, the moment class loads into the
memory the static block gets executed

*** bina main method ke "hello" print karwa sakta hu??

yes v1.6 and below me karwa sakte ho


-------------------------------------
-------------------------------------

class Test{
static{
System.out.println("Hello");
}
}
2)

class Test{
static{
System.out.println("Hello");
}

psvm(S[] a){
System.out.println("Radha");
}
}

//
Hello
Radha

1.6v and above wale me sab block and functions sequentially kaam karte the

after 1.6v sabse pehle static block ko dhund ke execute karega fir agge main
executed hoga

3) We can create multiple static blocks


=======================================

class Test{
static{
System.out.println("Hello");
}

psvm(S[] a){
System.out.println("Radha");
}

static{
System.out.println("Ram");
}
}

//
Hello
Ram
Radha

Uses
====

// static block me mai wo code likhta hu jo mai chahata hu wo class loading ke time
hi execute ho jaye

- example : native methods (wo code jinka code kisi dusri language me
implement kiya jata hai)
// static block is used to initialize the static members

class Test{ // *****


static int a;
static {
a = 10;
}
}

#### synchronized keyword


=========================

It ensure that at a time one one thread can enter into a critical section

##### Session 4 :
=================

## Static Variables and Methods


-------------------------------

Static Variables:
-----------------
- Static variables, also known as class variables, belong to the class rather
than instances of the class.

- There is only one copy of a static variable, shared among all instances of
the class.

Declaration
-----------
public class MyClass {
static int staticVariable;
}

Characteristics:
----------------
Shared Among Instances:
-----------------------
All instances of the class share the same static variable.

Declared with static Keyword:


-----------------------------
The static keyword is used to declare a static variable.

Memory Allocation:
------------------
Memory for a static variable is allocated once when the class is
loaded.

Accessed Using Class Name:


--------------------------
Static variables are accessed using the class name rather than through
instances.

Initialization:
---------------
Static variables are initialized to default values if not explicitly
initialized.

Example:
--------
public class Counter {
static int count = 0;

Counter() {
count++;
}

public static void main(String[] args) {


Counter c1 = new Counter();
Counter c2 = new Counter();
System.out.println("Count: " + Counter.count);
}
}
Static Methods:
===============

Static methods belong to the class rather than instances.

They can be called using the class name and don't require an instance to be
invoked.

Declaration:
------------
public class MyClass {
static void staticMethod() {
// Method body
}
}

Characteristics:
----------------
No Access to Instance Variables:
--------------------------------
Static methods cannot directly access instance variables; they operate
on class-level data.

Declared with static Keyword:


-----------------------------
The static keyword is used to declare a static method.

Accessed Using Class Name:


--------------------------
Static methods are called using the class name rather than through
instances.

Cannot Use this Keyword:


------------------------
Since static methods are not associated with instances, they cannot use
the this keyword.

Example:
--------
public class MathOperations {
static int add(int a, int b) {
return a + b;
}

public static void main(String[] args) {


int result = MathOperations.add(5, 3);
System.out.println("Sum: " + result);
}
}

## Reference Data Types:


========================
Definition:
-----------
Reference data types in Java are types that do not store the actual data but
instead store references (memory addresses) to the location where the data is
stored.

They include objects, arrays, and interfaces.

Characteristics:
----------------
Objects and Instances:
----------------------
Reference data types are used to create objects and instances of classes.

Dynamic Memory Allocation:


--------------------------
Memory for reference data types is allocated dynamically at runtime.

Stored in Heap Memory:


----------------------
Objects created using reference data types are stored in the heap memory.

Null Values:
------------
Reference variables can have a special value called null, indicating that
they are not pointing to any object.

Methods and Properties:


-----------------------
Reference data types often come with methods and properties associated with
the objects they represent.
Examples of Reference Data Types:
---------------------------------
Objects:
--------
Instances of classes are reference data types.
Example:
--------
// Creating an object of the String class
String myString = new String("Hello, Java!");

Arrays:
-------
Arrays are reference data types that store multiple elements of
the same type.
Example:
--------
// Creating an array of integers
int[] numbers = new int[]{1, 2, 3, 4, 5};

Interfaces:
-----------
Interfaces can be used as reference data types.
Example:
--------
// Using an interface reference variable
List<String> myList = new ArrayList<>();

```

public class ReferenceVariableExample {


public static void main(String[] args) {
// Reference variable declaration
MyClass obj1;

// Reference variable initialization


obj1 = new MyClass();

// Reference variable with polymorphism


ParentClass obj2 = new ChildClass();

// Reference variable set to null


MyClass obj3 = null;
}
}

```
## Reference methoods
---------------------

Definition:
-----------
Reference methods refer to methods associated with objects or classes,
accessed using reference variables.

Characteristics:
----------------
Accessing Object Behavior:
--------------------------
Reference methods allow the invocation of behavior (methods) associated
with objects.

obj1.myMethod(); // Invoking a method on an object

Polymorphism with Methods:


--------------------------
Polymorphism extends to methods, allowing overridden methods in
subclasses to be invoked through reference variables of the superclass.

obj2.someMethod(); // Invoking a polymorphic method

Dynamic Method Binding:


-----------------------
Java supports dynamic method binding, determining the method to invoke
at runtime based on the actual type of the object.

obj2.dynamicBindingMethod(); // Dynamic method binding

```

public class ReferenceMethodExample {


public static void main(String[] args) {
// Reference variable initialization
MyClass obj1 = new MyClass();

// Invoking a method on an object


obj1.myMethod();

// Reference variable with polymorphism


ParentClass obj2 = new ChildClass();

// Invoking a polymorphic method


obj2.someMethod();

// Dynamic method binding


obj2.dynamicBindingMethod();
}
}

```

## Difference between reference data types and primitive data types


==============================================================
Key Differences:
----------------
Storage Mechanism:
------------------
Primitive: Store actual values directly in memory.

Reference: Store memory addresses (references) pointing to the location


of the data.

Size:
-----
Primitive: Have fixed sizes (platform-dependent).

Reference: Can have dynamic sizes.

Associated Methods:
-------------------
Primitive: Do not have methods.

Reference: Often come with methods and properties associated with the
objects they represent.
Default Values:
---------------
Primitive: Assigned default values if not explicitly initialized.

Reference: Assigned null by default if not explicitly initialized.

Mutability:
-----------
Primitive: Values are immutable.

Reference: Values are mutable (changeable); changing the state of an


object affects the same instance.

## Difference between reference variable and static variable


============================================================
Key Differences:
----------------
Scope:
------
Reference Variable: Scoped to a particular instance of a class; each
object has its own copy.

Static Variable: Scoped to the class itself; shared among all instances
of the class.

Memory Allocation:
------------------
Reference Variable: Memory allocated for each instance of the class.

Static Variable: Memory allocated once for the entire class when it is
loaded.

Access:
-------
Reference Variable: Accessed through instances of the class.

Static Variable: Accessed using the class name; no instance required.


Initialization:
---------------
Reference Variable: Each instance can have different values.

Static Variable: Shared among all instances, often initialized to


default values.

##### Session 5
===============

## Constructors, initializing reference variables using constructors.


------------------------------------------------------------------

Constructors:
=============
- In Java, a constructor is a special method that is invoked when an
object is created. It has the same name as the class and is used to initialize the
object's state.

Example of a Class with a Constructor:


--------------------------------------
public class MyClass {
// Fields
private int value;

// Constructor
public MyClass(int value) {
this.value = value;
}

// Getter method
public int getValue() {
return value;
}
}

## Initializing Reference Variables Using Constructors:


-------------------------------------------------------

public class ReferenceInitializationExample {


public static void main(String[] args) {
// Creating an instance of MyClass using the
constructor
MyClass myObject = new MyClass(42);

// Accessing the value through the reference


int retrievedValue = myObject.getValue();

// Printing the retrieved value


System.out.println("Retrieved Value: " +
retrievedValue);
}
}

## Pass by value v/s pass by reference.


=======================================

# Pass by reference
===================

- Pass by reference means we are passing the memory address of a


variable to a function

- so if some code inside a function changes the value of that


variable than that will affect the original variable itself

////////////
public class Solution{
public static void main(String[] args){
StringBuilder sb = new StringBuilder("Hello");
modifyReference(sb);

System.out.println(sb); // Hello World


}
public static void modifyReference(StringBuilder str){
str.append(" World");
}
}
/////////////

# Pass by value
===============

- Pass by value means we are passing the copy of the variable to a


function

- so this time if some code inside a function changes the value


of that variable than that change will not affect the original variable

/////////////
public class Solution{
public static void main(String[] args)
{
int x = 10;
modifyValue(x);
System.out.println(x); // 10
}

public static void modifyValue(int value){


value = 20;
}
}
///////////

## Re-assigning a reference variable.


=====================================

public class ReassignReferenceVariable {


public static void main(String[] args) {
// Creating an object
StringBuilder str1 = new StringBuilder("Hello");

// Re-assigning the reference variable


StringBuilder str2 = str1;

// Modifying the object through one reference


str1.append(" World");

// Printing the content using the other reference


System.out.println("Content using str2: " + str2); // Output:
Hello World
}
}

## Passing reference variable to method


=======================================

public class ReferencePassingExample {


public static void main(String[] args) {
StringBuilder myStringBuilder = new StringBuilder("Hello");

System.out.println("Before method call: " + myStringBuilder);

modifyStringBuilder(myStringBuilder);

System.out.println("After method call: " + myStringBuilder);


}

// Method to modify the StringBuilder


public static void modifyStringBuilder(StringBuilder sb) {
sb.append(" World");
System.out.println("Inside method: " + sb);
}
}

//
Before method call: Hello
Inside method: Hello World
After method call: Hello World
## Initializing reference variable of different class
=====================================================

class MyClass {
int value;

// Constructor
public MyClass(int value) {
this.value = value;
}

// Getter method
public int getValue() {
return value;
}
}

public class ReferenceInitializationExample {


public static void main(String[] args) {
// Initializing a reference variable of a different class
MyClass myObject = new MyClass(42);

// Accessing the value through the reference


int retrievedValue = myObject.getValue();

// Printing the retrieved value


System.out.println("Retrieved Value: " + retrievedValue);
}
}

//
Retrieved Value: 42
## Heap memory and stack memory
================================

Heap Memory:
===========
Purpose:
--------
Heap memory is used for the dynamic allocation of memory during the
runtime of a Java program.

It is the memory space where objects and arrays are allocated.

Characteristics:
----------------
Objects created in the heap exist throughout the program's execution
until they are no longer referenced and become eligible for garbage collection.

Memory is managed by the Java Virtual Machine (JVM) garbage collector,


which automatically reclaims memory occupied by objects that are no longer in use.

Object Lifecycle:
-----------------
Objects in the heap memory are created using the new keyword.

Memory allocation and deallocation are handled dynamically.

Objects can be accessed from multiple parts of the program.

Stack Memory:
=============
Purpose:
--------
Stack memory is used for method execution and managing local variables.

It stores method call information, local variables, and control flow


data.

Characteristics:
----------------
Memory in the stack is managed in a last-in, first-out (LIFO) manner,
where the last method called is the first one to finish.

Memory is allocated and deallocated automatically as method calls are


made and return.

Variable Scope:
---------------
Local variables and method call information exist in the stack.

The scope of variables is limited to the method in which they are


declared.

Memory Allocation:
------------------
Memory for variables in the stack is allocated at the beginning of the
method and released when the method exits.

Efficient memory management as the memory allocation and deallocation


are handled automatically.

Summary:
========
Heap Memory:
Used for dynamic memory allocation, storing objects and arrays, managed
by the garbage collector.

Stack Memory:
Used for method execution, managing local variables, and method call
information, memory allocated and deallocated automatically.

##### Session 7
===============

# Upcasting &down casting of a reference variable


-------------------------------------------------

Upcasting:
----------
Definition:
-----------
Upcasting is the process of casting a reference variable of child class to
one of its superclass.

It involves treating an object of a derived class as an object of its base


class.
Key Points:
-----------
Implicit Conversion:
--------------------
Upcasting is an implicit conversion, meaning it happens automatically.

No Need for Casting Operator:


-----------------------------
When assigning an object of a derived class to a reference variable of its
base class, there is no need for a casting operator.

ChildClass childObj = new ChildClass();


ParentClass parentRef = childObj; // Upcasting

Access to Base Class Members:


-----------------------------
The upcasted reference variable can only access members of the base class,
even if the actual object is of the derived class.

parentRef.baseClassMethod(); // Accessing base class method

- Example
```

class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}

class Dog extends Animal {


void bark() {
System.out.println("Dog barks");
}
}

public class UpcastingExample {


public static void main(String[] args) {
Dog myDog = new Dog();

// Upcasting
Animal animalRef = myDog;

// Accessing base class method


animalRef.sound(); // Output: Animal makes a sound

// Not accessible: animalRef.bark(); // Error: Cannot resolve


method 'bark' in 'Animal'
}
}
```

Downcasting:
------------
Definition:
-----------
Downcasting is the process of casting a reference variable of super class to
one of its childclass.

It involves treating an object of a base class as an object of its derived


class.

Key Points:
-----------
Explicit Conversion:
--------------------
Downcasting is an explicit conversion, meaning it requires the casting
operator.

Risk of Runtime Error:


----------------------
Downcasting can lead to runtime errors (ClassCastException) if the
object being casted is not actually an instance of the specified subclass

ParentClass parentRef = new ChildClass();


ChildClass childObj = (ChildClass) parentRef; // Downcasting

Access to Derived Class Members:


--------------------------------
After downcasting, the reference variable can access members of the
derived class.

childObj.derivedClassMethod(); // Accessing derived class method

- Example :
-----------
```

class Vehicle {
void start() {
System.out.println("Vehicle starting");
}
}

class Car extends Vehicle {


void drive() {
System.out.println("Car driving");
}
}

public class DowncastingExample {


public static void main(String[] args) {
Vehicle myVehicle = new Car();

// Downcasting
Car myCar = (Car) myVehicle;

// Accessing derived class method


myCar.drive(); // Output: Car driving

// Not accessible: myCar.start(); // Error: Cannot resolve method 'start'


in 'Car'
}
}

```

##### Session 8
===============
Final variables, final methods and final class
----------------------------------------------

- final (variable, method, class)


=================================
1) variable : such that variable remain constant

2) method : such that we cannot override method

3) class : such that we cannot inherit class

- Final keyword is a modifier which is used for the restriction purpose

- Final keyword can be used in 3 different ways :


- final with class
- final with variable
- final with method

- Final Variable
----------------
- Once we declare the variable as final, we cannot further re-assign
that variable

- ex:
final int X = 10; // In java, usually the final variables are
written in uppercase letters

X = 20; // error

class final{
public static void main(String[] args){
final int A = 10;
System.out.println(A); // 10 // ok

A = 20;
System.out.println(A); // error : cannot assign value to a final
variable A
}
}

- Final Method
==============
- Once we declare the method as final, we cannot further override that
method inside the sub class

- final void myMethod(){


}

- code
======
class Test{
void mobileNumber(){
System.out.println("7653459871");
}

final void atmPin(){


System.out.println("8944");
}
}

class Thief extends Test{


@Override
void mobileNumber(){
System.out.println("7653459871");
}

@Override
void atmPin(){
System.out.println("8944");
}
}

class Final{
public static void main(String[] args){
Thief tf = new Thief;
tf.mobileNumber();
tf.atmPin(); // error : overriden method is final
}
}

- Final Class
==============
- Once we declare the class as final, we cannot further extend that
class on sub class

- final class A{

- code
======

- code
======
final class Test{
void mobileNumber(){
System.out.println("7653459871");
}

final void atmPin(){


System.out.println("8944");
}
}
class Thief extends Test{ // error : cannot inherit from final test
class
@Override
void mobileNumber(){
System.out.println("7653459871");
}

@Override
void atmPin(){
System.out.println("8944");
}
}

class Final{
public static void main(String[] args){
Thief tf = new Thief;
tf.mobileNumber();
tf.atmPin(); // error : overriden method is final
}
}

# Functional Interface
======================
- Interface which is having only one abstract method and any number of
default and static methods

- Before 1.8, only public abstract methods were only allowed inside the
iterfaces

- We can invoke lambda expression by using functional interface

@FunctionalInterface
public interface MyInterface{ // ok
public void sayHello();
}

@FunctionalInterface
public interface MyInterface{ // error
public void sayHello();
public void sayBye();
}

@FunctionalInterface
public interface MyInterface{ // error

@FunctionalInterface
public interface MyInterface{ // ok
public void sayHello();

default void sayBye();


}

@FunctionalInterface
public interface MyInterface{ // ok
public void sayHello();

default void sayBye();

static void sayOk();


}

- @FunctionalInterface : is the annotation, which is used to tell the


compiler that the given interface is functional interface

#### Inner Class


=================

- It is a class which is declared inside some another class or interface


- Advantages
============
- We use inner class to group logically related classes and interfaces
together
- Makes the code cleaner
- We can access private members of outer class inside inner class

- There are 4 types of inner classes in java


--------------------------------------------
- Nested Inner Class
- Method Local Inner Class
- Anonymous Inner Class

- Static Nested Inner Class

1) Nested Inner Class


---------------------
- class which is created directly inside outer class and not inside the
function of the outer class

- Example
---------
class Outer{
class Inner{
public void show(){
System.out.println("Nested Inner Class");
}
}
}

class Main{
public static void main(String[] args){
Outer.Inner in = new Outer().new Inner();

in.show();
}
}

// output :
-----------
In a nested class method

2) Method Local Inner Class


---------------------------
- class which is created inside the method of outer class

- Example
---------
class Outer{
void outerMethod(){
System.out.println("outerMethod");

class Inner{
void innerMethod(){
System.out.println("Method Local Inner Class");
}
}

// creating the object of inner class


Inner y = new Inner();

// calling inner class method over it's object


y.innerMethod();
}
}

class Main{
public static void main(String[] args){
Outer x = new Outer();

x.outerMethod();
}
}

- output
---------
outerMethod
Method Local Inner Class

3) Anonymous Inner Class


------------------------
- class which has no name
- it is created for extending other classes or implementing other interfaces

- Example
---------
class Test{
void show(){
System.out.println("show() method of super class");
}
}

class Main{

// An anonymous class with Test as base class


static Test t = new Test(){

// show() method
void show(){

// calling method show() of Test class using super keyword


super.show();
System.out.println("Inside Anonymous Class");
}
};

public static void main(String[] args){


t.show();
}
}

output :
--------
show() method of super class

Inside Anonymous Class

4) Static Nested Inner Class


----------------------------
- static class which is created directly inside outer class and not inside
the method of the outer class

- Example
---------
class Outer{
private static void outerMethod(){
System.out.println("outerMethod");

static class Inner{


public static void display(){
System.out.println("Static Nested Inner Class");

outerMethod();
}
}
}

class Main{
public static void main(String[] args){
Outer.Inner.display();
}
}

- Output
--------
outerMethod
Static Nested Inner Class
##### Enum
==========
- Enums are used to define constant values

- For example, if we want to define


- months of the year
- days of the week
- seasons
- name of flowers
- etc.

- this values will remain constant throughout, their meaning will not
change

# Note:
-------
Enum declaration can be done outside the class or inside the class but
not inside the method

- Example 1 : Declaration outside the class


--------------------------------------------

enum Color{RED, BLUE, GREEN, BLACK;}

public class Test{


public static void main(String[] args){
Color c1 = new Color.RED;
System.out.println(c1);
}
}

- Example 2 : Declaration inside the class


--------------------------------------------

public class Test{


enum Color{
RED,
GREEN,
BLACK;
}

public static void main(String[] args){


Color c1 = new Color.GREEN;
System.out.println(c1);
}
}

- Properties of Enum in Java


=============================
- Every enum constant is always implicitly public static final

- Since it is static, we can access it by using the enum Name.

- Since it is final, we can’t create child enums

- We can declare the main() method inside the enum

- Hence we can invoke the enum directly from the Command Prompt

- We can declare enum outside the class

- We can declare enum inside the class

- We cannot declare enum inside the method

## Session 9
============

Access modifiers (public, private, protected and default)


---------------------------------------------------------
1. Public:
----------
Public is the least restrictive access modifier. Members marked as public are
accessible from any class, whether it belongs to the same package or a different
one.

Key Points:
-----------
Accessible Everywhere:
----------------------
Public members are accessible from any class.

No Restriction on Access:
-------------------------
No restrictions on access within the same package or different packages.

Example:
--------

public class PublicExample {


public int publicField;

public void publicMethod() {


// Method body
}
}

2. Private:
-----------
Private is the most restrictive access modifier. Members marked as private
are only accessible within the same class.

Key Points:
Limited Access:
---------------
Private members are accessible only within the same class.

Encapsulation:
--------------
Encourages encapsulation by restricting direct access from outside the class.

Example:
--------
public class PrivateExample {
private int privateField;
private void privateMethod() {
// Method body
}
}

3. Protected:
-------------
Protected allows access to members within the same class, same package, and
subclasses (even if they are in different packages).

Key Points:
-----------
Access within Hierarchy:
------------------------
Protected members are accessible within the same class, same package, and
subclasses.

Enhanced Encapsulation:
-----------------------
Provides a balance between encapsulation and extensibility.

Example:
--------

public class ProtectedExample {


protected int protectedField;

protected void protectedMethod() {


// Method body
}
}

4. Default (Package-Private):
-----------------------------
The default access modifier (also known as package-private) allows access
only within the same package. It is applied when no access modifier is explicitly
specified.

Key Points:
-----------
Package Scope:
--------------
Default members are accessible within the same package.

No Access from Outside the Package:


-----------------------------------
Members with default access are not accessible from outside the package.

Example:
--------

class DefaultExample {
int defaultField;

void defaultMethod() {
// Method body
}
}

Summary:
--------
Public: Accessible from any class.
Private: Accessible only within the same class.
Protected: Accessible within the same class, same package, and subclasses.
Default (Package-Private): Accessible within the same package.

Packages and import statements.


===============================

Packages:
---------
Package in Java is a way to organize related classes and interfaces into a
single namespace.

-It helps avoid naming conflicts, provides better code organization, and
supports access control.

package com.example.myapp;

Import Statements:
------------------
Import statements in Java are used to bring classes or entire packages into
scope, making them accessible without using their fully qualified names.

import com.example.myapp.MyClass;

Summary:
--------
Packages: Organize related classes and interfaces into a namespace, manage access
control, and support better code organization.

Import Statements: Bring classes or entire packages into scope, reducing verbosity
and enhancing code readability.

Static Imports:
---------------
Static imports in Java allow developers to import the static members of a
class directly into another class.
This feature is particularly useful when working with utility classes or
classes containing a set of related constants or methods.

import static packageName.ClassName.staticMember;

Constructor chaining (with and without packages)


================================================

Constructor Chaining: The process of calling one constructor from another, enabling
the reuse of code and centralizing initialization logic.

Constructor Chaining Without Packages:


--------------------------------------

// File: Student.java
public class Student {
private String name;
private int age;

// Default constructor
public Student() {
// Default values or additional initialization logic
this("Unknown", 0);
}

// Parameterized constructor
public Student(String name, int age) {
// Shared initialization logic
this.name = name;
this.age = age;
}
// Getter and Setter methods...
}

In this example, the default constructor (Student()) calls the parameterized


constructor (Student(String name, int age)) using this("Unknown", 0);. This ensures
that common initialization logic is shared between constructors.

Constructor Chaining With Packages:


-----------------------------------

// File: com.example.school.Student.java
package com.example.school;

public class Student {


private String name;
private int age;

// Default constructor
public Student() {
// Default values or additional initialization logic
this("Unknown", 0);
}

// Parameterized constructor
public Student(String name, int age) {
// Shared initialization logic
this.name = name;
this.age = age;
}

// Getter and Setter methods...


}

The constructor chaining remains the same, and the concept of constructor chaining
is independent of the package structure.
Accessing protected variables and methods outside the package
=============================================================
Key Points:
Access within the Same Class:
-----------------------------
protected members are accessible within the same class.

Access within the Same Package:


-------------------------------
protected members are accessible within the same package.

Access by Subclasses:
---------------------
protected members are accessible by subclasses, even if they are in different
packages.

// File: com.example.packageA.BaseClass.java
package com.example.packageA;

public class BaseClass {


protected int protectedVariable;

protected void protectedMethod() {


// Method body
}
}

// File: com.example.packageB.DerivedClass.java
package com.example.packageB;

import com.example.packageA.BaseClass;

public class DerivedClass extends BaseClass {


public void accessProtectedMember() {
int value = protectedVariable; // Accessing protected variable
protectedMethod(); // Accessing protected method
}
}
### Session 10
---------------
---------------

Garbage Collection: Automatic memory management process in Java that identifies and
cleans up objects that are no longer reachable.

Mark and Sweep: Common garbage collection algorithm involving the identification
and removal of unreachable objects.

Generational Garbage Collection: Division of heap into Young Generation and Old
Generation, with different collection strategies for each.

Garbage Collection Triggers: Events like memory allocation or explicit invocation


can trigger garbage collection.

Manual Garbage Collection


-------------------------

class MyClass {
public void myMethod() {
// Some code

// Suggest garbage collection


System.gc();
}
}
Automatic Garbage Collection
----------------------------

class MyClass {
public void myMethod() {
// Some code

// Automatic garbage collection by the JVM


// No explicit code needed
}
}

Requesting Garbage Collection in Java:


--------------------------------------
Using System.gc():
------------------
The System class in Java provides a gc() method, which is a hint to the JVM
that it should run the garbage collector. This method is static, so it's invoked on
the System class directly.

public class GarbageCollectionExample {


public static void main(String[] args) {
// Some code

// Explicitly request garbage collection


System.gc();
}
}
Using Runtime.getRuntime().gc():
--------------------------------
The Runtime class also provides a gc() method that can be used to request
garbage collection. This method is similar to System.gc().

public class GarbageCollectionExample {


public static void main(String[] args) {
// Some code

// Explicitly request garbage collection


Runtime.getRuntime().gc();
}
}

Summary:
--------
Developers can request garbage collection in Java using System.gc() or
Runtime.getRuntime().gc().

Invoking these methods is a hint to the JVM, but the actual execution depends on
the JVM's decision.

Manually requesting garbage collection is generally discouraged, and developers


should trust the JVM's built-in garbage collector for efficient memory management.

Different ways to make object eligible for garbage collection: (Nulling a reference
variable, Re-assigning a reference variable & island of isolation)
=================================================================
Summary:
--------
Making an object eligible for garbage collection involves ensuring that there are
no live references to it.

Nulling a reference variable, reassigning a reference variable, and breaking


references in an island of isolation are common techniques.

Proper care should be taken to avoid unintentional loss of references to objects


still needed in the program.

1. Nulling a Reference Variable:


--------------------------------
Nulling a reference variable involves setting the variable to null,
indicating that it no longer points to any object. This makes the object previously
referenced by the variable eligible for garbage collection.

public class NullingReferenceExample {


public static void main(String[] args) {
MyClass obj = new MyClass(); // Creating an object

// Nulling the reference variable


obj = null; // The object is now eligible for garbage collection
}
}

2. Reassigning a Reference Variable:


------------------------------------
Reassigning a reference variable involves assigning it to a new object or a
different reference. This action makes the previous object pointed to by the
variable eligible for garbage collection.

public class ReassigningReferenceExample {


public static void main(String[] args) {
MyClass obj1 = new MyClass(); // Creating an object
MyClass obj2 = new MyClass(); // Creating another object

// Reassigning the reference variable


obj1 = obj2; // The first object is now eligible for garbage collection
}
}

3. Island of Isolation:
-----------------------
An "island of isolation" refers to a group of objects that reference each
other but are not accessible from the root of the object graph. If the references
between these objects are broken, the entire group becomes eligible for garbage
collection.

public class IslandOfIsolationExample {


public static void main(String[] args) {
MyClass obj1 = new MyClass();
MyClass obj2 = new MyClass();

// Creating an island of isolation


obj1.setOther(obj2);
obj2.setOther(obj1);

// Breaking the references


obj1 = null;
obj2 = null; // The entire island is now eligible for garbage collection
}
}

Finalize Method
===============

The finalize() method in Java is a method provided by the Object class that allows
an object to perform cleanup operations before it is garbage collected.

This method is called by the garbage collector just before an object is reclaimed,
giving the object an opportunity to release resources or perform other necessary
cleanup tasks.

```

protected void finalize() throws Throwable {


// Cleanup code or resource release logic
}
```

```

public class ResourceHolder {


private int resourceId;

public ResourceHolder(int resourceId) {


this.resourceId = resourceId;
}

// Other methods...

// Override finalize() for resource cleanup


protected void finalize() throws Throwable {
try {
// Release resources or perform cleanup operations
releaseResource(resourceId);
} finally {
// Call superclass finalize() to ensure proper cleanup
super.finalize();
}
}

private void releaseResource(int resourceId) {


// Logic to release the resource
System.out.println("Resource " + resourceId + " released.");
}
}

```

Summary:
--------
The finalize() method is a part of the Object class in Java and is called by the
garbage collector before reclaiming memory.

It is declared with the protected access modifier and can be overridden in


subclasses to provide cleanup logic.

Reliability of finalize() is limited, and alternatives like implementing


AutoCloseable or providing explicit resource release methods are often preferred.
Wrapper classes and constant pools
==================================

Wrapper Classes in Java:


------------------------
Wrapper classes in Java are a set of classes provided in the java.lang
package that encapsulate primitive data types (such as int, char, double) into
objects.

The purpose of wrapper classes is to provide a way to represent primitive


types as objects, allowing them to be used in scenarios where objects are required.

The wrapper classes include:


Integer
Double
Character
Boolean
Byte
Short
Long

Purpose of Wrapper Classes:


---------------------------
Object Representation:
----------------------
Wrapper classes allow primitive data types to be treated as objects

Compatibility with Collections:


-------------------------------
Many Java Collection classes (e.g., ArrayList, HashMap) work with
objects, not primitive types.

Null Values:
------------
Wrapper classes can represent null, whereas primitive types cannot.
Java Generics:
--------------
Wrapper classes are often used in Java Generics because generics do not
work with primitive types.

Constant Pools:
---------------
Part of the Java Class file structure containing a table of symbolic
references, including string literals, numeric literals, class/method/field names,
etc.

Key Points about Constant Pools:


String Interning:
-----------------
String literals in Java are often interned, meaning that only one copy
of each distinct string literal is stored in the constant pool. This reduces memory
overhead.

Class and Method References:


----------------------------
The constant pool contains references to class names, method names, and
field names used in the class.

Numeric Literals:
-----------------
Numeric literals, such as integer and floating-point constants, are
also stored in the constant pool.

Performance Optimization:
-------------------------
The constant pool is used for performance optimizations.

Autoboxing and Unboxing:


------------------------
When primitive types are converted to their corresponding wrapper classes
(autoboxing) or vice versa (unboxing),

- Example
---------
```

public class WrapperAndConstantPoolExample {


public static void main(String[] args) {
// Wrapper class example
Integer intObj = new Integer(42); // Boxing (explicit)
int intValue = intObj.intValue(); // Unboxing (explicit)

// Constant pool example


String str1 = "Hello"; // String literal (interned)
String str2 = "Hello"; // Reference to the same string in
the constant pool
String str3 = new String("Hello"); // New string object
(not interned)

System.out.println(str1 == str2); // true (same reference)


System.out.println(str1 == str3); // false (different
references)
}
}

```

String class, StringBuffer& StringBuilder class


===============================================
String Class in Java:
---------------------
The String class in Java is a fundamental class that represents sequences of
characters.

It is part of the java.lang package and is widely used in Java programming.

Strings in Java are immutable, meaning once a String object is created, its
value cannot be changed.

Key Features:
-------------
Immutable:
----------
Once a String object is created, its content cannot be modified.

String Literals:
----------------
String literals, enclosed in double quotes, are automatically interned in the
String pool. This means that duplicate string literals share the same memory.

Java String Pool:


-----------------
The String pool is a special memory area in the heap that stores string
literals. It helps in saving memory by reusing common strings.

String Concatenation:
---------------------
String concatenation in Java can be performed using the + operator. However,
it is recommended to use StringBuilder or StringBuffer for more efficient
concatenation, especially in loops.

Methods:
--------
The String class provides various methods for working with strings, such as
length(), charAt(), substring(), equals(), indexOf(), and more.

StringBuffer and StringBuilder Classes in Java:


-----------------------------------------------
Both StringBuffer and StringBuilder are classes in Java that represent
mutable sequences of characters.

They are part of the java.lang package and provide a way to modify the
content of a string.

Common Features:
----------------
Mutable:
--------
Both StringBuffer and StringBuilder are mutable, meaning that their contents
can be changed.

StringBuilder (JDK 5 onwards):


------------------------------
StringBuilder was introduced in Java 5 as a non-synchronized alternative to

StringBuffer. It is more efficient in a single-threaded environment.


StringBuffer:
-------------
Synchronized:
-------------
StringBuffer is synchronized, making it thread-safe. This means that it
ensures multiple threads cannot access it simultaneously.

Performance in Multi-threaded Environments:


-------------------------------------------
Due to synchronization, StringBuffer may have lower performance compared to
StringBuilder in a single-threaded environment.

Methods:
--------
StringBuffer provides methods similar to String for concatenation, appending,
reversing, deleting, and inserting.

StringBuffer sb = new StringBuffer("Hello");


sb.append(" World"); // "Hello World"

StringBuilder sb = new StringBuilder("Hello");


sb.append(" World"); // "Hello World"

Comparison Summary:
String:
-------
Immutable.
String pool for memory optimization.
String concatenation efficiency may vary.

StringBuffer:
-------------
Mutable (synchronized).
Thread-safe.
Performance may be impacted in a single-threaded environment.

StringBuilder:
--------------
Mutable (non-synchronized).
Not thread-safe.
Better performance in a single-threaded environment.

String Pool in Java:


====================
The String Pool is a special area in the Java heap memory that stores a pool
of string literals.

It is a mechanism used to optimize memory usage and improve performance by


reusing common string values.

String Literals:
----------------
String literals, enclosed in double quotes, are automatically interned in the
String pool. This means that duplicate string literals share the same memory.

Java String Pool:


-----------------
The String pool is a special memory area in the heap that stores string
literals. It helps in saving memory by reusing common strings.

String str1 = "Hello"; // Interned in the String Pool


String str2 = "Hello"; // Reference to the same string in the pool

String str3 = "Hello" + " World"; // Interned if compiler optimization is applied


String str4 = new String("Hello").intern(); // Interned explicitly

## Session 13 & 14
==================

Exception hierarchy, Errors, Checked and un-checked exceptions.


===============================================================

##### Exception
===============
- An unexpected event that occured during the runtime is called as Exception

- Real World Example :


----------------------
1) Lets take an example of Shyam, He is a guys who planned a tour to
Goa with his friends and during the journey what if shyam falls sick.

This unexpected event which occured at the normal flow is called


as Exception

- Actual Example :
----------------
1) ArithmeticException (Divide by Zero):
----------------------------------------
- We wrote a program to divide 2 numbers what if the 2nd number
which is used for the division operation is zero

- That's and unexpected situation or we can say Exception

public class DivideByZeroExample {


public static void main(String[] args) {
int numerator = 10;
int denominator = 0;

try {
int result = numerator / denominator; // This line
will throw ArithmeticException
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Error: " + e.getMessage());
}
}
}

2) NullPointerException:
------------------------
- String str = null;
- int length = str.length(); // This line will throw
NullPointerException

- We are trying to invoke a method on a object reference that is


null

public class NullPointerExceptionExample {


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

try {
int length = str.length(); // This line will throw
NullPointerException
System.out.println("Length: " + length);
} catch (NullPointerException e) {
System.out.println("Error: " + e.getMessage());
}
}
}

3) ArrayIndexOutOfBoundsException:
----------------------------------
- int[] numbers = {1, 2, 3};

- int value = numbers[5]; // This line will throw


ArrayIndexOutOfBoundsException

- We are trying to access an array element with illegal index

public class ArrayIndexOutOfBoundsExceptionExample {


public static void main(String[] args) {
int[] numbers = {1, 2, 3};

try {
int value = numbers[5]; // This line will throw
ArrayIndexOutOfBoundsException
System.out.println("Value: " + value);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Error: " + e.getMessage());
}
}
}

4) FileNotFoundException:
-------------------------
- File file = new File("nonexistent.txt");

- FileReader reader = new FileReader(file); // This line will throw


FileNotFoundException

- We are trying to access a file that doesn't exist

import java.io.File;
import java.io.FileReader;
import java.io.FileNotFoundException;

public class FileNotFoundExceptionExample {


public static void main(String[] args) {
try {
File file = new File("nonexistent.txt");
FileReader reader = new FileReader(file); //
This line will throw FileNotFoundException
System.out.println("File found!");
} catch (FileNotFoundException e) {
System.out.println("Error: " + e.getMessage());
}
}
}

##### Exception Handling


========================
- Exception Handling is an alternate source through which we can tackle the
exception

- Real World Example :


----------------------
1) So in the above example if the group asks to stop the bus at some
medical store or hospital and took the medicine then this process of handling the
unexpected situation is called as Exception Handling

- Actual Example :
----------------
1) What if the numerator is zero, we can say that can't divide a number
by zero

2) Can't invoke a method on object reference which is assigned a null


value

3) The element for the index doesn't exit

4) This file doesn't exist in the system

##### Exception Handling Techniques


===================================
Object oriented programming has provided several techniques to handle the
exceptions :

1) For Predefined Exception


============================

- try
- catch

2) For User-defined Exception


=============================
( If the user wanted to create his own exception )

- throw
- throws

3) And you want some line of code to be executed no matter if the exception arises
or not then

- finally (No matter the exception arises or not this block will get
executed)

##### 3)Describe the exception hierarchy in java.


=================================================
Refer Image
-----------

Object -> Throwable -> Error


-> Exception -> Checked Exception
-> Unchecked Exception (Runtime
Exception)

##### 4)What is the difference between “Exception” and “Error”?


===============================================================
- Object class is parent class of all the classes
- Throwable is the parent class of Error and Exception

- Error
=======
- Error occurs due to lack of system resource
- Error cannot be recovered
- 1 type : Unchecked (Runtime Error)

- Exception
===========
- Exception occurs due to program
- Exception can be recovered
- 2 Types of Exception:
- Checked (Compiletime Exception)
- Unchecked (Runtime Exception)

##### Checked and Unchecked Exception


======================================

- NOTE
=======
- You never gets an exception at compile time, that is actually a
warning to you by the compiler that this exception can come at run time.

- Exception occurs only at a run time

- Examples
==========
- Mother(Compiler) -> warns son to take the id, purse, etc such that
he should not face an unexpected event during the normal flow
===================
- Checked Exception
===================
- The exception which can be checked by the compiler

- Most common example : syntax issues

- Checked exception must be handled else the program will not be


compiled

====================
- Real World Example
====================
- Mother as a compiler son ko bata sakti hai ki
- PurseNotTakenException
- CompanyIdNotTakenException

- Examples
==========
- ClassNotFoundException

- IOException
- EOFException
- FileNotFoundException
- InterruptedIOException

- NoSuchMethodException

- SQLException

- RemoteException

- InterruptedException

// Custom checked exception


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

// Example usage of the checked exception


public class CheckedExample {
public static void main(String[] args) {
try {
throw new CustomCheckedException("This is a custom
checked exception.");
} catch (CustomCheckedException e) {
System.out.println("Caught Checked Exception: " +
e.getMessage());
}
}
}

======================
- Unchecked Exception
======================
- The exception which are ignored by the compiler

- Runtime exception

- Unchecked exception if not handled then JVM throws an exception

- Real World Example


====================
- Mother as a compiler son ko "nahi" bata sakti hai ki
- BikePunctureException
- TrafficFoundException

- Example
=========
- ArithmeticException

- ClassCastException

- NullPointerException

- IndexOutOfBoundException
- ArrayIndexOutOfBoundException
- StringIndexOutOfBoundException

- IllegalArgumentException
- NumberFormatException

// Custom unchecked exception


class CustomUncheckedException extends RuntimeException {
public CustomUncheckedException(String message) {
super(message);
}
}

// Example usage of the unchecked exception


public class UncheckedExample {
public static void main(String[] args) {
throw new CustomUncheckedException("This is a custom
unchecked exception.");
}
}
##### throw and throws exception
================================
- throw :
---------
- throw keyword is used to create an exception object manually

- programmer is responsible for the creation of an exception object

- throw keyword is mainly used for runtime exceptions (unchecked


exceptions)

- In case of throw keyword we can throw only single exception

- throw keyword is used with the method

- throw keyword is followed by new instance

- we cannot write any statement after throw keyword (Unreachable


Statement)

public class ThrowExample {


public static void main(String[] args) {
try {
throw new RuntimeException("This is a custom runtime
exception.");
} catch (RuntimeException e) {
System.out.println("Caught Exception: " +
e.getMessage());
}
}
}

- throws :
----------
- throws keyword is used to declare the exception while creating
function itself

- it indicates the caller method that a given exception can occur so we


have to handle it either try catch block or again declare it using throws keyword

- throws keyword is mainly used for compile time exceptions (checked


exceptions)

- In case of throws keyword we can declare multiple exceptions


separated by commas

- throws keyword is used with the method signature


- throws keyword is followed by the class

- No such rule in throws

public class ThrowsExample {


public static void main(String[] args) {
try {
methodThatThrowsException();
} catch (CustomCheckedException e) {
System.out.println("Caught Checked Exception: " +
e.getMessage());
}
}

public static void methodThatThrowsException() throws


CustomCheckedException {
throw new CustomCheckedException("This is a custom checked
exception.");
}
}

class CustomCheckedException extends Exception {


public CustomCheckedException(String message) {
super(message);
}
}

Finalize Method
===============

The finalize() method in Java is a method provided by the Object class that allows
an object to perform cleanup operations before it is garbage collected.

This method is called by the garbage collector just before an object is reclaimed,
giving the object an opportunity to release resources or perform other necessary
cleanup tasks.

```

protected void finalize() throws Throwable {


// Cleanup code or resource release logic
}

```
##### finally block
====================

- finally is a block that will always be executed whether the exception is


handled or not

- It guarantees to be invoked unliked finalize() method, thus we can use it


to release resources like file, database connection, socket, etc.

- If exception occurs then flow of execution will be


- try -> catch -> finally

- If exception does not occurs then flow of execution will be


- try -> finally

- We cannot directly use finally block .. [wrong code]

- try -> risky code


- catch -> handling code
- finally -> clean up code

- Multiple finally block is not possible unlike catch block

- if inside try block you provide jump or break statement than too finally
block will get executed. But before the jump statements.
- try -> finally -> break, continue, etc...

5) in which scenarios finally block will not get executed?


----------------------------------------------------------
1) System.exit(0) in try or catch
2) Any unhandled exception is raised inside finally block itself
3) If any error occured inside try block
4) if some thread gets terminated in between
Exception Propagation
=====================
- Exception Propagation is the technique through which we can handle the
exception inside the caller method

- Note: By default, Checked Exceptions are not forwarded in calling chain


(propagated).

- An exception is first thrown from the top of the stack and if it is not
caught, it drops down the call stack to the previous method.

// output of exception will occur from bottom to top

// every method will have 2 different paths, either handle the exception using try-
catch block or it can forward the exception to it's previous calling block

class Test{
public static void main(String[] args){
m1(); #1
}

public static void m1(){


m2(); #2
}

public static void m2(){


System.out.println(10/0); #3
}
}

output
======
exception at line number #1, #2, #3

Solution
========
class Test{
public static void main(String[] args){
m1(); #1
}

public static void m1(){


try{
m2(); #2
} catch(ArithmeticException e){
System.out.println("Exception Handled by M1");
}

}
public static void m2(){
System.out.println(10/0); #3
}
}

Output
======
Exception Handled by M1

##### Multiple Catch Block


===========================
- In Multiple Catch Block we have single try block and multiple catch blocks
- At a time only one exception can occur and only one catch block can handle
the exception
- The sequences of catch block must be starting from the most obivious catch
exception handler to the most general catch handler
- ex: 1) ArithmeticException e
2) NullPointerException e
3) Exception e

try{
int a = 10/ 0;
System.out.println(a); #1

int b[5];
System.out.println(b[7); #2

String c = null;
c.toUpperCase(); #3
}

catch(ArrayIndexOutOfBoundException e){

}
catch(ArithmeticException e){

}
catch(Exception e){

- Exception is the super class of all the exceptions and it can handle any
type of exception
- Exception (super class) must be placed at the last position inside the
multi catch block
Code
====

class Test{
public static void main(String[] args){
try{
int a = 10, b = 2, c;
c = a / b;
System.out.println(c); // ArithmeticException fi b = 0

int arr[] = {10, 20, 30};


System.out.println(arr[0]); // ArrayIndexOutOfBoundException if
arr[x] and x > 2

String str = "Radhe";


System.out.println(str.toUpperCase()); // Exception Handled if
str = null;
}
catch(ArrayIndexOutOfBoundException e){
System.out.println("ArrayIndexOutOfBoundException");
}
catch(ArithmeticException e){
System.out.println("ArithmeticException");
}
catch(NumberFormatException e){
System.out.println("NumberFormatException");
}
catch(Exception e){
System.out.println("Exception Handled");
}
}
}

##### Questions and Answers


============================

## How do you handle checked exceptions?


-----------------------------------------
- Checked Exceptions can be handled by either using a try-catch block or by
using the throws clause in the method declaration.

- If these exceptions are not handled properly, then the program would fail
to compile.
Session 15:
===========
java.io & java.nio Package
===========================

##### Serialization and Deserialization


=======================================
=======================================

Serialization
-------------
- Serialization is the process of converting the state of an object into
byte-stream such that we can transfer the state of an object over a network or we
can save it inside a file

- java.io.Serializable is a marker interface which is implemented on a class


in order to tell the compiler that the object of this class is eligible for
serialization process

- For Serialization, we call writeObject() method of ObjectOutputStream class

### Full Code


=============
1)
Solution.java
=============
import java.io.Serializable;

public class Solution implements Serializable{


private String name;
private String email;
private int age;
private String address;

public Solution(String name, String email, int age, String address){


this.name = name;
this.email = email;
this.age = age;
this.address = address;
}

public Solution(){
}

public void setName(String name){


this.name = name;
}

public void setEmail(String email){


this.email = email;
}

public void setAge(int age){


this.age = age;
}

public void setAdd(String address){


this.address = address;
}

public String getName(){


return name;
}

public int getAge(){


return age;
}

public String getEmail(){


return email;
}

public String getAdd(){


return address;
}

public void displayName(){


System.out.println("Hi my name is " + this.name);
}
}

2)
Serial.java
===========
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

public class Serial {


public static void main(String[] args) {

//creating an object of Student Class

Solution student = new Solution("Ravi Das", "ravidas1@gmail.com",


24, "Jaunpur");

FileOutputStream fos = null;


ObjectOutputStream oos = null;

try
{
// write data to file
fos = new FileOutputStream("test.txt");

oos = new ObjectOutputStream(fos);

// How to serialize
oos.writeObject(student);
System.out.println("State of an object is saved into a
file...");
}
catch(IOException e)
{
e.printStackTrace();
}
finally{

try{
if(oos != null){
oos.close();
}
if(fos != null){
fos.close();
}
}
catch(IOException e){
e.printStackTrace();
}
}

}
}

De-serialization
----------------
- De-serialization is the process of converting byte-stream into the state of
an object such that we can reconstruct the byte-stream into human understandable
data

- We do not need to implement any interface on the class for De-serialization

- For De-serialization, we call readObject() method of ObjectInputStream


class
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;

public class Deserial {


public static void main(String[] args) {

FileInputStream fis = null;


ObjectInputStream ois = null;

try
{
fis = new FileInputStream("test.txt");
ois = new ObjectInputStream(fis);

// Deserialize
Solution student = (Solution) ois.readObject();
System.out.println("After De-serializing : ");
System.out.println(student.getName());
System.out.println(student.getEmail());
System.out.println(student.getAge());
System.out.println(student.getAdd());
}
catch(IOException | ClassNotFoundException e)
{
e.printStackTrace();
}
finally
{
try
{
if(ois != null){
ois.close();
}
if(fis != null){
fis.close();
}
}
catch(IOException e){
e.printStackTrace();
}
}
}
}

##### Transient
===============
- Transient is a keyword in java, which tell the compiler that we do not want
to serialize this data member from the class

- Example
---------

import java.io.Serializable;
public class Solution implements Serializable
{
private String name;
private String email;
transient private int age;
--------------------------
private String address;
...

##### Shallow copy and deep copy (Prototype Design Pattern)


================================
================================

- There are 2 types of copy :


- Shallow copy
- Deep Copy

# Note : Only for objects not for primitive data types

- Shallow Copy
==============
- Shallow Copy copies the reference of the object and not the actual object,
so if some other object updates the value of that object which is shallow copied
using the reference

- than it will affect the shallow copied data as well

- clone() method bydefault does a shallow copy

- for using clone method


-------------------------
- implement clonable interface
- override clone method from Object class

// full code
------------

class Address{
private String city;
private String street;

public Address(String city, String street){


this.city = city;
this.street = street;
}

public void setCity(String city){


this.city = city;
}

public void setStreet(String street){


this.street = street;
}

public String getCity(){


return city;
}

public String getStreet(){


return street;
}

@Override
public String toString(){
return "Address : City = " + city + " and Street = " +
street;
}
}

class Person{
private String name;
private Address address;

public Person(String name, Address address){


this.name = name;
this.address = address;
}

public void setName(String name){


this.name = name;
}

public void setAddress(Address address){


this.address = address;
}

public String getName(){


return name;
}

public Address getAddress(){


return address;
}

@Override
public String toString(){
return "Person : Name = " + name + " and Address : " +
address;
}
}
public class Solution{
public static void main(String[] args){
Address originalAddress = new Address("Banglore", "Road
1");

Person originalPerson = new Person("Rajiv",


originalAddress);

// Shallow Copy
Person shallowCopy = originalPerson;

// Modify the original address


originalAddress.setCity("Mumbai");

// Display both objects


System.out.println("Original Person : " + originalPerson);

System.out.println("Shallow Copy Person : " + shallowCopy);

}
}

/*
Output
======
Original Person : Person : Name = Rajiv and Address : Address : City = Mumbai and
Street = Road 1

Shallow Copy Person : Person : Name = Rajiv and Address : Address : City = Mumbai
and Street = Road 1
*/

- Deep Copy
===========
- Deep copies the actual object itself, so now if any object changes the
value of the object from which deep copy created the copy of object for itself...
the changes will not affect the deep copy data at all

full code
=========

// package Deep Copy;

class Address{
private String city;
public Address(String city){
this.city = city;
}

public String getCity(){


return city;
}

public void setCity(String city){


this.city = city;
}

@Override
public String toString(){
return "City = " + city;
}
}

class Person{
private String name;
private Address address;

public Person(String name, Address address){


this.name = name;
this.address = address;
}

public Address getAddress(){


return address;
}

public void setAddress(Address address){


this.address = address;
}

public String getName(){


return name;
}

public void setName(String name){


this.name = name;
}

@Override
public String toString(){
return "Name : Person = " + name + " and Address : " +address;
}
}

public class Solution {


public static void main(String[] args) {
Address oriAddress = new Address("Banglore");
Person oriPerson = new Person("Rajiv", oriAddress);

// Shallow Copy
// Person shallowCopy = oriPerson;
// Deep Copy
Person deepCopy = new Person(oriPerson.getName(), new
Address(oriPerson.getAddress().getCity()));

oriAddress.setCity("Pune");

System.out.println("Original Person : " + oriPerson);


System.out.println("Shallow Copy Person : " + deepCopy);

}
}

/*
Output
------

Original Person : Name : Person = Rajiv and Address : City = Pune

Shallow Copy Person : Name : Person = Rajiv and Address : City = Banglore

*/

##### NIO Package


=================
- It is used to perform read, write, create delete operation on a file
- We already had IO package for that
- But NIO comes with multithreading capabilities
- It is also called as Non-blocking thread
- But the actual meaning is networking thread
- Using IO Package, at a time we can either read or write onto a file

- But using NIO Package, at a time we can read as well as write on to a


file

// code
-------
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

public class Solution{


public static void main(String[] args) {
Path path = Paths.get("test.txt");
try{
Files.createFile(path); // for creating a new file

Files.write(path, Arrays.asList("Hello")); // for writing


into a file

// Files.write(path, Collections.emptyList()); // for


removing everything written in a file
}
catch(IOException e){
e.printStackTrace();
}
// finally{
// try{
// Files.deleteIfExists(path);
// System.out.println("Files deleted in a finally
block.");
// }
// catch(IOException e){
// e.printStackTrace();
// }
// }

System.out.println("Done ... ");


}
}

##### Brief introduction to InputStream, OutputStream, Reader and Writer interfaces


===================================================================================

InputStream:
============
- The InputStream interface is designed for reading bytes from a source

- Common subclasses include


---------------------------
- FileInputStream
- ByteArrayInputStream
- DataInputStream
OutputStream:
=============
- The OutputStream interface is designed for writing bytes to a destination

- Common subclasses include


---------------------------
- FileOutputStream
- ByteArrayOutputStream
- DataOutputStream

InputStream and OutputStream Example:


=====================================

// package InputStream and OutputStream;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;

public class Solution {


public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try{
// write to a file
fos = new FileOutputStream("test.txt");
String content = "Hello, World!";
byte[] byteContent = content.getBytes();
fos.write(byteContent);

// reading from a file


fis = new FileInputStream("test.txt");
int data;
while((data = fis.read()) != -1){
System.out.print((char) data);
}
}catch(IOException e){
e.printStackTrace();
}
finally{
try{
if(fos != null){
fos.close();
}
if(fis != null){
fis.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
Reader:
=======
- The Reader interface is designed for reading characters from a source.

- Common subclasses include


---------------------------
- FileReader
- StringReader
- BufferedReader

Writer:
=======
- The Writer interface is designed for writing characters to a destination.

- Common subclasses include


- FileWriter
- StringWriter
- BufferedWriter

FileReader and FileWriter Example:


==========================
// package Reader Writer;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Solution {


public static void main(String[] args) {
FileReader reader = null;
FileWriter writer = null;

try{
// writing to a file
writer = new FileWriter("test.txt");
String content = "Hello, Writer!";
writer.write(content);

// reading from a file


reader = new FileReader("test.txt");
int data;
while((data = reader.read()) != -1){
System.out.print((char) data);
}
}catch(IOException e){
e.printStackTrace();
}
finally{
try{
if(reader != null){
reader.close();
}
if(writer != null){
writer.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}

Session 16:
===========
Object Class & java.util Package
================================

Date, DateTime, Calendar


========================
1. Date Class:
--------------
The Date class, part of the java.util package, represents a specific instant
in time with millisecond precision.

Despite its widespread usage in older Java code, it has several limitations
and is considered outdated.
Key Points:
-----------
Mutable:
--------
The Date class is mutable, allowing changes to its values after creation.

Deprecated Methods:
-------------------
Many methods within the Date class are deprecated due to issues and
limitations.

No Time Zone Information:


-------------------------
Date objects do not store time zone information, requiring separate handling
of time zones.

Time in Milliseconds:
---------------------
Time is represented in milliseconds since the epoch (January 1, 1970,
00:00:00 GMT).

```

import java.util.Date;

public class DateExample {


public static void main(String[] args) {
Date currentDate = new Date(); // Current date and time
System.out.println(currentDate);
}
}

```

Calendar Class:
---------------
The Calendar class, also in the java.util package, is an abstract class
providing methods for date and time manipulation.

It offers improved functionality compared to the Date class.

Key Points:
-----------
Locale-Sensitive:
-----------------
Calendar is locale-sensitive, considering regional differences in the first day of
the week and months.

Field Access:
-------------
Access and modification of individual fields such as year, month, day, hour,
minute, and second.

Month Indexing:
---------------
Months are zero-indexed, i.e., January is 0 and December is 11.

Extensibility:
--------------
The Calendar class is extensible, allowing concrete implementations like
GregorianCalendar.

```

import java.util.Calendar;

public class CalendarExample {


public static void main(String[] args) {
Calendar calendar = Calendar.getInstance(); // Current date and time
System.out.println(calendar.getTime());
}
}

```

LocalDateTime Class (java.time Package):


----------------------------------------
With the advent of Java 8, the java.time package introduced modern date and
time classes.

LocalDateTime is a part of this package, offering immutability and better


functionality than its predecessors.

Key Points:
-----------
Immutable:
----------
Instances of LocalDateTime are immutable, preventing changes after creation.

No Time Zone Information:


-------------------------
Similar to Date, LocalDateTime doesn't contain time zone information, often used in
conjunction with ZoneId for time zone handling.

Modern API:
-----------
The java.time package provides a comprehensive and modern API for dates, times, and
durations

```

import java.time.LocalDateTime;

public class LocalDateTimeExample {


public static void main(String[] args) {
LocalDateTime currentDateTime = LocalDateTime.now(); // Current
date and time
System.out.println(currentDateTime);
}
}

```

Interview Notes:
----------------
Why is the Date class considered outdated in Java?
--------------------------------------------------
The Date class is considered outdated due to mutability, deprecated methods, and
the lack of modern features like immutability and time zone support.

How does the Calendar class differ from the Date class?
-------------------------------------------------------
The Calendar class provides more functionality for date and time manipulation
compared to the Date class. It is locale-sensitive, allows access to individual
fields, and is extensible.
What are some drawbacks of the Date class in Java?
--------------------------------------------------
Drawbacks include mutability, deprecated methods, poor time zone handling, and the
absence of a modern API.

What advantages does the LocalDateTime class offer over the Date and Calendar
classes?
-----------------------------------------------------------------
LocalDateTime provides immutability, better manipulation features, and is part of
the modern java.time package, addressing many issues with the older date and time
classes.

Converting Date to String and String to Date using SimpleDateFormat class


===============================================================

Converting Date to String:


--------------------------

```

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateToStringExample {


public static void main(String[] args) {
Date currentDate = new Date();

// Creating SimpleDateFormat with desired format


SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

// Formatting Date to String


String formattedDate = sdf.format(currentDate);

System.out.println("Formatted Date: " + formattedDate);


}
}
```

Converting String to Date:


--------------------------

```

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class StringToDateExample {


public static void main(String[] args) {
String dateString = "15-01-2023 10:30:00";

// Creating SimpleDateFormat with the same format used for formatting


SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

try {
// Parsing String to Date
Date parsedDate = sdf.parse(dateString);
System.out.println("Parsed Date: " + parsedDate);
} catch (ParseException e) {
e.printStackTrace();
}
}
}

```
Object Class: Overriding to String, equals &hashcodemethod
===========================================================

1. toString() Method:
---------------------

```

public class MyClass {


private int id;
private String name;

// Constructors, getters, setters

@Override
public String toString() {
return "MyClass{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

```

2. equals(Object obj) Method:


-----------------------------

```

public class MyClass {


private int id;
private String name;

// Constructors, getters, setters

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;

MyClass myClass = (MyClass) obj;

return id == myClass.id && name.equals(myClass.name);


}
}
```

3. hashCode() Method:
---------------------

```

public class MyClass {


private int id;
private String name;

// Constructors, getters, setters

@Override
public int hashCode() {
return Objects.hash(id, name);
}
}

```

Summary:
--------
toString(): Provides a string representation of the object.

equals(Object obj): Checks if two objects are equal based on their content.

hashCode(): Returns a hash code value for the object.


Interview Tips:
---------------

Why is toString() important?


----------------------------
toString() aids in debugging and provides a human-readable representation of an
object.

What happens if equals() and hashCode() are not overridden together?


------------------------------------------------------------------
It may lead to inconsistent behavior when objects are used in hash-based data
structures like HashMap.

When is toString() implicitly called?


-------------------------------------
It is called implicitly when an object is concatenated with a string or printed
using System.out.println().

What is the role of Objects.hash() in hashCode()?


-------------------------------------------------
Objects.hash() simplifies the calculation of hash codes for multiple fields in the
class.

Session 22:
===========

### Multithreading
==================
- Q) What is process (task)?
- Program in execution is called as process.
- It is a heavyweight process
- Q) What is thread?
- A thread is a lightweight sub-process.

- Q) Why do I say that?


- Threads resides inside a process.
- And it is executed inside a process.

- Q) What is multithreading and what is multiprocessing (multitasking) then?


- Process of exectuing multiple threads at a time is called as
multithreading.

- and simlarly, The process of executing multiple processes at a time


is called as multiprocessing.

- Q) Why we require multithreading, if there already exists multiprocessing ?


- Suppose multiple processes are running simultaneously, now if the
scheduler wants to transfer control of CPU from one process to another
- Then scheduler first has to save the state of the process
inside Process Control Block (PCB).
- And then only it can transfer the control of CPU to another
process.
- This is called as Context Switching.
- And it is a heavy task and thus multiprocessing is a
heavyweight process.

- Whereas if talk about threads, they resides inside a process, they


execute inside a process. Thus we can say they are close to hardwares.
- Therefore threads do not require Context Switching.
- And thus thread based processing is faster than process based
processing.
- That's why we required multithreading.
- And this is a funda behind multitheading.

- Q) We know java is multi threaded? But question arises. How?


- When we execute any java application, internally 2 threads gets
executed.
- That is the main thread and a garbage collector thread.
- And this property of java application clearly shows that java is
multithreaded.

- Main Thread
-------------
- It is also called as Non-Dameon Thread / User Thread.
- And it is responsible for invoking the main method
- And I have read somewhere the priority of main thread is 5
(Thread.NORM_PRIORITY).

- Garbage Collector Thread


--------------------------
- It is called as Daemon Thread / Background Thread.
- It is responsible for invoking finalize method, which is used
for releasing / deallocating the memory which is unused.
- And it is also called as Finalizer.
- And as I have read somewhere that the priority of Garbage
Collector Thread is 8 (Thread.NORM_PRIORITY + 3).

# Application
==============
- Single Threaded : Javascript Programming Language
- Single Processing : DOS OS

- Multi Processing : Java Programming Language


- Mostly gaming application uses multithreading as it use more animations compared
to other applications

# More about Thread


===================
- Thread is a subclass of java.lang.Object class and it implements
java.lang.Runnable interface.

### There are 2 methods of creating threads :


- By extending the thread class
- By implementing the runnable interface

Thread Class vs Runnable Interface


===================================
- If we extend the Thread class, our class cannot extend any other class
because Java doesn’t support multiple inheritance.

- But, if we implement the Runnable interface, our class can still extend
other base class as well as it can implement more interfaces.

- We can achieve basic functionality of a thread by extending Thread class


because it provides some inbuilt methods like yield(), interrupt() etc. that are
not available in Runnable interface.

- Using runnable will give you an object that can be shared amongst multiple
threads.

%%% Refer Code %%%

1) By implementing Runnable
----------------------------
class MultithreadingDemo implements Runnable{
public void run(){
try{
System.out.println(
"Thread " + Thread.currentThread().getId() + " is running"
);
}
catch(Exception e){
System.out.println("Exception is caught");
}
}
}

public class Solution{


public static void main(String[] args) {
int n = 8;
for(int i = 0; i< n; i++){
Thread object = new Thread(new MultithreadingDemo());
object.start();
}
}
}

2) By extending Thread
-----------------------
class MultithreadDemo extends Thread{
public void run(){
try{
System.out.println(
"Thread "+ Thread.currentThread().getId() + " is running"
);
}
catch(Exception e){
System.out.println("Exception is caught.");
}
}
}

public class Solution {


public static void main(String[] args) {
int n = 8;
for(int i = 0; i < n; i++){
MultithreadDemo objDemo = new MultithreadDemo();
objDemo.start();
}
}
}

1. sleep(long millis) Method:


-----------------------------

The sleep method is used to pause the execution of the current thread for a
specified period of time.

Parameters:
-----------
millis: The duration in milliseconds for which the thread should sleep.

Example:

```

public class SleepExample {


public static void main(String[] args) {
System.out.println("Start of the program");

try {
Thread.sleep(2000); // Sleep for 2 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("End of the program after sleep");


}
}

```

2. join() Method:
-----------------

The join method is used to wait for a thread to finish its execution before
proceeding with the current thread.

Example:
--------
It is often used to coordinate the activities of multiple threads.

Example :
---------

```

public class JoinExample {


public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
System.out.println("Count: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

thread.start();

try {
thread.join(); // Wait for the 'thread' to finish
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("Main thread continues after 'thread' finishes");


}
}

```

3. yield() Method:
------------------

The yield method is used to suggest to the scheduler that the current thread is
willing to yield its current time slice.

Behavior:
---------
It does not guarantee that the scheduler will switch to another thread; it is a
hint.

Example:
--------

```

public class YieldExample {


public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
System.out.println("Thread 1: " + i);
Thread.yield(); // Suggest to yield the time slice
}
});

Thread thread2 = new Thread(() -> {


for (int i = 1; i <= 5; i++) {
System.out.println("Thread 2: " + i);
}
});

thread1.start();
thread2.start();
}
}

```

4. setPriority(int newPriority) Method:


---------------------------------------

The setPriority method is used to set the priority of a thread.

Parameters:
-----------
newPriority: The new priority value (an integer between Thread.MIN_PRIORITY and
Thread.MAX_PRIORITY).

Example:
--------

```

public class PriorityExample {


public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
System.out.println("Thread 1 with default priority: " +
Thread.currentThread().getPriority());
});

Thread thread2 = new Thread(() -> {


System.out.println("Thread 2 with default priority: " +
Thread.currentThread().getPriority());
});

thread1.start();
thread2.start();
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);

System.out.println("Main thread priority: " +


Thread.currentThread().getPriority());
}
}

```

5. getPriority() Method:
------------------------

The getPriority method is used to get the priority of a thread.

Returns:
--------
An integer representing the priority of the thread.

Example:
--------

```

public class PriorityExample {


public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
// ...
});

thread1.start();

int priority = thread1.getPriority();


System.out.println("Thread 1 priority: " + priority);
}
}

```
ThreadGroup Class in Java:
===========================
The ThreadGroup class in Java is a class that provides a way to group
multiple threads into a single unit.

It is part of the java.lang package and is useful for managing and organizing
threads in a hierarchical manner.

Key Concepts:
-------------

Grouping Threads:
-----------------
The primary purpose of ThreadGroup is to group multiple threads together. This
grouping is particularly helpful for managing and controlling the behavior of
related threads.

Hierarchy:
----------
ThreadGroup instances can be organized into a hierarchical structure. Each
ThreadGroup can have subgroups, creating a tree-like structure.

Naming:
-------
Each ThreadGroup is associated with a name, making it easier to identify and manage
groups of threads.

ThreadGroup as a Parent:
------------------------
A ThreadGroup can have child ThreadGroups, forming a parent-child relationship. The
top-level ThreadGroup is the main thread group.

Thread Management:
------------------
The ThreadGroup class provides methods for enumerating the threads within the
group, stopping all threads in the group, and setting the maximum priority of the
threads in the group.
Creating ThreadGroups:
----------------------
public class ThreadGroupExample {
public static void main(String[] args) {
ThreadGroup parentGroup = new ThreadGroup("ParentGroup");
ThreadGroup childGroup = new ThreadGroup(parentGroup, "ChildGroup");

Thread thread1 = new Thread(parentGroup, () -> {


// Thread 1 logic
});

Thread thread2 = new Thread(childGroup, () -> {


// Thread 2 logic
});

thread1.start();
thread2.start();
}
}

// End to end code


==================

public class ThreadGroupExample {


public static void main(String[] args) {
// Creating a ThreadGroup
ThreadGroup myGroup = new ThreadGroup("MyGroup");

// Creating threads and adding them to the ThreadGroup


Thread thread1 = new Thread(myGroup, new MyRunnable(), "Thread1");
Thread thread2 = new Thread(myGroup, new MyRunnable(), "Thread2");

// Starting threads
thread1.start();
thread2.start();

// Pausing to allow threads to run


try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}

// Interrupting all threads in the ThreadGroup


myGroup.interrupt();

// Waiting for threads to complete


try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("Main thread completes.");


}

static class MyRunnable implements Runnable {


@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + " is
running.");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Preserve interrupted
status
}
}
System.out.println(Thread.currentThread().getName() + " completes.");
}
}
}

Interview Notes:
----------------

Why use ThreadGroup in Java?


----------------------------
ThreadGroup provides a way to organize threads into a hierarchy, making it easier
to manage and control related threads.
How can threads be organized into a hierarchy using ThreadGroup?
----------------------------------------------------------------
ThreadGroup instances can be created with parent-child relationships, forming a
hierarchical structure.

What methods does ThreadGroup provide for managing threads?


-----------------------------------------------------------
ThreadGroup provides methods for enumerating threads, counting active threads,
interrupting threads, and listing information about the group.

What is the purpose of enumerate method in ThreadGroup?


-------------------------------------------------------
The enumerate method is used to fill an array with threads from the group. It can
also include threads from subgroups if specified.

How is a ThreadGroup interrupted, and what happens when it is interrupted?


--------------------------------------------------------------
The interrupt method interrupts all threads in the group, causing them to receive
an InterruptedException if they are in a waiting or sleeping state.

Sessions 23 & 24:


=================

##### Synchronization
=====================

%%%%%%%%%%

Example
=======
- Bank - Joint Account and all the members are trying to withdraw the amount
at same time
- Multithreading says yes this task must be executed, and each one should get
the output
- This leads to the Data Corruption

- Multithreading
================
public void run()
{
// same resource
}
t1.start();
t2.start();
t3.start();

- All the three threads t1, t2, t3 will access the same resource at the same
time in this case

- Synchronization
=================
public synchronized void run()
{
// same resource
}
t1.start();
t2.start();
t3.start();

- At a time single thread will access the same resource in this case

- Two types of synchronization methods are there :


1. Method Level Synchronization
2. Block Level Synchronization (Faster compared to Method Level
Synchronization)

- Above was the example of Method Level Synchronization

- Block Level Synchronization


-----------------------------
public void Solution{
synchronized(this){
// same resource
}
}

t1.start();
t2.start();
t3.start();

- So the time consuming part where all the 3 threads are supposed to entered
inside the method is already done at a one go.
%%%%%%%%%%

## Race Condition
=================
- There are 2 threads
- One thread is reading from the file
- and One thread is writing to the file

- Race condition means

- when one thread is reading from the file and other thread is
writing from the file and vice versa.

- What if one thread is reading from the file at much higher


speed than the other thread which is writing to the file and vice versa.

- This lead to the race condition.

- Race condition always leads to Data Corruption.

## We can avoid the race condition by using Synchronization

## Synchronization
------------------
- Synchronization is technique which ensures that when multiple threads
are sharing the same resource there shouldn't be "Race Condition".

- In other words, synchronization ensure that lock on an object is


acquired by only one thread at a time.

# What exactly happens when we use synchronized keyword?


- In java every object has a lock
- This lock can be accessed by only one thread at a time
- The lock will be released as soon as the thread complete it's job and
then other thread can acquire the lock

- This lock comes into picture only when an object gets the non-static
synchronized method or block.
- Whichever thread is suppose to execute the synchronized method first,
acquires the lock.
- Other threads have to be in "seeking lock state"

- Acquiring and releasing lock happens automatically.


- Once the thread acquired the lock on an object, it can have control
over all non-static synchronized method of that object.

# Difference bet’n synchronized method and block.


method :- all the statements are protected.

block :- only those statements are protected which are given inside
synchronized block.
# What do you mean by “Thread-Safe” class?
- Thread-safe class means a class having non-static synchronized
methods or methods having non-static synchronized blocks.

# What is the drawback of synchronization?


- You get Thread-Safety at the cost of performance.

// Java Program to synchronized method by


// using an anonymous class
import java.io.*;

class Test {
synchronized void test_function(int n)
{
// synchronized method
for (int i = 1; i <= 3; i++) {
System.out.println(n + i);
try {
Thread.sleep(500);
}
catch (Exception e) {
System.out.println(e);
}
}
}
}

// Driver Class
public class GFG {
// Main function
public static void main(String args[])
{
// only one object
final Test obj = new Test();

Thread a = new Thread() {


public void run() { obj.test_function(15); }
};

Thread b = new Thread() {


public void run() { obj.test_function(30); }
};

a.start();
b.start();
}
}

Output
16
17
18
31
32
33

DeadLock
========
- Even though synchronized method or block is used to avoid "Race Condition",
there can be danger of "DeadLock" inside it.

- DeadLock : eg. if one thread is working inside synchronized method or block


and if it gets stucked up ! Imagine what will happen?

- Neither this thread can complete it's job and release the lock, nor can
other thread acquire the lock

- This gives rise to DeadLock Situation.

*** - Solution to this is to have a Communication between the threads.


- if the thread realizes it cannot continue, it should come out of the
synchronized method or block and release the lock.
- Now other thread will acquire the lock, complete it's job and allow
first thread to resume.

================
// fake points //
================

- The deadlock has the following characteristics:


-------------------------------------------------
- Mutual Exclusion
- Hold and Wait
- No preemption
- Circular wait

- How to Avoid Deadlock in Java?


=================================
Deadlocks cannot be completely resolved. But we can avoid them by following
basic rules mentioned below:

- Avoid Nested Locks:


---------------------
- We must avoid giving locks to multiple threads, this is the main
reason for a deadlock condition. It normally happens when you give locks to
multiple threads.

Avoid Unnecessary Locks:


------------------------
- The locks should be given to the important threads. Giving locks to
the unnecessary threads that cause the deadlock condition.

Using Thread Join:


------------------
- A deadlock usually happens when one thread is waiting for the other
to finish. In this case, we can use join with a maximum time that a thread will
take.

=================
// fake points //
=================

##### What are the methods used for Thread communication? Explain them in brief.

- In order to have communication between the threads java.lang.Object class


has 3 methods which are final : wait(), notify(), notifyAll()

- wait()
---------
- It will make thread to release the lock and go to wait pool

- notify()
----------
- It will make thread to move from the wait pool to "seeking lock
state"

- notifyAll()
-------------
- it will make all threads to move from wait pool to seeking lock
state.

# If no thread is in the wait pool, and we invoke notify or notifyAll, what


will happen?
- nothing will happen. notify() and notifyAll() methods are not having
any side effects.

# wait(), notify() and notifyAll() methods can be called only from


synchronized method or a block. Why it is so?
- it is because these methods are used to control object lock and
object lock comes into picture in case of non-static synchronized method or block.

# Difference bet’n sleep and wait.


- wait() method releases the lock , whereas sleep does not.

##### The Producer-Consumer problem / Bounded Buffer Problem


===================================
- It is a classic synchronization problem in computer science where there are
two threads - a producer and a consumer - sharing a common resource.

- Consider a problem can be seen in a fast-food restaurant's kitchen:


- the kitchen is the common resource
- the chef is the producer who produces the food
- waiter is the consumer who serves the food to the customers.

We will try to solve this problem using synchronization mechanisms like


wait(), notify(), and notifyAll().

Session 25 : Generics and Reflection API


========================================

### Generics In Java


====================
- Generics is the java language feature that allows developers to write a
code by passing a type parameters to the methods, classes and interfaces.

- Q) But what is type parameters ?

class Test<T> { // HERE T ACTS AS A TYPE PARAMETER


T obj;

Test(T obj){
this.obj = obj;
}

public T getObject(){
return this.obj;
}
}
public class Solution{
public static void main(String[] args){
Test<Integer> tObj = new Test<Integer>(15);
// HERE Integer ACTS AS A TYPE ARGUMENT

System.out.println(tObj.getObject()); // 15

Test<String> tObj2 = new Test<String>("Rajiv");


System.out.println(tObj.getObject()); // Rajiv

// HERE String ACTS AS A TYPE ARGUMENT


}

- Q) Why we need this generics ?


- Code Reuse:
-------------
- We do not have to write the same class / method / interface
multiple times for different type of type arguments

- Type Safety:
--------------
example :

code 1)

public class Solution{


public static void main(String[] args){
ArrayList a1 = new ArrayList;

a1.add("String1");
a1.add("String2");
a1.add(10); // Compiler allows this

// other person ko pata hi nahi kya data hai


String s1 = (String)a1.get(0);

String s1 = (String)a1.get(1);

String s1 = (String)a1.get(2); // Runtime Exception


}
}

o/p :
-----
Exception in thread "main" java.lang.ClassCastException:
java.lang.Integer cannot be cast to java.lang.String at
Test.main(Test.java:19)

code 2)

public class Solution{


public static void main(String[] args){
ArrayList<String> a1 = new ArrayList;
a1.add("String1");
a1.add("String2");
a1.add(10); // Compiler Time Error

// other person ko pata hi nahi kya data hai


String s1 = a1.get(0);

String s1 = a1.get(1);

String s1 = a1.get(2);
}
}

o/p :
-----
15: error: no suitable method found for add(int) al.add(10);

- Conclusion :
--------------
- We do not require type casting every times

- Now, if we try to store different data inside data structure of some


different type arguments we will get Compile Time Error, rather than Run Time Error

- Compile Time Error is always better than Run Time Error,


because during copilation itself we will get to know what the error is..

- Types of type parameters in java generics (TENKV)


===================================================
- T (Type)
- E (Element)
- N (Number)
- K (Key)
- V (Value)

- Generics works only with references types


============================================
- Thus we cannot use primitive data types(int, char, byte, etc..) as a
type argument

- Solution<int> obj = new Solution<int>(20);


// Compile Time Error

- But primitive type arrays can be passed as a type arguments. Because


arrays are reference types

- ArrayList<int[]> a = new ArrayList<>();


// Ok

- Types of generics in java


===========================
- Generic Methods
------------------

public class Solution{

public static <T> void genericDemo(T element){

System.out.println(
element.getClass().getName() + " = " +element);
}

public static void main(String[] args){


genericDemo(100);

genericDemo("Hello");

genericDemo(1.0);
}
}

o/p:
----
java.lang.Integer = 100
java.lang.String = Hello
java.lang.Double = 1.0

- So we can say generic method is similar to the normal methods, the only
difference is we can pass type parameter to the method
- And passing a type parameter opens the door for many advantages

- Type-Safety:
Instance of String type argument can deal with String type
data only.

Instance of Integer type argument can deal with Integer


type data only and so on..

- Thus, Type Casting Is Not Required: There is no need to


typecast everytime.

- Code reusablility : We can use same code for different data


types.

- We get the error at compile time itself.

- Generic Classes
------------------

class Test<T> { // HERE T ACTS AS A TYPE PARAMETER


T obj;

Test(T obj){
this.obj = obj;
}

public T getObject(){
return this.obj;
}
}

public class Solution{


public static void main(String[] args){
Test<Integer> tObj = new Test<Integer>(15);
// HERE Integer ACTS AS A TYPE ARGUMENT

System.out.println(tObj.getObject()); // 15

Test<String> tObj2 = new Test<String>("Rajiv");


System.out.println(tObj.getObject()); // Rajiv

// HERE String ACTS AS A TYPE ARGUMENT


}

o/p
---
15
Rajiv

- So we can say generic class is similar to the normal classes, the only
difference is we can pass type parameter to the class
- And passing a type parameter opens the door for many advantages

- Type-Safety:
Instance of String type argument can deal with String type
data only.

Instance of Integer type argument can deal with Integer


type data only and so on..

- Thus, Type Casting Is Not Required: There is no need to


typecast everytime.

- Code reusablility : We can use same code for different data


types.

- We get the error at compile time itself.


- We can also pass multiple type parameters to Generic classes
===============================================================

class Test<T, U>{


T obj1;
U obj2;

Test(T obj1, U obj2){


this.obj1 = obj1;
this.obj2 = obj2;
}

public void print(){


System.out.println(obj1);
System.out.println(obj2);
}
}

public class Solution{


public static void main(String[] args){
Test<String, Integer> obj = new Test<String, Integer>("Hello",
100);
obj.print():
}
}

o/p
----
Hello
100

### Wildcard in java


=====================
- ? is called as the wildcard in generic programming
- It represents unknown type

- There are 3 types of wildcard in java


=========================================
- Unbounded Wildcard
====================
- Unbounded Wildcard specifies that the data structure can
contain any type of element

- example :
import java.util.Arrays;
import java.util.List;

public class Solution{


public static void printlist(List<?> list)
{
System.out.println(list);
}

public static void main(String[] args){


List<Integer> list1 = Array.asList(1, 2, 3);

List<Double> list2 = Arrays.asList(1.1, 2.2,


3.3);

printlist(list1);

printlist(list2);
}
}

- Upper Bounded Wildcard


========================
- Upper Bounded Wildcard specifies that the data structure can
contain elements of same type parameter and its sub types.

- example :

public class Solution{


public static double printlist(List<? extends Number> list)
{
System.out.println(list);
}

public static void main(String[] args){


List<Integer> list1 = Arrays.asList(4, 5, 6, 7);

List<Double> list2 = Arrays.asList(1.2, 2.2, 3.3);

printlist(list1);
printlist(list2);
}
}

- Lower Bounded Wildcard


========================
- Lower Bounded Wildcard specifies that the data structure can
contain elements of same type parameter and its super types.

- example :

public class Solution{


public static double printlist(List<? super Integer> list){
System.out.println(list);
}

public static void main(String[] args){


List<Integer> list1 = Arrays.asList(4, 5, 6, 7);

List<Number> list2 = Arrays.asList(1.2, 2.2, 3.3);

printlist(list1);
printlist(list2);
}
}

### Reflection in java


======================
- Reflection is an API that is used to examine or modify the behavior of
methods, classes, and interfaces at the runtime.

- The classes used by reflection are defined under java.lang.reflect package.

- Using reflection we can get information about class, constructor and method
of the unknown object at the runtime.

- Class : The getClass() method is used to get he name of the class to


which an unknown object belongs to

- Constructors : The getConstructors() method is used to get the name


of the Constructors of the class to which an unknown object belongs to

- Methods : The getMethods() method is used to get the name of methods


of the class to which an unknown object belongs to

- Using Reflection we can also invoke the method if we know the method name
and the type parameter. And to do so we use below 2 methods :
- getDeclaredMethod() : It creates object of the method to be invoked
- Class.getDeclaredMethod(name, parametertype)
- invoke() : It invokes a method of the class at runtime.
- Method.invoke(Object, parameter)

- Applications
==============
- The reflection API is mainly used in :
- IDE
- Debugger tools
- Testing tools

- Advantages
============
- Exposure to internals : we can access private members of the class
- Useful for debugging and testing tools.
- framework and libraries : many IDE use this feature for auto
correction and filling the code

- Disadvantages
===============
- Performance Overhead : Reflection operations are slower
- Security Risk : Because we are able to access members of the class
- Complexity in coding part

Lab 25:
 Invoke private methods of some other class using reflection.
 Create multiple threads using anonymous inner classes.
 Create multiple threads using lambda expressions.

%% VS CODE %%

1. Invoke Private Methods Using Reflection:


===========================================
import java.lang.reflect.Method;

class ExampleClass {
private void privateMethod(String message) {
System.out.println("Private method invoked with message: "
+ message);
}
}

public class ReflectionExample {


public static void main(String[] args) throws Exception {
// Create an instance of the class
ExampleClass exampleObject = new ExampleClass();

// Get the Class object


Class<?> exampleClass = ExampleClass.class;

// Get the private method


Method privateMethod =
exampleClass.getDeclaredMethod("privateMethod", String.class);

// Make the private method accessible


privateMethod.setAccessible(true);

// Invoke the private method


privateMethod.invoke(exampleObject, "Hello, Reflection!");
}
}

2. Create Multiple Threads Using Anonymous Inner Classes:


==========================================================
public class ThreadExample {
public static void main(String[] args) {
// Creating multiple threads using anonymous inner classes
Thread thread1 = new Thread(new Runnable() {
public void run() {
System.out.println("Thread 1 is running");
}
});

Thread thread2 = new Thread(new Runnable() {


public void run() {
System.out.println("Thread 2 is running");
}
});

// Start the threads


thread1.start();
thread2.start();
}
}

3. Create Multiple Threads Using Lambda Expressions:


====================================================
public class ThreadExample {
public static void main(String[] args) {
// Creating multiple threads using lambda expressions
Thread thread1 = new Thread(() -> {
System.out.println("Thread 1 is running");
});

Thread thread2 = new Thread(() -> {


System.out.println("Thread 2 is running");
});

// Start the threads


thread1.start();
thread2.start();
}
}

You might also like