Java, Classes and Objects
Tags: java, coding, beginners, tutorial
Published: 2024-06-28 18:43:14

## Classes and Objects in Java
Java is an object-oriented programming language that uses classes and objects as the fundamental building blocks. Understanding these concepts is crucial for mastering Java. This guide will cover everything you need to know about classes and objects in Java.

### Defining a Class
A class in Java is a blueprint for creating objects. It defines the structure and behavior that the objects of the class will have.

#### Syntax
```java
public class ClassName {
    // Fields (Variables)
    // Methods (Functions)
}
```

#### Example
```java
public class Car {
    // Fields
    String color;
    String model;
    int year;

    // Methods
    void displayInfo() {
        System.out.println("Model: " + model);
        System.out.println("Color: " + color);
        System.out.println("Year: " + year);
    }
}
```

### Creating Objects
Objects are instances of classes. You create an object from a class using the `new` keyword.

#### Syntax
```java
ClassName objectName = new ClassName();
```

#### Example
```java
public class Main {
    public static void main(String[] args) {
        Car myCar = new Car(); // Creating an object of the Car class
        myCar.color = "Red";
        myCar.model = "Tesla";
        myCar.year = 2022;
        myCar.displayInfo();
    }
}
```

### Fields and Methods
Fields (also known as variables or properties) represent the state of an object, while methods define the behavior of the object.

#### Fields
Fields are variables that hold the data of an object.

#### Example
```java
public class Car {
    String color;
    String model;
    int year;
}
```

#### Methods
Methods are functions defined within a class that describe the behaviors of the objects.

#### Example
```java
public class Car {
    String color;
    String model;
    int year;

    void displayInfo() {
        System.out.println("Model: " + model);
        System.out.println("Color: " + color);
        System.out.println("Year: " + year);
    }
}
```

### Constructors
Constructors are special methods that are called when an object is instantiated. They initialize the newly created object.

#### Default Constructor
If no constructor is defined, Java provides a default constructor with no arguments.

#### Example
```java
public class Car {
    String color;
    String model;
    int year;

    // Default constructor
    public Car() {
    }

    void displayInfo() {
        System.out.println("Model: " + model);
        System.out.println("Color: " + color);
        System.out.println("Year: " + year);
    }
}
```

#### Parameterized Constructor
A parameterized constructor allows you to initialize an object with specific values.

#### Example
```java
public class Car {
    String color;
    String model;
    int year;

    // Parameterized constructor
    public Car(String color, String model, int year) {
        this.color = color;
        this.model = model;
        this.year = year;
    }

    void displayInfo() {
        System.out.println("Model: " + model);
        System.out.println("Color: " + color);
        System.out.println("Year: " + year);
    }
}
```

#### Using the Parameterized Constructor
```java
public class Main {
    public static void main(String[] args) {
        Car myCar = new Car("Red", "Tesla", 2022);
        myCar.displayInfo();
    }
}
```

### Constructor Overloading
You can have multiple constructors in a class, each with a different parameter list. This is called constructor overloading.

#### Example
```java
public class Car {
    String color;
    String model;
    int year;

    // Default constructor
    public Car() {
        this.color = "Unknown";
        this.model = "Unknown";
        this.year = 0;
    }

    // Parameterized constructor
    public Car(String color, String model, int year) {
        this.color = color;
        this.model = model;
        this.year = year;
    }

    void displayInfo() {
        System.out.println("Model: " + model);
        System.out.println("Color: " + color);
        System.out.println("Year: " + year);
    }
}
```

#### Using the Overloaded Constructors
```java
public class Main {
    public static void main(String[] args) {
        Car defaultCar = new Car();
        defaultCar.displayInfo();

        Car myCar = new Car("Red", "Tesla", 2022);
        myCar.displayInfo();
    }
}
```

## Encapsulation, Access Modifiers, and Static Members in Java

### Encapsulation

Encapsulation is the principle of bundling data (fields) and methods (functions) that operate on the data within a single unit, called a class. It keeps the internal state of an object safe from outside interference and misuse.

### Access Modifiers

Access modifiers control the visibility and accessibility of classes, fields, methods, and constructors within Java programs. There are four main access modifiers:

- **public:** Accessible from any other class.
- **protected:** Accessible within the same package and by subclasses (even if they are in different packages).
- **(default) package-private:** Accessible only within the same package.
- **private:** Accessible only within the same class.

#### Example Usage:
```java
public class Car {
    public String color; // accessible everywhere
    protected String model; // accessible within the same package and subclasses
    int year; // package-private (default), accessible within the same package
    private String vin; // accessible only within the class

    // Constructor and methods
}
```

### Static Members

Static members (variables and methods) belong to the class itself rather than instances of the class. They are shared among all instances of the class and can be accessed without creating an object of the class.

#### Static Variables (Class Variables):
Static variables are shared among all instances of a class. They are initialized only once, at the start of the execution, and retain their value until the program terminates.

```java
public class Car {
    public static int count; // static variable to count instances

    // Constructor and methods
}
```

#### Static Methods:
Static methods belong to the class rather than any specific instance. They can access static variables and other static methods directly.

```java
public class Car {
    public static void displayCount() {
        System.out.println("Number of cars created: " + count);
    }

    // Constructor and other methods
}
```

#### Accessing Static Members:
Static members are accessed using the class name, not through object references.

```java
public class Main {
    public static void main(String[] args) {
        Car.count = 0; // Accessing static variable
        Car.displayCount(); // Calling static method
    }
}
```

### Access Modifiers in Java

Access modifiers in Java are keywords that define the accessibility (visibility) of classes, methods, variables, and constructors within Java programs. They control how these elements can be accessed by other classes and methods, helping to enforce encapsulation and ensure code integrity.

#### Types of Access Modifiers

Java provides four main access modifiers:

1. **public**
2. **protected**
3. **default (no modifier)**
4. **private**

Let's explore each of these access modifiers in detail:

#### 1. public

- **Accessible from:** Any other class in the same Java program, regardless of package.
- **Usage:** Use `public` when you want a class, method, variable, or constructor to be widely accessible.
- **Example:**

   ```java
   public class MyClass {
       public int publicVar;
       public void publicMethod() {
           // Method implementation
       }
   }
   ```

#### 2. protected

- **Accessible from:** Classes in the same package and subclasses (even if they are in different packages).
- **Usage:** Use `protected` to provide visibility within the same package and to subclasses.
- **Example:**

   ```java
   package mypackage;

   public class ParentClass {
       protected int protectedVar;
       protected void protectedMethod() {
           // Method implementation
       }
   }

   class ChildClass extends ParentClass {
       void accessProtected() {
           protectedVar = 10; // Accessing protected variable from subclass
           protectedMethod(); // Accessing protected method from subclass
       }
   }
   ```

#### 3. default (package-private)

- **Accessible from:** Classes in the same package only.
- **Usage:** When no access modifier is specified, it is default (package-private). Use this when elements need to be accessed only within the same package.
- **Example:**

   ```java
   package mypackage;

   class MyClass {
       int defaultVar;
       void defaultMethod() {
           // Method implementation
       }
   }

   public class AnotherClass {
       void accessDefault() {
           MyClass obj = new MyClass();
           obj.defaultVar = 20; // Accessing default variable from another class in the same package
           obj.defaultMethod(); // Accessing default method from another class in the same package
       }
   }
   ```

#### 4. private

- **Accessible from:** Only within the same class.
- **Usage:** Use `private` when you want to restrict access to within the same class, providing the highest level of encapsulation.
- **Example:**

   ```java
   public class MyClass {
       private int privateVar;
       private void privateMethod() {
           // Method implementation
       }

       public void accessPrivate() {
           privateVar = 30; // Accessing private variable within the same class
           privateMethod(); // Accessing private method within the same class
       }
   }
   ```

### Non-Access Modifiers in Java

Non-access modifiers in Java modify the behavior of classes, methods, variables, and constructors without affecting their accessibility. They provide additional functionality and characteristics to these elements, enhancing code functionality and behavior.

#### Types of Non-Access Modifiers

Java provides several non-access modifiers:

1. **static**
2. **final**
3. **abstract**

Let's explore each of these non-access modifiers in detail:

#### 1. static

- **Usage:** The `static` keyword is used to declare members (variables and methods) that belong to the class rather than instances of the class.
- **Variables:** Static variables (class variables) are shared among all instances of a class. They are initialized only once, at the start of the execution, and retain their value until the program terminates.
- **Methods:** Static methods belong to the class rather than any specific instance. They can access static variables and other static methods directly.
- **Example:**

   ```java
   public class MyClass {
       public static int count; // Static variable (class variable)
       public static void staticMethod() {
           // Static method implementation
       }
   }

   public class Main {
       public static void main(String[] args) {
           MyClass.count = 10; // Accessing static variable
           MyClass.staticMethod(); // Calling static method
       }
   }
   ```

#### 2. final

- **Usage:** The `final` keyword is used to declare constants, prevent method overriding, and prevent inheritance (when applied to classes).
- **Variables:** Final variables (constants) cannot be changed once initialized.
- **Methods:** Final methods cannot be overridden by subclasses.
- **Classes:** Final classes cannot be subclassed.
- **Example:**

   ```java
   public class MyClass {
       public static final int MAX_VALUE = 100; // Constant variable
       public final void finalMethod() {
           // Final method implementation
       }
   }

   public class SubClass extends MyClass {
       // Cannot override finalMethod()
   }
   ```

#### 3. abstract

- **Usage:** The `abstract` keyword is used to declare abstract classes and methods.
- **Abstract Classes:** Abstract classes cannot be instantiated on their own. They may contain abstract methods (methods without a body) that must be implemented by subclasses.
- **Abstract Methods:** Abstract methods are declared without a body and must be implemented by subclasses (unless the subclass itself is abstract).
- **Example:**

   ```java
   public abstract class Shape {
       abstract void draw(); // Abstract method
   }

   public class Circle extends Shape {
       @Override
       void draw() {
           // Method implementation
       }
   }
   ```

## Inheritance in Java and Access Modifiers

### Inheritance

Inheritance in Java is a mechanism where one class (subclass or derived class) inherits the properties and behaviors (methods and fields) of another class (superclass or base class). This allows for code reuse and allows you to create a hierarchical relationship between classes.

#### Syntax for Inheritance

```java
public class SuperClass {
    // Fields and methods
}

public class SubClass extends SuperClass {
    // Fields and methods of SubClass
}
```

#### Example

```java
public class Animal {
    String name;

    public void eat() {
        System.out.println(name + " is eating.");
    }
}

public class Dog extends Animal {
    public void bark() {
        System.out.println(name + " is barking.");
    }
}
```

In this example:
- `Animal` is the superclass with a field `name` and a method `eat()`.
- `Dog` is the subclass that inherits `name` and `eat()` from `Animal` and adds its own method `bark()`.

### Access Modifiers in Inheritance

Access modifiers control the visibility and accessibility of classes, methods, and fields in Java. They play a crucial role in inheritance as they determine whether a subclass can access members (fields and methods) of its superclass.

#### Access Modifiers for Normal Attributes and Methods

- **public:** Accessible from any other class.
- **protected:** Accessible within the same package and by subclasses (even if they are in different packages).
- **default (no modifier):** Accessible within the same package only.
- **private:** Accessible only within the same class.

```java
public class SuperClass {
    public String publicVar;
    protected String protectedVar;
    String defaultVar;
    private String privateVar;

    public void publicMethod() {
        // Method implementation
    }

    protected void protectedMethod() {
        // Method implementation
    }

    void defaultMethod() {
        // Method implementation
    }

    private void privateMethod() {
        // Method implementation
    }
}

public class SubClass extends SuperClass {
    public void accessSuperClassMembers() {
        publicVar = "Public"; // Accessible
        protectedVar = "Protected"; // Accessible
        defaultVar = "Default"; // Accessible
        // privateVar = "Private"; // Not accessible (compile-time error)

        publicMethod(); // Accessible
        protectedMethod(); // Accessible
        defaultMethod(); // Accessible
        // privateMethod(); // Not accessible (compile-time error)
    }
}
```

#### Access Modifiers for Static Attributes and Methods

Static members belong to the class rather than instances. They are inherited in a similar way to instance members, but their access is determined by the same rules as non-static members.

```java
public class SuperClass {
    public static String staticVar = "Static Variable";

    public static void staticMethod() {
        System.out.println("Static method in SuperClass.");
    }
}

public class SubClass extends SuperClass {
    public void accessSuperClassStaticMembers() {
        System.out.println(staticVar); // Accessing static variable
        staticMethod(); // Calling static method
    }
}
```

### Are Static Methods Inherited?

Static methods are inherited in Java, but they cannot be overridden like instance methods. When a subclass defines a static method with the same signature as a static method in the superclass, it hides the superclass's static method rather than overriding it.

```java
public class SuperClass {
    public static void staticMethod() {
        System.out.println("Static method in SuperClass.");
    }
}

public class SubClass extends SuperClass {
    public static void staticMethod() {
        System.out.println("Static method in SubClass.");
    }

    public static void main(String[] args) {
        SuperClass.staticMethod(); // Output: Static method in SuperClass.
        SubClass.staticMethod(); // Output: Static method in SubClass.
    }
}
```

### Syntax for Declaring Top-Level Class

For a top-level class (a class declared outside of any other class), here's the comprehensive list and order:

#### Keywords that can be used:
1. Access modifier: `public`
2. `abstract`
3. `final`

#### Order:
```
[public] [abstract|final] class ClassName
```

#### Syntax:
```java
[AccessModifier] [AbstractOrFinal] class ClassName {
    // Class body
}
```

#### Important notes:
1. If no access modifier is specified, the class has package-private access (visible only within its package).
2. `abstract` and `final` are mutually exclusive - a class cannot be both.
3. `protected` and `private` are not allowed for top-level classes.
4. The order of keywords matters - access modifier should come first, followed by `abstract` or `final`.

### Syntax for Declaring Nested Classes

#### Keywords that can be used:

1. Access modifiers: `public`, `protected`, `private`
2. `static`
3. `abstract`
4. `final`

#### Order:

```
[AccessModifier] [static] [abstract|final] class ClassName
```

#### Syntax:

```java
class OuterClass {
    [AccessModifier] [static] [AbstractOrFinal] class NestedClassName {
        // Nested class body
    }
}
```

#### Types of nested classes:

1. Static nested classes (use `static` keyword)
2. Non-static nested classes (inner classes, no `static` keyword)
3. Local classes (defined inside a method)
4. Anonymous classes (unnamed classes defined and instantiated in a single expression)

#### Important notes:

1. Nested classes can use any access modifier (`public`, `protected`, `private`, or package-private).
2. Static nested classes don't have access to instance members of the outer class.
3. Non-static nested classes (inner classes) have access to all members of the outer class, even private ones.
4. `abstract` and `final` are mutually exclusive for nested classes, just as with top-level classes.
5. Local and anonymous classes can't use access modifiers or be declared static.
6. The order of keywords matters - access modifier first, then `static` (if used), followed by `abstract` or `final`.

### Syntax for Declaring Attributes in Classes

#### Keywords that can be used:
1. Access modifiers: `public`, `protected`, `private`
2. `static`
3. `final`

#### Order:
```
[AccessModifier] [static] [final] dataType attributeName
```

#### Syntax:
```java
class ClassName {
    [AccessModifier] [static] [final] dataType attributeName [= initialValue];
}
```

#### Important notes:
1. If no access modifier is specified, the attribute has package-private access.
2. `static` attributes belong to the class rather than instances of the class.
3. `final` attributes cannot be reassigned after initialization.
4. The order of keywords matters - access modifier first, then `static` and `final` (if used).
5. Initialization is optional. Uninitialized attributes get default values (0, false, or null depending on the type).

### Syntax for Declaring Methods in Classes

#### Keywords that can be used:
1. Access modifiers: `public`, `protected`, `private`
2. `static`
3. `final`
4. `abstract`

#### Order:
```
[AccessModifier] [static] [final|abstract] returnType methodName(parameters)
```

#### Syntax:
```java
class ClassName {
    [AccessModifier] [static] [final|abstract] returnType methodName(paramType1 param1, paramType2 param2, ...) [throws ExceptionType] {
        // Method body
    }
}
```

#### Important notes:
1. If no access modifier is specified, the method has package-private access.
2. `static` methods belong to the class and can be called without creating an instance.
3. `final` methods cannot be overridden in subclasses.
4. `abstract` methods have no body and must be implemented by non-abstract subclasses.
5. `abstract` and `final` are mutually exclusive.
6. `abstract` methods cannot be `private`, `static`, or `final`.
7. The order of keywords matters - access modifier first, then `static`, followed by `final` or `abstract`.

### Inner Classes in Java

Inner classes in Java are classes defined within another class. They offer a way to logically group classes that are only used in one place, increase encapsulation, and improve code organization. Java supports several types of inner classes, each with its own characteristics and use cases.

#### Types of Inner Classes

Java supports the following types of inner classes:

1. **Nested Inner Class (Non-static Inner Class)**
2. **Static Nested Class**
3. **Local Inner Class (Method-Local Inner Class)**
4. **Anonymous Inner Class**

Let's explore each type in detail:

#### 1. Nested Inner Class (Non-static Inner Class)

- **Definition:** A nested inner class is a non-static inner class that has access to the enclosing class's instance variables and methods.
- **Usage:** Typically used for logical grouping and to access outer class members.
- **Syntax:**
  ```java
  public class OuterClass {
      // Outer class members

      class InnerClass {
          // Inner class members
      }
  }
  ```
- **Accessing Inner Class:**
  ```java
  OuterClass outer = new OuterClass();
  OuterClass.InnerClass inner = outer.new InnerClass();
  ```

#### 2. Static Nested Class

- **Definition:** A static nested class is a nested class that is declared static. It does not have access to the enclosing class's instance variables and methods unless explicitly passed an instance.
- **Usage:** Useful for grouping classes that are only used in conjunction with the outer class and do not need access to instance-specific data.
- **Syntax:**
  ```java
  public class OuterClass {
      // Outer class members

      static class NestedStaticClass {
          // Static nested class members
      }
  }
  ```
- **Accessing Static Nested Class:**
  ```java
  OuterClass.NestedStaticClass nested = new OuterClass.NestedStaticClass();
  ```

#### 3. Local Inner Class (Method-Local Inner Class)

- **Definition:** A local inner class is defined within a method or scope block and has limited visibility and lifespan.
- **Usage:** Useful when you need to perform some specific tasks and do not want to reuse the class elsewhere in the outer class.
- **Syntax:**
  ```java
  public class OuterClass {
      // Outer class members

      public void someMethod() {
          class LocalInnerClass {
              // Local inner class members
          }

          LocalInnerClass inner = new LocalInnerClass();
          // Use local inner class instance
      }
  }
  ```
- **Accessing Local Inner Class:**
  Local inner classes are instantiated within the method/block where they are defined and cannot be accessed outside of it.

### Abstract Classes in Java

Abstract classes in Java are used to define common characteristics and behaviors for a group of related classes while allowing for some methods to be implemented by the subclasses. Here’s a comprehensive guide to understanding and using abstract classes in Java:

#### 1. Introduction to Abstract Classes

An abstract class is a class that cannot be instantiated on its own and is meant to be subclassed. It can contain both abstract methods (methods without a body) and concrete methods (methods with a body).

#### 2. Declaring an Abstract Class

To declare an abstract class, use the `abstract` keyword before the class keyword.

**Example:**
```java
public abstract class Animal {
    abstract void makeSound();  // abstract method

    void sleep() {  // concrete method
        System.out.println("Sleeping...");
    }
}
```

#### 3. Abstract Methods

Abstract methods are methods declared without a body and must be implemented by subclasses.

**Example:**
```java
public abstract class Animal {
    abstract void makeSound();
}

public class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Bark");
    }
}
```

#### 4. Concrete Methods

Concrete methods in an abstract class have a body and can be inherited by subclasses.

**Example:**
```java
public abstract class Animal {
    void eat() {
        System.out.println("Eating...");
    }
}
```

#### 5. Creating Subclasses

Subclasses of an abstract class must provide implementations for all abstract methods in the superclass. If a subclass does not implement all abstract methods, it must also be declared abstract.

**Example:**
```java
public abstract class Animal {
    abstract void makeSound();
}

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

public class Cat extends Animal {
    @Override
    void makeSound() {
        System.out.println("Meow");
    }
}
```

#### 6. Instantiating Abstract Classes

Abstract classes cannot be instantiated directly. They can only be instantiated through their concrete subclasses.

**Example:**
```java
public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.makeSound();  // Output: Bark

        Animal cat = new Cat();
        cat.makeSound();  // Output: Meow
    }
}
```

#### 7. Constructors in Abstract Classes

Abstract classes can have constructors, which are called when a concrete subclass is instantiated.

**Example:**
```java
public abstract class Animal {
    String name;

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

    abstract void makeSound();
}

public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println(name + " says: Bark");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog("Rex");
        dog.makeSound();  // Output: Rex says: Bark
    }
}
```

#### 8. Abstract Classes with Fields and Methods

Abstract classes can contain fields (instance variables) and concrete methods that operate on those fields.

**Example:**
```java
public abstract class Animal {
    String name;

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

    abstract void makeSound();

    void sleep() {
        System.out.println(name + " is sleeping");
    }
}

public class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println(name + " says: Meow");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal cat = new Cat("Whiskers");
        cat.makeSound();  // Output: Whiskers says: Meow
        cat.sleep();  // Output: Whiskers is sleeping
    }
}
```

#### 9. Static Methods in Abstract Classes

Abstract classes can also contain static methods. These methods belong to the class itself rather than to any instance of the class. Static methods in an abstract class can be called without creating an instance of the class.

**Example:**
```java
public abstract class Animal {
    abstract void makeSound();

    static void info() {
        System.out.println("Animals are multicellular organisms that form the biological kingdom Animalia.");
    }
}

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

public class Main {
    public static void main(String[] args) {
        Animal.info();  // Output: Animals are multicellular organisms that form the biological kingdom Animalia.

        Dog dog = new Dog();
        dog.makeSound();  // Output: Bark
    }
}
```

### Syntax for Declaring Abstract Classes in Java

#### Keywords that can be used:
1. Access modifier: `public`
2. `abstract`

#### Order:
```
[public] abstract class ClassName
```

#### Syntax:
```java
[public] abstract class ClassName [extends SuperClass] [implements Interface1, Interface2, ...] {
    // Abstract class body
}
```

#### Examples:
1. `public abstract class Shape { }`
2. `abstract class Animal extends LivingOrganism implements Movable { }`

#### Important features and notes:
1. Abstract classes can have both abstract and concrete methods.
2. Abstract methods syntax:
   ```java
   abstract returnType methodName(parameters);
   ```
3. Concrete methods are declared normally.
4. Abstract classes can have constructors, instance variables, and static members.
5. Abstract classes cannot be instantiated directly.
6. Subclasses of an abstract class must implement all its abstract methods, or be declared abstract themselves.

#### Example with methods and variables:
```java
public abstract class Vehicle {
    protected String brand;
    
    public Vehicle(String brand) {
        this.brand = brand;
    }
    
    public abstract void start();
    
    public void stop() {
        System.out.println("Vehicle stopped");
    }
}
```

#### Important notes:
1. If no access modifier is specified, the abstract class has package-private access.
2. Abstract classes can extend other classes and implement interfaces.
3. Abstract classes can have `final` methods, which cannot be overridden in subclasses.
4. Abstract classes can have static methods and variables.
5. Abstract methods cannot have a body.
6. Abstract classes cannot be declared as `final`.
7. Abstract classes can have any number of abstract and non-abstract methods.

### Interfaces in Java

Interfaces in Java are a powerful tool for defining contracts that classes can implement. They allow for the definition of methods that must be implemented by any class that chooses to implement the interface. Here's a comprehensive guide to understanding and using interfaces in Java:

#### 1. Introduction to Interfaces

An interface in Java is a reference type, similar to a class, that can contain only constants, method signatures, default methods, static methods, and nested types. The methods in interfaces are abstract by default (except default and static methods), meaning they do not have a body and must be implemented by classes that implement the interface.

#### 2. Declaring an Interface

To declare an interface, use the `interface` keyword.

**Example:**
```java
public interface Animal {
    void eat();  // abstract method
    void sleep();  // abstract method
}
```

#### 3. Implementing an Interface

A class implements an interface by using the `implements` keyword. The class must provide concrete implementations for all abstract methods declared in the interface.

**Example:**
```java
public class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }

    @Override
    public void sleep() {
        System.out.println("Dog is sleeping");
    }
}
```

#### 4. Multiple Interfaces

A class can implement multiple interfaces, allowing it to inherit the behavior of more than one interface.

**Example:**
```java
public interface Mammal {
    void walk();
}

public class Human implements Animal, Mammal {
    @Override
    public void eat() {
        System.out.println("Human is eating");
    }

    @Override
    public void sleep() {
        System.out.println("Human is sleeping");
    }

    @Override
    public void walk() {
        System.out.println("Human is walking");
    }
}
```

#### 5. Default Methods

Java 8 introduced default methods in interfaces. These methods have a body and provide a default implementation. Classes that implement the interface can use the default implementation or override it.

**Example:**
```java
public interface Vehicle {
    void start();

    default void honk() {
        System.out.println("Honking...");
    }
}

public class Car implements Vehicle {
    @Override
    public void start() {
        System.out.println("Car is starting");
    }
}
```

#### 6. Static Methods

Interfaces can also have static methods, which belong to the interface itself rather than to instances of classes that implement the interface.

**Example:**
```java
public interface MathUtils {
    static int add(int a, int b) {
        return a + b;
    }
}

public class Calculator {
    public static void main(String[] args) {
        int sum = MathUtils.add(5, 3);
        System.out.println("Sum: " + sum);
    }
}
```

#### 7. Constants

Interfaces can declare constants, which are implicitly `public`, `static`, and `final`.

**Example:**
```java
public interface Constants {
    int MAX_AGE = 100;
    String GREETING = "Hello, World!";
}

public class TestConstants {
    public static void main(String[] args) {
        System.out.println("Max Age: " + Constants.MAX_AGE);
        System.out.println(Constants.GREETING);
    }
}
```

#### 8. Inheritance in Interfaces

An interface can extend another interface, inheriting its methods. A class that implements the sub-interface must implement methods from all parent interfaces.

**Example:**
```java
public interface Animal {
    void eat();
    void sleep();
}

public interface Pet extends Animal {
    void play();
}

public class Cat implements Pet {
    @Override
    public void eat() {
        System.out.println("Cat is eating");
    }

    @Override
    public void sleep() {
        System.out.println("Cat is sleeping");
    }

    @Override
    public void play() {
        System.out.println("Cat is playing");
    }
}
```

### Syntax for Declaring Interfaces in Java

#### Keywords that can be used:
1. Access modifier: `public`
2. `interface`

#### Order:
```
[public] interface InterfaceName
```

#### Syntax:
```java
[public] interface InterfaceName [extends Interface1, Interface2, ...] {
    // Interface body
}
```

#### Examples:
1. `public interface Drawable { }`
2. `interface Runnable extends Executable { }`

#### Components of an interface:
1. Constants `(implicitly public, static, and final)`:
   ```java
   Type CONSTANT_NAME = value;
   ```

2. Abstract methods `(implicitly public and abstract)`:
   ```java
   returnType methodName(parameters);
   ```

3. Default methods (Java 8+):
   ```java
   default returnType methodName(parameters) {
       // Method body
   }
   ```

4. Static methods (Java 8+):
   ```java
   static returnType methodName(parameters) {
       // Method body
   }
   ```

#### Example interface:
```java
public interface Vehicle {
    int MAX_SPEED = 120;
    
    void start();
    void stop();
    
    default void honk() {
        System.out.println("Beep beep!");
    }
    
    static int getMaxSpeed() {
        return MAX_SPEED;
    }
}
```

#### Important notes:
1. If no access modifier is specified, the interface has package-private access.
2. Interfaces can extend multiple other interfaces.
3. All methods in an interface are implicitly `public` and `abstract` unless specified as `default` or `static`.
4. All fields in an interface are implicitly `public`, `static`, and `final`.
5. Interfaces cannot be instantiated directly.
6. A class can implement multiple interfaces.
7. Since Java 8, interfaces can have default and static methods with implementations.
8. Interfaces cannot have constructors.
9. Interfaces cannot contain instance fields (non-static variables).

### Comparable Interface in Java

In Java, the `Comparable` interface is used to define the natural ordering of objects. Classes that implement `Comparable` can be sorted into ascending order based on their natural ordering. This interface contains a single method, `compareTo`, which compares the current object with another object of the same type.

#### Overview

- **Interface Purpose**: Provides a way to impose a natural ordering on the objects of a class.
- **Single Method**: Contains the `compareTo` method for comparing objects.
- **Generic Support**: Supports generic types (`<T extends Comparable<T>>`) to enforce type safety in comparisons.

#### Method

- **`compareTo(T o)`**: Compares the current object with the specified object for order. Returns a negative integer, zero, or a positive integer if the current object is less than, equal to, or greater than the specified object, respectively.

  ```java
  public interface Comparable<T> {
      public int compareTo(T o);
  }
  ```

#### Usage

- **Sorting**: Classes that implement `Comparable` can be sorted using methods like `Collections.sort()` or `Arrays.sort()` which rely on the natural ordering defined by `compareTo`.
- **Tree-Based Collections**: Used in collections like `TreeSet` or `TreeMap` where elements are stored in sorted order based on the natural ordering defined by `compareTo`.

#### Example

Here's an example of a `Person` class implementing `Comparable` to define ordering based on age:

```java
public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Implementing compareTo method based on age
    @Override
    public int compareTo(Person otherPerson) {
        return Integer.compare(this.age, otherPerson.age);
    }

    @Override
    public String toString() {
        return "Person{" +
               "name='" + name + '\'' +
               ", age=" + age +
               '}';
    }

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));

        // Sort people based on natural ordering (age)
        Collections.sort(people);

        // Print sorted list
        System.out.println("Sorted People by Age:");
        for (Person person : people) {
            System.out.println(person);
        }
    }
}
```

### Comparator Interface in Java

In Java, the `Comparator` interface provides a way to define custom ordering for objects. Unlike `Comparable`, which defines natural ordering for a class, `Comparator` allows sorting objects based on different criteria that may not be intrinsic to the objects themselves. This interface is particularly useful when you need to sort objects in multiple ways or when you don't have control over the class whose objects you want to sort.

#### Overview

- **Interface Purpose**: Allows defining external ordering on objects of a class.
- **Single Method**: Contains the `compare` method for comparing two objects.
- **Functional Interface**: Since Java 8, `Comparator` is a functional interface, but can be implemented without using lambda expressions or anonymous classes.

#### Method

- **`compare(T o1, T o2)`**: Compares its two arguments for order. Returns a negative integer, zero, or a positive integer if the first argument is less than, equal to, or greater than the second.

  ```java
  public interface Comparator<T> {
      int compare(T o1, T o2);
  }
  ```

#### Usage

- **Sorting**: Used with sorting methods like `Collections.sort()` or `Arrays.sort()` to provide custom ordering.
- **Tree-Based Collections**: Essential for defining sorting order in collections like `TreeSet` or `TreeMap`.
- **Multiple Criteria**: Allows sorting objects based on different attributes or criteria.

#### Example

Here's an example of using `Comparator` to sort `Person` objects based on their age without using lambda expressions or anonymous classes:

```java
import java.util.*;

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter methods
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    // Comparator for sorting by age
    static class AgeComparator implements Comparator<Person> {
        @Override
        public int compare(Person p1, Person p2) {
            return Integer.compare(p1.getAge(), p2.getAge());
        }
    }

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));

        // Sorting people by age using AgeComparator
        Collections.sort(people, new AgeComparator());

        // Print sorted list
        System.out.println("Sorted People by Age:");
        for (Person person : people) {
            System.out.println(person.getName() + " - " + person.getAge());
        }
    }
}
```

#### Methods

- **`reversed()`**: Returns a comparator that imposes the reverse ordering of the current comparator.
  
  ```java
  Comparator<Person> ageComparator = new AgeComparator();
  Comparator<Person> reverseAgeComparator = ageComparator.reversed();
  ```

### Methods from `Object` Class in Java

In Java, every class directly or indirectly inherits from the `Object` class. The `Object` class provides several fundamental methods that are inherited by all Java objects. Here, we'll explore and explain these methods along with their uses and examples.

#### `toString()`

- **Method Signature**: `public String toString()`
- **Description**: Returns a string representation of the object. By default, it returns a string consisting of the class name followed by "@" and the object's hash code.

  ```java
  public class Example {
      private int value = 10;

      @Override
      public String toString() {
          return "Example{" +
                 "value=" + value +
                 '}';
      }

      public static void main(String[] args) {
          Example obj = new Example();
          System.out.println(obj.toString());  // Output: Example{value=10}
      }
  }
  ```

- **Use**: Provides a meaningful string representation of the object's state for debugging and logging purposes.

#### `equals(Object obj)`

- **Method Signature**: `public boolean equals(Object obj)`
- **Description**: Indicates whether some other object is "equal to" this one. The default implementation in `Object` class checks if two references point to the same object in memory.

  ```java
  public class Person {
      private String name;

      // Constructor, getters, setters

      @Override
      public boolean equals(Object obj) {
          if (this == obj) return true;
          if (obj == null || getClass() != obj.getClass()) return false;
          Person person = (Person) obj;
          return Objects.equals(name, person.name);
      }

      public static void main(String[] args) {
          Person person1 = new Person("Alice");
          Person person2 = new Person("Alice");

          System.out.println(person1.equals(person2));  // Output: true
      }
  }
  ```

- **Use**: Allows custom definition of equality for objects based on specific attributes or criteria.