Instance methods are methods that are called on an instance (object) of the class they belong to. To invoke an instance method, you first need to create an object of the class.
Example syntax:
public void display(String message) {
// Code to be executed
}
Instance methods can return any data type, including primitive types (e.g., int, float) or user-defined types.
Memory Allocation of Instance Methods
Instance methods themselves are stored in the Permanent Generation (PermGen) space of the heap until Java 7, but since Java 8, they are stored in Metaspace. However, their local variables and parameters (arguments passed) are stored in the stack.
These methods can be called from within the class in which they are defined or from other classes, depending on the access modifiers.
Key Points:
1. Instance methods belong to objects, not to the class itself. 2. They are stored in a single memory location and can access the object they belong to using the this reference. 3. Instance methods can be overridden, as they are resolved at runtime through dynamic binding.
Example:
// Example to demonstrate accessing an instance method.
class Car {
String model = "";
// Instance method to set car model
public void setModel(String model) {
this.model = model;
}
}
public class Test {
public static void main(String[] args) {
// Create an instance of the Car class
Car myCar = new Car();
// Call the instance method to set model
myCar.setModel("Tesla Model S");
System.out.println(myCar.model);
}
}
Output:
Tesla Model S
Java Static Methods
Static methods, unlike instance methods, belong to the class rather than an object. They can be called without creating an instance of the class.
Example syntax:
public static void display(String message) {
// Code to be executed
}
Static methods must include the static modifier in their declaration.
Memory Allocation of Static Methods
Static methods are stored in the Metaspace area (starting from Java 8) since they are associated with the class rather than any object. However, their local variables and arguments are stored in the stack.
Key Points:
1. Static methods are called using the class name, without the need for an instance. 2. They are shared across all instances of the class. 3. Static methods cannot be overridden, but they can be hidden by subclass methods if both superclass and subclass declare a static method with the same name. This is called Method Hiding.
Example:
// Example to demonstrate accessing static methods.
class MathUtil {
public static double pi = 3.14159;
// Static method to calculate the circumference of a circle
public static double circumference(double radius) {
return 2 * pi * radius;
}
}
public class Test {
public static void main(String[] args) {
// Access static method and field using class name
double result = MathUtil.circumference(5);
System.out.println("Circumference: " + result);
// Access static method using an instance (though it's unnecessary)
MathUtil util = new MathUtil();
double result2 = util.circumference(7);
System.out.println("Circumference: " + result2);
}
}
Output:
Circumference: 31.4159
Circumference: 43.98226
Abstract Method
In Java, there are situations where we need only method declarations in superclass, which is accomplished using the abstract type modifier. Abstraction can be implemented through abstract classes and methods. In this discussion, we will explore Java Abstract Methods.
Java Abstract Method
An abstract method serves as a blueprint for other classes or interfaces. In this context, methods are declared but not implemented. Abstract methods must be implemented by subclasses or classes that implement the respective interfaces.
These methods are often referred to as “subclass responsibilities” because they lack implementations in the superclass. Consequently, any subclass must override these methods to provide concrete definitions.
Declaring Abstract Methods in Java
To declare an abstract method, use the following general syntax:
abstract returnType methodName(parameterList);
Note that no method body is included. Any concrete class (a class not marked with the abstract keyword) that extends an abstract class must implement all abstract methods from that class.
Key Points about Abstract Methods
1. Any class containing one or more abstract methods must itself be declared as abstract. 2. A class containing an abstract method must be abstract, but the reverse is not necessarily true. 3. If a non-abstract class extends an abstract class, it must implement all abstract methods of the abstract class; otherwise, the non-abstract class must also be declared as abstract. 4. The following combinations of modifiers with abstract methods are illegal:final
abstract native
abstract synchronized
abstract static
abstract private
abstract strictf
Example of Java Abstract Method
Example 1: Performing Addition and Subtraction with Abstraction
Here is a program demonstrating how to perform addition and subtraction using abstraction.
// Java Program to implement addition and subtraction using abstraction
// Abstract Class
abstract class MathOperation {
abstract void displayInfo();
}
// Class Add
class Add extends MathOperation {
void displayInfo() {
int a = 5;
int b = 10;
System.out.println("Addition: " + (a + b));
}
}
// Class Subtract
class Subtract extends MathOperation {
void displayInfo() {
int c = 15;
int d = 4;
System.out.println("Subtraction: " + (c - d));
}
}
// Driver Class
public class AbstractionDemo {
public static void main(String args[]) {
MathOperation addition = new Add();
addition.displayInfo();
MathOperation subtraction = new Subtract();
subtraction.displayInfo();
}
}
Output:
Addition: 15
Subtraction: 11
Example 2: Using Abstract Keyword with Classes and Methods
Here is another example illustrating the use of the abstract keyword.
// A Java program demonstrating the use of abstract keyword
// Abstract class
abstract class Shape {
abstract void draw();
void show() {
System.out.println("This is a concrete method in the abstract class.");
}
}
// Concrete class Circle
class Circle extends Shape {
void draw() {
System.out.println("Drawing a circle.");
}
}
// Driver class
public class AbstractExample {
public static void main(String args[]) {
Circle circle = new Circle();
circle.draw();
circle.show();
}
}
Output:
Drawing a circle.
This is a concrete method in the abstract class.
Example 3: Abstract Class with Multiple Abstract Methods
This program illustrates an abstract class containing multiple abstract methods.
// Java Program to implement an abstract class with multiple abstract methods
abstract class AreaCalculator {
abstract void calculateRectangleArea(int height, int width);
abstract void calculateSquareArea(int side);
abstract void calculateCircleArea(float radius);
}
// Class implementing the abstract methods
class Calculator extends AreaCalculator {
public void calculateRectangleArea(int height, int width) {
int area = height * width;
System.out.println("Area of rectangle: " + area);
}
public void calculateSquareArea(int side) {
int area = side * side;
System.out.println("Area of square: " + area);
}
public void calculateCircleArea(float radius) {
float area = 3.14f * radius * radius;
System.out.println("Area of circle: " + area);
}
public static void main(String[] args) {
Calculator calc = new Calculator();
calc.calculateRectangleArea(10, 5);
calc.calculateSquareArea(4);
calc.calculateCircleArea(3.0f);
}
}
Output:
Area of rectangle: 50
Area of square: 16
Area of circle: 28.259999
Abstract Method in Interface
All methods in an interface are inherently public and abstract, allowing for the declaration of abstract methods within an interface.
Here’s an implementation of this concept:
// Java Program to demonstrate abstract methods in an interface
// Declaring an interface
interface Operations {
int addTwoNumbers(int a, int b);
int addThreeNumbers(int a, int b, int c);
}
// Main Class
public class InterfaceExample implements Operations {
public int addTwoNumbers(int a, int b) {
return a + b;
}
public int addThreeNumbers(int a, int b, int c) {
return a + b + c;
}
public static void main(String args[]) {
Operations ops = new InterfaceExample();
System.out.println("Sum of two numbers: " + ops.addTwoNumbers(5, 10));
System.out.println("Sum of three numbers: " + ops.addThreeNumbers(5, 10, 15));
}
}
Output:
Sum of two numbers: 15
Sum of three numbers: 30
Final Method in Abstract Class
While you cannot use final with an abstract method, you can define a final method in an abstract class.
class Vehicle {
final void fuel() {
System.out.println("Fuel type: Petrol");
}
}
class Car extends Vehicle {
// This will cause an error
// void fuel() { System.out.println("Fuel type: Diesel"); }
}
Output:
Error: Cannot override the final method from Vehicle
Static Methods Cannot Be Overridden (Method Hiding):
When a static method is redefined in a subclass, it is method hiding, not overriding.
class Parent {
static void show() {
System.out.println("Parent static show()");
}
}
class Child extends Parent {
static void show() {
System.out.println("Child static show()");
}
}
public class Main {
public static void main(String[] args) {
Parent p = new Child();
p.show(); // Calls Parent's static show()
}
}
Output:
Parent static show()
Private Methods Cannot Be Overridden:
Private methods are not visible to the child class and hence cannot be overridden.
class Parent {
private void message() {
System.out.println("Parent's private message()");
}
public void callMessage() {
message();
}
}
class Child extends Parent {
private void message() {
System.out.println("Child's private message()");
}
}
public class Main {
public static void main(String[] args) {
Parent p = new Child();
p.callMessage(); // Calls Parent's message()
}
}
Output:
Parent's private message()
Covariant Return Type:
The return type of an overriding method can be a subtype of the original return type.
class Parent {
public Object getObject() {
return "Parent object";
}
}
class Child extends Parent {
@Override
public String getObject() {
return "Child object";
}
}
public class Main {
public static void main(String[] args) {
Parent p = new Child();
System.out.println(p.getObject());
}
}
Output:
Child object
the Parent Class Method using super:
The overridden method in the subclass can call the method from the parent class using the super keyword.
class Parent {
void display() {
System.out.println("Parent display()");
}
}
class Child extends Parent {
@Override
void display() {
super.display();
System.out.println("Child display()");
}
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
c.display(); // Calls both Parent's and Child's display()
}
}
Output:
Parent display()
Child display()
Method Overriding vs Method Overloading
1. Overloading is when methods have the same name but different signatures. Overriding is when a subclass provides a specific implementation of a method that already exists in the parent class. 2. Overloading is a form of compile-time polymorphism (method resolution at compile time), while overriding is a form of run-time polymorphism (method resolution at runtime).
Overriding
Overriding is a feature in Java where a subclass or child class provides a specific implementation of a method that is already present in its parent class or superclass. When a method in a subclass has the same name, parameters (signature), and return type (or a subtype) as a method in its superclass, it overrides the method from the superclass.
Method Overriding enables Run-Time Polymorphism. This means that the method that gets executed depends on the object that is used to call it, not the reference type. If the object is of the subclass, the subclass method will be called; otherwise, the superclass method will be executed.
Example of Method Overriding:
// Superclass
class Animal {
void sound() { System.out.println("Animal makes a sound"); }
}
// Subclass
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
// Driver class
class Main {
public static void main(String[] args) {
Animal a1 = new Animal();
a1.sound(); // Calls Animal's sound()
Animal a2 = new Dog();
a2.sound(); // Calls Dog's sound (Run-time Polymorphism)
}
}
Output:
Animal makes a sound
Dog barks
Rules for Method Overriding
1. Access Modifiers in Overriding : The overridden method in the subclass can provide more visibility, but not less. For example, a protected method in the superclass can be overridden as public in the subclass, but not private.
class Vehicle {
protected void start() {
System.out.println("Vehicle starts");
}
}
class Car extends Vehicle {
@Override
public void start() { // More accessible
System.out.println("Car starts");
}
}
class Main {
public static void main(String[] args) {
Vehicle v = new Car();
v.start(); // Calls Car's start()
}
}
Output:
Car starts
2. Final Methods Cannot Be Overridden : If a method is declared as final in the superclass, it cannot be overridden in the subclass.
class Bird {
final void fly() {
System.out.println("Bird is flying");
}
}
class Eagle extends Bird {
// This would produce an error if uncommented
// void fly() { System.out.println("Eagle flies faster"); }
}
Output:
Compilation error: cannot override final method
3. Static Methods and Method Hiding : Static methods cannot be overridden; they are hidden. A subclass can define a static method with the same signature as the one in its superclass, but this will hide the method in the superclass rather than overriding it.
class Parent {
static void display() {
System.out.println("Parent display");
}
void show() {
System.out.println("Parent show");
}
}
class Child extends Parent {
static void display() {
System.out.println("Child display");
}
@Override
void show() {
System.out.println("Child show");
}
}
class Main {
public static void main(String[] args) {
Parent p = new Child();
p.display(); // Calls Parent's static method
p.show(); // Calls Child's overridden method
}
}
Output:
Parent display
Child show
4. Private Methods Cannot Be Overridden : Private methods in the superclass cannot be overridden by subclasses. They are not visible to subclasses and are resolved at compile time.
class Super {
private void secretMethod() {
System.out.println("Super's secret method");
}
public void callSecret() {
secretMethod();
}
}
class Sub extends Super {
private void secretMethod() {
System.out.println("Sub's secret method");
}
}
public class Main {
public static void main(String[] args) {
Super sup = new Super();
sup.callSecret(); // Calls Super's private method
}
}
Output:
Super's secret method
5. Covariant Return Type in Overriding : The return type of the overriding method can be a subclass of the return type of the overridden method.
public class BitwiseOperators {
public static void main(String[] args) {
int a = 5; // 0101 in binary
int b = 3; // 0011 in binary
System.out.println("a & b: " + (a & b)); // AND operation
System.out.println("a | b: " + (a | b)); // OR operation
}
}
Output:
class SuperClass {
public Object getObject() {
return new Object();
}
}
class SubClass extends SuperClass {
@Override
public String getObject() {
return "This is a String";
}
}
public class Main {
public static void main(String[] args) {
SuperClass obj = new SubClass();
System.out.println(obj.getObject());
}
}
Output:
This is a String
6. Calling the Superclass Method : We can call the superclass version of the overridden method using the super keyword.
class Superhero {
void power() {
System.out.println("Superhero has generic powers");
}
}
class Superman extends Superhero {
@Override
void power() {
super.power();
System.out.println("Superman can fly and has super strength");
}
}
class Main {
public static void main(String[] args) {
Superman clark = new Superman();
clark.power();
}
}
Output:
Superhero has generic powers
Superman can fly and has super strength
Overriding and Exception Handling
1. Unchecked Exceptions: If the superclass method does not throw any exceptions, the subclass can only throw unchecked exceptions when overriding it.
2. Checked Exceptions : If the superclass method throws an exception, the overriding method can throw the same exception or its subclass, but not a higher-level exception.
class Animal {
void eat() throws Exception {
System.out.println("Animal is eating");
}
}
class Dog extends Animal {
@Override
void eat() throws RuntimeException {
System.out.println("Dog is eating");
}
}
Method Overloading
In Java, Method Overloading allows multiple methods to share the same name but differ in parameters—either by the number of parameters, types of parameters, or a combination of both. This feature is also called Compile-time Polymorphism, Static Polymorphism, or Early Binding. When overloaded methods are present, Java gives priority to the most specific match among the parameters.
Example of Method Overloading
// Java program demonstrating method overloading
public class Calculator {
// Overloaded add() method that accepts two integer parameters
public int add(int a, int b) {
return a + b;
}
// Overloaded add() method that accepts three integer parameters
public int add(int a, int b, int c) {
return a + b + c;
}
// Overloaded add() method that accepts two double parameters
public double add(double a, double b) {
return a + b;
}
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.add(5, 10)); // Two integers
System.out.println(calc.add(5, 10, 15)); // Three integers
System.out.println(calc.add(4.5, 3.5)); // Two doubles
}
}
Output:
15
30
8.0
Ways to Achieve Method Overloading in Java:
1. Changing the Number of Parameters 2. Changing the Data Types of the Arguments 3. Changing the Order of Parameters
1. Changing the Number of Parameters : This approach achieves method overloading by varying the number of input parameters in methods that share the same name.
// Java program to demonstrate method overloading by changing the number of parameters
class Multiply {
// Multiply two integers
public int multiply(int a, int b) {
return a * b;
}
// Multiply three integers
public int multiply(int a, int b, int c) {
return a * b * c;
}
public static void main(String[] args) {
Multiply obj = new Multiply();
System.out.println("Product of two integers: " + obj.multiply(2, 3));
System.out.println("Product of three integers: " + obj.multiply(2, 3, 4));
}
}
Output:
Product of two integers: 6
Product of three integers: 24
2. Changing Data Types of the Arguments : This method achieves overloading by having the same method name but with different parameter types.
// Java program to demonstrate method overloading by changing the data types of parameters
class Volume {
// Calculate volume using integer values
public int calculateVolume(int a, int b, int c) {
return a * b * c;
}
// Calculate volume using double values
public double calculateVolume(double a, double b, double c) {
return a * b * c;
}
public static void main(String[] args) {
Volume vol = new Volume();
System.out.println("Volume with integers: " + vol.calculateVolume(2, 3, 4));
System.out.println("Volume with doubles: " + vol.calculateVolume(2.5, 3.5, 4.5));
}
}
Output:
Volume with integers: 24
Volume with doubles: 39.375
3. Changing the Order of Parameters : You can achieve method overloading by altering the order in which parameters are passed.
// Java program to demonstrate method overloading by changing the order of parameters
class Display {
// Display information by name followed by age
public void showDetails(String name, int age) {
System.out.println("Name: " + name + ", Age: " + age);
}
// Display information by age followed by name
public void showDetails(int age, String name) {
System.out.println("Age: " + age + ", Name: " + name);
}
public static void main(String[] args) {
Display obj = new Display();
obj.showDetails("Alice", 30);
obj.showDetails(25, "Bob");
}
}
Output:
Name: Alice, Age: 30
Age: 25, Name: Bob
What if the exact prototype doesn’t match?
In situations where the parameters don’t match any exact method signature, Java prioritizes methods based on data type compatibility. The compiler tries to:
1. Convert the parameter to a higher data type within the same group (e.g., from byte to int). 2. If no match is found, it attempts to move to a higher data type in another group (e.g., from int to float).
class Demo {
public void display(int x) {
System.out.println("Integer: " + x);
}
public void display(String s) {
System.out.println("String: " + s);
}
public void display(byte b) {
System.out.println("Byte: " + b);
}
}
public class UseDemo {
public static void main(String[] args) {
byte a = 20;
Demo obj = new Demo();
obj.display(a); // Byte method
obj.display("Hello"); // String method
obj.display(500); // Integer method
obj.display('C'); // Char is promoted to int (ASCII value)
// Uncommenting the line below would cause a compilation error:
// obj.display(10.5); // No suitable method for double
}
}
Output:
Byte: 20
String: Hello
Integer: 500
Integer: 67
Advantages of Method Overloading
Enhanced Readability and Reusability: By using method overloading, the code becomes more intuitive, reducing the need for verbose method names.
Reduced Complexity: Methods performing similar tasks can share a name, making code easier to manage.
Efficiency: Overloading allows different versions of methods to handle related tasks with varying parameters, optimizing the function call.
Multiple Constructor Options: Overloaded constructors enable different ways to initialize objects, depending on the given arguments.
Inheritance is a feature in object-oriented programming where a new class (called derived or child class) is created by inheriting properties and behaviors (methods and variables) from an existing class (called base or parent class). It promotes code reusability and reduces redundancy.
#include <iostream>
using namespace std;
class A {
int a, b;
public:
void add(int x, int y)
{
a = x;
b = y;
cout << "Addition of a + b is: " << (a + b) << endl;
}
};
class B : public A {
public:
void print(int x, int y)
{
add(x, y);
}
};
int main()
{
B b1;
b1.print(5, 6);
return 0;
}
Output:
Addition of a + b is: 11
Here, class B inherits the add() method from class A.
Polymorphism:
Polymorphism is a feature in object-oriented programming where an object can take multiple forms. Polymorphism allows one task to be performed in different ways, either at compile-time or run-time.
Types of Polymorphism:
Compile-time polymorphism (Method Overloading)
Run-time polymorphism (Method Overriding)
Example of Polymorphism:
#include <iostream>
using namespace std;
class A {
int a, b, c;
public:
// Compile-time polymorphism (Method Overloading)
void add(int x, int y)
{
a = x;
b = y;
cout << "Addition of a + b is: " << (a + b) << endl;
}
void add(int x, int y, int z)
{
a = x;
b = y;
c = z;
cout << "Addition of a + b + c is: " << (a + b + c) << endl;
}
// Run-time polymorphism (Method Overriding)
virtual void print()
{
cout << "Class A's method is running" << endl;
}
};
class B : public A {
public:
void print()
{
cout << "Class B's method is running" << endl;
}
};
int main()
{
A a1;
// Compile-time polymorphism (Method Overloading)
a1.add(6, 5);
a1.add(1, 2, 3);
B b1;
// Run-time polymorphism (Method Overriding)
b1.print();
}
Output:
Addition of a + b is: 11
Addition of a + b + c is: 6
Class B's method is running
Difference between Inheritance and Polymorphism:
Inheritance
Polymorphism
Inheritance allows a new class (derived class) to inherit features from an existing class (base class).
Polymorphism allows methods to take multiple forms.
Inheritance applies to classes.
Polymorphism applies to methods or functions.
Inheritance supports code reusability and reduces code duplication.
Polymorphism allows the program to choose which function to execute at compile-time (overloading) or run-time (overriding).
Inheritance can be single, multiple, hierarchical, multilevel, or hybrid.
Polymorphism can be compile-time (overloading) or run-time (overriding).
Inheritance is used to model relationships between classes.
Polymorphism allows flexibility in implementing methods.
Example of Inheritance: A class Car can be derived from a class Vehicle, and Car can further inherit properties like engine type, wheels, etc.
Example of Polymorphism: The class Car can have a method setColor(), which changes the car’s color based on the input color value provided.
Function Overriding in C++
Method Overriding and Runtime Polymorphism in Java:
Java supports runtime polymorphism through method overriding. Dynamic method dispatch is the mechanism that resolves which overridden method will be executed at runtime, not during compile-time.
When a method is called on a superclass reference, Java determines which version of the method (from the superclass or subclass) to execute based on the actual object being referenced at the time of the call. This decision is made at runtime, depending on the type of the object (not the reference variable).
A superclass reference variable can refer to an object of a subclass, which is known as upcasting. Java uses this concept to enable method overriding during runtime.
If a superclass contains a method that is overridden by a subclass, the version of the method executed depends on the object type being referred to, even though the reference variable is of the superclass type. Below is an example demonstrating dynamic method dispatch:
Java Example of Dynamic Method Dispatch:
// A Java program to demonstrate Dynamic Method Dispatch
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
// overriding sound() method
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
// overriding sound() method
void sound() {
System.out.println("Cat meows");
}
}
public class DispatchDemo {
public static void main(String[] args) {
// creating objects of Animal, Dog, and Cat
Animal animal = new Animal();
Dog dog = new Dog();
Cat cat = new Cat();
// reference of type Animal
Animal ref;
// ref refers to Animal object
ref = animal;
ref.sound(); // calls Animal's version of sound()
// ref refers to Dog object
ref = dog;
ref.sound(); // calls Dog's version of sound()
// ref refers to Cat object
ref = cat;
ref.sound(); // calls Cat's version of sound()
}
}
Output:
Animal makes a sound
Dog barks
Cat meows
Explanation:
The above program has one superclass Animal and two subclasses Dog and Cat. Each subclass overrides the sound() method from the superclass.
First, an object of each class (Animal, Dog, Cat) is created.
Then a reference of type Animal is used to refer to objects of different types (upcasting).
The version of the sound() method called depends on the actual object type at runtime.
Example: Runtime Polymorphism with Data Members (Java):
In Java, runtime polymorphism works with methods but not with data members (variables). Variables are not overridden, so the reference variable will always access the data member of the superclass, not the subclass.
Java Example for Data Members:
// Java program to show that runtime polymorphism
// doesn't apply to data members (only methods)
class Animal {
int age = 5;
}
class Dog extends Animal {
int age = 10;
}
public class TestDemo {
public static void main(String[] args) {
Animal animal = new Dog(); // object of type Dog
// Data member of class Animal will be accessed
System.out.println(animal.age);
}
}
Output:
5
Explanation:
In this program, both the Animal and Dog classes have a common data member age. Even though the object is of type Dog and the reference variable is of type Animal, the data member of Animal will be accessed because data members are not overridden. Therefore, animal.age refers to the superclass Animal‘s age value.
Advantages of Dynamic Method Dispatch:
1. Supports Method Overriding: Dynamic method dispatch allows Java to support method overriding, which is crucial for implementing runtime polymorphism. 2. Provides Flexibility in Method Implementation: A class can define common methods that are shared by all its subclasses, while allowing each subclass to provide specific implementations for those methods. 3. Enhances Extensibility: It enables subclasses to add their unique behaviors while still using the reference variable of the superclass, promoting flexibility and scalability in code.
Difference between Compile-time and Run-time Polymorphism in Java
Polymorphism Explained:
The term polymorphism refers to the concept of having multiple forms. In simpler terms, polymorphism allows the same message or method to be processed in more than one way. In this discussion, we will explore the distinction between the two types of polymorphism: compile-time and runtime polymorphism.
Compile-Time Polymorphism:
When the binding of a method call to the method definition occurs at compile-time, it is known as compile-time polymorphism. Java resolves which method to call by examining the method signatures during compilation, which is why this type of polymorphism is also called static or early binding. Compile-time polymorphism is achieved through method overloading.
Method Overloading refers to having multiple methods in the same class with the same name but different parameter lists (method signatures). This is one way to implement polymorphism, although the specific method varies based on the language. In Java, method overloading is resolved at compile-time.
Here is an example demonstrating compile-time polymorphism:
Java Example of Compile-Time Polymorphism:
// Java program demonstrating compile-time polymorphism
public class Example {
// First version of the add method
public static int add(int a, int b) {
return a + b;
}
// Second version of the add method
public static double add(double a, double b) {
return a + b;
}
public static void main(String[] args) {
// The first add method is called
System.out.println(add(3, 4)); // Output: 7
// The second add method is called
System.out.println(add(3.5, 4.5)); // Output: 8.0
}
}
Output:
7
8.0
Run-Time Polymorphism:
When the method binding happens at runtime, it is called runtime polymorphism. This is achieved through method overriding in Java. The Java Virtual Machine (JVM) determines which overridden method to call at runtime based on the actual object, not during compilation.
Method Overriding occurs when a subclass provides its specific implementation of a method that is already defined in its superclass. This allows different classes to define the same method in their own way, which is resolved dynamically at runtime.
Java Example of Run-Time Polymorphism:
// Java program demonstrating run-time polymorphism
// Parent class
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
// Child class
class Dog extends Animal {
// Overriding the parent method
public void makeSound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal animalRef = new Dog(); // Upcasting
// The overridden method in Dog will be called
animalRef.makeSound(); // Output: Dog barks
}
}
Output:
Dog barks
In this example, even though the reference variable animalRef is of type Animal, it refers to an object of type Dog. At runtime, the JVM determines which makeSound method to execute based on the object type, resulting in the Dog class’s method being called.
Differences between Compile-Time and Run-Time Polymorphism:
Compile-Time Polymorphism
Run-Time Polymorphism
The method call is resolved by the compiler.
The method call is resolved during runtime by the JVM.
Also known as Static binding, Early binding, or Overloading.
Also known as Dynamic binding, Late binding, or Overriding.
Achieved by method overloading.
Achieved by method overriding.
Faster execution, as the method to execute is determined at compile time.
Slower execution in comparison, as the method is determined at runtime.
Does not involve inheritance.
Requires inheritance for method overriding.
Less flexible, since method calls are fixed at compile-time.
More flexible, since method calls are resolved at runtime.
Advantages of Polymorphism:
Compile-Time Polymorphism: Provides faster execution as the decision of which method to invoke is made at compile time, making the program more efficient.
Run-Time Polymorphism: Offers greater flexibility because method calls are determined at runtime, allowing different behaviors based on the actual object type being referenced.
Abstraction and Encapsulation in Object-Oriented Programming (OOP)
Abstraction and Encapsulation are key principles in Object-Oriented Programming (OOP). They are fundamental to building maintainable, reusable, and secure applications. Both concepts contribute to features like reusability, security, data hiding, and implementation concealment. Despite their relation, they serve different purposes and are implemented in unique ways. Let’s explore these differences with code examples.
Encapsulation in Java
Encapsulation is the process of bundling data (variables) and methods that operate on the data within a single unit or class. It ensures that the data is not accessible directly outside the class, thereby providing security and control over its modification. By restricting access to data through private variables and allowing controlled access via public methods, encapsulation prevents unauthorized access.
In simple terms, encapsulation can be thought of as a protective shield that keeps data safe from unauthorized access. This is why it is also referred to as data hiding.
Java Example of Encapsulation:
// Java program demonstrating encapsulation
class Student {
// Private variables, accessible only through public methods
private String studentName;
private int studentID;
private int studentAge;
// Getter method for age
public int getAge() {
return studentAge;
}
// Getter method for name
public String getName() {
return studentName;
}
// Getter method for ID
public int getID() {
return studentID;
}
// Setter method for age
public void setAge(int newAge) {
studentAge = newAge;
}
// Setter method for name
public void setName(String newName) {
studentName = newName;
}
// Setter method for ID
public void setID(int newID) {
studentID = newID;
}
}
public class TestEncapsulation {
public static void main(String[] args) {
// Creating an object of Student class
Student obj = new Student();
// Setting values of the variables
obj.setName("John");
obj.setAge(22);
obj.setID(1001);
// Displaying values of the variables
System.out.println("Student's name: " + obj.getName());
System.out.println("Student's age: " + obj.getAge());
System.out.println("Student's ID: " + obj.getID());
}
}
Output:
Student's name: John
Student's age: 22
Student's ID: 1001
In this example, encapsulation ensures that studentID, studentName, and studentAge can only be accessed or modified through the setter and getter methods, not directly.
Abstraction in Java
Abstraction is the concept of showing only the relevant details to the user and hiding unnecessary implementation details. It allows you to focus on what an object does rather than how it does it. Abstraction simplifies complex systems by breaking them into smaller, more understandable parts.
For example, you interact with a vehicle through its interface (like driving a car), but you do not need to know the internal mechanics of how the engine operates. Abstraction is often achieved in Java through abstract classes and interfaces.
Java Example of Abstraction:
// Java program demonstrating abstraction
abstract class Appliance {
String brand;
// Abstract methods that subclasses need to implement
abstract void turnOn();
abstract void turnOff();
// Constructor
public Appliance(String brand) {
this.brand = brand;
}
// Concrete method
public String getBrand() {
return brand;
}
}
class WashingMachine extends Appliance {
public WashingMachine(String brand) {
super(brand);
}
@Override
void turnOn() {
System.out.println(brand + " Washing Machine is now ON");
}
@Override
void turnOff() {
System.out.println(brand + " Washing Machine is now OFF");
}
}
class Refrigerator extends Appliance {
public Refrigerator(String brand) {
super(brand);
}
@Override
void turnOn() {
System.out.println(brand + " Refrigerator is now ON");
}
@Override
void turnOff() {
System.out.println(brand + " Refrigerator is now OFF");
}
}
public class TestAbstraction {
public static void main(String[] args) {
Appliance washingMachine = new WashingMachine("LG");
Appliance refrigerator = new Refrigerator("Samsung");
washingMachine.turnOn();
refrigerator.turnOn();
washingMachine.turnOff();
refrigerator.turnOff();
}
}
Output:
LG Washing Machine is now ON
Samsung Refrigerator is now ON
LG Washing Machine is now OFF
Samsung Refrigerator is now OFF
In this example, abstraction allows us to define the basic behavior of an appliance without showing the internal workings. The specific implementations for turnOn and turnOff are provided by the subclasses WashingMachine and Refrigerator.
Differences Between Abstraction and Encapsulation
Abstraction
Encapsulation
Abstraction focuses on hiding implementation details while showing only essential features to the user.
Encapsulation bundles the data and methods that operate on the data within one unit and restricts direct access to some of the object’s components.
It is used to reduce complexity and increase maintainability by defining clear interfaces.
It is used to hide data and provide controlled access, enhancing security and data integrity.
Abstraction is more about the design and involves creating abstract classes and interfaces.
Encapsulation is more about the implementation, using access modifiers like private, protected, and public.
Problems are solved at the interface level.
Problems are solved at the class level.
Abstraction can be achieved using abstract classes and interfaces.
Encapsulation is achieved using access modifiers and getter/setter methods.
Example: A TV remote control lets you operate the TV without showing how the circuits inside work.
Example: A person’s bank account details are hidden from unauthorized users, and access is granted only via public methods.
In Java, the abstract keyword is a non-access modifier applied to classes and methods, but not variables. It is primarily used to achieve abstraction, one of the core principles of Object-Oriented Programming (OOP). Below are the various contexts where the abstract keyword can be utilized in Java.
Characteristics of the abstract Keyword in Java
The abstract keyword is used to define abstract classes and methods. Here are its key characteristics:
Abstract classes cannot be instantiated: An abstract class is one that cannot be instantiated directly. It serves as a base class for other classes, which are responsible for providing concrete implementations of its abstract methods.
Abstract methods lack a body: An abstract method is declared using the abstract keyword and has no method body. It ends with a semicolon. Any class that extends an abstract class must provide implementations for all abstract methods.
Abstract classes can have both abstract and concrete methods: Along with abstract methods, an abstract class can also contain concrete methods with full implementations. These methods can be used by the abstract class itself or its subclasses.
Abstract classes can have constructors: While abstract classes cannot be instantiated, they can define constructors. These constructors are typically called during the instantiation of a concrete subclass.
Abstract classes can include instance variables: Abstract classes can declare instance variables, which can be accessed by both the abstract class and its subclasses.
Abstract classes can implement interfaces: An abstract class can implement interfaces and provide concrete implementations of the interface methods. However, the abstract class does not need to implement all methods immediately—this can be deferred to its subclasses.
Abstract Methods in Java
Abstract methods serve the purpose of declaring methods in a superclass without providing an implementation. Subclasses are responsible for implementing these methods. Abstract methods are often referred to as having “subclass responsibility.”
To declare an abstract method, use the following general syntax:
abstract returnType methodName(parameterList);
Since no method body is provided, any class that extends an abstract class must implement all of the abstract methods.
Rules for Abstract Methods
Some important rules associated with abstract methods are:
Any class containing one or more abstract methods must also be declared abstract.
You cannot combine the abstract modifier with the following modifiers: final, native, synchronized, static, private, or strictfp.
Abstract Classes in Java
An abstract class is a class that has partial implementation, meaning it may have methods that lack concrete definitions. To declare a class abstract, use the following syntax:
abstract class ClassName {
// class body
}
Abstract classes cannot be instantiated directly, and any class that extends an abstract class must either implement all abstract methods or be declared abstract itself.
Example of Abstract Classes and Methods
Here’s an example that demonstrates the use of abstract classes and methods:
// Abstract class representing a general vehicle
abstract class Vehicle {
// Abstract method (no implementation)
abstract void startEngine();
// Concrete method
void stopEngine() {
System.out.println("Engine stopped.");
}
}
// Concrete class representing a car
class Car extends Vehicle {
// Providing implementation for the abstract method
void startEngine() {
System.out.println("Car engine started.");
}
}
// Driver class to demonstrate abstract classes
public class Main {
public static void main(String[] args) {
Vehicle myCar = new Car(); // Vehicle reference, Car object
myCar.startEngine(); // Output: Car engine started.
myCar.stopEngine(); // Output: Engine stopped.
}
}
Output:
Car engine started.
Engine stopped.
Abstract Class in Java
In Java, an abstract class is defined using the abstract keyword. It is a class that cannot be instantiated on its own and may contain both abstract and concrete methods (methods with bodies). The abstract keyword can only be applied to classes and methods, not variables. In this article, we will explore the concept of abstract classes and their use in Java.
What is an Abstract Class?
An abstract class is a blueprint for other classes and cannot be used to create objects directly. It can only be subclassed, allowing other classes to inherit its properties. Declaring an abstract class in Java requires using the abstract keyword in its class definition. This approach allows for partial implementation of functionality, leaving subclasses to complete the abstract methods.
Illustration of Abstract Class
abstract class Shape {
int color;
// Abstract method (no implementation)
abstract void draw();
}
Important Points about Abstract Classes
Cannot Instantiate Abstract Classes: Instances of abstract classes cannot be created.
Constructors Are Allowed: Abstract classes can have constructors that are invoked when a subclass is instantiated.
No Abstract Methods Required: An abstract class can exist without any abstract methods.
Final Methods: Abstract classes can have final methods, but a method declared as abstract cannot be final, as this combination will result in an error.
Static Methods: Static methods can be defined in abstract classes.
Usage of Abstract Classes: Abstract classes can be used for both top-level (outer) and inner classes.
Incomplete Methods: If a subclass does not provide implementation for all abstract methods of a parent class, it must also be declared abstract.
Examples of Java Abstract Classes
1. Abstract Class with an Abstract Method: Here’s an example that demonstrates how an abstract class works in Java:
// Abstract class
abstract class Vehicle {
abstract void displayDetails();
}
// Class extending the abstract class
class Car extends Vehicle {
void displayDetails() {
String model = "Tesla";
int year = 2024;
double price = 55000.00;
System.out.println("Model: " + model);
System.out.println("Year: " + year);
System.out.println("Price: $" + price);
}
}
// Main class
public class Main {
public static void main(String[] args) {
Vehicle v = new Car();
v.displayDetails();
}
}
Output:
Model: Tesla
Year: 2024
Price: $55000.0
Examples of Java Abstract Classes
2. Abstract Class with an Abstract Method : Here’s an example that demonstrates how an abstract class works in Java:
// Abstract class
abstract class Course {
Course() {
System.out.println("Enrolled in the course");
}
abstract void courseSyllabus();
void study() {
System.out.println("Studying for exams!");
}
}
// Subclass extending the abstract class
class ComputerScience extends Course {
void courseSyllabus() {
System.out.println("Topics: Data Structures, Algorithms, AI");
}
}
// Main class
public class Main {
public static void main(String[] args) {
Course c = new ComputerScience();
c.courseSyllabus();
c.study();
}
}
Output:
Enrolled in the course
Topics: Data Structures, Algorithms, AI
Studying for exams!
An abstract class cannot be instantiated directly, but you can create references of the abstract class type.
// Abstract class
abstract class Animal {
abstract void sound();
}
// Concrete class
class Dog extends Animal {
void sound() {
System.out.println("Bark");
}
}
// Main class
public class Main {
public static void main(String[] args) {
// Animal a = new Animal(); // Error: Cannot instantiate the abstract class
Animal a = new Dog();
a.sound();
}
}
Output:
Bark
Observation 2: Abstract Class with Constructors
A constructor in an abstract class can be called when an instance of a subclass is created.
abstract class Appliance {
Appliance() {
System.out.println("Appliance Constructor");
}
abstract void use();
}
class WashingMachine extends Appliance {
WashingMachine() {
System.out.println("Washing Machine Constructor");
}
void use() {
System.out.println("Washing clothes");
}
}
public class Main {
public static void main(String[] args) {
WashingMachine wm = new WashingMachine();
wm.use();
}
}
Observation 3: Abstract Class Without Abstract Methods
Abstract classes can exist without having abstract methods.
abstract class Library {
void borrowBook() {
System.out.println("Borrowing a book");
}
}
class CityLibrary extends Library {}
public class Main {
public static void main(String[] args) {
CityLibrary cl = new CityLibrary();
cl.borrowBook();
}
}
Output:
Borrowing a book
Control Abstraction in Java with Examples
Before diving into control abstraction, let’s first understand the concept of abstraction.
Abstraction: Abstraction simplifies the complexity of a system by exposing only the essential features while hiding the intricate internal details. For example, when driving a car, the driver interacts with the steering wheel, pedals, and other controls, but the complex workings of the engine and electronics are abstracted away. The driver only needs to know how to operate the car, not how every internal mechanism functions.
Now, let’s explore an example of abstraction before delving into control abstraction:
abstract class Person {
abstract void displayDetails();
}
class Employee extends Person {
void displayDetails() {
String name = "John";
int age = 30;
double salary = 55000.50;
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Salary: $" + salary);
}
}
class Main {
public static void main(String[] args) {
Person employee = new Employee();
employee.displayDetails();
}
}
Output:
Name: John
Age: 30
Salary: $55000.5
In the above example, the details of an employee are abstracted through the Person class, and the specifics are implemented in the Employee class. Only essential information is shown to the user.
Types of Abstraction
There are two main types of abstraction:
1. Data Abstraction: This involves creating complex data types and exposing only essential operations. 2. Control Abstraction: This focuses on simplifying the program logic by removing unnecessary execution details and structuring the program into manageable parts.
Control Abstraction in Java
Control Abstraction in programming refers to the process of using higher-level operations and constructs (such as functions, loops, and conditional statements) to manage and simplify complex control flows. Instead of repeatedly writing out specific instructions, control abstraction encourages modular and reusable code that follows the DRY (Don’t Repeat Yourself) principle.
Key Features of Control Abstraction:
It promotes reusability by using methods and functions, thereby reducing code duplication.
Control abstraction bundles control statements into a single unit to make code easier to understand and manage.
It’s a fundamental feature of higher-level languages, including Java.
It focuses on how a task is achieved rather than the detailed steps involved in doing it.
It is often seen in structured programming through control structures like loops, conditionals, and function calls.
Example of Control Abstraction:
// Abstract class
abstract class Vehicle {
// Abstract method (does not have a body)
public abstract void makeSound();
// Regular method
public void startEngine() {
System.out.println("Engine starting...");
}
}
// Subclass (inherit from Vehicle)
class Car extends Vehicle {
public void makeSound() {
// The body of makeSound() is provided here
System.out.println("Car sound: Vroom Vroom");
}
}
class Main {
public static void main(String[] args) {
// Create a Car object
Car myCar = new Car();
myCar.startEngine(); // Regular method
myCar.makeSound(); // Abstract method implementation
}
}
Output:
Engine starting...
Car sound: Vroom Vroom
In this example:
startEngine() is a regular method defined in the Vehicle abstract class, and it’s used by any subclass.
makeSound() is an abstract method in Vehicle, and each subclass (like Car) must provide its own implementation of this method.
Key Steps of Control Flow:
1. The necessary resources are obtained. 2. The block of code is executed. 3. When control exits the block, resources are released or closed.
This structured approach ensures a clean flow of execution, making the program easier to read and maintain.
Difference Between Data Hiding and Abstraction in Java
Abstraction is the process of hiding the internal implementation and showcasing only the essential features or services. It allows the user to interact with the system without needing to understand its inner workings. This is accomplished through the use of abstract classes and interfaces in Java. Essentially, abstraction highlights only the necessary characteristics of an object, which distinguishes it from other objects, while suppressing non-essential details from the user.
Real-Life Example of Abstraction:
Consider an ATM machine. When you use an ATM, you see a graphical user interface (GUI) that displays services such as withdrawals, deposits, and checking balances. However, the internal mechanisms—such as how the transactions are processed—are hidden from the user.
Types of Abstraction
There are three main types of abstraction:
1. Procedural Abstraction: Involves a series of procedures (or functions) that are executed sequentially to achieve abstraction through the use of classes and methods. 2. Data Abstraction: Focuses on representing an object using a set of data while hiding its underlying implementation details. 3. Control Abstraction: Involves writing a program in such a way that the control flow details (such as loops, conditions, or method calls) are hidden, encapsulating the operations into higher-level functions or objects.
Advantages of Abstraction:
Security: Internal implementation details are hidden, which provides protection from unauthorized access.
Ease of Enhancement: Changes can be made to the internal system without affecting end users.
Flexibility: The system is easier to use for end users since they only interact with essential features.
Enhanced Application Quality: It helps in building more sophisticated and efficient applications by focusing on the most important aspects.
Implementation of Abstraction in Java
Abstraction is implemented using classes and interfaces, which represent only the significant traits and hide internal implementations. Here’s an example:
// Abstract class representing a creature
abstract class Creature {
// Abstract method hiding specific details
abstract void numberOfLegs();
}
// A class representing an Elephant, inheriting from Creature
class Elephant extends Creature {
// Implementing the abstract method
void numberOfLegs() {
System.out.println("The elephant has four legs.");
}
}
// A class representing a Human, also inheriting from Creature
class Human extends Creature {
// Implementing the abstract method
public void numberOfLegs() {
System.out.println("Humans have two legs.");
}
}
public class Main {
public static void main(String[] args) {
// Creating Human object
Human human = new Human();
human.numberOfLegs();
// Creating Elephant object
Elephant elephant = new Elephant();
elephant.numberOfLegs();
}
}
Output:
Humans have two legs.
The elephant has four legs
In this example, the Creature class is abstract and hides the internal details about the number of legs of different creatures. The concrete classes Elephant and Human provide specific implementations of the numberOfLegs method.
Data Hiding in Java
Data Hiding refers to the practice of hiding internal data from external access. This ensures that an object’s internal state cannot be directly accessed by other classes, maintaining security. Data hiding is usually achieved using access modifiers, such as private, which prevent external classes from directly accessing certain fields or methods.
Example of Data Hiding:
class BankAccount {
// Private variable for account balance
private double accountBalance;
// Getter method to access account balance securely
public double getAccountBalance() {
return accountBalance;
}
// Setter method to update account balance securely
public void setAccountBalance(double accountBalance) {
this.accountBalance = accountBalance;
}
}
In this example, the accountBalance variable is hidden from other classes by using the private access modifier. External classes can only access or modify the account balance through the public getter and setter methods, ensuring data protection.
Key Differences Between Abstraction and Data Hiding:
Abstraction focuses on hiding unnecessary implementation details and showing only essential features to the user.
Data Hiding restricts access to the internal data of a class, ensuring that only certain methods can access or modify it.
Both concepts work together to promote encapsulation, ensuring the security and integrity of the system’s internal workings.
In Java, constructors are used to initialize the attributes of an object, making the language more reflective of real-world scenarios. By default, Java provides a no-argument constructor that is automatically invoked if no constructors are defined. However, if you create a custom constructor, such as a parameterized one, you must explicitly define the default constructor, as it will no longer be provided automatically.
Note:
If a base class has a no-argument constructor, it is called automatically when a constructor of the derived class is invoked.
Example:
// Java Program to Demonstrate
// Constructor Invocation Without Using super Keyword
// Parent class
class Parent {
// Constructor of the parent class
Parent() {
System.out.println("Parent Class Constructor Called");
}
}
// Child class
class Child extends Parent {
// Constructor of the child class
Child() {
System.out.println("Child Class Constructor Called");
}
}
// Main class
public class Main {
public static void main(String[] args) {
// Creating an object of the child class
Child childObj = new Child();
// Note: The constructor of the parent class is invoked first,
// followed by the constructor of the child class.
}
}
Output:
Parent Class Constructor Called
Child Class Constructor Called
In Java, constructors are used to initialize the attributes of an object, making the language more reflective of real-world scenarios. By default, Java provides a no-argument constructor that is automatically invoked if no constructors are defined. However, if you create a custom constructor, such as a parameterized one, you must explicitly define the default constructor, as it will no longer be provided automatically.
Note:
If a base class has a no-argument constructor, it is called automatically when a constructor of the derived class is invoked.
Example:
// Java Program to Demonstrate
// Constructor Invocation Using super Keyword
// Base class
class Vehicle {
int speed;
// Parameterized constructor of the base class
Vehicle(int spd) {
speed = spd;
}
}
// Subclass
class Car extends Vehicle {
String model;
// Constructor of the subclass
Car(int spd, String mdl) {
// Using super to call the base class constructor
super(spd);
model = mdl;
}
// Method to display details
void displayDetails() {
System.out.println("Speed: " + speed + ", Model: " + model);
}
}
// Main class
public class Main {
public static void main(String[] args) {
// Creating an object of the subclass
Car carObj = new Car(120, "Sedan");
// Displaying details of the car
carObj.displayDetails();
}
}
Output:
Speed: 120, Model: Sedan
Explanation:
In this example, the super() keyword is used to call the base class (Vehicle) constructor with a parameter (speed). This call must be the first statement in the subclass (Car) constructor. Afterward, the model attribute is initialized, and the displayDetails() method prints the values of speed and model.
Key Points:
The base class constructor is always called before the subclass constructor.
The super() keyword is used to explicitly invoke the base class constructor and must be the first line in the subclass constructor.
If super() is not used, the default constructor of the base class is invoked automatically.
Multiple Inheritance
Multiple inheritance is an object-oriented concept where a class can inherit from more than one parent class. However, a problem arises when both parent classes have methods with the same signature. In such cases, the compiler is unable to decide which method to execute, leading to ambiguity. Java avoids these complexities by not supporting multiple inheritance through classes.
Note:
Java does not support multiple inheritance through classes, but it handles this scenario for interfaces using default methods introduced in Java 8.
Example 1:
// Java Program to Demonstrate Lack of Support for Multiple Inheritance
// First parent class
class Parent1 {
void display() {
System.out.println("Parent1");
}
}
// Second parent class
class Parent2 {
void display() {
System.out.println("Parent2");
}
}
// Class trying to inherit from both Parent1 and Parent2 (Invalid)
class Child extends Parent1, Parent2 {
public static void main(String[] args) {
Child obj = new Child();
obj.display(); // Ambiguity issue
}
}
Output:
Compilation Error
Explanation:
Java does not support multiple inheritance using classes. If you try to inherit from both Parent1 and Parent2, the compiler will throw an error because it cannot determine which display() method to call.
How Java Handles Multiple Inheritance with Interfaces
While Java doesn’t allow multiple inheritance through classes, it allows it through interfaces. Java 8 introduced default methods, which allow interfaces to provide default implementations of methods. If a class implements multiple interfaces with default methods that have the same signature, it must resolve the conflict by overriding the method.
Example 2: Handling Multiple Inheritance with Interfaces
// Interface 1
interface Interface1 {
default void show() {
System.out.println("Default Interface1");
}
}
// Interface 2
interface Interface2 {
default void show() {
System.out.println("Default Interface2");
}
}
// Implementation class that implements both interfaces
class MyClass implements Interface1, Interface2 {
// Overriding the show method to resolve ambiguity
@Override
public void show() {
Interface1.super.show(); // Explicitly calling Interface1's show method
Interface2.super.show(); // Explicitly calling Interface2's show method
}
// Methods to call specific interface methods
public void showInterface1() {
Interface1.super.show();
}
public void showInterface2() {
Interface2.super.show();
}
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.show(); // Resolving ambiguity
System.out.println("Calling individual interface methods:");
obj.showInterface1();
obj.showInterface2();
}
}
In this example, MyClass implements both Interface1 and Interface2, which both have a default show() method. By overriding the show() method in MyClass, we use Interface1.super.show() and Interface2.super.show() to specify which interface’s method to call. Additionally, the class provides methods to call individual interface methods if needed.
Example 3: Diamond Problem with Default Methods in Interfaces
// Root interface with a default method
interface RootInterface {
default void display() {
System.out.println("Default RootInterface");
}
}
// First child interface extending RootInterface
interface ChildInterface1 extends RootInterface {
}
// Second child interface extending RootInterface
interface ChildInterface2 extends RootInterface {
}
// Class implementing both child interfaces
class MyClass implements ChildInterface1, ChildInterface2 {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.display(); // No ambiguity, RootInterface's method is called
}
}
Output:
Default RootInterface
Explanation:
In this case, both ChildInterface1 and ChildInterface2 extend the same RootInterface, which has a default display() method. Since neither ChildInterface1 nor ChildInterface2 overrides the method, there is no ambiguity, and the RootInterface method is called directly.
Interfaces and Inheritance in Java
In Java, a class can extend another class and implement one or more interfaces. This relationship has a significant role in understanding Java’s approach to Multiple Inheritance.
Key Concepts:
1. Inheritance Hierarchy: Java enforces a clear hierarchy, where a class can inherit from another class using the extends keyword, and implement interfaces using the implements keyword. The hierarchy cannot be reversed; that is, a class cannot extend an interface, as doing so would violate the core principles of class-based inheritance.
Example of Class Implementing Multiple Interfaces
// Java program to demonstrate that a class can
// implement multiple interfaces
interface InterfaceA {
void method1();
}
interface InterfaceB {
void method2();
}
// class implements both interfaces
// and provides implementation for the methods
class ExampleClass implements InterfaceA, InterfaceB {
@Override
public void method1() {
System.out.println("Executing method1 from InterfaceA");
}
@Override
public void method2() {
System.out.println("Executing method2 from InterfaceB");
}
}
public class Main {
public static void main(String[] args) {
ExampleClass obj = new ExampleClass();
// calling the methods implemented in the class
obj.method1();
obj.method2();
}
}
2. Interface Inheritance: In Java, an interface can extend another interface, allowing for inheritance of method signatures, which can later be implemented in classes.
Example of Interface Inheritance
// Interface inheritance demonstration
interface InterfaceA {
void display();
}
interface InterfaceB extends InterfaceA {
void show();
}
class DemoClass implements InterfaceB {
@Override
public void display() {
System.out.println("Display method from InterfaceA");
}
@Override
public void show() {
System.out.println("Show method from InterfaceB");
}
}
public class Main {
public static void main(String[] args) {
DemoClass obj = new DemoClass();
obj.display();
obj.show();
}
}
Output:
Display method from InterfaceA
Show method from InterfaceB
Types of Inheritance in Java
Java supports different types of inheritance, such as single, multilevel, and hierarchical inheritance, while restricting multiple and hybrid inheritance through interfaces.
1. Single Inheritance: When a class inherits from only one superclass.
class A {
int num1;
void setNum1(int x) {
num1 = x;
}
}
class B extends A {
int num2;
void setNum2(int y) {
num2 = y;
}
void displayProduct() {
System.out.println("Product: " + (num1 * num2));
}
}
public class Main {
public static void main(String[] args) {
B obj = new B();
obj.setNum1(3);
obj.setNum2(4);
obj.displayProduct();
}
}
Output:
Product: 12
2. Multilevel Inheritance: Involves a chain of inheritance where a class is derived from another derived class.
class A {
int num1;
void setNum1(int x) {
num1 = x;
}
}
class B extends A {
int num2;
void setNum2(int y) {
num2 = y;
}
}
class C extends B {
void displaySum() {
System.out.println("Sum: " + (num1 + num2));
}
}
public class Main {
public static void main(String[] args) {
C obj = new C();
obj.setNum1(5);
obj.setNum2(10);
obj.displaySum();
}
}
Output:
Sum: 15
3. Hierarchical Inheritance: Multiple classes inherit from the same superclass.
class A {
void greet() {
System.out.println("Hello from Class A");
}
}
class B extends A {
void greetFromB() {
System.out.println("Greetings from Class B");
}
}
class C extends A {
void greetFromC() {
System.out.println("Greetings from Class C");
}
}
public class Main {
public static void main(String[] args) {
B objB = new B();
C objC = new C();
objB.greet();
objB.greetFromB();
objC.greet();
objC.greetFromC();
}
}
Output:
Hello from Class A
Greetings from Class B
Hello from Class A
Greetings from Class C
Inheritance in Interfaces
Java interfaces can inherit from multiple interfaces, enabling classes to implement complex behavior while avoiding ambiguity.
Employee Name: John
Department: IT
Role: Developer
Association, Composition and Aggregation in Java
In object-oriented programming, relationships between classes are fundamental for defining interactions between objects. Java, as an object-oriented language, allows modeling these relationships using association, aggregation, and composition. These concepts describe how instances of classes relate and interact with one another.
Association
Association represents a general relationship between two independent classes, where one class may use or interact with another. This relationship can be one-to-one, one-to-many, many-to-one, or many-to-many, without implying ownership. Classes in association are independent of each other.
Types of Association
Unidirectional Association: One class knows and interacts with another, but the reverse is not true. For example, a Customer class may be associated with an Order class, but the Order class does not need to be aware of the Customer.
Bidirectional Association: Both classes are aware of and interact with each other. For example, a Player class and a Team class might be associated in a bidirectional relationship, where a player belongs to a team, and the team knows which players are on it.
Example of Association:
// Java program illustrating Association
import java.util.*;
class Library {
private String libraryName;
private Set<Book> books;
public Library(String libraryName) {
this.libraryName = libraryName;
}
public String getLibraryName() {
return this.libraryName;
}
public void setBooks(Set<Book> books) {
this.books = books;
}
public Set<Book> getBooks() {
return this.books;
}
}
class Book {
private String title;
public Book(String title) {
this.title = title;
}
public String getTitle() {
return this.title;
}
}
public class AssociationExample {
public static void main(String[] args) {
// Creating Book objects
Book book1 = new Book("Java Basics");
Book book2 = new Book("Advanced Java");
// Adding books to a set
Set<Book> bookSet = new HashSet<>();
bookSet.add(book1);
bookSet.add(book2);
// Creating a Library object
Library library = new Library("City Library");
// Setting books for the library
library.setBooks(bookSet);
// Displaying library books
for (Book book : library.getBooks()) {
System.out.println(book.getTitle() + " is available at " + library.getLibraryName());
}
}
}
Output:
Java Basics is available at City Library
Advanced Java is available at City Library
In this example, the Library and Book classes are associated, where a library contains multiple books, forming a one-to-many relationship.
Aggregation
Aggregation is a specialized form of association that represents a “has-a” relationship. In aggregation, one class (the whole) contains other classes (the parts), but the lifecycle of the parts is independent of the whole. For instance, a University may contain multiple Departments, but a department can exist independently of the university.
Example of Aggregation:
// Java program illustrating Aggregation
import java.util.*;
class Course {
private String courseName;
private int courseId;
public Course(String courseName, int courseId) {
this.courseName = courseName;
this.courseId = courseId;
}
public String getCourseName() {
return this.courseName;
}
public int getCourseId() {
return this.courseId;
}
}
class Department {
private String deptName;
private List<Course> courses;
public Department(String deptName, List<Course> courses) {
this.deptName = deptName;
this.courses = courses;
}
public List<Course> getCourses() {
return this.courses;
}
}
class University {
private String universityName;
private List<Department> departments;
public University(String universityName, List<Department> departments) {
this.universityName = universityName;
this.departments = departments;
}
public int getTotalCoursesInUniversity() {
int totalCourses = 0;
for (Department dept : departments) {
totalCourses += dept.getCourses().size();
}
return totalCourses;
}
}
public class AggregationExample {
public static void main(String[] args) {
// Creating Course objects
Course c1 = new Course("Data Structures", 101);
Course c2 = new Course("Algorithms", 102);
Course c3 = new Course("Operating Systems", 103);
// Creating lists of courses
List<Course> csCourses = Arrays.asList(c1, c2);
List<Course> itCourses = Arrays.asList(c3);
// Creating Department objects
Department csDept = new Department("Computer Science", csCourses);
Department itDept = new Department("Information Technology", itCourses);
// Creating a list of departments
List<Department> departments = Arrays.asList(csDept, itDept);
// Creating a University object
University university = new University("Tech University", departments);
// Printing total courses in university
System.out.println("Total courses in university: " + university.getTotalCoursesInUniversity());
}
}
Output:
Total courses in university: 3
In this example, a university contains multiple departments, each having courses. The university and department are in an aggregation relationship, where departments can exist independently of the university.
Composition
Composition is a strong form of association where the lifecycle of the contained objects is dependent on the container object. If the container object is destroyed, the contained objects are also destroyed. This represents a “part-of” relationship, like a Car and its Engine. If the car is destroyed, the engine also ceases to exist.
Example of Composition:
// Java program illustrating Composition
import java.util.*;
class Engine {
private String engineType;
public Engine(String engineType) {
this.engineType = engineType;
}
public String getEngineType() {
return this.engineType;
}
}
class Car {
private String carName;
private Engine engine;
public Car(String carName, String engineType) {
this.carName = carName;
this.engine = new Engine(engineType); // Composition
}
public String getCarDetails() {
return this.carName + " has an engine of type " + engine.getEngineType();
}
}
public class CompositionExample {
public static void main(String[] args) {
// Creating a Car object with composition
Car car = new Car("Tesla", "Electric");
// Displaying car details
System.out.println(car.getCarDetails());
}
}
Output:
Tesla has an engine of type Electric
In this example, the Car and Engine have a composition relationship. The Engine cannot exist without the Car, representing strong ownership.
Key Differences Between Association, Aggregation, and Composition
Feature
Association
Aggregation
Composition
Definition
General relationship between two classes
“Has-a” relationship
“Part-of” relationship
Dependency
Classes can exist independently
Parts can exist independently of the whole
Parts depend on the whole
Lifecycle
Independent lifecycles
Independent lifecycles
Dependent lifecycles
Ownership
No ownership implied
Shared ownership
Exclusive ownership
Strength
Weak
Moderate
Strong
These relationships play a key role in designing well-structured, reusable, and maintainable software systems.
Object Oriented Programming (OOPs) Concept in Java
Object-Oriented Programming (OOP) in Java refers to a programming approach where objects are used to represent and execute the code’s functionality. Objects are the core components, performing specific tasks as defined by the programmer.
OOP is designed to model real-world entities, such as inheritance, encapsulation, polymorphism, etc., in code. The primary objective is to bind data and the functions that operate on the data, ensuring that no other part of the program can directly access this data except through those functions.
Key OOP Concepts in Java
Java is built on the foundation of Object-Oriented Programming. Understanding key OOP concepts such as inheritance, encapsulation, abstraction, and polymorphism is critical to writing scalable and maintainable Java code.
Prerequisites
Before diving into the pillars of OOP, let’s review the essentials of method declaration and message passing in Java.
Method Declaration
A method in Java consists of six main components:
1. Access Modifier: Defines the accessibility of the method within the application. The common types are:
public: Accessible in all classes.
protected: Accessible within the package and in subclasses.
private: Accessible only within the class.
Default (no modifier): Accessible only within the same package.
2. Return Type: Specifies the data type returned by the method, or void if no value is returned. 3. Method Name: Follows the same naming conventions as fields, but typically starts with a lowercase letter. 4. Parameter List: The input parameters (if any), defined as a comma-separated list within parentheses. 5. Exception List: Specifies any exceptions that the method might throw. 6. Method Body: Contains the block of code that is executed when the method is called.
Message Passing
Objects in Java communicate with one another by sending messages, which involve calling methods. A message consists of the object’s name, the method to be executed, and any required information.
Java OOP Concepts
OOP in Java is centered around several key concepts:
1. Class: A class is a blueprint or prototype that defines the properties and behaviors common to all objects of a specific type. A class typically consists of fields (attributes) and methods (behaviors). In Java, a class can be defined as:
public class Employee {
// Fields
String name;
int salary;
// Methods
void setDetails(String n, int s) {
name = n;
salary = s;
}
void displayDetails() {
System.out.println("Name: " + name);
System.out.println("Salary: " + salary);
}
}
2. Object: An object is an instance of a class. It represents a specific entity in a Java program, with its own state and behavior.
public class Main {
public static void main(String[] args) {
Employee emp = new Employee();
emp.setDetails("John Doe", 50000);
emp.displayDetails();
}
}
Output:
Name: John Doe
Salary: 50000
The Four Pillars of OOP
1. Abstraction: Abstraction involves hiding unnecessary details and showing only the essential features of an object. In Java, abstraction is achieved using abstract classes and interfaces.
Example of an abstract class:
abstract class Vehicle {
abstract void start();
}
class Car extends Vehicle {
void start() {
System.out.println("Car is starting");
}
}
2. Encapsulation: Encapsulation is the practice of wrapping data and methods into a single unit (class). It restricts direct access to the data by making the fields private and providing public getter and setter methods to modify and access them.
class Person {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
3. Inheritance: Inheritance allows a class to inherit properties and methods from another class. It promotes reusability and a hierarchical class structure. Inheritance is achieved using the extends keyword.
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
void sound() {
System.out.println("Dog barks");
}
}
4. Polymorphism: Polymorphism allows methods or objects to behave in different ways based on the context. It is of two types: compile-time (method overloading) and runtime (method overriding).
Method Overloading (Compile-time Polymorphism):
class Calculator {
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
}
Advantages of OOP over Procedural Programming
Code Reusability: By using classes and objects, you can reuse code efficiently without repetition.
Code Organization: Object-oriented code is better structured, making it easier to understand and maintain.
DRY Principle: OOP promotes the “Don’t Repeat Yourself” principle by allowing common code to be reused.
Faster Development: OOP speeds up development by encouraging modular components that can be reused across projects.
Java Classes and Objects
In Java, classes and objects are core concepts of Object-Oriented Programming (OOP), used to represent real-world entities and behaviors. A class acts as a blueprint for creating objects, while an object is an instance of a class. For example, “Dog” can be considered a class, while a specific dog named “Bruno” would be an object of that class.
In this article, we’ll explore Java classes, objects, and how to implement them in your programs.
Java Classes
A class in Java is a collection of objects with common properties and behaviors. It is essentially a user-defined prototype from which objects are created. For instance, “Car” is a class, while “BMW” is an object.
Properties of Java Classes
A class is not a physical entity but a blueprint used to create objects.
A class itself does not occupy memory; only objects do.
It groups variables (attributes) and methods (behaviors) under a common structure.
A class in Java can include the following components:
In this example, the Student class has two data members, id and name, with default values 0 and null, respectively.
Components of Java Classes
A class declaration typically contains the following:
Modifiers: A class can be public or have default access.
Class keyword: Used to declare a class.
Class name: Starts with an uppercase letter by convention.
Superclass: The parent class (if any), specified with the extends keyword.
Interfaces: Any implemented interfaces, separated by commas and declared with the implements keyword.
Body: The class body is enclosed in curly braces {}.
Constructors
Constructors initialize new objects. They have the same name as the class and no return type. Fields (data members) define the state of objects, while methods define the behavior.
Java Objects
An object in Java is an instance of a class, representing a specific entity or concept. Each object has:
State: Represented by the attributes (data members) of the object.
Behavior: Represented by the methods of the object.
Identity: A unique identifier that allows interaction between different objects.
For example, in a shopping system, objects could include “ShoppingCart”, “Product”, and “Customer”.
Declaring and Initializing Java Objects
When an object is created, the class is instantiated. The values of the object’s attributes (state) are unique for each instance.
Dog myDog; // Declaration
myDog = new Dog(); // Instantiation
Example of an Object Initialization
public class Dog {
// Instance variables
String name;
String breed;
int age;
String color;
// Constructor
public Dog(String name, String breed, int age, String color) {
this.name = name;
this.breed = breed;
this.age = age;
this.color = color;
}
@Override
public String toString() {
return "My dog's name is " + name + ", breed: " + breed + ", age: " + age + ", color: " + color;
}
public static void main(String[] args) {
Dog myDog = new Dog("Buddy", "Golden Retriever", 3, "Golden");
System.out.println(myDog.toString());
}
}
Output:
My dog's name is Buddy, breed: Golden Retriever, age: 3, color: Golden
Memory Allocation in Java Objects
When an object is created, it is allocated memory on the heap. All classes have at least one constructor; if none is explicitly defined, Java provides a default constructor.
Methods to Create Objects
There are several ways to create objects in Java:
1. Using the new keyword:
Car myCar = new Car();
2. Using Class.forName(String className):
Car obj = (Car)Class.forName("com.example.Car").newInstance();
3. Using clone() method:
Car car1 = new Car();
Car car2 = (Car)car1.clone();
4. Using Deserialization:
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
Car car = (Car)in.readObject();
Anonymous Objects
Anonymous objects are objects created without a reference and are often used for immediate method calls. These objects are destroyed after the method call completes.
new Car().drive();
Difference Between Class and Object
Class
Object
A blueprint for creating objects.
An instance of the class.
Does not occupy memory.
Occupies memory when instantiated.
Logical entity.
Physical entity.
Declared only once.
Multiple objects can be created from the same class.
Java Naming Conventions
In software development, especially in Java, writing clean and readable code is essential. One key aspect of this is following proper naming conventions. This practice ensures that your code is easy to read and understand, both for yourself and for other developers who may work on the same project. While at a smaller scale these conventions may not seem critical, they become increasingly important in large, industrial-level projects where maintaining clean, organized code can significantly reduce development time and confusion.
For example, when naming a variable meant to store displacement, a meaningful name like “displacement” should be used, rather than something unclear like “x” or “d”. As the project grows, meaningful names enhance readability and reduce errors.
Key Naming Guidelines
Classes: When naming a class, it should be a noun, reflecting the purpose it serves in the program. The name should describe what the class is about without needing to read its contents. Examples of good class names include AddNumbers, ReverseString, etc. Poor names would be generic or meaningless like A1 or Programming.
Interfaces: Interfaces usually describe capabilities and are often named with adjectives. For example, common Java interfaces like Runnable and Serializable describe behavior. It’s also a good practice to use the suffix “able” (e.g., Readable), though it’s not mandatory.
Methods: Methods, being actions, should be named as verbs. Their names should clearly describe what they do. For example, the main() method in Java signifies the starting point of the program and is a good method name conventionally.
Constants: Constants are fixed values and should be named in uppercase with words separated by underscores (_). Examples include PI, MAX_VALUE, MIN_VALUE.
Java Naming Conventions Overview
In Java, it is considered best practice to name classes, variables, and methods according to their intended functionality. This approach enhances the readability and maintainability of the code. Java follows the CamelCase convention for naming classes, methods, and variables, where words are written together without spaces, and each word after the first starts with an uppercase letter. For example, firstName or calculateArea.
However, there are a few exceptions:
Packages: Package names should be written in all lowercase, even if they contain multiple words.
Constants: Constants should be written in uppercase, with words separated by underscores.
Naming Conventions for Specific Java Elements
1. Classes and Interfaces
Classes: Class names should be nouns, using CamelCase, with each internal word starting with a capital letter. Avoid abbreviations or acronyms, and instead use meaningful words.Example: Student, Integer, DataProcessor
Interfaces: Interfaces should follow the same convention as classes, but should often reflect actions or behaviors, typically in the form of adjectives.Example: Runnable, Serializable
2. Methods
Method Names: Methods should be verbs, following the camelCase convention. The first word is in lowercase, and each subsequent word starts with an uppercase letter.Example: calculateTotal, findMaximum, printDetails
3. Variables
Variable Names: Variables should have meaningful names and be short but descriptive. They should not start with an underscore (_) or dollar sign ($), even though Java permits it. Single-character variable names (like i, j, k) should generally be reserved for temporary variables in loops or similar short-term uses.Example: int score, double interestRate, String username
4. Constants
Constant Names: Constants are named in all uppercase letters, with words separated by underscores (_).Example: MAX_LIMIT, PI, DEFAULT_TIMEOUT
5. Packages
Package Names: Package names should always be written in lowercase, with each part separated by dots (.). The first part of the package name should usually be the top-level domain name (e.g., com, org), followed by the company’s domain and additional parts.Example: com.example.projectname, org.utilities.math
Importance of Naming Conventions
Adopting proper naming conventions improves code clarity, making it easier for other developers to understand the purpose of each class, method, variable, and constant without having to examine the underlying implementation. Following these guidelines also helps avoid confusion and errors in larger codebases. Furthermore, adhering to established naming conventions is a sign of professionalism and helps teams maintain a consistent coding style across projects.
These conventions, though simple, are critical in producing clean, readable, and maintainable Java code—allowing teams to work more effectively and reducing the chances of misinterpretation or mistakes during development.
Methods in Java
A method in Java is a collection of statements designed to perform a specific task and, optionally, return a result. Methods can also execute tasks without returning any value. By using methods, Java allows code reuse, eliminating the need to rewrite similar logic multiple times. Unlike languages such as C, C++, or Python, every method in Java must belong to a class.
Key Points about Methods:
A method is similar to a function and is used to describe the behavior of an object.
It is a block of code that performs a particular task.
Syntax of a Method:
<access_modifier> <return_type> <method_name>(list_of_parameters) {
// body of the method
}
Advantages of Methods:
Code Reusability: Methods allow code to be written once and reused as needed, saving time and effort.
Code Optimization: Methods simplify complex operations and improve the structure of the code.
Method Declaration
A method declaration generally has six components:
1. Modifier: Defines the access level of the method (e.g., public, private, protected, default).
public: Accessible in all classes.
protected: Accessible within the class and its subclasses.
private: Accessible only within the defining class.
default: Accessible within the same class and package, with no modifier.
2. Return Type: Specifies the data type of the value returned by the method or void if no value is returned. 3. Method Name: The name given to the method, following the same rules as variable names but typically being a verb. It is required. 4. Parameter List: A comma-separated list of input parameters (preceded by their data types). If no parameters are needed, empty parentheses are used. This is optional. 5. Exception List: Specifies any exceptions the method can throw. This is optional. Method Body: The actual code executed by the method, enclosed in braces. This is also optional.
Types of Methods in Java
1. Predefined Methods: These are methods already provided by Java libraries, also known as standard or built-in methods. We can directly call these methods within our program.
2. User-defined Methods: Methods written by the programmer, tailored to specific tasks according to the program’s needs.
Ways to Create Methods in Java
1. Instance Methods: Access instance data using the object name. These methods are declared within a class.
void methodName() {
// method body
}
2. Static Methods: Access static data using the class name. These methods are declared with the static keyword.
static void methodName() {
// method body
}
Method Signature
A method signature consists of the method name and its parameter list (the number, type, and order of parameters). The return type and exceptions are not part of the method signature.
Example:
int max(int x, int y) // Method signature
In this example, the method max has two parameters of type int.
Naming a Method
The convention for naming methods in Java is to use verbs in lowercase or multi-word names starting with a verb. If the name contains multiple words, the first letter of each word after the first should be capitalized (CamelCase).
Rules:
Method names should be verbs and start with a lowercase letter.
If the method name contains more than one word, the first word must be a verb, followed by an adjective or noun.
In multi-word names, capitalize the first letter of each subsequent word (e.g., findMax, computeSum).
Method Calling
Methods need to be called to execute. They can be called in three situations:
The method completes all its statements.
The method reaches a return statement.
The method throws an exception.
Example:
class Calculator {
public int add(int a, int b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
int result = calc.add(5, 10);
System.out.println("Sum: " + result);
}
}
Output:
Sum: 15
Passing Parameters to Methods
Parameters can be passed in two ways:
1. Passing Arrays: Arrays can be passed as arguments to methods.
2. Varargs (Variable Arguments): When the number of parameters is unknown, we can use the varargs feature by specifying the parameter type followed by an ellipsis (...).
Method Overloading
Method overloading allows multiple methods with the same name but different parameter lists to coexist in the same class. This enables methods to handle various types of inputs.
Memory Allocation for Methods
Method calls are implemented using a stack. When a method is invoked, a stack frame is created to store its arguments, local variables, and return value. Once the method completes, its stack frame is removed from the stack.
Example:
class Example {
private int number;
private String name;
// Getter and Setter methods
public int getNumber() { return number; }
public String getName() { return name; }
public void setNumber(int number) { this.number = number; }
public void setName(String name) { this.name = name; }
// Other methods
public void printDetails() {
System.out.println("Number: " + number);
System.out.println("Name: " + name);
}
}
public class Main {
public static void main(String[] args) {
Example example = new Example();
example.setNumber(42);
example.setName("Java Example");
example.printDetails();
}
}
Output:
Number: 42
Name: Java Example
Access Modifiers in Java
Access modifiers in Java are essential for controlling the visibility and accessibility of classes, constructors, methods, variables, and data members. They provide a way to enforce security and manage how components of a program can interact with each other based on the access modifier used. In this section, we’ll explore the types of access modifiers available in Java, along with examples that demonstrate their use.
Types of Access Modifiers in Java
There are four access modifiers in Java:
1. Default (No keyword required) 2. Private 3. Protected 4. Public
1. Default Access Modifier
If no access modifier is specified, the default modifier is used. Members with the default access modifier are accessible only within the same package.
Example:
// Java program demonstrating default access modifier
package package1;
class ExampleClass {
void showMessage() {
System.out.println("This is a default access modifier example.");
}
}
// Attempting to access a default class from another package
package package2;
import package1.*;
public class TestClass {
public static void main(String[] args) {
ExampleClass example = new ExampleClass(); // Compile-time error
example.showMessage();
}
}
Output:
Compile-time error
2. Private Access Modifier
The private keyword is used to restrict access to class members. Members declared as private are accessible only within the class where they are defined.
Example:
// Java program illustrating private access modifier
package package1;
class ExampleClass {
private void displayMessage() {
System.out.println("This is a private method.");
}
}
public class TestClass {
public static void main(String[] args) {
ExampleClass example = new ExampleClass();
example.displayMessage(); // Compile-time error
}
}
Output:
error: displayMessage() has private access in ExampleClass
example.displayMessage();
3. Protected Access Modifier
The protected keyword allows access to members within the same package and also in subclasses, even if they are in different packages.
Example:
// Package 1 - Parent class with protected method
package package1;
public class ParentClass {
protected void displayMessage() {
System.out.println("This is a protected method.");
}
}
// Package 2 - Subclass inheriting from ParentClass
package package2;
import package1.ParentClass;
public class ChildClass extends ParentClass {
public static void main(String[] args) {
ChildClass child = new ChildClass();
child.displayMessage();
}
}
Output:
This is a protected method.
4. Public Access Modifier
The public keyword gives the widest access. Members declared as public can be accessed from any class, in any package.
Example:
// Java program demonstrating public access modifier
package package1;
public class ExampleClass {
public void showMessage() {
System.out.println("This is a public method.");
}
}
// Accessing a public method from another package
package package2;
import package1.ExampleClass;
public class TestClass {
public static void main(String[] args) {
ExampleClass example = new ExampleClass();
example.showMessage();
}
}
Output:
This is a public method.
Key Points:
Default access is package-private, meaning it’s accessible only within the same package.
Private access is the most restrictive, limiting access only to the class in which the member is declared.
Protected access allows visibility in the same package and in subclasses across different packages.
Public access provides unrestricted access throughout the program.
Best Practices for Using Access Modifiers
Always prefer the most restrictive access level for class members. For example, use private unless there’s a specific reason to expose a member.
Avoid using public fields, as this breaks encapsulation. Instead, use getter and setter methods to control access to fields.
Algorithm for Using Access Modifiers in Java
1. Define the class: Create a class representing your object or functionality. 2. Declare instance variables: Define the data members that represent the object’s state. 3. Choose access modifiers:
Use private for variables that should not be accessible outside the class.
Use protected for variables that can be accessed by subclasses, even in different packages.
Use public for variables that need to be accessed from any part of the program. Implement getter and setter methods: Even for public variables, it’s good practice to use accessors and mutators to control how values are accessed and modified.
4. Test visibility: Ensure that your chosen access levels make sense for the structure and interaction of your classes.
Java Constructors
In Java, a constructor is a special type of method used to initialize an object. Constructors are called when an object of a class is created, allowing developers to set initial values for the object’s attributes.
Whenever you create an object using the new keyword, a constructor is invoked to allocate memory for the object and initialize it.
Understanding constructors can greatly enhance your Java programming skills, particularly when working with large applications. Mastery of constructors is crucial for developing scalable, maintainable code, serving as a stepping stone toward more advanced programming concepts.
Example of a Java Constructor
Below is a simple demonstration of how constructors work in Java:
// Java Program to demonstrate
// Constructor usage
public class MyClass {
// Constructor
MyClass() {
System.out.println("Constructor called");
}
public static void main(String[] args) {
// Creating an object which calls the constructor
MyClass obj = new MyClass();
}
}
Output:
Constructor called
Differences Between Constructors and Methods
Name: A constructor must have the same name as the class, whereas methods can have any name.
Return Type: Constructors do not have a return type, while methods can return values (or be void if they return nothing).
Invocation: Constructors are called only once when an object is created, whereas methods can be called multiple times.
Syntax Example of a constructor:
class MyClass {
// A constructor
MyClass() {
// Initialization code here
}
}
// Creating an object, which invokes the constructor
MyClass obj = new MyClass();
Output:
Constructor called
Differences Between Constructors and Methods
Name: A constructor must have the same name as the class, whereas methods can have any name.
Return Type: Constructors do not have a return type, while methods can return values (or be void if they return nothing).
Invocation: Constructors are called only once when an object is created, whereas methods can be called multiple times.
Syntax example of a constructor:
class MyClass {
// A constructor
MyClass() {
// Initialization code here
}
}
// Creating an object, which invokes the constructor
MyClass obj = new MyClass();
Why Do We Need Constructors?
Constructors are used to initialize objects at the time of creation. For example, if we think of a class that represents a box, it might have fields like length, breadth, and height. When an object of this class is created, it wouldn’t make sense for it to have undefined dimensions, so constructors ensure these values are set.
When Is a Constructor Called?
A constructor is called whenever an object is instantiated using the new keyword. Some rules for writing constructors include:
The constructor must have the same name as the class.
A constructor cannot be abstract, final, static, or synchronized.
Access modifiers can be used with constructors to control which classes can instantiate objects.
1. Default Constructor : A default constructor is one without parameters. If no constructor is defined explicitly, Java automatically provides a default constructor.
Example:
// Java Program to demonstrate Default Constructor
public class MyClass {
// Default Constructor
MyClass() {
System.out.println("Default constructor called");
}
public static void main(String[] args) {
MyClass obj = new MyClass();
}
}
Output:
Default constructor called
2. Parameterized Constructor : A parameterized constructor takes arguments, allowing you to set object properties with specific values at the time of creation.
Example:
// Java Program to demonstrate Parameterized Constructor
public class MyClass {
String name;
int id;
// Parameterized Constructor
MyClass(String name, int id) {
this.name = name;
this.id = id;
}
public static void main(String[] args) {
MyClass obj = new MyClass("John", 101);
System.out.println("Name: " + obj.name + ", ID: " + obj.id);
}
}
Output:
Name: John, ID: 101
3. Copy Constructor : A copy constructor is used to create a new object as a copy of an existing object. While Java does not provide a built-in copy constructor, you can create one manually by passing an object of the same class to the constructor.
Example:
// Java Program to demonstrate Copy Constructor
public class MyClass {
String name;
int id;
// Parameterized Constructor
MyClass(String name, int id) {
this.name = name;
this.id = id;
}
// Copy Constructor
MyClass(MyClass obj) {
this.name = obj.name;
this.id = obj.id;
}
public static void main(String[] args) {
// Creating an object using a parameterized constructor
MyClass obj1 = new MyClass("Alice", 202);
// Creating a new object using the copy constructor
MyClass obj2 = new MyClass(obj1);
System.out.println("Name: " + obj2.name + ", ID: " + obj2.id);
}
}
Output:
Name: Alice, ID: 202
Constructor Overloading
Just like methods, constructors can also be overloaded by defining multiple constructors with different parameter lists. This allows for flexibility when creating objects with varying initial values.
Example:
// Java Program to demonstrate Constructor Overloading
public class MyClass {
// Constructor with one parameter
MyClass(String name) {
System.out.println("Name: " + name);
}
// Constructor with two parameters
MyClass(String name, int age) {
System.out.println("Name: " + name + ", Age: " + age);
}
public static void main(String[] args) {
// Calling different constructors
MyClass obj1 = new MyClass("Alice");
MyClass obj2 = new MyClass("Bob", 25);
}
}
Output:
Name: Alice
Name: Bob, Age: 25
Inheritance in Java
Java Inheritance Overview
Inheritance is one of the core concepts in Object-Oriented Programming (OOP), allowing a class to inherit properties and behaviors (fields and methods) from another class. In Java, this mechanism enables the creation of new classes that reuse, extend, and modify the functionality of existing ones. A class that inherits from another class can access the fields and methods of the parent class, while also adding its own methods and fields.
Why Do We Need Java Inheritance?
Inheritance offers several benefits, such as promoting code reusability, method overriding (which helps in achieving runtime polymorphism), and abstraction, allowing us to work with classes without needing to know the full details of their implementations. By leveraging inheritance, developers can create flexible and easily maintainable code.
Key Benefits:
Code Reusability: Common code in the superclass can be reused in the child classes, reducing redundancy.
Method Overriding: Enables the child class to provide specific implementations for methods defined in the parent class, facilitating runtime polymorphism.
Abstraction: Hides complex details from the user and only shows relevant functionalities.
Important Terms
Class: A blueprint for creating objects that share common attributes and behaviors.
Super Class (Parent Class): The class whose properties are inherited by another class.
Sub Class (Child Class): The class that inherits from another class. It can have additional fields and methods specific to itself.
Reusability: One of the major advantages of inheritance, where existing fields and methods from a parent class can be used in the child class without duplicating code.
How to Implement Inheritance in Java?
Inheritance in Java is implemented using the extends keyword. This keyword allows the subclass to extend the functionality of the superclass.
Syntax:
class SubClass extends SuperClass {
// fields and methods
}
Example of Java Inheritance
In this example, we have a base class Vehicle and a derived class Car that extends the Vehicle class. The TestDrive class is used to demonstrate the usage of inheritance.
// Base class
class Vehicle {
public int speed;
public int fuel;
// Constructor
public Vehicle(int speed, int fuel) {
this.speed = speed;
this.fuel = fuel;
}
// Methods
public void accelerate(int increment) {
speed += increment;
}
public void brake(int decrement) {
speed -= decrement;
}
@Override
public String toString() {
return "Speed: " + speed + " km/h, Fuel: " + fuel + " L";
}
}
// Derived class
class Car extends Vehicle {
public int numberOfDoors;
// Constructor
public Car(int speed, int fuel, int numberOfDoors) {
super(speed, fuel); // Calling the constructor of the superclass
this.numberOfDoors = numberOfDoors;
}
// Additional method for the Car class
public void lockDoors() {
System.out.println("Doors locked.");
}
@Override
public String toString() {
return super.toString() + ", Doors: " + numberOfDoors;
}
}
// Driver class
public class TestDrive {
public static void main(String[] args) {
Car myCar = new Car(80, 50, 4);
System.out.println(myCar.toString());
}
}
Output:
Speed: 80 km/h, Fuel: 50 L, Doors: 4
In this example, when an object of Car is created, the fields and methods from the Vehicle class are inherited, allowing the car to access properties like speed and fuel. The toString() method is overridden in the Car class to display additional information about the car.
Types of Inheritance in Java
Java supports several types of inheritance, each with its own structure and use cases:
1. Single Inheritance: A subclass inherits from one superclass. 2. Multilevel Inheritance: A chain of classes where a subclass inherits from a superclass, and another class inherits from that subclass. 3. Hierarchical Inheritance: One superclass is inherited by multiple subclasses. 4. Multiple Inheritance (Through Interfaces): In Java, a class can implement multiple interfaces, which allows it to inherit features from multiple sources. Java doesn’t support multiple inheritance with classes directly to avoid complexity, but interfaces provide a way to achieve this. 5. Hybrid Inheritance: A combination of multiple and other forms of inheritance. Java supports this through interfaces and combinations of single and hierarchical inheritance. Example:
In the following example, Dog is a subclass that inherits from the Animal superclass.
// Superclass
class Animal {
public void sound() {
System.out.println("This is an animal sound.");
}
}
// Subclass
class Dog extends Animal {
public void bark() {
System.out.println("The dog barks.");
}
}
// Driver class
public class TestAnimal {
public static void main(String[] args) {
Dog myDog = new Dog();
myDog.sound(); // Inherited from Animal
myDog.bark(); // Defined in Dog
}
}
Output:
This is an animal sound.
The dog barks.
Abstraction in Java
Abstraction in Java refers to the practice of showing only the necessary details and functionalities to the user while hiding the underlying implementation. By doing so, unnecessary complexities are kept out of sight, making it easier to work with the code.
Real-World Example of Abstraction:
A remote control for a TV is a great example of abstraction. You press buttons to change channels or adjust the volume, without needing to understand the inner workings of the TV or how the remote communicates with it.
What is Abstraction in Java?
In Java, abstraction is implemented using abstract classes and interfaces. If you want complete (100%) abstraction, interfaces are the tool of choice.
Data Abstraction is the process of focusing on the important characteristics of an object while ignoring irrelevant details. It helps in defining the key behaviors and properties of an object, distinguishing it from others in a meaningful way.
Real-Life Example of Abstraction:
Imagine driving a car. You know pressing the accelerator increases the speed, and pressing the brakes slows it down, but you don’t need to understand the detailed mechanism behind it. This is how abstraction simplifies interaction.
Java Abstract Classes and Abstract Methods
An abstract class is defined with the abstract keyword.
An abstract method is declared without any implementation.
An abstract class can have both abstract methods and regular methods.
If a class contains abstract methods, it must be declared as abstract.
Abstract classes cannot be instantiated directly; they must be extended.
Abstract classes can have constructors, both parameterized and default.
How to Implement Abstraction in Java
1. Define an abstract class or interface to represent the abstraction. 2. Declare common behaviors and properties as abstract methods within the class or interface. 3. Extend the abstract class or implement the interface in concrete classes. 4. Provide the specific implementations for the abstract methods in the concrete classes. 5. Use these concrete classes to achieve the desired functionality in your program.
When to Use Abstract Classes and Methods?
Abstract classes are useful when you want to define a template that outlines the structure of an abstraction but leave some details to be implemented by subclasses. A common example is the Shape hierarchy in design or simulation software, where shapes like circles, squares, and triangles share some characteristics but differ in how they calculate their areas.
Java Abstraction Example
Example 1:
// Abstract class for Shape
abstract class Shape {
String color;
// Abstract methods
abstract double area();
public abstract String toString();
// Constructor for Shape
public Shape(String color) {
this.color = color;
}
// Concrete method
public String getColor() {
return color;
}
}
// Circle class extending Shape
class Circle extends Shape {
double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
double area() {
return Math.PI * Math.pow(radius, 2);
}
@Override
public String toString() {
return "Circle color is " + getColor() + " and area is: " + area();
}
}
// Rectangle class extending Shape
class Rectangle extends Shape {
double length, width;
public Rectangle(String color, double length, double width) {
super(color);
this.length = length;
this.width = width;
}
@Override
double area() {
return length * width;
}
@Override
public String toString() {
return "Rectangle color is " + getColor() + " and area is: " + area();
}
}
// Main class
public class Main {
public static void main(String[] args) {
Shape circle = new Circle("Red", 5.0);
Shape rectangle = new Rectangle("Blue", 4.0, 6.0);
System.out.println(circle.toString());
System.out.println(rectangle.toString());
}
}
Output:
Circle color is Red and area is: 78.53981633974483
Rectangle color is Blue and area is: 24.0
Using Interfaces for Abstraction
Interfaces provide a way to achieve complete abstraction in Java. They contain method declarations but no method implementations.
Example:
// Shape interface
interface Shape {
double calculateArea();
}
// Circle class implementing Shape interface
class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
// Rectangle class implementing Shape interface
class Rectangle implements Shape {
private double length, width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double calculateArea() {
return length * width;
}
}
// Main class
public class Main {
public static void main(String[] args) {
Shape circle = new Circle(5.0);
Shape rectangle = new Rectangle(4.0, 6.0);
System.out.println("Area of Circle: " + circle.calculateArea());
System.out.println("Area of Rectangle: " + rectangle.calculateArea());
}
}
Output:
Area of Circle: 78.53981633974483
Area of Rectangle: 24.0
Advantages of Abstraction
1. Simplifies Code: Reduces complexity by hiding implementation details. 2. Increases Reusability: Encourages code reuse and avoids duplication. 3. Enhances Security: Provides essential details while hiding the rest. 4. Improves Maintainability: Easier to update and maintain. 5. Supports Modularity: Breaks down complex systems into smaller parts.
Disadvantages of Abstraction
1. Increased Complexity: May add unnecessary layers if not used properly. 2. Debugging Challenges: Can make debugging more difficult due to hidden layers. 3. Potential Performance Overhead: Additional abstraction layers might affect performance.
Encapsulation in Java
Encapsulation is a core principle of object-oriented programming (OOP) in Java. It refers to the bundling of data (variables) and methods (functions) that operate on that data into a single unit, typically a class. Encapsulation ensures that the internal implementation of a class is hidden from outside access, only exposing a public interface that allows interaction with the class through methods. This technique enhances security and data integrity, as the internal state of an object can only be modified in controlled ways.
In Java, encapsulation is achieved by declaring instance variables as private, which means they cannot be accessed directly from outside the class. To provide access to these variables, public getter and setter methods are used, which enable controlled access and modification of the private variables.
Key Aspects of Encapsulation
Data hiding: By using private variables, you ensure that the internal details of a class are hidden.
Access through methods: Public getter and setter methods allow controlled access to the variables.
Modularity: It promotes better design by keeping data and methods together in a single unit.
Security and validation: Setters can include validation logic to prevent invalid data from being assigned to variables.
Java Encapsulation Example
// Employee Class demonstrating encapsulation
class Employee {
// Private fields, not accessible outside the class
private String name;
private int id;
// Getter method for name
public String getName() {
return name;
}
// Setter method for name
public void setName(String name) {
this.name = name;
}
// Getter method for id
public int getId() {
return id;
}
// Setter method for id
public void setId(int id) {
this.id = id;
}
}
// Main class to test encapsulation
public class Main {
public static void main(String[] args) {
// Creating an object of the Employee class
Employee emp = new Employee();
// Using setter methods to assign values
emp.setName("Alice");
emp.setId(101);
// Using getter methods to retrieve and display values
System.out.println("Employee Name: " + emp.getName());
System.out.println("Employee ID: " + emp.getId());
}
}
Output:
Employee Name: Alice
Employee ID: 101
Advantages of Encapsulation
Data Hiding: Encapsulation prevents direct access to sensitive data, making it more secure.
1. Improved Flexibility: You can make instance variables read-only or write-only by providing only a getter or setter method, depending on the requirement. 2. Reusability: Encapsulated code is easier to maintain, reuse, and extend because it minimizes interdependencies. 3. Easy Testing: Since encapsulated code separates data and functions, it is easier to test individual components. 4. Adaptability: The internal implementation can change without affecting code that interacts with the class.
Disadvantages of Encapsulation
Increased Complexity: Encapsulation may lead to more boilerplate code, such as writing getter and setter methods for each private variable.
Difficult to Understand: Excessive use of encapsulation can make code more difficult to follow, especially if it adds layers of abstraction.
Limited Flexibility: Over-encapsulation can sometimes restrict the flexibility of your design, making it harder to quickly implement new features.
Java Encapsulation Example (Area Calculation)
Here’s another example that demonstrates encapsulation in a different context, where it is used to calculate the area of a rectangle:
// Rectangle Class to calculate area using encapsulation
class Rectangle {
// Private fields for length and breadth
private int length;
private int breadth;
// Constructor to initialize length and breadth
public Rectangle(int length, int breadth) {
this.length = length;
this.breadth = breadth;
}
// Getter for length
public int getLength() {
return length;
}
// Setter for length
public void setLength(int length) {
this.length = length;
}
// Getter for breadth
public int getBreadth() {
return breadth;
}
// Setter for breadth
public void setBreadth(int breadth) {
this.breadth = breadth;
}
// Method to calculate and print area
public void calculateArea() {
int area = length * breadth;
System.out.println("Area of the Rectangle: " + area);
}
}
// Main class to test encapsulation in rectangle class
public class Main {
public static void main(String[] args) {
// Creating a rectangle object
Rectangle rect = new Rectangle(5, 10);
// Accessing and modifying private fields via getters and setters
rect.setLength(8);
rect.setBreadth(12);
// Calculating and printing the area
rect.calculateArea();
}
}
Output:
Area of the Rectangle: 96
What are Interfaces in Java?
In Java, an interface is an abstract type used to specify a set of behaviors that a class must implement. It provides a blueprint for a class, defining static constants and abstract methods (methods without a body). An interface is primarily used to achieve abstraction and multiple inheritance. Traditionally, interfaces in Java could only contain abstract methods and final variables, but with the introduction of Java 8, interfaces can also contain default and static methods. Interfaces help define a “contract” or “is-a” relationship between classes, specifying the methods they must implement, without worrying about the internal details.
Syntax for Java Interfaces
To declare an interface in Java, use the interface keyword. By default, all methods declared in an interface are abstract and public, and all fields are public, static, and final.
A class that implements an interface must provide implementations for all the methods declared in the interface, using the implements keyword.
Features of Interfaces
1. Achieves total abstraction: An interface allows complete abstraction since it contains no method implementation. 2. Multiple inheritance: While Java does not support multiple inheritance with classes, an interface allows classes to implement multiple interfaces. 3. Loose coupling: Interfaces enable loose coupling between classes as they provide a level of abstraction. 4. Final variables: All variables declared in an interface are final, static, and public by default.
Relationship Between Class and Interface
In Java, a class can implement one or more interfaces, but an interface can only be implemented by a class, not the other way around. Classes must implement all the methods defined by the interfaces they implement. Additionally, interfaces can extend other interfaces, allowing for a flexible hierarchy of behavior definitions.
Example of a Simple Java Interface
interface Player {
int id = 10; // Constant by default
void move(); // Abstract method
}
class TestClass implements Player {
public void move() {
System.out.println("Player moves");
}
public static void main(String[] args) {
TestClass player = new TestClass();
player.move();
System.out.println("Player ID: " + Player.id);
}
}
Output:
Player moves
Player ID: 10
Example: Vehicle Interface
Consider the example of vehicles like bicycles and cars that have common functionalities. We can create a Vehicle interface to define the general behaviors that every vehicle must implement, and let each vehicle type provide its specific implementation.
interface Vehicle {
void changeGear(int gear);
void speedUp(int increment);
void applyBrakes(int decrement);
}
class Bicycle implements Vehicle {
int speed;
int gear;
public void changeGear(int newGear) {
gear = newGear;
}
public void speedUp(int increment) {
speed += increment;
}
public void applyBrakes(int decrement) {
speed -= decrement;
}
public void display() {
System.out.println("Speed: " + speed + " Gear: " + gear);
}
}
class Car implements Vehicle {
int speed;
int gear;
public void changeGear(int newGear) {
gear = newGear;
}
public void speedUp(int increment) {
speed += increment;
}
public void applyBrakes(int decrement) {
speed -= decrement;
}
public void display() {
System.out.println("Speed: " + speed + " Gear: " + gear);
}
}
public class Main {
public static void main(String[] args) {
Bicycle bike = new Bicycle();
bike.changeGear(2);
bike.speedUp(10);
bike.applyBrakes(3);
System.out.println("Bicycle state:");
bike.display();
Car car = new Car();
car.changeGear(3);
car.speedUp(20);
car.applyBrakes(5);
System.out.println("Car state:");
car.display();
}
}
Java doesn’t allow multiple inheritance with classes, but interfaces provide a way to achieve it. A class can implement multiple interfaces, inheriting behaviors from each one.
interface Machine {
void start();
}
interface Sound {
void playSound();
}
class Robot implements Machine, Sound {
public void start() {
System.out.println("Robot started.");
}
public void playSound() {
System.out.println("Robot playing sound.");
}
}
public class Main {
public static void main(String[] args) {
Robot r = new Robot();
r.start();
r.playSound();
}
}
Output:
Robot started.
Robot playing sound.
‘this’ reference in Java
In Java, this is a reference variable that refers to the current object instance. It helps in distinguishing between instance variables and parameters with the same name and can be used for various purposes such as invoking class methods, passing the current object as a parameter, and improving code readability.
Java this Reference Example:
In Java, this refers to the object on which a method or constructor is being invoked. It allows access to the instance variables and methods of the current object.
Here’s an example demonstrating the usage of this reference:
// Java program demonstrating the use of 'this' reference
class Employee {
String name;
int id;
// Constructor
Employee(String name, int id) {
this.name = name; // 'this' refers to the instance variable 'name'
this.id = id; // 'this' refers to the instance variable 'id'
}
// Method to display employee details
public void displayDetails() {
System.out.println("Employee Name: " + this.name);
System.out.println("Employee ID: " + this.id);
}
// Main method
public static void main(String[] args) {
Employee emp1 = new Employee("John", 101);
Employee emp2 = new Employee("Alice", 102);
emp1.displayDetails();
emp2.displayDetails();
}
}
Output:
Employee Name: John
Employee ID: 101
Employee Name: Alice
Employee ID: 102
Explanation:
In the above example, this is used in the constructor to refer to the instance variables name and id, differentiating them from the constructor’s parameters. The displayDetails method prints out the details of each employee using the this reference.
Different Uses of this in Java:
1. Referring to the Current Class Instance Variables :You can use this to differentiate between instance variables and parameters with the same name.
class Test {
int x;
Test(int x) {
this.x = x; // 'this' is used to differentiate the instance variable from the constructor parameter
}
}
2. Invoking the Current Class Constructor :this() can be used to call another constructor in the same class.
class Test {
int x;
Test() {
this(10); // Calls the parameterized constructor
}
Test(int x) {
this.x = x;
}
}
3. Returning the Current Class Instance: this can be used to return the current instance of the class.
class Test {
int x;
Test(int x) {
this.x = x;
}
Test getObject() {
return this; // Returns the current object
}
}
4. Passing the Current Class Instance as a Method Parameter: this can be passed as an argument to methods or constructors.
class Test {
void display(Test obj) {
System.out.println("Object received: " + obj);
}
void sendThis() {
display(this); // Passes the current object as a parameter
}
}
5. Invoking the Current Class Method:this can be used to call a method from within another method.
class Test {
void display() {
this.show(); // Calls the show method
}
void show() {
System.out.println("Inside show method");
}
}
Operators are the foundation of any programming language, enabling us to perform various computations, logic evaluations, and functions. Java offers several types of operators that are categorized based on the functions they perform.
Array Initialization in C
Initialization in C involves assigning initial values to variables. When an array is declared or memory is allocated for it, its elements initially contain garbage values. To give these elements meaningful values, we initialize the array. There are several methods for initializing an array in C.
1. Array Initialization During Declaration :This method involves initializing the array at the time of its declaration. We use an initializer list, which is a set of values enclosed in curly braces { } and separated by commas, to assign values to the array.
Examples:
int[] numbers; // Declaring an integer array
This declares an integer array numbers. However, it does not allocate memory yet.
2. Creating an Array :Allocate memory using the new keyword:
numbers = new int[5]; // Creates an array of 5 integers
This initializes the array to hold 5 integers, with default values of 0.
3. Accessing Array Elements :You can access and modify array elements using their index (starting from 0):
numbers[0] = 10; // Setting the first element to 10
int firstElement = numbers[0]; // Accessing the first element
4. Changing Array Elements :To update an element, assign a new value:
numbers[0] = 20; // Changes the first element to 20
5. Array Length :You can get the length of an array using its length property:
int length = numbers.length; // Returns 5 for this array
6. Array Initialization :You can declare and initialize arrays in different ways:
7. Iterating Through Arrays :You can loop through arrays using a for loop or for-each loop:
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]);
}
for (int number : numbers) {
System.out.println(number); // For-each loop
}
9. Array of Objects ;Arrays can also hold objects of a class:
Student[] students = new Student[3]; // Array of Student objects
students[0] = new Student("John", 1);
Output:
num1 = 20
num2 = 10
The product = 200
10. Passing Arrays to Methods :You can pass arrays to methods:
public class MultiplicationExample {
public static void main(String[] args) {
int num1 = 20, num2 = 10;
int product = num1 * num2;
System.out.println("The product = " + product);
}
}
Calling the method:
printArray(numbers);
11. Returning Arrays from Methods :A method can return an array:
public static int[] getArray() {
return new int[] {1, 2, 3};
}
12. Cloning Arrays :To clone an array (create a shallow copy):
int[] cloneArray = numbers.clone();
For multidimensional arrays, the cloning creates a shallow copy, meaning only the references are copied, not the actual elements.
13. Handling Array Exceptions :If you try to access an element outside the array size, Java throws an ArrayIndexOutOfBoundsException:
System.out.println(numbers[5]); // Throws an exception if the array has fewer elements
Arrays class in Java
The Arrays class in Java, part of the java.util package, provides several static methods to help developers perform operations on arrays in a more optimized and efficient manner. Here are some of the most common methods:
Key Methods in Arrays Class:
1. asList(): Converts an array to a fixed-size list.
Integer[] arr = {1, 2, 3};
List<Integer> list = Arrays.asList(arr);
2. binarySearch(): Searches for a specific element using the binary search algorithm.
int[] arr = {1, 2, 3, 4, 5};
int index = Arrays.binarySearch(arr, 3); // Returns index of 3
3. compare(): Compares two arrays lexicographically.
int result = Arrays.compare(arr1, arr2);
4. copyOf(): Copies the array to a new array of a specified length.
int[] newArr = Arrays.copyOf(arr, 10); // Extends or truncates
5. copyOfRange(): Copies a range of an array.
int[] newArr = Arrays.copyOfRange(arr, 1, 3);
6. deepEquals():Compares two arrays deeply for nested arrays.
boolean isEqual = Arrays.deepEquals(arr1, arr2);
7. deepHashCode(): Returns the hash code based on the “deep contents” of an array.
int hashCode = Arrays.deepHashCode(arr);
8. deepToString(): Converts an array to a string representation, handling nested arrays.
String arrStr = Arrays.deepToString(arr);
9. equals(): Compares two arrays for equality.
boolean isEqual = Arrays.equals(arr1, arr2);
10. fill(): Fills an array with a specified value.
Arrays.fill(arr, 100); // Fills all elements with 100
11. hashCode(): Returns the hash code for an array.
int hash = Arrays.hashCode(arr);
12. mismatch(): Finds the first index where two arrays differ.
int mismatchIndex = Arrays.mismatch(arr1, arr2);
13. parallelSort(): Sorts the array in parallel for faster sorting.
Arrays.parallelSort(arr);
14. sort(): Sorts an array in ascending order.
Arrays.sort(arr);
15. spliterator(): Returns a Spliterator covering the array.
16. stream(): Converts an array into a sequential stream.
Stream<int[]> stream = Arrays.stream(arr);
17. toString(): Converts an array to a string representation.
String arrStr = Arrays.toString(arr);
Multidimensional Arrays
Multidimensional arrays, simply put, are arrays made up of arrays. Data within these arrays are stored in a tabular structure, typically in row-major order.
Syntax:
data_type[1st_dimension][2nd_dimension][]...[Nth_dimension] array_name = new data_type[size1][size2]...[sizeN];
Where:
data_type: The type of data stored in the array (e.g., int, char, etc.).
dimension: Specifies the dimension of the array (e.g., 2D, 3D).
array_name: The name assigned to the array.
size1, size2,…, sizeN: Dimensions defining the size of the array.
Examples:
Two-dimensional array:
int[][] twoD_arr = new int[10][20];
The total number of elements a multidimensional array can store is the product of its dimensions. For example, an array int[][] x = new int[10][20] can hold a total of 10×20=20010 \times 20 = 20010×20=200 elements. Similarly, int[][][] x = new int[5][10][20] can store 5×10×20=10005 \times 10 \times 20 = 10005×10×20=1000 elements.
Applications of Multidimensional Arrays
Tabular Data: These arrays are often used to store tabular data, such as a student’s roll number and marks. A more complex use is to store images in 3D arrays.
Dynamic Programming: Many dynamic programming problems leverage multidimensional arrays to store intermediate states.
Algorithms: Common applications include matrix multiplication, adjacency matrices in graphs, and grid-based search problems.
Two-Dimensional Array (2D Array)
A 2D array is the most basic form of a multidimensional array and can be viewed as an array of one-dimensional arrays.
Declaration (Indirect Method):
Example:
int[][] arr = new int[10][20];
Initialization:
arr[0][0] = 1;
Examples:
public class TwoDArrayExample {
public static void main(String[] args) {
int[][] arr = new int[10][20];
arr[0][0] = 1;
System.out.println("arr[0][0] = " + arr[0][0]);
}
}
Output:
arr[0][0] = 1
Example: 2D array with default values in a 4×4 matrix:
public class TwoDArray {
public static void main(String[] args) {
int rows = 4;
int columns = 4;
int[][] array = new int[rows][columns];
int value = 1;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
array[i][j] = value++;
}
}
System.out.println("The 2D array is:");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
System.out.print(array[i][j] + " ");
}
System.out.println();
}
}
}
Output:
The 2D array is:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
Declaration (Direct Method):
int[][] arr = {{1, 2}, {3, 4}};
Output:
After adding: 8
Example:
public class DirectTwoDArray {
public static void main(String[] args) {
int[][] arr = { { 1, 2 }, { 3, 4 } };
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
System.out.println("arr[" + i + "][" + j + "] = " + arr[i][j]);
}
}
}
}
Different Ways To Declare And Initialize 2-D Array
A multi-dimensional array is an array with more than one dimension, commonly used as 2D or 3D arrays. Essentially, a multi-dimensional array is an array of arrays. A typical example of a 2D array is a chessboard, which consists of a grid of 64 square boxes arranged in 8 rows and 8 columns. Similarly, a 2D array can be visualized as a grid, where each element is identified by a row and column number. Accessing elements in a 2D array is similar to working with an Excel sheet, using row and column indices.
Two-dimensional arrays (2D arrays) are useful for implementing structures like games (Tic-Tac-Toe, Chess) or storing image pixels.
Declaring a 2D Array in Java
A 2D array can be declared in two common ways:
data_type array_name[][]; // OR
data_type[][] array_name;
data_type: This defines the type of elements the array will store, such as int, String, boolean, etc. Java, being statically typed, requires the data type to be declared.
array_name: This is the name you assign to the array.
Example: Declaring Different Types of 2D Arrays
data_type[][] array_Name = new data_type[no_of_rows][no_of_columns];
Here, no_of_rows and no_of_columns define the number of rows and columns in the array. The total number of elements in a 2D array is calculated by multiplying the number of rows by the number of columns. For instance:
int[][] myArray = new int[4][5]; // A 2D array with 4 rows and 5 columns
By default, Java assigns default values to the array elements depending on the data type. Below are various approaches to initialize a 2D array.
Different Approaches for Initializing 2D Arrays
Approach 1: Declaration and Initialization
In this approach, we declare the array and immediately initialize it.
public class Example {
public static void main(String[] args) {
int[][] integer2DArray = new int[3][4];
System.out.println("Default value of int array element: " + integer2DArray[0][0]); // Outputs 0
String[][] string2DArray = new String[2][3];
System.out.println("Default value of String array element: " + string2DArray[0][0]); // Outputs null
boolean[][] boolean2DArray = new boolean[2][2];
System.out.println("Default value of boolean array element: " + boolean2DArray[0][0]); // Outputs false
}
}
Approach 2: Initialization with Values
Here, we initialize the array elements directly without specifying the number of rows and columns. Java automatically deduces the size.
Example:
public class Example {
public static void main(String[] args) {
String[][] courses = {
{ "Math", "Science", "History" }, // Row 1
{ "Biology", "Physics", "Chemistry" }, // Row 2
{ "English", "Art" } // Row 3
};
System.out.println("Course in first row: " + courses[0][0]); // Math
System.out.println("Course in second row: " + courses[1][2]); // Chemistry
System.out.println("Course in third row: " + courses[2][1]); // Art
}
}
Approach 3: Initializing Each Element Separately
This method allows you to initialize elements individually.
Example:
public class Example {
public static void main(String[] args) {
int[][] marks = new int[2][2];
marks[0][0] = 85;
marks[0][1] = 90;
marks[1][0] = 78;
marks[1][1] = 88;
System.out.println("marks[0][0] = " + marks[0][0]);
System.out.println("marks[1][1] = " + marks[1][1]);
}
}
Approach 4: Using Loops for Initialization
When dealing with large arrays, initializing elements manually can be cumbersome. A more efficient approach is using loops.
Example:
public class Example {
public static void main(String[] args) {
int rows = 4, columns = 3;
int[][] array = new int[rows][columns];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
array[i][j] = i + j;
}
}
// Printing array elements
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
System.out.print(array[i][j] + " ");
}
System.out.println();
}
}
}
Approach 5: Jagged Arrays (Different Number of Columns per Row)
A jagged array allows rows to have different numbers of columns.
Example:
public class Example {
public static void main(String[] args) {
int[][] jaggedArray = new int[2][];
jaggedArray[0] = new int[3]; // First row has 3 columns
jaggedArray[1] = new int[2]; // Second row has 2 columns
jaggedArray[0][0] = 1;
jaggedArray[0][1] = 2;
jaggedArray[0][2] = 3;
jaggedArray[1][0] = 4;
jaggedArray[1][1] = 5;
// Printing the jagged array
for (int i = 0; i < jaggedArray.length; i++) {
for (int j = 0; j < jaggedArray[i].length; j++) {
System.out.print(jaggedArray[i][j] + " ");
}
System.out.println();
}
}
}
Jagged Arrays in Java
A jagged array is a type of multidimensional array where the number of columns varies from row to row. This makes the jagged array more flexible compared to a rectangular 2D array. Here’s a simplified representation of a jagged array in memory:
Row 1: [ ]
Row 2: [ ]
Row 3: [ ]
...
Declaring and Initializing a Jagged Array
Syntax:
data_type array_name[][] = new data_type[n][];
array_name[0] = new data_type[n1]; // n1 = no. of columns in row-1
array_name[1] = new data_type[n2]; // n2 = no. of columns in row-2
...
Alternative Ways to Initialize a Jagged Array:
int[][] arr = new int[][] {
new int[] {10, 20, 30, 40},
new int[] {50, 60, 70, 80, 90, 100},
new int[] {110, 120}
};
public class Main {
public static void main(String[] args) {
// Declare a 2D array with 2 rows
int[][] jaggedArray = new int[2][];
// First row has 3 columns
jaggedArray[0] = new int[3];
// Second row has 2 columns
jaggedArray[1] = new int[2];
// Initializing the array
int value = 0;
for (int i = 0; i < jaggedArray.length; i++) {
for (int j = 0; j < jaggedArray[i].length; j++) {
jaggedArray[i][j] = value++;
}
}
// Displaying the values of the jagged array
System.out.println("Contents of the Jagged Array:");
for (int i = 0; i < jaggedArray.length; i++) {
for (int j = 0; j < jaggedArray[i].length; j++) {
System.out.print(jaggedArray[i][j] + " ");
}
System.out.println();
}
}
}
Output:
Contents of the Jagged Array:
0 1 2
3 4
Example 2: Creating a Jagged Array with Incremental Row Sizes
In this example, the first row contains one element, the second row contains two elements, and so on.
public class Main {
public static void main(String[] args) {
int rows = 5;
// Declaring a jagged array with 5 rows
int[][] jaggedArray = new int[rows][];
// Creating the jagged array with varying columns per row
for (int i = 0; i < jaggedArray.length; i++) {
jaggedArray[i] = new int[i + 1]; // ith row has i+1 columns
}
// Initializing the array
int value = 0;
for (int i = 0; i < jaggedArray.length; i++) {
for (int j = 0; j < jaggedArray[i].length; j++) {
jaggedArray[i][j] = value++;
}
}
// Displaying the values of the jagged array
System.out.println("Contents of the Jagged Array:");
for (int i = 0; i < jaggedArray.length; i++) {
for (int j = 0; j < jaggedArray[i].length; j++) {
System.out.print(jaggedArray[i][j] + " ");
}
System.out.println();
}
}
}
Output:
Contents of the Jagged Array:
0
1 2
3 4 5
6 7 8 9
10 11 12 13 14
Final Arrays
In Java, the final keyword prevents reassigning a reference to another object, but it doesn’t make the referenced object itself immutable. This is applicable to arrays as well. Once an array is declared as final, the reference to that array cannot be changed, but the elements within the array can be modified. This means we can change the state of the object (the contents of the array), but not the reference to the object itself.
Illustration:
// Java Program Demonstrating Final Arrays
public class FinalArrayDemo {
public static void main(String[] args) {
final int[] arr = { 1, 2, 3, 4, 5 };
// Modifying an element in the final array
arr[3] = 10;
// Displaying the updated array
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
Output:
1
2
3
10
5
Implementation: Here’s another example showing how you can manipulate the contents of a final array:
// Java Program Demonstrating Final Arrays
public class FinalArrayExample {
public static void main(String[] args) {
final int[] arr = { 2, 4, 6, 8, 10 };
// Modifying array elements
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * 2;
System.out.println(arr[i]);
}
}
}
Output:
4
8
12
16
20
Explanation:
Even though the array arr is declared as final, you can still change its individual elements. The final keyword simply means that the reference to the array cannot be reassigned to another array. This behavior is consistent with how object references work in Java: when you declare an object as final, the reference can’t be changed, but the object’s internal state can still be modified.
Example 1:
// Program demonstrating modification of object fields
class Test {
int value = 100;
public static void main(String[] args) {
final Test obj = new Test();
obj.value = 200; // Modifying the internal state
System.out.println(obj.value);
}
}
Output:
200
util.Arrays vs reflect.Array in Java with Examples
The Array class in the java.lang.reflect package is part of Java Reflection and provides static methods to dynamically create and access Java arrays. This class is final, meaning it cannot be instantiated or extended. All its methods are static and accessed via the class name itself. On the other hand, the Arrays class in the java.util package is part of the Java Collection Framework and contains various utility methods for manipulating arrays, such as sorting and searching.
While both classes deal with arrays, the key difference is their usage. The Array class ensures type safety and is mainly used for reflective access to arrays. The Arrays class, on the other hand, offers a variety of methods for array operations.
Here is a breakdown of the differences between Array and Arrays:
Factor
Array
Arrays
Package
Belongs to java.lang.reflect
Belongs to java.util
Class Hierarchy
java.lang.Object → java.lang.reflect.Array
java.lang.Object → java.util.Arrays
Immutability
Immutable and final
Not immutable
Declaration
public final class Array extends Object
public class Arrays extends Object
Usage
Provides methods to create and access arrays reflectively, ensuring type safety
Contains utility methods for array manipulation (sorting, searching, etc.)
Example: Illustrating the Usage of Array vs Arrays
import java.lang.reflect.Array;
import java.util.Arrays;
public class ArrayVsArraysExample {
public static void main(String[] args) {
// Creating an integer array of size 5
int[] intArray = new int[5];
// Adding an element to the array using Array class
Array.setInt(intArray, 0, 42);
// Printing the array using Arrays class
System.out.println(Arrays.toString(intArray));
}
}
Operators are the foundation of any programming language, enabling us to perform various computations, logic evaluations, and functions. Java offers several types of operators that are categorized based on the functions they perform. These include:
Arithmetic Operators
Unary Operators
Assignment Operator
Relational Operators
Logical Operators
Ternary Operator
Bitwise Operators
Shift Operators
This section focuses on Arithmetic Operators, which allow us to carry out mathematical operations on basic data types. These operators can either be unary (applied to one operand) or binary (applied to two operands). Let’s dive into each arithmetic operator in Java.
Arithmetic Operators in Java
1. Addition (+)
This binary operator is used to add two operands.
Syntax:
num1 + num2
Example:
int num1 = 10, num2 = 20;
int sum = num1 + num2;
Output:
num1 = 10
num2 = 20
The sum = 30
Java Code Example:
public class AdditionExample {
public static void main(String[] args) {
int num1 = 10, num2 = 20;
int sum = num1 + num2;
System.out.println("The sum = " + sum);
}
}
2. Subtraction (-)
This binary operator is used to subtract the second operand from the first operand.
Syntax:
num1 - num2
Example:
int num1 = 20, num2 = 10;
int difference = num1 - num2;
Output:
num1 = 20
num2 = 10
The difference = 10
Java Code Example:
public class SubtractionExample {
public static void main(String[] args) {
int num1 = 20, num2 = 10;
int difference = num1 - num2;
System.out.println("The difference = " + difference);
}
}
3. Multiplication (*)
This binary operator multiplies two operands.
Syntax:
num1 * num2
Example:
int num1 = 20, num2 = 10;
int product = num1 * num2;
Output:
num1 = 20
num2 = 10
The product = 200
Java Code Example:
public class MultiplicationExample {
public static void main(String[] args) {
int num1 = 20, num2 = 10;
int product = num1 * num2;
System.out.println("The product = " + product);
}
}
4. Division (/)
This binary operator divides the first operand (dividend) by the second operand (divisor) and returns the quotient.
Syntax:
num1 / num2
Example:
int num1 = 20, num2 = 10;
int quotient = num1 / num2;
Output:
num1 = 20
num2 = 10
The quotient = 2
Java Code Example:
public class DivisionExample {
public static void main(String[] args) {
int num1 = 20, num2 = 10;
int quotient = num1 / num2;
System.out.println("The quotient = " + quotient);
}
}
5. Modulus (%)
This binary operator returns the remainder when the first operand (dividend) is divided by the second operand (divisor).
Syntax:
num1 % num2
Example:
int num1 = 5, num2 = 2;
int remainder = num1 % num2;
Output:
num1 = 5
num2 = 2
The remainder = 1
Java Code Example:
public class ModulusExample {
public static void main(String[] args) {
int num1 = 5, num2 = 2;
int remainder = num1 % num2;
System.out.println("The remainder = " + remainder);
}
}
Example Program Implementing All Arithmetic Operators
The following Java program takes user input and performs addition, subtraction, multiplication, and division:
import java.util.Scanner;
public class ArithmeticOperators {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("Enter the first number: ");
double num1 = sc.nextDouble();
System.out.print("Enter the second number: ");
double num2 = sc.nextDouble();
double sum = num1 + num2;
double difference = num1 - num2;
double product = num1 * num2;
double quotient = num1 / num2;
System.out.println("The sum of the two numbers is: " + sum);
System.out.println("The difference of the two numbers is: " + difference);
System.out.println("The product of the two numbers is: " + product);
System.out.println("The quotient of the two numbers is: " + quotient);
}
}
Input:
Enter the first number: 20
Enter the second number: 10
Output:
The sum of the two numbers is: 30.0
The difference of the two numbers is: 10.0
The product of the two numbers is: 200.0
The quotient of the two numbers is: 2.0
Java Unary Operator
Unary operators in Java operate on a single operand. These operators are used for tasks such as incrementing, decrementing, and negating values. Let’s explore the various unary operators and how they function.
Operator 1: Unary Minus (-)
This operator is used to reverse the sign of a value, converting a positive value into a negative one, or vice versa.
Syntax:
-(operand)
Example:
// Java Program demonstrating Unary Minus
class UnaryMinus {
public static void main(String[] args) {
int num = 20;
System.out.println("Number = " + num);
// Applying unary minus
num = -num;
System.out.println("After applying unary minus = " + num);
}
}
Output:
Number = 20
After applying unary minus = -20
Operator 2: NOT Operator (!)
This operator negates a boolean expression, flipping true to false and vice versa.
Syntax:
!(operand)
Example:
// Java Program demonstrating NOT Operator
class NotOperator {
public static void main(String[] args) {
boolean flag = true;
int a = 10, b = 1;
System.out.println("Initial flag: " + flag);
System.out.println("a = " + a + ", b = " + b);
// Applying NOT operator
System.out.println("Negated flag: " + !flag);
System.out.println("!(a < b) = " + !(a < b));
System.out.println("!(a > b) = " + !(a > b));
}
}
Output:
Initial flag: true
a = 10, b = 1
Negated flag: false
!(a < b) = true
!(a > b) = false
Operator 3: Increment (++)
The increment operator increases the value of an operand by one. It can be used in two forms:
Post-Increment: The operand is incremented after its current value is used.
Pre-Increment: The operand is incremented before its current value is used.
Example:
int num = 5;
System.out.println("Post-increment: " + (num++)); // Uses current value, then increments
System.out.println("Pre-increment: " + (++num)); // Increments, then uses new value
Operator 4: Decrement (--)
Similar to the increment operator, the decrement operator decreases the operand’s value by one and can also be used in two forms:
Post-Decrement: Decrements the operand after its current value is used.
Pre-Decrement: Decrements the operand before its current value is used.
Example:
int num = 5;
System.out.println("Post-decrement: " + (num--)); // Uses current value, then decrements
System.out.println("Pre-decrement: " + (--num)); // Decrements, then uses new value
Operator 5: Bitwise Complement (~)
This operator inverts all the bits of the operand. It converts 0s to 1s and 1s to 0s.
Syntax:
~(operand)
Example:
// Java Program demonstrating Bitwise Complement Operator
class BitwiseComplement {
public static void main(String[] args) {
int num = 5;
System.out.println("Number: " + num);
// Applying bitwise complement
System.out.println("Bitwise complement: " + ~num);
}
}
Output:
Number: 5
Bitwise complement: -6
Example Program Demonstrating All Unary Operators
Here’s a comprehensive program that demonstrates the use of all basic unary operators with user input:
import java.util.Scanner;
public class UnaryOperatorsExample {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("Enter a number: ");
int num = sc.nextInt();
// Unary plus
int result = +num;
System.out.println("Unary plus result: " + result);
// Unary minus
result = -num;
System.out.println("Unary minus result: " + result);
// Pre-increment
result = ++num;
System.out.println("Pre-increment result: " + result);
// Post-increment
result = num++;
System.out.println("Post-increment result: " + result);
// Pre-decrement
result = --num;
System.out.println("Pre-decrement result: " + result);
// Post-decrement
result = num--;
System.out.println("Post-decrement result: " + result);
sc.close();
}
}
Input:
Enter a number: 10
Output:
Unary plus result: 10
Unary minus result: -10
Pre-increment result: 11
Post-increment result: 11
Pre-decrement result: 11
Post-decrement result: 10
Java Assignment Operators
In Java, operators form the basic building blocks of programming, allowing developers to perform various types of calculations, comparisons, and logical operations. Assignment operators specifically help assign values to variables. They have right-to-left associativity, meaning the value on the right side of the operator is assigned to the variable on the left. Let’s dive into the different types of assignment operators used in Java.
Types of Assignment Operators:
1. Simple Assignment Operator (=): The simple assignment operator is used to assign a value to a variable. The operand on the right-hand side must match the data type of the variable on the left-hand side.
Syntax:
variable = value;
Example:
public class AssignmentOperatorExample {
public static void main(String[] args) {
int num = 15;
String name = "Java";
System.out.println("num is assigned: " + num);
System.out.println("name is assigned: " + name);
}
}
Output:
num is assigned: 15
name is assigned: Java
2. Add and Assign (+=): The compound += operator adds the value on the right to the value of the variable on the left and then assigns the result back to the variable on the left.
Syntax:
variable += value;
Example:
public class AddAssignExample {
public static void main(String[] args) {
int num1 = 5, num2 = 3;
num1 += num2;
System.out.println("After adding: " + num1);
}
}
Output:
After adding: 8
Note: This operator performs implicit type conversion if necessary. For instance, adding a double to an int using += will result in automatic narrowing conversion.
3. Subtract and Assign (-=): The compound -= operator subtracts the value on the right from the value on the left and then assigns the result back to the variable on the left.
Syntax:
variable -= value;
Example:
public class SubtractAssignExample {
public static void main(String[] args) {
int num1 = 10, num2 = 4;
num1 -= num2;
System.out.println("After subtracting: " + num1);
}
}
Output:
After subtracting: 6
4. Multiply and Assign (*=): This operator multiplies the current value of the variable by the value on the right and assigns the result back to the variable on the left.
Syntax:
variable *= value;
a *= 2; // Equivalent to a = a * 2
Example:
public class MultiplyAssignExample {
public static void main(String[] args) {
int num1 = 4, num2 = 3;
num1 *= num2;
System.out.println("After multiplying: " + num1);
}
}
Output:
After multiplying: 12
5. Divide and Assign (/=): This operator divides the current value of the variable by the value on the right and assigns the result back to the variable on the left.
Syntax:
variable /= value;
Example:
public class DivideAssignExample {
public static void main(String[] args) {
int num1 = 20, num2 = 4;
num1 /= num2;
System.out.println("After dividing: " + num1);
}
}
Output:
After dividing: 5
6. Modulus and Assign (%=): This operator performs division and assigns the remainder back to the variable on the left.
Syntax:
variable %= value;
Example:
public class ModulusAssignExample {
public static void main(String[] args) {
int num1 = 17, num2 = 5;
num1 %= num2;
System.out.println("After modulus: " + num1);
}
}
Output:
After modulus: 2
Java Relational Operators
Relational operators in Java are binary operators used to compare two operands and return a boolean value indicating the result of the comparison. These operators are often used in control flow statements such as loops and conditionals.
General Syntax:
variable1 relation_operator variable2
1. Equal to (==)
The == operator checks if two operands are equal. If they are, it returns true; otherwise, it returns false.
Logical operators are used to perform logical operations such as “AND,” “OR,” and “NOT,” similar to the functions of AND and OR gates in digital electronics. They combine two or more conditions or reverse the outcome of a given condition. An important point to remember is that in the case of the AND operator, the second condition is not evaluated if the first is false, while in the case of the OR operator, the second condition is not evaluated if the first is true. This feature is known as short-circuiting. Logical operators are commonly used in decision-making when multiple conditions need to be checked.
Logical Operators in Java
Let’s go through an example with some common logical operators in Java. Assume we have the following variables:
Syntax:
int a = 10;
int b = 20;
int c = 30;
AND Operator (&&):
This operator returns true if both conditions are true. Otherwise, it returns false.
Example:
Condition 1: c > a (true)
Condition 2: c > b (true)
Since both conditions are true, the result will be true.
Example:
if (c > a && c > b) {
System.out.println("Both conditions are true");
} else {
System.out.println("At least one condition is false");
}
Output:
Both conditions are true
OR Operator (||):
This operator returns true if at least one of the conditions is true. If both conditions are false, the result is false.
Example:
Condition 1: c > a (true)
Condition 2: c < b (false)
Since one of the conditions is true, the result will be true.
if (c > a || c < b) {
System.out.println("At least one condition is true");
} else {
System.out.println("Both conditions are false");
}
Output:
At least one condition is true
NOT Operator (!):
This unary operator returns the opposite of the condition. If the condition is true, it returns false, and vice versa.
In cases where the first condition of an AND operation is false, the second condition will not be evaluated.
Example:
int a = 10, b = 20, c = 15;
if ((a > c) && (++b > c)) {
System.out.println("Condition met");
}
System.out.println("Value of b: " + b);
Output:
Value of b: 20
Short-Circuiting in AND Operator:
In cases where the first condition of an AND operation is false, the second condition will not be evaluated.
Example:
int a = 10, b = 20, c = 15;
if ((a > c) && (++b > c)) {
System.out.println("Condition met");
}
System.out.println("Value of b: " + b);
Output:
Value of b: 20
Short-Circuiting in OR Operator:
In cases where the first condition of an OR operation is true, the second condition will not be evaluated.
Example:
int a = 10, b = 20, c = 15;
if ((a < c) || (++b < c)) {
System.out.println("Condition met");
}
System.out.println("Value of b: " + b);
Output:
Condition met
Value of b: 20
Boolean Values and Logical Operators:
Logical operators can also be applied directly to boolean values. For example:
Example:
boolean a = true;
boolean b = false;
System.out.println("a && b: " + (a && b)); // false
System.out.println("a || b: " + (a || b)); // true
System.out.println("!a: " + !a); // false
System.out.println("!b: " + !b); // true
Output:
a && b: false
a || b: true
!a: false
!b: true
Java Ternary Operator
The ternary operator is the only operator in Java that takes three operands. It serves as a concise alternative to the traditional if-else statement, allowing conditional logic in a single line. Although it operates similarly to if-else, it helps to reduce code size and improve readability.
int num1 = 10;
int num2 = 20;
int result = (num1 > num2) ? (num1 + num2) : (num1 - num2);
Since num1 < num2, the second expression is executed, so:
Syntax:
result = num1 - num2 = -10;
Example:
class TernaryExample {
public static void main(String[] args) {
int n1 = 5, n2 = 10, max;
System.out.println("First number: " + n1);
System.out.println("Second number: " + n2);
// Using ternary operator to find the maximum
max = (n1 > n2) ? n1 : n2;
System.out.println("The maximum number is: " + max);
}
}
Output:
First number: 5
Second number: 10
The maximum number is: 10
Bitwise operators
Bitwise operators are used to manipulate individual bits of a number. They are particularly useful when optimizing performance in certain cases, as they directly operate on the binary representation of numbers. Bitwise operators can be used with any integral type (e.g., char, short, int). They are often employed when performing operations on Binary Indexed Trees (BITs).
Here are the common bitwise operators in Java:
1. Bitwise OR (|)
The Bitwise OR operator is represented by the symbol |. It compares the corresponding bits of two operands, and if either of the bits is 1, the result is 1; otherwise, the result is 0. Example:
a = 6 = 0110 (In Binary)
b = 9 = 1001 (In Binary)
Bitwise OR Operation:
0110
| 1001
_________
1111 = 15 (In decimal)
2. Bitwise AND (&)
The Bitwise AND operator is represented by &. It compares the corresponding bits of two operands, and if both bits are 1, the result is 1; otherwise, the result is 0. Example:
a = 6 = 0110 (In Binary)
b = 9 = 1001 (In Binary)
Bitwise AND Operation:
0110
& 1001
_________
0000 = 0 (In decimal)
3. Bitwise XOR (^)
The Bitwise XOR operator is represented by ^. It compares the corresponding bits of two operands, and if the bits are different, the result is 1; otherwise, the result is 0. Example:
a = 6 = 0110 (In Binary)
b = 9 = 1001 (In Binary)
Bitwise XOR Operation:
0110
^ 1001
_________
1111 = 15 (In decimal)
4. Bitwise Complement (~)
The Bitwise NOT (complement) operator is represented by ~. It inverts all the bits of the operand, changing every 1 to 0 and every 0 to 1. Example:
a = 6 = 0110 (In Binary)
Bitwise NOT Operation:
~ 0110
_________
1001 = -7 (In decimal, as it returns the two's complement)
Code Example:
import java.util.Scanner;
public class BitwiseOperators {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("Enter first number: ");
int num1 = input.nextInt();
System.out.print("Enter second number: ");
int num2 = input.nextInt();
System.out.println("Bitwise AND: " + (num1 & num2));
System.out.println("Bitwise OR: " + (num1 | num2));
System.out.println("Bitwise XOR: " + (num1 ^ num2));
System.out.println("Bitwise NOT: " + (~num1));
System.out.println("Bitwise Left Shift: " + (num1 << 2));
System.out.println("Bitwise Right Shift: " + (num1 >> 2));
System.out.println("Bitwise Unsigned Right Shift: " + (num1 >>> 2));
input.close();
}
}
Input:
Enter first number: 4
Enter second number: 10
Output:
Bitwise AND: 0
Bitwise OR: 14
Bitwise XOR: 14
Bitwise NOT: -5
Bitwise Left Shift: 16
Bitwise Right Shift: 1
Bitwise Unsigned Right Shift: 1
Decision Making in Java (if, if-else, switch, break, continue, jump).
Decision-making in programming is like making decisions in real life. When coding, we often need to execute certain blocks of code only if specific conditions are met.
Java provides several control statements to control the flow of a program based on conditions. These statements direct the flow of execution, allowing it to branch and continue based on the program’s state.
Java’s Selection Statements:
if
if-else
nested-if
if-else-if
switch-case
jump (break, continue, return)
1. if Statement:
The simplest decision-making statement. It is used to decide whether a certain block of code will be executed or not, depending on whether the condition is true.
Syntax:
if(condition) {
// Statements to execute if the condition is true
}
Here, the condition evaluates to either true or false. If true, the block of statements inside the if block is executed.
If we do not use curly braces ({}), only the first statement after the if will be considered part of the if block.
Example:
class IfDemo {
public static void main(String args[]) {
int i = 10;
if (i < 15)
System.out.println("Inside If block"); // Part of if block
System.out.println("10 is less than 15"); // Outside of if block
System.out.println("I am not in if block");
}
}
Output:
Inside If block
10 is less than 15
I am not in if block
2. if-else Statement:
The if statement alone can execute code when a condition is true. But what if we want to execute something else when the condition is false? This is where the else statement comes in.
Syntax:
if (condition) {
// Executes if condition is true
} else {
// Executes if condition is false
}
Example:
class IfElseDemo {
public static void main(String args[]) {
int i = 10;
if (i < 15)
System.out.println("i is smaller than 15");
else
System.out.println("i is greater than 15");
}
}
Output:
i is smaller than 15
3. nested-if Statement:
A nested-if is an if statement inside another if. This allows you to perform more complex decision-making scenarios.
Syntax:
if (condition1) {
if (condition2) {
// Executes when both conditions are true
}
}
Example:
class NestedIfDemo {
public static void main(String args[]) {
int i = 10;
if (i == 10 || i < 15) {
if (i < 15)
System.out.println("i is smaller than 15");
if (i < 12)
System.out.println("i is smaller than 12 too");
} else {
System.out.println("i is greater than 15");
}
}
}
Output:
i is smaller than 15
i is smaller than 12 too
4. if-else-if Ladder:
The if-else-if ladder allows you to test multiple conditions. The conditions are evaluated from top to bottom, and as soon as one of them is true, its corresponding block is executed, and the rest are skipped.
Syntax:
if (condition1) {
// Executes if condition1 is true
} else if (condition2) {
// Executes if condition2 is true
} else {
// Executes if none of the above conditions are true
}
Example:
class IfElseIfDemo {
public static void main(String args[]) {
int i = 20;
if (i == 10)
System.out.println("i is 10");
else if (i == 15)
System.out.println("i is 15");
else if (i == 20)
System.out.println("i is 20");
else
System.out.println("i is not present");
}
}
Output:
i is 20
5. switch-case Statement:
The switch statement allows you to branch execution based on the value of an expression. It’s like having multiple if-else blocks for a single expression.
Syntax:
switch (expression) {
case value1:
// Code to be executed if expression equals value1
break;
case value2:
// Code to be executed if expression equals value2
break;
// Add more cases as needed
default:
// Code to be executed if none of the cases match
}
Example:
class SwitchDemo {
public static void main(String[] args) {
int num = 20;
switch (num) {
case 5:
System.out.println("It is 5");
break;
case 10:
System.out.println("It is 10");
break;
case 15:
System.out.println("It is 15");
break;
case 20:
System.out.println("It is 20");
break;
default:
System.out.println("Not present");
}
}
}
Output:
It is 20
6. Jump Statements:
Java has three main jump statements:
break: Used to exit a loop or a switch statement.
continue: Skips the remaining code in the current iteration of a loop and starts the next iteration.
return: Exits from the current method and passes control back to the caller.
Example of continue:
class ContinueDemo {
public static void main(String args[]) {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0)
continue;
System.out.print(i + " ");
}
}
}
Output:
1 3 5 7 9
If Statements with Examples
Decision Making in Java allows you to execute a specific block of code based on certain conditions. It’s a key part of controlling the flow of a program, similar to how we make decisions in real life.
Java’s if Statement:
The if statement is the simplest form of decision-making. It allows you to execute a block of code if a specific condition evaluates to true.
Syntax:
if (condition) {
// Statements to execute if the condition is true
}
How It Works:
1. Control enters the if block. 2. The condition is evaluated.
If the condition is true, the block of code within the if statement is executed.
If the condition is false, the control moves to the next part of the program.
Example:
class IfDemo {
public static void main(String args[]) {
int i = 10;
if (i < 15)
System.out.println("10 is less than 15");
System.out.println("Outside if-block");
}
}
Output:
10 is less than 15
Outside if-block
Java if-else Statement
Decision-making in Java allows for the execution of certain blocks of code based on specific conditions. The if statement tells us that if a condition is true, it will execute a block of statements; otherwise, it won’t. By combining if and else, you can handle both outcomes—what happens when the condition is true and when it’s false.
Understanding if-else in Java:
The if-else statement is used to control the flow of your program by executing code based on whether a condition is true or false.
Syntax of if-else Statement:
if (condition) {
// Executes this block if the condition is true
} else {
// Executes this block if the condition is false
}
Example Program:
// Java program to demonstrate if-else statement
class IfElseDemo {
public static void main(String args[]) {
int i = 20;
if (i < 15) {
System.out.println("i is smaller than 15");
} else {
System.out.println("i is greater than 15");
}
System.out.println("Outside if-else block");
}
}
Output:
i is greater than 15
Outside if-else block
Java if-else-if Ladder
Decision-making in Java allows us to write condition-based statements that execute particular blocks of code depending on specific conditions. The if-else-if ladder is used to handle multiple conditions by checking each one sequentially. As soon as a condition evaluates to true, its corresponding block is executed, and the rest of the ladder is ignored. If none of the conditions are true, the final else block is executed.
Syntax:
if (condition) {
// Executes if condition 1 is true
} else if (condition) {
// Executes if condition 2 is true
}
.
.
else {
// Executes if no conditions are true
}
Working of if-else-if Ladder:
1. Control enters the if block. 2. The first condition is evaluated:If true, the associated block is executed, and the rest of the ladder is skipped. If false, the flow moves to the next condition. 3. This process repeats for each condition in the ladder. 4. If none of the conditions are met, the else block is executed.
Example :
// Java program to demonstrate if-else-if ladder
class IfElseIfLadderDemo {
public static void main(String[] args) {
// Initializing expression
int i = 20;
// Condition 1
if (i == 10) {
System.out.println("i is 10");
}
// Condition 2
else if (i == 15) {
System.out.println("i is 15");
}
// Condition 3
else if (i == 20) {
System.out.println("i is 20");
}
// Default case
else {
System.out.println("i is not present");
}
System.out.println("Outside if-else-if ladder");
}
}
Output:
i is 20
Outside if-else-if ladder
Loops in Java
Looping is an essential feature in programming that allows a set of instructions to be executed repeatedly as long as a specified condition evaluates to true. In Java, loops are implemented in three different ways, each with a unique syntax and method of checking conditions.
while Loop
Looping is an essential feature in programming that allows a set of instructions to be executed repeatedly as long as a specified condition evaluates to true. In Java, loops are implemented in three different ways, each with a unique syntax and method of checking conditions.
while (boolean condition) {
// Loop body
}
Example:
public class WhileLoopExample {
public static void main(String[] args) {
int i = 0;
while (i <= 10) {
System.out.println(i);
i++;
}
}
}
Output:
0
1
2
3
4
5
6
7
8
9
10
Working of while loop:
The loop starts by checking the boolean condition.
If the condition is true, the loop body is executed.
After each execution, the condition is rechecked before the next iteration.
The loop terminates when the condition evaluates to false.
This is an entry control loop, meaning the condition is checked before the loop body is executed.
Java do-while loop with Examples
Loops are a fundamental concept in programming, used when we need to execute a block of code repeatedly. The do-while loop is an example of an Exit Control Loop in Java, meaning the loop body will always execute at least once, as the condition is checked after the loop body has been executed, unlike in for and while loops where the condition is checked beforehand.
Syntax of do-while Loop:
do {
// Loop body
update_expression;
} while (test_expression);
Output:
Print statement
For-each loop in Java
The for-each loop in Java, introduced in Java 5, is a simplified way to traverse arrays and collections. It provides a cleaner and more readable syntax when you need to access each element of an array or a collection like ArrayList.
Key Features of the For-Each Loop:
It begins with the keyword for, similar to a regular for-loop.
Instead of initializing and maintaining a loop counter, you declare a variable (of the same type as the array elements) that iterates through each element.
The loop body uses this variable directly, without needing to reference an array index.
The for-each loop is particularly useful for iterating through arrays or classes from the Java Collection Framework, such as ArrayList.
Syntax:
for (type var : array) {
// Statements using var
}
Example: Simple Array Traversal using For-Each Loop
public class Easy {
public static void main(String[] args) {
// Array declaration
int[] ar = {10, 50, 60, 80, 90};
// For-each loop to iterate through array
for (int element : ar) {
System.out.print(element + " ");
}
}
}
Output:
10 50 60 80 90
Equivalent Syntax with Regular For Loop:
for (int i = 0; i < ar.length; i++) {
int element = ar[i];
// Statements using element
}
Example: Finding the Highest Score using For-Each Loop
// Java program to illustrate for-each loop
public class ForEachExample {
public static void main(String[] args) {
int[] marks = {125, 132, 95, 116, 110};
int highestMarks = maximum(marks);
System.out.println("The highest score is " + highestMarks);
}
public static int maximum(int[] numbers) {
int maxSoFar = numbers[0];
// For-each loop to find the maximum value
for (int num : numbers) {
if (num > maxSoFar) {
maxSoFar = num;
}
}
return maxSoFar;
}
}
Output:
The highest score is 132
Limitations of the For-Each Loop
1. Modifying Array Elements: For-each loops don’t allow modification of array elements. Changing the loop variable does not affect the array:
for (int num : marks) {
num = num * 2; // Only changes num, not the actual array element
}
2. No Access to Index: You can’t get the index of the current element using a for-each loop:
for (int num : numbers) {
if (num == target) {
return ???; // No index available
}
}
3. Only Forward Iteration: For-each loops can only iterate forward, making it impossible to iterate in reverse order:
for (int i = numbers.length - 1; i >= 0; i--) {
System.out.println(numbers[i]); // Cannot be done using for-each
}
4. Handling Multiple Decision Statements: For-each loops struggle to process multiple decision-making conditions:
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] == arr[i]) {
// Hard to convert this to a for-each loop
}
}
Break Statement in Java
The break statement in Java is a control statement used to terminate the loop prematurely. Once the break statement is encountered, the loop terminates immediately, and the control moves to the next statement following the loop.
Syntax:
break;
When to Use the Break Statement:
1. When the exact number of iterations is not known in advance. 2. When you want to exit the loop based on some condition during iteration.
Use Cases for Break:
1. Terminating a sequence in a switch statement. 2. Exiting a loop. 3. Serving as a structured alternative to goto.
Example: Break in a Switch Statement
// Example of using break statement in switch
public class SwitchBreakExample {
public static void main(String[] args) {
// Assigning n an integer value
int n = 1;
// Passing n to switch
// The case matching n will execute and terminate due to break
switch (n) {
case 1:
System.out.println("Hello World");
break;
case 2:
System.out.println("Second Case");
break;
default:
System.out.println("Default Case");
}
}
}
Output:
Hello World
Using Break to Exit a Loop
With the break statement, you can force the loop to terminate immediately, skipping the remaining iterations. It’s commonly used when you want to stop the loop based on a specific condition.
Example: Breaking out of a Loop
// Java program to illustrate using break to exit a loop
public class BreakLoopDemo {
public static void main(String[] args) {
// The loop runs from 0 to 9
for (int i = 0; i < 10; i++) {
// Terminate loop when i equals 5
if (i == 5)
break;
System.out.println("i: " + i);
}
System.out.println("Loop complete.");
}
}
Output:
i: 0
i: 1
i: 2
i: 3
i: 4
Loop complete.
Time Complexity: O(1)
Auxiliary Space: O(1)
Using Break as a Form of Goto
Java doesn’t support the goto statement because it allows jumping in an unstructured manner, leading to complex and unreadable code. Instead, Java uses labels to identify a block of code. You can use the break statement to jump out of a labeled block.
Syntax:
label:
{
// Code block
statement1;
statement2;
// Break can be used to exit the block
break label;
}
Example: Break with a Label
// Java program to illustrate using break with label
public class BreakLabelDemo {
public static void main(String[] args) {
boolean condition = true;
// Label first
first: {
second: {
third: {
// Before break
System.out.println("Before the break statement");
// If condition is true, break out of the second block
if (condition)
break second;
System.out.println("This won't execute.");
}
System.out.println("This won't execute.");
}
// After exiting the second block
System.out.println("This is after second block.");
}
}
}
Output:
Before the break statement
This is after second block
return keyword in Java
In Java, the scope of a variable refers to the region in the code where the variable is accessible. Java has lexical (static) scoping, meaning the scope of a variable is determined at compile time and is not dependent on the function call stack. The scope rules in Java can be broadly classified into three categories based on where the variables are declared.
1. Member Variables (Class-Level Scope) 2. Local Variables (Method-Level Scope) 3. Block Variables (Loop or Block-Level Scope)
1. Member Variables (Class-Level Scope)
Member variables are declared inside a class but outside any method, constructor, or block. They can be accessed anywhere within the class and can have different access levels (e.g., public, private, protected, or default). Access to member variables outside the class depends on the access modifier used.
Example:
public class Test {
// Member variables
int a; // Default access modifier
private String b; // Private member variable
char c; // Default access modifier
void method1() {
// Member variables can be accessed here
System.out.println(a);
System.out.println(b);
}
int method2() {
return a;
}
}
Public: Accessible within the class, in subclasses, and outside the class.
Protected: Accessible within the class and in subclasses but not outside the package.
Default (no modifier): Accessible within the same package but not outside it.
Private: Only accessible within the class.
2. Local Variables (Method-Level Scope)
Local variables are declared inside a method or constructor and are only accessible within that method. They must be initialized before use, and their lifetime is limited to the method’s execution. Once the method finishes, local variables are destroyed.
Example:
public class Test {
void method1() {
// Local variable
int x = 10;
System.out.println(x); // Accessible inside the method
}
public static void main(String[] args) {
Test t = new Test();
t.method1();
}
}
Local variables do not retain their values once the method completes, and they are recreated each time the method is invoked.
3. Block Variables (Loop or Block-Level Scope)
Variables declared inside a block (within curly braces {}) are only accessible within that block. Once the block is exited, these variables are out of scope and cannot be accessed. This applies to variables declared inside loops or conditionals.
Example of Block-Level Scope:
public class Test {
public static void main(String[] args) {
{
int x = 10; // x is only accessible inside this block
System.out.println(x);
}
// Uncommenting the following line will cause an error
// System.out.println(x); // x is out of scope here
}
}
Loop Variables (Block Scope)
Variables declared inside a loop have scope limited to the loop. They cannot be accessed outside the loop.
Example:
class Test {
public static void main(String[] args) {
for (int x = 0; x < 4; x++) {
System.out.println(x); // x is accessible inside the loop
}
// Uncommenting the following line will result in an error
// System.out.println(x); // x is out of scope here
}
}
If you need to access a loop variable outside the loop, declare it before the loop:
class Test {
public static void main(String[] args) {
int x;
for (x = 0; x < 4; x++) {
System.out.println(x);
}
System.out.println(x); // x is accessible outside the loop
}
}
Output:
0
1
2
3
4
Loop Variable Scope with Overlapping Names
In Java, you cannot declare two variables with the same name within the same scope. However, in languages like C++, it’s possible to have the same variable name in nested scopes, which is not allowed in Java.
Incorrect Example in Java (compilation error):
class Test {
public static void main(String[] args) {
int a = 5;
for (int a = 0; a < 5; a++) { // Error: Variable 'a' is already defined
System.out.println(a);
}
}
}
Output:
Error: variable 'a' is already defined
Valid Example with Loop Variable Declaration After Loop
To avoid such errors, you can declare a variable outside the loop and use it after the loop finishes.
Example:
class Test {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
System.out.println(i); // Loop variable i
}
int i = 20; // Declare i after the loop
System.out.println(i); // Access new i outside the loop
}
}
Java provides multiple ways to take input from users through its I/O (Input/Output) package. These streams facilitate the reading of characters, data types, and objects from input devices such as keyboards or files. The most common ways to capture user input in Java are through the BufferedReader class and the Scanner class.
Methods to Take Input in Java
There are two primary ways to get input from the user or a file in Java:
1. Using BufferedReader Class 2. Using Scanner Class
1. Using BufferedReader Class to Take Input in Java
The BufferedReader class is part of Java’s I/O package and is used for reading streams of characters. The readLine() method of this class reads input as a string. InputStreamReader is often used in conjunction with BufferedReader to convert byte streams into character streams, enabling character-based input reading.
Note: BufferedReader can throw checked exceptions, which need to be handled using try-catch blocks or by declaring them with throws.
Example of Taking Input Using BufferedReader:
import java.io.*;
public class InputExample {
public static void main(String[] args) throws IOException {
// Creating a BufferedReader object
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
// Reading string input
System.out.println("Enter a string:");
String str = reader.readLine();
// Reading integer input
System.out.println("Enter an integer:");
int num = Integer.parseInt(reader.readLine());
// Output the values
System.out.println("Entered String: " + str);
System.out.println("Entered Integer: " + num);
}
}
Output:
Enter a string:
John Doe
Enter an integer:
123
Entered String: John Doe
Entered Integer: 123
Alternative Example of BufferedReader Input:
import java.io.*;
public class Example {
public static void main(String[] args) {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
System.out.println("Enter your name:");
String name = reader.readLine(); // Reading string input
System.out.println("Name: " + name);
} catch (IOException e) {
System.out.println("An error occurred: " + e.getMessage());
}
}
}
Sample Output:
Enter your name:
John
Name: John
2. Using Scanner Class for Taking Input in Java
The Scanner class is an advanced and user-friendly input class that is part of Java’s util package. It simplifies input handling by providing various methods like nextInt(), nextFloat(), next(), and nextLine(). These methods allow you to directly capture input without explicit parsing.
Advantages of Scanner:
Easier to use than BufferedReader.
Supports multiple data types (integers, floats, strings, etc.) directly.
No need for throws declarations as no checked exceptions are thrown.
Provides methods to read formatted input easily.
Example of Taking Input Using Scanner:
import java.util.Scanner;
class Main {
public static void main(String[] args) {
// Create Scanner object
Scanner sc = new Scanner(System.in);
// Input a String (single word)
String word = sc.next();
System.out.println("Entered word: " + word);
// Input a String (full sentence)
sc.nextLine(); // Clear the buffer
String sentence = sc.nextLine();
System.out.println("Entered sentence: " + sentence);
// Input an Integer
int num = sc.nextInt();
System.out.println("Entered Integer: " + num);
// Input a floating-point number
float f = sc.nextFloat();
System.out.println("Entered Float: " + f);
}
}
Output:
John
Hello World
123
45.67
Entered word: John
Entered sentence: Hello World
Entered Integer: 123
Entered Float: 45.67
Another Example:
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Enter your name:");
String name = sc.nextLine();
System.out.println("Enter your roll number:");
int rollNo = sc.nextInt();
System.out.println("Enter your marks:");
float marks = sc.nextFloat();
// Output the entered data
System.out.println("Name: " + name);
System.out.println("Roll Number: " + rollNo);
System.out.println("Marks: " + marks);
}
}
Output:
Enter your name:
Alice
Enter your roll number:
10
Enter your marks:
89.5
Name: Alice
Roll Number: 10
Marks: 89.5
Differences Between BufferedReader and Scanner
1. BufferedReader is a basic way to read input, typically used for reading streams of characters. It is faster than Scanner as it doesn’t perform any post-processing or parsing. 2. Scanner is more user-friendly for simple input, especially when you need to read different data types. However, Scanner is slower because it performs input parsing internally (like nextInt(), nextFloat()). 3. BufferedReader allows you to specify the size of the input stream to be read, making it more flexible when handling large input. 4. BufferedReader is synchronized, making it suitable for multi-threaded applications, while Scanner is not. 5. Scanner is typically preferred for smaller inputs due to its simplicity, while BufferedReader is favored for larger inputs due to better performance.
Scanner Class in Java
The Scanner class in Java is part of the java.util package and is used to obtain input from the user, including primitive data types like int, double, and boolean, as well as strings.
While the Scanner class is the simplest way to read input, it’s not the most efficient when performance is critical, such as in competitive programming, due to additional overhead.
Input Types in Java Scanner
The Scanner class can take various types of input from the user. Some of the commonly used methods for extracting data from an input stream are listed below:
Method
Description
nextBoolean()
Reads a Boolean value
nextByte()
Reads a Byte value
nextDouble()
Reads a Double value
nextFloat()
Reads a Float value
nextInt()
Reads an Integer value
nextLine()
Reads an entire line as a String
nextLong()
Reads a Long value
nextShort()
Reads a Short value
Example 1: Reading Data of Various Types
Below is a sample code that demonstrates how to read different types of data using the Scanner class.
// Java program to read data of various types using Scanner class.
import java.util.Scanner;
public class ScannerDemo1 {
public static void main(String[] args) {
// Creating a Scanner object for input
Scanner sc = new Scanner(System.in);
// Reading a String input
String name = sc.nextLine();
// Reading a Character input
char gender = sc.next().charAt(0);
// Reading numerical data
int age = sc.nextInt();
long mobileNo = sc.nextLong();
double cgpa = sc.nextDouble();
// Outputting the values to check input
System.out.println("Name: " + name);
System.out.println("Gender: " + gender);
System.out.println("Age: " + age);
System.out.println("Mobile Number: " + mobileNo);
System.out.println("CGPA: " + cgpa);
}
}
Input:
Alice
F
25
9876543210
8.9
Output:
Name: Alice
Gender: F
Age: 25
Mobile Number: 9876543210
CGPA: 8.9
Checking Input Type with hasNextXYZ() Methods
In some cases, we need to verify the type of the next input or whether the input has ended (e.g., when encountering the EOF marker). The hasNextXYZ() methods allow us to check if the next token is of the desired type. Here, XYZ represents the data type of interest.
For instance, we can check for an integer using hasNextInt() or a string using hasNextLine(). Similarly, to check for a single character, we can use hasNext().charAt(0).
Example 2: Calculating the Mean of Numbers
// Java program to read values using Scanner and calculate their mean.
import java.util.Scanner;
public class ScannerDemo2 {
public static void main(String[] args) {
// Initialize Scanner object
Scanner sc = new Scanner(System.in);
// Initialize sum and count variables
int sum = 0, count = 0;
// Read integers until non-integer input is encountered
while (sc.hasNextInt()) {
int num = sc.nextInt();
sum += num;
count++;
}
// Calculate and print the mean
if (count > 0) {
int mean = sum / count;
System.out.println("Mean: " + mean);
} else {
System.out.println("No integers were input. Mean cannot be calculated.");
}
}
}
Input:
1 2 3 4 5
Output:
Mean: 3
Key Points About the Scanner Class
1. Creating a Scanner Object: To create a Scanner object, we usually pass the predefined System.in object, which represents standard input. We can also pass a File object to read input from a file. 2. Reading Numerical Values: For each primitive data type, there is a corresponding nextXYZ() method to read its value. For example, nextShort() reads a short value, and nextInt() reads an int. 3. Reading Strings: To read strings, we use nextLine(). This method reads the entire line until a newline character is encountered. 4. Reading a Single Character: To read a single character, we can use next().charAt(0). The next() method reads the next token (a word or symbol), and charAt(0) returns the first character of that token. 5. How Scanner Reads Input: The Scanner class reads an entire line and divides it into tokens. Each token represents a meaningful piece of the input. For example, if the input string is “Hello world”, the Scanner object will treat “Hello” and “world” as two separate tokens, and we can iterate over these tokens using its various methods.
Ways to read input from console in Java
In Java, there are several ways to read input from the user in the command-line environment. Here are five methods:
1. Using BufferedReader Class
The BufferedReader class is the traditional way to take input, introduced in JDK 1.0. It wraps the standard input stream (System.in) in an InputStreamReader, which is further wrapped in a BufferedReader for efficient reading of input.
Key Points:
Input is buffered for performance.
Syntax can be a bit more complex compared to newer methods.
Example:
// Java program to demonstrate BufferedReader
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Test {
public static void main(String[] args) throws IOException {
// Using BufferedReader to get input
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
// Reading a string input
String name = reader.readLine();
// Printing the input
System.out.println(name);
}
}
Input:
John
Output:
John
2. Using Scanner Class
The Scanner class, introduced in JDK 1.5, is one of the most commonly used methods to read input. It can parse primitive types and strings using regular expressions.
Advantages:
Simplifies reading primitive types like int, float, and more.
Regular expressions can be used to tokenize input.
Example:
import java.util.Scanner;
public class InputExample {
public static void main(String[] args) {
// Create Scanner object
Scanner scn = new Scanner(System.in);
// Read a single word as a string
System.out.println("Enter a word:");
String word = scn.next();
// Read a full line as a string
System.out.println("Enter a sentence:");
scn.nextLine(); // consume the leftover newline
String sentence = scn.nextLine();
// Read an integer
System.out.println("Enter an integer:");
int number = scn.nextInt();
// Read a float value
System.out.println("Enter a floating point number:");
float decimal = scn.nextFloat();
// Display the inputs
System.out.println("Word: " + word);
System.out.println("Sentence: " + sentence);
System.out.println("Integer: "
Input:
Alice
25
4.5
Output:
You entered string: Alice
You entered integer: 25
You entered float: 4.5
3. Using Console Class
Introduced in JDK 1.6, the Console class is useful for reading input from the command line, especially for secure inputs like passwords (where input isn’t displayed). However, it may not work in non-interactive environments like IDEs.
Advantages:
Can hide input (useful for password entry).
Supports formatted input and output.
Example:
// Java program to demonstrate Console class
public class Sample {
public static void main(String[] args) {
// Using Console to get input
String name = System.console().readLine();
System.out.println("You entered string: " + name);
}
}
Input:
Michael
Output:
You entered string: Michael
4. Using Command-Line Arguments
This method uses the command-line arguments passed to the program at runtime. The arguments are stored as strings in the args[] array, and can be converted to other data types using methods like Integer.parseInt().
Example:
// Program to demonstrate command-line arguments
class Hello {
public static void main(String[] args) {
// Check if arguments were passed
if (args.length > 0) {
System.out.println("The command line arguments are:");
// Loop through the args array
for (String val : args)
System.out.println(val);
} else {
System.out.println("No command line arguments found.");
}
}
}
Command Line Arguments:
java Hello John Doe
Output:
The command line arguments are:
John
Doe
5. Using DataInputStream Class
The DataInputStream class allows reading primitive data types and strings from an input stream in a machine-independent way. This class is useful when working with binary input/output, and it was introduced in JDK 1.0.
Key Points:
Reads binary data in a machine-independent format.
Often used with DataOutputStream for consistent data handling.
Example:
// Java program to demonstrate DataInputStream
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
DataInputStream reader = new DataInputStream(System.in);
// Reading an integer input
System.out.print("Enter an integer: ");
int inputInt = Integer.parseInt(reader.readLine());
// Reading a string input
System.out.print("Enter a string: ");
String inputString = reader.readLine();
// Output the input values
System.out.println("You entered integer: " + inputInt);
System.out.println("You entered string: " + inputString);
}
}
Input:
Enter an integer: 30
Enter a string: Java Programming
Output:
You entered integer: 30
You entered string: Java Programming
Java System.out.println()
The System.out.println() method is used in Java to print a message or value to the console. It is a commonly used function that prints any argument passed to it, followed by a new line.
Breakdown of System.out.println()
The statement can be split into three parts for better understanding:
1. System: This is a final class from the java.lang package. 2. out: A static member of the System class, which is an instance of the PrintStream class. 3. println(): A method of the PrintStream class that prints the passed argument and adds a newline character at the end. It’s an enhanced version of print() that automatically moves the cursor to the next line.
Syntax:
System.out.println(parameter);
Example of System.out.println()
Example : Basic Output
public class Example {
public static void main(String[] args) {
System.out.println("Hello");
System.out.println("World");
System.out.println("Java Programming");
}
}
Output:
Hello
World
Java Programming
Java’s Other Standard Streams
System.in: The standard input stream used for reading input from the keyboard.
System.err: The standard error stream used to display error messages.
Example:
System.err.print("This is an error message");
Overloads of println() Method
Java supports method overloading, allowing multiple methods with the same name but different parameter types. PrintStream provides various overloads of the println() method for different data types.
Example of Overloaded println() Methods
public class Example {
public static void main(String[] args) {
int num = 15;
char letter = 'A';
String text = "Java";
double decimal = 25.78;
float floatNum = 10.5f;
boolean flag = true;
// Various overloaded versions of println()
System.out.println(); // Prints an empty line
System.out.println(num);
System.out.println(letter);
System.out.println(text);
System.out.println(decimal);
System.out.println(floatNum);
System.out.println(flag);
}
}
Output:
15
A
Java
25.78
10.5
true
Difference Between System.out.print() and System.out.println()
System.out.print(): Prints the provided text or value on the console and keeps the cursor on the same line.
System.out.println(): Prints the provided text or value, and moves the cursor to the next line.
Example:
public class Example {
public static void main(String[] args) {
System.out.println("Using print():");
// Using print()
System.out.print("Hello ");
System.out.print("World ");
System.out.print("Java");
System.out.println(); // Moving to the next line
System.out.println("Using println():");
// Using println()
System.out.println("Hello");
System.out.println("World");
System.out.println("Java");
}
}
Output:
Using print():
Hello World Java
Using println():
Hello
World
Java
Formatted Output in Java using printf()
In programming, it is often necessary to display output in a specific format. Java’s printf() method, similar to the C language’s printf, allows formatting of output using format specifiers. In this article, we’ll cover different ways to format output in Java using printf().
Formatting Using printf()
printf() uses format specifiers to format different data types. The commonly used data types for formatting are:
1. Number Formatting 2. Decimal Number Formatting 3. Boolean Formatting 4. Character Formatting 5. String Formatting 6. Date and Time Formatting
1. Number Formatting
For formatting integers (like int, long), the format specifier used is %d.
Example:
public class Example {
public static void main(String[] args) {
int number = 10000;
// Format with commas separating thousands
System.out.printf("%,d%n", number);
}
}
Output:
10,000
2. Decimal Number Formatting
To format decimal numbers, we use the %f specifier.
Example:
public class Example {
public static void main(String[] args) {
double pi = 3.14159265359;
// Formatting decimal numbers
System.out.printf("%f\n", pi);
System.out.printf("%5.3f\n", pi);
System.out.printf("%5.2f\n", pi);
}
}
Output:
3.141593
3.142
3.14
3. Boolean Formatting
Boolean values can be formatted using %b or %B.
Example:
public class Example {
public static void main(String[] args) {
boolean boolTrue = true;
boolean boolFalse = false;
Integer nullValue = null;
System.out.printf("%b\n", boolTrue); // true
System.out.printf("%B\n", boolTrue); // TRUE
System.out.printf("%b\n", boolFalse); // false
System.out.printf("%B\n", nullValue); // FALSE
}
}
Output:
true
TRUE
false
FALSE
4. Character Formatting
Character formatting is done using the %c or %C specifiers.
Example:
public class Example {
public static void main(String[] args) {
char character = 'a';
System.out.printf("%c\n", character); // a
System.out.printf("%C\n", character); // A (uppercase)
}
}
Output:
a
A
5. String Formatting
Strings are formatted using %s or %S.
Example:
public class Example {
public static void main(String[] args) {
String text = "java programming";
System.out.printf("%s\n", text); // java programming
System.out.printf("%S\n", text); // JAVA PROGRAMMING
}
}
Output:
java programming
JAVA PROGRAMMING
6. Date and Time Formatting
Formatting date and time is more complex and requires specific knowledge of format specifiers such as %tT, %tH, %tM, etc.
Example:
import java.util.Date;
public class Example {
public static void main(String[] args) {
Date currentTime = new Date();
System.out.printf("Current Time: %tT\n", currentTime); // Full time format
System.out.printf("Hours: %tH Minutes: %tM Seconds: %tS\n",
currentTime, currentTime, currentTime); // Individual components
// Time with AM/PM, milliseconds, nanoseconds, and time zone
System.out.printf("%1$tH:%1$tM:%1$tS %1$tp %1$tL %1$tN %1$tz%n", currentTime);
}
}
Output:
Current Time: 11:32:36
Hours: 11 Minutes: 32 Seconds: 36
11:32:36 am 198 198000000 +0000
Other Methods for Formatting
1. Decimal Formatting using DecimalFormat
DecimalFormat can be used to format numbers with various patterns.
Example:
import java.text.DecimalFormat;
public class DecimalFormatting {
public static void main(String[] args) {
double num = 123.4567;
// Formatting without fraction part
DecimalFormat df = new DecimalFormat("####");
System.out.println("Without fraction part: " + df.format(num));
// Formatting to 2 decimal places
df = new DecimalFormat("#.##");
System.out.println("Formatted to 2 decimal places: " + df.format(num));
// Formatting with appended zeroes
df = new DecimalFormat("#.000000");
System.out.println("With appended zeroes: " + df.format(num));
// Formatting with leading zeroes
df = new DecimalFormat("00000.00");
System.out.println("With leading zeroes: " + df.format(num));
// Formatting currency
double income = 23456.789;
df = new DecimalFormat("$###,###.##");
System.out.println("Formatted income: " + df.format(income));
}
}
Output:
Without fraction part: 123
Formatted to 2 decimal places: 123.46
With appended zeroes: 123.456700
With leading zeroes: 00123.46
Formatted income: $23,456.79
2. Formatting Dates using SimpleDateFormat
SimpleDateFormat allows date formatting based on custom patterns.
Example:
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormatting {
public static void main(String[] args) {
// Formatting date to 'dd-MM-yyyy'
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
String formattedDate = sdf.format(new Date());
System.out.println("Formatted Date: " + formattedDate);
// Parsing a string date
String dateStr = "02/18/1995";
sdf = new SimpleDateFormat("MM/dd/yyyy");
try {
Date date = sdf.parse(dateStr);
System.out.println("Parsed Date: " + date);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Output:
Formatted Date: 03-10-2024
Parsed Date: Sat Feb 18 00:00:00 UTC 1995
3. Static Variables
Static variables are shared across all instances of a class and are declared with the static keyword. Only one copy of a static variable exists, regardless of how many objects are created.
// Example of static variables
public class StaticVariableExample {
public static String company = "TechCorp"; // Static variable
public static void main(String[] args) {
System.out.println("Company: " + StaticVariableExample.company); // Accessing without object
}
}
Output:
Company: TechCorp
Java Variables
In Java, the scope of a variable refers to the region in the code where the variable is accessible. Java has lexical (static) scoping, meaning the scope of a variable is determined at compile time and is not dependent on the function call stack. The scope rules in Java can be broadly classified into three categories based on where the variables are declared.
1. Member Variables (Class-Level Scope) 2. Local Variables (Method-Level Scope) 3. Block Variables (Loop or Block-Level Scope)
1. Member Variables (Class-Level Scope)
Member variables are declared inside a class but outside any method, constructor, or block. They can be accessed anywhere within the class and can have different access levels (e.g., public, private, protected, or default). Access to member variables outside the class depends on the access modifier used.
Example:
public class Test {
// Member variables
int a; // Default access modifier
private String b; // Private member variable
char c; // Default access modifier
void method1() {
// Member variables can be accessed here
System.out.println(a);
System.out.println(b);
}
int method2() {
return a;
}
}
Public: Accessible within the class, in subclasses, and outside the class.
Protected: Accessible within the class and in subclasses but not outside the package.
Default (no modifier): Accessible within the same package but not outside it.
Private: Only accessible within the class.
2. Local Variables (Method-Level Scope)
Local variables are declared inside a method or constructor and are only accessible within that method. They must be initialized before use, and their lifetime is limited to the method’s execution. Once the method finishes, local variables are destroyed.
Example:
public class Test {
void method1() {
// Local variable
int x = 10;
System.out.println(x); // Accessible inside the method
}
public static void main(String[] args) {
Test t = new Test();
t.method1();
}
}
Local variables do not retain their values once the method completes, and they are recreated each time the method is invoked.
3. Block Variables (Loop or Block-Level Scope)
Variables declared inside a block (within curly braces {}) are only accessible within that block. Once the block is exited, these variables are out of scope and cannot be accessed. This applies to variables declared inside loops or conditionals.
Example of Block-Level Scope:
public class Test {
public static void main(String[] args) {
{
int x = 10; // x is only accessible inside this block
System.out.println(x);
}
// Uncommenting the following line will cause an error
// System.out.println(x); // x is out of scope here
}
}
Loop Variables (Block Scope)
Variables declared inside a loop have scope limited to the loop. They cannot be accessed outside the loop.
Example:
class Test {
public static void main(String[] args) {
for (int x = 0; x < 4; x++) {
System.out.println(x); // x is accessible inside the loop
}
// Uncommenting the following line will result in an error
// System.out.println(x); // x is out of scope here
}
}
If you need to access a loop variable outside the loop, declare it before the loop:
class Test {
public static void main(String[] args) {
int x;
for (x = 0; x < 4; x++) {
System.out.println(x);
}
System.out.println(x); // x is accessible outside the loop
}
}
Output:
0
1
2
3
4
Loop Variable Scope with Overlapping Names
In Java, you cannot declare two variables with the same name within the same scope. However, in languages like C++, it’s possible to have the same variable name in nested scopes, which is not allowed in Java.
Incorrect Example in Java (compilation error):
class Test {
public static void main(String[] args) {
int a = 5;
for (int a = 0; a < 5; a++) { // Error: Variable 'a' is already defined
System.out.println(a);
}
}
}
Output:
Error: variable 'a' is already defined
Valid Example with Loop Variable Declaration After Loop
To avoid such errors, you can declare a variable outside the loop and use it after the loop finishes.
Example:
class Test {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
System.out.println(i); // Loop variable i
}
int i = 20; // Declare i after the loop
System.out.println(i); // Access new i outside the loop
}
}