Java, Classes and Objects

Harsh Mishra - Jun 28 - - Dev Community

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

public class ClassName {
    // Fields (Variables)
    // Methods (Functions)
}
Enter fullscreen mode Exit fullscreen mode

Example

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Creating Objects

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

Syntax

ClassName objectName = new ClassName();
Enter fullscreen mode Exit fullscreen mode

Example

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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

public class Car {
    String color;
    String model;
    int year;
}
Enter fullscreen mode Exit fullscreen mode

Methods

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

Example

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Parameterized Constructor

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

Example

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Using the Parameterized Constructor

public class Main {
    public static void main(String[] args) {
        Car myCar = new Car("Red", "Tesla", 2022);
        myCar.displayInfo();
    }
}
Enter fullscreen mode Exit fullscreen mode

Constructor Overloading

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

Example

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Using the Overloaded Constructors

public class Main {
    public static void main(String[] args) {
        Car defaultCar = new Car();
        defaultCar.displayInfo();

        Car myCar = new Car("Red", "Tesla", 2022);
        myCar.displayInfo();
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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
}
Enter fullscreen mode Exit fullscreen mode

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.

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

    // Constructor and methods
}
Enter fullscreen mode Exit fullscreen mode

Static Methods:

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

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

    // Constructor and other methods
}
Enter fullscreen mode Exit fullscreen mode

Accessing Static Members:

Static members are accessed using the class name, not through object references.

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

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:
   public class MyClass {
       public int publicVar;
       public void publicMethod() {
           // Method implementation
       }
   }
Enter fullscreen mode Exit fullscreen mode

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:
   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
       }
   }
Enter fullscreen mode Exit fullscreen mode

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:
   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
       }
   }
Enter fullscreen mode Exit fullscreen mode

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:
   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
       }
   }
Enter fullscreen mode Exit fullscreen mode

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:
   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
       }
   }
Enter fullscreen mode Exit fullscreen mode

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:
   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()
   }
Enter fullscreen mode Exit fullscreen mode

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:
   public abstract class Shape {
       abstract void draw(); // Abstract method
   }

   public class Circle extends Shape {
       @Override
       void draw() {
           // Method implementation
       }
   }
Enter fullscreen mode Exit fullscreen mode

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

public class SuperClass {
    // Fields and methods
}

public class SubClass extends SuperClass {
    // Fields and methods of SubClass
}
Enter fullscreen mode Exit fullscreen mode

Example

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.");
    }
}
Enter fullscreen mode Exit fullscreen mode

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.
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)
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

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.
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Syntax:

[AccessModifier] [AbstractOrFinal] class ClassName {
    // Class body
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Syntax:

class OuterClass {
    [AccessModifier] [static] [AbstractOrFinal] class NestedClassName {
        // Nested class body
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Syntax:

class ClassName {
    [AccessModifier] [static] [final] dataType attributeName [= initialValue];
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Syntax:

class ClassName {
    [AccessModifier] [static] [final|abstract] returnType methodName(paramType1 param1, paramType2 param2, ...) [throws ExceptionType] {
        // Method body
    }
}
Enter fullscreen mode Exit fullscreen mode

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:
  public class OuterClass {
      // Outer class members

      class InnerClass {
          // Inner class members
      }
  }
Enter fullscreen mode Exit fullscreen mode
  • Accessing Inner Class:
  OuterClass outer = new OuterClass();
  OuterClass.InnerClass inner = outer.new InnerClass();
Enter fullscreen mode Exit fullscreen mode

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:
  public class OuterClass {
      // Outer class members

      static class NestedStaticClass {
          // Static nested class members
      }
  }
Enter fullscreen mode Exit fullscreen mode
  • Accessing Static Nested Class:
  OuterClass.NestedStaticClass nested = new OuterClass.NestedStaticClass();
Enter fullscreen mode Exit fullscreen mode

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:
  public class OuterClass {
      // Outer class members

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

          LocalInnerClass inner = new LocalInnerClass();
          // Use local inner class instance
      }
  }
Enter fullscreen mode Exit fullscreen mode
  • 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:

public abstract class Animal {
    abstract void makeSound();  // abstract method

    void sleep() {  // concrete method
        System.out.println("Sleeping...");
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Abstract Methods

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

Example:

public abstract class Animal {
    abstract void makeSound();
}

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

4. Concrete Methods

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

Example:

public abstract class Animal {
    void eat() {
        System.out.println("Eating...");
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

6. Instantiating Abstract Classes

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

Example:

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
    }
}
Enter fullscreen mode Exit fullscreen mode

7. Constructors in Abstract Classes

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

Example:

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
    }
}
Enter fullscreen mode Exit fullscreen mode

8. Abstract Classes with Fields and Methods

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

Example:

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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
    }
}
Enter fullscreen mode Exit fullscreen mode

Syntax for Declaring Abstract Classes in Java

Keywords that can be used:

  1. Access modifier: public
  2. abstract

Order:

[public] abstract class ClassName
Enter fullscreen mode Exit fullscreen mode

Syntax:

[public] abstract class ClassName [extends SuperClass] [implements Interface1, Interface2, ...] {
    // Abstract class body
}
Enter fullscreen mode Exit fullscreen mode

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:
   abstract returnType methodName(parameters);
Enter fullscreen mode Exit fullscreen mode
  1. Concrete methods are declared normally.
  2. Abstract classes can have constructors, instance variables, and static members.
  3. Abstract classes cannot be instantiated directly.
  4. Subclasses of an abstract class must implement all its abstract methods, or be declared abstract themselves.

Example with methods and variables:

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

public interface Animal {
    void eat();  // abstract method
    void sleep();  // abstract method
}
Enter fullscreen mode Exit fullscreen mode

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:

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Multiple Interfaces

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

Example:

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

7. Constants

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

Example:

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

Syntax for Declaring Interfaces in Java

Keywords that can be used:

  1. Access modifier: public
  2. interface

Order:

[public] interface InterfaceName
Enter fullscreen mode Exit fullscreen mode

Syntax:

[public] interface InterfaceName [extends Interface1, Interface2, ...] {
    // Interface body
}
Enter fullscreen mode Exit fullscreen mode

Examples:

  1. public interface Drawable { }
  2. interface Runnable extends Executable { }

Components of an interface:

  1. Constants (implicitly public, static, and final):
   Type CONSTANT_NAME = value;
Enter fullscreen mode Exit fullscreen mode
  1. Abstract methods (implicitly public and abstract):
   returnType methodName(parameters);
Enter fullscreen mode Exit fullscreen mode
  1. Default methods (Java 8+):
   default returnType methodName(parameters) {
       // Method body
   }
Enter fullscreen mode Exit fullscreen mode
  1. Static methods (Java 8+):
   static returnType methodName(parameters) {
       // Method body
   }
Enter fullscreen mode Exit fullscreen mode

Example interface:

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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.
  public interface Comparable<T> {
      public int compareTo(T o);
  }
Enter fullscreen mode Exit fullscreen mode

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:

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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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.
  public interface Comparator<T> {
      int compare(T o1, T o2);
  }
Enter fullscreen mode Exit fullscreen mode

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:

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());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Methods

  • reversed(): Returns a comparator that imposes the reverse ordering of the current comparator.
  Comparator<Person> ageComparator = new AgeComparator();
  Comparator<Person> reverseAgeComparator = ageComparator.reversed();
Enter fullscreen mode Exit fullscreen mode

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.
  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}
      }
  }
Enter fullscreen mode Exit fullscreen mode
  • 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.
  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
      }
  }
Enter fullscreen mode Exit fullscreen mode
  • Use: Allows custom definition of equality for objects based on specific attributes or criteria.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .