Object-Oriented Programming in Java

Welcome to our lesson on Object-Oriented Programming (OOP) in Java! If you’re coming from JavaScript, you’ll find many similarities in the syntax, especially if you’re familiar with ES6+ classes. However, Java’s OOP model is more rigid and class-based, while JavaScript’s classes are still built on its prototype-based system. Let’s explore how Java handles OOP concepts, drawing comparisons with JavaScript’s class syntax to help you quickly grasp these ideas.

Classes and Objects: Java vs JavaScript

Both Java and modern JavaScript use the class keyword to define classes, but there are some key differences:

// Java
public class Car {
    private String model;
    private int year;

    public Car(String model, int year) {
        this.model = model;
        this.year = year;
    }
}

Car myCar = new Car("Tesla", 2023);
// JavaScript
class Car {
  constructor(model, year) {
    this.model = model;
    this.year = year;
  }
}

const myCar = new Car('Tesla', 2023);

While the syntax looks similar, Java requires explicit declaration of properties and their types. The public and private keywords in Java are access modifiers, which we’ll discuss shortly.

Constructors and the new Keyword

Constructors work similarly in both languages, but Java constructors must have the same name as the class:

// Java
public class Person {
    private String name;

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

Person john = new Person("John");
// JavaScript
class Person {
  constructor(name) {
    this.name = name;
  }
}

const john = new Person('John');

The new keyword instantiates a new object based on the class definition in both languages.

Access Modifiers

Java uses access modifiers to control the visibility of class members:

public class Employee {
    private String name;  // Only accessible within this class
    public int id;        // Accessible from anywhere
    protected double salary;  // Accessible in same package and subclasses
}

JavaScript doesn’t have built-in access modifiers, but it does offer private fields with the # prefix in recent versions:

class Employee {
  #name; // Private field
  id; // Public field

  constructor(name, id) {
    this.#name = name;
    this.id = id;
  }
}

Inheritance and the extends Keyword

Both Java and JavaScript use the extends keyword for inheritance:

// Java
public class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}

public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }
}
// JavaScript
class Animal {
  makeSound() {
    console.log('Some sound');
  }
}

class Dog extends Animal {
  makeSound() {
    console.log('Woof!');
  }
}

The main difference is that Java uses the @Override annotation to explicitly indicate method overriding.

Interfaces and Multiple Inheritance

Java uses interfaces to achieve a form of multiple inheritance:

// Java
interface Swimmable {
    void swim();
}

interface Flyable {
    void fly();
}

public class Duck implements Swimmable, Flyable {
    public void swim() {
        System.out.println("Duck is swimming");
    }

    public void fly() {
        System.out.println("Duck is flying");
    }
}

JavaScript doesn’t have a direct equivalent to interfaces. Although not seen often in the wild, you can achieve similar functionality using the mixin pattern:

// JavaScript
const Swimmable = {
  swim() {
    console.log('Swimming');
  },
};

const Flyable = {
  fly() {
    console.log('Flying');
  },
};

class Duck {}
Object.assign(Duck.prototype, Swimmable, Flyable);

Abstract Classes

Java supports abstract classes, which are a middle ground between interfaces and concrete classes:

// Java
abstract class Shape {
    abstract double area();

    public void display() {
        System.out.println("This is a shape");
    }
}

class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    double area() {
        return Math.PI * radius * radius;
    }
}

JavaScript doesn’t have built-in support for abstract classes, but you can simulate them:

// JavaScript
class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new TypeError('Cannot instantiate abstract class');
    }
  }

  area() {
    throw new Error("Method 'area()' must be implemented");
  }

  display() {
    console.log('This is a shape');
  }
}

class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }

  area() {
    return Math.PI * this.radius ** 2;
  }
}

Conclusion

In this lesson, we’ve explored the core concepts of Object-Oriented Programming in Java, drawing parallels with JavaScript’s class syntax. We’ve covered classes and objects, constructors, access modifiers, inheritance, interfaces, and abstract classes. While both languages now use similar syntax for defining classes, Java’s OOP model is more rigid and provides stronger encapsulation and type safety.

As you move forward, remember that JavaScript’s class syntax is built on top of its prototype-based inheritance system, while Java’s class-based system is fundamental to the language. This understanding will help you navigate the differences between the two languages as you continue to learn Java.

Next, we’ll dive into the Java Collections Framework, where you’ll learn about Java’s powerful data structures that build upon these OOP principles. Get ready to explore Lists, Sets, and Maps – Java’s equivalents to JavaScript’s arrays and objects, but with some exciting twists!