Week 2 - Methods & Parameters
Class 4 - Recording
▶ Watch VideoMethods & Parameters in Java
A method in Java is a reusable block of code that performs a specific task. Methods help you organize logic, reduce duplication, and make your programs easier to read and maintain.
1. What Is a Method?
General syntax of a method:
[access_modifier] [other_modifiers] return_type methodName(parameter_list) throws ExceptionType {
// method body
}
1.1 Basic Example
This method takes two integers and returns their sum. It can be called multiple times with different values.
public int add(int a, int b) {
return a + b;
}
// Example usage:
int result = add(5, 3); // result = 8
2. Parameters and Arguments
2.1 Parameter vs Argument
- Parameter – the variable in the method definition.
- Argument – the actual value passed when calling the method.
public void greet(String name) { // 'name' is a parameter
System.out.println("Hello, " + name);
}
// Example usage:
greet("Tech Tiger"); // "Tech Tiger" is an argument
// Output: Hello, Tech Tiger
2.2 Java Is Pass-by-Value (Very Important Concept)
Java is always pass-by-value. That means the method receives a copy of the value. For objects, the value being copied is a reference to the object.
2.2.1 Primitive Example (cannot change original)
public static void changeValue(int x) {
x = 99; // changes only the local copy
}
public static void main(String[] args) {
int a = 10;
changeValue(a);
System.out.println(a); // Output: 10 (still 10, not 99)
}
The variable a in main does not change because the method received
a copy of its value.
2.2.2 Reference Example (can change object state)
class Person {
String name;
}
public static void rename(Person p) {
p.name = "New Name"; // changes the object referenced by p
}
public static void main(String[] args) {
Person person = new Person();
person.name = "Old Name";
rename(person);
System.out.println(person.name); // Output: New Name
}
Here the reference value is copied into the method, but both references
point to the same object,
so changing p.name affects person.name.
2.3 Varargs (...)
Varargs allow you to pass 0 or more arguments of the same type to a method.
public static int sum(int... numbers) {
int total = 0;
for (int n : numbers) {
total += n;
}
return total;
}
public static void main(String[] args) {
System.out.println(sum()); // Output: 0 (no args)
System.out.println(sum(1, 2, 3)); // Output: 6
System.out.println(sum(10, 20, 30, 40));// Output: 100
}
- Only one vararg parameter is allowed per method.
- It must be the last parameter in the list.
2.4 final Parameters
Using final in parameters prevents reassigning the parameter variable inside
the method.
It does not make the object itself immutable.
public void process(final String input) {
// input = "something else"; // ❌ Compile-time error
System.out.println(input);
}
Return Types & Method Overloading
3. Return Types
Every non-void method must return a value that matches its declared return
type.
public double divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Divider cannot be zero");
}
return (double) a / b;
}
public void printHello() { // returns nothing
System.out.println("Hello");
}
If your method has a return type (like int, double, etc.),
you must return a value on every execution path.
4. Method Overloading
Overloading means having multiple methods with the same name but different parameter lists (different number of parameters, types, or their order).
public int multiply(int a, int b) {
return a * b;
}
public double multiply(double a, double b) {
return a * b;
}
public int multiply(int a, int b, int c) {
return a * b * c;
}
public static void main(String[] args) {
System.out.println(multiply(2, 3)); // Output: 6 (int version)
System.out.println(multiply(2.5, 3.0)); // Output: 7.5 (double version)
System.out.println(multiply(2, 3, 4)); // Output: 24 (three-arg version)
}
Java decides which overload to call at compile time, based on the argument types.
Exceptions and Methods
5. Exceptions and Methods
Methods can signal error conditions using exceptions. They can either
handle exceptions internally (with
try-catch) or declare that they throw them using the throws
clause.
5.1 Declaring Exceptions with throws
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public void readFile(String path) throws IOException {
Files.readAllLines(Paths.get(path)); // may throw IOException
}
public void doWork() {
try {
readFile("data.txt");
System.out.println("File read successfully.");
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
}
}
5.2 Checked vs Unchecked Exceptions
- Checked exceptions (subclasses of
ExceptionexceptRuntimeException) must be handled or declared. - Unchecked exceptions (subclasses of
RuntimeException) do not need to be declared.
public int parse(String s) {
// NumberFormatException is an unchecked exception (RuntimeException)
return Integer.parseInt(s);
}
public static void main(String[] args) {
System.out.println(parse("123")); // OK
System.out.println(parse("abc")); // Throws NumberFormatException at runtime
}
Core Method Concepts
6. Core Method Concepts
6.1 Static vs Instance Methods
Static methods belong to the class; instance methods belong to an object.
class MathUtil {
// Static method (no instance required)
public static int square(int x) {
return x * x;
}
// Instance method (needs an object)
public int triple(int x) {
return 3 * x;
}
}
public static void main(String[] args) {
// Call static method
System.out.println(MathUtil.square(4)); // Output: 16
// Call instance method
MathUtil util = new MathUtil();
System.out.println(util.triple(4)); // Output: 12
}
6.2 Scope and the Call Stack
Variables declared inside a method are local to that method and exist only while the method runs.
public void demo() {
int x = 10; // local variable
System.out.println(x); // Output: 10
} // x is destroyed after this method finishes
Each method call creates a new stack frame. Deep or infinite recursion can
cause
StackOverflowError.
6.3 Recursion Example
public static int factorial(int n) {
if (n < 0) {
throw new IllegalArgumentException("n must be >= 0");
}
if (n == 0 || n == 1) {
return 1; // base case
}
return n * factorial(n - 1);// recursive call
}
public static void main(String[] args) {
System.out.println(factorial(5)); // Output: 120
}
Advanced Method Topics
7. Interesting & Lesser-Known Topics
7.1 Covariant Return Types
When overriding a method, the subclass can return a more specific type (a subtype of the original return type).
class Animal {}
class Dog extends Animal {}
class AnimalFactory {
Animal create() {
return new Animal();
}
}
class DogFactory extends AnimalFactory {
@Override
Dog create() { // covariant return type (Dog is a subclass of Animal)
return new Dog();
}
}
This allows code that uses DogFactory to directly get a Dog
without casting.
7.2 Method References & Lambdas
Methods can be treated like values using method references, often used with streams and collections.
import java.util.Arrays;
import java.util.List;
public class MethodRefDemo {
public static void printUpper(String s) {
System.out.println(s.toUpperCase());
}
public static void main(String[] args) {
List<String> names = Arrays.asList("java", "python", "kotlin");
// Using lambda
names.forEach(s -> MethodRefDemo.printUpper(s));
// Using method reference
names.forEach(MethodRefDemo::printUpper);
// Output:
// JAVA
// PYTHON
// KOTLIN
}
}
7.3 Default Methods in Interfaces
Interfaces can have default method implementations, which helps add new behavior without breaking existing classes.
interface Logger {
default void log(String msg) {
System.out.println("[LOG] " + msg);
}
}
class Service implements Logger {
public void doWork() {
log("Service started");
}
}
public class Main {
public static void main(String[] args) {
Service s = new Service();
s.doWork(); // Output: [LOG] Service started
}
}
7.4 Overloading with Varargs (Ambiguity)
Overloading and varargs can create confusing situations. The compiler picks the most specific method.
public void print(int x) {
System.out.println("int");
}
public void print(Integer... x) {
System.out.println("Integer varargs");
}
public static void main(String[] args) {
Example e = new Example();
e.print(5); // Output: int (primitive int is more specific)
}
7.5 Shadowing and Hiding
A parameter or local variable can shadow a field with the same name.
class Example {
int value = 10;
public void show(int value) {
System.out.println(value); // parameter
System.out.println(this.value); // field
}
}
public static void main(String[] args) {
Example e = new Example();
e.show(5);
// Output:
// 5
// 10
}
Static methods are hidden, not overridden:
class A {
static void hello() {
System.out.println("Hello from A");
}
}
class B extends A {
static void hello() {
System.out.println("Hello from B");
}
}
public static void main(String[] args) {
A a = new B();
a.hello(); // Output: Hello from A (decided by reference type A)
}
7.6 Reflection: Calling Methods Dynamically
Reflection lets you inspect and invoke methods at runtime, even if you didn't know them at compile time. This is used heavily in frameworks (e.g., Spring), but it can be slower and harder to debug.
import java.lang.reflect.Method;
public class ReflectionDemo {
public void sayHi(String name) {
System.out.println("Hi, " + name);
}
public static void main(String[] args) throws Exception {
ReflectionDemo obj = new ReflectionDemo();
Class<?> clazz = obj.getClass();
Method m = clazz.getMethod("sayHi", String.class);
m.invoke(obj, "Tech Tiger"); // Output: Hi, Tech Tiger
}
}
Complete Example & Best Practices
8. Complete Example: Putting It All Together
This example combines several ideas: static methods, instance methods, parameters, return values, exceptions, and varargs.
public class MethodDocDemo {
// Static utility method with exception
public static double safeDivide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Divider cannot be zero");
}
return (double) a / b;
}
// Overloaded method with varargs
public static int sum(int... values) {
int total = 0;
for (int v : values) {
total += v;
}
return total;
}
// Instance method using recursion
public int factorial(int n) {
if (n < 0) {
throw new IllegalArgumentException("n must be >= 0");
}
if (n == 0 || n == 1) {
return 1;
}
return n * factorial(n - 1);
}
public static void main(String[] args) {
// Using static method
System.out.println("10 / 2 = " + safeDivide(10, 2)); // Output: 10 / 2 = 5.0
// Using varargs method
System.out.println("Sum = " + sum(1, 2, 3, 4, 5)); // Output: Sum = 15
// Using instance method
MethodDocDemo demo = new MethodDocDemo();
System.out.println("Factorial(5) = " + demo.factorial(5)); // Output: 120
}
}
Best Practices for Methods
- Use descriptive method names that clearly indicate what the method does
- Keep methods focused on a single task (Single Responsibility Principle)
- Avoid too many parameters - consider using objects or builder patterns
- Handle exceptions appropriately - use checked exceptions for recoverable errors
- Document complex methods with JavaDoc comments
- Make methods as simple and readable as possible
- Use static methods for utility functions that don't depend on instance state
Class 5 - Recording
▶ Watch VideoArrays & String Manipulation in Java
1. Arrays in Java
Arrays are one of the fundamental data structures in Java. They allow you to store multiple values of the same data type under one variable name. Arrays are fast, memory-efficient, and ideal when the number of elements is known beforehand. However, arrays are also fixed in size, meaning once you create them, you cannot increase or decrease their length.
1.1 What is an Array?
An array is a sequence of values stored in contiguous memory locations. Each element can be accessed using an index, starting from 0. Arrays store elements of a single data type, such as integers, strings, or objects. Because of their predictable structure, arrays offer very fast access time—accessing any element is always O(1).
int[] numbers = new int[5]; // creates an array with 5 integer slots
1.2 Declaring and Initializing Arrays
Declaration
Declaring an array only tells Java to expect an array variable—it does not create memory
yet.
Declaring does not allocate space until you use the new keyword or assign a
literal array.
int[] arr;
int arr2[]; // valid but less preferred
Allocation
Allocation creates memory for a specific number of elements. The number you provide becomes the array’s final size.
arr = new int[3]; // array of size 3
Declaration + Initialization
You can declare and populate an array in one step. This is common when you already know the values.
int[] nums = {10, 20, 30};
String[] names = new String[] {"Alice", "Bob", "Charlie"};
1.3 Accessing and Updating Elements
Elements are accessed by index. Trying to access an index that does not exist results in an
exception.
The length field provides the total number of array elements.
int[] nums = {10, 20, 30};
int first = nums[0]; // 10
nums[1] = 99; // update second element
int length = nums.length; // 3
1.4 Iterating Over Arrays
Classic for-loop
The traditional for-loop provides access to both the value and the index, which is helpful when you need to modify specific elements or use their positions.
for (int i = 0; i < nums.length; i++) {
System.out.println("Index " + i + ": " + nums[i]);
}
Enhanced for-loop
Also known as the “for-each loop,” this version focuses only on values, making it ideal for reading array elements without caring about indexes.
for (int value : nums) {
System.out.println(value);
}
1.5 Multi-Dimensional Arrays
Java supports multi-dimensional arrays, which can be visualized as tables or grids. These arrays are actually arrays of arrays.
int[][] matrix = new int[2][3]; // 2 rows, 3 columns
Jagged Arrays
Unlike some languages, Java allows different row lengths in multi-dimensional arrays. Such arrays are called jagged arrays. They give more flexibility and save memory if rows differ in size.
int[][] jagged = new int[3][];
jagged[0] = new int[]{1, 2};
jagged[1] = new int[]{3, 4, 5};
jagged[2] = new int[]{6};
1.6 Arrays Utility Class
The java.util.Arrays class provides powerful functions that save time and
reduce complexity.
These include sorting, searching, copying, filling, comparing, and converting arrays to
String.
Arrays.sort(nums); // ascending order
Arrays.copyOf(nums, 3); // copy first 3 elements
Arrays.fill(nums, 7); // fill array with value
1.7 Common Array Exceptions
ArrayIndexOutOfBoundsException
This occurs when you try to access an index outside valid boundaries (0 to length - 1). It is one of the most common beginner errors.
nums[3]; // invalid if array has only 3 elements
NullPointerException
Happens when the array reference itself is null and you try to access properties or elements.
int[] a = null;
a.length; // causes NPE
1.8 Less-Known Facts about Arrays
- Arrays are full-fledged objects in Java—even
int[]extendsObject. - The
lengthproperty of arrays is final, meaning array size cannot change once created. - Java arrays are covariant, meaning
String[]is a subtype ofObject[], but this can cause runtime errors. - Arrays of reference types store
nullby default.
2. Strings in Java
Strings are one of the most used data types in Java. A String represents a sequence of characters, and Java treats Strings as immutable objects. This immutability is a core design principle that ensures security, thread safety, and optimization.
2.1 What is a String?
A String stores characters using UTF-16 encoding. Each character is stored in one or two bytes, depending on its Unicode value. Java stores String literals in a special area called the "String Pool" to reduce memory usage and avoid duplicate objects.
2.2 Creating Strings
Strings can be created as literals or using the new keyword.
Literal Strings are stored in a shared pool, while new String() always creates
a fresh object on the heap.
String s1 = "Hello"; // uses String Pool
String s2 = new String("Hello"); // always new object
2.3 Basic String Operations
Concatenation
Combining multiple strings:
String c = "Hello" + " World";
String d = "Hello".concat(" World");
Length & Character Access
String str = "Java";
str.length(); // number of characters
str.charAt(1); // returns 'a'
Substring Extraction
Creates a new string from a portion of an existing one:
t.substring(0, 5);
t.substring(6);
Search Operations
Locate characters or sequences inside the string:
text.indexOf('a');
text.lastIndexOf("desh");
Replacing Text
"banana".replace("na","NA");
2.4 Comparing Strings
equals() vs ==
== compares object references, not content.
equals() compares character sequences.
Case-insensitive comparison
a.equalsIgnoreCase(b);
2.5 Useful String Methods
trim()removes leading and trailing spaces.toUpperCase()andtoLowerCase()startsWith()andendsWith()contains()to check substring existence.split()to convert text into array pieces.isEmpty()to check if length == 0.
2.6 StringBuilder & StringBuffer
Since Strings are immutable, frequent modifications (like inside loops) create many
unnecessary objects.
StringBuilder and StringBuffer solve this by providing mutable
character sequences.
StringBuilder (fast, not thread-safe)
StringBuilder sb = new StringBuilder();
sb.append("Hello").append(" World");
StringBuffer (thread-safe)
StringBuffer sbuf = new StringBuffer();
sbuf.append("Safe Threads");
2.7 Strings & Character Arrays
Converting to a char array is useful for low-level manipulation, like reversing strings or masking passwords.
char[] chars = "Hello".toCharArray();
String rebuilt = new String(chars);
2.8 Common String Exceptions
StringIndexOutOfBoundsException
Occurs when accessing an invalid index:
"Java".charAt(4);
NullPointerException
String s = null;
s.length();
2.9 Less-Known String Facts
- Strings are stored in the String Pool to save memory.
- Strings are immutable, which increases security and reduces bugs.
- Some characters require two
charvalues due to UTF-16 surrogate pairs. intern()allows manual control of the String Pool.
3. Arrays & Strings Together
3.1 Splitting Strings into Arrays
Useful for reading CSV data, parsing input, etc.
String[] fruits = "apple,banana,orange".split(",");
3.2 Joining Arrays into Strings
String.join(" ", new String[]{"Java","is","fun"});
3.3 Reversing a String using Arrays
char[] c = "hello".toCharArray();
// reverse logic
String reversed = new String(c);
4. Best Practices Summary
- Use arrays when the size is fixed and performance is critical.
- Use ArrayList when size changes dynamically.
- Always use
equals()(not==) for string comparison. - Use StringBuilder for heavy string manipulation.
- Always check for null before using string methods.
- Prefer enhanced for-loop for readability.
- Use
Arrays.toString()for printing arrays.
Class 6 - Recording
▶ Watch VideoClass in Java
Definition
A class in Java is a blueprint/template used to create objects. It contains:
- Variables (fields)
- Methods (behaviors)
- Constructors
- Blocks
- Inner classes
Simple Class Example
class Car {
String brand;
int speed;
void run() {
System.out.println(brand + " is running at " + speed + " km/h");
}
}
Real World Explanation
The Car class is like a car blueprint. A blueprint is not a real car; it only describes what a car should have.
- brand → like "Toyota", "BMW"
- speed → how fast it can move
- run() → behavior of the car
Types of Classes (All Types With Real-World Examples)
1. Concrete (Regular) Class
class Employee {
String name;
int id;
}
Real World: A form used to create real employees.
2. Abstract Class
abstract class Animal {
abstract void sound();
}
Real World: All animals make sounds, but each type defines its own.
3. Final Class
final class Constants { }
Real World: Sealed class — cannot be extended or modified.
4. Static Nested Class
class Car {
static class Engine {
void info() { System.out.println("Engine info"); }
}
}
Real World: Department inside a company that works independently.
5. Member Inner Class
class A {
class B {
void message() {
System.out.println("Inner class B");
}
}
}
6. Local Inner Class
class Shop {
void bill() {
class Item {
String name;
}
}
}
Real World: Temporary class used only while generating bill.
7. Anonymous Inner Class
Runnable r = new Runnable() {
public void run() {
System.out.println("Running task");
}
};
Real World: A temporary worker hired for only one job.
8. Enum Class
enum Direction { NORTH, SOUTH, EAST, WEST }
9. POJO Class
class Student {
private String name;
private int age;
}
10. Immutable Class
final class User {
private final String username;
User(String username) { this.username = username; }
public String getUsername() { return username; }
}
11. Singleton Class
class Singleton {
private static Singleton obj = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return obj;
}
}
Objects
Definition
An object is a real instance of a class containing:
- State (variables)
- Behavior (methods)
- Identity (memory address)
Creating Objects (All Ways)
1. Using new keyword
Car c = new Car();
2. Using Reflection
Car c = Car.class.newInstance();
3. Using Constructor class
Constructor cons = Car.class.getConstructor();
Car c = cons.newInstance();
4. Using clone()
Car c2 = (Car)c1.clone();
5. Using Deserialization
Car c = (Car)ois.readObject();
6. Anonymous Object
new Car().run();
Memory Diagram
Stack: c → reference
Heap: Object { brand="BMW", speed=180 }
Method Area: Class Car loaded
Constructors in Java
A constructor in Java is a special block of code used to initialize an object. It runs automatically when an object is created and prepares the object with initial values. It has the same name as the class and no return type.
Why Do We Need Constructors?
Constructors allow your object to start its life with proper and meaningful initial values. Without constructors:
- Objects would start uninitialized
- We would do manual setup each time
- Code becomes repetitive and error-prone
Rules of Constructors
- Constructor name must be the same as class name
- No return type (not even
void) - Called automatically when object is created
- Cannot be static, final, or abstract
- Can be overloaded
- super() must be the first statement in constructor
- If no constructor is written, JVM creates a default one
Types of Constructors (All Types with Deep Explanation)
1. Default Constructor (JVM Provided)
If you do not write any constructor, Java automatically provides a default constructor that sets all fields to default values specific to data types.
class Car {
int speed; // default = 0
String brand; // default = null
boolean on; // default = false
}
Explanation
Here, Java silently adds:
Car() {
super(); // call to Object class
}
This constructor does nothing except calling parent class constructor.
Real World Example
It's like building a car from the factory with: no custom features. Everything is set to its default state: speed = 0, engine OFF, no color set.
2. No-Argument (No-Arg) Constructor
This is a constructor that accepts no parameters and sets object values manually.
class Car {
String brand;
int speed;
Car() {
brand = "Unknown";
speed = 0;
System.out.println("No-arg constructor executed");
}
}
Explanation
When you create:
Car c = new Car();
It sets initial values like:
- brand = "Unknown"
- speed = 0
Real World Example
You buy a car without selecting any features → Factory gives you a default model with standard features.
3. Parameterized Constructor
Used when you want to initialize objects with specific values.
class Car {
String brand;
int speed;
Car(String brand, int speed) {
this.brand = brand; // the brand variable of this object
this.speed = speed;
}
}
Explanation
Here:
Car c = new Car("BMW", 180);
It immediately creates a BMW car with 180 speed.
Real World Example
You place a custom order for a car:
- You choose the brand
- You choose the engine/speed
4. Copy Constructor
A user-defined constructor that copies one object into another. Java DOES NOT provide it automatically.
class Car {
String brand;
int speed;
Car(Car c) { // copy constructor
this.brand = c.brand;
this.speed = c.speed;
}
}
Explanation
You can write:
Car c1 = new Car("Tesla", 200);
Car c2 = new Car(c1); // copying object
Now c2 has the same values as c1.
Real World Example
You ask the factory to create “the same car as my friend’s car”. The factory clones the exact configuration.
5. Private Constructor
Used mostly for Singleton Pattern or factory classes. It prevents others from creating objects.
class DatabaseConnection {
private DatabaseConnection() {
System.out.println("Database connected");
}
private static DatabaseConnection instance = new DatabaseConnection();
public static DatabaseConnection getInstance() {
return instance;
}
}
Real World Use Case
A Database should have only one connection. Private constructor ensures no one creates multiple objects.
6. Protected Constructor
Accessible within:
- same package
- child classes (even if in different packages)
class Vehicle {
protected Vehicle() {
System.out.println("Protected Vehicle Constructor");
}
}
Real World Example
You can only build this type of vehicle if:
- You are part of the same company (package)
- Or you own a child company (subclass)
7. Constructor Overloading
You can create multiple constructors with different sets of parameters.
class Car {
Car() { System.out.println("Default car"); }
Car(int speed) { this.speed = speed; }
Car(String brand, int speed) { this.brand = brand; this.speed = speed; }
}
Real World Example
Different ways to buy a car:
- Buy base model
- Select speed only
- Select brand and speed
8. Constructor Chaining
Calling one constructor from another using this() or super().
class Car {
String brand;
int speed;
Car() {
this("Unknown", 0); // calling another constructor
}
Car(String brand, int speed) {
this.brand = brand;
this.speed = speed;
}
}
Real World Example
A base car model always uses a more detailed manufacturing process internally.
Using super() in Inheritance
class Vehicle {
Vehicle() {
System.out.println("Vehicle Constructor");
}
}
class Car extends Vehicle {
Car() {
super();
System.out.println("Car Constructor");
}
}
Output:
Vehicle Constructor
Car Constructor
Complete Real-World Constructor Example
class Car {
String brand;
int speed;
String fuelType;
// No-argument constructor
Car() {
this("Unknown", 0, "Petrol");
System.out.println("Default car created.");
}
// Parameterized constructor
Car(String brand, int speed, String fuelType) {
this.brand = brand;
this.speed = speed;
this.fuelType = fuelType;
}
// Copy constructor
Car(Car other) {
this.brand = other.brand;
this.speed = other.speed;
this.fuelType = other.fuelType;
}
void show() {
System.out.println("Brand: " + brand);
System.out.println("Speed: " + speed);
System.out.println("Fuel: " + fuelType);
}
}
public class Main {
public static void main(String[] args) {
Car c1 = new Car(); // no-arg
c1.show();
Car c2 = new Car("Tesla", 200, "Electric"); // parameterized
c2.show();
Car c3 = new Car(c2); // copy constructor
c3.show();
}
}
This demonstrates all constructor types in a real-world scenario.
Conclusion
Constructors are the backbone of object initialization in Java. They:
- Provide meaningful starting values
- Reduce code duplication
- Offer flexibility through overloading
- Ensure safe object creation using private/protected constructors
- Support inheritance with
super()
Class 7 - Recording
▶ Watch VideoInheritance in Java
Inheritance allows a subclass to acquire the properties and behaviors of a superclass. It promotes code reusability and method overriding.
Example:
class Animal {
void sound() {
System.out.println("Animals make sounds");
}
}
class Dog extends Animal {
void sound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Dog d = new Dog();
d.sound(); // Output: Dog barks
}
}
Important Points:
- Java does not support multiple inheritance with classes.
- All classes inherit from the Object class.
- Use
superto access parent methods or constructors.
Related Exceptions:
// ClassCastException example
Animal a = new Animal();
Dog d = (Dog) a; // Throws ClassCastException
Polymorphism in Java
Polymorphism means one interface, many implementations. It allows methods to behave differently based on the object.
1. Compile-time Polymorphism (Method Overloading)
class MathOps {
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
}
2. Runtime Polymorphism (Method Overriding)
class Bird {
void fly() {
System.out.println("Bird is flying");
}
}
class Eagle extends Bird {
void fly() {
System.out.println("Eagle flies high");
}
}
public class Test {
public static void main(String[] args) {
Bird b = new Eagle();
b.fly(); // Output: Eagle flies high
}
}
Important Points:
- Method overloading is resolved at compile-time.
- Method overriding is resolved at runtime.
- Overridden methods must have the same signature.
- Return type can be covariant (subtype allowed).
Related Exceptions:
// Wrong downcasting -> ClassCastException
Bird b = new Bird();
Eagle e = (Eagle) b; // Throws ClassCastException
Encapsulation in Java
Encapsulation wraps data (variables) and methods within a class and protects them from unauthorized access using private access modifiers.
Example:
class Student {
private String name;
private int age;
public String getName() { return name; }
public void setName(String name) {
this.name = name;
}
public int getAge() { return age; }
public void setAge(int age) {
if (age > 0) {
this.age = age;
} else {
System.out.println("Invalid age");
}
}
}
public class Main {
public static void main(String[] args) {
Student s = new Student();
s.setName("John");
s.setAge(20);
System.out.println(s.getName());
System.out.println(s.getAge());
}
}
Important Points:
- Use private variables and public getters/setters.
- Encapsulation protects data and enables validation.
- Improves maintainability and flexibility.
Related Exceptions:
// Custom exception in setter
public void setAge(int age) {
if (age <= 0) {
throw new IllegalArgumentException("Age must be positive");
}
this.age = age;
}