Java Programming Tutorial

A Comprehensive Guide for College Students

Introduction to Java

Java is a class-based, object-oriented programming language designed to be platform-independent. It follows the principle of "Write Once, Run Anywhere" (WORA).

Key Features of Java:

  • Object-Oriented
  • Platform Independent
  • Secure
  • Robust
  • Multi-threaded

Technical Definitions

JVM (Java Virtual Machine)
An abstract computing machine that provides a runtime environment in which Java bytecode can be executed. It enables Java's platform independence by acting as an intermediary between Java code and the operating system.
Bytecode
The intermediate code format that Java source code is compiled into. It's a low-level representation of Java programs that can be executed by the JVM.
Class
A blueprint or template for creating objects that defines their properties and behaviors. It's the fundamental building block of object-oriented programming in Java.
Object
An instance of a class that contains both data (attributes) and code (methods). Objects are used to model real-world entities in object-oriented programming.
Method
A block of code that performs a specific task. Methods are defined within classes and represent the behaviors that objects of that class can perform.
Constructor
A special method used to initialize objects. It's called when an object is created and can set initial values for object attributes.
Inheritance
A mechanism that allows a class to inherit properties and methods from another class. It promotes code reuse and establishes a relationship between parent and child classes.
Polymorphism
The ability of different classes to be treated as instances of the same class through base class/interface references. It allows the same method to behave differently based on the object that calls it.
Encapsulation
The bundling of data and the methods that operate on that data within a single unit (class), hiding internal details and providing an interface to interact with the object.
Interface
A contract that specifies what methods a class must implement. It defines a set of abstract methods that implementing classes must provide concrete implementations for.
Package
A namespace that organizes a set of related classes and interfaces. Packages help prevent naming conflicts and provide access protection.
Exception
An event that occurs during program execution that disrupts the normal flow of instructions. Java provides mechanisms to handle these exceptional conditions gracefully.
Garbage Collection
Automatic memory management in Java that identifies and removes objects no longer being used by the program, freeing up memory resources.
Thread
A lightweight unit of execution within a program. Java supports multithreading, allowing multiple threads to run concurrently within the same program.
API (Application Programming Interface)
A set of predefined classes and interfaces provided by Java that can be used to build applications. The Java API includes packages for GUI, networking, database connectivity, and more.

Java Basics

1. Basic Syntax

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

2. Data Types

Primitive Data Types:

byte

8-bit signed integer value

  • Range: -128 to 127
  • Default value: 0
  • Used for saving memory in large arrays
byte age = 25;
byte temperature = -10;

short

16-bit signed integer value

  • Range: -32,768 to 32,767
  • Default value: 0
  • Used for memory savings in specific cases
short population = 12000;
short temperature = -200;

int

32-bit signed integer value

  • Range: -231 to 231-1
  • Default value: 0
  • Most commonly used integer type
int population = 1000000;
int score = -50;

long

64-bit signed integer value

  • Range: -263 to 263-1
  • Default value: 0L
  • Used for very large integer values
long worldPopulation = 7800000000L;
long fileSize = 1234567890L;

float

32-bit floating-point number

  • Range: ±3.4e−038 to ±3.4e+038
  • Default value: 0.0f
  • Used for decimal numbers (6-7 decimal digits precision)
float price = 19.99f;
float temperature = -3.5f;

double

64-bit floating-point number

  • Range: ±1.7e−308 to ±1.7e+308
  • Default value: 0.0d
  • Used for precise decimal calculations (15-16 decimal digits precision)
double pi = 3.14159265359;
double scientificNotation = 1.23e-15;

boolean

Represents true/false values

  • Range: true or false only
  • Default value: false
  • Used for conditional logic and control flow
boolean isJavaFun = true;
boolean isCompleted = false;

char

16-bit Unicode character

  • Range: 0 to 65,535
  • Default value: '\u0000'
  • Used for storing single characters or Unicode symbols
char grade = 'A';
char symbol = '$';
char unicodeChar = '\u0041'; // Unicode for 'A'

3. Java Keywords

Keyword Category Description
public Access Modifier Accessible from any other class
private Access Modifier Accessible only within the declared class
protected Access Modifier Accessible within package and subclasses
if Control Flow Makes a conditional decision
else Control Flow Alternative path in conditional statement
switch Control Flow Multiple-choice decision statement
for Control Flow Loop with initialization, condition, and increment
while Control Flow Loops while condition is true
break Control Flow Exits a loop or switch statement
continue Control Flow Skips to next iteration of loop
int Data Type 32-bit integer value
boolean Data Type True or false value
char Data Type 16-bit Unicode character
byte Data Type 8-bit integer value
try Exception Handling Attempts to execute code that may throw exception
catch Exception Handling Handles exceptions thrown in try block
finally Exception Handling Always executes after try-catch
throw Exception Handling Throws an exception explicitly
class Class Related Declares a class
interface Class Related Declares an interface
extends Class Related Inherits from a class
implements Class Related Implements an interface
new Object Related Creates new object instance
this Object Related References current object
super Object Related References parent class
static Other Belongs to the class rather than instance
final Other Cannot be changed/inherited
void Other No return value
return Other Returns from a method

4. String Operations in Java

Strings in Java are immutable sequences of characters. The String class provides many useful methods for string manipulation.

String Creation

// Different ways to create strings
String str1 = "Hello World";              // String literal
String str2 = new String("Hello World");  // Using constructor
String str3 = String.valueOf(123);        // Converting other types to String
char[] chars = {'H', 'e', 'l', 'l', 'o'};
String str4 = new String(chars);          // From char array

String Concatenation

// Different ways to concatenate strings
String firstName = "John";
String lastName = "Doe";

// Using + operator
String fullName = firstName + " " + lastName;

// Using concat() method
String greeting = "Hello ".concat(firstName);

// Using StringBuilder (more efficient for multiple concatenations)
StringBuilder builder = new StringBuilder();
builder.append(firstName)
       .append(" ")
       .append(lastName);
String result = builder.toString();

String Methods

String text = "Hello World";

// Length and case operations
int length = text.length();          // Returns 11
String upper = text.toUpperCase();   // "HELLO WORLD"
String lower = text.toLowerCase();   // "hello world"

// Substring operations
String sub1 = text.substring(6);     // "World"
String sub2 = text.substring(0, 5);  // "Hello"

// Finding characters and substrings
char firstChar = text.charAt(0);     // 'H'
int index = text.indexOf("World");   // 6
boolean contains = text.contains("lo"); // true

// Trimming and replacing
String spacedText = "  Hello  ";
String trimmed = spacedText.trim();  // "Hello"
String replaced = text.replace('l', 'w'); // "Hewwo Worwd"

String Comparison

String str1 = "Hello";
String str2 = "hello";
String str3 = new String("Hello");

// Comparing strings
boolean equals = str1.equals(str2);        // false
boolean equalsIgnoreCase = 
    str1.equalsIgnoreCase(str2);          // true

// Compare with ==
boolean sameReference = (str1 == str3);    // false
boolean sameValue = str1.equals(str3);     // true

// Comparing order
int compare = str1.compareTo(str2);        // negative value
int compareIgnoreCase = 
    str1.compareToIgnoreCase(str2);        // 0

String Splitting and Joining

// Splitting strings
String sentence = "Java is awesome";
String[] words = sentence.split(" ");  // ["Java", "is", "awesome"]

// Joining strings
String[] fruits = {"apple", "banana", "orange"};
String joined = String.join(", ", fruits);  // "apple, banana, orange"

// Using StringJoiner
StringJoiner joiner = new StringJoiner(", ", "[", "]");
joiner.add("apple").add("banana").add("orange");
String result = joiner.toString();  // "[apple, banana, orange]"

String Formatting

String name = "John";
int age = 25;
double salary = 50000.50;

// Using String.format
String formatted = String.format(
    "Name: %s, Age: %d, Salary: %.2f", 
    name, age, salary
);

// Using printf
System.out.printf(
    "Name: %s, Age: %d, Salary: %.2f%n", 
    name, age, salary
);

// Using formatted strings (Java 15+)
String info = """
    Name: %s
    Age: %d
    Salary: %.2f
    """.formatted(name, age, salary);

5. The Object Class in Java

The Object class is the root of all classes in Java. Every class automatically inherits from Object, providing fundamental methods that all objects can use.

toString() Method

Returns a string representation of the object.

public class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

Person person = new Person("John", 30);
System.out.println(person);  // Person{name='John', age=30}

equals() Method

Compares two objects for equality. Should be overridden along with hashCode().

public class Student {
    private int id;
    private String name;
    
    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) 
            return false;
        
        Student student = (Student) obj;
        return id == student.id && 
               Objects.equals(name, student.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

hashCode() Method

Returns a hash code value for the object. Must be consistent with equals().

public class Product {
    private String code;
    private double price;
    
    public Product(String code, double price) {
        this.code = code;
        this.price = price;
    }
    
    @Override
    public int hashCode() {
        // Using Java 7+ method
        return Objects.hash(code, price);
        
        // Traditional way
        /*
        int result = 17;
        result = 31 * result + code.hashCode();
        result = 31 * result + Double.hashCode(price);
        return result;
        */
    }
}

clone() Method

Creates and returns a copy of the object. Requires implementing Cloneable interface.

public class Employee implements Cloneable {
    private String name;
    private Department dept;  // Another object
    
    public Employee(String name, Department dept) {
        this.name = name;
        this.dept = dept;
    }
    
    // Shallow copy
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    
    // Deep copy
    public Employee deepClone() throws CloneNotSupportedException {
        Employee cloned = (Employee) super.clone();
        cloned.dept = (Department) dept.clone();
        return cloned;
    }
}

getClass() Method

Returns the runtime class of the object. Used for reflection.

Object obj = new String("Hello");

// Get class information
Class cls = obj.getClass();
System.out.println(cls.getName());        // java.lang.String
System.out.println(cls.getSimpleName());  // String

// Check class properties
System.out.println(cls.isInterface());    // false
System.out.println(cls.getSuperclass());  // class java.lang.Object

// Get methods
Method[] methods = cls.getMethods();
for (Method method : methods) {
    System.out.println(method.getName());
}

finalize() Method

Called by garbage collector before object destruction (deprecated in newer versions).

public class ResourceHandler {
    private Resource resource;
    
    public ResourceHandler() {
        resource = new Resource();
    }
    
    // Note: finalize() is deprecated in newer Java versions
    // Use try-with-resources or explicit cleanup instead
    @Override
    protected void finalize() throws Throwable {
        try {
            if (resource != null) {
                resource.close();
            }
        } finally {
            super.finalize();
        }
    }
    
    // Modern approach: use AutoCloseable
    public class ModernResource implements AutoCloseable {
        @Override
        public void close() throws Exception {
            // Cleanup code here
        }
    }
}

wait(), notify(), and notifyAll()

Methods for thread synchronization and communication.

public class SharedResource {
    private boolean isDataReady = false;
    
    public synchronized void produceData() {
        isDataReady = true;
        notify();  // Notify one waiting thread
        // notifyAll();  // Notify all waiting threads
    }
    
    public synchronized void consumeData() {
        while (!isDataReady) {
            try {
                wait();  // Wait until notified
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        // Process data
        isDataReady = false;
    }
}

Object-Oriented Programming

Classes and Objects

public class Car {
    // Properties
    private String brand;
    private String model;
    
    // Constructor
    public Car(String brand, String model) {
        this.brand = brand;
        this.model = model;
    }
    
    // Method
    public void start() {
        System.out.println("The " + brand + " " + model + " is starting.");
    }
}

OOP Principles:

  • Encapsulation
  • Inheritance
  • Polymorphism
  • Abstraction

Encapsulation

The bundling of data and methods that operate on that data within a single unit (class), hiding internal details and restricting access to internal state.

  • Protects data from unauthorized access
  • Provides data hiding
  • Increases code maintainability
public class BankAccount {
    // Private fields (data hiding)
    private double balance;
    private String accountNumber;
    
    // Public constructor
    public BankAccount(String accountNumber) {
        this.accountNumber = accountNumber;
        this.balance = 0.0;
    }
    
    // Public methods (controlled access)
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
    
    public boolean withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            return true;
        }
        return false;
    }
    
    // Getter method
    public double getBalance() {
        return balance;
    }
}

Inheritance

A mechanism that allows a class to inherit properties and methods from another class, enabling code reuse and establishing relationships between classes.

  • Promotes code reuse
  • Establishes IS-A relationship
  • Supports method overriding
// Parent class
public class Animal {
    protected String name;
    protected int age;
    
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public void makeSound() {
        System.out.println("Some sound");
    }
}

// Child class inheriting from Animal
public class Dog extends Animal {
    private String breed;
    
    public Dog(String name, int age, String breed) {
        super(name, age);  // Call parent constructor
        this.breed = breed;
    }
    
    @Override
    public void makeSound() {
        System.out.println("Woof! Woof!");
    }
    
    public void fetch() {
        System.out.println(name + " is fetching the ball!");
    }
}

Polymorphism

The ability of different classes to be treated as instances of the same class through inheritance, allowing objects to take multiple forms.

  • Method overriding (Runtime polymorphism)
  • Method overloading (Compile-time polymorphism)
  • Increases code flexibility
// Example of both runtime and compile-time polymorphism
public class Shape {
    public double getArea() {
        return 0;
    }
}

public class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

public class Rectangle extends Shape {
    private double width;
    private double height;
    
    // Method overloading example
    public Rectangle(double size) {
        this.width = size;
        this.height = size;
    }
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double getArea() {
        return width * height;
    }
}

// Using polymorphism
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(4, 6);
System.out.println(circle.getArea());     // Uses Circle's getArea
System.out.println(rectangle.getArea());   // Uses Rectangle's getArea

Abstraction

The process of hiding complex implementation details and showing only the necessary features of an object, achieved through abstract classes and interfaces.

  • Reduces complexity
  • Provides implementation hiding
  • Supports interface-based programming
// Abstract class example
public abstract class Vehicle {
    protected String brand;
    
    public Vehicle(String brand) {
        this.brand = brand;
    }
    
    // Abstract method (must be implemented by subclasses)
    public abstract void start();
    
    // Concrete method
    public void stop() {
        System.out.println("Vehicle stopping...");
    }
}

// Interface example
public interface Flyable {
    void fly();
    void land();
}

// Concrete class implementing both
public class Airplane extends Vehicle implements Flyable {
    public Airplane(String brand) {
        super(brand);
    }
    
    @Override
    public void start() {
        System.out.println("Starting airplane engines...");
    }
    
    @Override
    public void fly() {
        System.out.println("Airplane is flying...");
    }
    
    @Override
    public void land() {
        System.out.println("Airplane is landing...");
    }
}

Advanced Concepts

1. Exception Handling

try {
    // Code that might throw an exception
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("Cannot divide by zero!");
} finally {
    System.out.println("This always executes");
}

2. Collections Framework

Common Collections:

  • ArrayList: Dynamic array implementation
  • HashMap: Key-value pairs
  • LinkedList: Doubly-linked list
  • HashSet: Unique elements

ArrayList<T>

A resizable array implementation of the List interface that allows dynamic growth.

  • Maintains insertion order
  • Fast random access O(1)
  • Good for frequent reading and random access
  • Not synchronized (not thread-safe)
// Creating an ArrayList
ArrayList<String> fruits = new ArrayList<>();

// Adding elements
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");

// Accessing elements
String firstFruit = fruits.get(0);  // Returns "Apple"

// Modifying elements
fruits.set(1, "Mango");  // Replaces "Banana" with "Mango"

// Removing elements
fruits.remove("Orange");  // Removes by value
fruits.remove(0);        // Removes by index

HashMap<K,V>

A hash table implementation of the Map interface that stores key-value pairs.

  • No guaranteed order
  • Fast access O(1) average case
  • Allows one null key and multiple null values
  • No duplicate keys allowed
// Creating a HashMap
HashMap<String, Integer> ages = new HashMap<>();

// Adding key-value pairs
ages.put("John", 25);
ages.put("Alice", 30);
ages.put("Bob", 35);

// Accessing values
int johnsAge = ages.get("John");  // Returns 25

// Checking if key exists
boolean hasAlice = ages.containsKey("Alice");  // Returns true

// Removing entries
ages.remove("Bob");  // Removes Bob's entry

// Iterating over entries
for (Map.Entry entry : ages.entrySet()) {
    System.out.println(entry.getKey() + " is " + entry.getValue() + " years old");
}

LinkedList<T>

A doubly-linked list implementation that can be used as a list or queue.

  • Maintains insertion order
  • Fast insertion/deletion at both ends O(1)
  • Slower random access O(n)
  • Good for frequent insertions/deletions
// Creating a LinkedList
LinkedList<String> tasks = new LinkedList<>();

// Adding elements
tasks.add("First Task");
tasks.addFirst("Priority Task");    // Adds at the beginning
tasks.addLast("Last Task");         // Adds at the end

// Accessing elements
String first = tasks.getFirst();    // Gets first element
String last = tasks.getLast();      // Gets last element

// Using as a Queue
tasks.offer("New Task");            // Adds to the end
String next = tasks.poll();         // Removes and returns first element

HashSet<T>

A collection that stores unique elements in a hash table.

  • No duplicate elements allowed
  • No guaranteed order
  • Fast operations O(1) average case
  • Allows one null element
// Creating a HashSet
HashSet<Integer> numbers = new HashSet<>();

// Adding elements (duplicates are ignored)
numbers.add(1);
numbers.add(2);
numbers.add(1);  // This won't be added (duplicate)

// Checking if element exists
boolean hasTwo = numbers.contains(2);  // Returns true

// Removing elements
numbers.remove(1);

// Checking size
int uniqueNumbers = numbers.size();  // Returns 1

TreeSet<T>

A NavigableSet implementation that stores elements in sorted order.

  • Elements stored in natural order
  • No duplicate elements allowed
  • Operations O(log n)
  • Null elements not allowed
// Creating a TreeSet
TreeSet<String> names = new TreeSet<>();

// Adding elements (automatically sorted)
names.add("Charlie");
names.add("Alice");
names.add("Bob");

// Getting elements
String first = names.first();     // Returns "Alice"
String last = names.last();       // Returns "Charlie"

// Getting ranges
Set<String> subset = names.subSet("A", "C");  // Names starting with A and B

PriorityQueue<T>

A queue that orders elements according to their natural order or a custom Comparator.

  • Elements processed by priority
  • Head is the smallest element
  • Operations O(log n)
  • No null elements allowed
// Creating a PriorityQueue
PriorityQueue<Integer> queue = new PriorityQueue<>();

// Adding elements
queue.offer(5);
queue.offer(1);
queue.offer(3);

// Accessing elements
int highest = queue.peek();    // Returns 1 (smallest)

// Removing elements
int next = queue.poll();       // Removes and returns 1

// Custom ordering (largest first)
PriorityQueue<Integer> reversed = new PriorityQueue<>(
    Collections.reverseOrder()
);

Practical Examples

Simple Calculator

public class Calculator {
    public double add(double a, double b) {
        return a + b;
    }
    
    public double subtract(double a, double b) {
        return a - b;
    }
    
    public double multiply(double a, double b) {
        return a * b;
    }
    
    public double divide(double a, double b) {
        if (b == 0) {
            throw new ArithmeticException("Cannot divide by zero");
        }
        return a / b;
    }
}

Best Practices:

  • Always comment your code
  • Follow naming conventions
  • Handle exceptions properly
  • Write clean, maintainable code
  • Test your code thoroughly