Author: Pooja Kotwani

  • OOPS in Java

    Object Oriented Programming (OOPs) Concept in Java

    Object-Oriented Programming (OOP) in Java refers to a programming approach where objects are used to represent and execute the code’s functionality. Objects are the core components, performing specific tasks as defined by the programmer.

    OOP is designed to model real-world entities, such as inheritance, encapsulation, polymorphism, etc., in code. The primary objective is to bind data and the functions that operate on the data, ensuring that no other part of the program can directly access this data except through those functions.

    Key OOP Concepts in Java

    Java is built on the foundation of Object-Oriented Programming. Understanding key OOP concepts such as inheritance, encapsulation, abstraction, and polymorphism is critical to writing scalable and maintainable Java code.

    Prerequisites

    Before diving into the pillars of OOP, let’s review the essentials of method declaration and message passing in Java.

    Method Declaration

    A method in Java consists of six main components:

    1. Access Modifier: Defines the accessibility of the method within the application. The common types are:

    • public: Accessible in all classes.
    • protected: Accessible within the package and in subclasses.
    • private: Accessible only within the class.
    • Default (no modifier): Accessible only within the same package.

    2. Return Type: Specifies the data type returned by the method, or void if no value is returned.
    3. Method Name: Follows the same naming conventions as fields, but typically starts with a lowercase letter.
    4. Parameter List: The input parameters (if any), defined as a comma-separated list within parentheses.
    5. Exception List: Specifies any exceptions that the method might throw.
    6. Method Body: Contains the block of code that is executed when the method is called.

    Message Passing

    Objects in Java communicate with one another by sending messages, which involve calling methods. A message consists of the object’s name, the method to be executed, and any required information.

    Java OOP Concepts

    OOP in Java is centered around several key concepts:

    1. Class: A class is a blueprint or prototype that defines the properties and behaviors common to all objects of a specific type. A class typically consists of fields (attributes) and methods (behaviors). In Java, a class can be defined as:

    public class Employee {
        // Fields
        String name;
        int salary;
    
        // Methods
        void setDetails(String n, int s) {
            name = n;
            salary = s;
        }
    
        void displayDetails() {
            System.out.println("Name: " + name);
            System.out.println("Salary: " + salary);
        }
    }

    2. Object: An object is an instance of a class. It represents a specific entity in a Java program, with its own state and behavior.

    public class Main {
        public static void main(String[] args) {
            Employee emp = new Employee();
            emp.setDetails("John Doe", 50000);
            emp.displayDetails();
        }
    }

    Output:

    Name: John Doe
    Salary: 50000
    The Four Pillars of OOP

    1. Abstraction: Abstraction involves hiding unnecessary details and showing only the essential features of an object. In Java, abstraction is achieved using abstract classes and interfaces.

    Example of an abstract class:

    abstract class Vehicle {
        abstract void start();
    }
    
    class Car extends Vehicle {
        void start() {
            System.out.println("Car is starting");
        }
    }

    2. Encapsulation: Encapsulation is the practice of wrapping data and methods into a single unit (class). It restricts direct access to the data by making the fields private and providing public getter and setter methods to modify and access them.

    class Person {
        private String name;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    }

    3. Inheritance: Inheritance allows a class to inherit properties and methods from another class. It promotes reusability and a hierarchical class structure. Inheritance is achieved using the extends keyword.

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

    4. Polymorphism: Polymorphism allows methods or objects to behave in different ways based on the context. It is of two types: compile-time (method overloading) and runtime (method overriding).

    Method Overloading (Compile-time Polymorphism):

    class Calculator {
        int add(int a, int b) {
            return a + b;
        }
    
        int add(int a, int b, int c) {
            return a + b + c;
        }
    }
    Advantages of OOP over Procedural Programming
    • Code Reusability: By using classes and objects, you can reuse code efficiently without repetition.
    • Code Organization: Object-oriented code is better structured, making it easier to understand and maintain.
    • DRY Principle: OOP promotes the “Don’t Repeat Yourself” principle by allowing common code to be reused.
    • Faster Development: OOP speeds up development by encouraging modular components that can be reused across projects.

    Java Classes and Objects

    In Java, classes and objects are core concepts of Object-Oriented Programming (OOP), used to represent real-world entities and behaviors. A class acts as a blueprint for creating objects, while an object is an instance of a class. For example, “Dog” can be considered a class, while a specific dog named “Bruno” would be an object of that class.

    In this article, we’ll explore Java classes, objects, and how to implement them in your programs.

    Java Classes

    class in Java is a collection of objects with common properties and behaviors. It is essentially a user-defined prototype from which objects are created. For instance, “Car” is a class, while “BMW” is an object.

    Properties of Java Classes

    • A class is not a physical entity but a blueprint used to create objects.
    • A class itself does not occupy memory; only objects do.
    • It groups variables (attributes) and methods (behaviors) under a common structure.

    A class in Java can include the following components:

    • Data members (variables)
    • Methods
    • Constructors
    • Nested Classes
    • Interfaces

    Class Declaration in Java

    access_modifier class ClassName {
        // variables (data members)
        // methods
        // constructors
        // nested classes
        // interfaces
    }

    In this example, the Student class has two data members, id and name, with default values 0 and null, respectively.

    Components of Java Classes

    A class declaration typically contains the following:

    • Modifiers: A class can be public or have default access.
    • Class keyword: Used to declare a class.
    • Class name: Starts with an uppercase letter by convention.
    • Superclass: The parent class (if any), specified with the extends keyword.
    • Interfaces: Any implemented interfaces, separated by commas and declared with the implements keyword.
    • Body: The class body is enclosed in curly braces {}.
    Constructors

    Constructors initialize new objects. They have the same name as the class and no return type. Fields (data members) define the state of objects, while methods define the behavior.

    Java Objects

    An object in Java is an instance of a class, representing a specific entity or concept. Each object has:

    • State: Represented by the attributes (data members) of the object.
    • Behavior: Represented by the methods of the object.
    • Identity: A unique identifier that allows interaction between different objects.

    For example, in a shopping system, objects could include “ShoppingCart”, “Product”, and “Customer”.

    Declaring and Initializing Java Objects

    When an object is created, the class is instantiated. The values of the object’s attributes (state) are unique for each instance.

    Dog myDog; // Declaration
    myDog = new Dog(); // Instantiation

    Example of an Object Initialization

    public class Dog {
        // Instance variables
        String name;
        String breed;
        int age;
        String color;
    
        // Constructor
        public Dog(String name, String breed, int age, String color) {
            this.name = name;
            this.breed = breed;
            this.age = age;
            this.color = color;
        }
    
        @Override
        public String toString() {
            return "My dog's name is " + name + ", breed: " + breed + ", age: " + age + ", color: " + color;
        }
    
        public static void main(String[] args) {
            Dog myDog = new Dog("Buddy", "Golden Retriever", 3, "Golden");
            System.out.println(myDog.toString());
        }
    }

    Output:

    My dog's name is Buddy, breed: Golden Retriever, age: 3, color: Golden
    Memory Allocation in Java Objects

    When an object is created, it is allocated memory on the heap. All classes have at least one constructor; if none is explicitly defined, Java provides a default constructor.

    Methods to Create Objects

    There are several ways to create objects in Java:

    1. Using the new keyword:

    Car myCar = new Car();

    2. Using Class.forName(String className):

    Car obj = (Car)Class.forName("com.example.Car").newInstance();

    3. Using clone() method:

    Car car1 = new Car();
    Car car2 = (Car)car1.clone();

    4. Using Deserialization:

    ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
    Car car = (Car)in.readObject();
    Anonymous Objects

    Anonymous objects are objects created without a reference and are often used for immediate method calls. These objects are destroyed after the method call completes.

    new Car().drive();
    Difference Between Class and Object
    ClassObject
    A blueprint for creating objects.An instance of the class.
    Does not occupy memory.Occupies memory when instantiated.
    Logical entity.Physical entity.
    Declared only once.Multiple objects can be created from the same class.

    Java Naming Conventions

    In software development, especially in Java, writing clean and readable code is essential. One key aspect of this is following proper naming conventions. This practice ensures that your code is easy to read and understand, both for yourself and for other developers who may work on the same project. While at a smaller scale these conventions may not seem critical, they become increasingly important in large, industrial-level projects where maintaining clean, organized code can significantly reduce development time and confusion.

    For example, when naming a variable meant to store displacement, a meaningful name like “displacement” should be used, rather than something unclear like “x” or “d”. As the project grows, meaningful names enhance readability and reduce errors.

    Key Naming Guidelines
    • Classes: When naming a class, it should be a noun, reflecting the purpose it serves in the program. The name should describe what the class is about without needing to read its contents. Examples of good class names include AddNumbers, ReverseString, etc. Poor names would be generic or meaningless like A1 or Programming.
    • Interfaces: Interfaces usually describe capabilities and are often named with adjectives. For example, common Java interfaces like Runnable and Serializable describe behavior. It’s also a good practice to use the suffix “able” (e.g., Readable), though it’s not mandatory.
    • Methods: Methods, being actions, should be named as verbs. Their names should clearly describe what they do. For example, the main() method in Java signifies the starting point of the program and is a good method name conventionally.
    • Constants: Constants are fixed values and should be named in uppercase with words separated by underscores (_). Examples include PI, MAX_VALUE, MIN_VALUE.
    Java Naming Conventions Overview

    In Java, it is considered best practice to name classes, variables, and methods according to their intended functionality. This approach enhances the readability and maintainability of the code. Java follows the CamelCase convention for naming classes, methods, and variables, where words are written together without spaces, and each word after the first starts with an uppercase letter. For example, firstName or calculateArea.

    However, there are a few exceptions:

    • Packages: Package names should be written in all lowercase, even if they contain multiple words.
    • Constants: Constants should be written in uppercase, with words separated by underscores.
    Naming Conventions for Specific Java Elements

    1. Classes and Interfaces

    • Classes: Class names should be nouns, using CamelCase, with each internal word starting with a capital letter. Avoid abbreviations or acronyms, and instead use meaningful words.Example: StudentIntegerDataProcessor
    • Interfaces: Interfaces should follow the same convention as classes, but should often reflect actions or behaviors, typically in the form of adjectives.Example: RunnableSerializable

    2. Methods

    • Method Names: Methods should be verbs, following the camelCase convention. The first word is in lowercase, and each subsequent word starts with an uppercase letter.Example: calculateTotalfindMaximumprintDetails

    3. Variables

    • Variable Names: Variables should have meaningful names and be short but descriptive. They should not start with an underscore (_) or dollar sign ($), even though Java permits it. Single-character variable names (like ijk) should generally be reserved for temporary variables in loops or similar short-term uses.Example: int scoredouble interestRateString username

    4. Constants

    • Constant Names: Constants are named in all uppercase letters, with words separated by underscores (_).Example: MAX_LIMITPIDEFAULT_TIMEOUT

    5. Packages

    • Package Names: Package names should always be written in lowercase, with each part separated by dots (.). The first part of the package name should usually be the top-level domain name (e.g., comorg), followed by the company’s domain and additional parts.Example: com.example.projectnameorg.utilities.math
    Importance of Naming Conventions

    Adopting proper naming conventions improves code clarity, making it easier for other developers to understand the purpose of each class, method, variable, and constant without having to examine the underlying implementation. Following these guidelines also helps avoid confusion and errors in larger codebases. Furthermore, adhering to established naming conventions is a sign of professionalism and helps teams maintain a consistent coding style across projects.

    These conventions, though simple, are critical in producing clean, readable, and maintainable Java code—allowing teams to work more effectively and reducing the chances of misinterpretation or mistakes during development.

    Methods in Java

    method in Java is a collection of statements designed to perform a specific task and, optionally, return a result. Methods can also execute tasks without returning any value. By using methods, Java allows code reuse, eliminating the need to rewrite similar logic multiple times. Unlike languages such as C, C++, or Python, every method in Java must belong to a class.

    Key Points about Methods:
    • A method is similar to a function and is used to describe the behavior of an object.
    • It is a block of code that performs a particular task.

    Syntax of a Method:

    <access_modifier> <return_type> <method_name>(list_of_parameters) {
        // body of the method
    }
    Advantages of Methods:
    • Code Reusability: Methods allow code to be written once and reused as needed, saving time and effort.
    • Code Optimization: Methods simplify complex operations and improve the structure of the code.
    Method Declaration

    A method declaration generally has six components:

    1. Modifier: Defines the access level of the method (e.g., public, private, protected, default).

    • public: Accessible in all classes.
    • protected: Accessible within the class and its subclasses.
    • private: Accessible only within the defining class.
    • default: Accessible within the same class and package, with no modifier.

    2. Return Type: Specifies the data type of the value returned by the method or void if no value is returned.
    3. Method Name: The name given to the method, following the same rules as variable names but typically being a verb. It is required.
    4. Parameter List: A comma-separated list of input parameters (preceded by their data types). If no parameters are needed, empty parentheses are used. This is optional.
    5. Exception List: Specifies any exceptions the method can throw. This is optional.
    Method Body: The actual code executed by the method, enclosed in braces. This is also optional.

    Types of Methods in Java

    1. Predefined Methods: These are methods already provided by Java libraries, also known as standard or built-in methods. We can directly call these methods within our program.

    2. User-defined Methods: Methods written by the programmer, tailored to specific tasks according to the program’s needs.

    Ways to Create Methods in Java

    1. Instance Methods: Access instance data using the object name. These methods are declared within a class.

    void methodName() {
        // method body
    }

    2. Static Methods: Access static data using the class name. These methods are declared with the static keyword.

    static void methodName() {
        // method body
    }
    Method Signature

    A method signature consists of the method name and its parameter list (the number, type, and order of parameters). The return type and exceptions are not part of the method signature.

    Example:

    int max(int x, int y)  // Method signature

    In this example, the method max has two parameters of type int.

    Naming a Method

    The convention for naming methods in Java is to use verbs in lowercase or multi-word names starting with a verb. If the name contains multiple words, the first letter of each word after the first should be capitalized (CamelCase).

    Rules:

    • Method names should be verbs and start with a lowercase letter.
    • If the method name contains more than one word, the first word must be a verb, followed by an adjective or noun.
    • In multi-word names, capitalize the first letter of each subsequent word (e.g., findMaxcomputeSum).
    Method Calling

    Methods need to be called to execute. They can be called in three situations:

    • The method completes all its statements.
    • The method reaches a return statement.
    • The method throws an exception.

    Example:

    class Calculator {
        public int add(int a, int b) {
            return a + b;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Calculator calc = new Calculator();
            int result = calc.add(5, 10);
            System.out.println("Sum: " + result);
        }
    }

    Output:

    Sum: 15
    Passing Parameters to Methods

    Parameters can be passed in two ways:

    1. Passing Arrays: Arrays can be passed as arguments to methods.

    2. Varargs (Variable Arguments): When the number of parameters is unknown, we can use the varargs feature by specifying the parameter type followed by an ellipsis (...).

    Method Overloading

    Method overloading allows multiple methods with the same name but different parameter lists to coexist in the same class. This enables methods to handle various types of inputs.

    Memory Allocation for Methods

    Method calls are implemented using a stack. When a method is invoked, a stack frame is created to store its arguments, local variables, and return value. Once the method completes, its stack frame is removed from the stack.

    Example:

    class Example {
        private int number;
        private String name;
    
        // Getter and Setter methods
        public int getNumber() { return number; }
        public String getName() { return name; }
    
        public void setNumber(int number) { this.number = number; }
        public void setName(String name) { this.name = name; }
    
        // Other methods
        public void printDetails() {
            System.out.println("Number: " + number);
            System.out.println("Name: " + name);
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Example example = new Example();
            example.setNumber(42);
            example.setName("Java Example");
            example.printDetails();
        }
    }

    Output:

    Number: 42
    Name: Java Example

    Access Modifiers in Java

    Access modifiers in Java are essential for controlling the visibility and accessibility of classes, constructors, methods, variables, and data members. They provide a way to enforce security and manage how components of a program can interact with each other based on the access modifier used. In this section, we’ll explore the types of access modifiers available in Java, along with examples that demonstrate their use.

    Types of Access Modifiers in Java

    There are four access modifiers in Java:

    1. Default (No keyword required)
    2. Private
    3. Protected
    4. Public

    1. Default Access Modifier

    If no access modifier is specified, the default modifier is used. Members with the default access modifier are accessible only within the same package.

    Example:

    // Java program demonstrating default access modifier
    package package1;
    
    class ExampleClass {
        void showMessage() {
            System.out.println("This is a default access modifier example.");
        }
    }
    // Attempting to access a default class from another package
    package package2;
    
    import package1.*;
    
    public class TestClass {
        public static void main(String[] args) {
            ExampleClass example = new ExampleClass(); // Compile-time error
            example.showMessage();
        }
    }

    Output:

    Compile-time error

    2. Private Access Modifier

    The private keyword is used to restrict access to class members. Members declared as private are accessible only within the class where they are defined.

    Example:

    // Java program illustrating private access modifier
    package package1;
    
    class ExampleClass {
        private void displayMessage() {
            System.out.println("This is a private method.");
        }
    }
    
    public class TestClass {
        public static void main(String[] args) {
            ExampleClass example = new ExampleClass();
            example.displayMessage();  // Compile-time error
        }
    }

    Output:

    error: displayMessage() has private access in ExampleClass
            example.displayMessage();

    3. Protected Access Modifier

    The protected keyword allows access to members within the same package and also in subclasses, even if they are in different packages.

    Example:

    // Package 1 - Parent class with protected method
    package package1;
    
    public class ParentClass {
        protected void displayMessage() {
            System.out.println("This is a protected method.");
        }
    }
    // Package 2 - Subclass inheriting from ParentClass
    package package2;
    
    import package1.ParentClass;
    
    public class ChildClass extends ParentClass {
        public static void main(String[] args) {
            ChildClass child = new ChildClass();
            child.displayMessage();
        }
    }

    Output:

    This is a protected method.

    4. Public Access Modifier

    The public keyword gives the widest access. Members declared as public can be accessed from any class, in any package.

    Example:

    // Java program demonstrating public access modifier
    package package1;
    
    public class ExampleClass {
        public void showMessage() {
            System.out.println("This is a public method.");
        }
    }
    // Accessing a public method from another package
    package package2;
    
    import package1.ExampleClass;
    
    public class TestClass {
        public static void main(String[] args) {
            ExampleClass example = new ExampleClass();
            example.showMessage();
        }
    }

    Output:

    This is a public method.
    Key Points:
    • Default access is package-private, meaning it’s accessible only within the same package.
    • Private access is the most restrictive, limiting access only to the class in which the member is declared.
    • Protected access allows visibility in the same package and in subclasses across different packages.
    • Public access provides unrestricted access throughout the program.
    Best Practices for Using Access Modifiers
    • Always prefer the most restrictive access level for class members. For example, use private unless there’s a specific reason to expose a member.
    • Avoid using public fields, as this breaks encapsulation. Instead, use getter and setter methods to control access to fields.
    Algorithm for Using Access Modifiers in Java

    1. Define the class: Create a class representing your object or functionality.
    2. Declare instance variables: Define the data members that represent the object’s state.
    3. Choose access modifiers:

    • Use private for variables that should not be accessible outside the class.
    • Use protected for variables that can be accessed by subclasses, even in different packages.
    • Use public for variables that need to be accessed from any part of the program.
      Implement getter and setter methods: Even for public variables, it’s good practice to use accessors and mutators to control how values are accessed and modified.

    4. Test visibility: Ensure that your chosen access levels make sense for the structure and interaction of your classes.

    Java Constructors

    In Java, a constructor is a special type of method used to initialize an object. Constructors are called when an object of a class is created, allowing developers to set initial values for the object’s attributes.

    Whenever you create an object using the new keyword, a constructor is invoked to allocate memory for the object and initialize it.

    Understanding constructors can greatly enhance your Java programming skills, particularly when working with large applications. Mastery of constructors is crucial for developing scalable, maintainable code, serving as a stepping stone toward more advanced programming concepts.

    Example of a Java Constructor

    Below is a simple demonstration of how constructors work in Java:

    // Java Program to demonstrate
    // Constructor usage
    public class MyClass {
    
        // Constructor
        MyClass() {
            System.out.println("Constructor called");
        }
    
        public static void main(String[] args) {
            // Creating an object which calls the constructor
            MyClass obj = new MyClass();
        }
    }

    Output:

    Constructor called

    Differences Between Constructors and Methods

    • Name: A constructor must have the same name as the class, whereas methods can have any name.
    • Return Type: Constructors do not have a return type, while methods can return values (or be void if they return nothing).
    • Invocation: Constructors are called only once when an object is created, whereas methods can be called multiple times.

    Syntax Example of a constructor:

    class MyClass {
        // A constructor
        MyClass() {
            // Initialization code here
        }
    }
    
    // Creating an object, which invokes the constructor
    MyClass obj = new MyClass();

    Output:

    Constructor called

    Differences Between Constructors and Methods

    • Name: A constructor must have the same name as the class, whereas methods can have any name.
    • Return Type: Constructors do not have a return type, while methods can return values (or be void if they return nothing).
    • Invocation: Constructors are called only once when an object is created, whereas methods can be called multiple times.

    Syntax example of a constructor:

    class MyClass {
        // A constructor
        MyClass() {
            // Initialization code here
        }
    }
    
    // Creating an object, which invokes the constructor
    MyClass obj = new MyClass();
    Why Do We Need Constructors?

    Constructors are used to initialize objects at the time of creation. For example, if we think of a class that represents a box, it might have fields like length, breadth, and height. When an object of this class is created, it wouldn’t make sense for it to have undefined dimensions, so constructors ensure these values are set.

    When Is a Constructor Called?

    A constructor is called whenever an object is instantiated using the new keyword. Some rules for writing constructors include:

    • The constructor must have the same name as the class.
    • A constructor cannot be abstractfinalstatic, or synchronized.
    • Access modifiers can be used with constructors to control which classes can instantiate objects.
    Types of Constructors in Java

    There are three types of constructors in Java:

    1. Default Constructor
    2. Parameterized Constructor
    3. Copy Constructor

    1. Default Constructor : A default constructor is one without parameters. If no constructor is defined explicitly, Java automatically provides a default constructor.

    Example:

    // Java Program to demonstrate Default Constructor
    public class MyClass {
    
        // Default Constructor
        MyClass() {
            System.out.println("Default constructor called");
        }
    
        public static void main(String[] args) {
            MyClass obj = new MyClass();
        }
    }

    Output:

    Default constructor called

    2. Parameterized Constructor : A parameterized constructor takes arguments, allowing you to set object properties with specific values at the time of creation.

    Example:

    // Java Program to demonstrate Parameterized Constructor
    public class MyClass {
        String name;
        int id;
    
        // Parameterized Constructor
        MyClass(String name, int id) {
            this.name = name;
            this.id = id;
        }
    
        public static void main(String[] args) {
            MyClass obj = new MyClass("John", 101);
            System.out.println("Name: " + obj.name + ", ID: " + obj.id);
        }
    }

    Output:

    Name: John, ID: 101

    3. Copy Constructor : A copy constructor is used to create a new object as a copy of an existing object. While Java does not provide a built-in copy constructor, you can create one manually by passing an object of the same class to the constructor.

    Example:

    // Java Program to demonstrate Copy Constructor
    public class MyClass {
        String name;
        int id;
    
        // Parameterized Constructor
        MyClass(String name, int id) {
            this.name = name;
            this.id = id;
        }
    
        // Copy Constructor
        MyClass(MyClass obj) {
            this.name = obj.name;
            this.id = obj.id;
        }
    
        public static void main(String[] args) {
            // Creating an object using a parameterized constructor
            MyClass obj1 = new MyClass("Alice", 202);
    
            // Creating a new object using the copy constructor
            MyClass obj2 = new MyClass(obj1);
    
            System.out.println("Name: " + obj2.name + ", ID: " + obj2.id);
        }
    }

    Output:

    Name: Alice, ID: 202
    Constructor Overloading

    Just like methods, constructors can also be overloaded by defining multiple constructors with different parameter lists. This allows for flexibility when creating objects with varying initial values.

    Example:

    // Java Program to demonstrate Constructor Overloading
    public class MyClass {
    
        // Constructor with one parameter
        MyClass(String name) {
            System.out.println("Name: " + name);
        }
    
        // Constructor with two parameters
        MyClass(String name, int age) {
            System.out.println("Name: " + name + ", Age: " + age);
        }
    
        public static void main(String[] args) {
            // Calling different constructors
            MyClass obj1 = new MyClass("Alice");
            MyClass obj2 = new MyClass("Bob", 25);
        }
    }

    Output:

    Name: Alice
    Name: Bob, Age: 25

    Inheritance in Java

    Java Inheritance Overview

    Inheritance is one of the core concepts in Object-Oriented Programming (OOP), allowing a class to inherit properties and behaviors (fields and methods) from another class. In Java, this mechanism enables the creation of new classes that reuse, extend, and modify the functionality of existing ones. A class that inherits from another class can access the fields and methods of the parent class, while also adding its own methods and fields.

    Why Do We Need Java Inheritance?

    Inheritance offers several benefits, such as promoting code reusabilitymethod overriding (which helps in achieving runtime polymorphism), and abstraction, allowing us to work with classes without needing to know the full details of their implementations. By leveraging inheritance, developers can create flexible and easily maintainable code.

    Key Benefits:
    • Code Reusability: Common code in the superclass can be reused in the child classes, reducing redundancy.
    • Method Overriding: Enables the child class to provide specific implementations for methods defined in the parent class, facilitating runtime polymorphism.
    • Abstraction: Hides complex details from the user and only shows relevant functionalities.
    Important Terms
    • Class: A blueprint for creating objects that share common attributes and behaviors.
    • Super Class (Parent Class): The class whose properties are inherited by another class.
    • Sub Class (Child Class): The class that inherits from another class. It can have additional fields and methods specific to itself.
    • Reusability: One of the major advantages of inheritance, where existing fields and methods from a parent class can be used in the child class without duplicating code.
    How to Implement Inheritance in Java?

    Inheritance in Java is implemented using the extends keyword. This keyword allows the subclass to extend the functionality of the superclass.

    Syntax:

    class SubClass extends SuperClass {
       // fields and methods
    }

    Example of Java Inheritance

    In this example, we have a base class Vehicle and a derived class Car that extends the Vehicle class. The TestDrive class is used to demonstrate the usage of inheritance.

    // Base class
    class Vehicle {
        public int speed;
        public int fuel;
    
        // Constructor
        public Vehicle(int speed, int fuel) {
            this.speed = speed;
            this.fuel = fuel;
        }
    
        // Methods
        public void accelerate(int increment) {
            speed += increment;
        }
    
        public void brake(int decrement) {
            speed -= decrement;
        }
    
        @Override
        public String toString() {
            return "Speed: " + speed + " km/h, Fuel: " + fuel + " L";
        }
    }
    
    // Derived class
    class Car extends Vehicle {
        public int numberOfDoors;
    
        // Constructor
        public Car(int speed, int fuel, int numberOfDoors) {
            super(speed, fuel); // Calling the constructor of the superclass
            this.numberOfDoors = numberOfDoors;
        }
    
        // Additional method for the Car class
        public void lockDoors() {
            System.out.println("Doors locked.");
        }
    
        @Override
        public String toString() {
            return super.toString() + ", Doors: " + numberOfDoors;
        }
    }
    
    // Driver class
    public class TestDrive {
        public static void main(String[] args) {
            Car myCar = new Car(80, 50, 4);
            System.out.println(myCar.toString());
        }
    }

    Output:

    Speed: 80 km/h, Fuel: 50 L, Doors: 4

    In this example, when an object of Car is created, the fields and methods from the Vehicle class are inherited, allowing the car to access properties like speed and fuel. The toString() method is overridden in the Car class to display additional information about the car.

    Types of Inheritance in Java

    Java supports several types of inheritance, each with its own structure and use cases:

    1. Single Inheritance: A subclass inherits from one superclass.
    2. Multilevel Inheritance: A chain of classes where a subclass inherits from a superclass, and another class inherits from that subclass.
    3. Hierarchical Inheritance: One superclass is inherited by multiple subclasses.
    4. Multiple Inheritance (Through Interfaces): In Java, a class can implement multiple interfaces, which allows it to inherit features from multiple sources. Java doesn’t support multiple inheritance with classes directly to avoid complexity, but interfaces provide a way to achieve this.
    5. Hybrid Inheritance: A combination of multiple and other forms of inheritance. Java supports this through interfaces and combinations of single and hierarchical inheritance.
    Example:

    In the following example, Dog is a subclass that inherits from the Animal superclass.

    // Superclass
    class Animal {
        public void sound() {
            System.out.println("This is an animal sound.");
        }
    }
    
    // Subclass
    class Dog extends Animal {
        public void bark() {
            System.out.println("The dog barks.");
        }
    }
    
    // Driver class
    public class TestAnimal {
        public static void main(String[] args) {
            Dog myDog = new Dog();
            myDog.sound();  // Inherited from Animal
            myDog.bark();   // Defined in Dog
        }
    }

    Output:

    This is an animal sound.
    The dog barks.

    Abstraction in Java

    Abstraction in Java refers to the practice of showing only the necessary details and functionalities to the user while hiding the underlying implementation. By doing so, unnecessary complexities are kept out of sight, making it easier to work with the code.

    Real-World Example of Abstraction:

    A remote control for a TV is a great example of abstraction. You press buttons to change channels or adjust the volume, without needing to understand the inner workings of the TV or how the remote communicates with it.

    What is Abstraction in Java?

    In Java, abstraction is implemented using abstract classes and interfaces. If you want complete (100%) abstraction, interfaces are the tool of choice.

    Data Abstraction is the process of focusing on the important characteristics of an object while ignoring irrelevant details. It helps in defining the key behaviors and properties of an object, distinguishing it from others in a meaningful way.

    Real-Life Example of Abstraction:

    Imagine driving a car. You know pressing the accelerator increases the speed, and pressing the brakes slows it down, but you don’t need to understand the detailed mechanism behind it. This is how abstraction simplifies interaction.

    Java Abstract Classes and Abstract Methods
    • An abstract class is defined with the abstract keyword.
    • An abstract method is declared without any implementation.
    • An abstract class can have both abstract methods and regular methods.
    • If a class contains abstract methods, it must be declared as abstract.
    • Abstract classes cannot be instantiated directly; they must be extended.
    • Abstract classes can have constructors, both parameterized and default.
    How to Implement Abstraction in Java

    1. Define an abstract class or interface to represent the abstraction.
    2. Declare common behaviors and properties as abstract methods within the class or interface.
    3. Extend the abstract class or implement the interface in concrete classes.
    4. Provide the specific implementations for the abstract methods in the concrete classes.
    5. Use these concrete classes to achieve the desired functionality in your program.

    When to Use Abstract Classes and Methods?

    Abstract classes are useful when you want to define a template that outlines the structure of an abstraction but leave some details to be implemented by subclasses. A common example is the Shape hierarchy in design or simulation software, where shapes like circles, squares, and triangles share some characteristics but differ in how they calculate their areas.

    Java Abstraction Example

    Example 1:

    // Abstract class for Shape
    abstract class Shape {
        String color;
    
        // Abstract methods
        abstract double area();
        public abstract String toString();
    
        // Constructor for Shape
        public Shape(String color) {
            this.color = color;
        }
    
        // Concrete method
        public String getColor() {
            return color;
        }
    }
    
    // Circle class extending Shape
    class Circle extends Shape {
        double radius;
    
        public Circle(String color, double radius) {
            super(color);
            this.radius = radius;
        }
    
        @Override
        double area() {
            return Math.PI * Math.pow(radius, 2);
        }
    
        @Override
        public String toString() {
            return "Circle color is " + getColor() + " and area is: " + area();
        }
    }
    
    // Rectangle class extending Shape
    class Rectangle extends Shape {
        double length, width;
    
        public Rectangle(String color, double length, double width) {
            super(color);
            this.length = length;
            this.width = width;
        }
    
        @Override
        double area() {
            return length * width;
        }
    
        @Override
        public String toString() {
            return "Rectangle color is " + getColor() + " and area is: " + area();
        }
    }
    
    // Main class
    public class Main {
        public static void main(String[] args) {
            Shape circle = new Circle("Red", 5.0);
            Shape rectangle = new Rectangle("Blue", 4.0, 6.0);
    
            System.out.println(circle.toString());
            System.out.println(rectangle.toString());
        }
    }

    Output:

    Circle color is Red and area is: 78.53981633974483
    Rectangle color is Blue and area is: 24.0
    Using Interfaces for Abstraction

    Interfaces provide a way to achieve complete abstraction in Java. They contain method declarations but no method implementations.

    Example:

    // Shape interface
    interface Shape {
        double calculateArea();
    }
    
    // Circle class implementing Shape interface
    class Circle implements Shape {
        private double radius;
    
        public Circle(double radius) {
            this.radius = radius;
        }
    
        @Override
        public double calculateArea() {
            return Math.PI * radius * radius;
        }
    }
    
    // Rectangle class implementing Shape interface
    class Rectangle implements Shape {
        private double length, width;
    
        public Rectangle(double length, double width) {
            this.length = length;
            this.width = width;
        }
    
        @Override
        public double calculateArea() {
            return length * width;
        }
    }
    
    // Main class
    public class Main {
        public static void main(String[] args) {
            Shape circle = new Circle(5.0);
            Shape rectangle = new Rectangle(4.0, 6.0);
    
            System.out.println("Area of Circle: " + circle.calculateArea());
            System.out.println("Area of Rectangle: " + rectangle.calculateArea());
        }
    }

    Output:

    Area of Circle: 78.53981633974483
    Area of Rectangle: 24.0
    Advantages of Abstraction

    1. Simplifies Code: Reduces complexity by hiding implementation details.
    2. Increases Reusability: Encourages code reuse and avoids duplication.
    3. Enhances Security: Provides essential details while hiding the rest.
    4. Improves Maintainability: Easier to update and maintain.
    5. Supports Modularity: Breaks down complex systems into smaller parts.

    Disadvantages of Abstraction

    1. Increased Complexity: May add unnecessary layers if not used properly.
    2. Debugging Challenges: Can make debugging more difficult due to hidden layers.
    3. Potential Performance Overhead: Additional abstraction layers might affect performance.

    Encapsulation in Java

    Encapsulation is a core principle of object-oriented programming (OOP) in Java. It refers to the bundling of data (variables) and methods (functions) that operate on that data into a single unit, typically a class. Encapsulation ensures that the internal implementation of a class is hidden from outside access, only exposing a public interface that allows interaction with the class through methods. This technique enhances security and data integrity, as the internal state of an object can only be modified in controlled ways.

    In Java, encapsulation is achieved by declaring instance variables as private, which means they cannot be accessed directly from outside the class. To provide access to these variables, public getter and setter methods are used, which enable controlled access and modification of the private variables.

    Key Aspects of Encapsulation
    • Data hiding: By using private variables, you ensure that the internal details of a class are hidden.
    • Access through methods: Public getter and setter methods allow controlled access to the variables.
    • Modularity: It promotes better design by keeping data and methods together in a single unit.
    • Security and validation: Setters can include validation logic to prevent invalid data from being assigned to variables.

    Java Encapsulation Example

    // Employee Class demonstrating encapsulation
    class Employee {
        // Private fields, not accessible outside the class
        private String name;
        private int id;
    
        // Getter method for name
        public String getName() {
            return name;
        }
    
        // Setter method for name
        public void setName(String name) {
            this.name = name;
        }
    
        // Getter method for id
        public int getId() {
            return id;
        }
    
        // Setter method for id
        public void setId(int id) {
            this.id = id;
        }
    }
    
    // Main class to test encapsulation
    public class Main {
        public static void main(String[] args) {
            // Creating an object of the Employee class
            Employee emp = new Employee();
    
            // Using setter methods to assign values
            emp.setName("Alice");
            emp.setId(101);
    
            // Using getter methods to retrieve and display values
            System.out.println("Employee Name: " + emp.getName());
            System.out.println("Employee ID: " + emp.getId());
        }
    }

    Output:

    Employee Name: Alice
    Employee ID: 101
    Advantages of Encapsulation

    Data Hiding: Encapsulation prevents direct access to sensitive data, making it more secure.

    1. Improved Flexibility: You can make instance variables read-only or write-only by providing only a getter or setter method, depending on the requirement.
    2. Reusability: Encapsulated code is easier to maintain, reuse, and extend because it minimizes interdependencies.
    3. Easy Testing: Since encapsulated code separates data and functions, it is easier to test individual components.
    4. Adaptability: The internal implementation can change without affecting code that interacts with the class.

    Disadvantages of Encapsulation
    • Increased Complexity: Encapsulation may lead to more boilerplate code, such as writing getter and setter methods for each private variable.
    • Difficult to Understand: Excessive use of encapsulation can make code more difficult to follow, especially if it adds layers of abstraction.
    • Limited Flexibility: Over-encapsulation can sometimes restrict the flexibility of your design, making it harder to quickly implement new features.
    Java Encapsulation Example (Area Calculation)

    Here’s another example that demonstrates encapsulation in a different context, where it is used to calculate the area of a rectangle:

    // Rectangle Class to calculate area using encapsulation
    class Rectangle {
        // Private fields for length and breadth
        private int length;
        private int breadth;
    
        // Constructor to initialize length and breadth
        public Rectangle(int length, int breadth) {
            this.length = length;
            this.breadth = breadth;
        }
    
        // Getter for length
        public int getLength() {
            return length;
        }
    
        // Setter for length
        public void setLength(int length) {
            this.length = length;
        }
    
        // Getter for breadth
        public int getBreadth() {
            return breadth;
        }
    
        // Setter for breadth
        public void setBreadth(int breadth) {
            this.breadth = breadth;
        }
    
        // Method to calculate and print area
        public void calculateArea() {
            int area = length * breadth;
            System.out.println("Area of the Rectangle: " + area);
        }
    }
    
    // Main class to test encapsulation in rectangle class
    public class Main {
        public static void main(String[] args) {
            // Creating a rectangle object
            Rectangle rect = new Rectangle(5, 10);
    
            // Accessing and modifying private fields via getters and setters
            rect.setLength(8);
            rect.setBreadth(12);
    
            // Calculating and printing the area
            rect.calculateArea();
        }
    }

    Output:

    Area of the Rectangle: 96

    What are Interfaces in Java?

    In Java, an interface is an abstract type used to specify a set of behaviors that a class must implement. It provides a blueprint for a class, defining static constants and abstract methods (methods without a body). An interface is primarily used to achieve abstraction and multiple inheritance. Traditionally, interfaces in Java could only contain abstract methods and final variables, but with the introduction of Java 8, interfaces can also contain default and static methods. Interfaces help define a “contract” or “is-a” relationship between classes, specifying the methods they must implement, without worrying about the internal details.

    Syntax for Java Interfaces

    To declare an interface in Java, use the interface keyword. By default, all methods declared in an interface are abstract and public, and all fields are public, static, and final.

    interface InterfaceName {
        // Declare constant fields
        // Declare abstract methods (no implementation)
    }

    A class that implements an interface must provide implementations for all the methods declared in the interface, using the implements keyword.

    Features of Interfaces

    1. Achieves total abstraction: An interface allows complete abstraction since it contains no method implementation.
    2. Multiple inheritance: While Java does not support multiple inheritance with classes, an interface allows classes to implement multiple interfaces.
    3. Loose coupling: Interfaces enable loose coupling between classes as they provide a level of abstraction.
    4. Final variables: All variables declared in an interface are final, static, and public by default.

    Relationship Between Class and Interface

    In Java, a class can implement one or more interfaces, but an interface can only be implemented by a class, not the other way around. Classes must implement all the methods defined by the interfaces they implement. Additionally, interfaces can extend other interfaces, allowing for a flexible hierarchy of behavior definitions.

    Example of a Simple Java Interface

    interface Player {
        int id = 10; // Constant by default
        void move(); // Abstract method
    }
    class TestClass implements Player {
        public void move() {
            System.out.println("Player moves");
        }
    
        public static void main(String[] args) {
            TestClass player = new TestClass();
            player.move();
            System.out.println("Player ID: " + Player.id);
        }
    }

    Output:

    Player moves
    Player ID: 10

    Example: Vehicle Interface

    Consider the example of vehicles like bicycles and cars that have common functionalities. We can create a Vehicle interface to define the general behaviors that every vehicle must implement, and let each vehicle type provide its specific implementation.

    interface Vehicle {
        void changeGear(int gear);
        void speedUp(int increment);
        void applyBrakes(int decrement);
    }
    
    class Bicycle implements Vehicle {
        int speed;
        int gear;
    
        public void changeGear(int newGear) {
            gear = newGear;
        }
    
        public void speedUp(int increment) {
            speed += increment;
        }
    
        public void applyBrakes(int decrement) {
            speed -= decrement;
        }
    
        public void display() {
            System.out.println("Speed: " + speed + " Gear: " + gear);
        }
    }
    
    class Car implements Vehicle {
        int speed;
        int gear;
    
        public void changeGear(int newGear) {
            gear = newGear;
        }
    
        public void speedUp(int increment) {
            speed += increment;
        }
    
        public void applyBrakes(int decrement) {
            speed -= decrement;
        }
    
        public void display() {
            System.out.println("Speed: " + speed + " Gear: " + gear);
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Bicycle bike = new Bicycle();
            bike.changeGear(2);
            bike.speedUp(10);
            bike.applyBrakes(3);
            System.out.println("Bicycle state:");
            bike.display();
    
            Car car = new Car();
            car.changeGear(3);
            car.speedUp(20);
            car.applyBrakes(5);
            System.out.println("Car state:");
            car.display();
        }
    }

    Output:

    Bicycle state:
    Speed: 7 Gear: 2
    Car state:
    Speed: 15 Gear: 3
    Multiple Inheritance Using Interfaces

    Java doesn’t allow multiple inheritance with classes, but interfaces provide a way to achieve it. A class can implement multiple interfaces, inheriting behaviors from each one.

    interface Machine {
        void start();
    }
    
    interface Sound {
        void playSound();
    }
    
    class Robot implements Machine, Sound {
        public void start() {
            System.out.println("Robot started.");
        }
    
        public void playSound() {
            System.out.println("Robot playing sound.");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Robot r = new Robot();
            r.start();
            r.playSound();
        }
    }

    Output:

    Robot started.
    Robot playing sound.

    ‘this’ reference in Java

    In Java, this is a reference variable that refers to the current object instance. It helps in distinguishing between instance variables and parameters with the same name and can be used for various purposes such as invoking class methods, passing the current object as a parameter, and improving code readability.

    Java this Reference Example:

    In Java, this refers to the object on which a method or constructor is being invoked. It allows access to the instance variables and methods of the current object.

    Here’s an example demonstrating the usage of this reference:

    // Java program demonstrating the use of 'this' reference
    
    class Employee {
        String name;
        int id;
    
        // Constructor
        Employee(String name, int id) {
            this.name = name;  // 'this' refers to the instance variable 'name'
            this.id = id;      // 'this' refers to the instance variable 'id'
        }
    
        // Method to display employee details
        public void displayDetails() {
            System.out.println("Employee Name: " + this.name);
            System.out.println("Employee ID: " + this.id);
        }
    
        // Main method
        public static void main(String[] args) {
            Employee emp1 = new Employee("John", 101);
            Employee emp2 = new Employee("Alice", 102);
    
            emp1.displayDetails();
            emp2.displayDetails();
        }
    }

    Output:

    Employee Name: John
    Employee ID: 101
    
    Employee Name: Alice
    Employee ID: 102

    Explanation:

    In the above example, this is used in the constructor to refer to the instance variables name and id, differentiating them from the constructor’s parameters. The displayDetails method prints out the details of each employee using the this reference.

    Different Uses of this in Java:

    1. Referring to the Current Class Instance Variables :You can use this to differentiate between instance variables and parameters with the same name.

    class Test {
        int x;
        Test(int x) {
            this.x = x;  // 'this' is used to differentiate the instance variable from the constructor parameter
        }
    }

    2. Invoking the Current Class Constructor :this() can be used to call another constructor in the same class.

    class Test {
        int x;
        Test() {
            this(10);  // Calls the parameterized constructor
        }
        Test(int x) {
            this.x = x;
        }
    }

    3. Returning the Current Class Instance: this can be used to return the current instance of the class.

    class Test {
        int x;
        Test(int x) {
            this.x = x;
        }
        Test getObject() {
            return this;  // Returns the current object
        }
    }

    4. Passing the Current Class Instance as a Method Parameter: this can be passed as an argument to methods or constructors.

    class Test {
        void display(Test obj) {
            System.out.println("Object received: " + obj);
        }
        void sendThis() {
            display(this);  // Passes the current object as a parameter
        }
    }

    5. Invoking the Current Class Method:this can be used to call a method from within another method.

    class Test {
        void display() {
            this.show();  // Calls the show method
        }
        void show() {
            System.out.println("Inside show method");
        }
    }
  • Arrays

    Arrays in Java

    Operators are the foundation of any programming language, enabling us to perform various computations, logic evaluations, and functions. Java offers several types of operators that are categorized based on the functions they perform.

    Array Initialization in C

    Initialization in C involves assigning initial values to variables. When an array is declared or memory is allocated for it, its elements initially contain garbage values. To give these elements meaningful values, we initialize the array. There are several methods for initializing an array in C.

    1. Array Initialization During Declaration :This method involves initializing the array at the time of its declaration. We use an initializer list, which is a set of values enclosed in curly braces { } and separated by commas, to assign values to the array.

    Examples:

    int[] numbers; // Declaring an integer array

    This declares an integer array numbers. However, it does not allocate memory yet.

    2. Creating an Array :Allocate memory using the new keyword:

    numbers = new int[5]; // Creates an array of 5 integers

    This initializes the array to hold 5 integers, with default values of 0.

    3. Accessing Array Elements :You can access and modify array elements using their index (starting from 0):

    numbers[0] = 10; // Setting the first element to 10
    int firstElement = numbers[0]; // Accessing the first element

    4. Changing Array Elements :To update an element, assign a new value:

    numbers[0] = 20; // Changes the first element to 20

    5. Array Length :You can get the length of an array using its length property:

    int length = numbers.length; // Returns 5 for this array

    6. Array Initialization :You can declare and initialize arrays in different ways:

    int[] numbers = new int[5]; // Declaration + memory allocation
    int[] otherNumbers = {1, 2, 3, 4, 5}; // Array literal initialization

    7. Iterating Through Arrays :You can loop through arrays using a for loop or for-each loop:

    for (int i = 0; i < numbers.length; i++) {
        System.out.println(numbers[i]);
    }
    
    for (int number : numbers) {
        System.out.println(number); // For-each loop
    }

    8. Multidimensional Arrays :Java supports multidimensional arrays, like 2D arrays (matrix):

    int[][] matrix = new int[3][3]; // 2D array
    matrix[0][0] = 1;

    Example:

    int[][] multiDimArray = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    }; // A 3x3 matrix

    9. Array of Objects ;Arrays can also hold objects of a class:

    Student[] students = new Student[3]; // Array of Student objects
    students[0] = new Student("John", 1);

    Output:

    num1 = 20
    num2 = 10
    The product = 200

    10. Passing Arrays to Methods :You can pass arrays to methods:

    public class MultiplicationExample {
        public static void main(String[] args) {
            int num1 = 20, num2 = 10;
            int product = num1 * num2;
            System.out.println("The product = " + product);
        }
    }

    Calling the method:

    printArray(numbers);

    11. Returning Arrays from Methods :A method can return an array:

    public static int[] getArray() {
        return new int[] {1, 2, 3};
    }

    12. Cloning Arrays :To clone an array (create a shallow copy):

    int[] cloneArray = numbers.clone();

    For multidimensional arrays, the cloning creates a shallow copy, meaning only the references are copied, not the actual elements.

    13. Handling Array Exceptions :If you try to access an element outside the array size, Java throws an ArrayIndexOutOfBoundsException:

    System.out.println(numbers[5]); // Throws an exception if the array has fewer elements

    Arrays class in Java

    The Arrays class in Java, part of the java.util package, provides several static methods to help developers perform operations on arrays in a more optimized and efficient manner. Here are some of the most common methods:

    Key Methods in Arrays Class:

    1. asList(): Converts an array to a fixed-size list.

    Integer[] arr = {1, 2, 3};
    List<Integer> list = Arrays.asList(arr);

    2. binarySearch(): Searches for a specific element using the binary search algorithm.

    int[] arr = {1, 2, 3, 4, 5};
    int index = Arrays.binarySearch(arr, 3); // Returns index of 3

    3. compare(): Compares two arrays lexicographically.

    int result = Arrays.compare(arr1, arr2);

    4. copyOf(): Copies the array to a new array of a specified length.

    int[] newArr = Arrays.copyOf(arr, 10); // Extends or truncates

    5. copyOfRange(): Copies a range of an array.

    int[] newArr = Arrays.copyOfRange(arr, 1, 3);

    6. deepEquals():Compares two arrays deeply for nested arrays.

    boolean isEqual = Arrays.deepEquals(arr1, arr2);

    7. deepHashCode(): Returns the hash code based on the “deep contents” of an array.

    int hashCode = Arrays.deepHashCode(arr);

    8. deepToString(): Converts an array to a string representation, handling nested arrays.

    String arrStr = Arrays.deepToString(arr);

    9. equals(): Compares two arrays for equality.

    boolean isEqual = Arrays.equals(arr1, arr2);

    10. fill(): Fills an array with a specified value.

    Arrays.fill(arr, 100); // Fills all elements with 100

    11. hashCode(): Returns the hash code for an array.

    int hash = Arrays.hashCode(arr);

    12. mismatch(): Finds the first index where two arrays differ.

    int mismatchIndex = Arrays.mismatch(arr1, arr2);

    13. parallelSort(): Sorts the array in parallel for faster sorting.

    Arrays.parallelSort(arr);

    14. sort(): Sorts an array in ascending order.

    Arrays.sort(arr);

    15. spliterator(): Returns a Spliterator covering the array.

    Spliterator<Integer> spliterator = Arrays.spliterator(arr);

    16. stream(): Converts an array into a sequential stream.

    Stream<int[]> stream = Arrays.stream(arr);

    17. toString(): Converts an array to a string representation.

    String arrStr = Arrays.toString(arr);
    Multidimensional Arrays

    Multidimensional arrays, simply put, are arrays made up of arrays. Data within these arrays are stored in a tabular structure, typically in row-major order.

    Syntax:

    data_type[1st_dimension][2nd_dimension][]...[Nth_dimension] array_name = new data_type[size1][size2]...[sizeN];

    Where:

    • data_type: The type of data stored in the array (e.g., intchar, etc.).
    • dimension: Specifies the dimension of the array (e.g., 2D, 3D).
    • array_name: The name assigned to the array.
    • size1, size2,…, sizeN: Dimensions defining the size of the array.

    Examples:

    Two-dimensional array:

    int[][] twoD_arr = new int[10][20];

    The total number of elements a multidimensional array can store is the product of its dimensions. For example, an array int[][] x = new int[10][20] can hold a total of 10×20=20010 \times 20 = 20010×20=200 elements. Similarly, int[][][] x = new int[5][10][20] can store 5×10×20=10005 \times 10 \times 20 = 10005×10×20=1000 elements.

    Applications of Multidimensional Arrays
    • Tabular Data: These arrays are often used to store tabular data, such as a student’s roll number and marks. A more complex use is to store images in 3D arrays.
    • Dynamic Programming: Many dynamic programming problems leverage multidimensional arrays to store intermediate states.
    • Algorithms: Common applications include matrix multiplication, adjacency matrices in graphs, and grid-based search problems.
    Two-Dimensional Array (2D Array)

    A 2D array is the most basic form of a multidimensional array and can be viewed as an array of one-dimensional arrays.

    Declaration (Indirect Method):

    Example:

    int[][] arr = new int[10][20];

    Initialization:

    arr[0][0] = 1;

    Examples:

    public class TwoDArrayExample {
        public static void main(String[] args) {
            int[][] arr = new int[10][20];
            arr[0][0] = 1;
    
            System.out.println("arr[0][0] = " + arr[0][0]);
        }
    }

    Output:

    arr[0][0] = 1

    Example: 2D array with default values in a 4×4 matrix:

    public class TwoDArray {
        public static void main(String[] args) {
            int rows = 4;
            int columns = 4;
            int[][] array = new int[rows][columns];
    
            int value = 1;
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < columns; j++) {
                    array[i][j] = value++;
                }
            }
    
            System.out.println("The 2D array is:");
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < columns; j++) {
                    System.out.print(array[i][j] + " ");
                }
                System.out.println();
            }
        }
    }

    Output:

    The 2D array is:
    1 2 3 4
    5 6 7 8
    9 10 11 12
    13 14 15 16

    Declaration (Direct Method):

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

    Output:

    After adding: 8

    Example:

    public class DirectTwoDArray {
        public static void main(String[] args) {
            int[][] arr = { { 1, 2 }, { 3, 4 } };
    
            for (int i = 0; i < 2; i++) {
                for (int j = 0; j < 2; j++) {
                    System.out.println("arr[" + i + "][" + j + "] = " + arr[i][j]);
                }
            }
        }
    }

    Output:

    arr[0][0] = 1
    arr[0][1] = 2
    arr[1][0] = 3
    arr[1][1] = 4

    Accessing Elements:

    To access elements in a 2D array, the syntax is:

    array[row_index][column_index];

    For example:

    arr[0][0] = 1;
    Three-Dimensional Array (3D Array)

    A 3D array can be seen as an array of 2D arrays.

    Declaration (Indirect Method):

    int[][][] arr = new int[10][20][30];

    Initialization:

    arr[0][0][0] = 1;

    Different Ways To Declare And Initialize 2-D Array

    A multi-dimensional array is an array with more than one dimension, commonly used as 2D or 3D arrays. Essentially, a multi-dimensional array is an array of arrays. A typical example of a 2D array is a chessboard, which consists of a grid of 64 square boxes arranged in 8 rows and 8 columns. Similarly, a 2D array can be visualized as a grid, where each element is identified by a row and column number. Accessing elements in a 2D array is similar to working with an Excel sheet, using row and column indices.

    Two-dimensional arrays (2D arrays) are useful for implementing structures like games (Tic-Tac-Toe, Chess) or storing image pixels.

    Declaring a 2D Array in Java

    A 2D array can be declared in two common ways:

    data_type array_name[][];   // OR
    data_type[][] array_name;
    • data_type: This defines the type of elements the array will store, such as intStringboolean, etc. Java, being statically typed, requires the data type to be declared.
    • array_name: This is the name you assign to the array.

    Example: Declaring Different Types of 2D Arrays

    data_type[][] array_Name = new data_type[no_of_rows][no_of_columns];

    Here, no_of_rows and no_of_columns define the number of rows and columns in the array. The total number of elements in a 2D array is calculated by multiplying the number of rows by the number of columns. For instance:

    int[][] myArray = new int[4][5];  // A 2D array with 4 rows and 5 columns

    By default, Java assigns default values to the array elements depending on the data type. Below are various approaches to initialize a 2D array.

    Different Approaches for Initializing 2D Arrays

    Approach 1: Declaration and Initialization

    In this approach, we declare the array and immediately initialize it.

    int[][][] arr = { {{1, 2}, {3, 4}}, {{5, 6}, {7, 8}} };

    Example:

    public class Example {
        public static void main(String[] args) {
            int[][] integer2DArray = new int[3][4];
            System.out.println("Default value of int array element: " + integer2DArray[0][0]);  // Outputs 0
    
            String[][] string2DArray = new String[2][3];
            System.out.println("Default value of String array element: " + string2DArray[0][0]);  // Outputs null
    
            boolean[][] boolean2DArray = new boolean[2][2];
            System.out.println("Default value of boolean array element: " + boolean2DArray[0][0]);  // Outputs false
        }
    }

    Approach 2: Initialization with Values

    Here, we initialize the array elements directly without specifying the number of rows and columns. Java automatically deduces the size.

    Example:

    public class Example {
        public static void main(String[] args) {
            String[][] courses = {
                { "Math", "Science", "History" },       // Row 1
                { "Biology", "Physics", "Chemistry" },  // Row 2
                { "English", "Art" }                    // Row 3
            };
    
            System.out.println("Course in first row: " + courses[0][0]);  // Math
            System.out.println("Course in second row: " + courses[1][2]);  // Chemistry
            System.out.println("Course in third row: " + courses[2][1]);  // Art
        }
    }

    Approach 3: Initializing Each Element Separately

    This method allows you to initialize elements individually.

    Example:

    public class Example {
        public static void main(String[] args) {
            int[][] marks = new int[2][2];
            marks[0][0] = 85;
            marks[0][1] = 90;
            marks[1][0] = 78;
            marks[1][1] = 88;
    
            System.out.println("marks[0][0] = " + marks[0][0]);
            System.out.println("marks[1][1] = " + marks[1][1]);
        }
    }

    Approach 4: Using Loops for Initialization

    When dealing with large arrays, initializing elements manually can be cumbersome. A more efficient approach is using loops.

    Example:

    public class Example {
        public static void main(String[] args) {
            int rows = 4, columns = 3;
            int[][] array = new int[rows][columns];
    
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < columns; j++) {
                    array[i][j] = i + j;
                }
            }
    
            // Printing array elements
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < columns; j++) {
                    System.out.print(array[i][j] + " ");
                }
                System.out.println();
            }
        }
    }

    Approach 5: Jagged Arrays (Different Number of Columns per Row)

    A jagged array allows rows to have different numbers of columns.

    Example:

    public class Example {
        public static void main(String[] args) {
            int[][] jaggedArray = new int[2][];
            jaggedArray[0] = new int[3];  // First row has 3 columns
            jaggedArray[1] = new int[2];  // Second row has 2 columns
    
            jaggedArray[0][0] = 1;
            jaggedArray[0][1] = 2;
            jaggedArray[0][2] = 3;
            jaggedArray[1][0] = 4;
            jaggedArray[1][1] = 5;
    
            // Printing the jagged array
            for (int i = 0; i < jaggedArray.length; i++) {
                for (int j = 0; j < jaggedArray[i].length; j++) {
                    System.out.print(jaggedArray[i][j] + " ");
                }
                System.out.println();
            }
        }
    }

    Jagged Arrays in Java

    A jagged array is a type of multidimensional array where the number of columns varies from row to row. This makes the jagged array more flexible compared to a rectangular 2D array. Here’s a simplified representation of a jagged array in memory:

    Row 1: [ ]
    Row 2: [   ]
    Row 3: [         ]
    ...

    Declaring and Initializing a Jagged Array

    Syntax:

    data_type array_name[][] = new data_type[n][];
    array_name[0] = new data_type[n1];  // n1 = no. of columns in row-1
    array_name[1] = new data_type[n2];  // n2 = no. of columns in row-2
    ...

    Alternative Ways to Initialize a Jagged Array:

    int[][] arr = new int[][] {
        new int[] {10, 20, 30, 40},
        new int[] {50, 60, 70, 80, 90, 100},
        new int[] {110, 120}
    };

    or simply:

    int[][] arr = {
        {10, 20, 30, 40},
        {50, 60, 70, 80, 90, 100},
        {110, 120}
    };

    Example 1: Jagged Array with Uneven Rows

    public class Main {
        public static void main(String[] args) {
            // Declare a 2D array with 2 rows
            int[][] jaggedArray = new int[2][];
    
            // First row has 3 columns
            jaggedArray[0] = new int[3];
    
            // Second row has 2 columns
            jaggedArray[1] = new int[2];
    
            // Initializing the array
            int value = 0;
            for (int i = 0; i < jaggedArray.length; i++) {
                for (int j = 0; j < jaggedArray[i].length; j++) {
                    jaggedArray[i][j] = value++;
                }
            }
    
            // Displaying the values of the jagged array
            System.out.println("Contents of the Jagged Array:");
            for (int i = 0; i < jaggedArray.length; i++) {
                for (int j = 0; j < jaggedArray[i].length; j++) {
                    System.out.print(jaggedArray[i][j] + " ");
                }
                System.out.println();
            }
        }
    }

    Output:

    Contents of the Jagged Array:
    0 1 2
    3 4

    Example 2: Creating a Jagged Array with Incremental Row Sizes

    In this example, the first row contains one element, the second row contains two elements, and so on.

    public class Main {
        public static void main(String[] args) {
            int rows = 5;
    
            // Declaring a jagged array with 5 rows
            int[][] jaggedArray = new int[rows][];
    
            // Creating the jagged array with varying columns per row
            for (int i = 0; i < jaggedArray.length; i++) {
                jaggedArray[i] = new int[i + 1];  // ith row has i+1 columns
            }
    
            // Initializing the array
            int value = 0;
            for (int i = 0; i < jaggedArray.length; i++) {
                for (int j = 0; j < jaggedArray[i].length; j++) {
                    jaggedArray[i][j] = value++;
                }
            }
    
            // Displaying the values of the jagged array
            System.out.println("Contents of the Jagged Array:");
            for (int i = 0; i < jaggedArray.length; i++) {
                for (int j = 0; j < jaggedArray[i].length; j++) {
                    System.out.print(jaggedArray[i][j] + " ");
                }
                System.out.println();
            }
        }
    }

    Output:

    Contents of the Jagged Array:
    0
    1 2
    3 4 5
    6 7 8 9
    10 11 12 13 14

    Final Arrays

    In Java, the final keyword prevents reassigning a reference to another object, but it doesn’t make the referenced object itself immutable. This is applicable to arrays as well. Once an array is declared as final, the reference to that array cannot be changed, but the elements within the array can be modified. This means we can change the state of the object (the contents of the array), but not the reference to the object itself.

    Illustration:

    // Java Program Demonstrating Final Arrays
    
    public class FinalArrayDemo {
        public static void main(String[] args) {
            final int[] arr = { 1, 2, 3, 4, 5 };
    
            // Modifying an element in the final array
            arr[3] = 10;
    
            // Displaying the updated array
            for (int i = 0; i < arr.length; i++) {
                System.out.println(arr[i]);
            }
        }
    }

    Output:

    1
    2
    3
    10
    5

    Implementation: Here’s another example showing how you can manipulate the contents of a final array:

    // Java Program Demonstrating Final Arrays
    
    public class FinalArrayExample {
        public static void main(String[] args) {
            final int[] arr = { 2, 4, 6, 8, 10 };
    
            // Modifying array elements
            for (int i = 0; i < arr.length; i++) {
                arr[i] = arr[i] * 2;
                System.out.println(arr[i]);
            }
        }
    }

    Output:

    4
    8
    12
    16
    20

    Explanation:

    Even though the array arr is declared as final, you can still change its individual elements. The final keyword simply means that the reference to the array cannot be reassigned to another array. This behavior is consistent with how object references work in Java: when you declare an object as final, the reference can’t be changed, but the object’s internal state can still be modified.

    Example 1:

    // Program demonstrating modification of object fields
    
    class Test {
        int value = 100;
    
        public static void main(String[] args) {
            final Test obj = new Test();
            obj.value = 200;  // Modifying the internal state
            System.out.println(obj.value);
        }
    }

    Output:

    200

    util.Arrays vs reflect.Array in Java with Examples

    The Array class in the java.lang.reflect package is part of Java Reflection and provides static methods to dynamically create and access Java arrays. This class is final, meaning it cannot be instantiated or extended. All its methods are static and accessed via the class name itself. On the other hand, the Arrays class in the java.util package is part of the Java Collection Framework and contains various utility methods for manipulating arrays, such as sorting and searching.

    While both classes deal with arrays, the key difference is their usage. The Array class ensures type safety and is mainly used for reflective access to arrays. The Arrays class, on the other hand, offers a variety of methods for array operations.

    Here is a breakdown of the differences between Array and Arrays:

    FactorArrayArrays
    PackageBelongs to java.lang.reflectBelongs to java.util
    Class Hierarchyjava.lang.Object → java.lang.reflect.Arrayjava.lang.Object → java.util.Arrays
    ImmutabilityImmutable and finalNot immutable
    Declarationpublic final class Array extends Objectpublic class Arrays extends Object
    UsageProvides methods to create and access arrays reflectively, ensuring type safetyContains utility methods for array manipulation (sorting, searching, etc.)

    Example: Illustrating the Usage of Array vs Arrays

    import java.lang.reflect.Array;
    import java.util.Arrays;
    
    public class ArrayVsArraysExample {
    
        public static void main(String[] args) {
    
            // Creating an integer array of size 5
            int[] intArray = new int[5];
    
            // Adding an element to the array using Array class
            Array.setInt(intArray, 0, 42);
    
            // Printing the array using Arrays class
            System.out.println(Arrays.toString(intArray));
        }
    }

    Output:

    [42, 0, 0, 0, 0]

  • Operators in Java

    Operators

    Operators are the foundation of any programming language, enabling us to perform various computations, logic evaluations, and functions. Java offers several types of operators that are categorized based on the functions they perform. These include:

    • Arithmetic Operators
    • Unary Operators
    • Assignment Operator
    • Relational Operators
    • Logical Operators
    • Ternary Operator
    • Bitwise Operators
    • Shift Operators

    This section focuses on Arithmetic Operators, which allow us to carry out mathematical operations on basic data types. These operators can either be unary (applied to one operand) or binary (applied to two operands). Let’s dive into each arithmetic operator in Java.

    Arithmetic Operators in Java

    1. Addition (+)

    This binary operator is used to add two operands.

    Syntax:

    num1 + num2

    Example:

    int num1 = 10, num2 = 20;
    int sum = num1 + num2;

    Output:

    num1 = 10
    num2 = 20
    The sum = 30

    Java Code Example:

    public class AdditionExample {
        public static void main(String[] args) {
            int num1 = 10, num2 = 20;
            int sum = num1 + num2;
            System.out.println("The sum = " + sum);
        }
    }

    2. Subtraction (-)

    This binary operator is used to subtract the second operand from the first operand.

    Syntax:

    num1 - num2

    Example:

    int num1 = 20, num2 = 10;
    int difference = num1 - num2;

    Output:

    num1 = 20
    num2 = 10
    The difference = 10

    Java Code Example:

    public class SubtractionExample {
        public static void main(String[] args) {
            int num1 = 20, num2 = 10;
            int difference = num1 - num2;
            System.out.println("The difference = " + difference);
        }
    }

    3. Multiplication (*)

    This binary operator multiplies two operands.

    Syntax:

    num1 * num2

    Example:

    int num1 = 20, num2 = 10;
    int product = num1 * num2;

    Output:

    num1 = 20
    num2 = 10
    The product = 200

    Java Code Example:

    public class MultiplicationExample {
        public static void main(String[] args) {
            int num1 = 20, num2 = 10;
            int product = num1 * num2;
            System.out.println("The product = " + product);
        }
    }

    4. Division (/)

    This binary operator divides the first operand (dividend) by the second operand (divisor) and returns the quotient.

    Syntax:

    num1 / num2

    Example:

    int num1 = 20, num2 = 10;
    int quotient = num1 / num2;

    Output:

    num1 = 20
    num2 = 10
    The quotient = 2

    Java Code Example:

    public class DivisionExample {
        public static void main(String[] args) {
            int num1 = 20, num2 = 10;
            int quotient = num1 / num2;
            System.out.println("The quotient = " + quotient);
        }
    }

    5. Modulus (%)

    This binary operator returns the remainder when the first operand (dividend) is divided by the second operand (divisor).

    Syntax:

    num1 % num2

    Example:

    int num1 = 5, num2 = 2;
    int remainder = num1 % num2;

    Output:

    num1 = 5
    num2 = 2
    The remainder = 1

    Java Code Example:

    public class ModulusExample {
        public static void main(String[] args) {
            int num1 = 5, num2 = 2;
            int remainder = num1 % num2;
            System.out.println("The remainder = " + remainder);
        }
    }

    Example Program Implementing All Arithmetic Operators

    The following Java program takes user input and performs addition, subtraction, multiplication, and division:

    import java.util.Scanner;
    
    public class ArithmeticOperators {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
    
            System.out.print("Enter the first number: ");
            double num1 = sc.nextDouble();
    
            System.out.print("Enter the second number: ");
            double num2 = sc.nextDouble();
    
            double sum = num1 + num2;
            double difference = num1 - num2;
            double product = num1 * num2;
            double quotient = num1 / num2;
    
            System.out.println("The sum of the two numbers is: " + sum);
            System.out.println("The difference of the two numbers is: " + difference);
            System.out.println("The product of the two numbers is: " + product);
            System.out.println("The quotient of the two numbers is: " + quotient);
        }
    }

    Input:

    Enter the first number: 20
    Enter the second number: 10

    Output:

    The sum of the two numbers is: 30.0
    The difference of the two numbers is: 10.0
    The product of the two numbers is: 200.0
    The quotient of the two numbers is: 2.0

    Java Unary Operator

    Unary operators in Java operate on a single operand. These operators are used for tasks such as incrementing, decrementing, and negating values. Let’s explore the various unary operators and how they function.

    Operator 1: Unary Minus (-)

    This operator is used to reverse the sign of a value, converting a positive value into a negative one, or vice versa.

    Syntax:

    -(operand)

    Example:

    // Java Program demonstrating Unary Minus
    class UnaryMinus {
        public static void main(String[] args) {
            int num = 20;
            System.out.println("Number = " + num);
    
            // Applying unary minus
            num = -num;
            System.out.println("After applying unary minus = " + num);
        }
    }

    Output:

    Number = 20
    After applying unary minus = -20

    Operator 2: NOT Operator (!)

    This operator negates a boolean expression, flipping true to false and vice versa.

    Syntax:

    !(operand)

    Example:

    // Java Program demonstrating NOT Operator
    class NotOperator {
        public static void main(String[] args) {
            boolean flag = true;
            int a = 10, b = 1;
    
            System.out.println("Initial flag: " + flag);
            System.out.println("a = " + a + ", b = " + b);
    
            // Applying NOT operator
            System.out.println("Negated flag: " + !flag);
            System.out.println("!(a < b) = " + !(a < b));
            System.out.println("!(a > b) = " + !(a > b));
        }
    }

    Output:

    Initial flag: true
    a = 10, b = 1
    Negated flag: false
    !(a < b) = true
    !(a > b) = false

    Operator 3: Increment (++)

    The increment operator increases the value of an operand by one. It can be used in two forms:

    • Post-Increment: The operand is incremented after its current value is used.
    • Pre-Increment: The operand is incremented before its current value is used.

    Example:

    int num = 5;
    System.out.println("Post-increment: " + (num++)); // Uses current value, then increments
    System.out.println("Pre-increment: " + (++num));  // Increments, then uses new value

    Operator 4: Decrement (--)

    Similar to the increment operator, the decrement operator decreases the operand’s value by one and can also be used in two forms:

    • Post-Decrement: Decrements the operand after its current value is used.
    • Pre-Decrement: Decrements the operand before its current value is used.

    Example:

    int num = 5;
    System.out.println("Post-decrement: " + (num--)); // Uses current value, then decrements
    System.out.println("Pre-decrement: " + (--num));  // Decrements, then uses new value

    Operator 5: Bitwise Complement (~)

    This operator inverts all the bits of the operand. It converts 0s to 1s and 1s to 0s.

    Syntax:

    ~(operand)

    Example:

    // Java Program demonstrating Bitwise Complement Operator
    class BitwiseComplement {
        public static void main(String[] args) {
            int num = 5;
            System.out.println("Number: " + num);
    
            // Applying bitwise complement
            System.out.println("Bitwise complement: " + ~num);
        }
    }

    Output:

    Number: 5
    Bitwise complement: -6

    Example Program Demonstrating All Unary Operators

    Here’s a comprehensive program that demonstrates the use of all basic unary operators with user input:

    import java.util.Scanner;
    
    public class UnaryOperatorsExample {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
    
            System.out.print("Enter a number: ");
            int num = sc.nextInt();
    
            // Unary plus
            int result = +num;
            System.out.println("Unary plus result: " + result);
    
            // Unary minus
            result = -num;
            System.out.println("Unary minus result: " + result);
    
            // Pre-increment
            result = ++num;
            System.out.println("Pre-increment result: " + result);
    
            // Post-increment
            result = num++;
            System.out.println("Post-increment result: " + result);
    
            // Pre-decrement
            result = --num;
            System.out.println("Pre-decrement result: " + result);
    
            // Post-decrement
            result = num--;
            System.out.println("Post-decrement result: " + result);
    
            sc.close();
        }
    }

    Input:

    Enter a number: 10

    Output:

    Unary plus result: 10
    Unary minus result: -10
    Pre-increment result: 11
    Post-increment result: 11
    Pre-decrement result: 11
    Post-decrement result: 10

    Java Assignment Operators

    In Java, operators form the basic building blocks of programming, allowing developers to perform various types of calculations, comparisons, and logical operations. Assignment operators specifically help assign values to variables. They have right-to-left associativity, meaning the value on the right side of the operator is assigned to the variable on the left. Let’s dive into the different types of assignment operators used in Java.

    Types of Assignment Operators:

    1. Simple Assignment Operator (=): The simple assignment operator is used to assign a value to a variable. The operand on the right-hand side must match the data type of the variable on the left-hand side.

    Syntax:

    variable = value;

    Example:

    public class AssignmentOperatorExample {
        public static void main(String[] args) {
            int num = 15;
            String name = "Java";
    
            System.out.println("num is assigned: " + num);
            System.out.println("name is assigned: " + name);
        }
    }

    Output:

    num is assigned: 15
    name is assigned: Java

    2. Add and Assign (+=): The compound += operator adds the value on the right to the value of the variable on the left and then assigns the result back to the variable on the left.

    Syntax:

    variable += value;

    Example:

    public class AddAssignExample {
        public static void main(String[] args) {
            int num1 = 5, num2 = 3;
            num1 += num2;
            System.out.println("After adding: " + num1);
        }
    }

    Output:

    After adding: 8

    Note: This operator performs implicit type conversion if necessary. For instance, adding a double to an int using += will result in automatic narrowing conversion.

    3. Subtract and Assign (-=): The compound -= operator subtracts the value on the right from the value on the left and then assigns the result back to the variable on the left.

    Syntax:

    variable -= value;

    Example:

    public class SubtractAssignExample {
        public static void main(String[] args) {
            int num1 = 10, num2 = 4;
            num1 -= num2;
            System.out.println("After subtracting: " + num1);
        }
    }

    Output:

    After subtracting: 6

    4. Multiply and Assign (*=): This operator multiplies the current value of the variable by the value on the right and assigns the result back to the variable on the left.

    Syntax:

    variable *= value;
    a *= 2;  // Equivalent to a = a * 2

    Example:

    public class MultiplyAssignExample {
        public static void main(String[] args) {
            int num1 = 4, num2 = 3;
            num1 *= num2;
            System.out.println("After multiplying: " + num1);
        }
    }

    Output:

    After multiplying: 12

    5. Divide and Assign (/=): This operator divides the current value of the variable by the value on the right and assigns the result back to the variable on the left.

    Syntax:

    variable /= value;

    Example:

    public class DivideAssignExample {
        public static void main(String[] args) {
            int num1 = 20, num2 = 4;
            num1 /= num2;
            System.out.println("After dividing: " + num1);
        }
    }

    Output:

    After dividing: 5

    6. Modulus and Assign (%=): This operator performs division and assigns the remainder back to the variable on the left.

    Syntax:

    variable %= value;

    Example:

    public class ModulusAssignExample {
        public static void main(String[] args) {
            int num1 = 17, num2 = 5;
            num1 %= num2;
            System.out.println("After modulus: " + num1);
        }
    }

    Output:

    After modulus: 2

    Java Relational Operators

    Relational operators in Java are binary operators used to compare two operands and return a boolean value indicating the result of the comparison. These operators are often used in control flow statements such as loops and conditionals.

    General Syntax:

    variable1 relation_operator variable2

    1. Equal to (==)

    The == operator checks if two operands are equal. If they are, it returns true; otherwise, it returns false.

    Syntax:

    var1 == var2

    Example:

    int var1 = 5, var2 = 10, var3 = 5;
    
    System.out.println("var1 == var2: " + (var1 == var2));  // false
    System.out.println("var1 == var3: " + (var1 == var3));  // true

    Output:

    var1 == var2: false
    var1 == var3: true

    2. Not Equal to (!=)

    The != operator checks if two operands are not equal. It returns true if the operands are unequal, and false if they are equal.

    Syntax:

    var1 != var2

    Loop Variables (Block Scope)

    Variables declared inside a loop have scope limited to the loop. They cannot be accessed outside the loop.

    Example:

    int var1 = 5, var2 = 10, var3 = 5;
    
    System.out.println("var1 != var2: " + (var1 != var2));  // true
    System.out.println("var1 != var3: " + (var1 != var3));  // false

    Output:

    var1 != var2: true
    var1 != var3: false

    3. Greater than (>)

    The > operator checks if the left operand is greater than the right operand. If true, it returns true; otherwise, false.

    Syntax:

    var1 > var2

    Example:

    int var1 = 30, var2 = 20;
    
    System.out.println("var1 > var2: " + (var1 > var2));  // true

    Output:

    var1 > var2: true

    4. Less than (<)

    The < operator checks if the left operand is less than the right operand.

    Syntax:

    var1 < var2

    Example:

    int var1 = 10, var2 = 20;
    
    System.out.println("var1 < var2: " + (var1 < var2));  // true

    Output:

    var1 < var2: true

    5. Greater than or equal to (>=)

    The >= operator checks if the left operand is greater than or equal to the right operand.

    Syntax:

    var1 >= var2

    Example:

    int var1 = 20, var2 = 20;
    
    System.out.println("var1 >= var2: " + (var1 >= var2));  // true

    Output:

    var1 >= var2: true

    6. Less than or equal to (<=)

    The <= operator checks if the left operand is less than or equal to the right operand.

    Syntax:

    var1 <= var2

    Example:

    int var1 = 10, var2 = 10;
    
    System.out.println("var1 <= var2: " + (var1 <= var2));  // true

    Output:

    var1 <= var2: true

    Java Logical Operators

    Logical operators are used to perform logical operations such as “AND,” “OR,” and “NOT,” similar to the functions of AND and OR gates in digital electronics. They combine two or more conditions or reverse the outcome of a given condition. An important point to remember is that in the case of the AND operator, the second condition is not evaluated if the first is false, while in the case of the OR operator, the second condition is not evaluated if the first is true. This feature is known as short-circuiting. Logical operators are commonly used in decision-making when multiple conditions need to be checked.

    Logical Operators in Java

    Let’s go through an example with some common logical operators in Java. Assume we have the following variables:

    Syntax:

    int a = 10;
    int b = 20;
    int c = 30;

    AND Operator (&&):

    This operator returns true if both conditions are true. Otherwise, it returns false.

    Example:

    • Condition 1: c > a (true)
    • Condition 2: c > b (true)

    Since both conditions are true, the result will be true.

    Example:

    if (c > a && c > b) {
        System.out.println("Both conditions are true");
    } else {
        System.out.println("At least one condition is false");
    }

    Output:

    Both conditions are true

    OR Operator (||):

    This operator returns true if at least one of the conditions is true. If both conditions are false, the result is false.

    Example:

    • Condition 1: c > a (true)
    • Condition 2: c < b (false)

    Since one of the conditions is true, the result will be true.

    if (c > a || c < b) {
        System.out.println("At least one condition is true");
    } else {
        System.out.println("Both conditions are false");
    }

    Output:

    At least one condition is true

    NOT Operator (!):

    This unary operator returns the opposite of the condition. If the condition is true, it returns false, and vice versa.

    Example:

    • Condition: c > a (true)
    • Applying NOT: !(c > a) (false)
    System.out.println(!(c > a));  // false
    System.out.println(!(c < a));  // true

    Output:

    false
    true

    Short-Circuiting in AND Operator:

    In cases where the first condition of an AND operation is false, the second condition will not be evaluated.

    Example:

    int a = 10, b = 20, c = 15;
    
    if ((a > c) && (++b > c)) {
        System.out.println("Condition met");
    }
    System.out.println("Value of b: " + b);

    Output:

    Value of b: 20

    Short-Circuiting in AND Operator:

    In cases where the first condition of an AND operation is false, the second condition will not be evaluated.

    Example:

    int a = 10, b = 20, c = 15;
    
    if ((a > c) && (++b > c)) {
        System.out.println("Condition met");
    }
    System.out.println("Value of b: " + b);

    Output:

    Value of b: 20

    Short-Circuiting in OR Operator:

    In cases where the first condition of an OR operation is true, the second condition will not be evaluated.

    Example:

    int a = 10, b = 20, c = 15;
    
    if ((a < c) || (++b < c)) {
        System.out.println("Condition met");
    }
    System.out.println("Value of b: " + b);

    Output:

    Condition met
    Value of b: 20

    Boolean Values and Logical Operators:

    Logical operators can also be applied directly to boolean values. For example:

    Example:

    boolean a = true;
    boolean b = false;
    
    System.out.println("a && b: " + (a && b)); // false
    System.out.println("a || b: " + (a || b)); // true
    System.out.println("!a: " + !a);           // false
    System.out.println("!b: " + !b);           // true

    Output:

    a && b: false
    a || b: true
    !a: false
    !b: true

    Java Ternary Operator

    The ternary operator is the only operator in Java that takes three operands. It serves as a concise alternative to the traditional if-else statement, allowing conditional logic in a single line. Although it operates similarly to if-else, it helps to reduce code size and improve readability.

    Syntax:

    variable = (condition) ? expression1 : expression2;

    This syntax translates to:

    • If condition is trueexpression1 is executed.
    • If condition is falseexpression2 is executed.

    Here’s the equivalent structure using an if-else statement:

    if(condition) {
        variable = expression1;
    } else {
        variable = expression2;
    }

    Example:

    int num1 = 10;
    int num2 = 20;
    int result = (num1 > num2) ? (num1 + num2) : (num1 - num2);

    Since num1 < num2, the second expression is executed, so:

    Syntax:

    result = num1 - num2 = -10;

    Example:

    class TernaryExample {
        public static void main(String[] args) {
            int n1 = 5, n2 = 10, max;
    
            System.out.println("First number: " + n1);
            System.out.println("Second number: " + n2);
    
            // Using ternary operator to find the maximum
            max = (n1 > n2) ? n1 : n2;
    
            System.out.println("The maximum number is: " + max);
        }
    }

    Output:

    First number: 5
    Second number: 10
    The maximum number is: 10

    Bitwise operators

    Bitwise operators are used to manipulate individual bits of a number. They are particularly useful when optimizing performance in certain cases, as they directly operate on the binary representation of numbers. Bitwise operators can be used with any integral type (e.g., charshortint). They are often employed when performing operations on Binary Indexed Trees (BITs).

    Here are the common bitwise operators in Java:

    1. Bitwise OR (|)

    The Bitwise OR operator is represented by the symbol |. It compares the corresponding bits of two operands, and if either of the bits is 1, the result is 1; otherwise, the result is 0.
    Example:

    a = 6 = 0110 (In Binary)
    b = 9 = 1001 (In Binary)
    
    Bitwise OR Operation:
      0110
    | 1001
    _________
      1111  = 15 (In decimal)

    2. Bitwise AND (&)

    The Bitwise AND operator is represented by &. It compares the corresponding bits of two operands, and if both bits are 1, the result is 1; otherwise, the result is 0.
    Example:

    a = 6 = 0110 (In Binary)
    b = 9 = 1001 (In Binary)
    
    Bitwise AND Operation:
      0110
    & 1001
    _________
      0000  = 0 (In decimal)

    3. Bitwise XOR (^)

    The Bitwise XOR operator is represented by ^. It compares the corresponding bits of two operands, and if the bits are different, the result is 1; otherwise, the result is 0.
    Example:

    a = 6 = 0110 (In Binary)
    b = 9 = 1001 (In Binary)
    
    Bitwise XOR Operation:
      0110
    ^ 1001
    _________
      1111  = 15 (In decimal)

    4. Bitwise Complement (~)

    The Bitwise NOT (complement) operator is represented by ~. It inverts all the bits of the operand, changing every 1 to 0 and every 0 to 1.
    Example:

    a = 6 = 0110 (In Binary)
    
    Bitwise NOT Operation:
    ~ 0110
    _________
      1001  = -7 (In decimal, as it returns the two's complement)

    Code Example:

    import java.util.Scanner;
    
    public class BitwiseOperators {
        public static void main(String[] args) {
            Scanner input = new Scanner(System.in);
    
            System.out.print("Enter first number: ");
            int num1 = input.nextInt();
    
            System.out.print("Enter second number: ");
            int num2 = input.nextInt();
    
            System.out.println("Bitwise AND: " + (num1 & num2));
            System.out.println("Bitwise OR: " + (num1 | num2));
            System.out.println("Bitwise XOR: " + (num1 ^ num2));
            System.out.println("Bitwise NOT: " + (~num1));
            System.out.println("Bitwise Left Shift: " + (num1 << 2));
            System.out.println("Bitwise Right Shift: " + (num1 >> 2));
            System.out.println("Bitwise Unsigned Right Shift: " + (num1 >>> 2));
    
            input.close();
        }
    }

    Input:

    Enter first number: 4
    Enter second number: 10

    Output:

    Bitwise AND: 0
    Bitwise OR: 14
    Bitwise XOR: 14
    Bitwise NOT: -5
    Bitwise Left Shift: 16
    Bitwise Right Shift: 1
    Bitwise Unsigned Right Shift: 1
  • Flow Control in java

    Decision Making in Java (if, if-else, switch, break, continue, jump).

    Decision-making in programming is like making decisions in real life. When coding, we often need to execute certain blocks of code only if specific conditions are met.

    Java provides several control statements to control the flow of a program based on conditions. These statements direct the flow of execution, allowing it to branch and continue based on the program’s state.

    Java’s Selection Statements:
    • if
    • if-else
    • nested-if
    • if-else-if
    • switch-case
    • jump (break, continue, return)

    1. if Statement:

    The simplest decision-making statement. It is used to decide whether a certain block of code will be executed or not, depending on whether the condition is true.

    Syntax:

    if(condition) {
       // Statements to execute if the condition is true
    }

    Here, the condition evaluates to either true or false. If true, the block of statements inside the if block is executed.

    If we do not use curly braces ({}), only the first statement after the if will be considered part of the if block.

    Example:

    class IfDemo {
        public static void main(String args[]) {
            int i = 10;
    
            if (i < 15)
                System.out.println("Inside If block"); // Part of if block
            System.out.println("10 is less than 15");  // Outside of if block
            System.out.println("I am not in if block");
        }
    }

    Output:

    Inside If block
    10 is less than 15
    I am not in if block

    2. if-else Statement:

    The if statement alone can execute code when a condition is true. But what if we want to execute something else when the condition is false? This is where the else statement comes in.

    Syntax:

    if (condition) {
        // Executes if condition is true
    } else {
        // Executes if condition is false
    }

    Example:

    class IfElseDemo {
        public static void main(String args[]) {
            int i = 10;
    
            if (i < 15)
                System.out.println("i is smaller than 15");
            else
                System.out.println("i is greater than 15");
        }
    }

    Output:

    i is smaller than 15

    3. nested-if Statement:

    nested-if is an if statement inside another if. This allows you to perform more complex decision-making scenarios.

    Syntax:

    if (condition1) {
       if (condition2) {
          // Executes when both conditions are true
       }
    }

    Example:

    class NestedIfDemo {
        public static void main(String args[]) {
            int i = 10;
    
            if (i == 10 || i < 15) {
                if (i < 15)
                    System.out.println("i is smaller than 15");
    
                if (i < 12)
                    System.out.println("i is smaller than 12 too");
            } else {
                System.out.println("i is greater than 15");
            }
        }
    }

    Output:

    i is smaller than 15
    i is smaller than 12 too

    4. if-else-if Ladder:

    The if-else-if ladder allows you to test multiple conditions. The conditions are evaluated from top to bottom, and as soon as one of them is true, its corresponding block is executed, and the rest are skipped.

    Syntax:

    if (condition1) {
        // Executes if condition1 is true
    } else if (condition2) {
        // Executes if condition2 is true
    } else {
        // Executes if none of the above conditions are true
    }

    Example:

    class IfElseIfDemo {
        public static void main(String args[]) {
            int i = 20;
    
            if (i == 10)
                System.out.println("i is 10");
            else if (i == 15)
                System.out.println("i is 15");
            else if (i == 20)
                System.out.println("i is 20");
            else
                System.out.println("i is not present");
        }
    }

    Output:

    i is 20

    5. switch-case Statement:

    The switch statement allows you to branch execution based on the value of an expression. It’s like having multiple if-else blocks for a single expression.

    Syntax:

    switch (expression) {
      case value1:
        // Code to be executed if expression equals value1
        break;
      case value2:
        // Code to be executed if expression equals value2
        break;
      // Add more cases as needed
      default:
        // Code to be executed if none of the cases match
    }

    Example:

    class SwitchDemo {
        public static void main(String[] args) {
            int num = 20;
            switch (num) {
              case 5:
                System.out.println("It is 5");
                break;
              case 10:
                System.out.println("It is 10");
                break;
              case 15:
                System.out.println("It is 15");
                break;
              case 20:
                System.out.println("It is 20");
                break;
              default:
                System.out.println("Not present");
            }
        }
    }

    Output:

    It is 20

    6. Jump Statements:

    Java has three main jump statements:

    • break: Used to exit a loop or a switch statement.
    • continue: Skips the remaining code in the current iteration of a loop and starts the next iteration.
    • return: Exits from the current method and passes control back to the caller.

    Example of continue:

    class ContinueDemo {
        public static void main(String args[]) {
            for (int i = 0; i < 10; i++) {
                if (i % 2 == 0)
                    continue;
    
                System.out.print(i + " ");
            }
        }
    }

    Output:

    1 3 5 7 9

    If Statements with Examples

    Decision Making in Java allows you to execute a specific block of code based on certain conditions. It’s a key part of controlling the flow of a program, similar to how we make decisions in real life.

    Java’s if Statement:

    The if statement is the simplest form of decision-making. It allows you to execute a block of code if a specific condition evaluates to true.

    Syntax:

    if (condition) {
       // Statements to execute if the condition is true
    }

    How It Works:

    1. Control enters the if block.
    2. The condition is evaluated.

    • If the condition is true, the block of code within the if statement is executed.
    • If the condition is false, the control moves to the next part of the program.

    Example:

    class IfDemo {
        public static void main(String args[]) {
            int i = 10;
    
            if (i < 15)
                System.out.println("10 is less than 15");
    
            System.out.println("Outside if-block");
        }
    }

    Output:

    10 is less than 15
    Outside if-block
    Java if-else Statement

    Decision-making in Java allows for the execution of certain blocks of code based on specific conditions. The if statement tells us that if a condition is true, it will execute a block of statements; otherwise, it won’t. By combining if and else, you can handle both outcomes—what happens when the condition is true and when it’s false.

    Understanding if-else in Java:

    The if-else statement is used to control the flow of your program by executing code based on whether a condition is true or false.

    Syntax of if-else Statement:

    if (condition) {
        // Executes this block if the condition is true
    } else {
        // Executes this block if the condition is false
    }

    Example Program:

    // Java program to demonstrate if-else statement
    
    class IfElseDemo {
        public static void main(String args[]) {
            int i = 20;
    
            if (i < 15) {
                System.out.println("i is smaller than 15");
            } else {
                System.out.println("i is greater than 15");
            }
    
            System.out.println("Outside if-else block");
        }
    }

    Output:

    i is greater than 15
    Outside if-else block

    Java if-else-if Ladder

    Decision-making in Java allows us to write condition-based statements that execute particular blocks of code depending on specific conditions. The if-else-if ladder is used to handle multiple conditions by checking each one sequentially. As soon as a condition evaluates to true, its corresponding block is executed, and the rest of the ladder is ignored. If none of the conditions are true, the final else block is executed.

    Syntax:

    if (condition) {
        // Executes if condition 1 is true
    } else if (condition) {
        // Executes if condition 2 is true
    }
    .
    .
    else {
        // Executes if no conditions are true
    }

    Working of if-else-if Ladder:

    1. Control enters the if block.
    2. The first condition is evaluated:If true, the associated block is executed, and the rest of the ladder is skipped.
    If false, the flow moves to the next condition.
    3. This process repeats for each condition in the ladder.
    4. If none of the conditions are met, the else block is executed.

    Example :

    // Java program to demonstrate if-else-if ladder
    
    class IfElseIfLadderDemo {
        public static void main(String[] args) {
            // Initializing expression
            int i = 20;
    
            // Condition 1
            if (i == 10) {
                System.out.println("i is 10");
            }
            // Condition 2
            else if (i == 15) {
                System.out.println("i is 15");
            }
            // Condition 3
            else if (i == 20) {
                System.out.println("i is 20");
            }
            // Default case
            else {
                System.out.println("i is not present");
            }
    
            System.out.println("Outside if-else-if ladder");
        }
    }

    Output:

    i is 20
    Outside if-else-if ladder

    Loops in Java

    Looping is an essential feature in programming that allows a set of instructions to be executed repeatedly as long as a specified condition evaluates to true. In Java, loops are implemented in three different ways, each with a unique syntax and method of checking conditions.

    while Loop

    Looping is an essential feature in programming that allows a set of instructions to be executed repeatedly as long as a specified condition evaluates to true. In Java, loops are implemented in three different ways, each with a unique syntax and method of checking conditions.

    while (boolean condition) {
        // Loop body
    }

    Example:

    public class WhileLoopExample {
        public static void main(String[] args) {
            int i = 0;
            while (i <= 10) {
                System.out.println(i);
                i++;
            }
        }
    }

    Output:

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    Working of while loop:

    • The loop starts by checking the boolean condition.
    • If the condition is true, the loop body is executed.
    • After each execution, the condition is rechecked before the next iteration.
    • The loop terminates when the condition evaluates to false.

    This is an entry control loop, meaning the condition is checked before the loop body is executed.

    Java do-while loop with Examples

    Loops are a fundamental concept in programming, used when we need to execute a block of code repeatedly. The do-while loop is an example of an Exit Control Loop in Java, meaning the loop body will always execute at least once, as the condition is checked after the loop body has been executed, unlike in for and while loops where the condition is checked beforehand.

    Syntax of do-while Loop:

    do {
        // Loop body
        update_expression;
    } while (test_expression);

    Output:

    Print statement

    For-each loop in Java

    The for-each loop in Java, introduced in Java 5, is a simplified way to traverse arrays and collections. It provides a cleaner and more readable syntax when you need to access each element of an array or a collection like ArrayList.

    Key Features of the For-Each Loop:
    • It begins with the keyword for, similar to a regular for-loop.
    • Instead of initializing and maintaining a loop counter, you declare a variable (of the same type as the array elements) that iterates through each element.
    • The loop body uses this variable directly, without needing to reference an array index.

    The for-each loop is particularly useful for iterating through arrays or classes from the Java Collection Framework, such as ArrayList.

    Syntax:

    for (type var : array) {
        // Statements using var
    }

    Example: Simple Array Traversal using For-Each Loop

    public class Easy {
        public static void main(String[] args) {
            // Array declaration
            int[] ar = {10, 50, 60, 80, 90};
    
            // For-each loop to iterate through array
            for (int element : ar) {
                System.out.print(element + " ");
            }
        }
    }

    Output:

    10 50 60 80 90

    Equivalent Syntax with Regular For Loop:

    for (int i = 0; i < ar.length; i++) {
        int element = ar[i];
        // Statements using element
    }

    Example: Finding the Highest Score using For-Each Loop

    // Java program to illustrate for-each loop
    public class ForEachExample {
        public static void main(String[] args) {
            int[] marks = {125, 132, 95, 116, 110};
    
            int highestMarks = maximum(marks);
            System.out.println("The highest score is " + highestMarks);
        }
    
        public static int maximum(int[] numbers) {
            int maxSoFar = numbers[0];
    
            // For-each loop to find the maximum value
            for (int num : numbers) {
                if (num > maxSoFar) {
                    maxSoFar = num;
                }
            }
            return maxSoFar;
        }
    }

    Output:

    The highest score is 132
    Limitations of the For-Each Loop

    1. Modifying Array Elements: For-each loops don’t allow modification of array elements. Changing the loop variable does not affect the array:

    for (int num : marks) {
        num = num * 2;  // Only changes num, not the actual array element
    }

    2. No Access to  Index: You can’t get the index of the current element using a for-each loop:

    for (int num : numbers) {
        if (num == target) {
            return ???;  // No index available
        }
    }

    3. Only Forward Iteration: For-each loops can only iterate forward, making it impossible to iterate in reverse order:

    for (int i = numbers.length - 1; i >= 0; i--) {
        System.out.println(numbers[i]);  // Cannot be done using for-each
    }

    4. Handling Multiple Decision Statements: For-each loops struggle to process multiple decision-making conditions:

    for (int i = 0; i < numbers.length; i++) {
        if (numbers[i] == arr[i]) {
            // Hard to convert this to a for-each loop
        }
    }

    Break Statement in Java

    The break statement in Java is a control statement used to terminate the loop prematurely. Once the break statement is encountered, the loop terminates immediately, and the control moves to the next statement following the loop.

    Syntax:

    break;
    When to Use the Break Statement:

    1. When the exact number of iterations is not known in advance.
    2. When you want to exit the loop based on some condition during iteration.

    Use Cases for Break:

    1. Terminating a sequence in a switch statement.
    2. Exiting a loop.
    3. Serving as a structured alternative to goto.

    Example: Break in a Switch Statement

    // Example of using break statement in switch
    public class SwitchBreakExample {
        public static void main(String[] args) {
            // Assigning n an integer value
            int n = 1;
    
            // Passing n to switch
            // The case matching n will execute and terminate due to break
            switch (n) {
                case 1:
                    System.out.println("Hello World");
                    break;
                case 2:
                    System.out.println("Second Case");
                    break;
                default:
                    System.out.println("Default Case");
            }
        }
    }

    Output:

    Hello World
    Using Break to Exit a Loop

    With the break statement, you can force the loop to terminate immediately, skipping the remaining iterations. It’s commonly used when you want to stop the loop based on a specific condition.

    Example: Breaking out of a Loop

    // Java program to illustrate using break to exit a loop
    public class BreakLoopDemo {
        public static void main(String[] args) {
            // The loop runs from 0 to 9
            for (int i = 0; i < 10; i++) {
                // Terminate loop when i equals 5
                if (i == 5)
                    break;
    
                System.out.println("i: " + i);
            }
            System.out.println("Loop complete.");
        }
    }

    Output:

    i: 0
    i: 1
    i: 2
    i: 3
    i: 4
    Loop complete.
    • Time Complexity: O(1)
    • Auxiliary Space: O(1)

    Using Break as a Form of Goto

    Java doesn’t support the goto statement because it allows jumping in an unstructured manner, leading to complex and unreadable code. Instead, Java uses labels to identify a block of code. You can use the break statement to jump out of a labeled block.

    Syntax:

    label:
    {
      // Code block
      statement1;
      statement2;
      // Break can be used to exit the block
      break label;
    }

    Example: Break with a Label

    // Java program to illustrate using break with label
    public class BreakLabelDemo {
        public static void main(String[] args) {
            boolean condition = true;
    
            // Label first
            first: {
                second: {
                    third: {
                        // Before break
                        System.out.println("Before the break statement");
    
                        // If condition is true, break out of the second block
                        if (condition)
                            break second;
    
                        System.out.println("This won't execute.");
                    }
                    System.out.println("This won't execute.");
                }
    
                // After exiting the second block
                System.out.println("This is after second block.");
            }
        }
    }

    Output:

    Before the break statement
    This is after second block

    return keyword in Java

    In Java, the scope of a variable refers to the region in the code where the variable is accessible. Java has lexical (static) scoping, meaning the scope of a variable is determined at compile time and is not dependent on the function call stack. The scope rules in Java can be broadly classified into three categories based on where the variables are declared.

    1. Member Variables (Class-Level Scope)
    2. Local Variables (Method-Level Scope)
    3. Block Variables (Loop or Block-Level Scope)

    1. Member Variables (Class-Level Scope)

    Member variables are declared inside a class but outside any method, constructor, or block. They can be accessed anywhere within the class and can have different access levels (e.g., publicprivateprotected, or default). Access to member variables outside the class depends on the access modifier used.

    Example:

    public class Test {
        // Member variables
        int a;                  // Default access modifier
        private String b;        // Private member variable
        char c;                  // Default access modifier
    
        void method1() {
            // Member variables can be accessed here
            System.out.println(a);
            System.out.println(b);
        }
    
        int method2() {
            return a;
        }
    }
    • Public: Accessible within the class, in subclasses, and outside the class.
    • Protected: Accessible within the class and in subclasses but not outside the package.
    • Default (no modifier): Accessible within the same package but not outside it.
    • Private: Only accessible within the class.


    2. Local Variables (Method-Level Scope)

    Local variables are declared inside a method or constructor and are only accessible within that method. They must be initialized before use, and their lifetime is limited to the method’s execution. Once the method finishes, local variables are destroyed.

    Example:

    public class Test {
        void method1() {
            // Local variable
            int x = 10;
            System.out.println(x);  // Accessible inside the method
        }
    
        public static void main(String[] args) {
            Test t = new Test();
            t.method1();
        }
    }

    Local variables do not retain their values once the method completes, and they are recreated each time the method is invoked.

    3. Block Variables (Loop or Block-Level Scope)

    Variables declared inside a block (within curly braces {}) are only accessible within that block. Once the block is exited, these variables are out of scope and cannot be accessed. This applies to variables declared inside loops or conditionals.

    Example of Block-Level Scope:

    public class Test {
        public static void main(String[] args) {
            {
                int x = 10;  // x is only accessible inside this block
                System.out.println(x);
            }
    
            // Uncommenting the following line will cause an error
            // System.out.println(x);  // x is out of scope here
        }
    }

    Loop Variables (Block Scope)

    Variables declared inside a loop have scope limited to the loop. They cannot be accessed outside the loop.

    Example:

    class Test {
        public static void main(String[] args) {
            for (int x = 0; x < 4; x++) {
                System.out.println(x);  // x is accessible inside the loop
            }
    
            // Uncommenting the following line will result in an error
            // System.out.println(x);  // x is out of scope here
        }
    }

    If you need to access a loop variable outside the loop, declare it before the loop:

    class Test {
        public static void main(String[] args) {
            int x;
            for (x = 0; x < 4; x++) {
                System.out.println(x);
            }
            System.out.println(x);  // x is accessible outside the loop
        }
    }

    Output:

    0
    1
    2
    3
    4

    Loop Variable Scope with Overlapping Names

    In Java, you cannot declare two variables with the same name within the same scope. However, in languages like C++, it’s possible to have the same variable name in nested scopes, which is not allowed in Java.

    Incorrect Example in Java (compilation error):

    class Test {
        public static void main(String[] args) {
            int a = 5;
            for (int a = 0; a < 5; a++) {  // Error: Variable 'a' is already defined
                System.out.println(a);
            }
        }
    }

    Output:

    Error: variable 'a' is already defined

    Valid Example with Loop Variable Declaration After Loop

    To avoid such errors, you can declare a variable outside the loop and use it after the loop finishes.

    Example:

    class Test {
        public static void main(String[] args) {
            for (int i = 1; i <= 10; i++) {
                System.out.println(i);  // Loop variable i
            }
    
            int i = 20;  // Declare i after the loop
            System.out.println(i);  // Access new i outside the loop
        }
    }

    Output:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    20
  • Input/Output in Java

    How to Take Input from User in Java

    Java provides multiple ways to take input from users through its I/O (Input/Output) package. These streams facilitate the reading of characters, data types, and objects from input devices such as keyboards or files. The most common ways to capture user input in Java are through the BufferedReader class and the Scanner class.

    Methods to Take Input in Java

    There are two primary ways to get input from the user or a file in Java:

    1. Using BufferedReader Class
    2. Using Scanner Class

    1. Using BufferedReader Class to Take Input in Java

    The BufferedReader class is part of Java’s I/O package and is used for reading streams of characters. The readLine() method of this class reads input as a string. InputStreamReader is often used in conjunction with BufferedReader to convert byte streams into character streams, enabling character-based input reading.

    Note: BufferedReader can throw checked exceptions, which need to be handled using try-catch blocks or by declaring them with throws.

    Example of Taking Input Using BufferedReader:

    import java.io.*;
    
    public class InputExample {
        public static void main(String[] args) throws IOException {
            // Creating a BufferedReader object
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    
            // Reading string input
            System.out.println("Enter a string:");
            String str = reader.readLine();
    
            // Reading integer input
            System.out.println("Enter an integer:");
            int num = Integer.parseInt(reader.readLine());
    
            // Output the values
            System.out.println("Entered String: " + str);
            System.out.println("Entered Integer: " + num);
        }
    }

    Output:

    Enter a string:
    John Doe
    Enter an integer:
    123
    Entered String: John Doe
    Entered Integer: 123

    Alternative Example of BufferedReader Input:

    import java.io.*;
    
    public class Example {
        public static void main(String[] args) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            try {
                System.out.println("Enter your name:");
                String name = reader.readLine();  // Reading string input
                System.out.println("Name: " + name);
            } catch (IOException e) {
                System.out.println("An error occurred: " + e.getMessage());
            }
        }
    }

    Sample Output:

    Enter your name:
    John
    Name: John

    2. Using Scanner Class for Taking Input in Java

    The Scanner class is an advanced and user-friendly input class that is part of Java’s util package. It simplifies input handling by providing various methods like nextInt()nextFloat()next(), and nextLine(). These methods allow you to directly capture input without explicit parsing.

    Advantages of Scanner:

    • Easier to use than BufferedReader.
    • Supports multiple data types (integers, floats, strings, etc.) directly.
    • No need for throws declarations as no checked exceptions are thrown.
    • Provides methods to read formatted input easily.

    Example of Taking Input Using Scanner:

    import java.util.Scanner;
    
    class Main {
        public static void main(String[] args) {
            // Create Scanner object
            Scanner sc = new Scanner(System.in);
    
            // Input a String (single word)
            String word = sc.next();
            System.out.println("Entered word: " + word);
    
            // Input a String (full sentence)
            sc.nextLine();  // Clear the buffer
            String sentence = sc.nextLine();
            System.out.println("Entered sentence: " + sentence);
    
            // Input an Integer
            int num = sc.nextInt();
            System.out.println("Entered Integer: " + num);
    
            // Input a floating-point number
            float f = sc.nextFloat();
            System.out.println("Entered Float: " + f);
        }
    }

    Output:

    John
    Hello World
    123
    45.67
    Entered word: John
    Entered sentence: Hello World
    Entered Integer: 123
    Entered Float: 45.67

    Another Example:

    import java.util.Scanner;
    
    class Main {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
    
            System.out.println("Enter your name:");
            String name = sc.nextLine();
    
            System.out.println("Enter your roll number:");
            int rollNo = sc.nextInt();
    
            System.out.println("Enter your marks:");
            float marks = sc.nextFloat();
    
            // Output the entered data
            System.out.println("Name: " + name);
            System.out.println("Roll Number: " + rollNo);
            System.out.println("Marks: " + marks);
        }
    }

    Output:

    Enter your name:
    Alice
    Enter your roll number:
    10
    Enter your marks:
    89.5
    Name: Alice
    Roll Number: 10
    Marks: 89.5
    Differences Between BufferedReader and Scanner

    1. BufferedReader is a basic way to read input, typically used for reading streams of characters. It is faster than Scanner as it doesn’t perform any post-processing or parsing.
    2. Scanner is more user-friendly for simple input, especially when you need to read different data types. However, Scanner is slower because it performs input parsing internally (like nextInt(), nextFloat()).
    3. BufferedReader allows you to specify the size of the input stream to be read, making it more flexible when handling large input.
    4. BufferedReader is synchronized, making it suitable for multi-threaded applications, while Scanner is not.
    5. Scanner is typically preferred for smaller inputs due to its simplicity, while BufferedReader is favored for larger inputs due to better performance.

    Scanner Class in Java

    The Scanner class in Java is part of the java.util package and is used to obtain input from the user, including primitive data types like intdouble, and boolean, as well as strings.

    While the Scanner class is the simplest way to read input, it’s not the most efficient when performance is critical, such as in competitive programming, due to additional overhead.

    Input Types in Java Scanner

    The Scanner class can take various types of input from the user. Some of the commonly used methods for extracting data from an input stream are listed below:

    MethodDescription
    nextBoolean()Reads a Boolean value
    nextByte()Reads a Byte value
    nextDouble()Reads a Double value
    nextFloat()Reads a Float value
    nextInt()Reads an Integer value
    nextLine()Reads an entire line as a String
    nextLong()Reads a Long value
    nextShort()Reads a Short value

    Example 1: Reading Data of Various Types

    Below is a sample code that demonstrates how to read different types of data using the Scanner class.

    // Java program to read data of various types using Scanner class.
    import java.util.Scanner;
    
    public class ScannerDemo1 {
        public static void main(String[] args) {
            // Creating a Scanner object for input
            Scanner sc = new Scanner(System.in);
    
            // Reading a String input
            String name = sc.nextLine();
    
            // Reading a Character input
            char gender = sc.next().charAt(0);
    
            // Reading numerical data
            int age = sc.nextInt();
            long mobileNo = sc.nextLong();
            double cgpa = sc.nextDouble();
    
            // Outputting the values to check input
            System.out.println("Name: " + name);
            System.out.println("Gender: " + gender);
            System.out.println("Age: " + age);
            System.out.println("Mobile Number: " + mobileNo);
            System.out.println("CGPA: " + cgpa);
        }
    }

    Input:

    Alice
    F
    25
    9876543210
    8.9

    Output:

    Name: Alice
    Gender: F
    Age: 25
    Mobile Number: 9876543210
    CGPA: 8.9
    Checking Input Type with hasNextXYZ() Methods

    In some cases, we need to verify the type of the next input or whether the input has ended (e.g., when encountering the EOF marker). The hasNextXYZ() methods allow us to check if the next token is of the desired type. Here, XYZ represents the data type of interest.

    For instance, we can check for an integer using hasNextInt() or a string using hasNextLine(). Similarly, to check for a single character, we can use hasNext().charAt(0).

    Example 2: Calculating the Mean of Numbers

    // Java program to read values using Scanner and calculate their mean.
    import java.util.Scanner;
    
    public class ScannerDemo2 {
        public static void main(String[] args) {
            // Initialize Scanner object
            Scanner sc = new Scanner(System.in);
    
            // Initialize sum and count variables
            int sum = 0, count = 0;
    
            // Read integers until non-integer input is encountered
            while (sc.hasNextInt()) {
                int num = sc.nextInt();
                sum += num;
                count++;
            }
    
            // Calculate and print the mean
            if (count > 0) {
                int mean = sum / count;
                System.out.println("Mean: " + mean);
            } else {
                System.out.println("No integers were input. Mean cannot be calculated.");
            }
        }
    }

    Input:

    1 2 3 4 5

    Output:

    Mean: 3

    Key Points About the Scanner Class

    1. Creating a Scanner Object: To create a Scanner object, we usually pass the predefined System.in object, which represents standard input. We can also pass a File object to read input from a file.
    2. Reading Numerical Values: For each primitive data type, there is a corresponding nextXYZ() method to read its value. For example, nextShort() reads a short value, and nextInt() reads an int.
    3. Reading Strings: To read strings, we use nextLine(). This method reads the entire line until a newline character is encountered.
    4. Reading a Single Character: To read a single character, we can use next().charAt(0). The next() method reads the next token (a word or symbol), and charAt(0) returns the first character of that token.
    5. How Scanner Reads Input: The Scanner class reads an entire line and divides it into tokens. Each token represents a meaningful piece of the input. For example, if the input string is “Hello world”, the Scanner object will treat “Hello” and “world” as two separate tokens, and we can iterate over these tokens using its various methods.

    Ways to read input from console in Java

    In Java, there are several ways to read input from the user in the command-line environment. Here are five methods:

    1. Using BufferedReader Class

    The BufferedReader class is the traditional way to take input, introduced in JDK 1.0. It wraps the standard input stream (System.in) in an InputStreamReader, which is further wrapped in a BufferedReader for efficient reading of input.

    Key Points:

    • Input is buffered for performance.
    • Syntax can be a bit more complex compared to newer methods.

    Example:

    // Java program to demonstrate BufferedReader
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    public class Test {
        public static void main(String[] args) throws IOException {
            // Using BufferedReader to get input
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    
            // Reading a string input
            String name = reader.readLine();
    
            // Printing the input
            System.out.println(name);
        }
    }

    Input:

    John

    Output:

    John
    2. Using Scanner Class

    The Scanner class, introduced in JDK 1.5, is one of the most commonly used methods to read input. It can parse primitive types and strings using regular expressions.

    Advantages:

    • Simplifies reading primitive types like intfloat, and more.
    • Regular expressions can be used to tokenize input.

    Example:

    import java.util.Scanner;
    
    public class InputExample {
    
        public static void main(String[] args) {
            // Create Scanner object
            Scanner scn = new Scanner(System.in);
    
            // Read a single word as a string
            System.out.println("Enter a word:");
            String word = scn.next();
    
            // Read a full line as a string
            System.out.println("Enter a sentence:");
            scn.nextLine(); // consume the leftover newline
            String sentence = scn.nextLine();
    
            // Read an integer
            System.out.println("Enter an integer:");
            int number = scn.nextInt();
    
            // Read a float value
            System.out.println("Enter a floating point number:");
            float decimal = scn.nextFloat();
    
            // Display the inputs
            System.out.println("Word: " + word);
            System.out.println("Sentence: " + sentence);
            System.out.println("Integer: "

    Input:

    Alice
    25
    4.5

    Output:

    You entered string: Alice
    You entered integer: 25
    You entered float: 4.5
    3. Using Console Class

    Introduced in JDK 1.6, the Console class is useful for reading input from the command line, especially for secure inputs like passwords (where input isn’t displayed). However, it may not work in non-interactive environments like IDEs.

    Advantages:

    • Can hide input (useful for password entry).
    • Supports formatted input and output.

    Example:

    // Java program to demonstrate Console class
    public class Sample {
        public static void main(String[] args) {
            // Using Console to get input
            String name = System.console().readLine();
            System.out.println("You entered string: " + name);
        }
    }

    Input:

    Michael

    Output:

    You entered string: Michael
    4. Using Command-Line Arguments

    This method uses the command-line arguments passed to the program at runtime. The arguments are stored as strings in the args[] array, and can be converted to other data types using methods like Integer.parseInt().

    Example:

    // Program to demonstrate command-line arguments
    class Hello {
        public static void main(String[] args) {
            // Check if arguments were passed
            if (args.length > 0) {
                System.out.println("The command line arguments are:");
                // Loop through the args array
                for (String val : args)
                    System.out.println(val);
            } else {
                System.out.println("No command line arguments found.");
            }
        }
    }

    Command Line Arguments:

    java Hello John Doe

    Output:

    The command line arguments are:
    John
    Doe
    5. Using DataInputStream Class

    The DataInputStream class allows reading primitive data types and strings from an input stream in a machine-independent way. This class is useful when working with binary input/output, and it was introduced in JDK 1.0.

    Key Points:

    • Reads binary data in a machine-independent format.
    • Often used with DataOutputStream for consistent data handling.

    Example:

    // Java program to demonstrate DataInputStream
    import java.io.*;
    
    public class Main {
        public static void main(String[] args) throws IOException {
            DataInputStream reader = new DataInputStream(System.in);
    
            // Reading an integer input
            System.out.print("Enter an integer: ");
            int inputInt = Integer.parseInt(reader.readLine());
    
            // Reading a string input
            System.out.print("Enter a string: ");
            String inputString = reader.readLine();
    
            // Output the input values
            System.out.println("You entered integer: " + inputInt);
            System.out.println("You entered string: " + inputString);
        }
    }

    Input:

    Enter an integer: 30
    Enter a string: Java Programming

    Output:

    You entered integer: 30
    You entered string: Java Programming

    Java System.out.println()

    The System.out.println() method is used in Java to print a message or value to the console. It is a commonly used function that prints any argument passed to it, followed by a new line.

    Breakdown of System.out.println()

    The statement can be split into three parts for better understanding:

    1. System: This is a final class from the java.lang package.
    2. out: A static member of the System class, which is an instance of the PrintStream class.
    3. println(): A method of the PrintStream class that prints the passed argument and adds a newline character at the end. It’s an enhanced version of print() that automatically moves the cursor to the next line.

    Syntax:

    System.out.println(parameter);

    Example of System.out.println()

    Example : Basic Output

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

    Output:

    Hello
    World
    Java Programming
    Java’s Other Standard Streams
    • System.in: The standard input stream used for reading input from the keyboard.
    • System.err: The standard error stream used to display error messages.

    Example:

    System.err.print("This is an error message");
    Overloads of println() Method

    Java supports method overloading, allowing multiple methods with the same name but different parameter types. PrintStream provides various overloads of the println() method for different data types.

    Example of Overloaded println() Methods

    public class Example {
        public static void main(String[] args) {
            int num = 15;
            char letter = 'A';
            String text = "Java";
            double decimal = 25.78;
            float floatNum = 10.5f;
            boolean flag = true;
    
            // Various overloaded versions of println()
            System.out.println(); // Prints an empty line
            System.out.println(num);
            System.out.println(letter);
            System.out.println(text);
            System.out.println(decimal);
            System.out.println(floatNum);
            System.out.println(flag);
        }
    }

    Output:

    15
    A
    Java
    25.78
    10.5
    true
    Difference Between System.out.print() and System.out.println()
    • System.out.print(): Prints the provided text or value on the console and keeps the cursor on the same line.
    • System.out.println(): Prints the provided text or value, and moves the cursor to the next line.

    Example:

    public class Example {
        public static void main(String[] args) {
            System.out.println("Using print():");
    
            // Using print()
            System.out.print("Hello ");
            System.out.print("World ");
            System.out.print("Java");
    
            System.out.println(); // Moving to the next line
            System.out.println("Using println():");
    
            // Using println()
            System.out.println("Hello");
            System.out.println("World");
            System.out.println("Java");
        }
    }

    Output:

    Using print():
    Hello World Java
    Using println():
    Hello
    World
    Java

    Formatted Output in Java using printf()

    In programming, it is often necessary to display output in a specific format. Java’s printf() method, similar to the C language’s printf, allows formatting of output using format specifiers. In this article, we’ll cover different ways to format output in Java using printf().

    Formatting Using printf()

    printf() uses format specifiers to format different data types. The commonly used data types for formatting are:

    1. Number Formatting
    2. Decimal Number Formatting
    3. Boolean Formatting
    4. Character Formatting
    5. String Formatting
    6. Date and Time Formatting

    1. Number Formatting

    For formatting integers (like intlong), the format specifier used is %d.

    Example:

    public class Example {
        public static void main(String[] args) {
            int number = 10000;
    
            // Format with commas separating thousands
            System.out.printf("%,d%n", number);
        }
    }

    Output:

    10,000

    2. Decimal Number Formatting

    To format decimal numbers, we use the %f specifier.

    Example:

    public class Example {
        public static void main(String[] args) {
            double pi = 3.14159265359;
    
            // Formatting decimal numbers
            System.out.printf("%f\n", pi);
            System.out.printf("%5.3f\n", pi);
            System.out.printf("%5.2f\n", pi);
        }
    }

    Output:

    3.141593
    3.142
     3.14

    3. Boolean Formatting

    Boolean values can be formatted using %b or %B.

    Example:

    public class Example {
        public static void main(String[] args) {
            boolean boolTrue = true;
            boolean boolFalse = false;
            Integer nullValue = null;
    
            System.out.printf("%b\n", boolTrue);  // true
            System.out.printf("%B\n", boolTrue);  // TRUE
            System.out.printf("%b\n", boolFalse); // false
            System.out.printf("%B\n", nullValue); // FALSE
        }
    }

    Output:

    true
    TRUE
    false
    FALSE

    4. Character Formatting

    Character formatting is done using the %c or %C specifiers.

    Example:

    public class Example {
        public static void main(String[] args) {
            char character = 'a';
    
            System.out.printf("%c\n", character); // a
            System.out.printf("%C\n", character); // A (uppercase)
        }
    }

    Output:

    a
    A

    5. String Formatting

    Strings are formatted using %s or %S.

    Example:

    public class Example {
        public static void main(String[] args) {
            String text = "java programming";
    
            System.out.printf("%s\n", text);  // java programming
            System.out.printf("%S\n", text);  // JAVA PROGRAMMING
        }
    }

    Output:

    java programming
    JAVA PROGRAMMING

    6. Date and Time Formatting

    Formatting date and time is more complex and requires specific knowledge of format specifiers such as %tT%tH%tM, etc.

    Example:

    import java.util.Date;
    
    public class Example {
        public static void main(String[] args) {
            Date currentTime = new Date();
    
            System.out.printf("Current Time: %tT\n", currentTime); // Full time format
            System.out.printf("Hours: %tH  Minutes: %tM Seconds: %tS\n",
                              currentTime, currentTime, currentTime); // Individual components
    
            // Time with AM/PM, milliseconds, nanoseconds, and time zone
            System.out.printf("%1$tH:%1$tM:%1$tS %1$tp %1$tL %1$tN %1$tz%n", currentTime);
        }
    }

    Output:

    Current Time: 11:32:36
    Hours: 11  Minutes: 32  Seconds: 36
    11:32:36 am 198 198000000 +0000
    Other Methods for Formatting

    1. Decimal Formatting using DecimalFormat

    DecimalFormat can be used to format numbers with various patterns.

    Example:

    import java.text.DecimalFormat;
    
    public class DecimalFormatting {
        public static void main(String[] args) {
            double num = 123.4567;
    
            // Formatting without fraction part
            DecimalFormat df = new DecimalFormat("####");
            System.out.println("Without fraction part: " + df.format(num));
    
            // Formatting to 2 decimal places
            df = new DecimalFormat("#.##");
            System.out.println("Formatted to 2 decimal places: " + df.format(num));
    
            // Formatting with appended zeroes
            df = new DecimalFormat("#.000000");
            System.out.println("With appended zeroes: " + df.format(num));
    
            // Formatting with leading zeroes
            df = new DecimalFormat("00000.00");
            System.out.println("With leading zeroes: " + df.format(num));
    
            // Formatting currency
            double income = 23456.789;
            df = new DecimalFormat("$###,###.##");
            System.out.println("Formatted income: " + df.format(income));
        }
    }

    Output:

    Without fraction part: 123
    Formatted to 2 decimal places: 123.46
    With appended zeroes: 123.456700
    With leading zeroes: 00123.46
    Formatted income: $23,456.79

    2. Formatting Dates using SimpleDateFormat

    SimpleDateFormat allows date formatting based on custom patterns.

    Example:

    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class DateFormatting {
        public static void main(String[] args) {
            // Formatting date to 'dd-MM-yyyy'
            SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
            String formattedDate = sdf.format(new Date());
            System.out.println("Formatted Date: " + formattedDate);
    
            // Parsing a string date
            String dateStr = "02/18/1995";
            sdf = new SimpleDateFormat("MM/dd/yyyy");
            try {
                Date date = sdf.parse(dateStr);
                System.out.println("Parsed Date: " + date);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    Output:

    Formatted Date: 03-10-2024
    Parsed Date: Sat Feb 18 00:00:00 UTC 1995

    3. Static Variables

    Static variables are shared across all instances of a class and are declared with the static keyword. Only one copy of a static variable exists, regardless of how many objects are created.

    // Example of static variables
    public class StaticVariableExample {
        public static String company = "TechCorp";  // Static variable
    
        public static void main(String[] args) {
            System.out.println("Company: " + StaticVariableExample.company);  // Accessing without object
        }
    }

    Output:

    Company: TechCorp

    Java Variables

    In Java, the scope of a variable refers to the region in the code where the variable is accessible. Java has lexical (static) scoping, meaning the scope of a variable is determined at compile time and is not dependent on the function call stack. The scope rules in Java can be broadly classified into three categories based on where the variables are declared.

    1. Member Variables (Class-Level Scope)
    2. Local Variables (Method-Level Scope)
    3. Block Variables (Loop or Block-Level Scope)

    1. Member Variables (Class-Level Scope)

    Member variables are declared inside a class but outside any method, constructor, or block. They can be accessed anywhere within the class and can have different access levels (e.g., publicprivateprotected, or default). Access to member variables outside the class depends on the access modifier used.

    Example:

    public class Test {
        // Member variables
        int a;                  // Default access modifier
        private String b;        // Private member variable
        char c;                  // Default access modifier
    
        void method1() {
            // Member variables can be accessed here
            System.out.println(a);
            System.out.println(b);
        }
    
        int method2() {
            return a;
        }
    }
    • Public: Accessible within the class, in subclasses, and outside the class.
    • Protected: Accessible within the class and in subclasses but not outside the package.
    • Default (no modifier): Accessible within the same package but not outside it.
    • Private: Only accessible within the class.

    2. Local Variables (Method-Level Scope)

    Local variables are declared inside a method or constructor and are only accessible within that method. They must be initialized before use, and their lifetime is limited to the method’s execution. Once the method finishes, local variables are destroyed.

    Example:

    public class Test {
        void method1() {
            // Local variable
            int x = 10;
            System.out.println(x);  // Accessible inside the method
        }
    
        public static void main(String[] args) {
            Test t = new Test();
            t.method1();
        }
    }

    Local variables do not retain their values once the method completes, and they are recreated each time the method is invoked.

    3. Block Variables (Loop or Block-Level Scope)

    Variables declared inside a block (within curly braces {}) are only accessible within that block. Once the block is exited, these variables are out of scope and cannot be accessed. This applies to variables declared inside loops or conditionals.

    Example of Block-Level Scope:

    public class Test {
        public static void main(String[] args) {
            {
                int x = 10;  // x is only accessible inside this block
                System.out.println(x);
            }
    
            // Uncommenting the following line will cause an error
            // System.out.println(x);  // x is out of scope here
        }
    }
    Loop Variables (Block Scope)

    Variables declared inside a loop have scope limited to the loop. They cannot be accessed outside the loop.

    Example:

    class Test {
        public static void main(String[] args) {
            for (int x = 0; x < 4; x++) {
                System.out.println(x);  // x is accessible inside the loop
            }
    
            // Uncommenting the following line will result in an error
            // System.out.println(x);  // x is out of scope here
        }
    }

    If you need to access a loop variable outside the loop, declare it before the loop:

    class Test {
        public static void main(String[] args) {
            int x;
            for (x = 0; x < 4; x++) {
                System.out.println(x);
            }
            System.out.println(x);  // x is accessible outside the loop
        }
    }

    Output:

    0
    1
    2
    3
    4
    Loop Variable Scope with Overlapping Names

    In Java, you cannot declare two variables with the same name within the same scope. However, in languages like C++, it’s possible to have the same variable name in nested scopes, which is not allowed in Java.

    Incorrect Example in Java (compilation error):

    class Test {
        public static void main(String[] args) {
            int a = 5;
            for (int a = 0; a < 5; a++) {  // Error: Variable 'a' is already defined
                System.out.println(a);
            }
        }
    }

    Output:

    Error: variable 'a' is already defined
    Valid Example with Loop Variable Declaration After Loop

    To avoid such errors, you can declare a variable outside the loop and use it after the loop finishes.

    Example:

    class Test {
        public static void main(String[] args) {
            for (int i = 1; i <= 10; i++) {
                System.out.println(i);  // Loop variable i
            }
    
            int i = 20;  // Declare i after the loop
            System.out.println(i);  // Access new i outside the loop
        }
    }

    Output:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    20
  • Basic Concepts of Java

    Java Basic Syntax

    Java is an object-oriented programming language, which means that it consists of objects that interact with each other through method calls to perform various tasks. Here’s an overview of some essential concepts in Java:

    Basic Terminologies in Java:

    1. Class:A class is a blueprint for objects. It defines a logical template that shares common properties and methods.

    2. ObjectAn object is an instance of a class. It represents an entity with a behavior and state.

    3. Method: A method defines the behavior of an object.

    4. Instance Variables: Each object has its own unique set of instance variables. These variables represent the state of an object, defined by the values assigned to them.

    Example: Compiling and Running a Java Program in a Console

    import java.util.*;
    public class Main {
        public static void main(String[] args) {
            System.out.println("Hello, World!");
        }
    }

    Output:

    Hello, World!

    5. Comments in Java: There are three types of comments in Java:

    • Single-line Comment:
    // This is a single-line comment
    • Multi-line Comment:
    /*
        This is a multi-line comment
    */
    • Documentation Comment:
    /** This is a documentation comment */

    6. Source File Name: The source file name should exactly match the public class name with the extension .java. If there is no public class, the file name can differ.

    Example:

    GFG.java  // valid
    gfg.java  // invalid if public class is GFG

    7.  Case Sensitivity:
    Java is case-sensitive, meaning identifiers like ABAbaB, and ab are considered different.

    Example:

    System.out.println("Hello");  // valid
    system.out.println("Hello");  // invalid

    8. Class Names: The first letter of a class name should be uppercase.If multiple words are used, each word should start with an uppercase letter.

    Example:

    class MyClass      // valid
    class 1Program     // invalid
    class My1Program   // valid
    class $Program     // valid but discouraged

    9. Main Method: The main() method is the entry point of every Java program:

    public static void main(String[] args) {
        // program logic
    }

    10. Method Names: Method names should start with a lowercase letter.If multiple words are used, the first letter of each word (after the first one) should be uppercase.
    Example:

    public void calculateSum() // valid

    11. Identifiers in Java: Identifiers are names given to classes, methods, variables, etc. They must follow certain rules:Can begin with a letter, underscore (_), or currency symbol. Subsequent characters can be letters, digits, or underscores. Java keywords cannot be used as identifiers. Legal identifiers: myVar, hello_world, $amount.
    Illegal identifiers: 1Var, -amount.

    12. White Spaces in Java: Blank lines, spaces, and comments are ignored by the Java compiler.

    13. Access Modifiers: Java provides access control to classes and methods through access modifiers:

    Access ModifierWithin ClassWithin PackageOutside Package by SubclassOutside Package
    PrivateYesNoNoNo
    DefaultYesYesNoNo
    ProtectedYesYesYesNo
    PublicYesYesYesYes

    14. Java Keywords:
    Keywords in Java have predefined meanings and cannot be used as identifiers. Examples include classinterfacevoidintpublicstatic, etc.

    Java Hello World Program

    Java is one of the most popular and widely-used programming languages, known for its speed, reliability, and security. Java applications can be found everywhere—from desktop software to web applications, scientific supercomputers to gaming consoles, and mobile phones to the Internet. In this guide, we’ll explore how to write a simple Java program.

    Steps to Implement a Java Program

    To implement a Java application, follow these key steps:

    • Creating the Program
    • Compiling the Program
    • Running the Program

    If you’re looking to dive deeper into Java and gain a strong understanding of the entire development process, consider enrolling in a structured Java programming course. These courses provide hands-on experience and cover everything from basic to advanced topics, allowing you to develop efficient and scalable applications.

    Example of a Simple Java Program

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

    Output:

    Hello, World!

    Primitive data type vs. Object data type in Java with Examples

    Java is known as a statically and strongly typed language because all data types (such as integers, characters, etc.) are predefined. Additionally, every constant or variable must be explicitly declared with a data type.

    Data Types in Java

    Java’s data types vary in size and in the values they can store. These types are categorized into two main groups:

    • Primitive Data Types: Includes booleancharintshortbytelongfloat, and double. For example, Boolean is a wrapper class for the primitive boolean type.
    • Non-Primitive Data Types (Reference types): Examples include StringArray, and more.

    Effectively understanding and using these data types is crucial for writing efficient and error-free Java code. If you’re aiming to become proficient in Java, it’s worth exploring detailed learning materials or taking courses to deepen your knowledge of data types and other core Java concepts.

    Primitive Data Types

    Primitive data types represent basic values and lack any special properties. Java supports eight primitive data types, as shown in the table below:

    TypeDescriptionDefaultSizeExample LiteralsRange
    1. booleanRepresents true or falsefalse8 bitstruefalseN/A
    byte8-bit signed integer08 bits(none)-128 to 127
    char16-bit Unicode character\u000016 bits'a''\u0041''β'0 to 65,535
    short16-bit signed integer016 bits(none)-32,768 to 32,767
    int32-bit signed integer032 bits-212-2,147,483,648 to 2,147,483,647
    long64-bit signed integer064 bits-2L0L1L-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
    float32-bit floating-point0.032 bits1.23e10f-1.23e-100fUp to 7 decimal digits
    double64-bit floating-point0.064 bits1.2345e300d1e1dUp to 16 decimal digits

    Below is a detailed breakdown of each data type:

    1. boolean Data Type: Represents logical values (true or false). Actual size depends on the virtual machine implementation.

    Syntax:

    boolean isAvailable;

    2. byte Data Type: An 8-bit signed integer, often used to save memory when working with large arrays.

    Syntax:

    byte age;

    3. short Data Type: A 16-bit signed integer. This is similar to byte, but used when larger ranges are required.

    Syntax:

    short population;

    4. int Data Type: A 32-bit signed integer and one of the most commonly used data types for numerical values.

    Syntax:

    int salary;

    5. long Data Type: A 64-bit signed integer, suitable for larger numerical values.

    Syntax:

    long distance;

    6. float Data Type: A 32-bit single-precision floating-point, typically used when saving memory is a priority for large floating-point arrays.

    Syntax:

    long distance;

    7. double Data Type: A 64-bit double-precision floating-point, typically the default choice for decimal values.

    Syntax

    double price;

    8. char Data Type: A 16-bit Unicode character. Java uses the Unicode system rather than ASCII, which allows it to represent a wide range of characters.

    Syntax:

    char initial;
    Non-Primitive (Reference) Data Types

    Non-Primitive data types store references to memory locations where data is stored. These include strings, arrays, and objects.

    1. String: A string is a sequence of characters. In Java, strings are objects, not primitive data types. They can be declared either with or without the new keyword.

    Example:

    String message = "Hello, World!";

    2. Class: A class in Java is a blueprint used to create objects. A class can contain fields (attributes) and methods to describe the behavior of the objects it represents.

    3. Object: An object is an instance of a class, representing real-world entities with states (fields) and behaviors (methods).

    4. Interface: An interface defines a set of methods that a class must implement. Interfaces provide a way to achieve abstraction in Java.

    5. Array: Arrays are used to store multiple values of the same type in a single variable. Java arrays are objects, and their size is fixed upon creation.

    Example:

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

    Java Identifiers

    In Java, identifiers are used to name various entities like classes, methods, variables, or labels. These names help in uniquely identifying different elements within a Java program.

    Example of Java Identifiers

    public class Test {
        public static void main(String[] args) {
            int a = 20;
        }
    }

    In the above code, we have 5 identifiers:

    • Test: This is the class name.
    • main: This is the method name.
    • String: This is a predefined class name.
    • args: This is a variable name.
    • a: This is also a variable name.
    Rules for Defining Java Identifiers

    There are specific rules for defining valid Java identifiers. Violating these rules will lead to a compile-time error. These rules are also similar in other programming languages like C and C++.

    1. Allowed Characters: Identifiers can contain letters (both uppercase and lowercase), digits ([0-9]), the dollar sign ($), and the underscore (_). Special characters such as @, %, or & are not allowed.
    Example: “my@name” is not a valid identifier because @ is not allowed.
    2. Starting Character: Identifiers cannot begin with a digit.
    Example: “123variable” is invalid.
    Case Sensitivity: Java is case-sensitive, so identifiers like MyVariable and myvariable are treated as distinct.
    3. Length: There is no restriction on the length of an identifier, but it’s recommended to keep it between 4 and 15 characters for readability.
    Reserved Words: Keywords or reserved words in Java cannot be used as identifiers.
    Example: int while = 20; is invalid because while is a reserved keyword in Java.

    Examples of Valid Identifiers:

    • MyVariable
    • MYVARIABLE
    • myvariable
    • x
    • i
    • x1
    • i1
    • _myvariable
    • $myvariable
    • sum_of_array
    • name123
    Examples of Invalid Identifiers:
    • My Variable // Contains a space.
    • 123name // Begins with a digit.
    • a+c // Contains a special character (+).
    • variable-2 // Contains a hyphen (-).
    • sum_&_difference // Contains an ampersand (&).
    Reserved Words in Java

    Programming languages reserve certain words to represent built-in functionalities. These are known as reserved words, and in Java, they fall into two categories: keywords (50) and literals (3). Keywords represent functions, while literals represent values. Identifiers are utilized by compilers during various phases of program analysis like lexical, syntax, and semantic analysis.

    Here are some reserved words in Java:

    KeywordKeywordKeywordKeyword
    abstractdefaultpackagesuper
    continuegotoprivatecase
    forpublicclassenum
    protectedtrybyteextends
    transientbooleandoubleinterface
    assertdoimplementsshort
    staticifstrictfpswitch
    throwsbreakelsecatch
    returnvoidlongfinally
    charfinalintsynchronized
    volatilefloatnativethis
    constthrowinstanceOfwhile

    These reserved words cannot be used as identifiers.

    int myVariable = 10;
    String $myString = "Hello";
    double _myDouble = 20.5;

    Operators in Java

    Java Operators: A Comprehensive Guide

    Java provides various operators that are categorized based on their functionality, making it easier to perform tasks like arithmetic operations, logical comparisons, and bitwise manipulations. In this guide, we will explore the different types of Java operators and their uses.

    What are Java Operators?

    Operators in Java are symbols that trigger specific operations on operands. They simplify complex tasks such as arithmetic operations, logical evaluations, and bitwise manipulations, enhancing the efficiency of the code.

    Types of Java Operators

    There are several types of operators in Java, each serving a unique purpose:

    1. Arithmetic Operators
    2. Unary Operators
    3. Assignment Operators
    4. Relational Operators
    5. Logical Operators
    6. Ternary Operator
    7. Bitwise Operators
    8. Shift Operators
    9. instanceof Operator

    1. Arithmetic Operators: Arithmetic operators perform basic mathematical operations such as addition, subtraction, multiplication, and division.

    • * : Multiplication
    • / : Division
    • % : Modulus (remainder)
    • + : Addition
    •  : Subtraction

    Example:

    public class ArithmeticOperators {
        public static void main(String[] args) {
            int x = 15;
            int y = 4;
    
            System.out.println("x + y = " + (x + y));
            System.out.println("x - y = " + (x - y));
            System.out.println("x * y = " + (x * y));
            System.out.println("x / y = " + (x / y));
            System.out.println("x % y = " + (x % y));
        }
    }

    Output:

    x + y = 19
    x - y = 11
    x * y = 60
    x / y = 3
    x % y = 3

    2. Unary Operators: Unary operators operate on a single operand. They are used to increment, decrement, or negate a value.

    • - : Unary minus (negates a value)
    • + : Unary plus (retains the positive value)
    • ++ : Increment operator (increases the value by 1)
    • -- : Decrement operator (decreases the value by 1)
    • ! : Logical NOT operator (inverts a boolean value)

    Example:

    public class UnaryOperators {
        public static void main(String[] args) {
            int a = 5;
            int b = 5;
    
            System.out.println("Post-Increment: " + (a++));
            System.out.println("Pre-Increment: " + (++a));
            System.out.println("Post-Decrement: " + (b--));
            System.out.println("Pre-Decrement: " + (--b));
        }
    }

    Output:

    Post-Increment: 5
    Pre-Increment: 7
    Post-Decrement: 5
    Pre-Decrement: 3

    3. Assignment Operators: Assignment operators assign values to variables. You can combine them with other operations to simplify code, such as += or -=.

    • = : Assign
    • +=-=*=/=%= : Compound assignment operators

    Example:

    public class AssignmentOperators {
        public static void main(String[] args) {
            int a = 10;
            a += 5;
            System.out.println("a after += 5: " + a);
            a *= 2;
            System.out.println("a after *= 2: " + a);
        }
    }

    Output:

    a after += 5: 15
    a after *= 2: 30

    4. Relational Operators: Relational operators compare two values and return a boolean result (true or false).

    • == : Equal to
    • != : Not equal to
    • < : Less than
    • > : Greater than
    • <= : Less than or equal to
    • >= : Greater than or equal to

    Example:

    public class RelationalOperators {
        public static void main(String[] args) {
            int a = 10, b = 5;
            System.out.println("a > b: " + (a > b));
            System.out.println("a == b: " + (a == b));
        }
    }

    5. Logical Operators: Logical operators are used to combine multiple boolean expressions.

    • && : Logical AND
    • || : Logical OR
    • ! : Logical NOT

    Example:

    public class LogicalOperators {
        public static void main(String[] args) {
            boolean x = true, y = false;
            System.out.println("x && y: " + (x && y));
            System.out.println("x || y: " + (x || y));
        }
    }

    Output:

    x && y: false
    x || y: true

    6. Ternary Operator: The ternary operator is a shorthand for an if-else statement.

    Syntax: condition ? value_if_true : value_if_false

    Example:

    public class TernaryOperator {
        public static void main(String[] args) {
            int a = 10, b = 20;
            int max = (a > b) ? a : b;
            System.out.println("Max value: " + max);
        }
    }

    Output:

    Max value: 20

    7. Bitwise Operators: Bitwise operators operate on binary representations of integers.

    • & : Bitwise AND
    • | : Bitwise OR
    • ^ : Bitwise XOR
    • ~ : Bitwise NOT

    Example:

    public class BitwiseOperators {
        public static void main(String[] args) {
            int a = 5; // 0101 in binary
            int b = 3; // 0011 in binary
            System.out.println("a & b: " + (a & b)); // AND operation
            System.out.println("a | b: " + (a | b)); // OR operation
        }
    }

    Output:

    a & b: 1
    a | b: 7

    8. Shift Operators: Shift operators shift bits left or right.

    • << : Left shift
    • >> : Right shift
    • >>> : Unsigned right shift

    Example:

    public class ShiftOperators {
        public static void main(String[] args) {
            int a = 8;
            System.out.println("Left Shift: " + (a << 1));
            System.out.println("Right Shift: " + (a >> 1));
        }
    }

    Output:

    Left Shift: 16
    Right Shift: 4

    9. instanceof Operator:The instanceof operator checks whether an object is an instance of a class or an interface.

    Example:

    class Animal {}
    class Dog extends Animal {}
    
    public class InstanceOfExample {
        public static void main(String[] args) {
            Animal a = new Dog();
            System.out.println(a instanceof Dog); // true
            System.out.println(a instanceof Animal); // true
        }
    }

    Output:

    true
    true

    Java Variables

    In Java, variables serve as containers to hold data values during the execution of a program. Each variable is assigned a specific data type that determines the kind and amount of data it can store. Essentially, a variable is a reference to a memory location that stores the data.

    Variables are crucial for storing and manipulating data in a program, and their values can be altered during program execution. Operations on the variable impact the referenced memory location. It’s important to note that all variables in Java must be declared before use.

    Declaring Variables in Java

    Variables in Java can be declared by specifying the data type followed by the variable name. The following two key elements must be considered during declaration:

    • datatype: The type of data that the variable can hold.
    • variable_name: The name given to the memory location.

    Example:

    // Example of variable declaration:
    float interestRate;  // Declaring a float variable
    int time = 10, speed = 20;  // Declaring and initializing integer variables
    char grade = 'A';  // Declaring and initializing a character variable
    Types of Variables in Java

    1. Local Variables
    2. Instance Variables
    3. Static Variables

    1. Local Variables: A local variable is declared within a method, constructor, or block, and its scope is limited to that block or method. Local variables must be initialized before use.

    // Example of local variables
    public class LocalVariableExample {
        public static void main(String[] args) {
            int x = 10;  // Local variable
            String message = "Hello, world!";  // Another local variable
    
            System.out.println("x = " + x);
            System.out.println("Message: " + message);
    
            if (x > 5) {
                String result = "x is greater than 5";  // Local variable within 'if' block
                System.out.println(result);
            }
    
            for (int i = 0; i < 3; i++) {
                String loopMessage = "Iteration " + i;  // Local variable within 'for' loop
                System.out.println(loopMessage);
            }
        }
    }

    Output:

    x = 10
    Message: Hello, world!
    x is greater than 5
    Iteration 0
    Iteration 1
    Iteration 2

    2. Instance Variables: Instance variables are non-static and are declared outside of any method, constructor, or block. These variables are created when an object is instantiated and are destroyed when the object is destroyed. They can have access specifiers and do not need to be initialized explicitly.

    // Example of instance variables
    public class InstanceVariableExample {
        public String name;
        public int age;
    
        public InstanceVariableExample(String name) {
            this.name = name;  // Initializing instance variable
        }
    
        public static void main(String[] args) {
            InstanceVariableExample person = new InstanceVariableExample("Alice");
            System.out.println("Name: " + person.name);
            System.out.println("Age: " + person.age);  // Default value for int is 0
        }
    }

    Output:

    Name: Alice
    Age: 0

    3. Static Variables: Static variables are shared across all instances of a class and are declared with the static keyword. Only one copy of a static variable exists, regardless of how many objects are created.

    // Example of static variables
    public class StaticVariableExample {
        public static String company = "TechCorp";  // Static variable
    
        public static void main(String[] args) {
            System.out.println("Company: " + StaticVariableExample.company);  // Accessing without object
        }
    }

    Output:

    Company: TechCorp

    Scope of a Variables

    In Java, the scope of a variable refers to the region in the code where the variable is accessible. Java has lexical (static) scoping, meaning the scope of a variable is determined at compile time and is not dependent on the function call stack. The scope rules in Java can be broadly classified into three categories based on where the variables are declared.

    1. Member Variables (Class-Level Scope)
    2. Local Variables (Method-Level Scope)
    3. Block Variables (Loop or Block-Level Scope)

    1. Member Variables (Class-Level Scope)

    Member variables are declared inside a class but outside any method, constructor, or block. They can be accessed anywhere within the class and can have different access levels (e.g., publicprivateprotected, or default). Access to member variables outside the class depends on the access modifier used.

    Example:

    public class Test {
        // Member variables
        int a;                  // Default access modifier
        private String b;        // Private member variable
        char c;                  // Default access modifier
    
        void method1() {
            // Member variables can be accessed here
            System.out.println(a);
            System.out.println(b);
        }
    
        int method2() {
            return a;
        }
    }
    • Public: Accessible within the class, in subclasses, and outside the class.
    • Protected: Accessible within the class and in subclasses but not outside the package.
    • Default (no modifier): Accessible within the same package but not outside it.
    • Private: Only accessible within the class.
    2. Local Variables (Method-Level Scope)

    Local variables are declared inside a method or constructor and are only accessible within that method. They must be initialized before use, and their lifetime is limited to the method’s execution. Once the method finishes, local variables are destroyed.

    Example:

    public class Test {
        void method1() {
            // Local variable
            int x = 10;
            System.out.println(x);  // Accessible inside the method
        }
    
        public static void main(String[] args) {
            Test t = new Test();
            t.method1();
        }
    }
    3. Block Variables (Loop or Block-Level Scope)

    Variables declared inside a block (within curly braces {}) are only accessible within that block. Once the block is exited, these variables are out of scope and cannot be accessed. This applies to variables declared inside loops or conditionals.

    Example of Block-Level Scope:

    public class Test {
        public static void main(String[] args) {
            {
                int x = 10;  // x is only accessible inside this block
                System.out.println(x);
            }
    
            // Uncommenting the following line will cause an error
            // System.out.println(x);  // x is out of scope here
        }
    }

    Loop Variables (Block Scope)

    Variables declared inside a loop have scope limited to the loop. They cannot be accessed outside the loop.

    Example:

    class Test {
        public static void main(String[] args) {
            for (int x = 0; x < 4; x++) {
                System.out.println(x);  // x is accessible inside the loop
            }
    
            // Uncommenting the following line will result in an error
            // System.out.println(x);  // x is out of scope here
        }
    }

    If you need to access a loop variable outside the loop, declare it before the loop:

    class Test {
        public static void main(String[] args) {
            int x;
            for (x = 0; x < 4; x++) {
                System.out.println(x);
            }
            System.out.println(x);  // x is accessible outside the loop
        }
    }

    Output:

    0
    1
    2
    3
    4

    Loop Variable Scope with Overlapping Names

    In Java, you cannot declare two variables with the same name within the same scope. However, in languages like C++, it’s possible to have the same variable name in nested scopes, which is not allowed in Java.

    Incorrect Example in Java (compilation error):

    class Test {
        public static void main(String[] args) {
            int a = 5;
            for (int a = 0; a < 5; a++) {  // Error: Variable 'a' is already defined
                System.out.println(a);
            }
        }
    }

    Output:

    Error: variable 'a' is already defined

    Valid Example with Loop Variable Declaration After Loop

    To avoid such errors, you can declare a variable outside the loop and use it after the loop finishes.

    Example:

    class Test {
        public static void main(String[] args) {
            for (int i = 1; i <= 10; i++) {
                System.out.println(i);  // Loop variable i
            }
    
            int i = 20;  // Declare i after the loop
            System.out.println(i);  // Access new i outside the loop
        }
    }

    Output:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    20
  • Overview​ of Java

    Introduction to Java

    Java is a high-level, class-based, object-oriented programming language that is designed to have as few implementation dependencies as possible. It is intended to let application developers “write once, run anywhere” (WORA), meaning that compiled Java code can run on all platforms that support Java without the need for recompilation. Java applications are typically compiled to bytecode that can run on any Java Virtual Machine (JVM) regardless of the underlying computer architecture.

    Key Features of Java:
    • Object-Oriented: Encourages the development of modular and reusable code.
    • Platform-Independent: The compiled bytecode can run on any platform with a JVM.
    • Simple and Familiar:Easier to learn and use compared to some other languages.
    • Secure: Provides a secure execution environment and prevents many security threats.
    • Robust: Strong memory management and exception handling.
    • Multithreaded:Supports concurrent execution of two or more parts of a program for maximum utilization of CPU.
    • High Performance:Includes Just-In-Time (JIT) compiler for improved performance.

    C++ vs Java vs Python

    FeatureC++JavaPython
    TypeCompiled languageCompiled to bytecode, runs on JVMInterpreted language
    MemoryManual memory managementAutomatic garbage collectionAutomatic garbage collection
    SyntaxComplex, supports multiple paradigmsCleaner, more consistent than C++Very clean, highly readable
    PerformanceHigh performance, close to hardwareSlower than C++ but highly portableSlower than C++ and Java
    Use CasesSystem/embedded programming, game devWeb applications, enterprise appsWeb development, data science
    LibrariesStandard Template Library (STL)Rich standard library and frameworksExtensive standard library
    Multi-threadingSupported but complexBuilt-in support with synchronizationSupported with Global Interpreter Lock (GIL)

    Just-In-Time (JIT) Compiler

    The Just-In-Time (JIT) compiler is a component of the Java Runtime Environment (JRE) that improves the performance of Java applications by compiling bytecode into native machine code at runtime. This allows Java programs to run faster as they are executed directly by the CPU rather than being interpreted by the JVM.

    Advantages:
    • Improved PerformanceConverts frequently executed bytecode to native code.
    • OptimizationsApplies various optimizations to the native code for better performance.
    • Adaptive Compilation: Compiles only the code that is executed frequently, reducing the overhead.

    Difference between JIT and JVM in Java

    JIT (Just-In-Time Compiler):
    • Part of the JVM.
    • Converts bytecode into native machine code at runtime.
    • Improves performance by compiling frequently executed bytecode.
    • Applies runtime optimizations to the code.
    JVM (Java Virtual Machine):
    • An abstract machine that enables Java bytecode to be executed on any platform.
    • Responsible for interpreting bytecode, managing memory, and providing runtime environment.
    • Includes components like the class loader, runtime data areas, and the execution engine (which includes the JIT compiler).

    Difference between Bytecode and Machine Code

    Bytecode:
    • Intermediate code generated by the Java compiler.
    • Platform-independent and can be executed on any system with a JVM.
    • Needs to be interpreted or compiled (JIT) to machine code for execution
    Machine Code:
    • Low-level code executed directly by the CPU.
    • Platform-specific and generated from bytecode by the JIT compiler.
    • Does not require further interpretation and runs directly on the hardware.

    How is Java Platform Independent?

    Java is platform-independent due to its use of the JVM and bytecode. Here’s how:

    1. Source Code Compilation:Java source code (.java files) is compiled by the Java compiler (javac) into bytecode (.class files). Bytecode is an intermediate, platform-independent code.
    2. Java Virtual Machine (JVM): The JVM is a platform-specific execution environment that runs Java bytecode. Every platform (Windows, macOS, Linux, etc.) has its own JVM implementation.
    3. Write Once, Run Anywhere: Since the bytecode is platform-independent and the JVM is platform-specific, Java applications can run on any device or operating system that has a JVM. This allows developers to write Java programs once and run them anywhere without modification.

  • Java Tutorial Roadmap

    Introduction to Java

    Overview of Java

    Java is a high-level, object-oriented, platform-independent programming language widely used for building enterprise applications, web applications, mobile apps, and backend systems.

    Java vs C++ vs Python

    • Java: Platform-independent, OOP-focused, strong memory management
    • C++: High performance, low-level memory control
    • Python: Simple syntax, rapid development, scripting-friendly

    Java Platform and Execution

    • Just-In-Time (JIT) Compiler
    • Difference between JIT and JVM
    • Bytecode vs Machine Code
    • How Java achieves platform independence

    Java Basic Concepts

    Java Basic Syntax

    • Structure of a Java program
    • Writing a Hello World program

    Data Types in Java

    • Primitive data types
    • Object (reference) data types

    Identifiers

    • Java identifier naming rules

    Variables in Java

    • Variable declaration and initialization
    • Scope of variables

    Input and Output in Java

    Console Input

    • Taking user input in Java
    • Scanner class
    • Different ways to read input from the console

    Output in Java

    • Using System.out.println()
    • Formatted output using printf()

    Operators in Java

    Types of Operators

    • Unary operators
    • Assignment operators
    • Relational operators
    • Logical operators
    • Ternary operator

    Flow Control in Java

    Decision-Making Statements

    • if statement
    • if-else statement
    • if-else-if ladder

    Loops

    • while loop
    • do-while loop
    • for-each loop

    Control Statements

    • break statement
    • continue statement
    • return keyword

    Arrays in Java

    Arrays Overview

    • Declaring and initializing arrays

    Types of Arrays

    • One-dimensional arrays
    • Multidimensional arrays
    • Jagged arrays
    • Final arrays

    Arrays Utility Classes

    • Arrays class
    • reflect.Array
    • Difference between util.Arrays and reflect.Array

    Object-Oriented Programming (OOP) in Java

    Java Naming Conventions

    • Class, method, and variable naming rules

    Classes and Objects

    • Understanding classes and objects
    • Object class in Java
    • Singleton design pattern

    Constructors

    • Default and parameterized constructors
    • Copy constructor
    • Constructor overloading
    • Constructor chaining
    • Private constructors and singleton classes

    Inheritance in Java

    Inheritance Concepts

    • Inheritance and constructors
    • Types of inheritance
    • Interfaces and inheritance

    Multiple Inheritance

    • Achieved using interfaces

    Abstraction in Java

    Abstraction Concepts

    • Abstract classes
    • Abstract methods
    • Control abstraction

    Data Hiding vs Abstraction

    • Key differences

    Encapsulation in Java

    Encapsulation Concepts

    • Data hiding using access modifiers

    Abstraction vs Encapsulation

    • Key differences and use cases

    Polymorphism in Java

    Polymorphism Concepts

    • Compile-time polymorphism
    • Runtime polymorphism

    Dynamic Method Dispatch

    • Method overriding

    Inheritance vs Polymorphism

    • Conceptual differences

    Methods in Java

    Methods Overview

    • Defining and calling methods

    Method Overloading

    • Compile-time polymorphism

    Method Overriding

    • Runtime polymorphism

    Memory Management in Java

    Memory Allocation

    • Stack vs Heap memory

    JVM Memory Areas

    • Types of memory areas allocated by JVM

    Garbage Collection

    • Garbage collection basics
    • Types of JVM garbage collectors

    Memory Leaks

    • Causes and prevention

    Java Virtual Machine (JVM)

    JVM Stack Area

    • Stack frames and execution

    Wrapper Classes

    Wrapper Classes Overview

    • Character class
    • Byte, Short, Long, Float classes

    Keywords in Java

    Java Keywords

    • List of Java keywords

    Important Keywords

    • super
    • final
    • static
    • enum
    • transient
    • volatile

    final vs finally vs finalize

    • Differences and use cases

    Access Modifiers

    Types of Access Modifiers

    • Public
    • Protected
    • Package-private
    • Private

    Access vs Non-Access Modifiers

    • Key differences

    Inner Classes in Java

    Inner Class Concepts

    • Types of inner classes

    Packages in Java

    Package Basics

    • Creating packages

    Common Packages

    • java.util
    • java.lang
    • java.io

    Exception Handling in Java

    Exception Basics

    • Types of exceptions
    • Checked vs unchecked exceptions

    Exception Handling Mechanism

    • try, catch, finally
    • Flow control in exception handling

    throw and throws

    • Custom exception handling

    Advanced Exception Topics

    • User-defined exceptions
    • Chained exceptions

    Multithreading in Java

    Thread Basics

    • Lifecycle and states of a thread
    • Main thread

    Thread Creation

    • Thread class
    • Runnable interface

    Thread Management

    • Thread priority
    • Naming threads
    • start() method behavior

    Synchronization and Concurrency

    Thread Synchronization

    • Importance of synchronization
    • Method-level and block-level synchronization

    Locks and Concurrency

    • Lock framework vs synchronization
    • Atomic vs volatile vs synchronized

    Deadlocks

    • Deadlock concepts
    • Prevention and avoidance

    Advanced Locks

    • Lock vs monitor
    • ReentrantLock

    File Handling in Java

    File Handling Basics

    • File class
    • Creating files

    Reading Files

    • Different ways to read text files

    Writing Files

    • Writing data into files
    • FileWriter class

    File Operations

    • Deleting files
    • File permissions

    Advanced File Handling

    • FileDescriptor
    • RandomAccessFile