Classes and Objects in Java: Detailed Notes, Examples & Practice Questions (Object Oriented Programming)

Hello dear students! I am so glad you are here. Today we are going to learn one of the most important chapters in your Object Oriented Programming course: Classes and Objects in Java. This chapter is the foundation of everything else in OOP. If you understand this chapter well, the rest of the course becomes much easier. So please read each section carefully, try every example, and answer all the questions I give you. Let us start!

What is a Class in Java?

In our previous lesson about the introduction to OOP, we learned that a class is like a blueprint. Now let me explain this in much more detail with Java code.

A class in Java is a user-defined blueprint or template from which objects are created. It is a logical structure that defines what kind of data an object will hold and what kind of actions that object can perform. Think of it as a design plan. Just like an architect draws a plan of a house on paper before building it, a programmer writes a class before creating objects from it.

A class in Java is declared using the class keyword. Inside the class body (which is enclosed in curly braces), we write two main things:

  • Fields (Instance Variables): These represent the data or state of the object. They tell us what information the object will store.
  • Methods: These represent the behavior or actions of the object. They tell us what the object can do.

Let me show you the general syntax of a class declaration in Java:

[access_modifier] class ClassName { // Field declarations (instance variables) dataType fieldName; // Constructor(s) ClassName(parameters) { // initialization code } // Method declarations returnType methodName(parameters) { // method body } }

Now let me write a real class that represents a Student. This is the kind of class you will often see in your exam.

class Student { // Fields (Instance Variables) – represent data/state String name; int age; String department; double GPA; // Method – represent behavior/action void study() { System.out.println(name + ” is studying in ” + department + ” department.”); } void displayInfo() { System.out.println(“Name: ” + name); System.out.println(“Age: ” + age); System.out.println(“Department: ” + department); System.out.println(“GPA: ” + GPA); } }

Look at this code carefully. The class name is Student. It has four fields: name, age, department, and GPA. These fields will hold data for each student. It has two methods: study() and displayInfo(). These methods define what a student can do. Right now, this is just a blueprint. No actual student exists yet. We have only defined what a student WILL look like when we create one.

Class: Student (Blueprint on Paper) +————————————————–+ | Fields (What data it holds) | | – name : String | | – age : int | | – department : String | | – GPA : double | |————————————————–| | Methods (What it can do) | | + study() : void | | + displayInfo() : void | +————————————————–+ No actual student exists here. This is only a plan.
Important Notes to Remember for Exam:
1. A class is declared using the class keyword in Java.
2. A class contains fields (data) and methods (behavior).
3. A class is a logical entity (it does not occupy memory for data, only for the class definition).
4. A class does NOT have any actual values in its fields. The fields are just declared, not initialized with specific values.
5. A class can have any number of fields and methods.
6. In Java, every class must be in a file. For a public class, the file name must match the class name (e.g., Student.java for public class Student).
MCQ

Question 1: Which of the following correctly defines a class in Java?

A) A physical entity that occupies memory    B) A blueprint from which objects are created    C) A method that creates variables    D) A keyword used to declare variables

Answer: B) A blueprint from which objects are created

A class is simply a blueprint or template. It defines the structure (fields and methods) but is not a physical entity itself. Option A describes an object, not a class. Option C is wrong because a class is not a method. Option D is wrong because class is not used to declare variables. The word “blueprint” is the key word to look for in the correct answer.

Fill in the Blank

Question 2: The ________ keyword is used to declare a class in Java.

Answer: class

In Java, the class keyword is used to declare a new class. For example, class Student { } declares a class named Student. This is a reserved keyword in Java, meaning you cannot use it as a variable name or method name. Always write it in lowercase.

True or False

Question 3: A class in Java occupies memory for the data of its fields as soon as it is declared.

Answer: False

A class definition does NOT occupy memory for field data. Memory for data is allocated only when an object is created from the class using the new keyword. The class definition itself is loaded into memory (in the method area), but that is just the code/instructions, not the actual data values. Think of it this way: the blueprint of a house does not need land space, but the actual house built from it does.

What is an Object in Java?

An object is a real, physical instance of a class. If the class is the blueprint of a house, the object is the actual house built from that blueprint, sitting on a piece of land, taking up space. When you create an object, Java allocates memory for all the fields defined in the class, and that object now has its own copy of each field with its own values.

You can create many objects from the same class, and each object will be independent. Changing one object’s data does not affect another object’s data. This is a very important point to understand well.

In Java, an object is created using the new keyword. The general syntax is:

ClassName referenceVariable = new ClassName();

Let me break this down because every part is important for your exam:

  • ClassName: The name of the class whose object you are creating.
  • referenceVariable: A name you give to refer to this object. It is like a remote control for the object.
  • new: This keyword tells Java to allocate memory for a new object.
  • ClassName(): This calls the constructor of the class to initialize the object.

Now let me create objects from the Student class we wrote earlier:

class Student { String name; int age; String department; double GPA; void study() { System.out.println(name + ” is studying in ” + department + ” department.”); } void displayInfo() { System.out.println(“Name: ” + name); System.out.println(“Age: ” + age); System.out.println(“Department: ” + department); System.out.println(“GPA: ” + GPA); } } public class Main { public static void main(String[] args) { // Creating first object Student s1 = new Student(); // Setting data for first object s1.name = “Abebe”; s1.age = 21; s1.department = “Computer Science”; s1.GPA = 3.75; // Creating second object Student s2 = new Student(); // Setting data for second object s2.name = “Tigist”; s2.age = 23; s2.department = “Electrical Engineering”; s2.GPA = 3.92; // Using objects s1.displayInfo(); System.out.println(“—“); s2.displayInfo(); System.out.println(“—“); s1.study(); s2.study(); } }

Output:

Name: Abebe Age: 21 Department: Computer Science GPA: 3.75 — Name: Tigist Age: 23 Department: Electrical Engineering GPA: 3.92 — Abebe is studying in Computer Science department. Tigist is studying in Electrical Engineering department.
Objects in Memory STACK HEAP +—————+ +—————————-+ | s1 —-+ | | Student Object 1 | | | | ——-> | name = “Abebe” | | v | | age = 21 | +—————+ | department = “Comp Sci” | | GPA = 3.75 | STACK | study() method link | +—————+ | displayInfo() method link | | s2 —-+ | +—————————-+ | | | | v | +—————————-+ +—————+ | Student Object 2 | | name = “Tigist” | (each reference | age = 23 | points to a | department = “Elec Eng” | separate object | GPA = 3.92 | in the heap) | study() method link | | displayInfo() method link | +—————————-+

See how s1 and s2 are two completely separate objects? Each one has its own name, age, department, and GPA. When I set s1.name to “Abebe”, it does not change s2.name. They are independent of each other. The reference variables s1 and s2 live in the stack, and the actual objects live in the heap.

Important Notes to Remember for Exam:
1. An object is created using the new keyword. Example: Student s1 = new Student();
2. The new keyword allocates memory in the heap for the object.
3. The reference variable (like s1) is stored in the stack and holds the memory address of the object.
4. Each object has its own copy of instance variables. Changing one object does not affect others.
5. You can create any number of objects from one class.
6. The process of creating an object is called instantiation.
7. You access an object’s fields and methods using the dot operator (.). Example: s1.name, s1.study().
MCQ

Question 4: What does the new keyword do when creating an object in Java?

A) Declares a new variable    B) Allocates memory for the object in the heap    C) Deletes an old object    D) Creates a new class

Answer: B) Allocates memory for the object in the heap

The new keyword tells the JVM to allocate memory space in the heap for the new object. It also calls the constructor to initialize the object’s fields. It does not just declare a variable (option A), it does not delete anything (option C), and it does not create a class (option D). Memory allocation in the heap is the key function of the new keyword.

MCQ

Question 5: If you create three Student objects from the same Student class, how many copies of the instance variables exist in memory?

A) One    B) Three    C) Zero    D) It depends on the number of fields

Answer: B) Three

Each object gets its own separate copy of all the instance variables. If the Student class has 4 fields (name, age, department, GPA) and you create 3 objects, then there are 3 separate copies of name, 3 separate copies of age, 3 separate copies of department, and 3 separate copies of GPA in memory. That is 12 variable slots total (3 objects times 4 fields each). Each object is independent with its own data.

True or False

Question 6: The reference variable and the object it points to are stored in the same memory area.

Answer: False

The reference variable (like s1) is stored in the stack memory, while the actual object it points to is stored in the heap memory. The reference variable holds only the memory address (a pointer) of the object, not the object itself. This separation is important to understand for memory-related exam questions.

Declaring and Initializing Fields

Fields (also called instance variables) are the data containers inside a class. Let me now explain how they work in more detail because there are some subtle points that examiners love to test.

When you declare a field without assigning a value, Java gives it a default value automatically. This is important because it is different from local variables (variables inside methods), which do NOT get default values.

Data TypeDefault Value
byte, short, int, long0
float, double0.0
booleanfalse
char‘\u0000’ (null character)
Any object reference (String, etc.)null
class Demo { int x; // default: 0 double y; // default: 0.0 boolean flag; // default: false String name; // default: null char ch; // default: ‘\u0000’ void showDefaults() { System.out.println(“x = ” + x); // x = 0 System.out.println(“y = ” + y); // y = 0.0 System.out.println(“flag = ” + flag); // flag = false System.out.println(“name = ” + name); // name = null System.out.println(“ch = ” + ch); // ch = } }

Notice that name prints as null (not “null” with quotes, just null). This means the String reference is not pointing to any object. And ch prints as nothing because the null character has no visible symbol. These default values are assigned by Java when the object is created, before any constructor code runs.

Important Notes to Remember for Exam:
1. Instance variables get default values automatically (0 for numbers, false for boolean, null for references).
2. Local variables (inside methods) do NOT get default values. You MUST initialize them before use, or you get a compile error.
3. The default value for any object reference type (including String) is null.
4. This difference between instance variables and local variables regarding default values is a very common exam question.
MCQ

Question 7: What is the default value of an int instance variable in Java if you do not initialize it?

A) null    B) 1    C) 0    D) Compile error

Answer: C) 0

For instance variables (fields declared inside a class but outside any method), Java automatically assigns default values. For integer types (byte, short, int, long), the default value is 0. The value null is only for reference types like String or objects. The value 1 is not a default. A compile error would only happen if this were a local variable, not an instance variable.

True or False

Question 8: A local variable declared inside a method gets a default value of 0 if it is not initialized.

Answer: False

Local variables (variables declared inside a method, constructor, or block) do NOT receive any default value in Java. If you try to use a local variable before assigning a value to it, the compiler will give an error. This is a strict rule in Java. Only instance variables (class-level fields) get default values. This is a very important distinction that examiners test frequently.

Access Modifiers for Fields

When you declare fields, you can use access modifiers to control who can access them. There are four access modifiers in Java, and you need to know all of them for your exam.

Access ModifierSame ClassSame PackageSubclass (different package)Everyone
privateYesNoNoNo
default (no keyword)YesYesNoNo
protectedYesYesYesNo
publicYesYesYesYes

For now, the most important ones to understand are private and public. When we want to achieve encapsulation, we make fields private and provide public methods to access them. When we directly access fields from outside (like s1.name = "Abebe"), those fields need to be public or have no modifier (default). In good OOP practice, fields should be private, but in simple exam examples, you might see them as public or default.

Methods in a Class

Methods define the behavior of an object. A method is a block of code that performs a specific task. In Java, every method must be inside a class. You cannot have a method floating on its own outside a class.

The general syntax of a method is:

[access_modifier] [static] returnType methodName(parameterList) { // method body return value; // if returnType is not void }

Let me explain each part:

  • access_modifier: Like public, private, protected, or default. Determines who can call this method.
  • static: Optional keyword. If present, the method belongs to the class, not to objects.
  • returnType: The type of value the method sends back. Use void if the method does not return anything.
  • methodName: A valid Java identifier. Follows naming conventions (camelCase).
  • parameterList: Optional. Values passed to the method when it is called. Can be empty.
  • return statement: Sends a value back. Required if returnType is not void.
class Calculator { // Method with no parameters and no return value void greet() { System.out.println(“Welcome to the Calculator!”); } // Method with parameters and a return value int add(int a, int b) { int sum = a + b; return sum; } // Method with parameters and no return value void displayResult(int result) { System.out.println(“The result is: ” + result); } // Method with no parameters but with return value double getPi() { return 3.14159; } } public class Main { public static void main(String[] args) { Calculator calc = new Calculator(); calc.greet(); // Welcome to the Calculator! int result = calc.add(10, 25); // add returns 35 calc.displayResult(result); // The result is: 35 double pi = calc.getPi(); // getPi returns 3.14159 System.out.println(“Pi = ” + pi); // Pi = 3.14159 } }

See how each method is different? greet() just prints something and returns nothing (void). add() takes two numbers, adds them, and returns the result (int). displayResult() takes a value and prints it but returns nothing (void). getPi() takes nothing but returns a value (double). You need to be comfortable with all these combinations for your exam.

Important Notes to Remember for Exam:
1. A method with void return type does NOT need a return statement.
2. A method with any other return type (int, double, String, etc.) MUST have a return statement that returns a value of that type.
3. The return statement immediately ends the method execution.
4. Parameters are the variables listed in the method declaration. Arguments are the actual values passed when calling the method.
5. A method can have any number of parameters, including zero.
6. Java supports method overloading (same name, different parameters) which we will discuss later in this lesson.
See also  Inheritance and Polymorphism in Java: Detailed Notes, Examples & Practice Questions (Object Oriented Programming)
MCQ

Question 9: What will happen if a method declares a return type of int but does not have a return statement?

A) Returns 0 by default    B) Returns null    C) Compile-time error    D) Runtime error

Answer: C) Compile-time error

If a method declares a non-void return type (like int, double, String, etc.), the Java compiler checks that every possible path through the method ends with a return statement that returns the correct type. If the compiler finds a path that does not return a value, it gives a “missing return statement” compile error. Unlike instance variables, methods do not get default return values. Only void methods can skip the return statement.

Fill in the Blank

Question 10: The values passed to a method when it is called are called ________, while the variables in the method declaration are called ________.

Answer: arguments, parameters

This distinction is important. Parameters are the variable declarations in the method signature, like int a, int b in int add(int a, int b). Arguments are the actual values you pass when calling the method, like 10, 25 in calc.add(10, 25). Parameters receive the values of arguments. Some people use these terms loosely, but in exam contexts, knowing the difference can earn you points.

Constructors in Java

Now we come to one of the most important topics in this chapter. A constructor is a special method inside a class that is used to initialize objects. When you create an object using new, the constructor is automatically called to set up the initial state of the object.

Think of a constructor as the “setup team” for a new object. When a new student joins a university, there is a registration process that sets up their initial information (name, ID, department). A constructor does exactly this for objects in Java.

Rules for Writing Constructors

There are strict rules for constructors that you must memorize:

  • The constructor name must be exactly the same as the class name.
  • A constructor has no return type, not even void.
  • A constructor is called automatically when you use the new keyword. You never call a constructor directly like a regular method.
  • A constructor can have parameters (just like a method).
  • You can have multiple constructors in the same class with different parameters (constructor overloading).

Types of Constructors

1. Default Constructor (No-Argument Constructor)

A default constructor is a constructor that takes no parameters. If you do not write ANY constructor in your class, Java automatically provides a default constructor that has an empty body. But if you write even one constructor yourself, Java will NOT provide the default one.

class Student { String name; int age; // Default constructor (no parameters) Student() { name = “Unknown”; age = 0; System.out.println(“Default constructor called.”); } void displayInfo() { System.out.println(“Name: ” + name + “, Age: ” + age); } } public class Main { public static void main(String[] args) { Student s1 = new Student(); // calls default constructor s1.displayInfo(); // Name: Unknown, Age: 0 } }

When we write new Student(), Java calls the Student() constructor automatically. Inside the constructor, we set default values for name and age. This way, even before the user sets specific values, the object has valid initial values instead of null and 0.

2. Parameterized Constructor

A parameterized constructor takes parameters. It allows you to pass values when creating the object, so the object is initialized with the values you provide right from the start.

class Student { String name; int age; String department; // Parameterized constructor Student(String n, int a, String dept) { name = n; age = a; department = dept; System.out.println(“Parameterized constructor called.”); } void displayInfo() { System.out.println(“Name: ” + name); System.out.println(“Age: ” + age); System.out.println(“Department: ” + department); } } public class Main { public static void main(String[] args) { // Pass values directly when creating the object Student s1 = new Student(“Abebe”, 21, “Computer Science”); Student s2 = new Student(“Tigist”, 23, “Civil Engineering”); s1.displayInfo(); System.out.println(“—“); s2.displayInfo(); } }

Output:

Parameterized constructor called. Parameterized constructor called. Name: Abebe Age: 21 Department: Computer Science — Name: Tigist Age: 23 Department: Civil Engineering

Now compare this with how we created objects earlier (without a constructor). Earlier, we had to write three separate lines to set data: s1.name = "Abebe"; s1.age = 21; s1.department = "Computer Science";. With a parameterized constructor, we do it all in one line: new Student("Abebe", 21, "Computer Science"). Much cleaner and more convenient!

Important Notes to Remember for Exam:
1. A constructor has the same name as the class and no return type (not even void).
2. If you write void Student(), it is NOT a constructor. It is a regular method that happens to have the class name.
3. Java provides a default constructor automatically only if you write no constructor at all.
4. If you write a parameterized constructor but no default constructor, then new ClassName() will cause a compile error.
5. A constructor is called only once per object, at the time of creation.
6. You can call one constructor from another using this() keyword (constructor chaining).
MCQ

Question 11: What will happen if you write void Student() { } inside the Student class and then try Student s = new Student();?

A) The constructor works fine    B) Compile error because Java looks for a constructor without void    C) Runtime error    D) Java automatically removes the void

Answer: B) Compile error because Java looks for a constructor without void

When you write void Student() { }, Java does NOT treat it as a constructor. It treats it as a regular method named “Student” that returns void. Since it is not a constructor, Java will provide the actual default constructor automatically. So new Student() would actually call the auto-generated default constructor, not your method. The real problem comes if you have ONLY void Student() { } and a parameterized constructor like Student(String n). Then the default constructor is NOT auto-generated, and new Student() fails because there is no matching constructor. But in the simplest case with only void Student() { }, new Student() actually compiles because Java provides the real default constructor. The key point is: a constructor must NOT have any return type.

MCQ

Question 12: A class has only one constructor: Book(String title, double price). Which of the following is valid?

A) Book b = new Book();    B) Book b = new Book("Java", 500);    C) Book b = new Book("Java");    D) Both A and B

Answer: B) Book b = new Book("Java", 500);

Since the class has only one constructor that takes two parameters (String and double), you MUST pass exactly two arguments that match those types when creating an object. Option A passes no arguments, so there is no matching constructor (and Java does not provide a default constructor because you already have a constructor). Option C passes only one argument, which also does not match. Only option B passes exactly two arguments of the correct types, so it matches the constructor.

True or False

Question 13: A constructor can be called multiple times on the same object after it is created.

Answer: False

A constructor is called only once, at the exact moment an object is created using the new keyword. You cannot call a constructor on an existing object like s1.Student(). That would be a syntax error. After an object is created, you can call regular methods on it as many times as you want, but the constructor runs only once during creation.

Constructor Overloading

Just like methods, you can have multiple constructors in the same class with different parameter lists. This is called constructor overloading. Each constructor provides a different way to initialize an object.

class Book { String title; String author; double price; // Constructor 1: No parameters Book() { title = “No Title”; author = “Unknown”; price = 0.0; } // Constructor 2: Two parameters Book(String title, String author) { this.title = title; this.author = author; price = 0.0; } // Constructor 3: Three parameters Book(String title, String author, double price) { this.title = title; this.author = author; this.price = price; } void display() { System.out.println(“Title: ” + title + “, Author: ” + author + “, Price: ” + price); } } public class Main { public static void main(String[] args) { Book b1 = new Book(); // uses Constructor 1 Book b2 = new Book(“Java Basics”, “John”); // uses Constructor 2 Book b3 = new Book(“OOP in Java”, “Jane”, 550.0); // uses Constructor 3 b1.display(); // Title: No Title, Author: Unknown, Price: 0.0 b2.display(); // Title: Java Basics, Author: John, Price: 0.0 b3.display(); // Title: OOP in Java, Author: Jane, Price: 550.0 } }

Java decides which constructor to call based on the number and types of arguments you pass. This is similar to method overloading, and the decision is made at compile time.

Constructor Chaining Using this()

You can call one constructor from another constructor in the same class using this(). This is called constructor chaining. It helps avoid repeating code in multiple constructors.

class Book { String title; String author; double price; // Constructor 3 (most detailed) Book(String title, String author, double price) { this.title = title; this.author = author; this.price = price; System.out.println(“Three-parameter constructor called.”); } // Constructor 2 calls Constructor 3 with a default price Book(String title, String author) { this(title, author, 0.0); // calls the three-parameter constructor System.out.println(“Two-parameter constructor called.”); } // Constructor 1 calls Constructor 2 with default author Book() { this(“No Title”, “Unknown”); // calls the two-parameter constructor System.out.println(“No-parameter constructor called.”); } } public class Main { public static void main(String[] args) { Book b = new Book(); } }

Output:

Three-parameter constructor called. Two-parameter constructor called. No-parameter constructor called.

Notice the order! Even though we called new Book() (no-parameter constructor), the execution goes from Constructor 3 to Constructor 2 to Constructor 1. This is because Constructor 1 calls Constructor 2 using this(), and Constructor 2 calls Constructor 3 using this(). The most detailed constructor runs first, then the less detailed ones. The this() call must always be the first statement in the constructor.

Important Notes to Remember for Exam:
1. this() is used to call another constructor in the same class.
2. super() is used to call the parent class constructor (we will learn this in inheritance).
3. this() must be the first statement in the constructor. You cannot write anything before it.
4. You cannot have a circular chain (Constructor A calls B, B calls C, C calls A). This causes a compile error.
5. Constructor chaining reduces code duplication by reusing initialization logic.
MCQ

Question 14: What will be the output of the following code?

class Demo { Demo() { System.out.print(“A “); } Demo(int x) { this(); System.out.print(“B “); } } public class Test { public static void main(String[] args) { Demo d = new Demo(5); } }

A) A B    B) B A    C) A    D) B

Answer: A) A B

When new Demo(5) is called, the parameterized constructor Demo(int x) runs. Its first statement is this(), which calls the no-parameter constructor Demo(). That constructor prints “A ” and finishes. Then control returns to the parameterized constructor, which prints “B “. So the output is “A B”. The key is that this() always runs the called constructor FIRST before continuing with the rest of the calling constructor.

True or False

Question 15: The this() call can be placed anywhere inside a constructor.

Answer: False

The this() call must ALWAYS be the very first statement in the constructor. If you try to put any code before it, even a simple System.out.println(), the Java compiler will give an error. This rule exists because the object must be fully initialized by the called constructor before the current constructor can do any additional work on it.

The “this” Keyword in Detail

We have been using the this keyword in several examples. Now let me explain it completely because it appears in many exam questions.

The this keyword is a reference variable that refers to the current object. It always points to the object on which the method or constructor is being called. Inside a constructor, this refers to the object being created. Inside a method, this refers to the object that called the method.

There are six main uses of this in Java:

Use 1: To distinguish between instance variables and local variables (most common)

class Student { String name; int age; Student(String name, int age) { this.name = name; // this.name = instance variable, name = parameter this.age = age; // this.age = instance variable, age = parameter } }

When the parameter name is the same as the instance variable name, Java gets confused. this.name clearly says “the name field of the current object” and the plain name means “the parameter.” Without this, both would refer to the parameter, and the instance variable would never be set!

Use 2: To call another constructor in the same class

Student() { this(“Unknown”, 0); // calls the parameterized constructor }

Use 3: To pass the current object as a parameter to a method

void printStudent(Student s) { s.displayInfo(); } void show() { printStudent(this); }

Use 4: To return the current object from a method

Student getSelf() { return this; }

Use 5: To call an instance method of the current object

void start() { this.study(); }

Use 6: To access an instance variable (even without naming conflict)

void display() { System.out.println(this.name); }
Important Notes to Remember for Exam:
1. this refers to the current object. Inside a constructor, it is the object being created. Inside a method, it is the object that called the method.
2. The most common use is resolving naming conflicts between instance variables and parameters.
3. this() calls a constructor. this (without parentheses) refers to the current object. They are different.
4. this cannot be used inside a static method or static block because static members do not belong to any object.
5. this can be used to return the current object (method chaining pattern).
MCQ

Question 16: What is the output of the following code?

class Test { int x = 10; void method(int x) { System.out.println(x); System.out.println(this.x); } public static void main(String[] args) { Test t = new Test(); t.method(25); } }

A) 10, 10    B) 25, 25    C) 25, 10    D) 10, 25

Answer: C) 25, 10

The parameter x in the method hides the instance variable x. When we print just x, Java uses the local variable (parameter), which is 25. When we print this.x, Java explicitly uses the instance variable of the current object, which is 10. This is a classic exam question that tests whether you understand the difference between x and this.x when there is a naming conflict.

True or False

Question 17: The this keyword can be used inside a static method to refer to the current object.

Answer: False

Static methods belong to the class, not to any specific object. Since there is no “current object” in a static context, this cannot be used inside a static method. If you try, the compiler will give an error saying “cannot use this in a static context.” This is a very common exam trick question.

Method Overloading in a Class

Method overloading means having two or more methods in the same class with the same name but different parameter lists. The parameters must differ in number, type, or order. The return type alone is NOT enough to overload a method.

Why do we need method overloading? Because it makes your code cleaner and more readable. Instead of having addInt(int, int), addDouble(double, double), and addThree(int, int, int), you can just have add() with different parameters. The compiler figures out which one to call based on the arguments you pass.

class MathOperations { // Version 1: two integers int add(int a, int b) { return a + b; } // Version 2: three integers (different number) int add(int a, int b, int c) { return a + b + c; } // Version 3: two doubles (different type) double add(double a, double b) { return a + b; } // Version 4: different order of parameter types int add(int a, double b) { return (int)(a + b); }
See also  Exception Handling in Java: Detailed Notes, Examples & Practice Questions (Object Oriented Programming)
int add(double a, int b) { return (int)(a + b); } } public class Main { public static void main(String[] args) { MathOperations m = new MathOperations(); System.out.println(m.add(5, 3)); // calls Version 1: 8 System.out.println(m.add(5, 3, 2)); // calls Version 2: 10 System.out.println(m.add(5.5, 3.5)); // calls Version 3: 9.0 System.out.println(m.add(5, 3.5)); // calls Version 4: 8 System.out.println(m.add(5.5, 3)); // calls Version 5: 8 } }

The compiler looks at the number and types of arguments you pass and matches them to the correct method. This matching happens at compile time, which is why method overloading is also called compile-time polymorphism.

Important Notes to Remember for Exam:
1. Overloaded methods must have the same name but different parameters (number, type, or order).
2. Changing only the return type does NOT overload a method. It causes a compile error.
3. The compiler decides which method to call at compile time based on argument types.
4. Constructors can also be overloaded (as we saw earlier).
5. The method signature = method name + parameter list. Return type is NOT part of the signature.
6. Changing only the access modifier does NOT overload a method.
MCQ

Question 18: Which of the following is a valid method overloading?

A) int calc(int a) {} and double calc(int a) {}    B) int calc(int a) {} and int calc(int b) {}    C) int calc(int a) {} and int calc(int a, int b) {}    D) int calc(int a) {} and private int calc(int a) {}

Answer: C) int calc(int a) {} and int calc(int a, int b) {}

Option A is wrong because only the return type is different (int vs double), but the parameters are the same. Return type alone cannot overload. Option B is wrong because the parameter names are different but the types are the same. Parameter names do not matter, only types matter. Option C is correct because the number of parameters is different (one int vs two ints). Option D is wrong because only the access modifier is different. Access modifier alone cannot overload a method.

Find the Output

Question 19: What will be the output?

class Demo { void show(int a) { System.out.println(“int: ” + a); } void show(double a) { System.out.println(“double: ” + a); } } public class Test { public static void main(String[] args) { Demo d = new Demo(); d.show(5); d.show(5.0); d.show(‘A’); } }

Output:

int: 5 double: 5.0 int: 65

Explanation: d.show(5) passes an integer literal, so the show(int) version is called. d.show(5.0) passes a double literal, so the show(double) version is called. d.show('A') passes a character. There is no show(char) version, so Java promotes the char to int (char is automatically promoted to int in Java). The ASCII value of ‘A’ is 65, so it prints “int: 65”. This type promotion during overloading resolution is a very common exam topic!

Static Members: Variables, Methods, and Blocks

We briefly touched on static members in the introduction lesson. Now let me go deep into this topic because it carries many marks in exams.

The word “static” means something that belongs to the class itself, not to any specific object. Let me explain the three types of static members.

Static Variables (Class Variables)

A static variable is declared using the static keyword. Unlike instance variables (where each object gets its own copy), there is only one copy of a static variable, and it is shared by all objects of the class.

class Student { String name; // Instance variable – each object has its own int id; // Instance variable – each object has its own static String university = “AAU”; // Static variable – shared by ALL objects static int studentCount = 0; // Static variable – shared counter Student(String name, int id) { this.name = name; this.id = id; studentCount++; // Every object increases the shared counter } void display() { System.out.println(“Name: ” + name + “, ID: ” + id + “, University: ” + university); } } public class Main { public static void main(String[] args) { System.out.println(“Initial count: ” + Student.studentCount); // 0 Student s1 = new Student(“Abebe”, 1); Student s2 = new Student(“Tigist”, 2); Student s3 = new Student(“Yohannes”, 3); s1.display(); s2.display(); s3.display(); System.out.println(“Total students: ” + Student.studentCount); // 3 // Change static variable using class name Student.university = “Addis Ababa University”; System.out.println(s1.university); // Addis Ababa University System.out.println(s2.university); // Addis Ababa University System.out.println(s3.university); // Addis Ababa University } }

Output:

Initial count: 0 Name: Abebe, ID: 1, University: AAU Name: Tigist, ID: 2, University: AAU Name: Yohannes, ID: 3, University: AAU Total students: 3 Addis Ababa University Addis Ababa University Addis Ababa University

Look at the studentCount variable. It is shared by all three objects. Each time a new Student is created, the same studentCount is increased. After three objects, it shows 3. If you check the count through s1, s2, or s3, or through the class name, you always get the same value because there is only ONE copy.

Similarly, when we change university to “Addis Ababa University”, ALL objects see the new value because they all share the same static variable.

Static vs Instance Variables in Memory METHOD AREA (Class Level) +———————————-+ | Static Variables (One Copy) | | university = “AAU” | | studentCount = 3 | +———————————-+ ^ ^ ^ | | | All objects share these (accessed through any object or class name) HEAP (Object Level) +——————+ +——————+ +——————+ | Object s1 | | Object s2 | | Object s3 | | name = “Abebe” | | name = “Tigist” | | name = “Yohannes”| | id = 1 | | id = 2 | | id = 3 | +——————+ +——————+ +——————+ Each object has its OWN copy of instance variables

Static Methods (Class Methods)

A static method also belongs to the class, not to any object. You can call a static method using the class name without creating any object. Static methods can only access static variables and other static methods directly. They cannot access instance variables or instance methods directly.

class Utility { static int multiplier = 5; // Static method – can be called without creating object static int multiply(int a) { return a * multiplier; // can access static variable } // Static method calling another static method static void showResult(int a) { int result = multiply(a); // OK – calling static from static System.out.println(“Result: ” + result); } // Instance method – needs an object to call void instanceMethod() { System.out.println(“This is an instance method.”); } // ERROR if uncommented: static method cannot access instance method // static void wrongMethod() { // instanceMethod(); // Compile error! // } } public class Main { public static void main(String[] args) { // Call static methods using class name (no object needed) Utility.showResult(10); // Result: 50 Utility.showResult(20); // Result: 100 int result = Utility.multiply(7); System.out.println(“7 x 5 = ” + result); // 7 x 5 = 35 // Can also access static variable using class name System.out.println(“Multiplier: ” + Utility.multiplier); // Multiplier: 5 } }

Static Initialization Block

A static block is a block of code inside a class that is marked with the static keyword. It is used to initialize static variables. The static block runs only once, when the class is first loaded into memory, before any object is created and before any static method is called.

class Config { static String dbName; static int maxConnections; // Static block runs once when class is loaded static { System.out.println(“Static block is running…”); dbName = “UniversityDB”; maxConnections = 100; System.out.println(“Database configured: ” + dbName); } Config() { System.out.println(“Constructor is running…”); } } public class Main { public static void main(String[] args) { System.out.println(“Before creating object”); Config c1 = new Config(); Config c2 = new Config(); } }

Output:

Static block is running… Database configured: UniversityDB Before creating object Constructor is running… Constructor is running…

Notice the order carefully. The static block runs FIRST, before anything else, when the class is loaded. The constructor runs each time an object is created. Even though we create two objects, the static block runs only once. This is because static initialization happens at the class level, not the object level.

Important Notes to Remember for Exam:
1. Static variables are shared by all objects. Only one copy exists.
2. Static methods can only directly access static variables and static methods.
3. Static methods CANNOT directly access instance variables or call instance methods.
4. Access static members using the class name: ClassName.staticMember.
5. The static block runs once when the class is first loaded into memory.
6. You can have multiple static blocks in a class, and they run in the order they appear.
7. main() is static because it must be callable without creating any object.
8. Static members are loaded before instance members.
MCQ

Question 20: What will be the output of the following code?

class Counter { static int count = 0; int num = 0; Counter() { count++; num++; } } public class Test { public static void main(String[] args) { Counter c1 = new Counter(); Counter c2 = new Counter(); Counter c3 = new Counter(); System.out.println(c1.count + ” ” + c1.num); System.out.println(c2.count + ” ” + c2.num); System.out.println(c3.count + ” ” + c3.num); } }

A) 1 1, 2 1, 3 1    B) 3 1, 3 1, 3 1    C) 1 1, 1 1, 1 1    D) 3 3, 3 3, 3 3

Answer: B) 3 1, 3 1, 3 1

The count variable is static, so it is shared by all objects. After creating three objects, count becomes 3. All three objects (c1, c2, c3) see the same count value of 3. The num variable is an instance variable, so each object has its own copy. Each object increments its own num by 1. So c1.num = 1, c2.num = 1, c3.num = 1. The output is “3 1” for all three objects because the static part is shared (3) but the instance part is separate (1 each).

MCQ

Question 21: When does a static initialization block run?

A) Every time an object is created    B) Only once when the class is first loaded into memory    C) When a static method is called    D) At the end of the program

Answer: B) Only once when the class is first loaded into memory

The static block executes exactly once, at the moment the JVM loads the class into memory. This happens before any object is created, before any static method is called, and before any static variable is accessed. Even if you create 100 objects, the static block runs only one time. This makes it ideal for one-time setup tasks like loading configuration or establishing database connections.

Find the Error

Question 22: Find the error in the following code and explain why it occurs:

class Demo { int x = 10; static void show() { System.out.println(x); } }

Error: Compile-time error. A static method cannot access a non-static (instance) variable directly.

Explanation: The method show() is static, which means it belongs to the class and can be called without any object. The variable x is an instance variable, which means it belongs to individual objects. Since the static method is not associated with any particular object, it cannot know which object’s x to access. That is why Java prevents this at compile time. To fix it, either make x static (static int x = 10;) or access x through an object reference (System.out.println(new Demo().x);).

Object as Method Parameter and Return Type

In Java, you can pass an object as an argument to a method, and you can also return an object from a method. These are very important concepts that often appear in practical exam questions.

Passing an Object as a Parameter

class Student { String name; int marks; Student(String name, int marks) { this.name = name; this.marks = marks; } void display() { System.out.println(“Name: ” + name + “, Marks: ” + marks); } } class StudentUtils { // Method that takes an object as parameter void compareStudents(Student s1, Student s2) { if (s1.marks > s2.marks) { System.out.println(s1.name + ” scored higher than ” + s2.name); } else if (s2.marks > s1.marks) { System.out.println(s2.name + ” scored higher than ” + s1.name); } else { System.out.println(“Both scored the same marks.”); } } } public class Main { public static void main(String[] args) { Student s1 = new Student(“Abebe”, 85); Student s2 = new Student(“Tigist”, 92); StudentUtils utils = new StudentUtils(); utils.compareStudents(s1, s2); // Tigist scored higher than Abebe } }

When you pass s1 and s2 to the method, you are actually passing references (memory addresses), not copies of the objects. The method parameters s1 and s2 point to the same objects in the heap. This means if the method modifies the object’s fields through the reference, the original object is also changed.

Returning an Object from a Method

class Student { String name; int marks; Student(String name, int marks) { this.name = name; this.marks = marks; } void display() { System.out.println(“Name: ” + name + “, Marks: ” + marks); } // Method that returns a Student object static Student createStudent(String name, int marks) { Student s = new Student(name, marks); return s; // returning the object } } public class Main { public static void main(String[] args) { // Get an object from a method without using new directly Student s = Student.createStudent(“Abebe”, 85); s.display(); // Name: Abebe, Marks: 85 } }

The return type of the method is Student, which means the method must return a Student object (or null). Inside the method, we create a new Student object and return it. The caller receives this object reference and can use it normally.

Important Notes to Remember for Exam:
1. When you pass an object to a method, you pass a reference (address), not a copy of the object.
2. Because references are passed, changes made to the object inside the method affect the original object.
3. A method can return an object. The return type must be the class type (or a parent class type).
4. For primitive types (int, double, etc.), values are passed by value (a copy is passed).
5. For objects, references are passed by value (a copy of the reference is passed, but it points to the same object).
Find the Output

Question 23: What will be the output?

class Box { int value; Box(int v) { value = v; } } class Demo { static void changeValue(Box b) { b.value = 100; } public static void main(String[] args) { Box box = new Box(50); System.out.println(“Before: ” + box.value); changeValue(box); System.out.println(“After: ” + box.value); } }

Output:

Before: 50 After: 100

Explanation: When we pass box to changeValue(), we are passing the reference (memory address) of the Box object. Inside the method, b points to the same object as box. When b.value = 100 is executed, it changes the actual object’s value. So after the method call, box.value is 100, not 50. This demonstrates that object references are passed, not copies of the objects themselves.

The toString() Method

The toString() method is a special method defined in the Object class (which is the parent of all classes in Java). It returns a string representation of the object. When you print an object using System.out.println(obj), Java automatically calls the toString() method of that object.

If you do not override toString() in your class, the default version returns something like ClassName@hashcode, which is not very useful.

class Student { String name; int age; Student(String name, int age) { this.name = name; this.age = age; } // Override toString() for meaningful output @Override public String toString() { return “Student[name=” + name + “, age=” + age + “]”; } } public class Main { public static void main(String[] args) { Student s1 = new Student(“Abebe”, 21); // Both lines produce the same output because // println automatically calls toString() System.out.println(s1); System.out.println(s1.toString()); } }

Output:

Student[name=Abebe, age=21] Student[name=Abebe, age=21]

Without the overridden toString(), the output would be something like Student@15db9742, which tells you nothing about the object. By overriding toString(), you control what gets printed when someone prints your object. This is a very good practice and often asked in exams.

MCQ

Question 24: What does System.out.println(obj) automatically call on the object?

A) equals()    B) hashCode()    C) toString()    D) getClass()

Answer: C) toString()

When you pass an object to System.out.println() or System.out.print(), Java automatically calls the object’s toString() method and prints whatever string it returns. If the class does not override toString(), the default version from the Object class is used, which returns the class name followed by @ and the hash code in hexadecimal.

Garbage Collection

In languages like C and C++, the programmer must manually free memory by writing code to delete objects. In Java, this is done automatically by a process called garbage collection.

When an object is no longer reachable (no reference variable points to it), it becomes eligible for garbage collection. The Java Garbage Collector (GC) runs in the background and automatically frees the memory used by such objects. You do not need to do anything.

public class Main { public static void main(String[] args) { Student s1 = new Student(“Abebe”, 21); s1 = new Student(“Tigist”, 23); // The first Student object (“Abebe”) is now unreachable // because s1 now points to the new object (“Tigist”) // The first object becomes eligible for garbage collection
See also  Inheritance and Polymorphism in Java
Student s2 = new Student(“Yohannes”, 20); s2 = null; // The Student object (“Yohannes”) is now unreachable // because s2 no longer points to anything // This object also becomes eligible for garbage collection } }
Garbage Collection Process Before: After s1 = new Student(“Tigist”): STACK HEAP STACK HEAP +——+ +——–+ +——+ +——–+ | s1 –+—>| Abebe | | s1 –+—>| Tigist | <-- s1 points here now +------+ +--------+ +------+ +--------+ +--------+ | Abebe | <-- NO reference pointing here +--------+ Eligible for garbage collection!

There are two ways to make an object eligible for garbage collection:

  • Set the reference variable to null: s1 = null;
  • Make the reference variable point to a different object: s1 = new Student(...);

Java also provides a method called System.gc() that you can call to suggest to the JVM that garbage collection should run. But note: it is only a suggestion, not a guarantee. The JVM decides when to actually run the garbage collector.

Before an object is destroyed by the garbage collector, Java calls the finalize() method of that object. You can override finalize() to perform any cleanup operations (like closing files or database connections). However, finalize() is deprecated in newer Java versions and is not reliable for cleanup.

Important Notes to Remember for Exam:
1. Garbage collection in Java is automatic. The programmer does not need to manually free memory.
2. An object becomes eligible for GC when it has no references pointing to it.
3. System.gc() only suggests garbage collection; it does not force it.
4. The finalize() method is called by the GC before destroying an object (but it is deprecated since Java 9).
5. Setting a reference to null or reassigning it makes the old object eligible for GC.
6. Java has four types of GC: Serial GC, Parallel GC, CMS GC, and G1 GC (you may need to know their names).
MCQ

Question 25: When does an object become eligible for garbage collection in Java?

A) When the program ends    B) When the object’s finalize() method is called    C) When no reference variable points to the object    D) When the object is declared as static

Answer: C) When no reference variable points to the object

An object becomes eligible for garbage collection when it is no longer reachable, meaning no active reference variable in the program points to that object. This can happen by setting the reference to null, by reassigning the reference to a different object, or when the reference goes out of scope. The program ending (option A) does clean up, but the question asks about eligibility for GC during program execution. finalize() (option B) is called BEFORE the object is destroyed, not to make it eligible. Static objects (option D) are not eligible because they are always reachable through the class.

True or False

Question 26: Calling System.gc() guarantees that garbage collection will happen immediately.

Answer: False

System.gc() is only a suggestion or request to the JVM. It does NOT guarantee that garbage collection will run immediately or at all. The JVM has its own logic to decide when garbage collection should run based on memory needs and other factors. This is an important point because some students think System.gc() forces GC, but it does not.

Complete Worked Example

Let me now give you one big example that puts together everything we have learned in this lesson. This type of comprehensive question often appears in exams.

class BankAccount { // Instance variables private String accountHolder; private String accountNumber; private double balance; // Static variable static int totalAccounts = 0; static String bankName = “Commercial Bank of Ethiopia”; // Static block static { System.out.println(“=== ” + bankName + ” System Started ===”); } // Default constructor BankAccount() { this(“Unknown”, “0000”, 0.0); } // Parameterized constructor (uses this() for chaining) BankAccount(String holder, String number, double initialBalance) { accountHolder = holder; accountNumber = number; if (initialBalance >= 0) { balance = initialBalance; } else { balance = 0; } totalAccounts++; System.out.println(“Account created for ” + accountHolder); } // Method: deposit (overloaded version 1) void deposit(double amount) { if (amount > 0) { balance = balance + amount; System.out.println(“Deposited: ” + amount + “. New balance: ” + balance); } else { System.out.println(“Invalid deposit amount!”); } } // Method: deposit (overloaded version 2 – different parameter type) void deposit(int amount) { deposit((double) amount); // reuse the double version } // Method: withdraw boolean withdraw(double amount) { if (amount > 0 && amount <= balance) { balance = balance - amount; System.out.println("Withdrawn: " + amount + ". New balance: " + balance); return true; } else { System.out.println("Withdrawal failed! Insufficient balance or invalid amount."); return false; } } // Getters String getHolder() { return accountHolder; } double getBalance() { return balance; } // toString override @Override public String toString() { return "Account[" + accountNumber + ", " + accountHolder + ", Balance: " + balance + "]"; } // Static method static void showBankInfo() { System.out.println("Bank: " + bankName); System.out.println("Total accounts: " + totalAccounts); } } public class Main { public static void main(String[] args) { BankAccount.showBankInfo(); System.out.println("---"); BankAccount a1 = new BankAccount("Abebe Kebede", "CBE001", 5000); BankAccount a2 = new BankAccount("Tigist Haile", "CBE002", 8000); BankAccount a3 = new BankAccount(); // uses default constructor System.out.println("---"); a1.deposit(1500); a1.deposit(500); // overloaded version (int) a1.withdraw(2000); System.out.println("---"); System.out.println(a1); System.out.println(a2); System.out.println(a3); System.out.println("---"); BankAccount.showBankInfo(); } }

Output:

=== Commercial Bank of Ethiopia System Started === Bank: Commercial Bank of Ethiopia Total accounts: 0 — Account created for Abebe Kebede Account created for Tigist Haile Account created for Unknown — Deposited: 1500.0. New balance: 6500.0 Deposited: 500.0. New balance: 7000.0 Withdrawn: 2000.0. New balance: 5000.0 — Account[CBE001, Abebe Kebede, Balance: 5000.0] Account[CBE002, Tigist Haile, Balance: 8000.0] Account[0000, Unknown, Balance: 0.0] — Bank: Commercial Bank of Ethiopia Total accounts: 3

Let me walk you through what happened step by step:

  • The static block ran first when the BankAccount class was loaded, printing the bank name header.
  • We called showBankInfo() before creating any objects. Total accounts was 0.
  • We created 3 accounts. a1 and a2 use the parameterized constructor, a3 uses the default constructor which chains to the parameterized one using this(). Each creation increments totalAccounts.
  • We used both overloaded versions of deposit() – one with double, one with int.
  • We printed the objects using System.out.println(a1), which automatically called our overridden toString() method.
  • Finally, we called showBankInfo() again, and now total accounts shows 3.
Find the Output

Question 27: What will be the output of the following code?

class Test { static int x = 10; int y = 20; static { x = 50; } Test() { y = 100; } public static void main(String[] args) { Test t1 = new Test(); Test t2 = new Test(); System.out.println(Test.x); System.out.println(t1.y); System.out.println(t2.x); } }

Output:

50 100 50

Explanation: The static block runs first and changes x from 10 to 50. Since x is static, all objects share this value. When we print Test.x, it shows 50. When we print t2.x, it also shows 50 because t2 accesses the same static variable. The instance variable y is set to 100 by the constructor. Each object has its own y, and for t1 it is 100. Even though y was initially 20, the constructor changes it to 100 for each object.

Exercises with Answers

Exercise 1: Create a class called Rectangle with private fields length and width (both double). Include: (a) a default constructor that sets both to 1.0, (b) a parameterized constructor, (c) getter and setter methods for both fields with validation (values must be positive), (d) methods calculateArea() and calculatePerimeter(), (e) a toString() method. Write a main method to test everything.

Area of rectangle:

Perimeter of rectangle:

class Rectangle { private double length; private double width; // Default constructor Rectangle() { this.length = 1.0; this.width = 1.0; } // Parameterized constructor Rectangle(double length, double width) { setLength(length); setWidth(width); } // Getters double getLength() { return length; } double getWidth() { return width; } // Setters with validation void setLength(double length) { if (length > 0) { this.length = length; } else { System.out.println(“Length must be positive! Set to 1.0”); this.length = 1.0; } } void setWidth(double width) { if (width > 0) { this.width = width; } else { System.out.println(“Width must be positive! Set to 1.0”); this.width = 1.0; } } // Calculate area double calculateArea() { return length * width; } // Calculate perimeter double calculatePerimeter() { return 2 * (length + width); } // Override toString @Override public String toString() { return “Rectangle[length=” + length + “, width=” + width + “, area=” + calculateArea() + “, perimeter=” + calculatePerimeter() + “]”; } } public class Main { public static void main(String[] args) { Rectangle r1 = new Rectangle(); Rectangle r2 = new Rectangle(10, 5); Rectangle r3 = new Rectangle(-3, 8); // Length must be positive! System.out.println(r1); System.out.println(r2); System.out.println(r3); r2.setLength(15); System.out.println(“Updated: ” + r2); } }

Output:

Rectangle[length=1.0, width=1.0, area=1.0, perimeter=4.0] Rectangle[length=10.0, width=5.0, area=50.0, perimeter=30.0] Length must be positive! Set to 1.0 Rectangle[length=1.0, width=8.0, area=8.0, perimeter=18.0] Updated: Rectangle[length=15.0, width=5.0, area=75.0, perimeter=40.0]
Exercise 2: Find and correct all errors in the following code:
class Product String productName; double price; void Product(String name, double p) { productName = name; price = p; } static void display() { System.out.println(productName + “: ” + price); } String toString() { return productName + ” – ” + price; } } public class Test { public static void main(String[] args) { Product p = new Product(); p.display(); System.out.println(p); } }

Errors found:

Error 1: Missing opening brace { after class Product.

Error 2: void Product(String name, double p) has a return type void. This makes it a regular method, NOT a constructor. Since there is no actual constructor, Java provides a default one (no parameters). But in main, we call new Product() which uses the default constructor, so productName and price stay at their default values (null and 0.0).

Error 3: The static method display() tries to access instance variables productName and price. Static methods cannot access instance variables directly.

Error 4: toString() should have @Override annotation and should be public (the original in Object class is public). Without public, it may not work correctly when printing with println().

Corrected code:

class Product { String productName; double price; // Removed void – now a proper constructor Product(String name, double p) { productName = name; price = p; } // Made instance method (removed static) void display() { System.out.println(productName + “: ” + price); } // Added @Override and public @Override public String toString() { return productName + ” – ” + price; } } public class Test { public static void main(String[] args) { Product p = new Product(“Laptop”, 25000); // must pass arguments now p.display(); System.out.println(p); } }
Exercise 3: Create a class Employee with a static variable companyName, instance variables name, id, and salary. Include a static block to initialize the company name. Include a parameterized constructor. Include a static method to change the company name and show that the change affects all existing objects. Demonstrate in main.
class Employee { static String companyName; String name; int id; double salary; static { companyName = “Ethio Telecom”; System.out.println(“Company initialized: ” + companyName); } Employee(String name, int id, double salary) { this.name = name; this.id = id; this.salary = salary; } void display() { System.out.println(“Company: ” + companyName + “, Name: ” + name + “, ID: ” + id + “, Salary: ” + salary); } static void changeCompany(String newName) { companyName = newName; } } public class Main { public static void main(String[] args) { Employee e1 = new Employee(“Abebe”, 101, 15000); Employee e2 = new Employee(“Tigist”, 102, 18000); e1.display(); e2.display(); System.out.println(“—“); Employee.changeCompany(“Ethiopian Airlines”); System.out.println(“After company change:”); e1.display(); e2.display(); } }

Output:

Company initialized: Ethio Telecom Company: Ethio Telecom, Name: Abebe, ID: 101, Salary: 15000.0 Company: Ethio Telecom, Name: Tigist, ID: 102, Salary: 18000.0 — After company change: Company: Ethiopian Airlines, Name: Abebe, ID: 101, Salary: 15000.0 Company: Ethiopian Airlines, Name: Tigist, ID: 102, Salary: 18000.0

Notice how changing the static companyName through the static method affected both existing objects. Both employees now show “Ethiopian Airlines” instead of “Ethio Telecom.” This proves that the static variable is shared.

More Practice Questions for Exam

MCQ

Question 28: What is the difference between instance variables and static variables?

A) Instance variables are shared, static are not    B) Static variables are shared by all objects, instance variables are separate for each object    C) There is no difference    D) Static variables cannot be accessed from methods

Answer: B) Static variables are shared by all objects, instance variables are separate for each object

This is the fundamental difference. If a class has 3 objects, each object has its own copy of instance variables (separate values). But there is only one copy of each static variable, and all 3 objects see and share that same value. Option A has it backwards. Option C is wrong because there is a clear difference. Option D is wrong because static variables CAN be accessed from methods (instance methods can access both static and instance variables).

Find the Output

Question 29: What will be the output?

class Test { int a; static int b; Test(int a) { this.a = a; b++; } public static void main(String[] args) { Test t1 = new Test(5); Test t2 = new Test(10); Test t3 = new Test(15); System.out.println(t1.a + ” ” + t1.b); System.out.println(t2.a + ” ” + t2.b); System.out.println(t3.a + ” ” + t3.b); } }

Output:

5 3 10 3 15 3

Explanation: Each object gets its own a value (5, 10, 15) because a is an instance variable. But b is static, so all three objects share the same b. After creating 3 objects, each constructor increments b by 1, so b becomes 3. Whether you access it through t1, t2, or t3, you get the same value: 3.

Find the Output

Question 30: What will be the output?

class Box { int size; Box(int size) { this.size = size; } public String toString() { return “Size=” + size; } } public class Test { public static void main(String[] args) { Box b1 = new Box(10); Box b2 = new Box(20); System.out.println(b1); System.out.println(b2); b2 = b1; System.out.println(b2); b1.size = 50; System.out.println(b1); System.out.println(b2); } }

Output:

Size=10 Size=20 Size=10 Size=50 Size=50

Explanation: First, b1 points to a Box of size 10 and b2 points to a Box of size 20. Then b2 = b1 makes b2 point to the SAME object as b1. Now both b1 and b2 reference the same Box(size=10) object. The Box(size=20) object has no references and becomes eligible for garbage collection. When we change b1.size = 50, since b2 points to the same object, b2.size is also 50. Both print “Size=50” because they point to the same object.

Short Answer

Question 31: Explain the difference between a constructor and a method. List at least four differences.

Answer:

1. Name: A constructor must have the same name as the class. A method can have any valid name following naming conventions.

2. Return type: A constructor has NO return type (not even void). A method must have a return type (void or a specific type).

3. Invocation: A constructor is called automatically when you use new. A method must be called explicitly by the programmer.

4. Purpose: A constructor initializes the object’s state (sets up initial values). A method performs an action or computation on the object.

5. Timing: A constructor runs only once per object, at creation time. A method can be called many times on the same object after creation.

MCQ

Question 32: How many times does a static block run if you create 5 objects of a class?

A) 5 times    B) 0 times    C) 1 time    D) Depends on the number of static variables

Answer: C) 1 time

A static block runs only once, when the class is first loaded into memory. This happens before any object is created. Even if you create 5, 10, or 1000 objects, the static block executes only that one time. The constructors run for each object (5 times), but the static block runs only once. This is because the static block belongs to the class, not to individual objects.

Final Exam Tips for Classes and Objects:
1. A constructor has the same name as the class and NO return type. If you see void ClassName(), it is a method, not a constructor.
2. If you write any constructor, Java will NOT provide the default constructor. new ClassName() will fail if you only wrote a parameterized constructor.
3. this() calls another constructor in the same class and MUST be the first statement.
4. this.variable resolves naming conflicts between instance variables and parameters.
5. Static methods cannot access instance members directly. This WILL be on your exam.
6. Static variables are shared (one copy). Instance variables are separate (each object has its own).
7. Static block runs ONCE when the class is loaded, before any constructor.
8. When passing objects to methods, references are passed, so changes inside the method affect the original object.
9. Overriding toString() is important for meaningful object printing.
10. An object becomes eligible for garbage collection when no reference points to it.
11. System.gc() only suggests garbage collection; it does NOT force it.
12. Practice writing complete classes with constructors, getters, setters, methods, and toString(). This is the most common practical exam question.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top