Why Java and History of Java
Definition
Java is a high-level, class-based, object-oriented programming language designed to have as few implementation dependencies as possible. It follows the principle of "Write Once, Run Anywhere" (WORA).
History
- Developed by James Gosling at Sun Microsystems
- First released in 1995
- Originally named "Oak", later renamed to "Java"
- Sun Microsystems was acquired by Oracle Corporation in 2010
- Current version: Java SE 21 (LTS) as of 2024
Key Features of Java
- Platform Independent: Java bytecode runs on any platform with JVM
- Object-Oriented: Everything in Java is an object (except primitives)
- Simple: Removed complex features like pointers and operator overloading from C++
- Secure: No explicit pointers, bytecode verification, security manager
- Robust: Strong type checking, exception handling, garbage collection
- Multithreaded: Built-in support for concurrent programming
- Architecture Neutral: No implementation-dependent features
- Distributed: Designed for distributed computing with RMI and EJB
Your First Java Program
// This is your first Java program
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
// Print numbers
System.out.println(42);
// Perform calculations
System.out.println(10 + 20);
}
}
Understanding the Program Structure
- public class HelloWorld: Declares a public class named HelloWorld. Every Java application must have at least one class.
- public static void main(String[] args): The entry point of any Java application. JVM calls this method to start execution.
- System.out.println(): Prints the argument to the console and moves to the next line.
- Comments: Lines starting with
//are single-line comments, ignored by the compiler.
More Examples - Working with Variables
public class VariablesDemo {
public static void main(String[] args) {
// Declaring and initializing variables
int age = 25;
double salary = 50000.50;
String name = "John Doe";
boolean isEmployed = true;
// Printing variables
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Salary: $" + salary);
System.out.println("Employed: " + isEmployed);
// Performing operations
int yearsToRetirement = 60 - age;
System.out.println("Years to retirement: " + yearsToRetirement);
}
}
Example: User Input and Processing
import java.util.Scanner;
public class UserInputDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Reading user input
System.out.print("Enter your name: ");
String name = scanner.nextLine();
System.out.print("Enter your age: ");
int age = scanner.nextInt();
// Processing and displaying
System.out.println("\\nWelcome, " + name + "!");
System.out.println("You are " + age + " years old.");
if (age >= 18) {
System.out.println("You are an adult.");
} else {
System.out.println("You are a minor.");
}
scanner.close();
}
}
Important Notes
- The filename must match the public class name exactly (case-sensitive) and have the
.javaextension. - Java is case-sensitive:
HelloWorldandhelloworldare different. - Every statement must end with a semicolon (
;). - Strings must be enclosed in double quotes (
"text"), while characters use single quotes ('a').
Practice Questions
Q1: Write a program that prints your name, age, and favorite programming language.
Q2: Create a program that calculates and displays the area of a rectangle (length × width).
Q3: Write a program that takes two numbers as input and prints their sum, difference, and product.
JVM, JRE, and JDK
JDK (Java Development Kit)
Definition: JDK is a software development environment used for developing Java applications. It includes the JRE plus development tools.
- Contains compiler (javac), debugger, and other development tools
- Required for developing Java applications
- Includes JRE and development tools
JRE (Java Runtime Environment)
Definition: JRE provides the runtime environment to execute Java applications. It contains the JVM and standard class libraries.
- Required to run Java applications
- Contains JVM and core libraries
- Does not include development tools
JVM (Java Virtual Machine)
Definition: JVM is an abstract machine that provides the runtime environment in which Java bytecode can be executed. It is platform-dependent.
- Executes Java bytecode
- Provides memory management (Garbage Collection)
- Platform-specific implementation
- Responsible for: Loading, Linking, and Initialization
Relationship
| Component | Contains | Purpose |
|---|---|---|
| JDK | JRE + Development Tools (javac, jar, javadoc) | Develop and Run Java programs |
| JRE | JVM + Library Classes | Run Java programs |
| JVM | Class Loader, Bytecode Verifier, Interpreter | Execute bytecode |
Java Compilation Process
- Source Code (.java): Write Java source code
- Compilation: javac compiles source to bytecode (.class)
- Bytecode (.class): Platform-independent intermediate code
- JVM Execution: JVM interprets/JIT compiles bytecode to machine code
- Output: Program executes on the host machine
# Compile Java source file
javac HelloWorld.java
# Run the compiled program
java HelloWorld
# Output: Hello, World!
JVM Architecture (Detailed)
The JVM consists of three main subsystems:
1. Class Loader Subsystem
- Loading: Reads .class files and loads them into memory
- Linking: Verifies bytecode, prepares memory for static variables, and resolves symbolic references
- Initialization: Executes static initializers and static blocks
2. Runtime Data Areas
- Method Area: Stores class-level data (class structure, method data, static variables)
- Heap: Stores objects and instance variables (garbage collection happens here)
- Stack: Stores method calls and local variables (one stack per thread)
- PC Register: Holds address of current instruction being executed
- Native Method Stack: For native (non-Java) method calls
3. Execution Engine
- Interpreter: Executes bytecode line by line (slower)
- JIT Compiler: Compiles frequently used code to native machine code (faster)
- Garbage Collector: Automatically manages memory by removing unused objects
Real-World Example: How Java Code Executes
public class Example {
static int count = 0; // Stored in Method Area
public static void main(String[] args) { // Stack frame created
int x = 10; // x stored in Stack
Example obj = new Example(); // obj reference in Stack, object in Heap
obj.display(x);
}
void display(int num) { // New stack frame created
System.out.println("Number: " + num);
// Stack frame destroyed after method completes
}
}
Compilation and Execution Process Example
# Step 1: Create Java source file
# Write your code in HelloWorld.java
# Step 2: Compile Java source file
javac HelloWorld.java
# Creates HelloWorld.class (bytecode file)
# Step 3: View bytecode (optional)
javap -c HelloWorld
# Shows the bytecode instructions
# Step 4: Run the compiled program
java HelloWorld
# JVM loads, verifies, and executes bytecode
# Output: Hello, World!
# The .class file can run on any platform with JVM
# Windows, Linux, Mac - all use the same .class file!
Advantages of This Architecture
- Platform Independence: Same bytecode runs on all platforms
- Security: Bytecode verifier checks code before execution
- Performance: JIT compiler optimizes frequently used code
- Memory Management: Automatic garbage collection prevents memory leaks
- Dynamic Linking: Classes loaded only when needed
Exam Tips
- JDK = JRE + Development Tools (javac, javadoc, jar, etc.)
- JRE = JVM + Core Libraries (java.lang, java.util, etc.)
- JVM is platform-dependent, but bytecode is platform-independent
- Bytecode verification happens during the linking phase
- Heap stores objects, Stack stores method calls and local variables
- Static variables are stored in Method Area, not Heap
Practice Questions
Q1: What is the difference between JIT compiler and interpreter in JVM?
Q2: Where are static variables stored in JVM memory?
Q3: Why is bytecode called platform-independent but JVM is platform-dependent?
Q4: What happens during the "linking" phase of class loading?
Java Source File Structure
Definition
A Java source file has a specific structure that must be followed. The file can contain package declaration, import statements, and class definitions in a specific order.
Structure Order (Must Follow)
- Package Statement (optional, must be first if present)
- Import Statements (optional)
- Class/Interface Declarations
// 1. Package declaration (optional, must be first)
package com.example.myapp;
// 2. Import statements (optional)
import java.util.Scanner;
import java.util.ArrayList;
// 3. Class declaration
public class SourceFileStructure {
// Fields (instance variables)
private String name;
private int age;
// Constructor
public SourceFileStructure(String name, int age) {
this.name = name;
this.age = age;
}
// Methods
public void display() {
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
// Main method - entry point
public static void main(String[] args) {
SourceFileStructure obj = new SourceFileStructure("John", 25);
obj.display();
}
}
Rules for Java Source Files
- Only one public class per source file
- Filename must match the public class name (case-sensitive)
- A source file can have multiple non-public classes
- Package statement must be the first statement (if present)
- Import statements come after package and before class declarations
Access Specifiers
| Modifier | Class | Package | Subclass | World |
|---|---|---|---|---|
| public | Yes | Yes | Yes | Yes |
| protected | Yes | Yes | Yes | No |
| default (no modifier) | Yes | Yes | No | No |
| private | Yes | No | No | No |
Common Mistake
Placing import statements before the package declaration will cause a compilation error. Always follow the order: package, imports, class.
Data Types in Java
Definition
Data types specify the type of data that can be stored in a variable. Java is a statically-typed language, meaning all variables must be declared with a data type before use.
Categories of Data Types
Java has two categories of data types:
- Primitive Data Types: Built-in types provided by Java (8 types)
- Reference Data Types: Objects, Arrays, Interfaces, Classes
Primitive Data Types
| Type | Size | Range | Default |
|---|---|---|---|
| byte | 1 byte | -128 to 127 | 0 |
| short | 2 bytes | -32,768 to 32,767 | 0 |
| int | 4 bytes | -2^31 to 2^31-1 | 0 |
| long | 8 bytes | -2^63 to 2^63-1 | 0L |
| float | 4 bytes | ~7 decimal digits | 0.0f |
| double | 8 bytes | ~15 decimal digits | 0.0d |
| char | 2 bytes | 0 to 65,535 (Unicode) | '\u0000' |
| boolean | 1 bit | true or false | false |
public class DataTypesDemo {
public static void main(String[] args) {
// Integer types
byte b = 100;
short s = 10000;
int i = 100000;
long l = 10000000000L; // Note: L suffix for long
// Floating-point types
float f = 3.14f; // Note: f suffix for float
double d = 3.14159265359;
// Character type
char c = 'A';
char unicode = '\u0041'; // Also 'A'
// Boolean type
boolean isJavaFun = true;
// Reference type (String is a class)
String name = "Java";
System.out.println("byte: " + b);
System.out.println("int: " + i);
System.out.println("double: " + d);
System.out.println("char: " + c);
System.out.println("boolean: " + isJavaFun);
}
}
Type Casting
public class TypeCasting {
public static void main(String[] args) {
// Widening (Implicit) - automatic
int intVal = 100;
double doubleVal = intVal; // int to double
System.out.println("Widening: " + doubleVal); // 100.0
// Narrowing (Explicit) - requires cast
double d = 9.78;
int i = (int) d; // double to int (truncates)
System.out.println("Narrowing: " + i); // 9
// String to int
String str = "123";
int num = Integer.parseInt(str);
// int to String
int value = 456;
String s = String.valueOf(value);
}
}
Variables
- Local Variables: Declared inside methods, must be initialized before use
- Instance Variables: Declared in class but outside methods, have default values
- Static Variables: Declared with static keyword, shared across all instances
Exam Tip
Remember the size hierarchy: byte (1) < short (2) < int (4) < long (8) and float (4) < double (8). char is 2 bytes because Java uses Unicode.
Operators in Java
Definition
Operators are special symbols that perform operations on operands (variables and values). Java provides a rich set of operators classified into several categories.
1. Arithmetic Operators
| Operator | Name | Example | Result |
|---|---|---|---|
| + | Addition | 5 + 3 | 8 |
| - | Subtraction | 5 - 3 | 2 |
| * | Multiplication | 5 * 3 | 15 |
| / | Division | 5 / 3 | 1 (integer division) |
| % | Modulus | 5 % 3 | 2 |
2. Relational Operators
| Operator | Name | Example |
|---|---|---|
| == | Equal to | a == b |
| != | Not equal to | a != b |
| > | Greater than | a > b |
| < | Less than | a < b |
| >= | Greater than or equal | a >= b |
| <= | Less than or equal | a <= b |
3. Logical Operators
public class LogicalOperators {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
// AND - both must be true
System.out.println("a && b: " + (a && b)); // false
// OR - at least one must be true
System.out.println("a || b: " + (a || b)); // true
// NOT - reverses the value
System.out.println("!a: " + !a); // false
// Practical example
int age = 25;
boolean hasLicense = true;
boolean canDrive = (age >= 18) && hasLicense;
System.out.println("Can drive: " + canDrive); // true
}
}
4. Assignment Operators
int x = 10;
x += 5; // x = x + 5; x = 15
x -= 3; // x = x - 3; x = 12
x *= 2; // x = x * 2; x = 24
x /= 4; // x = x / 4; x = 6
x %= 4; // x = x % 4; x = 2
5. Unary Operators
int a = 5;
// Pre-increment: increment first, then use
System.out.println(++a); // Output: 6
// Post-increment: use first, then increment
a = 5;
System.out.println(a++); // Output: 5
System.out.println(a); // Output: 6
// Pre-decrement and Post-decrement work similarly
int b = 10;
System.out.println(--b); // Output: 9
System.out.println(b--); // Output: 9
System.out.println(b); // Output: 8
6. Ternary Operator
// Syntax: condition ? value_if_true : value_if_false
int age = 20;
String status = (age >= 18) ? "Adult" : "Minor";
System.out.println(status); // Output: Adult
// Finding maximum
int a = 10, b = 20;
int max = (a > b) ? a : b;
System.out.println("Max: " + max); // Output: Max: 20
7. Bitwise Operators
| Operator | Name | Description |
|---|---|---|
| & | AND | Sets bit to 1 if both bits are 1 |
| | | OR | Sets bit to 1 if at least one bit is 1 |
| ^ | XOR | Sets bit to 1 if only one bit is 1 |
| ~ | NOT | Inverts all the bits |
| << | Left Shift | Shifts bits left, fills with 0 |
| >> | Right Shift | Shifts bits right, preserves sign |
Bitwise Operators Example
public class BitwiseDemo {
public static void main(String[] args) {
int a = 5; // Binary: 0101
int b = 3; // Binary: 0011
System.out.println("a & b: " + (a & b)); // 1 (0001)
System.out.println("a | b: " + (a | b)); // 7 (0111)
System.out.println("a ^ b: " + (a ^ b)); // 6 (0110)
System.out.println("~a: " + (~a)); // -6 (inverts all bits)
// Shift operators
System.out.println("a << 1: " + (a << 1)); // 10 (multiply by 2)
System.out.println("a >> 1: " + (a >> 1)); // 2 (divide by 2)
}
}
8. instanceof Operator
public class InstanceOfDemo {
public static void main(String[] args) {
String str = "Hello";
Integer num = 100;
Object obj = "Test";
// Check object types
System.out.println(str instanceof String); // true
System.out.println(num instanceof Integer); // true
System.out.println(obj instanceof String); // true
System.out.println(str instanceof Object); // true (String is subclass of Object)
}
}
Operator Precedence
Operators are evaluated in a specific order when multiple operators appear in an expression:
| Priority | Operators | Associativity |
|---|---|---|
| 1 (Highest) | Postfix (++, --) | Left to Right |
| 2 | Unary (++, --, +, -, !, ~) | Right to Left |
| 3 | Multiplicative (*, /, %) | Left to Right |
| 4 | Additive (+, -) | Left to Right |
| 5 | Shift (<<, >>, >>>) | Left to Right |
| 6 | Relational (<, >, <=, >=, instanceof) | Left to Right |
| 7 | Equality (==, !=) | Left to Right |
| 8 | Bitwise AND (&) | Left to Right |
| 9 | Bitwise XOR (^) | Left to Right |
| 10 | Bitwise OR (|) | Left to Right |
| 11 | Logical AND (&&) | Left to Right |
| 12 | Logical OR (||) | Left to Right |
| 13 | Ternary (?:) | Right to Left |
| 14 (Lowest) | Assignment (=, +=, -=, etc.) | Right to Left |
Complex Expressions Example
public class OperatorPrecedence {
public static void main(String[] args) {
// Understanding precedence
int result1 = 10 + 5 * 2;
System.out.println(result1); // 20, not 30 (* has higher precedence)
// Using parentheses to change order
int result2 = (10 + 5) * 2;
System.out.println(result2); // 30
// Complex expression
int a = 5, b = 10, c = 15;
int result3 = a + b * c / 3 - 2;
System.out.println(result3); // 5 + (10*15/3) - 2 = 5 + 50 - 2 = 53
// Combining logical operators
boolean flag = (a < b) && (b < c) || (a > c);
System.out.println(flag); // true
}
}
Common Pitfalls and Best Practices
Important Points
- Division by Zero: Integer division by zero throws ArithmeticException; floating-point returns Infinity or NaN
- Integer Division:
5/2returns2, not2.5(use5.0/2for decimal result) - Increment Operators: Use parentheses for clarity:
(++a)vs(a++) - Comparing Objects: Use
.equals()for content comparison, not==(which compares references) - Short-Circuit Evaluation:
&&and||stop evaluating once result is determined
Short-Circuit Evaluation Example
public class ShortCircuit {
public static void main(String[] args) {
int x = 5;
// && stops if first condition is false
if (x < 3 && ++x > 4) {
System.out.println("True");
}
System.out.println("x = " + x); // x = 5 (++x was not executed)
// || stops if first condition is true
if (x > 3 || ++x > 6) {
System.out.println("True");
}
System.out.println("x = " + x); // x = 5 (++x was not executed)
}
}
Practice Questions
Q1: What is the output of: int x = 5; System.out.println(x++ + ++x);
Q2: Calculate the result: int result = 10 + 5 * 2 / 4 - 1;
Q3: What's the difference between & and && operators?
Q4: Write a program to swap two numbers without using a third variable (use XOR).
Q5: What is the output of: 5 % -3 and -5 % 3?
Exam Tip
Understand the difference between ++i (pre-increment) and i++ (post-increment). In expressions, pre-increment increments first and then uses the value, while post-increment uses the value first and then increments.
Control Statements
Definition
Control statements determine the flow of execution of a program. They allow decision making, looping, and branching in code.
1. if-else Statement
public class IfElseDemo {
public static void main(String[] args) {
int age = 20;
// Simple if
if (age >= 18) {
System.out.println("You are an adult");
}
// if-else
if (age >= 18) {
System.out.println("Can vote");
} else {
System.out.println("Cannot vote");
}
// if-else-if ladder
int score = 85;
if (score >= 90) {
System.out.println("Grade: A");
} else if (score >= 80) {
System.out.println("Grade: B");
} else if (score >= 70) {
System.out.println("Grade: C");
} else {
System.out.println("Grade: F");
}
}
}
2. switch Statement
public class SwitchDemo {
public static void main(String[] args) {
int day = 3;
switch (day) {
case 1:
System.out.println("Monday");
break;
case 2:
System.out.println("Tuesday");
break;
case 3:
System.out.println("Wednesday");
break;
case 4:
System.out.println("Thursday");
break;
case 5:
System.out.println("Friday");
break;
default:
System.out.println("Weekend");
}
// Switch with String (Java 7+)
String fruit = "Apple";
switch (fruit) {
case "Apple":
System.out.println("Red fruit");
break;
case "Banana":
System.out.println("Yellow fruit");
break;
default:
System.out.println("Unknown fruit");
}
}
}
3. for Loop
public class ForLoopDemo {
public static void main(String[] args) {
// Basic for loop
for (int i = 1; i <= 5; i++) {
System.out.println("Count: " + i);
}
// Enhanced for loop (for-each)
int[] numbers = {10, 20, 30, 40, 50};
for (int num : numbers) {
System.out.println("Number: " + num);
}
// Nested for loop
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
System.out.print(i * j + " ");
}
System.out.println();
}
}
}
4. while Loop
public class WhileLoopDemo {
public static void main(String[] args) {
// while loop
int count = 1;
while (count <= 5) {
System.out.println("Count: " + count);
count++;
}
// do-while loop (executes at least once)
int num = 1;
do {
System.out.println("Number: " + num);
num++;
} while (num <= 5);
}
}
5. break and continue
public class BreakContinueDemo {
public static void main(String[] args) {
// break - exits the loop
for (int i = 1; i <= 10; i++) {
if (i == 5) {
break; // Exits loop when i is 5
}
System.out.println(i); // Prints 1, 2, 3, 4
}
// continue - skips current iteration
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue; // Skips when i is 3
}
System.out.println(i); // Prints 1, 2, 4, 5
}
}
}
Warning
Unlike Python, Java does not have an else clause for loops. The do-while loop is guaranteed to execute at least once, unlike the while loop which may not execute at all if the condition is initially false.
Arrays in Java
Definition
An array is a container object that holds a fixed number of values of a single type. The length of an array is established when the array is created and cannot be changed after creation.
Key Points
- Arrays are objects in Java
- Array size is fixed once created
- Arrays are zero-indexed (first element at index 0)
- Arrays can store primitives or objects
- The
lengthproperty gives the array size
Array Declaration and Initialization
public class ArrayDemo {
public static void main(String[] args) {
// Declaration and initialization
int[] numbers = new int[5]; // Array of 5 integers
// Assigning values
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
numbers[3] = 40;
numbers[4] = 50;
// Declaration with values (array literal)
int[] scores = {85, 90, 78, 92, 88};
// String array
String[] fruits = {"Apple", "Banana", "Cherry"};
// Accessing elements
System.out.println("First fruit: " + fruits[0]);
System.out.println("Array length: " + fruits.length);
// Iterating with for loop
for (int i = 0; i < scores.length; i++) {
System.out.println("Score " + i + ": " + scores[i]);
}
// Iterating with enhanced for loop
for (String fruit : fruits) {
System.out.println(fruit);
}
}
}
Multidimensional Arrays
public class MultiDimensionalArray {
public static void main(String[] args) {
// 2D array declaration
int[][] matrix = new int[3][3];
// 2D array with values
int[][] grid = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// Accessing elements
System.out.println("Element at [1][2]: " + grid[1][2]); // 6
// Iterating 2D array
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[i].length; j++) {
System.out.print(grid[i][j] + " ");
}
System.out.println();
}
// Jagged array (rows with different lengths)
int[][] jagged = new int[3][];
jagged[0] = new int[2];
jagged[1] = new int[4];
jagged[2] = new int[3];
}
}
Common Mistake
Accessing an array index that is out of bounds (negative or >= length) throws ArrayIndexOutOfBoundsException. Always check the array length before accessing elements.
Strings in Java
Definition
A String in Java is an object that represents a sequence of characters. Strings are immutable, meaning once created, their values cannot be changed.
Key Points
- Strings are objects of the
java.lang.Stringclass - Strings are immutable (cannot be modified after creation)
- String literals are stored in the String Pool
- Use
equals()for content comparison, not==
String Creation
public class StringDemo {
public static void main(String[] args) {
// String literal (stored in String Pool)
String s1 = "Hello";
// Using new keyword (creates new object in heap)
String s2 = new String("Hello");
// Concatenation
String fullName = "John" + " " + "Doe";
// String length
System.out.println("Length: " + s1.length()); // 5
// Comparison
System.out.println(s1 == s2); // false (different objects)
System.out.println(s1.equals(s2)); // true (same content)
System.out.println(s1.equalsIgnoreCase("HELLO")); // true
}
}
Common String Methods
public class StringMethods {
public static void main(String[] args) {
String str = "Hello World";
// Character access
char ch = str.charAt(0); // 'H'
// Substring
String sub = str.substring(0, 5); // "Hello"
// Case conversion
String upper = str.toUpperCase(); // "HELLO WORLD"
String lower = str.toLowerCase(); // "hello world"
// Trim whitespace
String padded = " Java ";
String trimmed = padded.trim(); // "Java"
// Replace
String replaced = str.replace('l', 'x'); // "Hexxo Worxd"
// Split
String csv = "apple,banana,cherry";
String[] parts = csv.split(","); // ["apple", "banana", "cherry"]
// Contains and indexOf
boolean contains = str.contains("World"); // true
int index = str.indexOf("World"); // 6
// StartsWith and EndsWith
boolean starts = str.startsWith("Hello"); // true
boolean ends = str.endsWith("World"); // true
// isEmpty and isBlank (Java 11+)
boolean empty = "".isEmpty(); // true
}
}
StringBuilder and StringBuffer
public class StringBuilderDemo {
public static void main(String[] args) {
// StringBuilder - mutable, not thread-safe, faster
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
System.out.println(sb.toString()); // "Hello World"
sb.insert(5, ","); // "Hello, World"
sb.reverse(); // "dlroW ,olleH"
sb.delete(0, 5); // ", olleH"
// StringBuffer - mutable, thread-safe, slower
StringBuffer buffer = new StringBuffer("Thread Safe");
}
}
| Feature | String | StringBuilder | StringBuffer |
|---|---|---|---|
| Mutability | Immutable | Mutable | Mutable |
| Thread Safety | Yes (immutable) | No | Yes (synchronized) |
| Performance | Slow for modifications | Fast | Slower than StringBuilder |
Exam Tip
Use StringBuilder for single-threaded string manipulation and StringBuffer for multi-threaded environments. Never use == to compare String content; always use equals().
Object-Oriented Programming Concepts
Definition
Object-Oriented Programming (OOP) is a programming paradigm based on the concept of "objects" which contain data (fields) and code (methods). Java is fundamentally an object-oriented language.
Four Pillars of OOP
- Encapsulation: Bundling data and methods that operate on the data within a single unit (class)
- Inheritance: Mechanism where one class acquires properties and behaviors of another class
- Polymorphism: Ability of an object to take many forms
- Abstraction: Hiding implementation details and showing only functionality
Classes and Objects
public class Student {
// Instance variables (fields)
private String name;
private int age;
private String rollNo;
// Constructor
public Student(String name, int age, String rollNo) {
this.name = name;
this.age = age;
this.rollNo = rollNo;
}
// Default constructor
public Student() {
this.name = "Unknown";
this.age = 0;
}
// Getter methods
public String getName() {
return name;
}
public int getAge() {
return age;
}
// Setter methods
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
if (age > 0) {
this.age = age;
}
}
// Method
public void display() {
System.out.println("Name: " + name + ", Age: " + age);
}
// Main method for testing
public static void main(String[] args) {
// Creating objects
Student s1 = new Student("John", 20, "CS101");
Student s2 = new Student();
s1.display();
s2.setName("Jane");
s2.display();
}
}
Inheritance
// Parent class (Superclass)
class Animal {
protected String name;
public void eat() {
System.out.println(name + " is eating");
}
public void sleep() {
System.out.println(name + " is sleeping");
}
}
// Child class (Subclass)
class Dog extends Animal {
public Dog(String name) {
this.name = name;
}
// Additional method
public void bark() {
System.out.println(name + " is barking");
}
// Method overriding
@Override
public void eat() {
System.out.println(name + " is eating dog food");
}
}
public class InheritanceDemo {
public static void main(String[] args) {
Dog dog = new Dog("Buddy");
dog.eat(); // Overridden method
dog.sleep(); // Inherited method
dog.bark(); // Own method
}
}
Polymorphism
class Shape {
public void draw() {
System.out.println("Drawing a shape");
}
}
class Circle extends Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
class Rectangle extends Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}
public class PolymorphismDemo {
public static void main(String[] args) {
// Runtime polymorphism
Shape shape1 = new Circle();
Shape shape2 = new Rectangle();
shape1.draw(); // Drawing a circle
shape2.draw(); // Drawing a rectangle
// Method overloading (compile-time polymorphism)
Calculator calc = new Calculator();
System.out.println(calc.add(5, 3));
System.out.println(calc.add(5.0, 3.0));
}
}
class Calculator {
// Method overloading
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
Abstract Classes and Interfaces
// Abstract class
abstract class Vehicle {
protected String brand;
public Vehicle(String brand) {
this.brand = brand;
}
// Abstract method (no implementation)
public abstract void start();
// Concrete method
public void stop() {
System.out.println(brand + " stopped");
}
}
// Interface
interface Electric {
void charge(); // implicitly public abstract
int VOLTAGE = 220; // implicitly public static final
}
// Concrete class implementing abstract class and interface
class Tesla extends Vehicle implements Electric {
public Tesla() {
super("Tesla");
}
@Override
public void start() {
System.out.println("Tesla starting silently");
}
@Override
public void charge() {
System.out.println("Charging at " + VOLTAGE + "V");
}
}
| Feature | Abstract Class | Interface |
|---|---|---|
| Methods | Can have abstract and concrete methods | Only abstract methods (before Java 8) |
| Variables | Can have any type of variables | Only public static final (constants) |
| Inheritance | Single inheritance only | Multiple interfaces can be implemented |
| Constructor | Can have constructors | Cannot have constructors |
| Keyword | extends | implements |
Detailed Example: Bank Account System
// Demonstrating all OOP principles
// Abstraction using abstract class
abstract class Account {
protected String accountNumber;
protected String holderName;
protected double balance;
public Account(String accountNumber, String holderName, double initialBalance) {
this.accountNumber = accountNumber;
this.holderName = holderName;
this.balance = initialBalance;
}
// Abstract method (must be implemented by subclasses)
public abstract void calculateInterest();
// Concrete method
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposited: $" + amount);
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("Withdrawn: $" + amount);
} else {
System.out.println("Insufficient balance");
}
}
public double getBalance() {
return balance;
}
}
// Inheritance and Polymorphism
class SavingsAccount extends Account {
private double interestRate;
public SavingsAccount(String accountNumber, String holderName, double balance, double interestRate) {
super(accountNumber, holderName, balance);
this.interestRate = interestRate;
}
@Override
public void calculateInterest() {
double interest = balance * interestRate / 100;
balance += interest;
System.out.println("Interest added: $" + interest);
}
}
class CurrentAccount extends Account {
private double overdraftLimit;
public CurrentAccount(String accountNumber, String holderName, double balance, double overdraftLimit) {
super(accountNumber, holderName, balance);
this.overdraftLimit = overdraftLimit;
}
@Override
public void withdraw(double amount) {
if (amount > 0 && amount <= (balance + overdraftLimit)) {
balance -= amount;
System.out.println("Withdrawn: $" + amount);
} else {
System.out.println("Exceeds overdraft limit");
}
}
@Override
public void calculateInterest() {
// No interest for current accounts
System.out.println("No interest for current accounts");
}
}
public class BankAccountSystem {
public static void main(String[] args) {
// Polymorphism: Parent reference to child object
Account savings = new SavingsAccount("SAV001", "John Doe", 1000, 5.0);
Account current = new CurrentAccount("CUR001", "Jane Smith", 500, 200);
// Demonstrating polymorphism
savings.deposit(500);
savings.calculateInterest();
System.out.println("Savings Balance: $" + savings.getBalance());
current.withdraw(600); // Uses overdraft
System.out.println("Current Balance: $" + current.getBalance());
}
}
Access Modifiers in Detail
| Modifier | Class | Package | Subclass | World |
|---|---|---|---|---|
| public | ✓ | ✓ | ✓ | ✓ |
| protected | ✓ | ✓ | ✓ | ✗ |
| default (no modifier) | ✓ | ✓ | ✗ | ✗ |
| private | ✓ | ✗ | ✗ | ✗ |
Method Overloading vs Overriding
class Calculator {
// Method Overloading (Compile-time Polymorphism)
public int add(int a, int b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
public double add(double a, double b) {
return a + b;
}
}
class Shape {
public void draw() {
System.out.println("Drawing a shape");
}
}
class Circle extends Shape {
// Method Overriding (Runtime Polymorphism)
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
class Rectangle extends Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}
public class PolymorphismDemo {
public static void main(String[] args) {
// Overloading example
Calculator calc = new Calculator();
System.out.println(calc.add(5, 3)); // 8
System.out.println(calc.add(5, 3, 2)); // 10
System.out.println(calc.add(5.5, 3.2)); // 8.7
// Overriding example (Runtime Polymorphism)
Shape shape1 = new Circle();
Shape shape2 = new Rectangle();
shape1.draw(); // Drawing a circle
shape2.draw(); // Drawing a rectangle
}
}
this and super Keywords
class Parent {
int value = 100;
public Parent() {
System.out.println("Parent constructor");
}
public void display() {
System.out.println("Parent display: " + value);
}
}
class Child extends Parent {
int value = 200;
public Child() {
super(); // Calls parent constructor
System.out.println("Child constructor");
}
public void display() {
System.out.println("Child value: " + this.value); // 200
System.out.println("Parent value: " + super.value); // 100
super.display(); // Calls parent method
}
}
public class ThisSuperDemo {
public static void main(String[] args) {
Child child = new Child();
child.display();
}
}
Static vs Instance Members
class Counter {
// Static variable (shared by all instances)
static int count = 0;
// Instance variable (unique to each instance)
int id;
public Counter() {
count++; // Increment shared counter
id = count; // Assign unique id
}
// Static method
public static int getCount() {
return count;
}
// Instance method
public void display() {
System.out.println("Object ID: " + id + ", Total objects: " + count);
}
}
public class StaticDemo {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();
c1.display(); // Object ID: 1, Total objects: 3
c2.display(); // Object ID: 2, Total objects: 3
c3.display(); // Object ID: 3, Total objects: 3
System.out.println("Total created: " + Counter.getCount()); // 3
}
}
final Keyword
final class FinalClass { // Cannot be extended
final int MAX_VALUE = 100; // Constant (cannot be changed)
final void display() { // Cannot be overridden
System.out.println("Final method");
}
}
class Example {
final int x; // Blank final variable
public Example(int x) {
this.x = x; // Must be initialized in constructor
}
}
Practice Questions
Q1: Create a class hierarchy for Vehicle → Car, Bike with appropriate properties and methods.
Q2: Implement a Shape class with calculateArea() method, then create Circle and Rectangle subclasses.
Q3: What's the difference between method overloading and method overriding? Provide examples.
Q4: Explain the use of 'this' and 'super' keywords with examples.
Q5: When would you use an abstract class vs an interface?
Q6: Create an Employee class with encapsulation (private fields, public getters/setters).
Q7: What is the output if you call a static method using an object reference? Is it allowed?
Exam Tip
Remember: Java supports single inheritance for classes but multiple inheritance through interfaces. From Java 8, interfaces can have default and static methods with implementations.
Packages, CLASSPATH, and JAR Files
Definition
A package in Java is a namespace that organizes classes and interfaces. Packages prevent naming conflicts and provide access control.
Types of Packages
- Built-in Packages: java.lang, java.util, java.io, etc.
- User-defined Packages: Created by programmers
Creating and Using Packages
// Package declaration (must match directory structure)
package com.example.util;
public class MathHelper {
public static int add(int a, int b) {
return a + b;
}
public static int multiply(int a, int b) {
return a * b;
}
}
// Import single class
import com.example.util.MathHelper;
// Import all classes from package
import java.util.*;
// Static import - import static members
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
public class Main {
public static void main(String[] args) {
// Using imported class
int sum = MathHelper.add(5, 3);
// Using static imports (no class name needed)
double area = PI * 5 * 5;
double root = sqrt(16);
// Using fully qualified name (no import needed)
java.util.Date date = new java.util.Date();
}
}
CLASSPATH
Definition: CLASSPATH is an environment variable that tells the JVM where to look for user-defined classes and packages.
# Windows
set CLASSPATH=.;C:\myproject\classes;C:\libs\mylib.jar
# Linux/Mac
export CLASSPATH=.:/home/user/classes:/home/user/libs/mylib.jar
# Using -cp or -classpath option
java -cp .;lib/mylib.jar Main
JAR Files
Definition: JAR (Java Archive) is a package file format used to aggregate many Java class files and associated resources into one file for distribution.
# Create a JAR file
jar cf myapp.jar *.class
# Create JAR with manifest
jar cfm myapp.jar manifest.txt *.class
# View contents of JAR
jar tf myapp.jar
# Extract JAR
jar xf myapp.jar
# Run executable JAR
java -jar myapp.jar
Common Built-in Packages
| Package | Description |
|---|---|
| java.lang | Core classes (String, Math, System) - auto-imported |
| java.util | Collections, Date, Scanner, Random |
| java.io | Input/Output classes |
| java.nio | New I/O classes |
| java.net | Networking classes |
| java.sql | Database connectivity (JDBC) |
Note
The java.lang package is automatically imported in every Java program. You do not need to explicitly import classes like String, System, or Math.