Created: 2023-08-27 09:58
Status: #concept
Subject: Programming
Tags: Java Java Data Type Class Object-Oriented Programming Class Diagram Java Abstract Class Java Interface

Java Class

They define the blueprint and internal state of an Object

Syntax

<modifier>* class <class_name> {
    <attribute_declaration>*
    <constructor_declaration>*
    <method_declaration>*
}

Terminology

  1. Variables or Attributes declared inside a class are called instance variables.
  2. Constructor is the public method (with the same name as the Class) that is called to instantiate a class instance when the new keyword is used.
  3. Method is a Function on the object; it can have special access modifiers or be static and used with the Class.staticMethod() or default with classInstance.instanceMethod().
  4. Getters & Setters are methods that simply return a private instance variable.

Access Modifiers

They determine whether we can invoke or use classes, variables, or methods in our program.

Modifier Same Class Same Package Subclass Universe/Program
private Yes
default Yes Yes
protected Yes Yes Yes
public Yes Yes Yes Yes

Non-access Modifier

These keywords provide information and constraints on a class, method, or variable.

Constructors

A Constructor is invoked when we use the new Class() statement and returns a reference to an instantiated object based on the Class.

  • We can declare parameters that can be passed during a new Class(...params) Memory allocation invocation.
  • using Class variable only declares a reference type Pointer to a Class, but the new keyword allocates memory for its members and instantiates an object reference to return to the declaration.
  • any attribute that was left uninitialized will be set to null if it's a reference type or all bits will be 0 if it's a primitive value type.

class Animal {
  String name;

  // the constructor MUST MATCH the class name
  Animal(String n) {
      this.name = n;
  }
}

Animal pet = new Animal("Murphy");

Java Class new Instantiation.png

They are not inherited by any subclass that extends the class with them.

Therefore, we must call the super() function call in a new Constructor, or it will be implicitly called, starting from the most distant superclass.

The this Keyword behaves similarly to JavaScript which is a reference to the current instance of the class.

  • it cannot be used in a subclass unless we call super() in the constructor.
  • however, unlike JavaScript, we need to declare the Variables in the class that we will call with this.variable.

Destructors

Java does not have any explicit Destructor syntax like constructors, but we can use a method's .finalize() method to act like one.

  • the JVM runs the .finalize() method on every instance that is garbage collected.
  • we can @Override this behavior to be used as a destructor to close Database connections or files.

Constructor Overloading

Classes may contain multiple constructor definitions to have different ways to instantiate the class.

  • however, it's not possible to have the same parameters for two different constructors.

public Person(String name) {
        this.name = name;
        this.age = 0;
        this.weight = 0;
        this.height = 0;
    }

public Person(String name, int age) {
    this.name = name;
    this.age = age;
    this.weight = 0;
    this.height = 0;
}
public static void main(String[] args) {
    Person paul = new Person("Paul", 24);
    Person ada = new Person("Ada");

    System.out.println(paul); // Paul is 24 years old.
    System.out.println(ada);  // Eve is 0 years old.
}

Calling a Constructor from the Same Class

Just like how we can call super(...params) to use the parent class's constructor, we can use this(...params) to use the current class's other constructor.

  • similar to super(), we must call an overloaded constructor this() at the first part of the constructor before doing anything else.
  • this helps us prevent Code Duplication.

public Person(String name) {
    this(name, 0);
    // here the code of the second constructor is run, and the age is set to 0
}

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

Inheritance with extends

A subclass can extends a superclass to do Inheritance, carrying over all methods and attributes of the parent.

class Animal {
  String name;
}

class Dog extends Animal {
  Dog(String n) {
    name = n;
  }

  void bark() {
    System.out.println(this.name + " barked!");
  }
}

Methods

Methods are Functions defined and scoped in the Class definition.

  • they define the behavior of the class and its instances.
  • we can access methods with the Method Reference Operator ::.
    • Class::new returns the default constructor while Class::toString returns the overriden toString() method.

ArrayList<Integer> values = list.stream()
    .filter(value -> value > 5)
    .map(value -> value * 2)
    .collectnew);

Method Overriding

We can enforce Polymorphism by overriding previously defined class methods on subclasses.

  • we should use the @Override annotation on the line before the Overriden Method definition to let the Static Type Checker parse the code.

We CANNOT override variable attributes, only methods. Otherwise, it will violate the Liskov Substitution Principle.

Method Overloading

Similar to the Class Constructor methods, we can also define multiple function signatures to overload methods.

  • we cannot have two methods with the same parameters however.

public void growOlder() {
    this.age = this.age + 1;
}

public void growOlder(int years) {
    this.age = this.age + years;
}

Conventional Methods

These are some notable conventions most programmers implement in their classes.

Overriding the public String toString() Method

When we invoke System.out.println(ourObject), it will implicitly invoke ourObject.toString() to print it out an object's string representation.

  • we can override this behavior and print out different outputs depending on our object.

public class Account {

    private double balance;
    private String owner;

    public Account(String owner, double balance) {
        this.balance = balance;
        this.owner = owner;
    }

    @Override
    public String toString() {
        return this.owner + " balance: " + this.balance;
    }
}

Using .equals(Object obj) Instead of ==

Just like String values, we cannot directly do == on objects since that will compare their Memory Address instead of values.

To combat this, programmers implement a Class's own public boolean equals(Object obj) method.

  • this method is used by many other utilities like ArrayList's .contains(Object obj) method.
  • by default, every object has this method, but it is implemented in the same way == checks the reference, so we must define our own implementation.

The Algorithm to check if an Object is equal to the instance is as follows;

public boolean equals(Object compared) {
        // if the variables are located in the same position, they are equal
        if (this == compared) {
            return true;
        }

        // if the type of the compared object is not SimpleDate, the objects are not equal
        if (!(compared instanceof SimpleDate)) {
            return false;
        }

        // convert the Object type compared object
        // into a SimpleDate type object called comparedSimpleDate
        SimpleDate comparedSimpleDate = (SimpleDate) compared;

        // if the values of the object variables are the same, the objects are equal
        if (this.day == comparedSimpleDate.day &&
            this.month == comparedSimpleDate.month &&
            this.year == comparedSimpleDate.year) {
            return true;
        }

        // otherwise the objects are not equal
        return false;
    }

Compatibility with Collections.sort()

To make an object be sortable in a java.util.Collection, we need to implement the Comparable<T> Java Interface then @Override the int compareTo(T obj) method.

References