Object Oriented Programming in Java

Object Oriented Programming in Java

Part 1

Classes and Objects:

Class:

  • A class is a named group of properties (fields/attributes) and functions (methods).

  • It serves as a template for creating objects.

  • A class creates a new data type that can be used to instantiate objects.

  • It defines the structure and behavior of objects that belong to it.

Object:

  • An object is an instance of a class, representing a specific entity in a program.

  • When you declare an object of a class, you are creating an instance of that class.

  • Each object occupies space in memory and has its own unique identity.

Properties of Objects:

  • Objects are characterized by three essential properties:

    • State: Represents the current values of the object's attributes (data).

    • Identity: Distinguishes one object from another; it's where the object's value is stored in memory.

    • Behavior: Describes the actions or operations that an object can perform.

Logical vs. Physical:

  • A class is a logical construct, that defines the structure and behavior of objects.

  • An object has a physical reality, occupying space in memory and representing a specific instance of a class.

Let's illustrate the concepts of state, identity, and behavior with a simple example using a hypothetical Car class:

// The state of an object refers to the values stored in its attributes 
// or member variables. In the context of a Car object, the state could 
// include attributes such as make, model, color, and speed. 
class Car {
    String make;
    String model;
    String color;
    int speed;

    // The behavior of an object refers to the actions or operations that it 
    // can perform. These actions are typically defined by methods or member 
   // functions associated with the object's class. 
   // Continuing with the Car example, the behavior of a Car object could 
   // include methods like accelerate(), brake(), and changeColor()
     void accelerate() {
        // function
    }

    void brake() {
        // function
    }

    void changeColor(String newColor) {
        // function
    }
}
// The identity of an object distinguishes it from other objects and represents 
// its unique reference or location in memory. In Java, each object has a unique 
// identity that is provided by its memory address. 
// However, as Java abstracts memory management, you typically 
// don't work directly with memory addresses. 
// Instead, you can use object references to refer to individual objects.
Car myCar = new Car();

Constructors:

  • Constructors are special methods used for initializing objects.

  • They are invoked when an object is created using the new keyword.

  • Once defined, the constructor is automatically called when the object is created, before the new operator completes.

  • Constructors look a little strange because they have no return type, not even void.

  • This is because the implicit return type of a class’ constructor is the class type itself.

  • Default Constructor:

    • A constructor with no parameters.

    • If no constructor is explicitly defined in a class, Java provides a default constructor.

    • Example:

        // We dont have to make a constructor it is the by default constructor
        // It uses the parameter
      
        // CLASS
        public class Student {
            // data members, properties or instance variables
            int rno = 1;
            String name = "Demo";
            float marks = 1.0f;
            public static void main(String[] args) {
                Student mydemo = new Student();
                System.out.println(mydemo.rno);
                System.out.println(mydemo.name);
                System.out.println(mydemo.marks);
            }
        }
      
        // Output:
        1
        Demo
        1.0
      
  • Parameterized Constructor:

    • A constructor with parameters.

    • Used to initialize object properties with specified values.

    • Example:

        public class Student {
            // data members, properties or instance variables
            int rno = 1;
            String name = "Demo";
            float marks = 1.0f;
      
            Student (int rno,String name,float marks) {
                this.rno = rno;
                this.name = name;
                this.marks = marks;
                }
      
            public static void main(String[] args) {
                Student mydemo = new Student(1,"ABC",5.5f);
                System.out.println(mydemo.rno);
                System.out.println(mydemo.name);
                System.out.println(mydemo.marks);
            }
        }
      
        // Output
        1
        ABC
        5.5
      
  • Copy Constructor:

    • A constructor that takes an object of the same class as a parameter and initializes a new object with the same values.

    • Helps in creating a new object with the same state as an existing object.

    • Example:

        public class Student {
            // data members, properties or instance variables
            int rno = 1;
            String name = "Demo";
            float marks = 1.0f;
      
            Student (int rno,String name,float marks) {
                this.rno = rno;
                this.name = name;
                this.marks = marks;
                }
      
            Student (Student other){
                this.rno = other.rno;
                this.name = other.name;
                this.marks = other.marks;
            }
      
            public static void main(String[] args) {
                Student mydemo = new Student(1,"ABC",5.5f);
                System.out.println(mydemo.rno);
                System.out.println(mydemo.name);
                System.out.println(mydemo.marks);
      
                Student xyz = new Student(mydemo);
                System.out.println(xyz.rno);
                System.out.println(xyz.name);
                System.out.println(xyz.marks);
      
            }
        }
      
        // Output
        1
        ABC
        5.5
        1
        ABC
        5.5
      
  • Chained Constructors:

    • Calling one constructor from another using this() keyword.

    • Helps in code reuse and reducing redundancy.

    • Example:

        public class Student {
            // data members, properties or instance variables
            int rno = 1;
            String name = "Demo";
            float marks = 1.0f;
      
            Student (int rno,String name,float marks) {
                this.rno = rno;
                this.name = name;
                this.marks = marks;
                }
      
            Student (Student other){
                this.rno = other.rno;
                this.name = other.name;
                this.marks = other.marks;
            }
      
            Student () {
                this (22,"Default One",5.4f);
            }
      
            public static void main(String[] args) {
                Student xyz = new Student();
                System.out.println(xyz.rno);
                System.out.println(xyz.name);
                System.out.println(xyz.marks);
      
            }
        }
      
        // Output
        22
        Default One
        5.4
      

Parameters and Arguments:

Parameters are variables defined by a method that receive values when the method is called. Arguments are values passed to a method when it is invoked.

public class HelloWorld {
    public static void main(String[] args) {
        int x = 2;
        // Calling the method 'Square' and passing 'x' as an argument
        int ans = Square(x);
        System.out.println(ans);
    }
    // Method definition with a parameter 'x'
    // 'x' here is a parameter
    public static int Square(int x){
        return x * x;
    }
}

The 'this' Keyword:

  • The this keyword is used inside a method to refer to the current object.

  • It allows methods to access and manipulate the object's fields and methods.

  • Similar to self keyword in Python.

  •   public class Student {
          Student (int rno,String name,float marks) {
              this.rno = rno;
              this.name = name;
              this.marks = marks;
          }
      }
    

How to access instance variables?

The dot operator links the name of the object with the name of an instance variable. Although commonly referred to as the dot operator, the formal specification for Java categorizes the '.' as a separator.

class Car {
    String brand;
    String model;
    int year;

    void displayInfo() {
        System.out.println("Brand: " + brand);
        System.out.println("Model: " + model);
        System.out.println("Year: " + year);
    }
}

public class Main {
    public static void main(String[] args) {
        // Creating an instance of the Car class
        Car myCar = new Car();

        // Using dot operator to set the values of object fields
        myCar.brand = "Toyota";
        myCar.model = "Camry";
        myCar.year = 2022;

        // Using dot operator to call the method of the object
        myCar.displayInfo();
    }
}

New Keyword:

Car myCar = new Car();

The 'new' keyword dynamically allocates(that is, allocates at run time) memory for an object & returns a reference to it. This reference is, more or less, the address in memory of the object allocated by the new. This reference is then stored in the variable.

Thus, in Java, all class objects must be dynamically allocated.

Dynamic Memory Allocation and Object References in Java

Box myBox //Declaration
new Box(); //Instantiation
myBox = new Box(); // Assigned
  1. Declaration: Box myBox;

    • At this stage, you're only declaring a reference variable myBox of type Box. No memory has been allocated yet.
  2. Instantiation: new Box();

    • This is where memory is allocated for the Box object. The new keyword is responsible for this allocation.

    • Once new Box() is executed, memory is allocated on the heap for a new Box object.

    • The new operator returns the address (reference) of the newly allocated memory.

  3. Assignment: Box myBox = new Box();

    • Now, the reference returned by new Box() is assigned to the reference variable myBox.

    • So, myBox now holds the address of the newly created Box object in the heap.

  4. Object Reference:

    • Once memory is allocated, the reference variable holds the memory address (reference) of the actual object. This reference allows you to access and manipulate the object.

    •   Box myBox = new Box();
        // Here myBox is the reference variable (object reference)
        // Box() is the by-default constructor
      
  5. Safety of References:

    • Unlike pointers in languages like C or C++, Java's references cannot be manipulated to point to arbitrary memory locations. This ensures safety and prevents unauthorized memory access.

    • Java references are managed by the JVM, and you cannot perform low-level memory operations like you can with pointers.

  6. Object Assignment and Sharing:

    • Assigning one reference variable to another does not create a new object. Instead, both reference variables point to the same object in memory.

    •   Box b1 = new Box();
        Box b2 = b1; // b2 references the same object as b1
      
  7. Effects of Object Sharing:

    • Changes made to the object through one reference variable are reflected when accessed through the other reference variable, as they both point to the same object.

    • Changes made via b1 will affect the object accessed through b2, and vice versa.

    •   class Box {
            int width;
            int height;
      
            public Box() {
                width = 0;
                height = 0;
            }
      
            public Box(int width, int height) {
                this.width = width;
                this.height = height;
            }
        }
      
        public class ObjectSharingExample {
            public static void main(String[] args) {
                // Creating two Box objects
                Box b1 = new Box(10, 20);
                Box b2 = b1; // b2 references the same object as b1
      
                // Modifying b2's width
                b2.width = 30;
      
                // Displaying the values of b1 and b2
                System.out.println("b1: width = " + b1.width + ", height = " + b1.height);
                System.out.println("b2: width = " + b2.width + ", height = " + b2.height);
      
                // Modifying b1's height
                b1.height = 50;
      
                // Displaying the values of b1 and b2 again
                System.out.println("After modifying b1's height:");
                System.out.println("b1: width = " + b1.width + ", height = " + b1.height);
                System.out.println("b2: width = " + b2.width + ", height = " + b2.height);
            }
        }
      

The 'final' Keyword:

  • Field Declaration:

    • A field can be declared as final, preventing its contents from being modified, essentially making it a constant.

    • Example:

    •   public class Main {
            public static void main(String[] args) {
      
                final int FILE_OPEN = 2;
                FILE_OPEN = 3; //error
      
            }
        }
      
        // Output
        java: cannot assign a value to final variable FILE_OPEN
      
  • Immutability:

    • final guarantees immutability only when instance variables are primitive types, not reference types.

    • If an instance variable of a reference type is final, the reference to an object will never change, but the value of the object itself can change.

    •   public class Student {
            Student () {
                this (22,"Default One",5.4f);
            }
      
            Student (int rno,String name,float marks) {
                this.rno = rno;
                this.name = name;
                this.marks = marks;
            }
        }
            public static void main(String[] args) {
      
                // final int FILE_OPEN = 2;
                // FILE_OPEN = 3;
      
                final Student check = new Student();
      
                check.name = "AbhayGRT"; // this will work
                System.out.println(check.name);
                check.name = "AbhayGRT";
                System.out.println(check.name);
                check = new Student(); // but this will not
            }
        }
      
        // Output
        Default One
        AbhayGRT
        java: cannot assign a value to final variable check
      

The finalize() Method:

  • Finalization:

    • Java provides a mechanism called finalization to perform actions when an object is about to be reclaimed by the garbage collector.

    • To add a finalizer to a class, you define the finalize() method. It's called by the Java runtime just before the object is garbage-collected.

    • Garbage collection in Java is automatic and managed by the Java Virtual Machine (JVM).

    • The JVM decides when to run the garbage collector based on various factors such as memory pressure, object allocation rate, etc.

    • Unlike in languages like C++, where you can explicitly call the destructor or deallocate memory, Java does not provide a way to directly invoke the garbage collector.

    • Example:

        public class GarbageCollectorExample {
      
            public static void main(String[] args) {
                // Creating a large number of MyObject instances
                for (int i = 0; i < 1000000; i++) {
                    new MyObject(); // Creating and discarding MyObject instances
                }
            }
      
            // Inner class representing an object
            static class MyObject {
                // Override the finalize() method to define finalization behavior
                @Override
                protected void finalize() throws Throwable {
                    System.out.println("Object is Destroying"); // Finalization behavior
                }
            }
        }