Project Based Learning in Java (24CSH-301 / 24ITH-301) - Lab 2
Experiment 1.2: Product Management, Library System, and Student Information System
Create Java programs to manage product details, library systems, and student
information using classes, inheritance, and abstraction.
AimImplement Java programs to demonstrate object-oriented concepts through product management, library systems, and a student-teacher information system using constructors, inheritance, and abstraction.
ObjectivesTo learn about classes, and about the encapsulation and aggregation concepts in Java.
Mapped COCO1, CO2
Lab Brief
What You Need To Build
Easy Level
Product Class
Write a Java program to create a Product class with attributes
id, name, and price. The program should
demonstrate the use of constructors and methods to display product details.
Keep all three attributes private.
Initialise them through a constructor using this.
Display the state with a dedicated method.
Read the details from the user with Scanner.
Medium Level
Library Management System
Write a Java program to implement a library management system. The program
should use a base class Book and derived classes
Fiction and NonFiction.
Share title, author, price via protected fields.
Chain constructors with super(...).
Override displayDetails() in both subclasses.
Hold either subclass in a Book reference.
Hard Level
Student Information System
Design a student information system using an abstract class
Person with attributes name, age, and a
method displayDetails(). Derive Student and
Teacher classes that override it.
Declare displayDetails() as abstract in Person.
Student adds rollNumber.
Teacher adds subject.
Never create a Person object directly.
Input/Apparatus used:
Hardware - minimum 384 MB RAM, 100 GB hard disk. Software - JDK with any
Java IDE such as Eclipse, NetBeans, or IntelliJ IDEA, or a plain terminal
with javac and java.
From the Lab Manual
Reading Material
Java - What is OOP?
OOP stands for Object-Oriented Programming.
Procedural programming is about writing procedures or methods that perform
operations on the data, while object-oriented programming is about creating
objects that contain both data and methods.
Object-oriented programming has several advantages over procedural programming:
OOP is faster and easier to execute.
OOP provides a clear structure for the programs.
OOP helps to keep the Java code DRY - "Don't Repeat Yourself" - and makes the code easier to maintain, modify, and debug.
OOP makes it possible to create full reusable applications with less code and shorter development time.
Java - What are Classes and Objects?
Classes and objects are the two main aspects of object-oriented programming.
A class is a template for objects, and an object is an instance of a class.
When the individual objects are created, they inherit all the variables and
methods from the class.
Java Inheritance (Subclass and Superclass)
In Java, it is possible to inherit attributes and methods from one class to
another. We group the "inheritance concept" into two categories:
Subclass (child) - the class that inherits from another class.
Superclass (parent) - the class being inherited from.
To inherit from a class, use the extends keyword. In the example
below, the Car class (subclass) inherits the attributes and methods
from the Vehicle class (superclass):
class Vehicle {
protected String brand = "Ford"; // Vehicle attribute
public void honk() { // Vehicle method
System.out.println("Tuut, tuut!");
}
}
class Car extends Vehicle {
private String modelName = "Mustang"; // Car attribute
public static void main(String[] args) {
// Create a myCar object
Car myCar = new Car();
// Call the honk() method (from the Vehicle class) on the myCar object
myCar.honk();
// Display the brand attribute (from the Vehicle class)
// and the modelName attribute (from the Car class)
System.out.println(myCar.brand + " " + myCar.modelName);
}
}
Why and when to use inheritance?
It is useful for code reusability: reuse attributes and methods of an existing
class when you create a new class.
Before Coding
Prerequisite Concepts
Experiment 1.1 used one class as a container for main. This
experiment is where object-oriented programming actually starts: every task is
modelled as classes first, and main only creates objects and calls
their methods. Revise these ideas before writing any code.
Classes and Objects
A class is a template that bundles data (fields) and behaviour (methods).
An object is an instance of that template created with new,
which allocates memory and runs the constructor. One class can produce any
number of independent objects.
class Product { /* fields + methods */ }
Product p1 = new Product(101, "Mouse", 799.5);
Product p2 = new Product(102, "Keyboard", 1499.0);
Constructors and this
A constructor has the same name as the class, no return type, and runs
exactly once per object at creation. When a parameter shadows a field,
this.field refers to the object's own copy.
Keep fields private and expose behaviour through
public methods. Outside code cannot corrupt the state directly;
it must go through methods the class controls. All three tasks follow this
pattern.
private double price; // hidden data
public void displayProductDetails() // controlled access
{ ... }
Inheritance: extends and super
A subclass inherits the fields and methods of its superclass with
extends. Its constructor must first initialise the inherited
part by calling super(...) - and that call has to be the first
statement.
class Fiction extends Book {
public Fiction(String title, String author, double price) {
super(title, author, price); // run Book's constructor first
}
}
The protected Modifier
private would hide title even from
Fiction; public would expose it to everyone.
protected sits in between: the field is visible inside the class,
its subclasses, and the same package - exactly what a base class like
Book needs.
class Book {
protected String title; // Fiction and NonFiction can use it
protected String author;
protected double price;
}
Method Overriding and Runtime Polymorphism
A subclass replaces an inherited method by declaring it again with the same
signature - mark it with @Override so the compiler checks the
match. Which version runs is decided at runtime by the object's actual type,
not the reference type.
Book b = new Fiction("The Guide", "R. K. Narayan", 350);
b.displayDetails(); // Fiction's version runs, not Book's
Abstract Classes and Methods
An abstract class cannot be instantiated - it exists only to be
extended. An abstract method has no body; it is a contract that
every concrete subclass must fulfil by overriding it.
abstract class Person {
public abstract void displayDetails(); // no body here
}
// new Person(...) -> compile error
Aggregation (HAS-A)
Inheritance models IS-A (a Fiction IS-A Book).
Aggregation models HAS-A: one class holds references to objects of another
class that can exist independently. A library HAS books; the books are not
a kind of library.
class Library {
private Book[] shelf; // a Library HAS-A collection of Books
}
Easy Level
Product Class: Constructor and Display Method
Problem statement: Write a Java program to create a
Product class with attributes id, name,
and price. The program should demonstrate the use of constructors
and methods to display product details.
Class Design
Member
Purpose
id, name, price
Private fields - product state hidden from outside code (encapsulation)
Product(id, name, price)
Constructor - copies the parameters into the fields using this
displayProductDetails()
Prints the ID, name, and price of this object
ProductDemo.main
Reads input, creates one Product, calls the display method
Figure 1. The Product class: private fields, one constructor, one display method.
Algorithm
Start the program and create a Scanner object.
Read the product ID with nextInt(), then consume the leftover newline with nextLine().
Read the product name with nextLine() so names with spaces work.
Read the product price with nextDouble().
Create a Product object - the constructor copies the three values into the private fields.
Call displayProductDetails() to print the ID, name, and price.
Close the scanner and end the program.
Java Program
import java.util.Scanner;
class Product {
// Attributes
private int id;
private String name;
private double price;
// Constructor to initialise Product
public Product(int id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
// Method to display product details
public void displayProductDetails() {
System.out.println("\nProduct Details:");
System.out.println("ID: " + id);
System.out.println("Name: " + name);
System.out.println("Price: Rs." + price);
}
}
public class ProductDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Taking product details from the user
System.out.print("Enter Product ID: ");
int id = scanner.nextInt();
scanner.nextLine(); // Consume newline
System.out.print("Enter Product Name: ");
String name = scanner.nextLine();
System.out.print("Enter Product Price: Rs.");
double price = scanner.nextDouble();
// Creating Product object
Product product = new Product(id, name, price);
// Displaying product details
product.displayProductDetails();
scanner.close();
}
}
Sample Output
Enter Product ID: 101
Enter Product Name: Wireless Mouse
Enter Product Price: Rs.799.5
Product Details:
ID: 101
Name: Wireless Mouse
Price: Rs.799.5
Lab note:
The scanner.nextLine() right after nextInt() is not
optional. nextInt() leaves the newline in the buffer, so without
that extra call the product name would silently come out empty. This is the
single most common bug in this experiment.
Problem statement: Write a Java program to implement a library
management system. The program should use a base class Book and
derived classes Fiction and NonFiction.
Who Does What
Class
Responsibility
Book (base)
Holds protectedtitle, author, price; prints the common fields in displayDetails()
Fiction
Calls super(...), overrides displayDetails() to add its heading, then reuses the base version
NonFiction
Same as Fiction but with the Non-Fiction heading
LibraryManagementSystem
Reads input and decides which subclass to instantiate; stores both in Book references
Keywords Doing the Work
Keyword
Role here
extends
Makes Fiction and NonFiction inherit everything in Book
protected
Lets subclasses see the fields while still hiding them from unrelated code
super
super(...) chains constructors; super.displayDetails() reuses the base printing
@Override
Asks the compiler to verify the method really replaces an inherited one
Figure 2. Fiction and NonFiction inherit from Book and override displayDetails().
Algorithm
Read the type, title, author, and price for the first book.
Create a Fiction or NonFiction object based on the type and store it in a Book reference.
Consume the leftover newline, then repeat both steps for the second book.
Call displayDetails() on both references - the overridden subclass version runs (runtime polymorphism).
Inside each override, print the heading, then call super.displayDetails() for the common fields.
Java Program
import java.util.Scanner;
// Base class: Book
class Book {
protected String title;
protected String author;
protected double price;
// Constructor
public Book(String title, String author, double price) {
this.title = title;
this.author = author;
this.price = price;
}
// Method to display book details (overridden in derived classes)
public void displayDetails() {
System.out.println("Title: " + title);
System.out.println("Author: " + author);
System.out.println("Price: Rs." + price);
}
}
// Derived class: Fiction
class Fiction extends Book {
public Fiction(String title, String author, double price) {
super(title, author, price);
}
// @Override is an annotation, not code: it asks the compiler to verify
// that this method really overrides Book's displayDetails()
@Override
public void displayDetails() {
System.out.println("\nFiction Book Details:");
super.displayDetails();
}
}
// Derived class: NonFiction
class NonFiction extends Book {
public NonFiction(String title, String author, double price) {
super(title, author, price);
}
// @Override annotation again: compiler-checked override
@Override
public void displayDetails() {
System.out.println("\nNon-Fiction Book Details:");
super.displayDetails();
}
}
public class LibraryManagementSystem {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Taking input for the first book
System.out.println("Book 1:");
System.out.print("Type (Fiction/Non-Fiction): ");
String type1 = scanner.nextLine();
System.out.print("Title: ");
String title1 = scanner.nextLine();
System.out.print("Author: ");
String author1 = scanner.nextLine();
System.out.print("Price: Rs.");
double price1 = scanner.nextDouble();
scanner.nextLine(); // Consume newline
// Creating object based on type
Book book1 = (type1.equalsIgnoreCase("Fiction"))
? new Fiction(title1, author1, price1)
: new NonFiction(title1, author1, price1);
// Taking input for the second book
System.out.println("\nBook 2:");
System.out.print("Type (Fiction/Non-Fiction): ");
String type2 = scanner.nextLine();
System.out.print("Title: ");
String title2 = scanner.nextLine();
System.out.print("Author: ");
String author2 = scanner.nextLine();
System.out.print("Price: Rs.");
double price2 = scanner.nextDouble();
// Creating object based on type
Book book2 = (type2.equalsIgnoreCase("Fiction"))
? new Fiction(title2, author2, price2)
: new NonFiction(title2, author2, price2);
// Displaying book details
book1.displayDetails();
book2.displayDetails();
scanner.close();
}
}
Sample Output
Book 1:
Type (Fiction/Non-Fiction): Fiction
Title: The Guide
Author: R. K. Narayan
Price: Rs.350
Book 2:
Type (Fiction/Non-Fiction): Non-Fiction
Title: Wings of Fire
Author: A. P. J. Abdul Kalam
Price: Rs.450
Fiction Book Details:
Title: The Guide
Author: R. K. Narayan
Price: Rs.350.0
Non-Fiction Book Details:
Title: Wings of Fire
Author: A. P. J. Abdul Kalam
Price: Rs.450.0
Polymorphism in action:book1 is declared as Book but holds a
Fiction object, so book1.displayDetails() runs the
Fiction version - the decision happens at runtime (dynamic method
dispatch). And because the overrides call super.displayDetails(),
the common printing lives in exactly one place instead of being copy-pasted
into every subclass.
Hard Level
Student Information System: Abstract Person, Student, Teacher
Problem statement: Design a student information system using
Java with the following features: use an abstract class Person with
attributes name, age, and methods like
displayDetails(). Create derived classes Student and
Teacher to override displayDetails() and add unique
attributes like rollNumber for students and subject
for teachers.
Class Design
Class
Members
Person (abstract)
protected name, age; constructor; abstract displayDetails() with no body
Reads input, builds one Student and one Teacher, displays both
Abstract Class Rules
Rule
Consequence here
Cannot be instantiated
new Person(...) is a compile-time error
May have fields and constructors
Person(name, age) runs via super(name, age) from each subclass
Abstract method has no body
displayDetails() is only a contract in Person
Concrete subclasses must override
Student and Teacher each supply their own version
Figure 3. Person defines the contract; Student and Teacher implement it with their own attributes.
Algorithm
Read the student's name, age, and roll number; consume the leftover newline after the numeric reads.
Create a Student object - its constructor calls super(name, age), then stores rollNumber.
Read the teacher's name, age, and subject the same way.
Create a Teacher object - its constructor calls super(name, age), then stores subject.
Call displayDetails() on each object - the subclass implementation runs.
Note that Person itself is never instantiated; it only defines the shared state and the contract.
Java Program
import java.util.Scanner;
// Abstract class Person
abstract class Person {
protected String name;
protected int age;
// Constructor
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Abstract method to be overridden by derived classes
public abstract void displayDetails();
}
// Derived class Student
class Student extends Person {
private int rollNumber;
// Constructor
public Student(String name, int age, int rollNumber) {
super(name, age);
this.rollNumber = rollNumber;
}
// Overriding displayDetails method
// (@Override is an annotation - a compile-time check, not runtime code)
@Override
public void displayDetails() {
System.out.println("\nStudent Details:");
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Roll Number: " + rollNumber);
}
}
// Derived class Teacher
class Teacher extends Person {
private String subject;
// Constructor
public Teacher(String name, int age, String subject) {
super(name, age);
this.subject = subject;
}
// Overriding displayDetails method
@Override
public void displayDetails() {
System.out.println("\nTeacher Details:");
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Subject: " + subject);
}
}
public class StudentInformationSystem {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Input for Student
System.out.println("Add Student:");
System.out.print("Name: ");
String studentName = scanner.nextLine();
System.out.print("Age: ");
int studentAge = scanner.nextInt();
System.out.print("Roll Number: ");
int rollNumber = scanner.nextInt();
scanner.nextLine(); // Consume newline
Student student = new Student(studentName, studentAge, rollNumber);
// Input for Teacher
System.out.println("\nAdd Teacher:");
System.out.print("Name: ");
String teacherName = scanner.nextLine();
System.out.print("Age: ");
int teacherAge = scanner.nextInt();
scanner.nextLine(); // Consume newline
System.out.print("Subject: ");
String subject = scanner.nextLine();
Teacher teacher = new Teacher(teacherName, teacherAge, subject);
// Display details
student.displayDetails();
teacher.displayDetails();
scanner.close();
}
}
Sample Output
Add Student:
Name: Aman Kumar
Age: 19
Roll Number: 1101
Add Teacher:
Name: Neha Sharma
Age: 38
Subject: Java Programming
Student Details:
Name: Aman Kumar
Age: 19
Roll Number: 1101
Teacher Details:
Name: Neha Sharma
Age: 38
Subject: Java Programming
Why abstract, not a normal base class?
A plain Person could be instantiated, but a "person" with no role
makes no sense in this system - only students and teachers do. Declaring the
class abstract makes that rule a compile-time guarantee, and the
abstract displayDetails() forces every future subclass (say,
Staff) to provide its own display logic. Its constructor still
runs - every super(name, age) call executes it.
Review
Quiz and Viva Questions
1. What is a class and what is an object?
A class is a template that defines fields and methods; an object is an instance of that class created with new. One class can produce many objects, each with its own copy of the fields.
2. What is a constructor, and how is it different from a method?
A constructor initialises a new object. It has the same name as the class, has no return type (not even void), and runs automatically exactly once when new creates the object - it cannot be called again like a normal method.
3. What do you mean by encapsulation?
Encapsulation is bundling data and the methods that operate on it inside one class, and hiding the data with private so it can only be reached through controlled public methods - like Product's fields and displayProductDetails().
4. What is inheritance in Java?
Inheritance lets a subclass acquire the fields and methods of a superclass using extends, so common code is written once in the base class - Fiction and NonFiction reuse everything in Book.
5. Is multiple inheritance possible in Java?
Not with classes - a class can extend only one class, which avoids the diamond ambiguity problem. Java achieves a similar effect through interfaces: a class can implement any number of them.
6. What is the scope of the protected access modifier?
A protected member is accessible within its own class, all classes in the same package, and subclasses even in other packages. That is why Fiction can read title while unrelated code cannot.
7. What does the super keyword do?
super(...) calls the superclass constructor and must be the first statement of the subclass constructor; super.method() calls the superclass version of an overridden method, as in super.displayDetails().
8. What is method overriding, and how is it different from overloading?
Overriding is a subclass redefining an inherited method with the same signature; the choice is made at runtime. Overloading is the same class having multiple methods with the same name but different parameter lists; the choice is made at compile time.
9. What is an abstract class? Can it be instantiated?
An abstract class is declared with the abstract keyword and may contain abstract methods (without a body). It cannot be instantiated with new - it exists only to be extended by concrete subclasses.
10. Can an abstract class have a constructor?
Yes. It cannot be called with new, but it runs whenever a subclass constructor calls super(...) - exactly how Person(name, age) initialises the shared fields for Student and Teacher.
11. What is runtime polymorphism (dynamic method dispatch)?
When an overridden method is called through a base-class reference, the JVM picks the version belonging to the object's actual type at runtime. Book book1 = new Fiction(...) followed by book1.displayDetails() runs the Fiction version.
12. What is aggregation, and how does it differ from inheritance?
Aggregation is a HAS-A relationship: one class holds references to objects of another class that can exist independently (a library has books). Inheritance is an IS-A relationship (a fiction book is a book). Use inheritance for specialisation, aggregation for composition of parts.
13. Why is @Override written above the overriding methods?
It asks the compiler to verify that the method really overrides an inherited one. Without it, a small typo like displaydetails() would silently create a new method instead of overriding, and the base version would run.