Notes: Object Oriented Java Programming with Java

B. Tech. Third Semester (Information Technology), Rashtrasant Tukadoji Maharaj University, Nagpur

Subject Code: BIT3T10

Syllabus

UNIT 1: Object Oriented Programming Fundamentals Object Oriented Programming features: Object Oriented Programming features: objects and classes, Abstraction, Encapsulation, Inheritance, Polymorphism, Characteristics of Java, Java Source File Structure – Compilation. Fundamental Programming Structures in Java, features of Java, Introduction of JDK, JRE and JVM, Operators and Data Types.

UNIT 2: Control Statements, String Handling & Arrays Control Statements: Selection statement, Looping/Iterative statements, Jump/ Transfer statements. Arrays: Declaration and initialization of an array, One Dimensional Array, Two-Dimensional Array. String Handling: String constructors, toString methods, Methods for String Comparison, Searching String and Modifying String. Command line arguments, static modifier, this keyword, Garbage collection, Method overloading.

UNIT 3: Inheritance and Package Inheritance: Inheritance fundamentals, Types of inheritance, Advantages and disadvantages of inheritance. Use of abstract modifiers, Method Overriding, super keyword, final modifier Packages: Package Fundamental, Types of Packages, importing packages.

UNIT 4: Interface and Exception Handling Interface: Concept of interface, advantages of interface, relationship between classes and interface, Exception Handling: Fundamental Exception type: Checked, Unchecked Exceptions, throw and throws keywords, creating user defined exceptions, Built-in Exceptions.

UNIT 5: Multithreading and Collection Fundamentals Threads and Multithreading: Fundamentals, Thread Life Cycle, Ways of creating threads, Thread priorities, Interthread Communication. Collection Framework: Difference between Array and Collection, List interface and its classes, Set interface and its classes, Map interface and its classes.

UNIT 1 – Object-Oriented Programming Fundamentals

Welcome to the foundational unit of Object-Oriented Programming (OOP) with Java. This unit introduces the core concepts of OOP and the basic building blocks of the Java programming language.

1. Object-Oriented Programming (OOP) Features

Object-Oriented Programming is a programming paradigm based on the concept of “objects,” which can contain data in the form of fields1 (often known as attributes or properties) and code in the form of procedures (often known2 as methods).

Objects and Classes

  • Class: A class is a blueprint or template for creating objects. It defines a set of attributes and methods that the3 objects of that class will have. For example, a Car class could define properties like color and brand, and methods like startEngine() and accelerate().
  • Object: An object is an instance of a class. It is a real-world entity that has a state and behavior. If Car is the class, then a specific red Toyota Corolla is an object of that class.

Example Code:

// A simple class definition for a Car
class Car {
    // Fields (attributes)
    String color;
    String brand;

    // Methods (behaviors)
    void startEngine() {
        System.out.println("The " + brand + " engine has started.");
    }

    void accelerate() {
        System.out.println("The " + color + " car is accelerating.");
    }

}

// A class to create and use Car objects
public class Garage {
    public static void main(String[] args) {
        // Create an object (instance) of the Car class
        Car myCar = new Car();
        myCar.brand = "Toyota";
        myCar.color = "Red";

        // Call methods on the object
        myCar.startEngine(); // Output: The Toyota engine has started.
        myCar.accelerate(); // Output: The Red car is accelerating.
    }

}

Abstraction

Abstraction means hiding complex implementation details and showing only the essential features of the object. It helps in managing complexity. For instance, you know how to drive a car without needing to know the inner workings of the combustion engine.

Encapsulation

Encapsulation is the bundling of data (attributes) and the methods that operate on that data into a single unit,4 i.e., a class. It also involves restricting direct access to some of an object’s components, which is a key principle for data protection. This is typically achieved using private access modifiers.

Example Code:

class Student {
    // Private fields - cannot be accessed directly from outside the class
    private String name;
    private int age;

    // Public getter method for name
    public String getName() {
        return name;
    }

    // Public setter method for name
    public void setName(String name) {
        this.name = name;
    }

    // Public getter for age
    public int getAge() {
        return age;
    }

    // Public setter for age with validation
    public void setAge(int age) {
        if (age > 0) { // Simple validation
            this.age = age;
        }
    }
}

public class School {
    public static void main(String[] args) {
        Student s1 = new Student();
        s1.setName("Alice");
        s1.setAge(21);

        System.out.println("Student Name: " + s1.getName());
        System.out.println("Student Age: " + s1.getAge());
    }
}

Inheritance

Inheritance is a mechanism where a new class (subclass or child class) derives attributes and methods from an existing class (superclass or parent class). This promotes code reusability. The extends keyword is used to achieve inheritance in Java.

Example Code:

// Superclass (Parent)
class Vehicle {
    String brand;

    void honk() {
        System.out.println("Honk, Honk!");
    }
}

// Subclass (Child) - inherits from Vehicle
class Motorcycle extends Vehicle {
    String model;

    void display() {
        System.out.println("Brand: " + brand + ", Model: " + model);
    }
}

public class Main {
    public static void main(String[] args) {
        Motorcycle myMotorcycle = new Motorcycle();
        myMotorcycle.brand = "Yamaha"; // Inherited from Vehicle
        myMotorcycle.model = "R15";
        myMotorcycle.honk(); // Inherited method
        myMotorcycle.display();
    }
}

Polymorphism

Polymorphism (from Greek, meaning “many forms”) is the ability of an object to take on many forms. In Java, it often refers to method overriding (runtime polymorphism), where a subclass provides a specific implementation of a method that is already provided by its superclass.5

Example Code:

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

// Subclass 1
class Dog extends Animal {
    @Override // Annotation indicating method override
    public void makeSound() {
        System.out.println("Woof Woof");
    }
}

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

public class Farm {
    public static void main(String[] args) {
        Animal myDog = new Dog(); // A Dog object is treated as an Animal
        Animal myCat = new Cat(); // A Cat object is treated as an Animal

        myDog.makeSound(); // Calls the Dog's version of makeSound() -> Woof Woof
        myCat.makeSound(); // Calls the Cat's version of makeSound() -> Meow
    }
}

2. Java Fundamentals

Characteristics of Java

  • Platform Independent: Java’s “Write Once, Run Anywhere” (WORA) philosophy is possible due to the Java Virtual Machine (JVM). Java code is compiled into platform-independent bytecode that can run on any machine with a JVM.
  • Object-Oriented: As we’ve seen, Java is fundamentally an object-oriented language.
  • Simple: Java was designed to be easy to learn, with a syntax similar to C++ but simplified to remove complex features like pointers and multiple inheritance.
  • Robust: Java has features like automatic memory management (garbage collection) and exception handling, which make it a reliable language.
  • Secure: Java’s security features are built into the language and the runtime system, including a bytecode verifier and a security manager.

Java Source File Structure and Compilation

A Java source file (with a .java extension) has the following structure:

  1. Package Declaration (Optional): package com.example;
  2. Import Statements (Optional): import java.util.List;
  3. Class Definition: public class MyClass { … }

Compilation Process:

  1. You write your code in a .java file (e.g., MyClass.java).
  2. You use the Java compiler (javac) to compile the source file: javac MyClass.java.
  3. If there are no errors, the compiler produces a .class file (e.g., MyClass.class). This file contains bytecode, which is the machine language for the JVM.
  4. You can then run the program using the java command, which starts the JVM: java MyClass.

Introduction to JDK, JRE, and JVM

These three components are crucial to the Java ecosystem.

  • JVM (Java Virtual Machine): The JVM is an abstract machine. It’s a specification that provides a runtime environment in which Java bytecode can be executed. It is what makes Java platform-independent.
  • JRE (Java Runtime Environment): The JRE is the software package that contains what is required to run a Java program. It includes the JVM, core classes, and supporting files. If you only want to run Java applications, you only need the JRE.
  • JDK (Java Development Kit): The JDK is a software development kit used to develop Java applications. It includes the JRE, a compiler (javac), a debugger, and other development tools. You need the JDK to write and compile Java code.

In short: JDK = JRE + Development Tools, and JRE = JVM + Core Libraries.

3. Fundamental Programming Structures in Java

Data Types

Java has two categories of data types:

1. Primitive Data Types: These are the most basic data types. There are eight of them:

  • byte: 8-bit integer.
  • short: 16-bit integer.
  • int: 32-bit integer (most commonly used for integer values).
  • long: 64-bit integer.
  • float: 32-bit floating-point number.
  • double: 64-bit floating-point number (most commonly used for decimal values).
  • char: 16-bit Unicode character.
  • boolean: Represents two values: true or false.

2. Reference/Object Data Types: These refer to objects.

  • Classes, Interfaces, Arrays, and Strings are reference data types. They are created using defined classes or arrays.

Operators

Operators are special symbols that perform specific operations on one, two, or three operands, and then return a6 result.

  • Arithmetic: + (addition), – (subtraction), * (multiplication), / (division), % (modulus/remainder).
  • Assignment: = (assignment), +=, -=, *=, /=.
  • Relational: == (equal to), != (not equal to), > (greater than), < (less than), >= (greater than or equal to), <= (less than or equal to).
  • Logical: && (logical AND), || (logical OR), ! (logical NOT).

Unary: + (unary plus), – (unary minus), ++ (increment), — (decrement).

Example Code:

public class DataTypesAndOperators {
    public static void main(String[] args) {
        // --- Data Types ---
        int age = 25;
        double salary = 65000.50;
        char initial = 'J';
        boolean isEmployed = true;
        String name = "John Doe"; // String is a reference type

        System.out.println("Name: " + name);
        System.out.println("Age: " + age);

        // --- Operators ---
        int a = 10;
        int b = 3;

        // Arithmetic
        System.out.println("a + b = " + (a + b)); // 13
        System.out.println("a % b = " + (a % b)); // 1 (remainder of 10/3)

        // Relational
        System.out.println("Is a > b? " + (a > b)); // true

        // Logical
        boolean condition1 = true;
        boolean condition2 = false;
        System.out.println("condition1 && condition2: " + (condition1 && condition2)); // false

        // Increment
        a++; // a is now 11
        System.out.println("New value of a: " + a);
    }
}

Sources:

  1. https://github.com/ShreyanshJoshi/CS-F213—OOP
  2. https://leportella.com/scala-iii/
  3. https://github.com/Hira990/Hira990
  4. https://www.scribd.com/document/794483636/kasi-puneeth-ram-report-6
  5. https://androiddevhub.com/java-encapsulation/
  6. https://www.simplilearn.com/tutorials/java-tutorial/java-encapsulation
  7. https://bscit.great-site.net/core-java/assignment-2-6/
  8. http://www.studiestoday.com/concept-informatics-practices-cbse-class-xi-informatics-practices-introduction-programming-concepts


UNIT 2 – Control Statements, String Handling & Arrays

This unit explores the fundamental structures that control the flow of execution in a Java program, how to manage collections of data using arrays, and how to work with text using Java’s String class.

1. Control Statements 📝

Control statements are the backbone of any program, allowing you to dictate the execution path based on certain conditions or to repeat a block of code.

Selection Statements

These statements select or ignore a block of code based on a condition.

  • if, if-else, if-else-if: The if statement executes a block of code if a condition is true. The if-else statement executes one block if the condition is true and another1 if it’s false. The if-else-if ladder allows for checking multiple conditions.

    Example Code:
    public class GradeChecker {
        public static void main(String[] args) {
            int score = 75;
            char grade;

            if (score >= 90) {
                grade = 'A';
            } else if (score >= 80) {
                grade = 'B';
            } else if (score >= 70) {
                grade = 'C';
            } else {
                grade = 'D';
            }
            System.out.println("Your grade is: " + grade); // Output: Your grade is: C
        }
    }
  • switch: The switch statement provides a clean way to handle multi-way branching based on the value of a variable (byte, short, char, int, String, enum).

    Example Code:
    public class DayOfWeek {
        public static void main(String[] args) {
            int day = 4;
            String dayString;

            switch (day) {
                case 1: dayString = "Monday"; break;
                case 2: dayString = "Tuesday"; break;
                case 3: dayString = "Wednesday"; break;
                case 4: dayString = "Thursday"; break;
                // ... other cases
                default: dayString = "Invalid day"; break;
            }
            System.out.println(dayString); // Output: Thursday
        }
    }

Looping / Iterative Statements

Loops are used to execute a block of code repeatedly.

  • for loop: Ideal when you know the number of iterations in advance.
  • while loop: Used when a loop needs to continue as long as a condition is true. The condition is checked before the loop body is executed.
  • do-while loop: Similar to a while loop, but the condition is checked after the loop body. This guarantees the loop body is executed at least once.

Example Code:

public class LoopExamples {
    public static void main(String[] args) {
        // for loop: Print numbers 1 to 5
        System.out.println("For Loop:");
        for (int i = 1; i <= 5; i++) {
            System.out.print(i + " ");
        }
        System.out.println(); // Newline

        // while loop:
        System.out.println("While Loop:");
        int count = 1;
        while (count <= 5) {
            System.out.print(count + " ");
            count++;
        }
    }
}

Jump / Transfer Statements

These statements transfer control to another part of the program.

  • break: Terminates the loop or switch statement immediately.
  • continue: Skips the current iteration of a loop and proceeds to the next one.
  • return: Exits from the current method and returns control to the caller.

Example Code:

public class JumpStatements {
    public static void main(String[] args) {
        System.out.println("Using break and continue:");
        for (int i = 1; i <= 10; i++) {
            if (i % 2 == 0) {
                continue; // Skip even numbers
            }
            if (i == 7) {
                break; // Exit the loop if i is 7
            }
            System.out.print(i + " "); // Prints 1 3 5
        }
    }
}

2. Arrays 🔢

An array is a container object that holds a fixed number of values of a single type.

Declaration and Initialization

You declare an array by specifying its type, followed by square brackets [], and its name. You initialize it using the new keyword to allocate memory.

Java

// Declaration
int[] numbers;

// Initialization (allocating memory for 5 integers)
numbers = new int[5];

// Declaration and Initialization in one line
String[] names = new String[3];

// Declaration, initialization, and assignment
double[] values = {10.5, 20.0, 33.1};

One-Dimensional Array

A simple, linear array.

Java

public class OneDArray {
    public static void main(String[] args) {
        // Declare and initialize an array of integers
        int[] scores = {98, 87, 92, 78, 85};

        System.out.println("Element at index 2: " + scores[2]); // 92

        // Iterate through the array using an enhanced for-loop
        System.out.print("All scores: ");
        for (int score : scores) {
            System.out.print(score + " ");
        }
    }
}

Two-Dimensional Array

An array of arrays, useful for representing grids or matrices.

Example Code:

public class TwoDArray {
    public static void main(String[] args) {
        // Declare and initialize a 2x3 integer matrix
        int[][] matrix = { {1, 2, 3}, {4, 5, 6} };

        // Access an element
        System.out.println("Element at row 1, column 2: " + matrix[1][2]); // 6

        // Iterate through the 2D array using nested loops
        for (int i = 0; i < matrix.length; i++) { // Iterate over rows
            for (int j = 0; j < matrix[i].length; j++) { // Iterate over columns
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println(); // Newline after each row
        }
    }
}

3. String Handling 🔡

In Java, String is an object that represents a sequence of characters. Strings are immutable, meaning once a String object is created, it cannot be changed.

String Constructors & toString()

  • Constructors: String objects can be created using a string literal (most common) or the new keyword. String s1 = “Hello”; // Literal String s2 = new String(“World”); // Using constructor
  • toString() method: This method returns the string representation of an object. It’s often overridden in custom classes to provide meaningful output.

Common String Methods

  • Comparison:
  • equals(Object anObject): Compares two strings for equality (case-sensitive).
  • equalsIgnoreCase(String anotherString): Compares two strings, ignoring case.
  • Searching:
  • indexOf(int ch): Returns the index of the first occurrence of a character.
  • contains(CharSequence s): Returns true if the string contains the specified sequence.
  • Modifying (these methods return a new string):
  • substring(int beginIndex, int endIndex): Extracts a part of the string.
  • toLowerCase() / toUpperCase(): Converts the string to lower or upper case.
  • trim(): Removes leading and trailing whitespace.
  • replace(char oldChar, char newChar): Replaces all occurrences of a character.

Example Code:

public class StringMethodsDemo {
    public static void main(String[] args) {
        String greeting = "  Hello, World!  ";

        System.out.println("Original: '" + greeting + "'");
        System.out.println("Length: " + greeting.length());
        System.out.println("Uppercase: " + greeting.toUpperCase());
        System.out.println("Trimmed: '" + greeting.trim() + "'");
        System.out.println("Substring (7-12): " + greeting.trim().substring(7, 12)); // "World"
        System.out.println("Contains 'World': " + greeting.contains("World")); // true
        System.out.println("Replaced 'l' with 'z': " + greeting.replace('l', 'z'));
    }
}

4. Other Key Concepts 🚀

Command-Line Arguments

These are arguments passed to the program when it’s run from the command line. They are stored in the String[] args array of the main method.

Example Code:

// Save as CommandLine.java
// Compile: javac CommandLine.java
// Run: java CommandLine arg1 arg2
public class CommandLine {
    public static void main(String[] args) {
        if (args.length > 0) {
            System.out.println("You provided " + args.length + " arguments:");
            for (String arg : args) {
                System.out.println(arg);
            }
        } else {
            System.out.println("No arguments were provided.");
        }
    }
}

static Modifier

The static keyword is used for members that belong to the class itself, rather than to an instance of the class.

  • Static variable: Shared by all instances of the class.

Static method: Can be called without creating an instance of the class (ClassName.methodName()).

Example Code:

class Thing {
    public String name;
    public static int count = 0; // Static variable

    public Thing(String name) {
        this.name = name;
        count++; // Increment the static counter
    }
    public static void showCount() { // Static method
        System.out.println("Total things created: " + count);
    }
}
// Usage:
// Thing t1 = new Thing("A");
// Thing t2 = new Thing("B");
// Thing.showCount(); // Output: Total things created: 2

this Keyword

The this keyword is a reference to the current object. It is used to:

  1. Disambiguate between instance variables and parameters with the same name.
  2. Call another constructor in the same class (constructor chaining).

Example Code:

class Employee {
    private String name;
    private int id;

    // Use 'this' to distinguish instance variable from parameter
    public Employee(String name, int id) {
        this.name = name;
        this.id = id;
    }
}

Garbage Collection

Garbage collection is Java’s automatic process of finding and deleting objects that can no longer be reached (referenced). This prevents memory leaks and means developers don’t have to manually deallocate memory. It runs in the background on the JVM.

Method Overloading

Method overloading is a feature that allows a class to have more than one method with the same name, as long as their parameter lists are different2 (i.e., different number of parameters, different types of parameters, or both).

Example Code:

class Calculator {
    // Overloaded add method
    public int add(int a, int b) {
        return a + b;
    }

    public double add(double a, double b) {
        return a + b;
    }

    public int add(int a, int b, int c) {
        return a + b + c;
    }
}

public class OverloadingDemo {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println(calc.add(5, 10));        // Calls the first method
        System.out.println(calc.add(3.5, 2.2));     // Calls the second method
        System.out.println(calc.add(1, 2, 3));      // Calls the third method
    }
}

Sources

  1. https://www.scribd.com/document/505418138/conditional-statement
  2. https://github.com/alexanderxablaza/javastudy
  3. http://npochina.com/hd/84.html
  4. https://github.com/AnkitAg2803/java-program
  5. https://brainly.com/question/39189441
  6. https://velog.io/@rowehnn/Java-%ED%95%A8%EC%88%98-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9overloading
  7. https://felixrante.com/mastering-java-object-oriented-programming/

UNIT 3 – Inheritance and Packages

This unit delves deeper into one of the core pillars of OOP—Inheritance. We will explore its mechanisms, keywords, and types. We will also learn how to organize and manage our code using Packages.

1. Inheritance 🧬

Inheritance is a fundamental mechanism in OOP that allows a new class (subclass) to acquire the properties (fields) and behaviors (methods) of an existing class (superclass). It represents an “is-a” relationship. For example, a Dog is an Animal.

Inheritance Fundamentals:

  • The subclass is also called the child class or derived class.
  • The superclass is also called the parent class or base class.
  • The extends keyword is used to establish the inheritance relationship.

Example Code:

// Superclass
class Animal {
    void eat() {
        System.out.println("This animal eats food.");
    }
}

// Subclass
// Dog inherits the eat() method from Animal
class Dog extends Animal {
    void bark() {
        System.out.println("The dog barks.");
    }
}

Types of Inheritance

Java supports the following types of inheritance:

  1. Single Inheritance: A subclass inherits from only one superclass. (e.g., Dog extends Animal).
  2. Multilevel Inheritance: A class inherits from a class, which in turn inherits from another class. (e.g., Puppy extends Dog, and Dog extends Animal).
  3. Hierarchical Inheritance: Multiple subclasses inherit from a single superclass. (e.g., Dog extends Animal, and Cat extends Animal).

Note: Java does not support Multiple Inheritance (a class extending more than one class directly) to avoid complexity and ambiguity (the “Diamond Problem”). This capability is achieved using interfaces.

Advantages and Disadvantages of Inheritance

Advantages:

  • Code Reusability: A child class can reuse the fields and methods of the parent class.
  • Method Overriding: Allows for runtime polymorphism, where a subclass can provide a specific implementation for an inherited method.
  • Logical Structure: Promotes a clean, hierarchical organization of code.

Disadvantages:Tight Coupling: The subclass is tightly coupled to the superclass. Changes in the superclass can unintentionally break the functionality of the subclass.

2. Key Concepts in Inheritance

Use of abstract Modifier

The abstract keyword is used to create classes and methods that are incomplete and are meant to be extended.

  • Abstract Class: An abstract class cannot be instantiated (you cannot create an object of it). It serves as a template for subclasses.
  • Abstract Method: An abstract method is a method declared without an implementation (no body, just a signature). Any non-abstract subclass must provide an implementation for all abstract methods of its superclass.

Example Code:

// Abstract class
abstract class Shape {
    // Abstract method (no body)
    abstract void draw();

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

class Circle extends Shape {
    // Must provide implementation for the abstract method
    @Override
    void draw() {
        System.out.println("Drawing a circle.");
    }
}

Method Overriding

Method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its superclass.1

Rules for Overriding:

  1. The method name must be the same.
  2. The parameter list must be the same.
  3. The return type must be the same (or a subtype, known as a covariant return type). The @Override annotation is not mandatory but is highly recommended to ensure you are correctly overriding a method.

super Keyword

The super keyword is a reference variable used to refer to the immediate parent class object.

  1. To call the superclass constructor: super() can be used to call the constructor of the parent class. It must be the first statement in the subclass constructor.
  2. To access superclass members: super.methodName() or super.fieldName can be used to access methods or fields of the parent class, especially when the subclass has a member with the same name.

Example Code:

class Person {
    String name;

    Person(String name) {
        this.name = name;
        System.out.println("Person constructor called.");
    }

    void greet() {
        System.out.println("Hello, I am a person.");
    }
}

class Employee extends Person {
    Employee(String name) {
        // 1. Calling the superclass constructor
        super(name);
        System.out.println("Employee constructor called.");
    }

    @Override
    void greet() {
        // 2. Accessing the superclass method
        super.greet();
        System.out.println("Specifically, I am an employee named " + name);
    }
}

final Modifier

The final keyword is used to apply restrictions.

  • final variable: Creates a constant. Its value cannot be changed once assigned.
  • final method: Prevents the method from being overridden by a subclass.
  • final class: Prevents the class from being inherited (subclassed). String is a good example of a final class in Java.

Example Code:

// final class cannot be extended
final class SuperComputer {
    final double PI = 3.14159; // final variable (constant)

    // final method cannot be overridden
    final void bootSequence() {
        System.out.println("Secure boot sequence initiated.");
    }
}

3. Packages 📦

A package in Java is a mechanism for organizing related classes and interfaces into a namespace. They help in preventing naming conflicts and controlling access to code.

Package Fundamentals:

  • Packages correspond to the directory structure of your file system. A package com.example.project would be located in a project folder, inside an example folder, inside a com folder.
  • The package statement must be the first line in a Java source file.

Types of Packages

  1. Built-in Packages (from the Java API): These are the libraries included with the JDK. Examples include:
  • java.lang: Contains fundamental classes like Object, String, System. This package is automatically imported into every Java program.
  • java.util: Contains utility classes like Scanner, List, Map.
  • java.io: For input and output operations.
  1. User-defined Packages: Packages created by developers to organize their own application code.

Importing Packages

The import statement is used to bring a class or an entire package into the scope of the current file, allowing you to use them without referring to their full, qualified name.

  • Importing a specific class: import java.util.Scanner;

Importing all classes in a package: import java.util.*; (Using the wildcard *). This is less specific but convenient.

Example Code:

// Declaring the file belongs to this package
package com.mycompany.app;

// Importing the Scanner class from the java.util package
import java.util.Scanner;

public class UserInputReader {
    public static void main(String[] args) {
        // We can now use Scanner without writing java.util.Scanner
        Scanner scanner = new Scanner(System.in);

        System.out.print("Enter your name: ");
        String name = scanner.nextLine();

        System.out.println("Hello, " + name + "!");
        scanner.close();
    }
}

Sources:

  1. https://leetcode.com/discuss/interview-question/3828150/oops-cheatsheet-for-interviews-30-questions
  2. https://www.toutiao.com/article/7270069728390218280/
  3. https://docs.pingcode.com/baike/403381

UNIT 4 – Interface and Exception Handling

This unit introduces two powerful concepts in Java: interfaces, which are key to achieving full abstraction and flexible design, and exception handling, which provides a structured way to manage errors that occur during program execution.

1. Interface 📝

An interface in Java is a reference type, similar to a class, that can contain only constants, method signatures, default methods, static methods,1 and nested types. It is a blueprint of a class that defines a2 “contract” of what a class can do, without specifying how it does it.

Concept of Interface

  • An interface is implicitly abstract. You cannot create an instance of an interface.
  • Methods in an interface are implicitly public and abstract.
  • Variables declared in an interface are implicitly public, static, and final (i.e., they are constants).
  • A class uses the implements keyword to “sign the contract” with an interface.

Example Code:

// Defining an interface
interface Drivable {
    // A constant (public static final by default)
    int MAX_SPEED = 120;

    // Abstract methods (public abstract by default)
    void start();
    void stop();
    void steer(String direction);
}

Advantages of Interface

  • Achieve 100% Abstraction: Interfaces define methods without any implementation, providing pure abstraction.
  • Enable Multiple Inheritance: A class can implement multiple interfaces, allowing it to inherit the abstract methods of all of them. This is how Java resolves the “diamond problem” of multiple class-based inheritance.
  • Support Loose Coupling: By programming to an interface rather than a specific implementation class, you can easily swap out the underlying implementation without changing the code that uses it.

Relationship Between Classes and Interface

A class that implements an interface must provide a concrete implementation for all the abstract methods declared in that interface.

Example Code:

// Interface defining a contract for any "drawable" object
interface Drawable {
    void draw(); // Abstract method
}

// Class implementing the interface
class Circle implements Drawable {
    // Providing the implementation for the draw method
    @Override
    public void draw() {
        System.out.println("Drawing a circle.");
    }
}

// Another class implementing the same interface
class Rectangle implements Drawable {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle.");
    }
}

public class ArtShow {
    public static void main(String[] args) {
        // Programming to the interface
        Drawable myDrawing = new Circle();
        myDrawing.draw(); // Output: Drawing a circle.

        myDrawing = new Rectangle();
        myDrawing.draw(); // Output: Drawing a rectangle.
    }
}

2. Exception Handling 🛡️

Exception handling is a powerful mechanism that handles runtime errors in a controlled manner, so that the normal flow of the application is not disrupted. The core idea is to “try” a block of code, “catch” any exceptions that occur, and optionally run cleanup code in a “finally” block.

Fundamental Exception Types

Exceptions in Java are objects that inherit from the Throwable class. The two main types are:

  1. Checked Exceptions: These are exceptions that are checked at compile-time. The compiler forces you to handle them using a try-catch block or by declaring the method with the throws keyword. They typically represent recoverable, external errors.
  • Examples: IOException, FileNotFoundException, SQLException.
  1. Unchecked Exceptions (Runtime Exceptions): These are exceptions that are not checked at compile-time. They usually occur due to programming errors.
  • Examples: NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException.

Example Code  (Handling a Built-in Exception)

public class ExceptionDemo {
    public static void main(String[] args) {
        try {
            // This code might cause an ArithmeticException
            int result = 10 / 0;
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            // This block executes if the exception occurs
            System.err.println("Error: Cannot divide by zero.");
        } finally {
            // This block always executes, whether an exception occurred or not
            System.out.println("This is the finally block.");
        }
        System.out.println("Program continues...");
    }
}

throw and throws Keywords

  • throw: The throw keyword is used to manually throw an exception object from within a method or block of code. It’s used to signal an error condition.

throws: The throws keyword is used in a method signature to declare that the method might throw one or more specified exceptions. It delegates the responsibility of handling the exception to the method that calls it.

Example Code:

public class BankAccount {
    // The method signature declares that it can throw an exception
    public static void validateAge(int age) throws IllegalArgumentException {
        if (age < 18) {
            // Manually throwing an exception object
            throw new IllegalArgumentException("Applicant is not old enough.");
        } else {
            System.out.println("Age is valid. Account can be created.");
        }
    }

    public static void main(String[] args) {
        try {
            validateAge(16);
        } catch (IllegalArgumentException e) {
            System.err.println("Account creation failed: " + e.getMessage());
        }
    }
}

Creating User-Defined Exceptions

You can create your own custom exceptions to handle application-specific errors. You do this by creating a class that extends Exception (for a checked exception) or RuntimeException (for an unchecked exception).

Example Code:

// 1. Create a custom exception class
class InsufficientFundsException extends Exception {
    public InsufficientFundsException(String message) {
        super(message); // Pass the message to the Exception superclass
    }
}

// 2. Use the custom exception in your application
class CheckingAccount {
    private double balance = 500.00;

    public void withdraw(double amount) throws InsufficientFundsException {
        if (amount > balance) {
            throw new InsufficientFundsException("Withdrawal amount exceeds balance.");
        }
        balance -= amount;
        System.out.println("Withdrawal successful. New balance: " + balance);
    }
}

// 3. Handle the custom exception
public class Bank {
    public static void main(String[] args) {
        CheckingAccount acc = new CheckingAccount();
        try {
            acc.withdraw(600.00);
        } catch (InsufficientFundsException e) {
            System.err.println("Transaction failed: " + e.getMessage());
        }
    }
}

Sources:

  1. https://www.geeksforgeeks.org/difference-between-abstract-class-and-interface-in-java/
  2. https://www.programmerpulse.com/articles/interfaces-default-methods-mro/
  3. https://github.com/dinexh/Java_Files
  4. https://www.scribd.com/document/706991579/Eroju-Java-Anadu-Repu-Bava-Antadu

Unit 5: Multithreading and Collection Framework

1. Fundamentals of Threads and Multithreading

Multithreading in Java allows multiple threads to execute concurrently within a single program, enabling tasks to run in parallel. A thread is the smallest unit of execution, and multithreading optimizes resource usage, especially on multi-core CPUs. It enhances application responsiveness (e.g., keeping a UI active while processing data) but requires careful management to avoid issues like race conditions or deadlocks.

Key Benefits:

  • Improved performance via parallel execution.
  • Efficient resource utilization.
  • Enhanced user experience with responsive applications.

Challenges:

  • Synchronization to prevent data inconsistencies.
  • Avoiding deadlocks and race conditions.

Sample Code: Identifying the Main Thread

public class MainThreadExample {
    public static void main(String[] args) {
        Thread currentThread = Thread.currentThread();
        System.out.println("Current thread: " + currentThread.getName());
    }
}

Explanation: This code retrieves and prints the name of the main thread (typically “main”), demonstrating how every Java program runs in a thread by default.

2. Thread Life Cycle

A thread in Java transitions through several states during its execution, defined by the Thread.State enum in the Java API (Thread.State):

  • NEW: Thread created but not started.
  • RUNNABLE: Thread is running or ready to run, awaiting CPU time.
  • BLOCKED: Thread is waiting for a monitor lock to enter a synchronized block/method.
  • WAITING: Thread waits indefinitely for another thread’s action (e.g., via wait() or join()).
  • TIMED_WAITING: Thread waits for a specified time (e.g., via sleep() or wait(timeout)).
  • TERMINATED: Thread has completed execution.

Sample Code: Observing Thread States

public class ThreadStateExample {
  public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
      try {
        Thread.sleep(2000); // Enters TIMED_WAITING state
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });
    System.out.println("State after creation: " + thread.getState()); // NEW
    thread.start();
    System.out.println("State after start: " + thread.getState()); // RUNNABLE
    Thread.sleep(100); // Allow thread to enter sleep
    System.out.println("State while sleeping: " + thread.getState()); // TIMED_WAITING
    thread.join(); // Wait for thread to finish
    System.out.println("State after completion: " + thread.getState()); // TERMINATED
  }
}

Explanation: This code creates a thread, starts it, makes it sleep, and checks its state at various points, illustrating transitions between NEW, RUNNABLE, TIMED_WAITING, and TERMINATED states.

3. Ways of Creating Threads

Java provides two primary ways to create threads (Java Multithreading):

  1. Extending the Thread Class:
    • Create a subclass of Thread.
    • Override the run() method.
    • Instantiate and call start().
  2. Implementing the Runnable Interface:
    • Implement Runnable and define run().
    • Pass the instance to a Thread constructor and call start().
    • Preferred due to flexibility and support for single inheritance.

Java 8+ also allows using lambda expressions for concise thread creation.

Sample Code:

// Extending Thread
class MyThread extends Thread {
  public void run() {
    System.out.println("Thread via extending Thread");
  }
}

// Implementing Runnable
class MyRunnable implements Runnable {
  public void run() {
    System.out.println("Thread via implementing Runnable");
  }
}

public class ThreadCreationExample {
  public static void main(String[] args) {
    // Using Thread subclass
    MyThread thread1 = new MyThread();
    thread1.start();

    // Using Runnable
    Thread thread2 = new Thread(new MyRunnable());
    thread2.start();

    // Using lambda (Java 8+)
    Thread thread3 = new Thread(() -> System.out.println("Thread via lambda"));
    thread3.start();
  }
}

Explanation: This code demonstrates three ways to create threads, showing output from each thread. The Runnable approach is more flexible as it avoids tying the class to the Thread hierarchy.

4. Thread Priorities

Threads in Java have priorities ranging from 1 (Thread.MIN_PRIORITY) to 10 (Thread.MAX_PRIORITY), with 5 (Thread.NORM_PRIORITY) as the default. Priorities influence thread scheduling, but the actual execution order depends on the operating system’s scheduler.

Sample Code: Setting Thread Priorities

public class ThreadPriorityExample {
  public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
      System.out.println("Thread 1 priority: " + Thread.currentThread().getPriority());
    });
    thread1.setPriority(Thread.MIN_PRIORITY); // 1
    Thread thread2 = new Thread(() -> {
      System.out.println("Thread 2 priority: " + Thread.currentThread().getPriority());
    });
    thread2.setPriority(Thread.MAX_PRIORITY); // 10
    thread1.start();
    thread2.start();
  }
}

Explanation: This code sets different priorities for two threads and prints them. Note that execution order may vary due to OS scheduling.

5. Interthread Communication

Threads communicate using wait(), notify(), and notifyAll() methods on shared objects within synchronized blocks. This is crucial for coordinating tasks, such as in the producer-consumer problem, where one thread produces data and another consumes it.

Sample Code: Producer-Consumer

import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumerExample {
  public static void main(String[] args) {
    Buffer buffer = new Buffer(5);
    Thread producer = new Thread(() -> {
      for (int i = 0; i < 10; i++) {
        try {
          buffer.produce(i);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    });
    Thread consumer = new Thread(() -> {
      for (int i = 0; i < 10; i++) {
        try {
          buffer.consume();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    });
    producer.start();
    consumer.start();
  }
}
class Buffer {
  private Queue < Integer > queue = new LinkedList < > ();
  private int capacity;
  public Buffer(int capacity) {
    this.capacity = capacity;
  }
  public synchronized void produce(int item) throws InterruptedException {
    while (queue.size() == capacity) {
      wait(); // Wait if buffer is full
    }
    queue.add(item);
    System.out.println("Produced: " + item);
    notify(); // Notify consumer
  }
  public synchronized void consume() throws InterruptedException {
    while (queue.isEmpty()) {
      wait(); // Wait if buffer is empty
    }
    int item = queue.poll();
    System.out.println("Consumed: " + item);
    notify(); // Notify producer
  }
}

Explanation: This code implements a producer-consumer scenario using a fixed-size buffer. The producer adds items, and the consumer removes them, with wait() and notify() ensuring proper coordination.

6. Collection Framework

The Java Collection Framework provides a standardized architecture for storing and manipulating groups of objects, offering more flexibility than arrays (Java Collections).

Difference between Array and Collection

FeatureArrayCollection
SizeFixed at creationDynamic, can grow/shrink
TypePrimitives and objectsObjects only (autoboxing for primitives)
FunctionalityBasic operationsRich methods (add, remove, etc.)
PerformanceFaster for primitivesOverhead due to object wrapping

Sample Code: Array vs Collection

import java.util.ArrayList;
import java.util.List;
public class ArrayVsCollection {
  public static void main(String[] args) {
    // Array
    int[] array = new int[5];
    array[0] = 1;
    array[1] = 2;
    // Collection
    List < Integer > list = new ArrayList < > ();
    list.add(1);
    list.add(2);
    list.add(3);
    System.out.println("Array: " + java.util.Arrays.toString(array));
    System.out.println("List: " + list);
  }
}

Explanation: This code shows arrays’ fixed size versus the dynamic nature of an ArrayList, which can grow as needed.

List Interface and Its Classes

The List interface supports ordered collections with duplicates. Key implementations:

  • ArrayList: Dynamic array, fast for random access, slower for insertions/deletions.
  • LinkedList: Doubly-linked list, fast for insertions/deletions, slower for random access.
  • Vector: Synchronized ArrayList, thread-safe but slower, less used in modern Java.

Sample Code: Using ArrayList

import java.util.ArrayList;
import java.util.List;
public class ArrayListExample {
  public static void main(String[] args) {
    List < String > list = new ArrayList < > ();
    list.add("Apple");
    list.add("Banana");
    list.add("Cherry");
    System.out.println("List: " + list);
    System.out.println("Element at index 1: " + list.get(1));
    list.remove(1);
    System.out.println("After removing index 1: " + list);
  }
}

Explanation: This code demonstrates adding, accessing, and removing elements in an ArrayList, showcasing its dynamic nature.

Set Interface and Its Classes

The Set interface ensures unique elements. Key implementations:

  • HashSet: Unordered, fast operations using a hash table.
  • LinkedHashSet: Maintains insertion order.
  • TreeSet: Maintains sorted order using a red-black tree.

Sample Code: Using HashSet

import java.util.HashSet;
import java.util.Set;
public class HashSetExample {
  public static void main(String[] args) {
    Set < String > set = new HashSet < > ();
    set.add("Apple");
    set.add("Banana");
    set.add("Apple"); // Duplicate ignored
    System.out.println("Set: " + set);
    System.out.println("Contains 'Banana': " + set.contains("Banana"));
    set.remove("Apple");
    System.out.println("After removing 'Apple': " + set);
  }
}

Explanation: This code shows that HashSet prevents duplicates and supports fast operations like add, contains, and remove.

Map Interface and Its Classes

The Map interface stores key-value pairs with unique keys. Key implementations:

  • HashMap: Unordered, fast operations.
  • LinkedHashMap: Maintains insertion or access order.
  • TreeMap: Keys sorted, slower operations.

Sample Code: Using HashMap

import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
  public static void main(String[] args) {
    Map < String, Integer > map = new HashMap < > ();
    map.put("Apple", 1);
    map.put("Banana", 2);
    map.put("Cherry", 3);
    System.out.println("Map: " + map);
    System.out.println("Value for 'Banana': " + map.get("Banana"));
    map.remove("Apple");
    System.out.println("After removing 'Apple': " + map);
  }
}

Explanation: This code demonstrates storing, retrieving, and removing key-value pairs in a HashMap, highlighting its efficiency for key-based access.

Question Bank: Object Oriented Programming using Java

Unit 1: Object-Oriented Programming Fundamentals

  1. What are the four main features of object-oriented programming (OOP)? Explain each with respect to Java.
  2. Differentiate between a class and an object in Java. Provide an example of each.
  3. Explain the role of abstraction in OOP. Write a Java program to demonstrate abstraction using an abstract class.
  4. How does encapsulation enhance security in Java programs? Provide a code example with private fields and public methods.
  5. What is polymorphism in Java? Describe the two types of polymorphism with examples.
  6. Explain the roles of JDK, JRE, and JVM in Java program execution.
  7. Write a Java program to demonstrate the use of arithmetic and logical operators with appropriate data types.
  8. What are the characteristics of Java that make it platform-independent? Explain with reference to bytecode.
  9. Describe the structure of a Java source file. Write a sample Java program and explain its compilation process.
  10. Write a Java program to create a Person class with fields for name and age, and demonstrate object creation and data access.

Unit 2: Control Statements, String Handling, and Arrays

  1. What are the differences between if-else and switch statements in Java? Provide scenarios where each is preferred.
  2. Write a Java program to print the first 10 Fibonacci numbers using a for loop.
  3. Explain the break and continue statements. Write a program to demonstrate their use in a while loop.
  4. Write a Java program to declare and initialize a two-dimensional array to store a 3×3 matrix and print its elements.
  5. What is the purpose of the toString() method in the String class? Write a program to compare two strings using equals().
  6. Write a Java program to search for a substring in a given string using indexOf() and modify it using replace().
  7. Explain the static modifier. Write a program to demonstrate a static method and variable in a class.
  8. What is the role of the this keyword in Java? Provide a code example to resolve variable shadowing.
  9. Write a Java program to accept command-line arguments and print them in reverse order.
  10. Explain method overloading. Write a program with an overloaded method to calculate the area of a circle and rectangle.

Unit 3: Inheritance and Packages

  1. What is inheritance in Java? List two advantages and one disadvantage of using inheritance.
  2. Write a Java program to demonstrate multilevel inheritance with three classes: Animal, Mammal, and Dog.
  3. Explain the super keyword in Java. Write a program to call a superclass constructor using super.
  4. What is the purpose of the final modifier? Provide examples of its use with a variable, method, and class.
  5. Write a Java program to create an abstract class Shape with an abstract method draw(), implemented by subclasses Circle and Rectangle.
  6. What are packages in Java? Explain the difference between built-in and user-defined packages.
  7. Write a Java program to create a class Calculator in a package math.utils and access it from another class.
  8. Explain method overriding with an example. How does it differ from method overloading?
  9. Write a Java program to demonstrate the use of protected access modifier in an inheritance hierarchy.
  10. What are the types of inheritance supported in Java? Write a program to demonstrate single inheritance.

Unit 4: Interfaces and Exception Handling

  1. What is an interface in Java? List two advantages of using interfaces over abstract classes.
  2. Write a Java program to create an interface Vehicle with a method start(), implemented by classes Car and Bike.
  3. Explain the difference between checked and unchecked exceptions in Java. Provide one example of each.
  4. Write a Java program to handle an ArrayIndexOutOfBoundsException using a try-catch block.
  5. What is the purpose of the throws keyword? Write a program that declares a method throwing an IOException.
  6. Write a Java program to create a custom exception InvalidAgeException and use it to validate an age input.
  7. Explain the role of the throw keyword. Write a program to throw a NullPointerException explicitly.
  8. What is the purpose of the finally block? Write a program to demonstrate its execution regardless of an exception.
  9. List three built-in exceptions in Java and their causes. Write a program to catch one of them.
  10. Write a Java program to demonstrate multiple catch clauses for handling different types of exceptions in a single try block.

Unit 5: Multithreading and Collection Framework

  1. What is multithreading in Java? Explain how it improves application performance.
  2. Describe the thread life cycle in Java, briefly explaining each state.
  3. Write a Java program to create a thread using the Runnable interface to print numbers from 1 to 10.
  4. Explain thread priorities in Java. Write a program to create two threads with different priorities and display their priorities.
  5. Write a Java program to implement a producer-consumer scenario using wait() and notify() for interthread communication.
  6. What is the difference between an array and a collection in Java? List two advantages of collections.
  7. Write a Java program to create an ArrayList, add elements, remove an element, and iterate using an Iterator.
  8. Explain the Set interface and its implementations. Write a program to demonstrate HashSet preventing duplicates.
  9. Write a Java program to create a HashMap, store student names with roll numbers, and retrieve a name by roll number.
  10. What are the differences between List, Set, and Map interfaces? Write a program to demonstrate one operation for each.

Author

  • Dr. Anil Warbhe is a freelance technical consultant and a passionate advocate for simplifying complex technologies. His expertise lies in developing custom mobile applications, websites, and web applications, providing technical consultancy on server administration, and offering insightful perspectives on current tech trends through his writing.

    View all posts

Leave a Reply

Your email address will not be published. Required fields are marked *