0% found this document useful (0 votes)
6 views

Java Basics

The document provides a comprehensive overview of Java programming, covering topics such as the basic structure of a Java program, data types, control flow statements, classes and objects, methods, and access modifiers. It explains key object-oriented programming concepts including encapsulation, inheritance, polymorphism, and abstraction, along with practical examples. Additionally, it details the significance of the main method, the compilation process, and the use of access modifiers in Java.

Uploaded by

shwetanshu.g92
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

Java Basics

The document provides a comprehensive overview of Java programming, covering topics such as the basic structure of a Java program, data types, control flow statements, classes and objects, methods, and access modifiers. It explains key object-oriented programming concepts including encapsulation, inheritance, polymorphism, and abstraction, along with practical examples. Additionally, it details the significance of the main method, the compilation process, and the use of access modifiers in Java.

Uploaded by

shwetanshu.g92
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 52

 Basic Structure of a Java Program

 Data Types and Variables


 Operators
 Control Flow Statements
 Classes and Objects
 Methods
 Access Modifiers

1. Basic Structure of a Java Program

Every Java program consists of classes and methods. Here's the structure:

public class Main {

public static void main(String[] args) {

// This is a simple comment

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

Explanation:

 public class Main: This declares a class named Main. In Java, everything is written
inside a class.
 public static void main(String[] args): This is the main method, the entry
point of any Java program.
 System.out.println("Hello, World!");: This prints "Hello, World!" to the
console.

1. Purpose of String[] args

 The main method in Java is the entry point of any application. When you run a Java
program, the Java Virtual Machine (JVM) looks for the main method with this exact
signature:

public static void main(String[] args)

 String[] args is an array of String values, which can be used to pass command-
line arguments to the program. When the program is executed from the command
line, the arguments that follow the program's name are passed to the main method as
elements of the args array.

For example, if you run the following command in the terminal:

java Main Hello World


Then args would be an array containing:
args[0] = "Hello";

args[1] = "World";

2. Can I Use a Different Name Instead of args?

Yes, you can use any valid variable name in place of args, like this:

public static void main(String[] myArguments) {

System.out.println("First argument: " + myArguments[0]);

}
 The String[] args parameter allows the program to accept command-line arguments.

 You can replace args with any valid name (e.g., input, myArgs), but the type (String[])
must remain the same

2. Multiple Programs with Their Own main Methods ?

If you have multiple Java programs (classes), each with its own main method, here's how you
differentiate between them:

 Each class can have its own main method. When you compile the code, the .class
files for each class are generated. // Using javac Main
 To run a specific program, you specify the class name that contains the main method
you want to execute.

For example, say you have two classes, Main and AnotherProgram:

Compile both programs:


javac Main.java AnotherProgram.java
This will generate two .class files: Main.class and AnotherProgram.class.
To run Main:
java Main

1. What Happens When You Run java Main Without Compiling (javac
Main.java)?

When you run the command java Main, the Java Virtual Machine (JVM) expects a file
called Main.class to exist, which is the compiled bytecode of your Java program. This file is
generated when you compile the Java file using javac Main.java.

If you haven't compiled the program yet, there will be no .class file for the JVM to execute.
As a result, the JVM will throw an error.
The JVM will throw an error like this:

Error: Could not find or load main class Main

Caused by: java.lang.ClassNotFoundException: Main

2. Why Do We Need to Compile Using javac First?

Java source code (in .java files) is written in human-readable form, but the JVM doesn't
understand this directly. It can only run bytecode (which is stored in .class files). The
javac command compiles your Java code into bytecode.

 javac Main.java: This command compiles the Java code and generates a
Main.class file that contains the bytecode.
 java Main: This command runs the bytecode from the Main.class file.

 One public class per file: Java enforces this rule because the filename must match the
public class name.
 You can have multiple classes in the same file, but only one can be public.

 If you need more than one public class, put each class in its own file

7. Access Modifiers

These define the accessibility of classes, methods, and variables:

 public: Accessible from everywhere.


 private: Accessible only within the class.
 protected: Accessible within the package and by subclasses.
 default: Accessible only within the package (no keyword is required).

1. Public Access Modifier

 Public members (classes, methods, variables) can be accessed from anywhere in the
program, including different packages.
 Classes can be declared public only at the top level (i.e., not inside another class).

2. Private Access Modifier

 Private members (variables, methods, constructors) can only be accessed within the same
class. They are not visible to other classes, even in the same package.
 Top-level classes cannot be private, but inner classes can be.
3. Protected Access Modifier

 Protected members can be accessed by:


1. Classes in the same package.
2. Subclasses, even if they are in different packages (through inheritance).

 Top-level classes cannot be declared protected.

package mypackage;

public class Parent {

protected String protectedVar = "I am protected"; // Accessible to subclasses and classes in the
same package

protected void showProtected() {

System.out.println("Protected method in Parent");

package anotherpackage;

import mypackage.Parent;

public class Child extends Parent {

public void accessParent() {

System.out.println(protectedVar); // Inherited from Parent, can access because it's protected

showProtected(); // Inherited protected method

}
4. Default Access (Package-Private)

 If you don’t specify an access modifier (like public, private, or protected), the default
access level is package-private.
 Package-private members are accessible only within the same package, but not from
outside the package, even in subclasses.
 Top-level classes can have default access and are accessible only within the same package.

package mypackage;

class PackageClass { // Default access modifier (no keyword)

String defaultVar = "I am default (package-private)"; // Default access level

void showDefault() {

System.out.println("Default method in PackageClass");

package mypackage;

public class Main {

public static void main(String[] args) {

PackageClass obj = new PackageClass(); // Can access because it's in the same package

System.out.println(obj.defaultVar); // Accessible because Main is in the same package

obj.showDefault(); // Accessible within the same package

Key OOP Concepts in Java

Java is an object-oriented programming language, which means it revolves around the


concept of objects and classes. Here are the four main pillars of OOP:
1. Encapsulation
2. Inheritance
3. Polymorphism
4. Abstraction

 Encapsulation: Protects the data and only exposes what’s necessary.


 Inheritance: Allows one class to inherit members from another class.
 Polymorphism: Allows objects of different types to be treated as objects of a
common type.
 Abstraction: Hides the complex implementation details and exposes only the
essentials.

1. Encapsulation

Encapsulation is the concept of bundling data (variables) and methods (functions) that
operate on that data into a single unit, called a class. It also involves restricting access to
certain parts of the object’s data and methods using access modifiers (like private, public,
and protected).

Key Ideas:

 Keep sensitive data hidden from the outside world using private access.
 Provide controlled access to data through getters and setters.

class Student {

private String name; // private field (data hidden)

// Constructor

public Student(String name) {

this.name = name;

// Getter (controlled access)

public String getName() {

return name;

}
// Setter (controlled modification)

public void setName(String name) {

this.name = name;

public class Main {

public static void main(String[] args) {

Student student = new Student("John");

System.out.println("Name: " + student.getName()); // Accessing name via getter

student.setName("Jane"); // Modifying name via setter

System.out.println("Updated Name: " + student.getName());

2. Inheritance

Inheritance allows a class to inherit fields and methods from another class, promoting code
reuse. The class that inherits is called the subclass (or child class), and the class being
inherited from is the superclass (or parent class).

Key Ideas:

 The subclass can use the parent class's members (variables and methods).
 Inheritance is done using the extends keyword.

// Parent class

class Animal {

public void makeSound() {

System.out.println("The animal makes a sound.");

}
// Child class

class Dog extends Animal {

// Overriding the parent method

@Override

public void makeSound() {

System.out.println("The dog barks.");

public class Main {

public static void main(String[] args) {

Animal animal = new Animal();

Dog dog = new Dog();

animal.makeSound(); // Calls parent method

dog.makeSound(); // Calls overridden method in Dog

3. Polymorphism

Polymorphism means "many forms." In Java, it allows objects of different classes to be


treated as objects of a common superclass. There are two types of polymorphism:

 Compile-time (Method Overloading): Methods with the same name but different
parameters.
 Run-time (Method Overriding): A subclass provides a specific implementation of a method
that is already defined in its superclass.

class Calculator {

public int add(int a, int b) {

return a + b;
}

public int add(int a, int b, int c) {

return a + b + c;

public class Main {

public static void main(String[] args) {

Calculator calc = new Calculator();

System.out.println("Sum: " + calc.add(2, 3)); // Calls first add method

System.out.println("Sum: " + calc.add(2, 3, 4)); // Calls overloaded add method

//OverRiding

class Animal {

public void makeSound() {

System.out.println("The animal makes a sound.");

class Cat extends Animal {

@Override

public void makeSound() {

System.out.println("The cat meows.");

}
}

In the overriding example:

 Even though the reference type is Animal, the actual object is Cat, and the Cat's
version of makeSound is executed at runtime.

public class Main {

public static void main(String[] args) {

Animal myAnimal = new Cat(); // Polymorphism: Animal reference, Cat object

myAnimal.makeSound(); // Calls the overridden method in Cat

In Animal myAnimal = new Dog();, the reference type is Animal, but the object type is
Dog.

 At run-time, when you call myAnimal.makeSound(), the Java Virtual Machine


(JVM) sees that

4. Abstraction

Abstraction is the concept of hiding the implementation details and exposing only the
essential features of an object. It can be achieved using abstract classes and interfaces.

Key Ideas:

 An abstract class can have both abstract (unimplemented) and non-abstract methods.
 An interface is a completely abstract class; it can only have abstract methods (before Java 8).
Java 8 and later allows default and static methods in interfaces.

Key Idea of Abstraction:

 At the abstract level, you are focusing on what should happen (the behavior), without
worrying about how it is actually done.
 The child class (or implementing class) is responsible for providing the specific details of how
to perform that behavior.

Why Is This Useful?


This kind of abstraction is especially useful in larger systems, where you don’t want to
worry about the implementation details every time. You just want to know that a certain
behavior is available, and it can be used in a standardized way.

abstract class Animal {

// This defines "what" the object can do, but no details on "how"

public abstract void makeSound();

Now, each specific animal (child class) defines how it makes that sound:

class Dog extends Animal {

// This is the "how" for the Dog class

@Override

public void makeSound() {

System.out.println("Dog barks");

class Cat extends Animal {

// This is the "how" for the Cat class

@Override

public void makeSound() {

System.out.println("Cat meows");

Achieving Abstraction

The abstraction comes from the fact that, as a user of the Animal class, you don’t need to
know how the Dog or Cat makes a sound. You just need to know that any Animal can make a
sound, and how it does that is left to the child classes to define.

When you write code that interacts with an Animal object, you're dealing with the abstract
behavior — "an animal can make a sound" — without needing to care about the specific
details:
public class Main {

public static void main(String[] args) {

Animal myDog = new Dog();

myDog.makeSound(); // Output: Dog barks

Animal myCat = new Cat();

myCat.makeSound(); // Output: Cat meows

What is Abstraction?

Abstraction is the process of hiding the implementation details and showing only the
essential features of an object. In other words, you focus on what an object does rather than
how it does it.

In Java, abstraction is achieved using two mechanisms:

1. Abstract classes
2. Interfaces

1. Abstract Classes

An abstract class is a class that cannot be instantiated on its own. It may have abstract
methods, which are methods that are declared without an implementation (i.e., the method
just has a name and parameters, but no body). Subclasses are required to provide the
implementation for those abstract methods.

Key Points:

 An abstract class can have both abstract methods (without body) and regular methods
(with body).
 You cannot create an instance of an abstract class.
 Subclasses must override and implement the abstract methods.

Example of Abstract Class:

// Abstract class with one abstract method

abstract class Animal {

// Abstract method (no implementation)


public abstract void makeSound();

// Regular method (with implementation)

public void sleep() {

System.out.println("The animal is sleeping.");

class Dog extends Animal {

// Must provide implementation for abstract method

@Override

public void makeSound() {

System.out.println("The dog barks.");

class Cat extends Animal {

@Override

public void makeSound() {

System.out.println("The cat meows.");

public class Main {

public static void main(String[] args) {

// Animal myAnimal = new Animal(); // Error: Cannot instantiate the abstract class
Animal dog = new Dog(); // Create a Dog object

dog.makeSound(); // Output: The dog barks

dog.sleep(); // Output: The animal is sleeping

Animal cat = new Cat(); // Create a Cat object

cat.makeSound(); // Output: The cat meows

2. Interfaces

An interface in Java is another way to achieve abstraction. It defines a contract that any class
implementing the interface must follow. Unlike abstract classes, interfaces can only have
abstract methods (until Java 8, where default methods were introduced).

Key Points:

 All methods in an interface are implicitly abstract and public (until Java 8).
 A class can implement multiple interfaces, but it can only extend one abstract class.
 Interfaces provide full abstraction (no concrete methods, unless using default methods from
Java 8 onwards).

// Interface with abstract methods

interface Animal {

public void makeSound(); // Interface method, no body

public void sleep(); // Interface method, no body

// Implementing the interface in Dog class

class Dog implements Animal {

@Override

public void makeSound() {

System.out.println("The dog barks.");

}
@Override

public void sleep() {

System.out.println("The dog is sleeping.");

// Implementing the interface in Cat class

class Cat implements Animal {

@Override

public void makeSound() {

System.out.println("The cat meows.");

@Override

public void sleep() {

System.out.println("The cat is sleeping.");

public class Main {

public static void main(String[] args) {

Animal dog = new Dog(); // Create a Dog object

dog.makeSound(); // Output: The dog barks

dog.sleep(); // Output: The dog is sleeping

Animal cat = new Cat(); // Create a Cat object


cat.makeSound(); // Output: The cat meows

cat.sleep(); // Output: The cat is sleeping

Abstract Class vs Interface

Abstract Class Interface


Can have only abstract methods (until Java
Can have both abstract and concrete methods.
8).
A class can extend only one abstract class. A class can implement multiple interfaces.
Can have fields (attributes) with any access Can only have public static final fields
modifier. (constants).
Provides partial abstraction. Provides full abstraction.

abstract class Vehicle {

// Regular method in abstract class

public void start() {

System.out.println("Vehicle is starting.");

// Abstract method in abstract class

public abstract void drive();

interface Electric {

void chargeBattery(); // Abstract method in interface

class Tesla extends Vehicle implements Electric {

@Override

public void drive() {


System.out.println("Tesla is driving.");

@Override

public void chargeBattery() {

System.out.println("Tesla is charging its battery.");

public class Main {

public static void main(String[] args) {

Tesla myTesla = new Tesla();

myTesla.start(); // Calls method from Vehicle class

myTesla.drive(); // Calls overridden method from Tesla class

myTesla.chargeBattery(); // Calls method from Electric interface

Here we are using Tesla myTesla = new Tesla(); not like Animal cat = new Cat(). Still we are able to
fetch all 3 class.

abstract class Vehicle {

public void start() {

System.out.println("Vehicle is starting.");

public abstract void drive();

}
interface Electric {

void chargeBattery();

class Tesla extends Vehicle implements Electric {

@Override

public void drive() {

System.out.println("Tesla is driving.");

@Override

public void chargeBattery() {

System.out.println("Tesla is charging its battery.");

public class Main {

public static void main(String[] args) {

Vehicle myVehicle = new Tesla();

// You can call methods that are in the Vehicle class:

myVehicle.start(); // Output: Vehicle is starting.

myVehicle.drive(); // Output: Tesla is driving.

// However, you cannot directly call the chargeBattery() method

// because it's defined in the Electric interface, not in Vehicle.

// myVehicle.chargeBattery(); // This would cause an error!


}

How Java Achieves Multiple Inheritance Through Interfaces:

Java does not support multiple inheritance directly through classes. This means a class
cannot extend more than one class. However, Java allows a class to implement multiple
interfaces, which is Java's way of achieving multiple inheritance.

interface Animal {

void eat(); // Abstract method - no body

void sleep(); // Abstract method - no body

class Dog implements Animal {

@Override

public void eat() {

System.out.println("Dog eats");

@Override

public void sleep() {

System.out.println("Dog sleeps");

2. Difference between an Abstract Class and an Interface:

Feature Abstract Class Interface


Before Java 8: only abstract
Method Can have both abstract methods and methods
implementation concrete methods (with body) After Java 8: can have default
and static methods
Can have instance variables (non-final, Can only have public static
Fields
non-static fields) final (constants)
A class can extend only one abstract A class can implement multiple
Inheritance
class interfaces
3. How Java Achieves Multiple Inheritance Through Interfaces:

Java does not support multiple inheritance directly through classes. This means a class
cannot extend more than one class. However, Java allows a class to implement multiple
interfaces, which is Java's way of achieving multiple inheritance.

interface Flyable {

void fly();

interface Swimmable {

void swim();

class Duck implements Flyable, Swimmable {

@Override

public void fly() {

System.out.println("Duck flies.");

@Override

public void swim() {

System.out.println("Duck swims.");

Here, the Duck class implements two interfaces: Flyable and Swimmable. This means a Duck can
both fly and swim. Even though Java doesn't allow multiple inheritance using classes (i.e., a class
can't extend more than one class), it achieves multiple inheritance through interfaces.
what is difference between extends and implements ?
The terms extends and implements in Java are both used for inheritance, but they serve
different purposes and are used in different contexts. Let’s break down the differences
between them:

1. extends:

 Used for classes and abstract classes.


 It is used when a class inherits from another class or an abstract class.
 When a class extends another class, it inherits all non-private fields and methods from the
parent class. It can also override methods from the parent class if needed.

Key Points:

 A class can only extend one class (single inheritance).


 The class being extended can be either a regular class or an abstract class.
 The extends keyword is also used for interfaces to inherit from other interfaces.

class Animal {

void eat() {

System.out.println("Animal is eating");

class Dog extends Animal {

// Inherits the eat() method from Animal

void bark() {

System.out.println("Dog is barking");

 For interfaces, the extends keyword can be used to extend another interface

interface Swimmable {

void swim();

interface FastSwimmable extends Swimmable {


void swimFast();

The FastSwimmable interface extends Swimmable, which means any class that implements
FastSwimmable must implement both swim() and swimFast().

2. implements:

 Used for interfaces.


 It is used when a class implements an interface.
 When a class implements an interface, it must provide implementations for all the abstract
methods declared in the interface.
 A class can implement multiple interfaces, which allows for multiple inheritance in Java via
interfaces.

Key Points:

 A class can implement multiple interfaces.


 All methods in an interface are implicitly public abstract, and any class that implements
an interface must provide the implementation for those methods (unless the class is
abstract).

interface Flyable {

void fly();

interface Swimmable {

void swim();

class Duck implements Flyable, Swimmable {

@Override

public void fly() {

System.out.println("Duck is flying");

@Override
public void swim() {

System.out.println("Duck is swimming");

Here, the Duck class implements both the Flyable and Swimmable interfaces. It must provide
implementations for both fly() and swim() methods.

4. Default and Static Methods in Interfaces (Java 8 onward):

Starting from Java 8, interfaces can also contain default methods and static methods. This
was introduced to allow interfaces to evolve without breaking existing classes that implement
the interface.

Default Methods:

A default method is a method in an interface that has a body (implementation). It allows the
interface to provide a default behavior that implementing classes can either use or override.

Example:
interface Vehicle {

void drive();

default void honk() {

System.out.println("Vehicle is honking");

class Car implements Vehicle {

@Override

public void drive() {

System.out.println("Car is driving");

// Optionally override the default method


@Override

public void honk() {

System.out.println("Car is honking");

Here, the Vehicle interface has a default method honk(), which provides a default
implementation. The Car class can use this method or choose to override it.

static Methods:

Interfaces can also have static methods. These methods can be called on the interface itself,
not on the objects of the classes that implement the interface.

Example:

Key Points to Remember:

1. Interfaces define the behavior that a class must implement but do not provide the
implementation (except with default methods from Java 8).
2. Multiple inheritance is achieved by allowing a class to implement multiple interfaces.
3. Default methods allow interfaces to provide method implementations, while static methods
allow utility-like methods that can be called on the interface itself.

1. What are Exceptions?

An exception is an event that occurs during the execution of a program that disrupts its
normal flow. In simpler terms, it's an error or an unwanted event that can occur while your
program is running, like dividing by zero or trying to access an invalid array index.

Java provides a way to handle these exceptions gracefully using exception handling
mechanisms so that the program doesn’t crash abruptly.

2. Types of Exceptions:

 Checked Exceptions: These exceptions are checked at compile time by the compiler.
The programmer must handle these exceptions, otherwise, the program won’t
compile. Examples include:
o IOException
o SQLException
o FileNotFoundException

 Unchecked Exceptions (Runtime Exceptions): These are exceptions that occur


during the execution of the program and are not checked at compile time. Examples
include:
o ArithmeticException
o NullPointerException
o ArrayIndexOutOfBoundsException

3. Try-Catch Block

To handle exceptions, Java provides the try-catch block. When an exception occurs inside
the try block, the flow of control moves to the corresponding catch block where the
exception is handled.

try {

// Code that might throw an exception

} catch (ExceptionType e) {

// Code to handle the exception

The finally block is used to execute code that must run whether an exception occurs or not. It’s
typically used for clean-up actions like closing files, releasing resources, etc.

public class Main {

public static void main(String[] args) {

try {

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

System.out.println(numbers[3]); // This will throw ArrayIndexOutOfBoundsException

} catch (ArrayIndexOutOfBoundsException e) {

System.out.println("Index out of bounds.");

} finally {

System.out.println("This will always execute.");

5. Throw and Throws Keyword

 throw keyword: It is used to explicitly throw an exception, either predefined or user-


defined.
public class Main {

public static void main(String[] args) {

validateAge(15);

static void validateAge(int age) {

if (age < 18) {

throw new IllegalArgumentException("Age must be 18 or above.");

System.out.println("Valid age.");

throws keyword: It is used in the method signature to indicate that a method can throw an
exception, and it must be handled by the caller.

import java.io.*;

public class Main {

public static void main(String[] args) throws IOException {

FileReader file = new FileReader("somefile.txt");

BufferedReader fileInput = new BufferedReader(file);

throw new IOException("File not found");

6. Custom Exceptions
Java allows you to create your own exceptions. To do this, you create a class that extends
Exception (for checked exceptions) or RuntimeException (for unchecked exceptions).

class InvalidAgeException extends Exception {

public InvalidAgeException(String message) {

super(message);

public class Main {

public static void main(String[] args) {

try {

validateAge(15);

} catch (InvalidAgeException e) {

System.out.println(e.getMessage());

static void validateAge(int age) throws InvalidAgeException {

if (age < 18) {

throw new InvalidAgeException("Age must be 18 or above.");

System.out.println("Valid age.");

what does super do ?


In Java, the keyword super is used to refer to the parent class (superclass). It serves multiple
purposes when dealing with inheritance.

 super(): Calls the parent class constructor.

 super.method(): Calls a method from the parent class, even if it’s overridden in the child
class.

 super.field: Accesses a field from the parent class when there’s a name conflict
between the parent and child classes.

Java Collections Framework :

the Java Collections Framework is essentially a powerful set of pre-built data


structures and algorithms designed to handle different types of collections of data.
Whether you're working with lists, sets, maps, or queues, the framework gives you
efficient ways to store, retrieve, and manipulate data using these underlying data
structures.

2. Data Structure Characteristics in Collections Framework:

 ArrayList (from List interface) is like a dynamic array where you can access
elements in constant time, but inserting or deleting elements in the middle may take
time.
 LinkedList (from List interface) is a doubly linked list, which makes it easy to
insert or remove elements at any position, but accessing elements by index is slower
compared to an array.
 HashSet (from Set interface) uses a hash table to store elements. It allows constant-
time performance for basic operations (add, remove, check), but doesn’t guarantee
any order of elements.
 TreeSet (from Set interface) uses a red-black tree to store elements. It ensures that
the elements are stored in sorted order, but operations take slightly longer
(logarithmic time).
 HashMap (from Map interface) uses a hash table to store key-value pairs, providing
constant-time access to keys and values.
 TreeMap (from Map interface) uses a red-black tree to store key-value pairs,
ensuring that the keys are always sorted.

Multithreading in Java

Multithreading is a Java feature that allows concurrent execution of two or more parts of a
program, referred to as threads. Each thread runs in parallel, allowing more efficient use of
CPU resources. This is useful when you want to perform multiple tasks at the same time.

Here’s what we will cover:

1. What is a Thread?
2. Creating Threads in Java
3. Thread Lifecycle
4. Thread Methods
5. Thread Synchronization
6. Deadlock in Threads

1. What is a Thread?

A thread is a lightweight subprocess, the smallest unit of processing. Multithreading is the


process of executing multiple threads simultaneously, which makes programs more efficient
and faster by utilizing CPU resources effectively.

 Main Thread: Every Java program has at least one thread, known as the main thread, which
is created by the JVM (Java Virtual Machine).
 Multithreading: It is the process of running multiple threads concurrently to perform various
tasks in parallel.

2. Creating Threads in Java

There are two ways to create a thread in Java:

1. Extending the Thread class:

You can create a new class that extends Thread and overrides the run() method to define the
task for the thread.

Example:

class MyThread extends Thread {

public void run() {

System.out.println("Thread is running.");

public class Main {

public static void main(String[] args) {

MyThread thread = new MyThread();

thread.start(); // Start the thread

}
}

2. Implementing the Runnable interface:

Another way is to implement the Runnable interface and pass it to a Thread object.

Example:

class MyRunnable implements Runnable {

public void run() {

System.out.println("Thread is running.");

public class Main {

public static void main(String[] args) {

MyRunnable myRunnable = new MyRunnable();

Thread thread = new Thread(myRunnable); // Pass the runnable to the thread

thread.start();

Both methods are widely used, but implementing Runnable is generally preferred
because it allows the class to extend other classes if needed (since Java does not
support multiple inheritance).

3. Thread Lifecycle

A thread in Java has the following lifecycle stages:

1. New: The thread is created, but not yet started.


2. Runnable: The thread is ready to run or is running.
3. Blocked/Waiting: The thread is temporarily paused and waiting for a resource or event.
4. Timed Waiting: The thread is paused for a specific amount of time.
5. Terminated: The thread has finished execution.

4. Thread Methods
Java provides several methods to control the behavior of threads:

 start(): Starts the thread and calls the run() method.


 run(): Contains the code to be executed by the thread.
 sleep(long millis): Causes the thread to pause for a specified time in milliseconds.
 join(): Waits for the thread to die (i.e., finish its execution).
 yield(): Temporarily pauses the execution of the current thread, allowing other threads to
execute.
 isAlive(): Returns true if the thread is still alive, otherwise false.

class MyThread extends Thread {

public void run() {

for (int i = 1; i <= 5; i++) {

System.out.println(i);

try {

Thread.sleep(1000); // Sleep for 1 second

} catch (InterruptedException e) {

System.out.println(e);

public class Main {

public static void main(String[] args) {

MyThread thread1 = new MyThread();

MyThread thread2 = new MyThread();

thread1.start();

try {
thread1.join(); // Wait for thread1 to finish before starting thread2

} catch (InterruptedException e) {

System.out.println(e);

thread2.start();

5. Thread Synchronization

Thread synchronization is essential when multiple threads try to access shared resources.
Without synchronization, it can lead to race conditions, where the outcome depends on the
thread scheduling and can be inconsistent.

Java provides the synchronized keyword to lock a method or a block of code, ensuring that
only one thread can access it at a time.

class Counter {

private int count = 0;

public synchronized void increment() {

count++;

public int getCount() {

return count;

public class Main {


public static void main(String[] args) {

Counter counter = new Counter();

Thread thread1 = new Thread(() -> {

for (int i = 0; i < 1000; i++) {

counter.increment();

});

Thread thread2 = new Thread(() -> {

for (int i = 0; i < 1000; i++) {

counter.increment();

});

thread1.start();

thread2.start();

try {

thread1.join();

thread2.join();

} catch (InterruptedException e) {

e.printStackTrace();

}
System.out.println("Final count: " + counter.getCount()); // Should print 2000

In this example, the increment() method is synchronized, ensuring that only


one thread can modify the count variable at a time.

Types of Synchronization:

 Method-level synchronization: Synchronize an entire method using the synchronized


keyword.
 Block-level synchronization: Synchronize a specific block of code instead of the whole
method.

Block-level synchronization example:

public void increment() {

synchronized (this) {

count++;

6. Deadlock in Threads

A deadlock occurs when two or more threads are blocked forever, each waiting for the other
to release a resource. This can happen if threads hold locks on different resources and try to
acquire locks held by the other thread.

Summary:

 Multithreading allows concurrent execution of multiple parts of a program (threads) for


better CPU utilization.
 Threads can be created by extending the Thread class or implementing the Runnable
interface.
 The thread lifecycle includes states like new, runnable, blocked, waiting, and terminated.
 Synchronization ensures that only one thread can access a shared resource at a time,
preventing race conditions.
 Deadlocks can occur when two threads wait for each other to release resources, causing
them to be stuck indefinitely.
File I/O (Input and Output) in Java

Java provides various classes and methods to work with files and perform input/output (I/O)
operations. These are primarily located in the java.io and java.nio.file packages.

We'll cover:

1. File Class
2. Reading and Writing Files
3. Streams in Java
4. Working with Buffered Streams
5. Try-with-resources for better resource management

1. File Class

The File class is used to represent file and directory pathnames. You can use it to create,
delete, and check the existence of files and directories, among other things.

import java.io.File;

public class Main {

public static void main(String[] args) {

File file = new File("test.txt");

if (file.exists()) {

System.out.println("File exists.");

} else {

System.out.println("File does not exist.");

Common methods of the File class:

 exists() – Checks if a file or directory exists.


 createNewFile() – Creates a new file.
 delete() – Deletes a file or directory.
 isDirectory() – Checks if it's a directory.
 list() – Returns an array of file/directory names in a directory.

2. Reading and Writing Files

In Java, we can read and write files using different classes like FileReader,
BufferedReader, FileWriter, and BufferedWriter.

Reading a File (Using FileReader and BufferedReader):

We use FileReader for reading character files and wrap it with BufferedReader for
efficient reading of lines.

Example:

import java.io.*;

public class Main {

public static void main(String[] args) {

try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))) {

String line;

while ((line = reader.readLine()) != null) {

System.out.println(line);

} catch (IOException e) {

e.printStackTrace();

 }

readLine() reads a line of text and returns null when the end of the file is reached.

Writing to a File (Using FileWriter and BufferedWriter):

FileWriter is used to write characters to a file, and BufferedWriter is used for more
efficient writing.

import java.io.*;
public class Main {

public static void main(String[] args) {

try (BufferedWriter writer = new BufferedWriter(new FileWriter("test.txt"))) {

writer.write("Hello, World!");

writer.newLine();

writer.write("Java I/O is powerful.");

} catch (IOException e) {

e.printStackTrace();

3. Streams in Java

Streams are used to perform input and output operations in Java. A stream is a sequence of
data that flows from a source to a destination.

There are two types of streams in Java:

 InputStream: Reads data (input).


 OutputStream: Writes data (output).

Example of reading using FileInputStream:

import java.io.*;

public class Main {

public static void main(String[] args) {

try (FileInputStream fis = new FileInputStream("test.txt")) {

int i;

while ((i = fis.read()) != -1) {


System.out.print((char) i); // Print each character

} catch (IOException e) {

e.printStackTrace();

In this case, FileInputStream reads byte by byte from a file. Each byte is cast to a character
before being printed.

Example of writing using FileOutputStream:

import java.io.*;

public class Main {

public static void main(String[] args) {

try (FileOutputStream fos = new FileOutputStream("output.txt")) {

String str = "Writing data using FileOutputStream.";

fos.write(str.getBytes());

} catch (IOException e) {

e.printStackTrace();

Here, FileOutputStream writes bytes to a file. We convert the string into bytes
using the getBytes() method.

4. Buffered Streams
Buffered streams, such as BufferedInputStream and BufferedOutputStream, are used to
make reading and writing operations more efficient by buffering data. This reduces the
number of I/O operations by reading/writing larger chunks of data at once.

Example of using BufferedInputStream:

import java.io.*;

public class Main {

public static void main(String[] args) {

try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.txt"))) {

int data;

while ((data = bis.read()) != -1) {

System.out.print((char) data);

} catch (IOException e) {

e.printStackTrace();

Why Use Both Together?

 BufferedReader is often used with FileReader to improve performance for larger text files.
 FileReader is responsible for connecting to the file and reading characters, while
BufferedReader adds buffering, which speeds up the process by reducing the number of
file I/O operations.

1. What is JDBC?

JDBC (Java Database Connectivity) is an API that allows Java applications to interact with
relational databases like MySQL, PostgreSQL, Oracle, etc. It provides methods to execute
SQL queries, retrieve results, and handle database transactions.
2. Setting Up Your Environment

Before we start coding, here’s what you need:

1. Database: Install a database like MySQL or PostgreSQL.


2. JDBC Driver: Download the driver for your database (e.g., MySQL Connector for MySQL).
3. Add Driver to Classpath: If using an IDE like IntelliJ IDEA or Eclipse, include the JDBC driver
.jar file in your project dependencies.

3. Steps to Connect to a Database

1. Load the Driver Class (Optional for modern drivers).


2. Establish a Connection using DriverManager.
3. Create a Statement to execute SQL queries.
4. Execute Queries (SELECT, INSERT, etc.) and process results.
5. Close Resources to avoid memory leaks.

4. Example Code: Connecting to a Database

Here’s a simple example to connect to a MySQL database and retrieve data:

Database Table

Let’s assume a table users with the following structure:

sql
Copy code
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
email VARCHAR(100)
);

Java Code
java
Copy code
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class JDBCExample {


public static void main(String[] args) {
// Database URL, username, and password
String url = "jdbc:mysql://localhost:3306/your_database";
String user = "root";
String password = "your_password";

// JDBC Connection
try (Connection connection = DriverManager.getConnection(url, user,
password);
Statement statement = connection.createStatement()) {

System.out.println("Connected to the database!");

// Execute a SELECT query


String query = "SELECT * FROM users";
ResultSet resultSet = statement.executeQuery(query);

// Process the result set


while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String email = resultSet.getString("email");
System.out.printf("ID: %d, Name: %s, Email: %s%n", id,
name, email);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

5. Key Classes in JDBC

1. DriverManager: Manages the drivers and establishes connections.


2. Connection: Represents a connection to the database.
3. Statement: Executes static SQL queries.
4. PreparedStatement: Executes parameterized SQL queries (safer against SQL injection).
5. ResultSet: Holds the results of a query.

What is ORM?

 ORM stands for Object-Relational Mapping.


 It is a technique to map Java objects (classes) to database tables, making it easier to work
with databases in an object-oriented way.

Why Hibernate?

 Automates repetitive SQL tasks like CRUD operations.


 Allows developers to focus on business logic rather than database interactions.
 Provides advanced features like lazy loading, caching, and transaction management.

Steps to Learn Hibernate

1. Setup Hibernate
Install Hibernate in your project using Maven/Gradle or manually add the required
.jar files.
2. Hibernate Configuration
Learn how to configure Hibernate using an XML or hibernate.cfg.xml file and
annotations.
3. Mapping Entities
Map Java classes to database tables using annotations like @Entity and @Table.
4. Basic CRUD with Hibernate
Perform CRUD operations using Hibernate's Session object.
5. Hibernate Query Language (HQL)
Learn how to write object-oriented queries with HQL.
6. Advanced Hibernate Features
Explore caching, transactions, and relationships (@OneToMany, @ManyToOne, etc.).

Generics in Java

Why Use Generics?

Generics were introduced in Java 5 to:

1. Provide type safety at compile time.


2. Eliminate the need for typecasting.
3. Make code more reusable and readable.

1. Basics of Generics

Generics allow you to define classes, interfaces, and methods with type parameters.

Example Without Generics:


java
Copy code
import java.util.ArrayList;

public class NonGenericExample {


public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("Hello");
list.add(123); // No error, but it's unsafe

// Casting is required
String str = (String) list.get(0);
System.out.println(str);

// This will throw a runtime error (ClassCastException)


String invalid = (String) list.get(1);
}
}

Example With Generics:


java
Copy code
import java.util.ArrayList;

public class GenericExample {


public static void main(String[] args) {
// Type-safe collection
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
// list.add(123); // Compile-time error

String str = list.get(0); // No casting needed


System.out.println(str);
}
}

2. Generic Classes

You can create your own generic classes.

Example:
java
Copy code
// A generic class with a type parameter T
class Box<T> {
private T item;

public void setItem(T item) {


this.item = item;
}

public T getItem() {
return item;
}
}

public class GenericClassExample {


public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setItem("Generics are cool!");
System.out.println("String Box: " + stringBox.getItem());

Box<Integer> intBox = new Box<>();


intBox.setItem(42);
System.out.println("Integer Box: " + intBox.getItem());
}
}

3. Generic Methods

You can also create methods that are generic.

Example:
java
Copy code
public class GenericMethodExample {
// A generic method with type parameter T
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}

public static void main(String[] args) {


String[] stringArray = {"Java", "Generics", "Example"};
Integer[] intArray = {1, 2, 3};

System.out.println("String Array:");
printArray(stringArray);

System.out.println("Integer Array:");
printArray(intArray);
}
}

4. Bounded Type Parameters

You can restrict the types used as parameters with bounds.

Syntax:
java
Copy code
<T extends Superclass>

Example:
java
Copy code
class Calculator<T extends Number> {
public double add(T num1, T num2) {
return num1.doubleValue() + num2.doubleValue();
}
}

public class BoundedGenericExample {


public static void main(String[] args) {
Calculator<Integer> intCalc = new Calculator<>();
System.out.println("Sum (Integers): " + intCalc.add(10, 20));

Calculator<Double> doubleCalc = new Calculator<>();


System.out.println("Sum (Doubles): " + doubleCalc.add(5.5, 2.5));
}
}

5. Wildcards in Generics

Wildcards allow flexibility when working with generics.

Types of Wildcards:

1. Unbounded Wildcard: <?> (any type)


2. Upper Bounded Wildcard: <? extends Type> (Type or its subclasses)
3. Lower Bounded Wildcard: <? super Type> (Type or its superclasses)
Examples:

 Unbounded Wildcard:

java
Copy code
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}

 Upper Bounded Wildcard:

java
Copy code
public static void printNumbers(List<? extends Number> list) {
for (Number num : list) {
System.out.println(num);
}
}

 Lower Bounded Wildcard:

java
Copy code
public static void addNumbers(List<? super Integer> list) {
list.add(10);
}

Annotations in Java

What Are Annotations?

Annotations in Java are special markers or metadata that provide information about the code
but do not affect the program's logic. They are often used to instruct the compiler or provide
runtime hints.

1. Common Built-in Annotations

Here are some frequently used built-in annotations in Java:

Marker Annotations (No arguments)

 @Override: Ensures a method overrides a superclass method.


 @Deprecated: Marks a method or class as obsolete.
 @SuppressWarnings: Suppresses specific warnings.
Example:
java
Copy code
class Example {
@Deprecated
public void oldMethod() {
System.out.println("This method is deprecated.");
}

@Override
public String toString() {
return "Example class";
}

@SuppressWarnings("unchecked")
public void unsafeCode() {
List rawList = new ArrayList();
rawList.add("Unchecked warning suppressed.");
}
}

2. Types of Annotations

1. Built-in Annotations: Provided by Java (e.g., @Override, @SuppressWarnings).


2. Meta-Annotations: Used to define other annotations.
3. Custom Annotations: Created by developers for specific purposes.

3. Meta-Annotations

Meta-annotations are annotations that are applied to other annotations to provide additional
information. Common ones include:

Annotation Description

@Retention Specifies how long the annotation is retained: source, class, or runtime.

@Target Specifies where the annotation can be applied (e.g., methods, fields).

@Documented Indicates that the annotation should be included in the Javadoc.

@Inherited Indicates that the annotation is inherited by subclasses.

4. Creating Custom Annotations

You can define your annotations using @interface.


Steps to Create and Use Custom Annotations

1. Define the annotation.


2. Use it in your code.
3. Optionally process it using Reflection.

Example: Custom Annotation

Define the Annotation:

java
Copy code
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// Define the annotation


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation {
String author() default "Unknown";
String date();
int version() default 1;
}

Use the Annotation:

java
Copy code
@MyAnnotation(author = "John Doe", date = "2024-11-16", version = 2)
public class MyClass {

@MyAnnotation(author = "Jane Doe", date = "2024-11-16")


public void myMethod() {
System.out.println("My annotated method.");
}
}

Process the Annotation (Using Reflection):

java
Copy code
import java.lang.reflect.Method;

public class AnnotationProcessor {


public static void main(String[] args) {
try {
// Get class-level annotation
Class<MyClass> obj = MyClass.class;
if (obj.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation =
obj.getAnnotation(MyAnnotation.class);
System.out.printf("Class Annotation: Author=%s, Date=%s,
Version=%d%n",
annotation.author(), annotation.date(),
annotation.version());
}

// Get method-level annotations


for (Method method : obj.getDeclaredMethods()) {
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation =
method.getAnnotation(MyAnnotation.class);
System.out.printf("Method %s: Author=%s, Date=%s%n",
method.getName(), annotation.author(),
annotation.date());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

5. Key Points

1. Retention Policies:
o SOURCE: Available in the source code but discarded during compilation.
o CLASS: Present in the class file but not available at runtime.
o RUNTIME: Available at runtime and can be accessed via reflection.

2. Targets:
o ElementType.TYPE: Classes, interfaces, enums.
o ElementType.METHOD: Methods.
o ElementType.FIELD: Fields.

3. Default Values:
o Default values are optional and can be overridden when using the annotation.

6. Real-World Use Cases

1. Frameworks: Hibernate (@Entity, @Table), Spring (@RestController, @Autowired).


2. Code Validation: Mark methods for testing (@Test in JUnit).
3. Custom Rules: Define project-specific validations or configurations.

Would you like to dive deeper into Reflection with annotations or explore real-world
annotations like those in Spring Framework? 😊

4o

You said:
what are reflections ?
ChatGPT said:
ChatGPT

Reflection in Java

What is Reflection?

Reflection is a feature in Java that allows you to inspect, analyze, and manipulate the
structure and behavior of classes, methods, fields, and constructors at runtime. It is part of the
java.lang.reflect package.

Why Use Reflection?

Reflection is useful for:

1. Inspecting classes and objects at runtime: Retrieve metadata about a class, such as its
methods and fields.
2. Accessing private members: Bypass access modifiers to access private fields or methods.
3. Dynamic method invocation: Call methods dynamically based on runtime information.
4. Frameworks and tools: Many frameworks like Spring and Hibernate rely on reflection for
dynamic proxy creation, dependency injection, and more.

Basic Reflection API

1. Class: Represents a class or interface in memory.

java
Copy code
Class<?> cls = MyClass.class;

2. Field: Represents a class field (variable).

java
Copy code
Field field = cls.getDeclaredField("fieldName");

3. Method: Represents a class method.

java
Copy code
Method method = cls.getDeclaredMethod("methodName", parameterTypes);

4. Constructor: Represents a class constructor.

java
Copy code
Constructor<?> constructor =
cls.getDeclaredConstructor(parameterTypes);

Reflection in Action

1. Accessing Class Information


java
Copy code
public class ReflectionExample {
public static void main(String[] args) {
// Get the Class object
Class<?> cls = String.class;

// Print class name


System.out.println("Class Name: " + cls.getName());

// Print all methods


System.out.println("Methods:");
for (Method method : cls.getDeclaredMethods()) {
System.out.println(method.getName());
}

// Print all fields


System.out.println("Fields:");
for (Field field : cls.getDeclaredFields()) {
System.out.println(field.getName());
}
}
}

2. Accessing and Modifying Fields


java
Copy code
import java.lang.reflect.Field;

class MyClass {
private String message = "Hello Reflection!";
}

public class AccessPrivateField {


public static void main(String[] args) throws Exception {
MyClass obj = new MyClass();

// Get the Class object


Class<?> cls = obj.getClass();

// Access private field


Field field = cls.getDeclaredField("message");
field.setAccessible(true); // Bypass private access

// Get and modify field value


System.out.println("Original Value: " + field.get(obj));
field.set(obj, "Reflection is powerful!");
System.out.println("Modified Value: " + field.get(obj));
}
}
3. Invoking Methods Dynamically
java
Copy code
import java.lang.reflect.Method;

class MyClass {
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}

public class DynamicMethodInvocation {


public static void main(String[] args) throws Exception {
MyClass obj = new MyClass();

// Get the Class object


Class<?> cls = obj.getClass();

// Access and invoke the method


Method method = cls.getDeclaredMethod("sayHello", String.class);
method.invoke(obj, "Reflection");
}
}

4. Creating Objects Dynamically


java
Copy code
import java.lang.reflect.Constructor;

class MyClass {
private String name;

public MyClass(String name) {


this.name = name;
}

public void display() {


System.out.println("Name: " + name);
}
}

public class DynamicObjectCreation {


public static void main(String[] args) throws Exception {
// Get the Class object
Class<?> cls = MyClass.class;

// Get constructor and create a new instance


Constructor<?> constructor =
cls.getDeclaredConstructor(String.class);
Object obj = constructor.newInstance("Dynamic Object");

// Invoke method
Method method = cls.getDeclaredMethod("display");
method.invoke(obj);
}
}
Reflection Use Cases

1. Dependency Injection: Frameworks like Spring use reflection to inject dependencies


dynamically.
2. Runtime Analysis: IDE tools and debuggers inspect code structure at runtime.
3. Dynamic Proxy Creation: Creating proxies for interfaces and classes (used in AOP and
logging frameworks).
4. Serialization/Deserialization: Libraries like Jackson and Gson use reflection to map JSON to
objects.

Advantages of Reflection

1. Enables dynamic behavior in applications.


2. Powers many modern frameworks and tools.
3. Allows inspection and modification of code structure at runtime.

Disadvantages of Reflection

1. Performance Overhead: Reflection is slower than direct access due to its dynamic nature.
2. Security Risks: Bypassing access modifiers can compromise security.
3. Code Complexity: Makes debugging and maintenance more challenging.

When to Use Reflection?

 Use reflection sparingly, only when dynamic behavior is essential (e.g., in frameworks or
tools).
 Avoid using reflection in performance-critical code or simple use cases where it is
unnecessary.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy