Introduction to Classes & Objects in Java

In Java, object-oriented programming (OOP) allows developers to model real-world entities and their interactions. At the heart of OOP lies the class. A class serves as a blueprint for creating objects, which encapsulate both state and behavior.
Imagine you’re building a student management system. Instead of writing scattered, unorganized code, you can create modular, reusable components like Student
, Course
, and Teacher
classes. This modularity makes the code easier to maintain, scale, and debug.
By understanding classes and objects, you can write clean and organized Java programs.
Definition of a Class
A class in Java is a blueprint for creating objects. It defines the properties (fields) and behaviors (methods) that the objects will possess.
Syntax of a Class
Here’s the basic structure of a class in Java:
class ClassName {
// Fields (Instance Variables)
DataType fieldName;
// Methods (Actions)
ReturnType methodName() {
// Method body
}
}
Class Members
Class members are elements defined within a class. These include:
Fields (Instance Variables): Represent the state or attributes of an object.
Methods: Define the behavior or actions an object can perform.
Access Modifiers: Control the visibility and accessibility of fields and methods.
1. Fields (Instance Variables)
Fields are variables that store the state or data of an object.
Example:
String name;
int age;
2. Methods
Methods perform actions and define the behavior of a class. Methods can return values or perform tasks.
Example:
void introduce() {
System.out.println("Hi, I'm " + name + " and I'm " + age + " years old.");
}
3. Access Modifiers
Access modifiers control the visibility of class members. The key access modifiers are:
public
: The member can be accessed from any other class.private
: The member can only be accessed within the class itself.protected
: The member can be accessed within the package and by subclasses.Default (Package-Private): When no modifier is specified, the member is accessible within the same package.
Example:
public String name;
private int age;
String address; // Default access (package-private)
Creating Instances of a Class
An object is an instance of a class. To create an object, use the new
keyword, which allocates memory and initializes the object.
Syntax for Creating an Object
ClassName objectName = new ClassName();
Code Example with Access Modifiers and File Conventions
In Java, the file name must match the public
class name. Other classes can reside in the same file, but only one class can be declared public
.
Example Code:
Create a file named Main.java
with the following content:
// The public class name matches the file name (Main.java)
public class Main {
public static void main(String[] args) {
// Creating instances of the Person class
Person person1 = new Person();
person1.name = "Alice";
person1.age = 25;
person1.introduce();
Person person2 = new Person();
person2.name = "Bob";
person2.age = 30;
person2.introduce();
}
}
// Another class in the same file (non-public)
class Person {
// Fields with different access modifiers
public String name; // Accessible everywhere
int age; // Default access (package-private)
// Method to introduce the person
public void introduce() {
System.out.println("Hi, I'm " + name + " and I'm " + age + " years old.");
}
}
Explanation of the Code
File Name:
The file is namedMain.java
because thepublic
class isMain
.Class Definitions:
The
Main
class contains themain
method, which is the entry point of the program.The
Person
class is defined in the same file but is not declaredpublic
, so it is accessible only within the same package.
Access Modifiers:
The
name
field in thePerson
class ispublic
, allowing it to be accessed directly from theMain
class.The
age
field has default (package-private) access, meaning it can be accessed from any class within the same package.
Creating Objects:
Instances of the
Person
class are created using thenew
keyword.Fields are assigned values, and the
introduce()
method is called to display the person's information.
Output
When you compile and run the program, you will get:
Hi, I'm Alice and I'm 25 years old.
Hi, I'm Bob and I'm 30 years old.
Key Points to Remember
Class Structure: Classes define fields (state) and methods (behavior).
Access Modifiers:
public
,private
,protected
, and default (package-private).
File Naming:
The
public
class name must match the file name.Multiple non-public classes can exist in the same file.
Object Creation:
- Use the
new
keyword to instantiate objects.
- Use the
Encapsulation:
- Proper use of access modifiers helps in encapsulating data and maintaining code integrity.
In Java, the static
keyword provides a way to create class-level members that are shared across all instances of a class. This is useful when you need to maintain shared data or behavior, such as a counter that tracks the number of instances created. You can access static
members without creating an instance of the class, which makes them efficient for tasks that don’t rely on object-specific data.
The final
keyword is used to enforce immutability and prevent changes. Whether you want to declare constants, prevent method overriding, or avoid subclassing, final
helps maintain integrity in your code.
Together, static
and final
play crucial roles in writing efficient and maintainable Java programs.
1. Static Variables and Methods
Static Variables
A static variable (also known as a class variable) is shared among all instances of a class. When a static variable is modified by one instance, the change is reflected across all other instances.
Key Points:
Declared using the
static
keyword.Belongs to the class itself, not to any specific instance.
Initialized only once, at the time of class loading.
Static Methods
A static method belongs to the class rather than any instance. You can call a static method without creating an instance of the class.
Key Points:
Declared using the
static
keyword.Can access static variables directly.
Cannot access instance variables or methods unless an instance is explicitly referenced.
Example of Static Variables and Methods
class Counter {
// Static variable shared by all instances
static int count = 0;
// Static method to access the static variable
static void displayCount() {
System.out.println("Count: " + count);
}
// Instance method to increment the static variable
void increment() {
count++;
}
}
public class Main {
public static void main(String[] args) {
Counter obj1 = new Counter();
Counter obj2 = new Counter();
obj1.increment();
obj2.increment();
// Accessing static method without creating an instance
Counter.displayCount(); // Output: Count: 2
}
}
Explanation
Static Variable:
count
is a static variable that keeps track of the number of increments.Static Method:
displayCount()
can be called without creating an instance ofCounter
.Shared State:
Bothobj1
andobj2
modify the samecount
variable.
2. Static Blocks
A static block is used for static initialization. It runs once when the class is loaded into memory, before any objects are created.
Key Points:
Declared using
static { }
.Used to initialize static variables or perform setup tasks.
Example of Static Blocks
class Database {
static String dbName;
// Static block for initialization
static {
dbName = "CustomerDB";
System.out.println("Static block executed: Database initialized.");
}
static void connect() {
System.out.println("Connecting to " + dbName);
}
}
public class Main {
public static void main(String[] args) {
Database.connect();
}
}
Output
Static block executed: Database initialized.
Connecting to CustomerDB
Explanation
Static Block:
The static block initializes thedbName
variable when the classDatabase
is first loaded.Single Execution:
The static block runs only once, no matter how many objects are created.
3. The final
Keyword
The final
keyword is used to impose restrictions on variables, methods, and classes.
Final with Variables
When applied to variables,
final
makes them constants.Once initialized, a
final
variable cannot be changed.
Example:
class Constants {
final double PI = 3.14159;
void display() {
System.out.println("Value of PI: " + PI);
}
}
public class Main {
public static void main(String[] args) {
Constants constants = new Constants();
constants.display();
}
}
Final with Methods
- When applied to methods,
final
prevents them from being overridden in subclasses.
Example:
class Parent {
final void show() {
System.out.println("This is a final method.");
}
}
class Child extends Parent {
// Error: Cannot override the final method from Parent
// void show() {
// System.out.println("Trying to override.");
// }
}
Final with Classes
- When applied to classes,
final
prevents them from being subclassed.
Example:
final class Vehicle {
void display() {
System.out.println("This is a vehicle.");
}
}
// Error: Cannot subclass a final class
// class Car extends Vehicle {
// }
Comprehensive Example: Combining Static and Final
class Counter {
// Static variable
static int count = 0;
// Final static constant
static final int MAX_COUNT = 5;
// Static block for initialization
static {
System.out.println("Static block executed: Counter class loaded.");
}
// Method to increment count with a check
void increment() {
if (count < MAX_COUNT) {
count++;
System.out.println("Count incremented to: " + count);
} else {
System.out.println("Maximum count reached: " + MAX_COUNT);
}
}
// Static method to display count
static void displayCount() {
System.out.println("Current count: " + count);
}
}
public class Main {
public static void main(String[] args) {
Counter obj1 = new Counter();
Counter obj2 = new Counter();
obj1.increment();
obj2.increment();
obj1.increment();
obj2.increment();
obj1.increment();
obj2.increment(); // Should reach MAX_COUNT
Counter.displayCount();
}
}
Output
Static block executed: Counter class loaded.
Count incremented to: 1
Count incremented to: 2
Count incremented to: 3
Count incremented to: 4
Count incremented to: 5
Maximum count reached: 5
Current count: 5
Explanation
Static Variable:
count
is shared among all instances.Static Constant:
MAX_COUNT
is afinal static
constant, representing the maximum allowable count.Static Block:
The static block initializes when theCounter
class is first loaded.Increment Logic:
Theincrement()
method checks ifcount
has reachedMAX_COUNT
.
Key Takeaways
Static Members:
Shared across all instances and can be accessed without creating an object.Static Block:
Runs once when the class is loaded, useful for static initialization.Final Keyword:
Variables: For constants (immutable values).
Methods: Prevents overriding.
Classes: Prevents subclassing.
When you create an object in Java, it needs to be initialized with meaningful values. Imagine creating a Car
object—each car should have its own model and manufacturing year. Rather than setting these properties manually for every object, Java provides a mechanism called a constructor to initialize objects at the time of creation.
Constructors help ensure that every object starts in a valid state, either with default values or values provided by the user. This reduces the risk of uninitialized fields and improves code readability and maintainability.
Definition of Constructors
A constructor is a special method used to initialize objects. It has the same name as the class and does not have a return type—not even void
. When an object is created, the constructor is called automatically to set up the initial state of the object.
Key Characteristics of Constructors:
The name of the constructor matches the class name.
It has no return type.
It is called automatically when an object is created using the
new
keyword.You can have multiple constructors in a class (constructor overloading).
Types of Constructors
1. No-Argument Constructor
A no-argument constructor (also called a default constructor) initializes an object with default values. If no constructor is explicitly defined, Java provides a default constructor.
Syntax:
class ClassName {
// No-argument constructor
ClassName() {
// Initialization code
}
}
Example:
class Car {
String model;
int year;
// No-argument constructor
Car() {
model = "Unknown";
year = 2000;
}
void displayInfo() {
System.out.println(model + " - " + year);
}
}
public class Main {
public static void main(String[] args) {
Car car1 = new Car();
car1.displayInfo(); // Output: Unknown - 2000
}
}
Explanation:
The no-argument constructor initializes
model
to"Unknown"
andyear
to2000
.When
new Car()
is called, this constructor is invoked.
2. Parameterized Constructor
A parameterized constructor allows you to initialize an object with user-specified values. This provides flexibility when creating objects with different initial states.
Syntax:
class ClassName {
// Parameterized constructor
ClassName(DataType param1, DataType param2) {
// Initialization code
}
}
Example:
class Car {
String model;
int year;
// Parameterized constructor
Car(String model, int year) {
this.model = model;
this.year = year;
}
void displayInfo() {
System.out.println(model + " - " + year);
}
}
public class Main {
public static void main(String[] args) {
Car car2 = new Car("Toyota", 2021);
car2.displayInfo(); // Output: Toyota - 2021
}
}
Explanation:
The parameterized constructor takes
model
andyear
as parameters and initializes the fields accordingly.The
this
keyword refers to the current object’s fields, differentiating them from the constructor parameters.
Constructor Overloading
Constructor overloading allows a class to have multiple constructors with different parameter lists. This provides multiple ways to initialize an object.
Example:
class Car {
String model;
int year;
// No-argument constructor
Car() {
model = "Unknown";
year = 2000;
}
// Parameterized constructor
Car(String model, int year) {
this.model = model;
this.year = year;
}
// Another parameterized constructor with one parameter
Car(String model) {
this.model = model;
this.year = 2022; // Default year
}
void displayInfo() {
System.out.println(model + " - " + year);
}
}
public class Main {
public static void main(String[] args) {
Car car1 = new Car();
Car car2 = new Car("Toyota", 2021);
Car car3 = new Car("Honda");
car1.displayInfo(); // Output: Unknown - 2000
car2.displayInfo(); // Output: Toyota - 2021
car3.displayInfo(); // Output: Honda - 2022
}
}
Explanation:
No-Argument Constructor: Initializes the fields with default values.
Parameterized Constructor (Two Parameters): Allows specifying both
model
andyear
.Parameterized Constructor (One Parameter): Initializes
model
and setsyear
to a default value (2022
).
When creating an object, the appropriate constructor is called based on the arguments passed.
Key Points to Remember
Constructors are special methods used to initialize objects.
A no-argument constructor provides default initialization.
A parameterized constructor allows custom initialization with user-specified values.
Constructor overloading enables multiple ways to initialize objects within a class.
The
this
Keyword refers to the current object's fields and differentiates them from parameters.
Benefits of Using Constructors
Ensures Initialization: Objects are always initialized properly.
Encapsulation: Keeps object setup logic inside the class.
Flexibility: Constructor overloading allows different initialization scenarios.
Readability: Easier to understand how objects are set up.
Imagine you’re writing a Calculator
class that needs to perform addition. Sometimes, you want to add two integers; other times, you might want to add two floating-point numbers. Instead of creating multiple methods with different names like addIntegers
and addDoubles
, wouldn't it be cleaner to have a single method named add
that works with different types of arguments?
Method overloading allows you to define multiple methods with the same name but different parameters. This makes your code more readable, intuitive, and flexible by allowing methods to handle a variety of input types and numbers of arguments.
What is Method Overloading?
Method overloading occurs when multiple methods in the same class have the same name but different parameter lists. The compiler determines which method to call based on the number, type, and order of arguments provided during the method call.
Key Rules for Method Overloading
Same Method Name: The methods must have the same name.
Different Parameters: The methods must differ in:
The number of parameters, or
The type of parameters, or
The order of parameters (if types are different).
Return Type: The return type can differ, but it does not play a role in determining which method to call.
Examples of Method Overloading
Example 1: Different Parameter Types
class Calculator {
// Method to add two integers
int add(int a, int b) {
return a + b;
}
// Method to add two doubles
double add(double a, double b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println("Integer Addition: " + calc.add(5, 3));
System.out.println("Double Addition: " + calc.add(5.5, 3.2));
}
}
Output
Integer Addition: 8
Double Addition: 8.7
Explanation
The
add(int a, int b)
method handles integer addition.The
add(double a, double b)
method handles double addition.When calling
calc.add(5, 3)
, the compiler selects the method with integer parameters.When calling
calc.add(5.5, 3.2)
, the compiler selects the method with double parameters.
Example 2: Different Number of Parameters
class Printer {
// Method to print one message
void print(String message) {
System.out.println(message);
}
// Overloaded method to print a message multiple times
void print(String message, int times) {
for (int i = 0; i < times; i++) {
System.out.println(message);
}
}
}
public class Main {
public static void main(String[] args) {
Printer printer = new Printer();
printer.print("Hello, World!");
printer.print("Java is fun!", 3);
}
}
Output
Hello, World!
Java is fun!
Java is fun!
Java is fun!
Explanation
The
print(String message)
method prints the message once.The
print(String message, int times)
method prints the message a specified number of times.When calling
printer.print("Hello, World!")
, the compiler selects the single-parameter method.When calling
printer.print("Java is fun!", 3)
, the compiler selects the two-parameter method.
Example 3: Different Parameter Order
class Display {
void show(String message, int count) {
System.out.println("Message: " + message + ", Count: " + count);
}
void show(int count, String message) {
System.out.println("Count: " + count + ", Message: " + message);
}
}
public class Main {
public static void main(String[] args) {
Display display = new Display();
display.show("Overloading", 2);
display.show(5, "Method");
}
}
Output
Message: Overloading, Count: 2
Count: 5, Message: Method
Explanation
The methods
show(String message, int count)
andshow(int count, String message)
have the same name but different parameter orders.The compiler resolves the correct method based on the argument order in the method call.
Benefits of Method Overloading
Improves Code Readability:
You can use the same method name for similar operations, making the code more intuitive.Flexibility:
A single method name can handle different data types or numbers of arguments.Avoids Redundancy:
No need to create multiple methods with different names for similar operations.Enhances Maintainability:
Changes to the method's behavior can be localized to one method name, reducing the risk of errors.
Key Takeaways
Method Overloading allows defining multiple methods with the same name but different parameters.
The compiler determines which method to call based on the argument types, number, and order.
Return types do not distinguish overloaded methods.
Overloading enhances code readability, flexibility, and maintainability.
Subscribe to my newsletter
Read articles from Jyotiprakash Mishra directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Jyotiprakash Mishra
Jyotiprakash Mishra
I am Jyotiprakash, a deeply driven computer systems engineer, software developer, teacher, and philosopher. With a decade of professional experience, I have contributed to various cutting-edge software products in network security, mobile apps, and healthcare software at renowned companies like Oracle, Yahoo, and Epic. My academic journey has taken me to prestigious institutions such as the University of Wisconsin-Madison and BITS Pilani in India, where I consistently ranked among the top of my class. At my core, I am a computer enthusiast with a profound interest in understanding the intricacies of computer programming. My skills are not limited to application programming in Java; I have also delved deeply into computer hardware, learning about various architectures, low-level assembly programming, Linux kernel implementation, and writing device drivers. The contributions of Linus Torvalds, Ken Thompson, and Dennis Ritchie—who revolutionized the computer industry—inspire me. I believe that real contributions to computer science are made by mastering all levels of abstraction and understanding systems inside out. In addition to my professional pursuits, I am passionate about teaching and sharing knowledge. I have spent two years as a teaching assistant at UW Madison, where I taught complex concepts in operating systems, computer graphics, and data structures to both graduate and undergraduate students. Currently, I am an assistant professor at KIIT, Bhubaneswar, where I continue to teach computer science to undergraduate and graduate students. I am also working on writing a few free books on systems programming, as I believe in freely sharing knowledge to empower others.