0% found this document useful (0 votes)
12 views776 pages

E-Book+-+1.9

The document is a comprehensive guide to learning Java, aimed at students and beginners in the IT field. It covers foundational concepts in programming, Java basics, object-oriented programming, and practical exercises to enhance learning. The author, Imran Afzal, is an experienced IT educator and provides additional resources for further study on his website.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views776 pages

E-Book+-+1.9

The document is a comprehensive guide to learning Java, aimed at students and beginners in the IT field. It covers foundational concepts in programming, Java basics, object-oriented programming, and practical exercises to enhance learning. The author, Imran Afzal, is an experienced IT educator and provides additional resources for further study on his website.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 776

THE COMPLETE JAVA

TRAINING
Java Made Simple: A Complete Learning Path
for Students and Beginners

Imran Afzal
www.utclisolutions.com
Preface

Welcome to the world of Information Technology (IT)! This is an area with many possibilities, and
it's important to know the basics. This book is a fantastic starting point for learning about IT. Whether
you're new to this field or looking to expand your knowledge, this book is perfect for you.

Having a good foundation in IT is key if you want to succeed in roles like managing Linux or Windows
systems, working with VMware networking, handling storage management, or developing security
solutions. You should understand the basics of how computers work, especially the CPU, which is
like the brain of the computer. This includes knowing about the different parts of the CPU, like cores
and sockets, and the difference between 32-bit and 64-bit systems. It's also important to understand
networking basics, particularly the OSI network layer and how it compares to TCP/IP. By mastering
these fundamental concepts, you'll be well-prepared to excel in any IT-related career. This book is
designed to help you build that strong foundation and set you on the path to success in the ever-
evolving IT industry.

First Edition
I recommend a thorough review of the content in each chapter before moving to the next. For
supplementary educational resources, including video courses, downloadable books, audio books,
certification materials, and much more, visit my website at www.utclisolutions.com. I strongly
encourage you to explore these additional materials to enhance your learning experience.

"Education is the passport to the future, for tomorrow belongs to those who prepare for it today." -
Malcolm
Acknowledgements

I am grateful to God who enabled me to write this book successfully.

A big thank you to my family, students, friends, and colleagues for their helpful feedback. Their input
greatly enhanced the quality of this book.

I also want to thank my wife and kids for being patient when I was focused on writing this book.
Their support meant a lot and helped me complete this project.

The Complete Java Training Imran Afzal


About the Author

Imran Afzal is a well-known expert in IT and education, recognized for


being a best-selling instructor and author, as well as the CEO of UTCLI
Solutions. He's taught over a million students worldwide, proving his skills
in creating educational content, mentoring, and entrepreneurship. He holds
an MBA degree and has vast experience of more than 20 years in IT,
including roles in systems administration, engineering, leadership, and
training.

His IT career started in 2001 at Time Warner in New York City as a systems administrator. Over the
years, Imran has worked with many big companies in finance, fashion, and tech media. He's led
important IT projects, like moving data centers, setting up VMWare, implementing monitoring tools,
and transitioning to Amazon's cloud services. Imran graduated with honors from Baruch College, City
University of New York, in Computer Information Systems and earned an MBA from the New York
Institute of Technology (NYIT). He holds several professional certifications in areas like Linux
Systems Management, UNIX Operating Systems, VMWare, and Windows Server. Since 2010, Imran
has focused on teaching, offering both in-person and online courses. His online courses are very
popular and highly rated, with over a million students enrolled globally. He has helped thousands of
students land their dream jobs.

Additionally, Imran is dedicated to education and community service, founding a non-profit school
for children from pre-K to 10th grade.

The Complete Java Training Imran Afzal


About the Book

Chapter 1: Introduction

This chapter introduces computer languages and their evolution. It covers types of programming
languages, the concept of object-oriented programming, and real-world programming applications. It
also discusses the roles of programmers and developers.

Chapter 2: Welcome to JAVA

Here, learners are introduced to Java and its importance in modern software development. The
chapter explains how to succeed as a Java developer and provides an overview of essential tools like
JDK, JRE, JVM, and IDEs.

Chapter 3: Lab Setup

This chapter guides learners through setting up the development environment for Java. It includes
step-by-step instructions for installing JDK on Windows and Linux and introduces JShell for
interactive programming.

Chapter 4: Java Basics

Students learn how to write and run their first Java programs. This chapter covers Java syntax,
variables, data types, type casting, operators, and other foundational concepts essential for beginners.

Chapter 5: Introduction to IDE

This chapter introduces the IntelliJ IDEA IDE, from installation to writing and executing Java
programs. It also covers conditional statements and how to use an IDE efficiently for Java
development.

Chapter 6: Control Flow and Methods

Learners explore control flow statements like if-else, nested conditions, and method creation. This
chapter covers method overloading, return types, and provides extensive practice through exercises.

The Complete Java Training Imran Afzal


Chapter 7: Loops and Control Statements

This chapter explains switch statements, traditional vs enhanced switch, and loops like for, while, and
do-while. It includes nested loops, debugging, local variables, and user input through Scanner.

Chapter 8: Object Oriented Programming – Inheritance

Students are introduced to classes, objects, constructors, getters and setters, and reference types. The
chapter explores inheritance, exception handling, the java.lang.Object class, and string manipulation.

Chapter 9: Pillars of OOP (Composition, Encapsulation & Polymorphism)

This chapter focuses on key OOP principles. It covers composition, encapsulation, and polymorphism
with practical exercises, leading to a strong understanding of Java classes and relationships.

Chapter 10: Java Arrays

Arrays and their uses in Java are explained here. The chapter covers single and multi-dimensional
arrays, Java's Arrays utility class, runtime arguments, reference types, and summary exercises for
revision.

Chapter 11: ArrayList & LinkedList

Learners dive into Java's collection framework, starting with ArrayList and LinkedList. It includes
comparisons, exercises, and introductions to Queue, Stack, Iterators, and Enumeration.

Chapter 12: Abstract Classes & Interfaces

This chapter explains abstraction and how to implement abstract classes and interfaces in Java. It
includes hands-on exercises and a summary comparing abstract classes with interfaces.

Chapter 13: Introduction to Generics

Students are introduced to Java Generics and their benefits. The chapter includes practical examples,
exercises, and an introduction to the Comparable interface for sorting objects.

The Complete Java Training Imran Afzal


Table of Contents

Introduction ............................................................................................................................... 1
1.1- What is Computer Language .............................................................................................. 2
1.2- Types of Programming Languages .................................................................................... 4
1.3- Object-Oriented Programming (OOP) .............................................................................. 6
1.4- Programming Applications Examples ............................................................................... 9
1.5- Programmer vs Developer ................................................................................................ 13
Welcome to JAVA .................................................................................................................... 17
2.1- Introduction to Java .......................................................................................................... 18
2.2- Importance of Java ............................................................................................................ 20
2.3- How to Succeed as a Java Developer ............................................................................... 23
2.4- Introduction to Software Tools JDK, JRE, JVM & IDE .................................................. 26
Lab Setup ................................................................................................................................. 30
3.1- Lab Setup .......................................................................................................................... 31
3.2- Installation of JDK on Windows ....................................................................................... 33
3.3- Installation of JDK on Linux ............................................................................................ 34
3.4- Introduction to JShell ....................................................................................................... 35
Java Basics ............................................................................................................................... 38
4.1- Java First Program using Notepad and Command Prompt............................................. 39
4.2- Java First Program - Hello World using JShell................................................................. 40
4.3- Variables in Java ............................................................................................................... 43
4.4- Variables in Expressions .................................................................................................. 47
4.5- Data Types in Java ............................................................................................................ 49
4.6- Primitive Data Types Examples....................................................................................... 54
4.7- Type Casting in Java ......................................................................................................... 56
4.8- Float vs Double ................................................................................................................. 59
4.9- Scientific Notations in Float and Double ......................................................................... 63
4.10- Char vs String .................................................................................................................. 66
4.11- Summarizing Primitive Data Types ................................................................................ 70
4.12- What are Operators? ........................................................................................................ 74
4.13- Arithmetic Operators ...................................................................................................... 80

The Complete Java Training Imran Afzal


Integrated Development Environment ................................................................................... 86
5.1- Introduction to IDE .......................................................................................................... 87
5.2- Installing IntelliJ IDEA for Windows ............................................................................... 89
5.3- Installing IntelliJ IDEA for CentOS Linux ...................................................................... 90
5.4- IntelliJ IDEA Functionalities ........................................................................................... 91
5.5- Hello World in IntelliJ IDEA............................................................................................ 92
5.6- Conditional Statements (if-then) in Java .......................................................................... 95
5.7- Logical Operators ............................................................................................................. 99
5.8- OR and NOT Logical Operators ................................................................................... 102
5.9- Assignment Operators in Java ........................................................................................ 107
5.10- Comparison Operators in Java .......................................................................................110
5.11- Ternary Operator in Java ................................................................................................113
5.12- Operators Exercises in Java ...........................................................................................116
Expressions and Statements ................................................................................................. 122
6.1- Keywords and Expressions in Java ................................................................................. 123
6.2- Whitespace and Indentation in Java............................................................................... 126
6.3- If-Then-Else Control Statement in Java ......................................................................... 129
6.4- Ladder If-Then-Else Exercises in Java .......................................................................... 132
6.5- Nested If-Then-Else Concept in Java ............................................................................ 138
6.6- Methods in Java .............................................................................................................. 140
6.7- Return Statement in a Method ....................................................................................... 143
6.8- Summarizing Methods in Java ....................................................................................... 147
6.9- Method Exercises in Java – Part 1 ...................................................................................151
6.10- Method Exercises in Java – Part 2 ................................................................................ 155
6.11- Method Exercises Part 3 ............................................................................................... 158
6.12- Method Exercises Part 4 ................................................................................................161
6.13- Method Overloading ..................................................................................................... 163
6.14- Method Overloading Exercises Part 1 .......................................................................... 165
6.15- Method Overloading Exercise Part 2 ............................................................................ 168
6.16- Method Overloading Exercises Part 3 .......................................................................... 170
6.17- Method Overloading Exercises Part 4 .......................................................................... 172
6.18- Method Overloading Exercises Part 5 .......................................................................... 174
6.19- Method Overloading Exercises Part 6 .......................................................................... 176

The Complete Java Training Imran Afzal


6.20- Method Overloading Exercises Part 7 .......................................................................... 178
Loops and Control Statements .............................................................................................. 183
7.1- Switch Statement ............................................................................................................. 184
7.2- Switch Statement Exercises Part 1.................................................................................. 188
7.3- Switch Statement Exercises Part 2 ..................................................................................191
7.4- Traditional vs Enhanced Switch Statement ................................................................... 194
7.5- Enhanced Switch Statement Exercises Part 1 ................................................................ 197
7.6- Enhanced Switch Statement Exercises Part 2 ................................................................ 198
7.7- Nested Switch Statement ................................................................................................ 202
7.8- Traditional Nested Switch Statement Exercises ............................................................ 208
7.9- Introduction to Loops in Java ......................................................................................... 216
7.10- For Loop ........................................................................................................................ 222
7.11- For Loop Exercise ......................................................................................................... 232
7.12- Introduction to Debugger ............................................................................................. 235
7.13- Debugger in IntelliJ IDEA ............................................................................................ 238
7.14- For Loop Quick Summary ............................................................................................ 242
7.15- The While Statements ................................................................................................... 245
7.16- The While Statements Exercise Part 1 .......................................................................... 251
7.17- The While Statements Exercise .................................................................................... 258
7.18- Do-While Statement ...................................................................................................... 260
7.19- While & Do-While Statements Quick Summary .......................................................... 263
7.20- Introduction to Nested Loops in Java .......................................................................... 269
7.21- Nested Loop Exercise Part 1......................................................................................... 272
7.22- Nested Loop Exercise Part 2 ........................................................................................ 278
7.23- Local Variables.............................................................................................................. 284
7.24- Class and Object ........................................................................................................... 289
7.25- Parsing Values and User Input ..................................................................................... 295
7.26- Introduction to Scanner ................................................................................................ 298
7.27- Input with Scanner Exercise Part 1 .............................................................................. 303
7.28- Input with Scanner Exercise Part 2 .............................................................................. 306
7.29- Input with Scanner Exercise Part 3 .............................................................................. 310
Object Oriented Programming – Inheritance ....................................................................... 315
8.1- Classes and Objects ........................................................................................................ 316

The Complete Java Training Imran Afzal


8.2- Using Getter and Setters ................................................................................................. 321
8.3- Classes, Object, Getter and Setter Exercise Part 1 ......................................................... 325
8.4- Classes, Object, Getter and Setter Exercise Part 2 ......................................................... 329
8.5- Classes, Object, Getter and Setter Exercise Part 3 ......................................................... 334
8.6- Introduction to Constructors .......................................................................................... 340
8.7- Constructors Exercise Part 1 .......................................................................................... 346
8.8- Constructors Exercise Part 2 .......................................................................................... 351
8.9- Clear Understanding of Reference, Object and Instance .............................................. 357
8.10- Static versus Instance Variables and Methods ............................................................. 359
8.11- Exception Handling ...................................................................................................... 365
8.12- Exception Handling Exercise....................................................................................... 370
8.13- Introduction to Inheritance........................................................................................... 373
8.14- Inheritance Exercise, Part 3 .......................................................................................... 379
8.15- The Plain Old Java Object ............................................................................................ 383
8.16- The Plain Old Java Object Exercise Part 1 ................................................................... 389
8.17- The Plain Old Java Object Exercise Part 2 ................................................................... 394
8.18- The Plain Old Java Object Exercise Part 3 ................................................................... 399
8.19- The Plain Old Java Object Exercise Part 4 ................................................................... 403
8.20- Introduction to java.lang.Object................................................................................... 408
8.21- Summary of This and Super Keyword ...........................................................................411
8.22- Summary of Method Overloading and Overriding ...................................................... 418
8.23- Introduction to Text Block ........................................................................................... 423
8.24- String Methods Part 1 ................................................................................................... 426
8.25- String Methods Part 2 ................................................................................................... 433
8.26- String and String Builder Class..................................................................................... 440
8.27- Inheritance Section Summary....................................................................................... 444
Pillars of OOP (Composition, Encapsulation & Polymorphism) ........................................ 450
9.1- Introduction to Composition .......................................................................................... 451
9.2- Composition Exercise Part 1 .......................................................................................... 457
9.3- Composition Exercise Part 2 .......................................................................................... 467
9.4- Composition Exercise Part 3 .......................................................................................... 474
9.5- Introduction to Encapsulation ....................................................................................... 480
9.6- Encapsulation Exercise .................................................................................................. 487

The Complete Java Training Imran Afzal


9.7- Introduction to Polymorphism ....................................................................................... 493
9.8- Polymorphism Exercise Part 1 ....................................................................................... 502
9.9- Polymorphism Exercise Part 2 ....................................................................................... 510
9.10- Polymorphism Exercise Part 3 ...................................................................................... 520
9.11- Polymorphism Exercise Part 4 ...................................................................................... 525
9.12- Polymorphism Exercise Part 5 ...................................................................................... 537
9.13- Introduction to Java Packages ...................................................................................... 542
Java Arrays ............................................................................................................................. 548
10.1- Introduction to Arrays ................................................................................................... 549
10.2- Arrays Exercise Part 1 ................................................................................................... 552
10.3- Arrays Exercise Part 2 ................................................................................................... 556
10.4- Arrays Exercise Part 3 ................................................................................................... 559
10.5- Arrays Exercise Part 4 ................................................................................................... 562
10.6- Introduction to Java Util Arrays Class .......................................................................... 566
10.7- Java Util Arrays Class Method Exercise Part 1 ............................................................. 570
10.8- Java Util Arrays Class Method Exercise Part 2 ............................................................. 573
10.9- Java Util Arrays Class Method Exercise Part 3 ............................................................. 576
10.10- Java dot Util dot Arrays Class Method Exercise Part 4 ............................................... 580
10.11- Arrays Revision ............................................................................................................ 585
10.12- References and Value Types ....................................................................................... 588
10.13- Runtime Arguments .................................................................................................... 591
10.14- Array Summary Exercise Part 1 ................................................................................... 594
10.15- Array Summary Exercise Part 2 ................................................................................... 597
10.16- Introduction to Two Dimensional Arrays ................................................................... 601
10.17- Two Dimensional Arrays Exercise .............................................................................. 607
10.18- Multi Dimensional Arrays ........................................................................................... 615
ArrayList & LinkedList ......................................................................................................... 622
11.1- Introduction to List........................................................................................................ 623
11.2- Array List Exercise Part 1 .............................................................................................. 626
11.3- Array List Exercise Part 2 .............................................................................................. 629
11.4- Array List Exercise Part 3 .............................................................................................. 632
11.5- Summary of Array and ArrayList ................................................................................... 637
11.6- Introduction to Linked List in Relation to an Array and an ArrayList ......................... 641

The Complete Java Training Imran Afzal


11.7- Linked List Exercise Part 1 ........................................................................................... 645
11.8- Linked List Exercise Part 2 ........................................................................................... 649
11.9- Introduction to Queue and Stack in Linked List .......................................................... 653
11.10- Queue and Stack Exercise ........................................................................................... 655
11.11- Introduction to Iterators .............................................................................................. 660
11.12- Linked List and Iterators Exercise .............................................................................. 663
11.13- Introduction to Autoboxing and Unboxing ................................................................ 669
11.14- Auto Boxing and Un boxing Exercise ......................................................................... 671
11.15- Introduction to the Enumeration ................................................................................ 674
11.16- Enumeration Exercise ................................................................................................. 678
Abstract Classes & Interfaces................................................................................................ 683
12.1- Introduction to Abstraction ........................................................................................... 684
12.2- Abstraction Exercise Part 1 ........................................................................................... 687
12.3- Abstraction Exercise Part 2 ........................................................................................... 691
12.4- Abstraction Exercise Part 3 ........................................................................................... 700
12.5- Introduction to Interface ................................................................................................711
12.6- Interface Exercise Part 1 ............................................................................................... 716
12.7- Interface Exercise Part 2 ............................................................................................... 723
12.8- Interface and Abstract Class Summary ......................................................................... 730
Introduction to Generics ....................................................................................................... 733
13.1- Introduction to Generics ............................................................................................... 734
13.2- Generics Exercise Part 1................................................................................................ 737
13.3- Generics Exercise Part 2 ............................................................................................... 742
13.4- Introduction to Comparable ......................................................................................... 753
13.5- Interface Comparable Exercise..................................................................................... 760

The Complete Java Training Imran Afzal


Chapter 1

Introduction

1
The Complete Java Training Imran Afzal
1.1- What is Computer Language

1.1.1- The Basics of Computer Language

Computers do not understand human languages such as English, French, or Chinese. Instead, they
only comprehend a series of zeros and ones, known as binary code. The computer stores all characters
as numerical representations on the hard disk, as it does not inherently understand words like "hello"
or "how are you." The process of converting readable data into binary code is called encoding.

1.1.2- The Origin of Binary Code

Computers operate using electricity. When the power supply is turned off, the computer shuts down,
demonstrating how binary concepts align with electrical signals. The binary system, consisting of zeros
and ones, originates from this fundamental electrical principle: zero represents "off," and one
represents "on." Electrical switches in older devices often display these symbols, indicating their
function. Similarly, computers process and store data by translating it into sequences of on and off
states.

Figure - 1.1.2

1.1.3- Translating Characters into Binary

To illustrate binary translation, each letter corresponds to a unique binary number in a binary code
chart.

2
The Complete Java Training Imran Afzal
Figure – 1.1.3

For example:

• A = 1000001

• B = 1000010

• H = 1001000

• E = 1000101

• L = 1001100

• O = 1001111

When typing "HELLO" on a keyboard, the operating system translates it into these binary digits. The
computer processes the information by recognizing each character's binary equivalent and
transmitting it as electrical signals in an on-off sequence.

1.1.5- The Need for High-Level Programming Languages

While it is possible to write programs directly in binary, doing so would be extremely tedious and time-
consuming. High-level programming languages like Java, Python, and Ruby provide a more accessible
way for humans to write, debug, and troubleshoot code efficiently. Regardless of the programming

3
The Complete Java Training Imran Afzal
language used, all instructions eventually translate into binary code executed by the computer
hardware.

1.1.6- Low-Level vs. High-Level Languages

Binary language is classified as a low-level language because it directly interacts with the computer
hardware. In contrast, programming languages like Java or Python are high-level languages because
they operate above binary code. A crucial intermediary component, known as a compiler, translates
high-level programming instructions into binary machine code. This translation allows developers to
write human-readable code while ensuring computers execute it efficiently.

1.1.7- The Role of the Compiler

To bridge the gap between high-level programming languages and a computer’s native binary
language, a compiler is used. A compiler is a software tool that translates high-level code into machine
language, enabling the computer to execute it efficiently.

Understanding how computers process language provides insight into the necessity of programming
languages and the essential role they play in software development.

1.2- Types of Programming Languages

1.2.1- Introduction to Programming Language Categories

Programming languages are broadly classified into two categories: low-level languages and high-
level languages.

Low-level languages interact directly with machine hardware and are executed by the CPU. In contrast,
high-level languages are designed to be user-friendly, resembling human language and allowing
programmers to write code more efficiently while reducing errors. These languages facilitate easier
expression of programming logic and intentions.

Various types of programming languages exist, each serving distinct purposes and applications. Below
are some of the main categories.

4
The Complete Java Training Imran Afzal
1.2.2- Procedural Programming Languages

Procedural programming follows a structured approach where a program is divided into a sequence
of procedures or functions. These functions are executed step by step to complete tasks or solve
problems in a logical manner. Procedural languages are widely used for system programming and
software development.
Examples: C, Pascal, Fortran

1.2.3- Object-Oriented Programming (OOP) Languages

Object-oriented programming organizes code around objects, which are representations of real-world
entities. Objects contain attributes (data) and methods (functions) that define their behavior.

This programming paradigm promotes code reusability, modularity, and scalability. Objects can repr
esent anything, such as a table, a chair, a phone, or a game character. OOP is particularly powerful f
or designing complex software systems.
Examples: Java, C++, Python, C#, Ruby, PHP

1.2.4- Functional Programming Languages

Functional programming treats computation as evaluations of mathematical functions. This


paradigm emphasizes immutability (unchangeable data) and avoids side effects that could alter the
program state unexpectedly. Functional languages are often used in complex computations, artificial
intelligence, and data science.
Examples: Haskell, Lisp, Erlang

1.2.5- Scripting Languages

Scripting languages are used for automation, scripting, and rapid application development.
Unlike compiled languages, they are often interpreted at runtime. These languages are widely used for
web development, task automation, and system administration.
Examples: Bash (Shell Scripting), JavaScript, Perl, Ruby

5
The Complete Java Training Imran Afzal
1.2.6- Markup Languages

Markup languages are not traditional programming languages but are essential for defining the
structure, presentation, and behavior of documents. They are widely used in web
development to format and display content.
Examples: HTML, XML, CSS

1.2.7- Query Languages

Query languages are specialized for retrieving, managing, and manipulating data in databases.
These languages provide powerful tools for structuring and accessing stored information efficiently.
Examples: SQL (Structured Query Language)

1.2.8- Other Types of Programming Languages

Apart from the major categories, several specialized programming languages exist for niche
applications:

• Domain-Specific Languages (DSLs): Designed for specific tasks (e.g., R for statistical
computing).

• Concurrent Programming Languages: Used for multi-threaded and parallel computing


(e.g., Go, Erlang).

• Logic Programming Languages: Based on formal logic for problem-solving (e.g., Prolog).

Understanding these types of programming languages helps in choosing the right one for a specific
application, ensuring efficiency and effectiveness in software development.

1.3- Object-Oriented Programming (OOP)

1.3.1- Introduction to Object-Oriented Programming

Object-oriented programming (OOP) is a programming paradigm that focuses on objects. An object


can be anything with a specific structure and the ability to perform certain actions. Objects represent
real-world entities such as people, places, or things.

6
The Complete Java Training Imran Afzal
In contrast, some entities are not considered objects because they lack a clear structure or defined
actions. Examples of such non-object entities include emotions (e.g., happiness) or abstract concepts
(e.g., freedom). These concepts do not have a tangible form or specific behavior, making them
unsuitable for object-oriented programming.

1.3.2- Understanding OOP Through an Example

To understand OOP better, let’s compare it with procedural (sequential) programming, which
follows a step-by-step approach to executing instructions.

Example: Procedural Programming

Consider the following scenario: You want to write a program that simulates driving a car to buy
groceries. A procedural approach might look like this:

1. Unlock the car

2. Start the car

3. Check rear-view and side-view mirrors

4. Fasten seatbelt

5. Drive to the grocery store

6. Buy a loaf of bread and a dozen eggs

7. Return to the car

8. Unlock the car

9. Start the car

10. Check rear-view and side-view mirrors

11. Fasten seatbelt

12. Drive back home

As you can see, steps 1-4 are repeated in steps 8-11 when returning home. If you were to stop at
another store, you would repeat these steps again, making the program inefficient and redundant.

7
The Complete Java Training Imran Afzal
Figure – 1.3.2

1.3.3- Optimizing with Object-Oriented Programming

In OOP, we define an object and its related functions inside something called a class.

1. Define a Car class

o The Car object has functions such as:

• Unlock the car


• Start the car
• Check rear-view and side-view mirrors
• Fasten seatbelt

2. Rewrite the code using the Car object:

• Use the Car object to start the journey


• Drive to the grocery store and purchase items
• Use the Car object again to return home

This revised approach reduces the number of lines in the program from 11 to just 5, making it easier
to read, maintain, and modify.

1.3.4- Why OOP is Powerful

Object-oriented programming allows for better organization and management of data by grouping
it with the code that operates on it. OOP treats everything in a program as an object that has:

8
The Complete Java Training Imran Afzal
1. Attributes (Data/Properties): Characteristics of the object (e.g., a car's color, model, speed).

2. Methods (Functions/Behaviors): Actions the object can perform (e.g., starting the engine,
driving).

By breaking down a program into smaller, reusable objects, OOP enhances:

• Code reusability (Avoid writing the same code multiple times)

• Modularity (Easier debugging and maintenance)

• Scalability (Easier program expansion with new features)

Object-oriented programming simplifies software development by organizing data and functions into
self-contained objects that interact with each other. This approach makes code more efficient,
modular, and reusable, helping programmers build scalable and maintainable applications.

1.4- Programming Applications Examples

Having explored different programming languages, it is essential to understand which language is best
suited for specific applications. Different languages have unique features that make them ideal for
certain types of software development. Below, we explore some widely used programming languages
and their practical applications.

1.4.1- C++: A Powerful and Versatile Language

C++ was developed as an extension of the C programming language in the late 1970s, incorporating
object-oriented programming features. It is widely used for high-performance applications requiring
efficiency and direct hardware interaction.

1.4.2- Applications of C++

1. Operating Systems: Many modern operating systems are built using C++. Windows,
macOS and Linux are some of its examples.

2. Game Development: C++ is preferred for high-performance game engines like Grand
Theft Auto, The Witcher, World of Warcraft, Doom, League of Legends.

9
The Complete Java Training Imran Afzal
3. Graphics and Multimedia Applications: C++ powers some of the most popular design
tools. Examples include Adobe Software, Autodesk, Blender, DirectX.

4. Financial Systems: Used in trading systems and risk management software.

5. Database Management Systems (DBMS): Many popular databases are built using C++.
Examples include MySQL, PostgreSQL, MongoDB.

6. Embedded Systems: Found in medical devices, automotive systems, and smart home
automation.

Figure – 1.4.2

1.4.3- Java: Platform-Independent and Enterprise-Ready

Java is a programming language known for its ability to run on different platforms without
modification. It is widely used in enterprise applications, web services, and mobile applications.

1.4.4- Applications of Java

1. Enterprise Applications: Java is used for large-scale business software such as Customer
Relationship Management (CRM), Human Resources Management Systems
(HRMS), Supply Chain Management (SCM).

2. Web Development: Java powers secure and scalable web applications. Its examples include
Banking Systems, E-commerce Platforms, Content Management Systems.

10
The Complete Java Training Imran Afzal
3. Mobile Applications: Java is the primary language for Android development. WhatsApp,
Facebook, Instagram, Spotify, Airbnb, Uber are few of its examples.

4. Scientific Applications: Used in data analysis and computation-heavy applications like


Image Processing, Computational Chemistry, Bioinformatics, Astronomy.

5. Game Development: Many games leverage Java's portability. Examples include Minecraft,
RuneScape, Puzzle Games.

Figure – 1.4.4

1.4.5- Python: Simplicity and Versatility

Python is an easy-to-learn language known for its readability and versatility. It is widely used in web
development, data science, artificial intelligence, and automation.

1.4.6- Applications of Python

1. Web Development: Python frameworks power dynamic web applications. Platforms like
Instagram, Pinterest, Dropbox, Reddit are its examples.

2. Data Analysis & Visualization: Python excels in handling big data and analytics. Examples
include Jupyter Notebook, Pandas, Seaborn.

11
The Complete Java Training Imran Afzal
3. Machine Learning & AI: Python is a leading language for AI applications. Examples include
Image Classification, Virtual Assistants, Healthcare AI.

4. Scientific Computing: Used in physics simulations, astronomy, and more.

5. Automation & Scripting: Python simplifies repetitive tasks and system administration.

6. Desktop Applications: Python can build user-friendly desktop software. Its examples are
Calculator, Text Editor, Music Player, Spreadsheets.

7. Game Development: Python is used for simpler games such as Snake Game, Tic Tac Toe,
Hangman, Sudoku.

Figure – 1.4.6

1.4.7- C#: The Microsoft Ecosystem Language

C# was developed by Microsoft and is used extensively in Windows application development, gaming,
and enterprise software.

1.4.8- Applications of C#

1. Windows Desktop Applications: Common in productivity tools such as Text Editors,


Media Players, File Compression Software.

12
The Complete Java Training Imran Afzal
2. Web Applications: C# is widely used in ASP.NET for dynamic websites like E-commerce
Platforms, Real Estate Listings, Online Learning Systems.

3. Mobile Applications: C# is used for cross-platform mobile apps. Examples include Fitness
Tracking, Expense Management, and News Apps.

4. Game Development: C# is the primary language for Unity game engine. 2D Platformers,
Racing Games, Adventure Simulations are some of its examples.

1.4.9- Ruby: A Dynamic and Readable Language

Ruby is known for its simplicity and readability, making it ideal for rapid web development and
automation tasks.

1.4.10- Applications of Ruby

1. Web Applications: Ruby on Rails is a popular framework for web development. Its examples
are Social Networking Sites, Online Marketplaces, Blogging Platforms.

2. Command-Line Tools: Ruby is used for scripting utilities like Backup Utilities, Password
Generators, and Network Monitoring Tools.

3. Automation Scripts: Ruby helps automate repetitive system tasks.

4. Testing & Quality Assurance: Used in software testing frameworks.

5. Chatbots: Ruby is used in customer support and language-learning chatbots.

Each programming language is suited for specific applications based on its strengths and features.
Understanding these differences helps developers choose the right tool for their projects. Whether
you need a high-performance game, a scalable web application, or an AI-powered system, selecting
the appropriate language is crucial for efficiency and success.

1.5- Programmer vs Developer

Understanding the distinction between a computer programmer and a software developer is crucial,
as both roles contribute differently to the software development lifecycle.

13
The Complete Java Training Imran Afzal
A computer programmer specializes in programming languages, writing the code necessary to create
and implement software functionality. Their responsibilities often include coding algorithms, solving
technical problems, and ensuring that the application adheres to industry standards.

Programmers are also involved in writing tests to validate software functionality and collaborating
with quality assurance teams to conduct rigorous testing, such as functional and security testing. They
focus on technical implementation, emphasizing the reliability and accuracy of application features.

A software developer, on the other hand, plays a broader role. They are responsible for application
design, emphasizing user experience, application flow, and usability. Developers work closely with
clients, product managers, business analysts, and designers to understand customer needs and
incorporate effective solutions into the application. Their focus is on creating user-friendly
applications that address customer challenges, ensuring smooth user interaction, and designing
intuitive and accessible interfaces.

1.5.1- Practical Scenario: ABC Bank's Mobile Banking Application

Consider ABC Bank, a financial institution that has decided to develop a mobile banking application
to meet the growing demand for mobile banking services.

• Software Developers:
o Conduct user research and analyze customer needs.
o Design wireframes and mockups for the application's interface.
o Incorporate features like Touch ID, facial recognition, personalized notifications, and account
alerts.
o Maintain a feedback loop with stakeholders and customers to refine the application's design
and functionality.
o Ensure the application delivers optimal user experience.

• Computer Programmers:

o Collaborate with the IT department, software architects, and developers to implement


technical requirements.

14
The Complete Java Training Imran Afzal
o Develop an authentication module for secure user login.

o Implement encryption protocols and establish secure connections to safeguard sensitive


information.

o Build core functionalities, such as account balance displays, transaction histories, e-statements,
and payment gateways.

o Write and execute comprehensive test cases to ensure the application's reliability and security.

1.5.2- Key Differences Between Programmers and Developers

• Focus
o Programmers emphasize technical implementation and coding.
o Developers prioritize understanding customer needs and designing user-friendly interfaces.

• Responsibilities
o Programmers specialize in programming languages, algorithms, and problem-solving.
o Developers are involved in requirement analysis, system design, testing, and documentation.

• Collaboration
o Programmers often work with IT teams and quality assurance professionals.
o Developers engage with stakeholders, business analysts, and designers to align application
features with user needs.

1.5.3- Choosing the Right Role

The decision between becoming a programmer or a developer depends on individual strengths,


interests, and career goals.

• Programmer: Ideal for those who enjoy the technical aspects of coding, have strong logical
reasoning, and excel at problem-solving.
• Developer: Suited for individuals who prefer a holistic approach to software development,
involving design, collaboration, and user experience.

15
The Complete Java Training Imran Afzal
Both roles are equally significant and offer unique opportunities in the software development field,
allowing professionals to contribute based on their skills and aspirations.

16
The Complete Java Training Imran Afzal
Chapter 2

Welcome to JAVA

17
The Complete Java Training Imran Afzal
2.1- Introduction to Java

2.1.1- What is Java?

Java is a high-level, class-based, object-oriented programming language designed to have


minimal implementation dependencies. It is a general-purpose language that allows developers to
write code once and run it anywhere, a concept known as "Write Once, Run Anywhere" (WORA).
This means that Java programs can run on any system that supports Java without requiring
recompilation.

2.1.2- Understanding Java in Simple Terms

Java enables programmers to develop software and applications for a wide range of devices, including
computers, smartphones, and embedded systems. One of its biggest advantages is platform
independence, which allows Java applications to function on different operating systems without
modification.

Java is considered a two-stage system because of its unique compilation process:

1. Compilation Step: The Java source code is compiled into an intermediate form called
bytecode by a program known as the Java Compiler (javac).

2. Execution Step: The bytecode is then translated into machine code by a program called the
Java Virtual Machine (JVM).

This process makes Java more secure and portable, as the bytecode runs in a controlled
environment, reducing the risk of harmful actions.

2.1.3- Why is Java So Popular?

Java was designed to be:

• Easy to learn and use – Its syntax is similar to English-like structures, making it beginner-
friendly.

• Efficient and reliable – Java programs are stable and can run on different devices.

18
The Complete Java Training Imran Afzal
• Secure – Java applications run within a sandboxed environment, which prevents
unauthorized access and execution of harmful code.

These features have contributed to Java’s widespread adoption in various domains, including
enterprise applications, web development, mobile apps, and cloud computing.

2.1.4- The History of Java

Origins and Early Development

Java was originally developed by James Gosling, Mike Sheridan, and Patrick Naughton at Sun
Microsystems in the early 1990s. The project was initially named Oak, inspired by an oak tree outside
James Gosling’s office. However, the name Oak was already trademarked by another company,
requiring Sun Microsystems to select a new name.

2.1.5- Why is Java Called Java?

In 1995, the name Java was chosen as a replacement for Oak. The name is said to be inspired by
coffee beans from Java Island in Indonesia, reflecting the developers' love for coffee. This is why
Java’s logo features a steaming coffee cup, symbolizing energy, dynamism, and innovation.

2.1.6- Java’s Evolution

• Initially, Java was developed for interactive television systems, but the project later shifted
focus to the World Wide Web due to the growing demand for internet applications.

• 1995: Java was released to the public and became extremely popular due to its "Write Once,
Run Anywhere" capability.

• 2010: Oracle Corporation acquired Sun Microsystems, gaining ownership of Java.

• Today, Java remains one of the most widely used programming languages in the world,
powering enterprise software, mobile applications, and cloud computing platforms.

Java’s unique combination of platform independence, security, ease of use, and efficiency has
made it one of the most powerful and popular programming languages. Its ability to run on various
devices, combined with its strong community support, ensures that Java continues to be a leading
choice for developers worldwide.

19
The Complete Java Training Imran Afzal
2.2- Importance of Java

Java is one of the most influential programming languages in the software development industry. It
has gained widespread adoption due to its flexibility, robustness, platform independence, and
strong community support. These features make Java suitable for various applications, from
enterprise systems to web and mobile development.

2.2.1- Key Aspects of Java’s Flexibility

1. Extensive Libraries and Frameworks

Java offers a vast collection of libraries and frameworks that enhance its functionality. These
libraries simplify complex programming tasks and extend Java’s capabilities in various domains,
including:

• Database Connectivity – JDBC (Java Database Connectivity) for interacting with databases.

• Networking – APIs for building distributed systems.

• Web Development – Frameworks like Spring, Hibernate, and Struts.

• Machine Learning & AI – Libraries such as DeepLearning4J and Weka.

These tools make Java adaptable to various projects and industries.

2. Compatibility with Existing Systems

One of Java’s greatest advantages is its ability to integrate with legacy systems seamlessly. It
supports various integration mechanisms such as:

• JDBC (Java Database Connectivity) – Enables Java applications to interact with databases.

• RMI (Remote Method Invocation) – Facilitates communication between distributed


objects.

• JMS (Java Message Service) – Helps in enterprise-level messaging and data exchange.

This ensures that Java applications can communicate with older technologies while maintaining
scalability and reliability.

20
The Complete Java Training Imran Afzal
3. Strong Development Tools and Ecosystem

Java provides a robust development environment with feature-rich tools that enhance productivity.
Some of the most popular Integrated Development Environments (IDEs) for Java include:

• Eclipse – A widely used open-source IDE.

• IntelliJ IDEA – A powerful and intelligent IDE for enterprise development.

• NetBeans – A flexible IDE for Java and web applications.

These tools offer code completion, debugging, refactoring, and profiling features that make Java
development efficient and adaptable.

4. Large and Active Community Support

Java has a large global community of developers who continuously contribute to its evolution. This
community-driven approach ensures:

• Regular updates and improvements.

• A vast repository of open-source projects and libraries.

• Forums and resources such as Stack Overflow, GitHub, and Oracle’s Java Community
Process (JCP), which provide support and solutions to developers.

This active ecosystem helps Java remain relevant, innovative, and adaptable to modern
development needs.

2.2.2- Key Features of Java

1. Platform Independence

Java’s platform independence is one of its defining features. A Java program can run on any device
or operating system that supports Java Virtual Machine (JVM). This is possible due to Java’s two-
stage execution process:

• Compilation – Java source code is compiled into bytecode.

21
The Complete Java Training Imran Afzal
• Interpretation – The JVM interprets bytecode into machine code for execution on different
systems.

This allows Java applications to run across Windows, macOS, Linux, and even embedded systems
without modification, making it cost-effective and timesaving.

2. Backward Compatibility

Java ensures backward compatibility, meaning applications written in older Java versions continue
to work on newer Java releases without requiring major modifications. This feature is crucial for
businesses and developers because:

• It reduces maintenance costs by ensuring long-term support.

• It allows seamless software upgrades without breaking existing applications.

3. Robustness and Security

Java was designed with a focus on stability and security, making it a preferred language for critical
applications. Some of its built-in security features include:

• Strict Compile-Time Checks – Prevents errors before execution.

• Exception Handling Mechanism – Ensures applications handle unexpected conditions


gracefully.

• Automatic Memory Management (Garbage Collection) – Prevents memory leaks and


improves performance.

• Sandboxing and Access Control – Restricts unauthorized access to system resources.

• Cryptography Libraries – Provides tools for encryption and secure communication.

These features make Java a reliable choice for banking applications, e-commerce platforms, and
enterprise-level software that require high security and stability.

Java’s extensive flexibility, compatibility, platform independence, and security features make it
one of the most versatile and widely used programming languages today. Its ability to adapt to

22
The Complete Java Training Imran Afzal
changing software development trends ensures its continued dominance in areas like enterprise
applications, mobile development, web services, and cloud computing.

With its strong community support, backward compatibility, and rich ecosystem of tools, Java
remains a reliable, future-proof language for developers worldwide.

2.3- How to Succeed as a Java Developer

In this lecture, we're going to talk about how to succeed as a Java Developer, if you're looking at
learning a new programming language to develop your programmatic skills or for any other reason for
that matter.

2.3.1- Why Learn Java

One of the primary reasons to consider learning Java is its extensive adoption by top companies. With
a history spanning over 20 years, Java has established itself as a stable and reliable programming
language favored by prominent IT corporations. According to StackShare.io, around ten thousand
companies include Java in their tech stack, including industry leaders such as Google, Netflix,
Facebook, Airbnb, Uber, and many more. While these companies also utilize other emerging
technologies, Java remains a key component in their software development ecosystem. According to
the TIOBE Index, which tracks the popularity of programming languages, Java consistently ranks
among the top languages.

2.3.2- Job Market and Salary Expectations

It is no surprise that highly trained and skilled Java developers are in high demand, often commanding
attractive salaries. According to Glassdoor.com, which surveys job market data, the average base pay
for entry-level Java developers is around $47,100 per annum, with the potential for significant growth
as experience and skills increase. Salaries can reach up to $154,000 per annum for experienced Java
developers. Please note that these figures are approximate and can vary based on factors such as
location, industry, and individual qualifications.

23
The Complete Java Training Imran Afzal
2.3.3- Community Support

Extensive community support for Java is another significant advantage. As we discussed earlier, Java
has been widely adopted by developers of all levels, from beginners to experts, over many years. This
widespread usage has cultivated a thriving online community dedicated to Java programming. The
importance of such community support cannot be overstated for developers. It provides a platform
for knowledge sharing, problem-solving, and collaboration, ultimately enhancing the learning and
development experience. According to the Stack Overflow survey conducted in 2022, Java
consistently ranks among the most popular programming languages, further underscoring the
significance of its community support.

2.3.4- Java’s Versatility and Applications

Another compelling reason to consider Java as a successful developer’s choice is its wide range of
applications. When choosing a language, the intended purpose is crucial, although it can often be
unclear, especially for students or those exploring various fields. In such cases, opting for a language
that offers diverse opportunities becomes advantageous. This is where Java's versatility comes into
play. Java has gained significant popularity in the development of Android applications, making it a
prominent language in that domain. Additionally, Java finds extensive usage in web applications,
desktop applications, and enterprise applications. It has played a pivotal role in enabling the
development of leading big data technologies like Hadoop. While other languages exist for these
applications, Java's established presence and widespread adoption make it a reliable choice across
multiple domains. Its compatibility with various platforms and frameworks expands the possibilities
for developers, allowing them to explore different fields and seize diverse opportunities.

2.3.5- Development Tools and APIs

In conclusion, learning Java opens up numerous opportunities for future prospects. The availability
of powerful development tools is a key factor that contributes to its effectiveness. Working with these
tools in a professional environment enhances productivity and enables better project workflow
management. Integrated Development Environments like Eclipse, NetBeans, and IntelliJ IDEA offer
features such as debugging, code refactoring, auto-completion, and syntax highlighting, which greatly
simplify and accelerate coding tasks. Another advantage of Java is its extensive API (Application
Programming Interface) and the availability of multiple open-source libraries. The Java API, included

24
The Complete Java Training Imran Afzal
in the JDK (Java Development Kit), provides a rich collection of classes, packages, and interfaces for
developers to leverage. Additionally, there are optional APIs that can be downloaded separately to
enhance functionality. Moreover, developers can utilize open-source libraries, which are third-party
software components, to augment their projects. These libraries can be added to the project's class
path or managed through tools like Maven for seamless dependency management.

2.3.6- Tips to Succeed as a Java Developer

• To succeed as a Java developer, it is important to stay updated with the latest advancements,
frameworks, and best practices through continuous learning. Explore online resources,
documentation, tutorials, and participate in forums to expand your knowledge.
• Apply your knowledge by working on real-world projects. Building practical applications will
help you gain hands-on experience, problem-solving skills, and a
portfolio to showcase your abilities to potential employers.
• Engage with the Java community, join developer forums, and participate in open-source
projects. Collaborating with other developers exposes you to diverse
perspectives, fosters learning, and helps you stay connected with the latest trends and
opportunities.
• Familiarize yourself with powerful Java development tools like Eclipse, NetBeans, and IntelliJ
IDEA. These IDEs offer a wide range of features that streamline development,
debugging, and code maintenance, ultimately boosting your productivity.
• Write clean, efficient, and well-structured code. Follow coding conventions, use proper
naming conventions, and leverage design patterns to ensure your code is
maintainable and scalable.
• Invest time in understanding the core concepts of Java, including Object-Oriented
Programming (OOP), inheritance, polymorphism, and exception handling. A strong
foundation will empower you to tackle complex projects with confidence.
• Actively seek feedback from peers, mentors, and experienced developers to enhance your
skills. Embrace constructive criticism, learn from your mistakes, and strive for continuous
improvement in your coding practices.

25
The Complete Java Training Imran Afzal
By following these guidelines and continually honing your skills, you can position yourself for success
as a Java developer, opening up a world of opportunities in a language known for its versatility,
industry support, and robustness.

2.4- Introduction to Software Tools JDK, JRE, JVM & IDE

To write and run a Java program, we must first set up a Java environment on our local machine. Before
we proceed with setting up a Java environment on our local machine, let's first understand what the
term environment means.

In a general sense, an environment refers to the surroundings or conditions in which something exists
or operates. In the context of setting up a Java environment on a local machine, it specifically refers
to the configuration and preparation necessary for running Java programs. Setting up a Java
environment involves installing the necessary software and tools required to develop and execute Java
programs like JDK.

2.4.1- Understanding JDK (Java Development Kit)

The first thing that we install is JDK, which is the Java Development Kit. It contains all the things
required to run Java programs. We must have JDK installed on any system in order to write Java code
and run it.

If we define JDK in other words, it is a software development kit used to develop Java applications
and applets. It includes:

• A Java compiler, which translates Java code into a form that can be run on a computer.

• A set of libraries and tools that are needed to develop Java applications.

2.4.2- Introduction to IDE (Integrated Development Environment)

We must also have a code editor or an IDE, which stands for Integrated Development
Environment. We will use IntelliJ IDEA in our course, but you can also use Eclipse or Visual
Studio Code.

26
The Complete Java Training Imran Afzal
So, what is an IDE?
An IDE is a software application that provides comprehensive facilities to computer programmers
for software development. An IDE typically consists of:

• A source code editor

• Build automation tools

• A debugger

• Some IDEs also include a compiler or interpreter.

2.4.3- Understanding JRE (Java Runtime Environment)

Now, coming back to the JDK that we installed on our computer, it includes a component called JRE,
which stands for Java Runtime Environment.

JRE includes:

• Libraries

• Java Virtual Machine (JVM), which is the most important Java component.

If we define JRE in other words, it is a software package that provides a runtime environment in
which Java applications can be executed. It includes:

• A Java Virtual Machine (JVM), which interprets Java bytecode and executes it on a
computer.

• A set of libraries and tools needed to run Java applications.

2.4.4- Understanding JVM (Java Virtual Machine)

To put it simply, JDK, JRE, and JVM are stored one inside the other, level by level. Now, let’s
discuss how Java code is actually executed.

27
The Complete Java Training Imran Afzal
2.4.5- Java Code Execution Process

Our Java code executes in two steps:

1. Compilation

2. Execution

Step 1: Compilation

• The code we write in Java is called source code.

• The source code is stored with the .java extension.

• All of this source code is sent to the compiler, which is included in the JDK.

• The compiler converts the source code into byte code, which is stored with the .class
extension.

• This byte code can be run on any system that has JRE installed.

If you write Java code in Linux, Windows, or Mac, you simply convert it to byte code, which can
then run on any system. Therefore, Java is called a portable or multiplatform language. This
covers the first step, which is compilation.

Step 2: Execution

• In this step, the JVM takes this byte code and converts it into native code.

• Native code is the machine code that a system can understand.

• Once your byte code is converted to native code, your machine will be able to understand
it regardless of whether it is running Linux, Windows, or Mac.

If we define the Java code execution process in other words:

• The process starts when we write Java code and save it in files with the .java extension.
• The JDK has a compiler that converts this source code into byte code with the .class
extension. This process is called compilation.

28
The Complete Java Training Imran Afzal
• This byte code is given to the JVM, which is inside the JRE.
• The JVM converts this byte code into native code, which is called execution.
• JVM is also referred to as an interpreter.

Therefore, Java is known as a two-stage system, as it involves both compilation and


interpretation.

29
The Complete Java Training Imran Afzal
Chapter 3

Lab Setup

30
The Complete Java Training Imran Afzal
3.1- Lab Setup

Let’s go through our lab design, the lab that you will be practicing Java throughout this course. So,
the first thing you need to do is download and install JDK.

Don't worry if this feels a bit technical. We'll guide you through the process on both Windows and
Linux. Just follow along, and you will be all set in no time.

3.1.1- System Requirements Check

Step 1: Identify Your Computer Specifications

Before installing the JDK, it's important to check:

• What kind of computer you have

• If your system has enough resources (Memory, CPU, and Disk Space)

These are basic checks to ensure everything runs smoothly on your computer.

Step 2: Installing JDK on Windows

If you're installing the JDK on Windows, remember that most modern computers use the 64-bit
version. Installing the 64-bit JDK ensures compatibility and lets your system perform at its best.

Minimum System Requirements for Windows

• At least 2GB of RAM

• A processor with a speed of 1 GHz or higher

• 200MB to 500MB of free disk space

Once you have confirmed that your system meets these requirements, you are ready to install the
JDK.

When you install the latest JDK, it also includes Java Runtime Environment (JRE). That means
no extra downloads or installations—everything comes bundled in one convenient package.

31
The Complete Java Training Imran Afzal
Step 3: Using Java Shell (JShell)

After installing the JDK, you will also find a handy tool called Java Shell, or in short, JShell.

What is JShell?

• JShell is a simple interactive tool that lets you try out Java features without writing a full
program.

• It is perfect for Java beginners who want to experiment with Java quickly.

JShell Minimum System Requirements

• Same as JDK (If your system meets the JDK requirements, JShell will work perfectly).

Step 4: Installing IntelliJ IDEA

Next, move on to installing IntelliJ IDEA, or simply IDEA, which is an Integrated Development
Environment (IDE) that makes programming in Java a breeze. IntelliJ IDEA helps you create well-
documented programs and projects efficiently.

Minimum System Requirements for IntelliJ IDEA

• 4GB of RAM

• A modern multi-core processor

• 2.5GB of free disk space

Most modern computers will have these specifications, so you should be good to go.

JDK Installation on Linux

• Now, let’s talk about Linux users.


• We will run a few installation commands in the terminal to get the JDK up and running.
• The system requirements for installing the JDK on Linux are pretty much the same as
Windows, so there is no need to worry about extra hardware resources.

32
The Complete Java Training Imran Afzal
• Once the JDK is installed, you will get JShell, which will give you the same experience as
Windows.

Installing IntelliJ IDEA on Linux

To install IntelliJ IDEA on Linux, we will:

• Write simple installation commands

• Execute the commands in the terminal

Once that’s done, both your Windows and Linux systems will be equipped with a powerful Java
environment.

3.2- Installation of JDK on Windows

If you're using Mac, Linux, or have already installed JDK on Windows, you can skip this lecture.

Now, let's begin the JDK installation on Windows.

• Open your favorite browser and go to the URL: java.sun.com.


• Click on the Java SE 17.0.5 (LTS) version.
• You should now see three tabs: Linux, macOS, and Windows.
• Select the Windows tab and click on the "x64 Installer" download link.
• This will download the installer file to your computer.
• Once the download begins, wait for it to be completed.
• After the download is complete, open the installer file and follow the prompts to install the
JDK.
• Make sure to remember the installation location, as you will need it later.

After the installation is complete, you need to set the JAVA_HOME environment variable to point
to the JDK installation folder.

For this, follow these steps:

33
The Complete Java Training Imran Afzal
1. Go to the Start menu and search for "Environment Variables."

2. Click on Edit the system environment variables.

3. Click on the Environment Variables button.

4. Under the System variables section, scroll down and find the "Path" variable.

5. Click on the Edit button.

6. Click on the New button and add the path to the JDK installation folder.

7. Here is my JDK installation path for reference. Paste your path here.

8. Click OK to save the changes.

Let’s test it to verify the JDK installation.

1. Open a Command Prompt.

2. Enter the following command:

java -version

3. This should display the version of the JDK you have installed.

4. If you see an error message, ensure the JDK installation location is correctly set in the
PATH environment variable.

It’s done, congratulations, your Java setup is complete.

3.3- Installation of JDK on Linux

We're going to install JDK version 17 LTS on CentOS Linux. If you use Windows, check out the
relevant lecture for your operating system.

• First, run the machine as the root user and enter the root password.
• Use the following command to check if JDK is already installed: java -version
• If JDK is not installed, proceed with the installation.

34
The Complete Java Training Imran Afzal
• Download Oracle Java JDK 17 LTS using the wget command with the official download
link: wget <JDK_17_LTS_Download_Link>
• This will download JDK 17 LTS along with all the required dependencies. This step may take
some time.
• Once the download is complete, install Oracle Java JDK 17 using the rpm command:
rpm -ivh <JDK_17_LTS_File_Name>.rpm
• Press Enter to start the installation process. This may take some time.
• After the installation is complete, verify the installation by running the following command:
java -version
• This should display the version of the JDK that is currently being used.

It’s done, congratulations, your Java setup is complete and ready to bring your ideas to life.

3.4- Introduction to JShell

JShell is a command-line tool in Java that allows you to execute Java code snippets and commands
interactively. It is included in the Java Development Kit (JDK) starting from version 9.

3.4.1- Why Use JShell?

When you enter code at the JShell prompt, the tool immediately executes the code and prints the
results to the console.

For example, if you enter a simple math expression such as:

2+2

JShell will execute the expression and print the result:

JShell can be used in conjunction with IDEs, text editors, and build systems. It provides several
commands to control the execution of your code. Some important ones include:

• /list – Lists the code snippets entered in the session


35
The Complete Java Training Imran Afzal
• /exit – Exits JShell

JShell has some limitations; it does not replace the need for an IDE but serves as a quick tool to
get started with Java.

3.4.2- Running JShell on Windows

JShell is automatically installed when you install the JDK.

1. Open the Windows terminal by typing "cmd" in the Windows Start search bar or by
pressing Windows + R keys.

2. Once the terminal is open, type: jshell

3. Now, the terminal becomes a JShell prompt, displaying the Java version in use.
4. To see an introduction to JShell, type: /help intro and press Enter.
5. To view available JShell commands and their uses, type: /help command
6. JShell provides commands to save and open files, reset JShell, review typing history, and
more.

3.4.3- Using the /list Command

The /list command allows you to view the code snippets and commands entered in the current
JShell session.

Example:

1. Type:

2+2

2. Press Enter, and JShell will return:

3. Now type:

/list

4. It will display the previously entered code snippets.

36
The Complete Java Training Imran Afzal
3.4.4- Exiting JShell

To quit JShell, type:

/exit

and press Enter.

JShell will display a goodbye message and exit.

3.4.5- Running JShell on Linux

To run JShell on Linux (CentOS):

1. Open the CentOS terminal.

2. Type:

jshell

1. The terminal becomes a JShell prompt, just like in Windows.


To see the introduction to JShell, type:
/help intro
and press Enter.

It works exactly the same as Windows, allowing users to experiment with Java interactively.

37
The Complete Java Training Imran Afzal
Chapter 4
Java Basics

38
The Complete Java Training Imran Afzal
4.1- Java First Program using Notepad and Command Prompt

This is the only lecture where Java code will be written in Notepad and executed using the Windows
command prompt. All other Java code will be written using JShell and later in an IDE.

The goal is to provide a clear understanding of how Java runs behind the scenes, why Java is called a
two-stage system language, how Java converts source code into bytecode in the form of a .class file,
and how the JVM (Java Virtual Machine) executes that bytecode.

4.1.1- Writing Java Code in Notepad

1. Open Notepad.

2. Write the following code:.

class helloWorld
{
public static void main(String args[])
{
System.out.print("Hello World");
}

}
3. After writing the code, save it with the same name as the class (helloWorld.java), because
Java requires the filename and public class name to match.

4. The file must have the .java extension as per Java rules.

5. Save the file in a directory named "java-code".

4.1.2- Compiling and Running Java Code in Command Prompt

1. Open the Windows command prompt and navigate to the directory where the Java source
file is saved.

cd path\to\java-code

39
The Complete Java Training Imran Afzal
2. Confirm that the file exists using the dir command.
3. Compile the Java program using the Java compiler:

javac helloWorld.java

4. The Java compiler converts the source code into byte code and creates a file named
helloWorld.class.

5. Use the dir command again to confirm the .class file is created.

6. Open the class file using any text editor if desired. The contents are in bytecode format and
will not be human-readable.

4.1.3- Running the Program with the JVM

1. In the Command Prompt, run the compiled class using the Java interpreter:

java helloWorld

2. This command calls the JVM, which converts the bytecode into machine language and
executes the program. The displayed output will be:

Hello World

This basic exercise demonstrates how Java code flows from source (.java) to compiled bytecode (.class)
and is ultimately executed by the JVM on any machine with a Java runtime installed.

4.2- Java First Program - Hello World using JShell

Hello World is a simple program that is often used as a starting point when learning a new
programming language. This program typically just displays the words Hello, World on the screen. It
allows users to test the syntax of the language and provides a sense of accomplishment after
completing it. Even experienced programmers feel satisfaction after writing their first Hello World
program in a new language.

4.2.1- Basic Rules for Writing Java Code

Before writing Java code, it's important to remember some basic rules:

40
The Complete Java Training Imran Afzal
• Java is case-sensitive, so be careful when using keywords, variables, and method names.

• Every statement in Java must end with a semicolon.

• Java uses curly braces {} to group blocks of code.

• Java is an object-oriented programming language, meaning all code is organized into


classes. Each class must have a unique name and should be saved in a separate file with the
same name followed by the .java extension.

• Every Java program must have a main method, which is the entry point of the program:

public static void main(String[] args)

• Java follows camelCase naming conventions. The first letter of each word in a multi-word
identifier is capitalized, except for the first word, which is lowercase. For example, helloWorld
is a valid identifier in Java.

• Java uses // for single-line comments and /* */ for multi-line comments.

4.2.2- Importance of Comments in Java

• Comments help document the code, making it easier to understand.

• Comments serve as a reminder of the purpose of a particular piece of code.

• Comments improve readability, making it easier to follow the program's logic.

4.2.3- Writing Java Code in JShell

JShell allows writing and running Java code snippets and expressions, rather than complete Java
programs.

In the previous lecture, the full Java program to print Hello World was written according to Java
rules:

class helloWorld

41
The Complete Java Training Imran Afzal
public static void main(String args[])

System.out.print("Hello World");

Instead of writing an entire Java class in JShell, only a Java statement can be executed directly:

System.out.println("Hello, World!");

4.2.4- Running Hello World in JShell

1. Open the command prompt and type:

jshell

2. JShell prompt appears, allowing you to enter Java statements.

3. Enter the statement:

System.out.println("Hello, World!");

4. Press Enter, and the output appears:

Hello, World!

4.2.5- Common Errors in JShell

• Using single quotation marks ('Hello World') instead of double quotation marks results in
an unclosed character literal error.

• Forgetting quotation marks around the text will also cause an error.

42
The Complete Java Training Imran Afzal
4.2.6- Understanding Multiline Statements in JShell

If a closing bracket is missing, JShell enters multiline mode, shown as:

...>

This indicates JShell is waiting for more input before execution. Additional lines can be added, and
execution occurs only when the set of brackets is complete.

To exit multiline mode without executing, press:

CTRL + C

After completing the brackets and pressing Enter, the output is displayed. In JShell, a semicolon is
optional, making it easier to experiment with Java code snippets interactively.

4.3- Variables in Java

4.3.1- Keywords in Java

Before discussing variables, it's important to understand keywords.

In Java, a keyword is a reserved word that has a specific meaning in the programming language. These
words cannot be used as names for variables, methods, or other identifiers in code. Keywords are
case-sensitive, meaning int is different from Int or INT.

4.3.2- Types of Keywords

• Primitive data types: Keywords like int, double, and char represent basic types of data.

• Control statements: Keywords like if, else, and while control the flow of program execution.

• Modifiers: Keywords like private, public, and final modify the behavior or accessibility of
classes, methods, or variables.

• Other keywords: Keywords like this, super, and null serve various purposes in Java.

Each of these topics will be discussed step-by-step later on.

43
The Complete Java Training Imran Afzal
4.3.3- What Are Variables?

A variable is a way to store data in memory. The name of the variable allows access to stored data,
while the computer determines where it is stored in RAM. As the name suggests, variables can change
values during program execution.

4.3.4- Declaring a Variable

A variable must be declared with a specific data type before use.

For example, to declare an integer variable named age:

int age;

Once declared, a value can be assigned using the = operator:

age = 25;

4.3.4- Primitive Data Types in Java

Java has several primitive data types, including:

• int – stores integers

• float / double – stores floating-point numbers

• char – stores single characters

• boolean – stores true/false values

Choosing the right data type is important for performance and accuracy. Using an integer (int) for a
decimal value may result in precision loss.

In addition to primitive data types, Java also supports object data types, which reference objects that
contain both data and behavior (methods). Objects will be covered in later lectures.

4.3.5- Defining Variables in JShell

To define an integer variable age with the value 25 in JShell:

44
The Complete Java Training Imran Afzal
int age = 25;

This creates a variable of type int, assigns the name age, and stores the value 25.

Printing a Variable

To print the value of age:

System.out.print(age);

Changing a Variable’s Value

To change age from 25 to 30:

age = 30;

This replaces the previous value.

4.3.6- Statements in Java

A statement is a piece of code that performs a specific action.

Types of Statements

• Expression statements – contain expressions that evaluate to a value. Example:

int age = 25;

• Declaration statements – declare a new variable. Example:

int myVar;

• Control statements – control the flow of execution (if, while, for).

• Block statements – groups multiple statements inside {}.

4.3.7- Declaration vs. Definition

• Declaration: Creating a variable without assigning a value. Example:

45
The Complete Java Training Imran Afzal
int age;

• Definition: Declaring and assigning a value. Example:

int age = 25;

4.3.8- Practice Question

Modify the myAge variable from 30 to 65 and print the new value.

int myAge = 30;

myAge = 65;

System.out.print(myAge);

This outputs 65.

A variable’s value can change during program execution using the assignment operator =.

4.3.9- Listing Commands in JShell

To view all executed commands in JShell:

/list

To clear the screen:

CTRL + L

4.3.10- Storing an Arithmetic Expression in a Variable

A variable can store the result of an arithmetic operation:

int myNumber = 5 + 10;

The expression 5 + 10 evaluates to 15, which is then stored in myNumber.

46
The Complete Java Training Imran Afzal
To print the value of myNumber:

System.out.print(myNumber);

This outputs 15.

4.4- Variables in Expressions

In the previous lecture, variables were discussed, including how data is stored in them and how they
are used. Now, some complex mathematical expressions will be explored, and their results will be
stored in variables.

4.4.1- Using Variables in Arithmetic Expressions

A literal value in an expression can have its result stored in a literal variable.

For example:

int literalVariable = (20 / 2) + (10 + 2);

Here, (20 / 2) + (10 + 2) is a simple arithmetic expression in Java, combining division and
addition.

• / operator performs division.

• + operator performs addition.

In Java:

• If both operands in a division operation are integers, the result is an integer.

• If at least one operand is a floating-point number, the result is a floating-point value.

4.4.2- Order of Execution Using Parentheses

Operations inside parentheses are evaluated first, followed by those outside.

47
The Complete Java Training Imran Afzal
Step-by-step evaluation:

(20 / 2) + (10 + 2)

(10) + (12)

22

The result 22 is stored in the variable literalVariable.

4.4.3- Defining Variables in JShell

1. Open JShell.

2. Create a variable literalVariable and assign the arithmetic expression:

int literalVariable = (20 / 2) + (10 + 2);

3. Press Enter, and the result will be displayed.

4.4.4- Using Variables in New Expressions

1. Create a variable secondVariable and assign it the value 50:

int secondVariable = 50;

2. List all variables and their values using the /var command:

/var

This will display all previously defined variables.

3. Add literalVariable and secondVariable and store the result in a new variable sum:

int sum = literalVariable + secondVariable;

4. List variables again using the /var command to view updated values.

48
The Complete Java Training Imran Afzal
4.4.5- Modifying Variable Values

1. Change the value of secondVariable by multiplying it by 3:

secondVariable = secondVariable * 3;

• Initially, secondVariable = 50.


• After multiplication by 3, secondVariable = 150.

2. Update sum by adding the new secondVariable value:

sum = sum + secondVariable;

• Initially, sum = 72.


• After adding 150, sum = 222.

These examples demonstrate how variables in Java can store mathematical expressions, be
modified, and be used for more complex operations.

4.5- Data Types in Java

A primitive data type is a basic type of data built into a programming language. These data types
represent fundamental values such as numbers and characters and are the simplest data types
available in Java.

Eight Primitive Data Types in Java

Data Size Description Range Example


Type

Byte 8-bit Small integer values -128 to 127 byte b = 100;

Short 16-bit Medium integer values -32,768 to 32,767 short s = 100;

Int 32-bit Standard integer values - int i = 100000;


2,147,483,648 to 2,147,483,647

49
The Complete Java Training Imran Afzal
Long 64-bit Large integer values -9.223 × 10¹⁸ to 9.223 × 10¹⁸ long l =
10000L;

Float 32-bit Floating-point numbers ±3.402823 × 10³⁸ float f = 3.14f;

Double 64-bit High-precision ±1.797 × 10³⁰⁸ double d =


floating-point numbers 3.14;

Char 16-bit Single character '\u0000' to '\uffff' char c = 'A';


(Unicode)

Boolean 1-bit True or False values true / false boolean b =


true;

4.5.1- Understanding Signed and Unsigned Data Types

In Java, all integer primitive types (byte, short, int, long) are signed, meaning they can hold both
positive and negative values. Java does not have built-in unsigned data types, meaning it cannot
store only non-negative numbers.

4.5.2- Detailed Explanation of Each Data Type

1. byte Data Type

• Represents small whole numbers.

• Used to save memory in large arrays or when handling binary data.

• Range: -128 to 127.

Example:

byte b = 100;

If you try to assign 128, Java will return an error since the byte range is exceeded.

2. short Data Type

• Represents medium-range whole numbers.

50
The Complete Java Training Imran Afzal
• Range: -32,768 to 32,767.

Example:

short s = 100;

If you try short s = 32768;, an error occurs since 32768 exceeds the short range.

3. int Data Type

• The most commonly used integer type due to its balance between range and memory
usage.

• Range: -2,147,483,648 to 2,147,483,647.

Example:

int i = 1000000;

You can also perform operations using int variables:

int i1 = 500000;

int i2 = 250000;

int i3 = i1 + i2; // Result: 750000

4. long Data Type

• Used for storing very large integers.

• Range: -9.223 × 10¹⁸ to 9.223 × 10¹⁸.

• Requires L suffix (10000L) to indicate a long value.

Example:

long l = 10000L;

5. float Data Type

51
The Complete Java Training Imran Afzal
• Used for storing fractional values with less precision than double.

• Range: ±3.402823 × 10³⁸.

• Requires f suffix (3.14f) to indicate a float value.

Example:

float f = 3.14f;

6. double Data Type

• Used for high-precision fractional values.

• Range: ±1.797 × 10³⁰⁸.

Example:

double d = 3.14;

Since double has higher precision than float, it is preferred when working with scientific
calculations.

7. char Data Type

• Stores single characters (letters, digits, symbols).

• Uses single quotes 'A'.

• Supports Unicode characters ('\u0000' to '\uffff').

Example:

char c = 'A';

8. boolean Data Type

• Used to represent true or false values.

Example:

boolean isJavaFun = true;

52
The Complete Java Training Imran Afzal
Boolean values are useful in conditional statements (if, while, etc.).

4.5.3- Why Is String Not a Primitive Data Type?

String is not a primitive data type in Java because:

• It is a class, making it a reference type.

• It provides methods for operations like concatenation, comparison, and substring


searching.

Example of String:

String s = "Hello, World!";

String concatenation:

String s1 = "Hello, ";

String s2 = "World!";

String s3 = s1 + s2; // "Hello, World!"

In Java, the + operator is overloaded for Strings, allowing concatenation.

• Java has eight primitive data types: byte, short, int, long, float, double, char, and boolean.

• Java does not support unsigned data types.

• int is the most commonly used integer type.

• float is used for approximate decimal values, while double provides higher precision.

• char stores single characters.

• boolean stores true or false values.

• String is not a primitive type but a class.

53
The Complete Java Training Imran Afzal
These fundamental data types form the building blocks of Java programs.

4.6- Primitive Data Types Examples

In the previous lecture, primitive data types and their role in Java were covered. Now, practice
exercises and examples of how primitive data types work will be explored.

4.6.1- Byte Data Type Example

The byte data type is an 8-bit signed integer that can hold values from -128 to 127.

4.6.2- Adding Two byte Values

When two byte values are added, the result is of type int, due to Java’s widening primitive
conversion.

Example

byte x = 10;

byte y = 20;

int sum = x + y;

System.out.println(sum);

4.6.3- Why is the Result an int?

In Java, arithmetic operations between two values result in the larger data type to prevent loss of
precision. Since int has a larger range than byte, the result is stored as an int.

Data Type Size Range

Byte 8-bit -128 to 127

Short 16-bit -32,768 to 32,767

54
The Complete Java Training Imran Afzal
Int 32-bit -2,147,483,648 to 2,147,483,647

Long 64-bit -9.223 × 10¹⁸ to 9.223 × 10¹⁸

Since int has a much larger range than byte, the sum of two byte values must be stored in an int
variable to avoid overflow issues.

4.6.4- Adding byte and short Values

A byte value can also be added to a short value. The result will still be stored in an int.

Example

byte x = 10;

short y = 20;

int sum = x + y;

System.out.println(sum);

• x is of type byte, and y is of type short.

• The result of x + y is stored as an int, since short cannot hold the operation's result.

4.6.5- Other Arithmetic Combinations

Operation Result Type

byte + int Int

byte + long Long

int + float Float

double + double Double

55
The Complete Java Training Imran Afzal
Since Java automatically promotes smaller types to larger types, operations involving different
primitive data types will always result in the largest type in the operation.

4.6.6- Checking Data Type Ranges in Java

The MIN_VALUE and MAX_VALUE fields can be used to check the range of a data type.

Example

System.out.println("Min Range of Short is: " + Short.MIN_VALUE + " and Max Range of Short is:
" + Short.MAX_VALUE);

Output

Min Range of Short is: -32768 and Max Range of Short is: 32767

The Short class is a wrapper class for the primitive short type. It provides static fields like
MIN_VALUE and MAX_VALUE to check limits. Wrapper classes will be covered in future lectures.

4.7- Type Casting in Java

4.7.1- What is Type Casting?

Casting in Java is the process of converting one data type into another. There are two types of
casting in Java:

1. Widening Casting (Implicit Casting) – Converts a smaller data type to a larger one.

2. Narrowing Casting (Explicit Casting) – Converts a larger data type to a smaller one.

4.7.2- Widening Casting (Implicit Casting)

Widening casting occurs automatically when a smaller data type is assigned to a larger data type.

Example

int i = 123;

56
The Complete Java Training Imran Afzal
double d = i;

Here, int is automatically converted to double without any explicit casting.

Example: Widening short to int

short s = 100;

int j = s;

Since int has a larger range than short, this conversion happens safely without data loss.

Converting char to int

A char in Java is stored as an integer (ASCII value).

char c = 'A';

intk;

int k = c;

Instead of storing 'A', k will store the ASCII value of 'A', which is 65.

4.7.3- ASCII (American Standard Code for Information Interchange)

ASCII is a character encoding standard that represents characters using 7-bit integers. Every
character has a corresponding ASCII value.

Character ASCII Value

'A' 65

'B' 66

'a' 97

57
The Complete Java Training Imran Afzal
'0' 48

To convert an ASCII code to a character:

int asciiCode = 65;

char c = (char) asciiCode;

Now, c will store 'A'.

4.7.4- Narrowing Casting (Explicit Casting)

Narrowing casting is manually done when converting a larger data type into a smaller one.

Example

double pi = 3.14;

int roundedPi = (int) pi;

• The decimal portion is lost, and roundedPi will store 3.

4.7.5- Example: Converting float to int

float f = 3.14f;

inta;

a = (int) f;

• The value 3.14 is truncated to 3.

• The decimal portion is lost, which means precision loss occurs.

58
The Complete Java Training Imran Afzal
4.7.6- Important Notes on Type Casting

1. Java does not check if a value fits within a variable's range.

• Example: Assigning an out-of-range value causes errors.


• Example (Incorrect):

byte b = 200; // Error! Out of range for byte (-128 to 127)

2. Java is statically typed, meaning the data type must be known at compile-time.

3. Use type casting carefully to avoid:

• Precision loss (e.g., double to int)


• Unexpected behavior in large calculations

Summary

Type of Casting Direction Automatic? Example

Widening Casting Smaller → Larger Yes (Implicit) int → double

Narrowing Casting Larger → Smaller No (Explicit) double → int

• Widening casting happens automatically without data loss.

• Narrowing casting requires explicit conversion and may lead to data loss.

• ASCII values can be converted to characters using type casting.

Understanding type casting helps control data conversion and prevent errors when working with
different data types.

4.8- Float vs Double

4.8.1- Float Data Type

In Java, float is a single-precision 32-bit IEEE 754 floating-point data type. It represents fractional
values and is used when the higher precision of double is not needed.

59
The Complete Java Training Imran Afzal
4.8.2- IEEE 754 Standard

IEEE 754 is a widely used standard for representing floating-point numbers in computers. It defines
how numbers with fractional components, such as 3.14 or 0.1, are stored and specifies operations like
addition and multiplication.

A 32-bit floating-point number consists of:

• sign bit
• 8 exponent bits
• 23 mantissa (fractional) bits

4.8.3- Declaring Float Variables in Java

When using float in Java, a value must end with either f or F to distinguish it from double. Both
uppercase and lowercase work, but lowercase f is more common.

Example: Declaring Float Variables

float num1 = 3.14f;

float num2 = 2.71F;

System.out.println(num1);

System.out.println(num2);

4.8.4- Key Characteristics of Float

• Requires less memory than double (4 bytes).


• Has less precision, leading to rounding errors in calculations.
• Generally faster than double due to a smaller memory footprint.

60
The Complete Java Training Imran Afzal
4.8.5- Checking the Range of Float

System.out.println("Range of Float: Min - " + Float.MIN_VALUE + " to Max - " +


Float.MAX_VALUE);

Example: Float Expressions and Error Handling

When performing arithmetic operations with float, use the f suffix to avoid type incompatibility errors.

Example: Using Float in Expressions

float num = 4.0f / 2.0f + 3.0f * 5.0f; // Output: 17.0

4.8.6- Double Data Type

double is a 64-bit double-precision floating-point data type, typically used when higher precision is
required.

4.8.7- Double-Precision Format

A double-precision floating-point number consists of:

• 1 sign bit

• 11 exponent bits

• 52 mantissa bits

4.8.8- Declaring Double Variables in Java

The d or D suffix is optional for double values since double is the default type for decimal numbers.

Example: Declaring Double Variables

double price = 987.90;

double taxRate = 0.09;

61
The Complete Java Training Imran Afzal
double total = price + (price * taxRate);

System.out.println("Total: " + total); // Output: Total: 1076.811

4.8.9- Key Characteristics of Double

• Uses 64 bits, providing a higher range and greater precision than float.
• Requires more memory (8 bytes).
• Generally slower than float but provides higher precision, making it suitable for scientific
calculations.

4.8.10- Checking the Range of Double

System.out.println("Range of Double: Min - " + Double.MIN_VALUE + " to Max - " +


Double.MAX_VALUE);

4.8.11- Comparison: Float vs. Double

Feature Float Double

Memory Occupies 4 bytes Occupies 8 bytes

Accuracy Lower accuracy Higher accuracy

Precision Single-precision Double-precision

Keyword Defined using float keyword Defined using double


Used keyword

Default Type Not used as default for floating-point numbers Default for floating-point
numbers

Data Loss No data loss when converting to double Data loss when converting to
float

Use Cases Preferred when memory is constrained and high Used when more precision is
precision is not needed required

62
The Complete Java Training Imran Afzal
Suffix Requires f or F suffix d or D suffix is optional

4.9- Scientific Notations in Float and Double

4.9.1- What is Scientific Notation?

Scientific notation is a way to represent very large or very small numbers in a compact form,
commonly used in mathematics, science, and engineering.

In scientific notation, a number is written as a base number multiplied by a power of 10.

4.9.2- Format

Base×10Exponent\text{Base} \times 10^\text{Exponent}Base×10Exponent

• The base number is always between 1 and 10.

• The exponent represents how many times the base should be multiplied by 10.

Example

• The number 123,456,789 can be written in scientific notation as 1.23456789 × 10^8.

• The distance from Earth to the Sun (93,000,000 miles) in scientific notation is
9.3×10^7 miles.

In Java, scientific notation is used to represent floating-point numbers using the E or e notation
(E stands for exponent). For example, in Java 1.23456789 × 10^8 will be represented as
1.23456789E8.

This notation is especially helpful in scientific calculations, programming, and engineering where
precision with large-scale values is essential.

4.9.3- Examples in JShell

To understand how scientific notation works in Java, let's look at some examples using JShell.

63
The Complete Java Training Imran Afzal
Example 1: Storing Large Numbers in Scientific Notation

double num = 2.5E10;

System.out.println(num);

Explanation

• 2.5E10 is equivalent to 2.5 × 10^10. The value is stored in a double variable. Java automatically
supports scientific notation for floating-point values.

• Here, 2.5 is the mantissa (base number), and 10 is the exponent.

• The output will be:

25000000000.0

Example 2: Multiplying Scientific Notation Numbers

double numOne = 4.56E10 * 5.89E9;

System.out.println(numOne);

Explanation

In this example, 4.56E10 and 5.89E9 are multiplied. Their respective mantissas (4.56 and 5.89) are
multiplied, and their exponents (10 and 9) are added.

• The output will be:

2.68584E20

Example 3: Calculating the Circumference of a Circle

double pi = 3.14;

double radius = 5000;

64
The Complete Java Training Imran Afzal
double circumference = 2 * pi * radius;

System.out.println("Circumference: " + circumference);

Explanation

The formula 2 × π × r is used to calculate the circumference. Here, pi is approximated as 3.14, and
radius is 5000. The result is stored in circumference and printed.

Example 4: Converting Celsius to Fahrenheit

double celsius = 37.5;

double fahrenheit = (9.0 / 5.0) * celsius + 32;

System.out.println("Fahrenheit: " + fahrenheit);

Explanation

To convert temperature, the formula (9.0 / 5.0) * Celsius + 32 is used. The result is stored in fahrenheit
and displayed. Using 9.0 / 5.0 ensures proper floating-point division, avoiding integer truncation.

Concept Example Result

Scientific Notation double num = 2.5E10; 25000000000.0

Multiplication 4.56E10 * 5.89E9 2.68584E20

Circumference of a Circle 2 * pi * radius 31400.0

Celsius to Fahrenheit (9.0 / 5.0) * 37.5 + 32 99.5

Scientific notation helps store and compute large or small numbers efficiently in Java using float
and double.

65
The Complete Java Training Imran Afzal
4.10- Char vs String

4.10.1- Char Data Type

The char data type represents a single 16-bit Unicode character in Java. It can store letters, digits,
punctuation marks, and other symbols.

• Single quotes (' ') are used to denote a char literal.

• char can store 65,536 unique Unicode characters.

• char supports ASCII and Unicode characters.

Example: Creating a char Variable

char ch = 'A';

System.out.println(ch);

Output

Note: Single quotes ' ' are used for char, while double quotes " " are used for String.

4.10.2- Uses of char in Java

The char data type is useful for storing and manipulating individual characters. It is often used
in:

• Processing text data (letters, digits, punctuation).


• Escape sequences (e.g., '\n' for a newline, '\t' for a tab).
• Character-based algorithms (e.g., sorting characters in a word).

66
The Complete Java Training Imran Afzal
4.10.3- Unicode in char

Java supports Unicode, allowing it to store characters from different languages. To use a
Unicode character, write \u followed by a four-digit hexadecimal code.

Example: Unicode Character in char

char uniCode = '\u0043'; // Unicode for 'C'

System.out.println(uniCode);

Output

4.10.4- Assigning Integer Values to char

Since char includes all ASCII characters, it can store integer values corresponding to ASCII
codes.

Example: ASCII to char Conversion

char ascii = 65; // ASCII code for 'A'

System.out.println(ascii);

Output

String Data Type

A String is a sequence of characters stored in double quotes (" ").

• Unlike char, String is not a primitive data type.

• String is a class in Java’s library, making it a non-primitive data type.

67
The Complete Java Training Imran Afzal
• Used for text processing and manipulation.

Example: Creating a String Variable

String company = "Java Programming with Nixware";

System.out.println(company);

Output

Java Programming with Nixware

More Examples with Strings

String s1 = "Hello";

String s2 = "World";

String s3 = "Program";

System.out.println(s1);

System.out.println(s2);

System.out.println(s3);

Output

Hello

World

Program

68
The Complete Java Training Imran Afzal
4.10.5- Concatenating Strings with Other Data Types

The + operator can concatenate String values with other data types.

Example: String Concatenation with Integers

int num1 = 100;

int num2 = 200;

int sum = num1 + num2;

System.out.println("Sum is: " + sum);

Output

Sum is: 300

4.10.6- Summary: char vs String

Feature Char String

Data Type Primitive Non-primitive (Class)

Stores Single character Sequence of characters

Syntax Uses single quotes ('A') Uses double quotes ("Hello")

Memory Usage Requires 2 bytes per Uses more memory (depends on the length
character of the string)

Operations Used in character-based Used in text processing & string


operations manipulation

Supports Yes Yes


Unicode?

69
The Complete Java Training Imran Afzal
Both char and String are essential in Java, and choosing between them depends on the need—
char for single characters, String for text sequences.

4.11- Summarizing Primitive Data Types

This lecture provides a summary of all Java primitive data types covered in previous lectures. Each
type has a specific size, purpose, and usage.

4.11.1- Primitive Data Types Overview

Data Size Range Usage


Type

Byte 8-bit -128 to 127 Compact data storage, binary data


processing.

Short 16- -32,768 to 32,767 Handles larger values than byte, used
bit in binary data.

Int 32- -2,147,483,648 to 2,147,483,647 Most commonly used integer


bit type.

Long 64- -9,223,372,036,854,775,808 to Used for very large numbers.


bit 9,223,372,036,854,775,807

Float 32- ±3.40282347 × 10³⁸ Approximate decimal values, less


bit precision than double.

Double 64- ±1.797 × 10³⁰⁸ High-precision floating-point values,


bit used in scientific calculations.

Char 16- Unicode characters (0 to 65,535) Stores a single character, supports


bit Unicode.

Boolean 1-bit true / false Used in conditional logic.

70
The Complete Java Training Imran Afzal
4.11.2- Detailed Summary of Each Primitive Type

1. byte (8-bit signed integer)

• Stores small whole numbers.


• Range: -128 to 127.
• Used for compact storage in files or network streams.

Example

byte b = 100;

2. short (16-bit signed integer)

• Range: -32,768 to 32,767.

• Slightly larger than byte, often used in binary processing.

Example

short s = 1500;

3. int (32-bit signed integer)

• Most commonly used integer type in Java.

• Range: -2,147,483,648 to 2,147,483,647.

• Used in calculations, loops, and indexing arrays.

Example

int num = 500000;

4. long (64-bit signed integer)

• Used for large integer values beyond int range.

• Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.

71
The Complete Java Training Imran Afzal
Example

long bigNum = 10000000000L; // 'L' suffix required

5. float (32-bit floating-point number)

• Used for approximate decimal values.

• Less precision than double, but uses less memory.

• Range: ±3.40282347 × 10³⁸.

Example

float f = 3.14f; // 'f' suffix required

6. double (64-bit floating-point number)

• Provides higher precision than float.

• Used in scientific calculations where accuracy is essential.

• Range: ±1.797 × 10³⁰⁸.

Example

double d = 3.1415926535;

7. char (16-bit Unicode character)

• Stores single characters, including letters, digits, and symbols.

• Supports Unicode characters from 0 to 65,535.

Example

char letter = 'A';

char unicodeChar = '\u0043'; // Unicode for 'C'

72
The Complete Java Training Imran Afzal
8. boolean (1-bit logical value)

• Stores true or false.

• Used in conditional statements and loops.

Example

boolean isJavaFun = true;

4.11.3- Non-Primitive Data Type: String

Although not a primitive type, String is commonly used for text storage.

• Immutable: Once created, a String cannot be changed.

• Supports concatenation, searching, and formatting.

Example

String message = "Hello, Java!";

4.11.4- Key Differences Between Primitive Data Types

Feature Int double Char boolean

Size 32-bit 64-bit 16-bit 1-bit

Stores Whole numbers Decimal values Single characters true or false

Example int a = 100; double d = 5.75; char c = 'A'; boolean isTrue = true;

Usage Arithmetic Scientific calculations Text processing Conditional logic

By now, you should be familiar with all Java primitive data types.

• int is the most commonly used integer type.


• double is preferred over float for accuracy.
• char supports Unicode, storing characters from different languages.
73
The Complete Java Training Imran Afzal
• boolean is essential for logical conditions.
• String is non-primitive but widely used for text storage.

Understanding primitive data types is fundamental for Java programming.

4.12- What are Operators?

4.12.1- Introduction to Operators in Java

An operator in Java is a symbol that performs a specific operation on one, two, or three operands
and produces a result.

For example, consider the statement:

d = a + b * c;

• Here, d, a, b, and c are operands.

• = (assignment), + (addition), and * (multiplication) are operators.

• Since * has higher precedence than +, b * c is evaluated first, then added to a, and finally
assigned to d.

4.12.2- What is an Operand?

An operand is a value or variable on which an operation is performed.

For example:

5+2

• 5 and 2 are operands.

• + is the operator.

• The result is 7.

Operands can also be variables:

74
The Complete Java Training Imran Afzal
int x = 10;

int y = 5;

int sum = x + y; // sum stores 15

• x and y are operands.

• + is the operator performing addition.

Operands can be of different types, including:

• Integers (int)

• Floating-point numbers (float, double)

• Characters (char)

• Strings (String)

Example

String result = "Hello" + " World";

System.out.println(result);

Output

Hello World

The + operator concatenates (joins) two strings.

4.12.3- What is an Expression?

An expression is a combination of variables, literals, and operators that represents a single value
or operation.

75
The Complete Java Training Imran Afzal
Examples of Expressions:

int sum = 15 + 12; // Expression: 15 + 12

double area = 3.14 * radius * radius; // Expression: 3.14 * radius * radius

Expressions are often used in:

• Assignments (x = 15 + 12;)
• Control statements (if (x > y) { })
• Loops (while (x < 100) { })

4.12.4- Example in JShell

int sum = 6 + 7; // sum stores 13

• 6 + 7 is an expression.

• int sum = is an assignment statement.

• The result (13) is stored in sum.

4.12.5- Comments in Java

A comment is a non-executable text that explains code.

There are two types of comments in Java:

1. Single-Line Comment (//)

// This is a single-line comment

int x = 10; // This comment explains the variable x

2. Multi-Line Comment (/* */)

/*

76
The Complete Java Training Imran Afzal
This is a multi-line comment.

It spans multiple lines.

*/

4.12.6- Arithmetic Operators in Java

Operator Description Example Result

+ Addition 10 + 5 15

- Subtraction 10 - 5 5

* Multiplication 10 * 5 50

/ Division 10 / 2 5

% Modulus (Remainder) 10 % 3 1

Addition (+) Example

int x = 10;

int y = 5;

int sum = x + y;

System.out.println(sum);

Output

15

Subtraction (-) Example

int x = 10;

77
The Complete Java Training Imran Afzal
int y = 5;

int result = x - y;

System.out.println(result);

Output

Multiplication (*) Example

int x = 10;

int y = 5;

int product = x * y;

System.out.println(product);

Output

50

Modulus (%) Example

int x = 10;

int y = 3;

int remainder = x % y;

System.out.println(remainder);

78
The Complete Java Training Imran Afzal
Output

The modulus operator (%) returns the remainder of division.

4.12.7- Working with char Values

Characters (char) are stored as ASCII values in Java.

char firstValue = 'A';


char secondValue = 'B';
System.out.println(firstValue + secondValue);

Output

131

• 'A' has an ASCII value of 65.

• 'B' has an ASCII value of 66.

• 65 + 66 = 131.

To concatenate characters as a string:

System.out.println("" + firstValue + secondValue);

Output

AB

Adding an empty string ("") converts the characters into a string.

Understanding operators and expressions is essential for performing calculations and writing
efficient Java programs.

79
The Complete Java Training Imran Afzal
4.13- Arithmetic Operators

4.13.1- Overview of Java Operators

Java provides various operators for performing calculations and logic-based operations. Before
focusing on arithmetic operators, let's briefly go over the main types of operators in Java:

• Arithmetic Operators – Perform basic mathematical operations like addition, subtraction,


multiplication, division, increment, and decrement.
• Relational Operators – Compare two values (e.g., >, <, ==).
• Logical Operators – Combine boolean expressions (&&, ||, !).
• Assignment Operators – Assign values (=, +=, -=).
• Conditional Operators – Execute different actions based on conditions (? :).
• Bitwise Operators – Perform bit-level operations (&, |, ^).
• Ternary Operator – A shorthand for if-else.
• instanceof Operator – Checks if an object belongs to a specific class.

Now, let's dive deep into arithmetic operators.

4.13.2- Arithmetic Operators in Java

Operator Description Example

+ Addition int sum = a + b;

- Subtraction int diff = a - b;

* Multiplication int product = a * b;

/ Division int quotient = a / b;

% Modulus (Remainder) int remainder = a % b;

++ Increment (Increases by 1) x++ or ++x

-- Decrement (Decreases by 1) x-- or --x

80
The Complete Java Training Imran Afzal
4.13.3- Working with Curly Braces in JShell

Curly braces {} are used to group multiple statements as a single block of code.

Example 1: Addition

int firstVariable = 2;

int secondVariable = 3;

int sum = firstVariable + secondVariable;

System.out.println("Result is: " + sum); // Output: Result is: 5

4.13.4- Increment (++) Operator

The increment operator (++) increases a variable’s value by 1.

There are two types:

• Postfix Increment (x++) – Increments after the expression is evaluated.


• Prefix Increment (++x) – Increments before the expression is evaluated.

Example: Postfix Increment (x++)

int x = 5;

int y = x++; // y is 5, then x becomes 6.

Example: Prefix Increment (++x)

int x = 5;

81
The Complete Java Training Imran Afzal
int y = ++x; // x becomes 6, then y is assigned 6.

4.13.5- Using Compound Assignment (+=) for Increment

Another way to increment a value is with the compound assignment operator (+=).

int x = 10;

int y = 5;

x += y; // x becomes 15

Example: Increment using Compound Assignment

int myNumber = 10;

myNumber += 1; // Equivalent to myNumber = myNumber + 1

System.out.println("Result is: " + myNumber); // Output: Result is: 11

4.13.6- Decrement (--) Operator

The decrement operator (--) decreases a variable’s value by 1.

Postfix Decrement (x--) – Decrements after evaluation.


Prefix Decrement (--x) – Decrements before evaluation.

Example: Postfix Decrement (x--)

int x = 5;

int y = x--; // y is 5, then x becomes 4.

82
The Complete Java Training Imran Afzal
Example: Prefix Decrement (--x)

int x = 5;

int y = --x; // x becomes 4, then y is assigned 4.

Example: Decrement using Compound Assignment (-=)

int myNumber = 10;

myNumber -= 1; // Equivalent to myNumber = myNumber - 1

System.out.println("Result is: " + myNumber); // Output: 9

4.13.7- Multiplication (*=) and Division (/=) using Compound Assignment

The multiplication (*=) and division (/=) operators allow quick updates of variable values.

Example: Multiplication (*=)

int x = 10;

int y = 5;

x *= y; // x becomes 50

System.out.println("Result is: " + x); // Output: 50

83
The Complete Java Training Imran Afzal
Example: Division (/=)

int x = 10;

int y = 5;

x /= y; // x becomes 2

System.out.println("Result is: " + x); // Output: 2

Example with Decimal (double) Values

The compound assignment operator can also be used with floating-point numbers (double).

double pointNumber = 5.5;

pointNumber += 4.5; // pointNumber becomes 10.0

System.out.println("Result is: " + pointNumber); // Output: 10.0

Modulus (%) Operator

The modulus operator (%) returns the remainder of a division.

Example: Modulus Operator (%)

int x = 10;

84
The Complete Java Training Imran Afzal
int y = 3;

int remainder = x % y;

System.out.println(remainder); // Output: 1

• 10 / 3 results in 3 remainder 1.

Arithmetic operators are fundamental to Java programming and help perform essential
mathematical calculations efficiently.

85
The Complete Java Training Imran Afzal
Chapter 5

Integrated Development Environment

86
The Complete Java Training Imran Afzal
5.1- Introduction to IDE

5.1.1- Understanding an Integrated Development Environment (IDE)

An Integrated Development Environment (IDE) is a crucial tool for software development, offering
a comprehensive environment for writing, debugging, and managing code efficiently. Unlike basic
tools such as JShell, an IDE provides advanced features that support professional software
development.

5.1.2- What is an IDE and Why Use It?

An IDE is a software application designed to streamline the coding process by integrating essential
development tools within a single platform. These typically include:

• Code Editor – Assists in writing and formatting code.

• Compiler or Interpreter – Translates code into executable programs.

• Debugger – Helps identify and resolve errors in the code.

• Version Control System – Facilitates tracking changes and collaboration.

• Build Automation Tools – Simplifies the process of compiling and packaging applications.

One of the biggest advantages of using an IDE is that it consolidates these tools into a unified
interface, eliminating the need to switch between different applications. Additionally, features such as
code completion, syntax highlighting, and real-time error checking significantly enhance productivity
and code accuracy.

5.1.3- Popular Java IDEs

There are several IDEs available for Java development, each with unique features. Some widely used
Java IDEs include:

• Eclipse – A powerful, open-source IDE with extensive plugin support.

• NetBeans – A user-friendly IDE known for its robust development tools.

87
The Complete Java Training Imran Afzal
• IntelliJ IDEA – A feature-rich IDE that provides intelligent coding assistance and powerful
debugging tools.

5.1.4- Why Use IntelliJ IDEA?

Among the various Java IDEs, IntelliJ IDEA is widely preferred due to its extensive features and
user-friendly experience. Developed by JetBrains, it is known for its efficiency and intelligent
development tools.

5.1.5- Key Benefits of IntelliJ IDEA

• Intelligent Code Completion – Suggests relevant variable names, methods, and classes,
accelerating coding speed and improving accuracy.

• Advanced Refactoring Tools – Helps restructure and optimize code effortlessly.

• Built-in Debugger – Provides a powerful debugging environment with breakpoints and


variable inspection.

• Integrated Version Control – Seamlessly integrates with Git, Mercurial, and other version
control systems to manage code efficiently.

• Customizable User Interface – Allows developers to modify the interface to match personal
preferences.

• Framework and Library Support – Offers built-in support for popular Java frameworks
such as Spring, Hibernate, and Android development.

• Regular Updates and Improvements – JetBrains actively maintains and enhances IntelliJ
IDEA with frequent updates and new features.

5.1.6- Comparing an IDE to a Text Editor

To understand the importance of an IDE, it is helpful to compare it with a standard text editor:

88
The Complete Java Training Imran Afzal
Feature IDE Text Editor

Feature Set Includes a code editor, compiler, Primarily used for writing code with
debugger, and project management minimal development tools.
tools.

Code Offers code completion, syntax Limited support, often only syntax
Assistance highlighting, and error checking. highlighting.

Development Integrates version control, build Requires external tools for


Tools automation, and project management. advanced development tasks.

Learning Curve More powerful but requires time to Easier to use but lacks advanced
learn. features.

Choosing an IDE over a text editor enhances the development process by making it more efficient,
error-free, and manageable. As Java development progresses, leveraging an IDE like IntelliJ IDEA
ensures smooth and professional coding experience.

5.2- Installing IntelliJ IDEA for Windows

5.2.1- Downloading IntelliJ IDEA

To begin using IntelliJ IDEA for Java development, the first step is downloading the software. Open
a web browser and go to jetbrains.com. Navigate to the "Developer Tools" tab and select "IntelliJ
IDEA." Click the "Download" button. Ensure that the Windows option is selected. Choose the
Community Edition for free access. The Ultimate Edition is a paid version but is not required for
basic development. Once the download begins, wait for the .exe file to complete downloading.

5.2.2- Installing IntelliJ IDEA

After successfully downloading the setup file, proceed with the installation process. Locate the
downloaded .exe file and double-click it to start the installation. When the installation wizard appears,
click "Next" to proceed. Read and accept the license agreement. Choose a destination folder where
IntelliJ IDEA will be installed, then click "Next." Select the components to install, ensuring that
IntelliJ IDEA Community Edition is checked. Click "Install" to begin the installation process.

89
The Complete Java Training Imran Afzal
This may take a few minutes. Once the installation is complete, click "Finish" to close the setup
wizard.

5.2.3- Successful Installation

IntelliJ IDEA is now successfully installed on Windows. The next step is exploring its features and
configuring it for Java development.

5.3- Installing IntelliJ IDEA for CentOS Linux

5.3.1- Setting Up the Environment

To install IntelliJ IDEA on CentOS Linux, begin by logging in as the root user. Enter the root
password to proceed. If you are using a virtual machine, ensure that CentOS is running. For users
connecting from a Windows machine, PuTTY can be used to establish a secure connection to CentOS.

5.3.2- Enabling Snaps on CentOS

Before downloading IntelliJ IDEA, the Snap package manager must be enabled. Start by installing the
Extra Packages for Enterprise Linux (EPEL) repository using the command:
sudo yum install epel-release
This installation may take a few moments to complete.

Next, install the systemd unit that manages Snap communication by running:
sudo yum install snapd

Once installed, enable and start the snapd.socket service:


sudo systemctl enable --now snapd.socket

To enable classic Snap support, create a symbolic link by executing:


sudo ln -s /var/lib/snapd/snap /snap

5.3.3- Installing IntelliJ IDEA

With Snap enabled, IntelliJ IDEA can now be installed directly from the terminal using:
sudo snap install intellij-idea-community --classic

90
The Complete Java Training Imran Afzal
If an error message appears stating "too early for operation, device not yet seeded or device
model not acknowledged," rerun the command. The installation may take some time to complete.

IntelliJ IDEA is now successfully installed on CentOS Linux and ready for use.

5.4- IntelliJ IDEA Functionalities

5.4.1- Setting Up the JDK in IntelliJ IDEA

Before writing Java code, the first step is associating the JDK (Java Development Kit) with IntelliJ
IDEA. The JDK functions as a software development kit, providing essential tools for compiling and
running Java programs. It includes a compiler, debugger, and other resources necessary for
development. If IntelliJ IDEA has not been started yet, open it now. Accept the user agreement, and
proceed with the initial setup options. The Welcome screen provides options to create a new project,
open an existing one, or retrieve a project from a Version Control System (VCS) such as Git.

5.4.2- Creating a New Java Project

Select "New Project" from the Welcome screen. IntelliJ IDEA will detect the JDK installed on the
system. The version number should match the previously downloaded JDK, for example, JDK 17. If
the JDK is not installed, it must be installed before proceeding. Verify that Java and IntelliJ are selected
and that the correct JDK is displayed. This screen will be frequently used when starting new projects.

5.4.3- Customizing IntelliJ IDEA Settings

On the main IntelliJ screen, click "Cancel" and navigate to the "Customize" option to adjust the
settings. Under "Settings," the theme can be changed. The "Document" background is preferred for
reducing eye strain, but the "IntelliJ Light" theme is also an option. Settings can be changed anytime
from this menu.

Under "Editor" and then "General," enable "Auto Import." This feature automatically adds missing
import statements and removes unnecessary ones. Check both "Add unambiguous imports on the
fly" and "Optimize imports on the fly" to streamline coding. Under "Code Folding," disable options
such as "Imports," "File Headers," "One-line Methods," "Closures," and "Generic Constructor and

91
The Complete Java Training Imran Afzal
Method Parameters" to display as much code as possible. In the "Appearance" section, ensure that
"Show Line Numbers" is enabled. Once the settings are configured, click "OK."

5.4.4- Creating and Configuring a Java Project

Return to the Projects page, where all previously created projects will be listed. Click "New Project"
to start a new one. Provide the project name, location, and language. The JDK should be automatically
set to Java. The project name should avoid spaces to prevent file path issues. Pascal case (e.g.,
"MyFirstProgram") can be used for clarity. The default project location is "~\IdeaProjects" on both
Windows and Linux systems, but a custom directory can be selected.

An option called "Sample Code" is available, which provides example files and code snippets. If
starting from scratch, uncheck this option and press "Create." The project structure will appear on the
left panel. The "src" (source) folder is where Java files should be created. Right-click "src," select
"New," then "Java Class." Name the class "HelloWorld" and press Enter.

5.4.5- Writing and Running Java Code in IntelliJ IDEA

The generated class file will open in the editor. Place the cursor at the end of the line and press Enter.
Use the shortcut "psvm" (public static void main) and press the Tab key to generate the main method
automatically. Add System.out.println("Hello World"); inside the main method to print "Hello World"
to the console. Ensure the statement ends with a semicolon.

To execute the program, click the green "Run" button in the top-right corner or the green arrow next
to the class name. The output "Hello World" will appear in the running pane. A successful execution
will show an exit code of 0. IntelliJ IDEA is now set up, and the first Java program has been
successfully executed.

5.5- Hello World in IntelliJ IDEA

5.5.1- Understanding the Structure of a Java Program

The "Hello World" program in IntelliJ IDEA provides an introduction to Java syntax and the structure
of a Java class. When examining the code, IntelliJ automatically applies formatting, including
indentation, color coding, and highlighting to improve readability.

92
The Complete Java Training Imran Afzal
The program contains fundamental Java elements such as the public keyword and the class keyword.

The public Keyword

The public keyword is an access modifier that allows the class, method, or field to be accessible from
anywhere in the program. Access modifiers help define which parts of the code can interact with a
particular class or method.

The class Keyword and Class Structure

The class keyword is used to define a class, which acts as a blueprint for objects, containing both data
(variables) and behavior (methods). Curly braces {} define the boundaries of a class, enclosing its
contents. Everything enclosed in these braces belongs to the class.

The structure of a basic Java class looks like this:

public class MyFirstProgram {

public static void main(String[] args) {

System.out.print("Hello World!");

5.5.2- Understanding the Main Method

Within the class, there is a special method called main. A method is a group of statements that perform
a specific action. The main method serves as the entry point for Java programs, meaning Java starts
execution from this method.

public static void main(String[] args)

93
The Complete Java Training Imran Afzal
The public keyword ensures the method is accessible from anywhere. The static keyword allows the
method to be run without creating an object of the class. The void keyword specifies that the method
does not return any value. The parentheses () in the method declaration indicate that the method can
accept parameters. The main method must be written with precise syntax; even minor changes, such
as using an uppercase "C" in class, will cause IntelliJ IDEA to flag an error. String[] args) is a parameter
that allows command-line arguments to be passed.

5.5.3- Code Blocks and Execution

Curly braces {} are also used to define code blocks within methods. Statements placed within these
braces belong to that method. The statement:

System.out.println("Hello World");

prints text to the console on a single line.

The println method moves the cursor to the next line after printing the message, whereas print does
not.

For practice, modifying the statement to:

System.out.println("Hello, Nixware Company");

and running the code will display the updated message in the console.

5.5.4- Creating a New Java Class

To create another Java class in IntelliJ IDEA, right-click on the src folder, select "New," then "Java
Class." Name the new class MyClass and press Enter. This creates a new Java file named MyClass.java
with the basic class structure:

public class MyClass {

94
The Complete Java Training Imran Afzal
5.5.5- Writing and Running Code in a New Class

Inside MyClass, declare the main method and add print statements:

public static void main(String[] args) {

System.out.print("Hello, Nixware");

System.out.print("American Company");

When executed, both messages will be printed on the same line.

Hello, NixwareAmerican Company

To print the above output on a new line, use println:

System.out.println("Hello, Nixware");

System.out.print("American Company");

The println method adds a newline after the message, moving the cursor to the next line.

5.6- Conditional Statements (if-then) in Java

5.6.1- Understanding Conditional Statements

Conditional statements in Java, commonly known as if-then statements, are used to control the flow
of program execution based on Boolean conditions. These conditions allow a program to make
decisions and perform different actions depending on the input or current state.

The basic structure of an if-then statement involves:

• The if keyword

95
The Complete Java Training Imran Afzal
• A Boolean expression inside parentheses

• A block of code enclosed in curly braces {} that runs only if the condition is true

These are widely used for validating user input, checking variable states, and implementing logical
branching.

5.6.2- Syntax of an if-then Statement

if (condition) {

// block of code to execute if condition is true

• The condition must evaluate to true or false

• If true, the code inside the block is executed

• If false, the block is skipped

5.6.3- Advantages of Using if Statements

• Conditional Execution: Ensures that certain statements execute only when a specified
condition is met, improving efficiency and adaptability.

• Improved Readability: Organizes program logic, making it easier to understand how


different scenarios are handled.

• Better Error Handling: Helps detect invalid inputs and error conditions, preventing
unexpected crashes.

• Enhanced Flexibility: By combining if-else statements, complex decision-making logic can


be implemented.

• Reusability: Allows developers to create reusable functions that handle various conditions,
improving code efficiency.

96
The Complete Java Training Imran Afzal
5.6.4- Limitations of if Statements

• Increased Complexity: Excessive use of nested if statements can make the code difficult to
read and debug.

• Performance Concerns: A large number of if statements can slow down execution, especially
if conditions are complex.

• Repetitive Code: Excessive if conditions may lead to redundant code, making maintenance
more challenging.

• Readability Issues: Overly complex conditions reduce clarity, making it harder for other
developers to understand the logic.

• Overuse in Problem-Solving: Heavy reliance on if statements may prevent developers from


considering alternative solutions such as data structures or different programming techniques.

5.6.5- Practical Examples of if Statements

Example 1: Basic Boolean Condition

boolean flag = true;

if (flag) {

System.out.println("Flag is True");

• flag is assigned the value true.

• The if condition checks flag, which evaluates to true.

• The message "Flag is True" is printed to the console.

Output

Flag is true

97
The Complete Java Training Imran Afzal
Example 2: Checking a Boolean Variable

boolean user = true;

if (user == true) {

System.out.println("It's True");

• The condition user == true explicitly compares the value of user.

• Since user is true, the statement inside the if block is executed.

Output

It's true

Example 3: Comparing an Integer Value

int x = 5;

if (x > 0) {

System.out.println("X is Positive");

• Variable x is assigned 5.

• The condition x > 0 evaluates to true.

• The console outputs "X is Positive".

If the condition was false (e.g., x = -3), the code block would not execute.

98
The Complete Java Training Imran Afzal
Output

x is positive

If the condition evaluates to false, the code inside the if block will not be executed, and the program
will proceed with the next statements after the if block.

5.7- Logical Operators

5.7.1- Introduction to Logical Operators in Java

Logical operators in Java are used to perform logical operations on Boolean expressions. They are
essential for creating compound conditions in control structures like if statements and loops.

Java provides the following logical operators:

• AND (&&): Returns true if both conditions are true.

• OR (||): Returns true if any one of the conditions is true.

• NOT (!): Negates the Boolean value.

These operators are useful for checking multiple conditions in a single statement. They are also critical
for maintaining concise and readable code.

Java also includes bitwise logical operators, like bitwise AND (&) and OR (|), which operate on
individual bits of binary numbers. These are more relevant in low-level operations such as encryption,
compression, or performance-critical code, and will be discussed separately.

5.7.2- Importance of Logical Operators

• Allow combining multiple conditions into a single, efficient check.

• Improve code readability and maintainability.

• Useful in control flow: if, else if, while, and for loops.

• Help reduce unnecessary computations by short-circuiting evaluation.

99
The Complete Java Training Imran Afzal
• Improve performance and logic structure in complex programs.

5.7.3- Limitations of Logical Operators

• Overusing them in one line can reduce code readability.

• Incorrect usage may lead to logical bugs.

• Can slow performance in complex evaluations.

• Can confuse beginners if Boolean logic is not well understood.

5.7.4- Example 1: Simple Condition Using Integer

int age = 18;

if (age > 18) {

System.out.println("Condition is True");

This condition checks whether the age variable is greater than 18. Since age is 18, the condition is
false, and nothing is printed.

5.7.5- Example 2: String Equality Check

String gender = "male";

if (gender.equals("male")) {

System.out.println("Condition is True");

100
The Complete Java Training Imran Afzal
Here, .equals() is used instead of == to compare the string content. The gender value is "male", so
the condition is true and prints:
Condition is True

5.7.6- Example 3: Using Logical AND (&&)

if (age > 18 && gender.equals("male")) {

System.out.println("Eligible");

This condition combines two expressions:

• age > 18 (false)

• gender.equals("male") (true)

Since one of the conditions is false, the overall result is false, and the statement is not executed.

5.7.7- Example 4: Multiple Conditions and Assignment

int a = 10, b = 20, c = 20, d = 0;

if (a < b && b == c) {

d = a + b + c;

System.out.println("The sum is: " + d);

In this case:

101
The Complete Java Training Imran Afzal
• a < b is true (10 < 20)

• b == c is true (20 == 20)

Both conditions are true, so the code inside the if block runs:

• d = 10 + 20 + 20 = 50

• Output: The sum is: 50

5.7.8- Summary of Logical Usage

Condition Result Output

age > 18 false (no output)

gender.equals("male") true Condition is True

age > 18 && gender.equals("male") false (no output)

a < b && b == c true The sum is: 50

Using logical operators allows combining checks efficiently. They’re fundamental for building
intelligent, branching logic in Java programs.

5.8- OR and NOT Logical Operators

5.8.1- Understanding the OR Logical Operator

The OR (||) logical operator in Java is used to evaluate multiple conditions and returns true if at least
one of the conditions is met. It is commonly used in control flow statements such as if-else and while
loops to make decisions based on multiple conditions.

102
The Complete Java Training Imran Afzal
Example: Checking if Either Condition is True

Create a class file named OrOperator and implement the following code:

public class OrOperator {

public static void main(String[] args) {

int x = 4, y = 5;

if (x > 4 || y > 4) {

System.out.println("x or y is greater than 4");

Output

x or y is greater than 4

In this case, y > 4 is true, so the if statement executes even though x > 4 is false. The OR operator
ensures the condition evaluates to true if at least one of the conditions is met.

Example: Checking Exam Score Range

Create a class file named OrOperator1 and implement the following:

public class OrOperator1 {

public static void main(String[] args) {

103
The Complete Java Training Imran Afzal
double examScore = 85;

if (examScore >= 60 || examScore <= 100) {

System.out.println("The student has passed the exam.");

Output

The student has passed the exam.

Since examScore >= 60 is true, the if block executes.

Example: Checking if a Number is Even or Divisible by 3

Create a class file named OrOperator2 and implement the following:

public class OrOperator2 {

public static void main(String[] args) {

int num = 6;

if (num % 2 == 0 || num % 3 == 0) {

System.out.println("The number is even or divisible by 3.");

104
The Complete Java Training Imran Afzal
}

Output

The number is even or divisible by 3.

Since num % 2 == 0 is true, the OR condition ensures the statement executes.

5.8.2- Advantages of the OR Operator

• Concise and easy to read – Multiple conditions can be checked in a single line.

• Flexibility – Allows decisions based on any true condition.

• Improved performance – Short-circuiting stops evaluation once a true condition is found.

• Error handling – Helps validate multiple conditions at once.

5.8.3- Limitations of the OR Operator

• Complexity – Checking many conditions can make the code harder to read.

• Reduced performance – Evaluating unnecessary conditions may slow execution.

• Lack of granularity – The OR operator only indicates if a condition is true, not which one
specifically.

5.8.4- Understanding the NOT Logical Operator

The NOT (!) operator is a unary operator that negates a Boolean value. It is used to invert conditions,
often simplifying control flow and improving code readability.

Example: Checking if a Number is Odd

Create a class file named NotOperator and implement the following:

public class NotOperator {

public static void main(String[] args) {

105
The Complete Java Training Imran Afzal
int num = 7;

if (!(num % 2 == 0)) {

System.out.println("The number is odd.");

Output

The number is odd.

The condition num % 2 == 0 evaluates to false, but the NOT operator (!) inverts it to true, so the if
block executes.

5.8.5- Advantages of the NOT Operator

• Simplifies control flow – Helps avoid complex conditions.

• Improved readability – Clearly indicates negation in logic.

• Error handling – Useful for checking conditions and handling errors efficiently.

• Reusability – Can be applied to Boolean-returning functions.

5.8.6- Limitations of the NOT Operator

• Complexity – Can make code harder to understand when combined with other logical
operators.

• Confusing for beginners – Requires a clear understanding of Boolean logic.

• Potential performance impact – Negating many conditions can slow execution.

106
The Complete Java Training Imran Afzal
Both OR (||) and NOT (!) operators are essential for implementing decision-making logic
efficiently in Java programs.

5.9- Assignment Operators in Java

5.9.1- Understanding Assignment Operators

Assignment operators in Java are used to assign values to variables. The basic assignment operator (=)
assigns the value on the right side to the variable on the left side. For example:

int x = 5;

This assigns the value 5 to the variable x.

5.9.2- Common Assignment Operators in Java

1. Equal (=) – Assigns a value to a variable.

2. Plus Equal (+=) – Adds the right-side value to the variable and assigns the result.

3. Minus Equal (-=) – Subtracts the right-side value from the variable and assigns the result.

4. Multiply Equal (*=) – Multiplies the variable by the right-side value and assigns the result.

5. Divide Equal (/=) – Divides the variable by the right-side value and assigns the result.

6. Modulus Equal (%=) – Computes the remainder of division and assigns the result.

5.9.3- Advantages of Using Assignment Operators

• Conciseness – Combines assignment and operation in one step, improving readability.

• Improved Readability – x += 10; is clearer than x = x + 10;.

• Increased Efficiency – Reduces redundant operations.

• Chaining – Allows multiple operations in a single line.

• Reduced Errors – Prevents accidental mistakes in variable updates.

• Avoids Unnecessary Variables – Enhances efficiency by minimizing extra variable usage.

107
The Complete Java Training Imran Afzal
5.9.4- Practical Examples of Assignment Operators

Example 1: Basic Assignment Operators

Create a class file named AssignOperator and add the following code:

public class AssignOperator {

public static void main(String[] args) {

int x = 10;

x += 5; // Equivalent to x = x + 5;

System.out.println("Updated value of x: " + x);

Output

Updated value of x: 15

Example 2: Difference Between = and ==

Create a class file named ComparisonExample and implement the following code:

public class ComparisonExample {

public static void main(String[] args) {

int x = 5, y = 5;

if (x == y) {

108
The Complete Java Training Imran Afzal
System.out.println("Both Are Equal");

Output

Both Are Equal

The == operator compares the values of x and y. Since both are equal, the condition evaluates to true,
and the message is printed.

Example 3: Misuse of = Instead of == in Condition

Create a class file named AssignmentVsComparison and implement the following:

public class AssignmentVsComparison {

public static void main(String[] args) {

boolean z = true;

if (z == false) { // Correct use of '==' for comparison

System.out.println("This will not be printed.");

109
The Complete Java Training Imran Afzal
Output
(No output)

Here, z = false assigns false to z, and since false is returned, the if condition never executes. This
demonstrates why = should not be used inside a condition when a comparison is intended.

Assignment operators simplify operations while improving readability and efficiency but
distinguishing between = (assignment) and == (comparison) is crucial to avoid logical errors in Java
programs.

5.10- Comparison Operators in Java

5.10.1- Understanding Comparison Operators

Comparison operators in Java are used to compare two values and return a Boolean result (true or
false). They play a critical role in decision-making within programs and are commonly used in
conditional statements, loops, searching, sorting, and debugging.

5.10.2- Common Comparison Operators in Java

1. Equal to (==) – Checks if two values are equal.

2. Not equal to (!=) – Checks if two values are different.

3. Greater than (>) – Checks if the left value is greater than the right value.

4. Less than (<) – Checks if the left value is smaller than the right value.

5. Greater than or equal to (>=) – Checks if the left value is greater than or equal to the right
value.

6. Less than or equal to (<=) – Checks if the left value is smaller than or equal to the right
value.

5.10.3- Use Cases of Comparison Operators

• Conditional Statements – Used in if-else and switch cases to make decisions.

110
The Complete Java Training Imran Afzal
• Loop Control – Used in while, do-while, and for loops to define iteration conditions.

• Searching and Sorting – Helps compare and arrange data efficiently.

• Object Comparison – Used to check properties of objects.

• Debugging and Testing – Ensures that a program behaves as expected.

5.10.4- Advantages of Using Comparison Operators

• Simplicity – Easy to use and understand, making code more readable.

• Flexibility – Works with different data types (integers, floats, strings, objects).

• Conditional Control Flow – Allows for dynamic decision-making.

• Efficient Searching and Sorting – Essential for data structure manipulation.

5.10.5- Limitations of Comparison Operators

• Limited Functionality – Only handles basic comparisons; complex conditions require


additional logic.

• Inconsistencies Across Data Types – May not work as expected when comparing different
types.

• Overuse Can Reduce Readability – Excessive nested conditions make code harder to
maintain.

• Restricted Object Comparison – Only works on exposed properties.

5.10.6- Example 1: Determine the Largest Value

int x = 5, y = 3, z = 4;

if (x > y && x > z) {

System.out.println("x is the largest number");

111
The Complete Java Training Imran Afzal
}

This if statement checks two conditions:

• x > y → true

• x > z → true

The && (AND) operator ensures both conditions must be true for the block to execute. Since both
are true, the message “x is the largest number” is printed.

5.10.7- Example 2: Assign Grade Based on Score

int score = 85;

String grade;

if (score >= 80 && score <= 95) {

grade = "A";

System.out.println(grade);

In this example:

• score >= 80 → true

• score <= 95 → true

Both conditions are satisfied, so "A" is assigned to grade, and "A" is printed.

112
The Complete Java Training Imran Afzal
This pattern ensures grades are only assigned if the score falls within a defined inclusive range. If the
score were below 80 or above 95, the block would be skipped.

5.10.8- Summary

Scenario Code Snippet Result

Find largest number if (x > y && x > z) Prints: x is the largest number

Grade assignment by range if (score >= 80 && score <= 95) Prints: A

Comparison operators provide precise control over program logic, making them foundational in any
Java application.

Let me know if you'd like this content compiled with others into a complete eBook or exported in
document format.

5.11- Ternary Operator in Java

5.11.1- Understanding the Ternary Operator

The ternary operator in Java is a concise alternative to an if-else statement. It is the only operator
in Java that works with three operands, making it a shorthand for conditional expressions.

Syntax

condition ? expression1 : expression2;

• If the condition evaluates to true, expression1 is executed.

• If the condition evaluates to false, expression2 is executed.

This makes it a shorter alternative to the traditional if-else structure, useful for assigning values based
on a condition.

113
The Complete Java Training Imran Afzal
5.11.2- Practical Examples of the Ternary Operator

Example 1: String Comparison with Ternary Operator

String flag = "American";

boolean isAmerican = (flag.equals("American")) ? true : false;

System.out.println(isAmerican);

In this example:

• The condition checks whether the value of flag is "American".

• If true, isAmerican is assigned true.

• The result is printed:


Output: true

Note: It’s important to use .equals() when comparing string content instead of ==.

Example 2: Age-Based Category

int age = 20;

String status;

status = (age < 18) ? "Under Age" : "Senior";

System.out.println(status);

Here:

• The condition checks if age is less than 18.

• Since age is 20, the condition is false.

114
The Complete Java Training Imran Afzal
• status is assigned "Senior".

• Output: Senior

Example 3: Checking Pass or Fail

int marks = 50;

String result;

result = (marks > 40) ? "Pass" : "Fail";

System.out.println(result);

• The condition checks whether marks are greater than 40.

• As marks is 50, the condition is true.

• "Pass" is assigned to result.

• Output: Pass

Example 4: Number Sign Check

int number = 24;

String result;

result = (number > 0) ? "Positive Number" : "Negative Number";

System.out.println(result);

• The condition checks whether the value of number is greater than 0.

• Since 24 is positive, "Positive Number" is assigned.

• Output: Positive Number

115
The Complete Java Training Imran Afzal
5.11.3- Advantages of the Ternary Operator

• Concise – Reduces multiple lines of if-else into a single line.

• Readable – Improves code clarity for simple conditions.

• Efficient – Executes faster than an if-else block in many cases.

Summary of Examples

Scenario Condition Output

Nationality Check flag.equals("American") true

Age Category age < 18 Senior

Pass/Fail Evaluation marks > 40 Pass

Positive/Negative Check number > 0 Positive Number

The ternary operator streamlines conditional assignments and makes your code more concise and
readable.

5.12- Operators Exercises in Java

5.12.1- Understanding Operators in Java

Java provides various operators that perform different operations within a program. These include:

• Arithmetic Operators – Perform basic mathematical operations like addition, subtraction,


multiplication, division, and modulus.

• Comparison Operators – Compare values and return a Boolean result (true or false).

• Logical Operators – Combine multiple conditions using AND (&&), OR (||), and NOT (!).

• Assignment Operators – Assign values to variables (=, +=, -=, *=, /=, %=).

116
The Complete Java Training Imran Afzal
• Ternary Operator – Provides a shorthand way of writing an if-else condition using the
format:

condition ? expression1 : expression2;

5.12.2- Exercise 1: Checking if a Number is Within a Range

Create a class file named ChallengeOne and implement the following code:

public class challangeOne {

public static void main(String[] args) {

int num = 15;

if (num > 10 && num < 20) {

System.out.print("The number is within the range of 10 to 20.");

Output

The number is within the range of 10 to 20.

This program uses the logical AND operator to verify whether num lies strictly between 10 and 20.
Since 15 satisfies both conditions, the message is printed.

5.12.3- Exercise 2: Using the OR Operator to Check for Multiple Values

Create a class file named ChallengeTwo and implement the following code:

117
The Complete Java Training Imran Afzal
public class challangeTwo {

public static void main(String[] args) {

int val = 5;

if (val == 3 || val == 5 || val == 7) {

System.out.println("The value is 3, 5 or 7.");

This example demonstrates the logical OR operator (||) to check whether a variable matches any
one of several values. Since val is 5, the second condition is true and the message is displayed.

Output

The value is 3, 5, or 7.

5.12.4- Exercise 3: Checking if One Variable is Less Than or Equal to Another

Create a class file named ChallengeThree and implement the following code:

public class challangeThree {

public static void main(String[] args) {

int num1 = 10;

int num2 = 15;

118
The Complete Java Training Imran Afzal
if (num1 <= num2) {

System.out.println("num1 is less than or equal to num2.");

Output

num1 is less than or equal to num2.

This uses the less than or equal to (<=) comparison operator. Since 10 <= 15, the condition
evaluates to true.

5.12.5- Exercise 4: Nested Logical Conditions with Grouping

Create a class file named ChallengeFour and implement the following code:

public class challangeFour {

public static void main(String[] args) {

int x = 25;

int y = 30;

int z = 35;

if (x > 20 && (y < 30 || z > 30)) {

119
The Complete Java Training Imran Afzal
System.out.println("The condition is true.");

This challenge uses nested conditions:

• x > 20 is true

• (y < 30 || z > 30) is true (because z > 30 is true)

Since both the main condition and the grouped condition are true, the message is printed.

Output

The condition is true.

5.12.6- Exercise 5: Using the Ternary Operator

Create a class file named ChallengeFive and implement the following code:

public class challangeFive {

public static void main(String[] args) {

int a = 5;

int b = 10;

String result = (a > b) ? "a is greater than b" : "a is less than b";

System.out.println(result);

120
The Complete Java Training Imran Afzal
}

Here, the ternary operator checks whether a is greater than b. Since 5 > 10 is false, the string "a is
less than b" is assigned to result and printed.

These exercises demonstrate how Java operators work in practical scenarios. Understanding logical,
comparison, assignment, and ternary operators enables efficient decision-making in programming.

121
The Complete Java Training Imran Afzal
Chapter 6

Expressions and Statements

122
The Complete Java Training Imran Afzal
6.1- Keywords and Expressions in Java

6.1.1- Understanding Keywords in Java

Java has 51 reserved keywords as of JDK 17, each having a specific predefined meaning. Examples
include int, double, class, and if. These cannot be used as names for variables, methods, or classes.

Additionally, Java includes contextual keywords and literals like true, false, and null, which, although
not reserved words, also cannot be used as identifiers.

Example of an invalid variable name:

double double = 2.0; // Invalid: "double" is a keyword

Corrected version:

double double1 = 2.0; // Valid

IDEs such as IntelliJ IDEA will highlight such issues with red underlines, making them easy to identify
and correct.

6.1.2- Understanding Expressions

An expression is a combination of literals, variables, operators, or method calls that evaluates to a


single value. Expressions are used for calculations, assignments, comparisons, and more.

Example:

double miles = 70 * 0.621371;

Here, 70 * 0.621371 is the expression. The full line is a complete statement because it ends with a
semicolon and performs an assignment.

123
The Complete Java Training Imran Afzal
6.1.3- Statements and Code Blocks

A statement is a complete instruction in Java that may declare a variable, assign a value, or call a
method. A group of statements can be wrapped in curly braces {} to form a block of code, which
defines scope and logical grouping.

6.1.4- Practical Code Example 1: Unit Conversion Using Expressions

public class ExperState {

public static void main(String[] args) {

double double1 = 2.0;

double miles = 70 * 0.621371;

System.out.println("Miles: " + miles);

Here:

• 70 * 0.621371 is the expression.

• double miles = ...; and System.out.println(...); are statements.

6.1.5- Practical Code Example 2: Expression Inside Conditional Statement

public class ExperState1 {

public static void main(String[] args) {

int x = 5;

124
The Complete Java Training Imran Afzal
if (x > 3) {

x = x + 10;

System.out.println("Updated x: " + x);

In this example:

• x > 3 is an expression that evaluates to true.

• x = x + 10 is both an expression and a statement.

• The entire if structure is a code block containing two statements.

6.1.6- Summary of Terms

Term Description

Keyword Reserved words like int, class, public

Expression A unit of code that evaluates to a single value

Statement A complete command that ends with a semicolon

Code Block A group of statements wrapped in {}

This concept builds a foundation for writing valid Java programs by understanding how keywords,
expressions, and statements work individually and together.

125
The Complete Java Training Imran Afzal
6.2- Whitespace and Indentation in Java

6.2.1- Understanding Whitespace in Java

Whitespace in Java consists of spaces, tabs, and newline characters that help improve code
readability. Although whitespace is ignored by the Java compiler, it is essential for structuring and
organizing code effectively.

Code Example and Organization

int number = 100;

System.out.println("This is the Nixware company and its services include consulting");

System.out.println("This is the Nixware company" +

"and its services" +

" include consulting");

int number2 = 200; System.out.println("Hello World");

The example begins with a simple variable declaration int number = 100;, which demonstrates a valid
Java statement. A statement includes a complete line of executable code, starting with a data type and
ending with a semicolon. In this case, it involves assigning the value 100 to the variable number.

Java statements can also include output commands like System.out.println(), which display messages
on the screen. This type of statement can be written on a single line or split across multiple lines using
the string concatenation operator +. When splitting long print statements, Java treats it as a single
continuous statement as long as it doesn’t end with a semicolon.

126
The Complete Java Training Imran Afzal
Java also allows multiple statements on one line, separated by semicolons. For example:

int number2 = 200; System.out.println("Hello World");

This is valid Java syntax but is discouraged because it can hinder readability. It’s better to write one
statement per line or break longer statements into more manageable pieces.

6.2.2- Types of Whitespace in Java

• Horizontal Whitespace: Spaces and tabs that separate words or tokens in a line of code, used
for aligning variable declarations and method arguments.

Example

int x = 10;

• Vertical Whitespace: Newline characters and line breaks that separate blocks of code, making
it easier to distinguish different sections.

Though adding extra whitespace does not change program behavior, too much or poorly placed
whitespace can confuse readers. Consistent use of whitespace improves alignment and clarity across
the codebase.

6.2.3- Indentation in Java

Indentation refers to how code is visually aligned to indicate block structure. It is typically applied
using tab or space characters and is especially important when working with nested structures like
loops, conditionals, or method blocks.

Without indentation, code becomes harder to follow. Consider the following unindented and indented
versions:

Without Indentation:

public class Example{

127
The Complete Java Training Imran Afzal
public static void main(String[] args){

int x=10;

if(x>5){

System.out.println("X is greater than 5");

With Proper Indentation:

public class Example {

public static void main(String[] args) {

int x = 10;

if (x > 5) {

System.out.println("X is greater than 5");

}
Indentation makes it easier to visually parse code structure, especially in large programs. Tools like
IntelliJ provide a Reformat Code option under the "Code" menu, which helps enforce standard
formatting rules across the project.

128
The Complete Java Training Imran Afzal
By maintaining proper whitespace and indentation, Java code becomes easier to understand, debug,
and maintain, supporting clean and professional coding practices.

6.3- If-Then-Else Control Statement in Java

6.3.1- Understanding If-Then and If-Then-Else Statements

The if-then and if-then-else control statements in Java are essential for decision-making. They allow
programs to execute specific blocks of code based on certain conditions.

The if-then Statement

The if-then statement executes a block of code only when the specified condition is true. The syntax
includes the if keyword followed by the condition in parentheses, and the block of code to execute
within curly braces.

int num = -5;

if(num > 0) {

System.out.println("Number is Positive");

else {

System.out.println("Number is Negative");

In the above example, the variable num is assigned -5. Since -5 is not greater than 0, the condition
evaluates to false, and the else block executes. Therefore, the output is "Number is Negative".

129
The Complete Java Training Imran Afzal
The if-then-else Statement

The if-then-else statement provides two execution paths: one when the condition is true and another
when it is false. This is useful when a fallback action is required.

int num = 10;

if(num >= 0 && num <= 10) {

System.out.println("The Number is within the Range");

else {

System.out.println("The Number is not within the Range");

Here, num is set to 10. The condition checks if the number lies between 0 and 10 (inclusive). Since it
does, the if block runs and prints "The Number is within the Range".

6.3.2- Checking for Vowels and Consonants

The if-then-else structure can also be used to check character values. In this example, a letter is checked
to determine if it's a vowel.

int letter = 'a';

if(letter == 'a' || letter == 'e' || letter == 'i' || letter == 'o' || letter == 'u') {

System.out.println("The Letter is a Vowel");

130
The Complete Java Training Imran Afzal
else {

System.out.println("The Letter is a Consonant");

Since the value assigned to letter is 'a', which is one of the vowels, the output will be "The Letter is a
Vowel".

6.3.3- Ladder if-else Statement

A ladder if-else statement is used to test multiple conditions in sequence. Once a condition is found
to be true, its corresponding code block runs, and the rest are skipped.

int score = 65;

if(score >= 90) {

System.out.println("Grade A");

else if(score >= 80) {

System.out.println("Grade B");

else if(score >= 70) {

System.out.println("Grade C");

131
The Complete Java Training Imran Afzal
else if(score >= 60) {

System.out.println("Grade D");

else {

System.out.println("Grade F");

In this example, since the score is 65, the fourth condition score >= 60 evaluates to true, and "Grade
D" is printed.

These structures make programs dynamic and capable of handling varied user inputs or conditions
efficiently.

6.4- Ladder If-Then-Else Exercises in Java

6.4.1- Age Category Example

A ladder if-else statement is useful when multiple conditions need to be checked sequentially. In
this example, a person's age is evaluated to determine their category: child, teenager, adult, middle-
aged adult, or senior citizen. The variable age is assigned a value of 30. A series of conditions are
checked in sequence:

int age = 30;

if(age < 18){

System.out.println("Child");

132
The Complete Java Training Imran Afzal
else if(age >= 18 && age <= 21){

System.out.println("Teenager");

else if(age >= 21 && age <= 65){

System.out.println("Adult");

else {

System.out.println("Senior Citizen");

Since age is 30, the third condition matches, and the output is "Adult".

6.4.2- Day of the Week Message

A ladder if-else structure can also evaluate a String representing a day of the week to print custom
messages. The variable day is set to "Tuesday".

String day = "Tuesday";

if(day == "Monday") {

System.out.println("Monday Blues");

else if(day == "Tuesday") {

133
The Complete Java Training Imran Afzal
System.out.println("Tuesday");

else if(day == "Wednesday") {

System.out.println("Hump Day");

else if(day == "Thursday") {

System.out.println("Almost Friday");

else if(day == "Friday") {

System.out.println("TGIF");

else if(day == "Saturday") {

System.out.println("Weekend");

else if(day == "Sunday") {

System.out.println("Weekend Hangover");

134
The Complete Java Training Imran Afzal
else {

System.out.println("Invalid Day");

Since the value is "Tuesday", the output is "Tuesday". Note: For accurate string comparisons in Java,
.equals() should be used instead of ==.

6.4.3- Tax Bracket Determination

This example determines a tax bracket based on a given salary using ladder if-else statements.

int salary = 80000;

if(salary < 18200) {

System.out.println("Tax Bracket:No Tax");

else if(salary < 37000) {

System.out.println("Tax Bracket:Low");

else if(salary >= 37000 && salary <= 90000) {

System.out.println("Tax Bracket:Medium");

else if(salary >= 90000 && salary <= 180000) {

135
The Complete Java Training Imran Afzal
System.out.println("Tax Bracket:High");

else {

System.out.println("Tax Bracket:Very High");

Since the salary is 80000, the program prints "Tax Bracket:Medium".

6.4.4- BMI (Body Mass Index) Evaluation

This program calculates BMI and categorizes it using a ladder if-else structure.

double weight = 70, height = 1.7;

double bmi = (weight / height * height);

if(bmi < 18.5) {

System.out.println("Under Weight");

else if(bmi >= 18.5 && bmi <= 25) {

System.out.println("Normal Weight");

else if(bmi >= 25 && bmi <= 30) {

136
The Complete Java Training Imran Afzal
System.out.println("Over Weight");

else {

System.out.println("Obese");

Given the weight and height, the calculated BMI falls in the "Normal Weight" category.

6.4.5- Credit Score Evaluation

A credit score is evaluated to determine the creditworthiness of an individual.

int creditScore = 650;

if(creditScore >= 800) {

System.out.println("Excellent");

else if(creditScore >= 700 && creditScore <= 800) {

System.out.println("Good");

else if(creditScore >= 600 && creditScore <= 700) {

System.out.println("Fair");

137
The Complete Java Training Imran Afzal
}

else {

System.out.println("Poor");

Since the credit score is 650, the result is "Fair". This use of ladder if-else helps classify values into
categories efficiently and clearly.

6.5- Nested If-Then-Else Concept in Java

6.5.1- Understanding Nested If-Then-Else Statements

A nested if-then-else statement is an if-then-else structure placed inside another if-then-else statement.
The inner if statement is executed only when the outer if condition is true. This allows more precise
decision-making based on multiple conditions.

The structure follows this syntax:

• The outer if statement checks condition1.

• If condition1 is true, the inner if statement checks condition2.

• If condition2 is true, a block of code executes. Otherwise, the inner else executes.

• If condition1 is false, the outer else executes.

6.5.2- Example: Checking if a Number is Positive and Even

This example uses a nested if structure to determine whether a number is both positive and even.

int number = 5;

if(number > 0) {

138
The Complete Java Training Imran Afzal
if(number % 2 == 0) {

System.out.println("Number is Positive and Even");

else {

System.out.println("Number is not Positive but Even");

else {

System.out.println("Number is not Positive");

In this program, the outer condition checks if the number is greater than 0. If true, it proceeds to the
inner condition that verifies if the number is divisible by 2 (even). Since the number is 5, which is
greater than 0 but not divisible by 2, the output will be:
"Number is not Positive but Even"
(Note: The message is slightly inaccurate; it should state the number is positive but not even.)

6.5.3- Example: Offering a Discount Based on Purchase Amount and Membership Status

This example applies nested conditions to decide the discount percentage based on two factors:
purchase amount and membership status.

int purchaseAmount = 120;

String isMember = "yes";

139
The Complete Java Training Imran Afzal
if(purchaseAmount > 100) {

if(isMember == "yes") {

System.out.println("You are eligible for 10% discount");

else {

System.out.println("You are eligible for a 5% discount");

else {

System.out.println("You are not eligible for a discount");

The outer if checks if the purchaseAmount exceeds 100. If so, it evaluates the isMember variable. If
the customer is a member, they receive a 10% discount. Otherwise, a 5% discount is applied. If the
purchase amount is 100 or less, no discount is provided.

In this example, since the purchase amount is 120 and the customer is a member (isMember ==
"yes"), the output is:
"You are eligible for 10% discount"

Note: For accurate string comparison in Java, .equals() should be used instead of ==. For example:

if(isMember.equals("yes"))

This ensures proper evaluation of string values.

6.6- Methods in Java


6.6.1- Understanding Methods in Java
A method in Java is a reusable block of code that performs a specific task. Methods can be invoked

140
The Complete Java Training Imran Afzal
(called) multiple times, allowing code reusability and better structure. Each method belongs to a class
and includes the following parts:

• Modifiers: Keywords such as public, private, or static that define the method’s access level
and behavior.

• Return Type: The type of value the method returns. If the method doesn’t return anything,
the return type is void.

• Method Name: A meaningful name written in camelCase, such as printString.

• Parameter List: Variables passed into the method inside parentheses. A method can also take
no parameters.

• Method Body: The block of code enclosed in curly braces {} that defines what the method
does.

6.6.2- Java Main Method

The main method is the entry point for any Java application and follows this syntax:

public static void main(String[] args) { }

• public: The method is accessible from anywhere.

• static: It belongs to the class rather than an instance.

• void: It returns no value.

• main: The method name.

• String[] args: An array to hold command-line arguments.

6.6.3- Example 1: Non-Parameterized Method

This example defines a method that doesn’t take any parameters.

public class MyMethodClass {

141
The Complete Java Training Imran Afzal
public static void printString() {

System.out.println("Hello World");

public static void main(String[] args) {

printString();

The printString method is defined with no parameters and simply prints "Hello World". It is then
called from the main method using its name followed by parentheses.

6.6.4- Example 2: Parameterized Method

This example defines a method that accepts two parameters.

public class ParaMethodClass {

public static void Modifiers(int number, String name) {

System.out.println("First Parameter Value is: " + number);

System.out.println("Second Parameter Value is: " + name);

142
The Complete Java Training Imran Afzal
public static void main(String[] args) {

Modifiers(1, "Nixware");

Here, the method Modifiers takes two parameters: an int and a String. When called with the arguments
1 and "Nixware", it prints their values to the console.

6.6.5- Method Naming and Formatting Conventions

• camelCase: Use lowercase for the first word and uppercase for subsequent words (e.g.,
calculateTotal).

• Descriptive Names: Method names should reflect what the method does.

• Avoid Special Characters: Method names should not contain spaces or special symbols.

• Consistent Indentation: Code inside the method body should be indented for readability.

• Descriptive Parameters: Use meaningful names for method parameters, matching the data
they represent.

Using methods helps organize and modularize your Java programs, making them easier to read,
maintain, and debug.

6.7- Return Statement in a Method

6.7.1- Understanding Methods That Return Values

A method in Java can return a value by specifying a return type before the method name. The return
type defines the kind of data the method will send back to the caller. A return statement is required
to return the value from the method.

143
The Complete Java Training Imran Afzal
6.7.2- Example: Adding Two Numbers Using a Void Method

To perform addition using a method that does not return a value, define a method with void as its
return type. The method accepts two parameters, performs the addition, and prints the result.

public class MethodExample {

public static void AddNumbers(int num1, int num2) {

int sum = num1 + num2;

System.out.println("Sum is: " + sum);

public static void main(String[] args) {

AddNumbers(23, 30);

This method calculates the sum of two integers and displays the result using System.out.println. It
does not return the result to the calling code.

6.7.3- Example: Calculating the Area of a Circle (Void Method)

In this example, the area is calculated and printed inside the method. It uses void since it doesn’t return
a value.

144
The Complete Java Training Imran Afzal
public class MethodExample1 {

public static void calculateArea(double radius, double pi) {

double result = radius * pi;

System.out.println("Area of circle is: " + result);

public static void main(String[] args) {

calculateArea(10, 3.14);

The method calculateArea accepts radius and pi as parameters and prints the computed area directly.

6.7.2- Understanding the Return Statement

Instead of printing results inside the method, you can return the computed value using the return
keyword. This allows the caller to receive and use the result.

public class MethodExample1 {

public static double calculateArea(double radius, double pi) {

145
The Complete Java Training Imran Afzal
double result = radius * pi;

return result;

public static void main(String[] args) {

double area = calculateArea(10, 3.14);

System.out.println("Area of circle is: " + area);

In this modified method, calculateArea returns a double value. The caller captures the result in a
variable named area and then prints it.

6.7.3- Example: Summing Two Numbers Using a Return Statement

This example demonstrates how to define a method that adds two integers and returns the result using
the return statement.

public class MethodExample3 {

public static int Add(int a, int b) {

return a + b;

146
The Complete Java Training Imran Afzal
}

public static void main(String[] args) {

int sum = Add(100, 200);

System.out.println(sum);

The method Add returns the result of the addition. The returned value is stored in the sum variable
and then printed.

6.7.4- Summary of Return Types and Usage

• void: Used when the method performs an action but does not return a value.

• int, double, etc.: Used when the method calculates and returns a specific type of result.

• A method with a return type must include a return statement with a matching value or
expression.

• The returned value must be handled by the caller, typically by assigning it to a variable or using
it directly in another expression.

6.8- Summarizing Methods in Java

6.8.1- Understanding Methods in Java

A method in Java is a block of executable code that performs a specific task. It can be called multiple
times with different arguments, allowing code reusability and modularity. Methods can return values,
perform operations, or execute statements without returning data.

147
The Complete Java Training Imran Afzal
Some programming languages differentiate between functions (which return a value) and procedures
(which do not). In Java, the terms function and method are often used interchangeably.

6.8.2- Components of a Method Declaration

To define a method in Java, several components are required:

• Modifiers: Keywords like public or static that control access and behavior.

• Return Type: Specifies the data type of the value the method returns. If no value is returned,
the return type is void.

• Method Name: Written in camelCase (e.g., calculateSum).

• Method Parameters: Optional variables that the method accepts as input, enclosed in
parentheses. If no parameters are needed, an empty () is used.

• Method Body: Contains the code to be executed, enclosed in {}.

6.8.3- Method Parameters and Return Types

Parameters must be listed in the method declaration with their data types and names. When calling a
method, the number and type of arguments must match the method’s parameter list. Java does not
support default parameter values, so all arguments must be explicitly provided.

If a method has a non-void return type, every possible path within the method must end with a valid
return statement that returns a value matching the declared type. For void methods, a return statement
can be used to exit early, but it's not required.

6.8.4- Example: Method with a Boolean Return Type

public boolean CheckNumber(int number) {

if (number <= 1) {

return true;

148
The Complete Java Training Imran Afzal
}

This method checks if the input number is less than or equal to 1. If true, it returns true. However, if
the condition is false, no value is returned, which would cause a compilation error since the return
type is boolean and all paths must return a value.

6.8.4- Correcting the Return Path

To avoid such issues, ensure that all execution paths return a value:

public boolean CheckNumber(int number) {

if (number <= 1) {

return true;

return false;

This method correctly returns false when the condition is not met, satisfying the requirement for a
return value in all paths.

6.8.5- Using Return in Void Methods

Void methods do not return data, but you can still use return; to exit early:

public void CheckNumber(int number) {

if (number <= 1) {

return;

149
The Complete Java Training Imran Afzal
}

// continue with other logic if needed

This version exits the method early if the condition is true, but since the method’s return type is void,
it does not return any value.

6.8.6- Method Signature and Overloading

The unique identity of a method in a class is defined by its method signature, which includes the
method name and the parameter list (number and types). You can declare multiple methods with the
same name if their signatures are different—this concept is known as method overloading.

public void display() { }

public void display(String message) { }

Here, both methods share the same name but accept different parameters, making them valid
overloads.

6.8.7- Revisiting the Main Method

The main method in Java serves as the program's entry point and has a strict signature that the JVM
recognizes:

public static void main(String[] args) { }

• public: Accessible from outside the class.

• static: Belongs to the class, not an instance.

• void: Does not return any value.

• main: Method name recognized by the JVM.

• String[] args: Parameter used to receive command-line input.

In IntelliJ IDEA, the shortcut psvm can be used to auto-generate the main method signature.

150
The Complete Java Training Imran Afzal
By understanding how method declarations, return types, and parameters work together, you can write
clean, reusable, and efficient Java code.

6.9- Method Exercises in Java – Part 1

6.9.1- Checking if a Number is Even

This program defines a method to check whether a given integer is even or odd.

public class MethodPractice {

public static boolean isEven(int number) {

if (number % 2 == 0) {

return true;

} else {

return false;

public static void main(String[] args) {

int myNumber = 20;

boolean result = isEven(myNumber);

151
The Complete Java Training Imran Afzal
if (result) {

System.out.println("Number is even");

} else {

System.out.println("Number is odd");

The method isEven is defined with the return type boolean and takes an integer parameter. It checks
if the number is divisible by 2 using the modulo operator %. If the remainder is 0, the method returns
true; otherwise, it returns false. In the main method, the result is evaluated using an if statement to
print the appropriate message. Since the input value is 20, the output will be:
"Number is even"

6.9.2- Calculating Student Position Based on Marks

This program calculates a student's position based on their marks and prints their name with the
assigned position.

public class PositionCalculation {

public static int PositionMethod(int number) {

if (number < 50) {

152
The Complete Java Training Imran Afzal
return 0;

} else if (number >= 50 && number <= 60) {

return 5;

} else if (number >= 61 && number <= 70) {

return 4;

} else if (number >= 71 && number <= 80) {

return 3;

} else if (number >= 81 && number <= 90) {

return 2;

} else {

return 1;

public static void printPosition(String stdName, int position) {

System.out.println("Student Name is : " + stdName + " & Position is : " + position);

153
The Complete Java Training Imran Afzal
public static void main(String[] args) {

int getPosition;

getPosition = PositionMethod(65);

printPosition("James", getPosition);

getPosition = PositionMethod(82);

printPosition("William", getPosition);

The method PositionMethod takes an integer score and uses a ladder if-else structure to determine
the student’s position:

• Returns 0 for scores below 50 (fail)

• Returns 5 for scores between 50–60

• Returns 4 for scores between 61–70

• Returns 3 for scores between 71–80

• Returns 2 for scores between 81–90

• Returns 1 for scores above 90

154
The Complete Java Training Imran Afzal
The second method, printPosition, takes a student’s name and position and prints a combined
message. In the main method:

• For score 65, PositionMethod returns 4, and the output is:


"Student Name is : James & Position is : 4"

• For score 82, PositionMethod returns 2, and the output is:


"Student Name is : William & Position is : 2"

6.10- Method Exercises in Java – Part 2

6.10.1- Converting Temperature Between Celsius and Fahrenheit

This program converts a given temperature from Celsius to Fahrenheit and vice versa, then prints the
result. It consists of two methods: one for performing the calculation and another for printing the
result.

public class TemperatureConverter {

public static double calculateTemperature(char tempChar, double temperature) {

double result;

if (tempChar == 'F') {

result = (temperature - 32) * 5 / 9;

} else if (tempChar == 'C') {

result = (temperature * 9 / 5) + 32;

} else {

155
The Complete Java Training Imran Afzal
result = 0;

return result;

public static void printResult(char temp_name, double result) {

System.out.println("Temperature is : " + result + "" + temp_name);

public static void main(String[] args) {

char tempName = 'C';

double tempValue = 80.2;

double calculatedResult = calculateTemperature(tempName, tempValue);

printResult(tempName, calculatedResult);

156
The Complete Java Training Imran Afzal
The calculateTemperature method takes a character (tempChar) and a temperature value. If tempChar
is 'F', it converts Fahrenheit to Celsius using the formula C = (F - 32) * 5 / 9. If it's 'C', it converts
Celsius to Fahrenheit using F = (C * 9 / 5) + 32. For any other input, the method returns 0.

The printResult method simply displays the temperature along with the unit.

In the main method, the unit is set to 'C' and the value is 80.2, which will be converted and printed as
Fahrenheit.

6.10.2- Checking if a Year is a Leap Year

This program determines whether a given year is a leap year based on divisibility rules.

public class LeapYear {

public static void checkLeapYear(int year) {

if ((year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0))) {

System.out.println(year + " is a leap year");

} else {

System.out.println(year + " is not a leap year");

public static void main(String[] args) {

157
The Complete Java Training Imran Afzal
int checkYear = 2019;

checkLeapYear(checkYear);

A year is a leap year if it is:

• Divisible by 400, or

• Divisible by 4 but not by 100

The checkLeapYear method uses these rules and prints the result accordingly. In the above example,
the method is called with the value 2019. Since 2019 is not divisible by 4, it is not a leap year. Changing
the value to 2020 and re-running the program would print that 2020 is a leap year.

These examples illustrate the practical use of conditional logic, method parameters, return types, and
method calls in Java.

6.11- Method Exercises Part 3

6.11.1- Converting Distances Between Units

This program converts distances between different measurement units, including miles to kilometers,
meters to kilometers, and kilometers to miles. The results are displayed in a structured format.

Create a class file named DistanceConversion and define four methods:

• milesToKilometers(double miles): Converts miles to kilometers.

• metersToKilometers(double meters): Converts meters to kilometers.

• kilometersToMiles(double kilometers): Converts kilometers to miles.

158
The Complete Java Training Imran Afzal
• printDistance(double firstValue, String valueType, double secondValue, String
convertedValue): Prints the conversion result.

public static void main(String[] args) {

double miles = 400;

double kilometers = milesToKilometers(miles);

PrintDistance(miles,"Miles",kilometers,"Kilometers");

double meters = 4000;

kilometers = metersToKilometers(meters);

PrintDistance(meters,"meters",kilometers,"Kilometers");

kilometers = 200;

miles = kilometersToMiles(kilometers);

PrintDistance(kilometers,"kilometers",miles,"miles");

159
The Complete Java Training Imran Afzal
public static double milesToKilometers(double miles) {

return miles * 1.609344;

public static double metersToKilometers(double meters) {

return meters / 1000;

public static double kilometersToMiles(double kilometers) {

return kilometers / 1.609344;

public static void PrintDistance(double firstValue, String valueType, double secondValue, String
convertedValue ){

System.out.println(firstValue + " " + valueType + " is equal to " + secondValue + " " +
convertedValue);

When executed, the program outputs:

400.0 Miles is equal to 643.7376 Kilometers

4000.0 Meters is equal to 4.0 Kilometers

200.0 Kilometers is equal to 124.2742 Miles

The methods efficiently perform conversions and print structured output, demonstrating method
calls, parameter passing, and return values in Java.

160
The Complete Java Training Imran Afzal
6.12- Method Exercises Part 4

6.12.1- Calculating Perimeter and Area of a Rectangle and Square

This program calculates the perimeter and area of both rectangles and squares using methods for
structured computation.

Create a class file named AreaPerimeter and define four methods:

• rectanglePerimeter(int length, int width): Calculates the perimeter of a rectangle.

• rectangleArea(int length, int width): Calculates the area of a rectangle.

• squarePerimeter(int side): Calculates the perimeter of a square.

• squareArea(int side): Calculates the area of a square.

public class ShapeCalculator {

// Method to calculate rectangle perimeter

public static int rectanglePerimeter(int length, int width) {

return 2 * (length + width);

// Method to calculate rectangle area

public static int rectangleArea(int length, int width) {

return length * width;

161
The Complete Java Training Imran Afzal
// Method to calculate square perimeter

public static int squarePerimeter(int side) {

return 4 * side;

// Method to calculate square area

public static int squareArea(int side) {

return side * side;

public static void main(String[] args) {

int rectangleLength = 5;

int rectangleWidth = 10;

int squareSide = 5;

System.out.println("Rectangle Perimeter: " + rectanglePerimeter(rectangleLength,


rectangleWidth));

162
The Complete Java Training Imran Afzal
System.out.println("Rectangle Area: " + rectangleArea(rectangleLength, rectangleWidth));

System.out.println("Square Perimeter: " + squarePerimeter(squareSide));

System.out.println("Square Area: " + squareArea(squareSide));

When executed, the program outputs:

Rectangle Perimeter: 30

Rectangle Area: 50

Square Perimeter: 20

Square Area: 25

The program efficiently calculates and prints the perimeter and area using methods.

6.13- Method Overloading

Method overloading occurs when a class contains multiple methods with the same name but different
parameter declarations. This allows a single method name to be used for various tasks by accepting
different sets of parameters. Java determines which version of the overloaded method to execute
based on the input parameters provided during the method call.

A method signature in Java consists of the method's name and its parameters, including their
number, types, and order. The return type and parameter names are not considered part of the
signature. The uniqueness of the method signature allows the Java compiler to differentiate overloaded
methods.

163
The Complete Java Training Imran Afzal
For example, a method with one integer parameter can be overloaded by defining another method
with the same name but with a single float parameter. Similarly, changing the number or order of
parameters also constitutes valid method overloading. However, having the same parameter list with
only a different return type is not a valid overload and will cause a compiler error.

6.13.1- Benefits of Method Overloading

• Code Reusability: The same method name can be used for different tasks by varying
parameters.

• Improved Readability: Using the same method name for similar operations makes code
more intuitive.

• Type Safety: Different method implementations handle various data types while maintaining
a consistent interface.

public class MethodOverloading {

public static void main(String[] args) {

add(10, 20);

add(100, 200, 400);

public static void add(int a, int b) {

System.out.println("Method with Two Parameters: " + a + " & " + b);

164
The Complete Java Training Imran Afzal
public static void add(int a, int b, int c) {

System.out.println("Method with Three Parameters: " + a + ", " + b + " & " + c);

When executed, the program outputs:

Method with Two Parameters: 10 & 20

Method with Three Parameters: 100, 200 & 400

The Java compiler automatically selects the appropriate method based on the number of arguments
passed. This demonstrates how method overloading allows flexibility and improves code organization.

6.14- Method Overloading Exercises Part 1

Method overloading allows multiple methods with the same name but different parameter lists to
perform distinct operations. This exercise demonstrates method overloading by calculating the area
of various shapes: rectangle, square, triangle, and circle.

6.14.1- Formula Reference for Area Calculations

• Rectangle: length * width

• Square: side * side

• Triangle: 0.5 * base * height

• Circle: π * radius * radius (where π ≈ 3.14)

165
The Complete Java Training Imran Afzal
6.14.2- Implementing the Program

Create a class named Shapes with a main method and four overloaded methods named area, each
handling a different shape’s calculation.

public class Shapes {

// Rectangle area (length * width)

public static double area(double length, double width) {

return length * width;

// Square area (side * side)

public static double area(int side) {

return side * side;

// Triangle area (0.5 * base * height)

public static double area(double base, double height, boolean isTriangle) {

return 0.5 * base * height;

166
The Complete Java Training Imran Afzal
}

// Circle area (π * radius * radius)

public static double area(double radius) {

return 3.14 * radius * radius;

public static void main(String[] args) {

System.out.println("Area of rectangle : " + area(30.0, 20.0));

System.out.println("Area of square : " + area(30));

System.out.println("Area of triangle : " + area(50.0, 10.0, true));

System.out.println("Area of circle : " + area(40.0));

6.14.3- Program Execution and Output

Area of rectangle: 600.0

Area of square: 900.0

Area of triangle: 250.0

167
The Complete Java Training Imran Afzal
Area of circle: 5024.0

Each area method is uniquely defined by its parameter list, showcasing method overloading. The
program successfully calculates and prints the area for different shapes using the same method name,
enhancing code reusability and readability.

6.15- Method Overloading Exercise Part 2

Method overloading enables defining multiple methods with the same name but different parameter
lists, allowing them to handle different cases efficiently. This exercise demonstrates method
overloading by implementing a method called concatenate, which concatenates strings in different
ways based on the parameters provided.

6.15.1- Implementing String Concatenation with Method Overloading

The program consists of two overloaded versions of the concatenate method:

1. The first version accepts two string parameters and returns their concatenation.

2. The second version accepts three parameters: two strings and a separator, inserting the
separator between them before returning the result.

6.15.2- Java Program for String Concatenation Overloading

Create a class named ConcatenationStrings with a main method and two overloaded concatenate
methods:

public class ConcatenationStrings {

public static String Concatenate(String s1, String s2) {

return s1 + s2;

168
The Complete Java Training Imran Afzal
}

public static String Concatenate(String s1, String s2, String separator) {

return s1 + separator + s2;

public static void main(String[] args) {

System.out.println("Concatenation without Separator: " + Concatenate("Nixware",


"Company"));

System.out.println("Concatenation with Separator: " + Concatenate("Nixware", "Company", "--


"));

6.15.3- Program Execution and Output

Concatenation without Separator: NixwareCompany

Concatenation with Separator: Nixware--Company

The Java compiler differentiates between the overloaded methods based on the number of arguments
passed during method calls. This example effectively demonstrates how method overloading improves
flexibility and reusability in Java programming.

169
The Complete Java Training Imran Afzal
6.16- Method Overloading Exercises Part 3

Method overloading allows multiple methods with the same name but different parameter lists to
handle different types of inputs. This example demonstrates method overloading by creating a
program that converts lengths between centimeters, feet, and inches using two overloaded methods
named convert.

6.16.1- Implementation of Length Conversion Using Method Overloading

The program includes two versions of the convert method:

1. The first method takes a single double parameter and converts centimeters to feet.

2. The second method takes two parameters: a double (length) and a char (unit). Based on
the unit value, it either converts:

• Feet to inches if the unit is 'f'.


• Inches to centimeters if the unit is 'i'.
• Returns 0 if the unit is neither 'f' nor 'i'.

6.16.2- Java Program for Length Conversion Overloading

Create a class named LengthConverter with a main method and two overloaded convert methods:

public class LengthConverter {

public static double convert(double length) {

return length / 30.48;

170
The Complete Java Training Imran Afzal
public static double convert(double length, char unit) {

if (unit == 'f') {

return length * 12;

} else if (unit == 'i') {

return length * 2.54;

} else {

return 0;

public static void main(String[] args) {

System.out.println("10 centimeters is equivalent to " + convert(10) + " feet");

System.out.println("10 feet is equivalent to " + convert(10, 'f') + " inches");

System.out.println("10 inches is equivalent to " + convert(10, 'i') + " centimeters");

171
The Complete Java Training Imran Afzal
6.16.3- Program Execution and Output

10 centimeters is equivalent to 0.32808398950131235 feet.

10 feet is equivalent to 120.0 inches.

10 inches is equivalent to 25.4 centimeters.

This example demonstrates how method overloading allows flexibility in handling different input
types while maintaining a clear and readable code structure.

6.17- Method Overloading Exercises Part 4

Method overloading allows multiple methods with the same name but different parameter
configurations to handle various input types. This example demonstrates method overloading by
creating a program that converts seconds into minutes and hours using two overloaded
timeConvert methods.

6.17.1- Implementation of Time Conversion Using Method Overloading

The program consists of two versions of the timeConvert method:

1. Parametric Method: Takes an integer parameter representing seconds and converts it into
minutes and seconds, then formats the result as "X M Y S" (X minutes, Y seconds).

2. Non-Parametric Method: Calls the parametric method with a predefined value (e.g., 123450
seconds) and returns its result.

To extend the program, we modify the parametric method to also convert minutes into hours and
format the output as "X Hours, Y Minutes, Z Seconds".

6.17.2- Java Program for Time Conversion Overloading

Create a class named TimeConverter with a main method and two overloaded timeConvert
methods:

public class TimeConverter {

172
The Complete Java Training Imran Afzal
public static String timeConvert(int seconds) {

int calculateMinutes = seconds / 60;

int calculateHours = calulateMinutes / 60;

int remainingSecond = seconds % 60;

int remainingMinutes = calulateMinutes % 60;

return calulateHours + "H " +remainingMinutes + "M " + remainingSecond + "S";

public static String timeConvert() {

return timeConvert(12350);

public static void main(String[] args) {

String result = timeConvert();

System.out.println(result);

173
The Complete Java Training Imran Afzal
6.17.3- Program Execution and Output

3 Hours, 25 Minutes, 50 Seconds

This program demonstrates method overloading, where the timeConvert method is overloaded to
handle different types of input while keeping the code structure clean and readable.

6.18- Method Overloading Exercises Part 5

Method overloading allows multiple methods with the same name but different parameter
configurations to handle various input types. This example demonstrates method overloading by
creating a program that calculates and displays the mileage and speed of a car using overloaded
carFunctions methods.

6.18.1- Implementation of Car Functions Using Method Overloading

The program consists of three versions of the carFunctions method:

1. Mileage Calculation: Takes distance and fuel consumption as parameters and returns the
mileage formatted as "Mileage: X km/l" (X is the calculated mileage).

2. Speed Calculation: Takes distance and time as parameters and returns the speed formatted
as "Speed: Y km/h" (Y is the calculated speed).

3. Combined Method: Calls both the mileage and speed methods internally, concatenating their
results with a line break and returning a string displaying both values.

6.18.2- Java Program for Car Functions Using Method Overloading

Create a class named Car with a main method and three overloaded carFunctions methods:

class Car {

public static String carFunctions(double distance, double fuel){

174
The Complete Java Training Imran Afzal
return "Mileage: " + (distance / fuel) + " km/l";

public static String carFunctions(double distance, int time){

return "Speed: " + (distance / time) + " km/h";

public static String carFunctions(){

return carFunctions(100.0, 5.0) + "\n" + carFunctions(200.0, 2);

public static void main(String[] args) {

String result = carFunctions();

System.out.println(result);

175
The Complete Java Training Imran Afzal
6.18.3- Program Execution and Output

Mileage: 20.0 km/l

Speed: 100.0 km/h

This program demonstrates method overloading by allowing the carFunctions method to handle
different input types while keeping the code structure clean and efficient.

6.19- Method Overloading Exercises Part 6

Method overloading allows multiple methods with the same name but different parameter
configurations to handle various input types. This example demonstrates method overloading by
creating a program that stores and retrieves a person's personal and professional information using
overloaded personInfo methods.

6.19.1- Implementation of Person Information Storage Using Method Overloading

The program consists of three versions of the personInfo method:

1. Personal Information Storage: Takes name, age, and address as parameters and returns a
formatted string containing personal details.

2. Professional Information Storage: Takes profession, company, and years of experience


as parameters and returns a formatted string containing professional details.

3. Combined Method: Calls both the personal and professional information methods,
concatenating their results with a line break and returning a string displaying all details.

6.19.2- Java Program for Storing Person Information Using Method Overloading

Create a class named Person with a main method and three overloaded personInfo methods:

class Person {

public static String personInfo(String name, int age, String address) {

176
The Complete Java Training Imran Afzal
return "Person Information: \nName - " + name + "\nAge - " + age + " years \nAddress - "
+ address;

public static String personInfo(String profession, String company, int experience)

return "\nProfession - " + profession + "\nCompany - " + company + "\nExperience - " +


experience + " years";

public static String personInfo()

return personInfo("John",35,"New York") + personInfo("IT","Nixware",15);

public static void main(String[] args) {

String getInfo = personInfo();

177
The Complete Java Training Imran Afzal
System.out.println(getInfo);

6.19.3- Program Execution and Output

Person Information:

Name - John

Age - 35 years

Address - New York

Profession - IT

Company - Nixware

Experience - 15 years

This program demonstrates method overloading by allowing the personInfo method to handle
different types of input while keeping the code structure efficient and readable.

6.20- Method Overloading Exercises Part 7

Method overloading allows multiple methods with the same name but different parameter
configurations to handle various input types. This example demonstrates method overloading by
creating a program that calculates a person’s age in years, months, and days using overloaded findAge
methods.

6.20.1- Implementation of Age Calculation Using Method Overloading

The program consists of three versions of the findAge method:

This program demonstrates method overloading by calculating a person’s age in years, months, and
days based on their birth date and the current date. It also handles the conversion of the total age into
specific units like total years, months, or days using overloaded methods.
178
The Complete Java Training Imran Afzal
The program consists of three overloaded versions of the findAge method:

1. Calculate Total Age in Days


Accepts six parameters: birth year, current year, birth month, current month, birth day, and
current day. It calculates the total number of days between the two dates by first computing
the differences in years, months, and days, then converting these differences into total days.

2. Convert Days into Years, Months, and Days


Accepts four parameters: total number of days (ageValue), years (y), months (m), and a
character flag (ch) that determines whether to extract years ('y'), months ('m'), or days ('d').

3. Ensure Positive Values


Accepts a single integer and returns its absolute value. This is useful when any age calculation
results in a negative number due to input order.

6.20.2- Java Program for Calculating Age Using Method Overloading

class Age {

public static int findAge(int birth_year, int current_year, int birth_month, int current_month, int
birth_day, int current_day) {

int years = current_year - birth_year;

int months = current_month - birth_month;

int days = current_day - birth_day;

int ageInDays = (years * 365) + (months * 31) + days;

ageInDays = findAge(ageInDays); // Ensure positive

return ageInDays;

179
The Complete Java Training Imran Afzal
}

public static int findAge(int ageValue, int y, int m, char ch) {

if (ch == 'y') {

ageValue = ageValue / 365;

} else if (ch == 'm') {

ageValue = (ageValue - y * 365) / 30;

} else {

ageValue = (ageValue - y * 365 - m * 30);

return ageValue;

public static int findAge(int checkNegative) {

if (checkNegative < 0) {

checkNegative = -1 * checkNegative;

180
The Complete Java Training Imran Afzal
return checkNegative;

public static void main(String[] args) {

int totalDays = findAge(1994, 2023, 7, 11, 24, 11);

int getYears = findAge(totalDays, 0, 0, 'y');

int getMonth = findAge(totalDays, getYears, 0, 'm');

int getDays = findAge(totalDays, getYears, getMonth, 'd');

System.out.println(getYears + " Years " + getMonth + " Months " + getDays + " Days");

6.20.3- Program Execution and Output

When executed with the input:

• Birth Date: 24 July 1994

• Current Date: 11 November 2023

The program produces the following output:

30 Years 6 Months 17 Days

181
The Complete Java Training Imran Afzal
This result is calculated by first finding the total number of days between the dates and then breaking
that total into years, months, and days using arithmetic formulas.

This example demonstrates the power of method overloading in handling multiple variations of
related logic under a single method name. It organizes the code cleanly by reusing method names and
ensures modular, readable, and scalable logic for age-based computations.

182
The Complete Java Training Imran Afzal
Chapter 7

Loops and Control Statements

183
The Complete Java Training Imran Afzal
7.1- Switch Statement

The switch statement in Java is a control structure used to execute one of several possible branches
of code based on the value of an expression. It provides an efficient and readable way to handle
multiple cases, avoiding the complexity of lengthy if-else statements.

7.1.1- Syntax and Structure of Switch Statement

The switch statement starts with the keyword switch, followed by an expression enclosed in
parentheses. The code block is enclosed within curly braces { } and contains multiple case labels, each
representing a possible value for the expression. A break statement is used to exit the switch block
after a match is found. If no cases match, the default keyword executes the fallback code.

7.1.2- Comparison Between Switch and If-Else Statements

Key Differences

Expression Type:

• The if-else statement supports expressions of any type (int, float, double, objects).
• The switch statement only supports primitive data types like int, char, short, byte, and
wrapper classes (Integer, Character, Short).

Handling Multiple Conditions:

• The if-else statement evaluates multiple conditions independently.


• The switch statement evaluates a single expression and compares it with multiple case
values.

Readability:

• The switch statement is more readable and organized when dealing with multiple
conditions.
• The if-else statement can become cluttered with nested conditions.

184
The Complete Java Training Imran Afzal
Performance:

• The switch statement is generally faster than if-else, especially when handling multiple
cases.

7.1.3- Advantages of Switch Statement

• Improved Readability: The code structure is clear and easy to understand.

• Cleaner Code: Groups related conditions, making it easier to maintain.

• Easy to Use: Requires less typing than an equivalent if-else structure.

• Better Handling of Multiple Cases: Reduces the need for deeply nested conditions.

• Better Type Safety: Only allows certain data types, reducing type mismatch errors.

• Default Case Handling: Provides a built-in way to handle unexpected cases, improving error
handling.

7.1.4- Limitations of Switch Statement

• Limited Data Types: Only supports int, char, short, byte, and their wrapper classes.

• No Range Checks: Unlike if-else, the switch statement cannot evaluate range conditions
(e.g., x > 10 && x < 50).

• No Use of Expressions: Only constant values are allowed in case labels.

• No Null Support: Null values cannot be used as switch expressions.

• Fall-Through Behavior: Missing a break statement can lead to unintended execution of


subsequent cases.

• No Short-Circuit Evaluation: All cases are evaluated, even if an earlier match is found, which
may impact performance.

7.1.5- Example of a Switch Statement in Java

public class SwitchExample {

185
The Complete Java Training Imran Afzal
public static void main(String[] args) {

int day = 3;

switch (day) {

case 1:

System.out.println("Monday");

break;

case 2:

System.out.println("Tuesday");

break;

case 3:

System.out.println("Wednesday");

break;

case 4:

System.out.println("Thursday");

break;

case 5:

System.out.println("Friday");

186
The Complete Java Training Imran Afzal
break;

case 6:

System.out.println("Saturday");

break;

case 7:

System.out.println("Sunday");

break;

default:

System.out.println("Invalid day");

7.1.6- Program Execution and Output

Wednesday

This example demonstrates the switch statement in action. The program evaluates the variable day
and executes the matching case block. Since day = 3, the output is "Wednesday". If the value does
not match any case, the default statement executes.

The switch statement is a powerful alternative to if-else when handling multiple discrete values,
improving performance and readability in structured decision-making scenarios.

187
The Complete Java Training Imran Afzal
7.2- Switch Statement Exercises Part 1

The switch statement provides a structured approach for evaluating a single expression and executing
the corresponding case block based on its value. To demonstrate its usage, a program will be created
to determine the day of the week based on a given integer value. The same functionality will be
implemented using both an if-else statement and a switch statement to highlight their differences.

7.2.1- Implementation of Day Selection Using If-Else Statement

An if-else statement sequentially checks conditions and executes the corresponding block when a
condition evaluates to true. If none of the conditions match, the else block executes as a fallback.

7.2.2- Java Program Using If-Else Statement

public class MySwitchStatements {

public static void main(String[] args) {

int day = 3; // Define a variable with a value

if (day == 1) {

System.out.println("Monday");

} else if (day == 2) {

System.out.println("Tuesday");

} else if (day == 3) {

System.out.println("Wednesday");

} else {

System.out.println("Invalid day");

188
The Complete Java Training Imran Afzal
}

7.2.3- Execution and Output

Wednesday

Since the variable day is assigned the value 3, the third condition (day == 3) evaluates to true,
executing the corresponding print statement and displaying Wednesday as the output.

7.2.4- Implementation of Day Selection Using Switch Statement

The switch statement evaluates an expression and executes the case block that matches the given
value. The break statement is used to exit the switch block after executing a case to prevent fall-
through behavior. The default case handles unexpected values.

7.2.5- Java Program Using Switch Statement

public class MySwitchStatements1 {

public static void main(String[] args) {

int day = 3; // Define a variable with a value

switch (day) {

case 1:

189
The Complete Java Training Imran Afzal
System.out.println("Monday");

break;

case 2:

System.out.println("Tuesday");

break;

case 3:

System.out.println("Wednesday");

break;

default:

System.out.println("Invalid day");

7.2.6- Execution and Output

Wednesday

Since day is assigned the value 3, the switch statement executes the case 3 block, printing Wednesday
to the console. If day were not 1, 2, or 3, the default case would execute, displaying Invalid day.

7.2.7- Comparison of If-Else and Switch Statements

Feature If-Else Statement Switch Statement

Readability Becomes complex with multiple More readable for handling discrete
conditions values

Performance Slower for multiple conditions Faster when handling multiple fixed
cases

190
The Complete Java Training Imran Afzal
Flexibility Can evaluate expressions and ranges Can only evaluate single values

Code Length Requires multiple condition checks Uses structured case blocks

The switch statement improves readability and performance when handling multiple cases, whereas
if-else provides greater flexibility in evaluating complex expressions and conditions.

7.3- Switch Statement Exercises Part 2

The switch statement is useful for evaluating a single expression and executing the corresponding
case block based on its value. This example demonstrates using a switch statement to determine the
size category based on a given numeric value.

7.3.1- Implementation of Size Selection Using Switch Statement

A switch statement is used to check the value of the number variable and assign the corresponding
size to the size variable. If the number matches one of the predefined values, the appropriate size is
assigned; otherwise, a default size is assigned.

7.3.2- Java Program for Size Selection Using Switch Statement

// Java Program to check the size

// using the switch...case statement

class Main {

public static void main(String[] args) {

int number = 49;

191
The Complete Java Training Imran Afzal
String size;

// switch statement to check size

switch (number) {

case 29:

size = "Small";

break;

case 42:

size = "Medium";

break;

// match the value of week

case 44:

size = "Large";

break;

192
The Complete Java Training Imran Afzal
case 48: case 49: case 50:

size = "Extra Large";

break;

default:

size = "Unknown";

break;

System.out.println("Size: " + size);

7.3.3- Execution and Output

Size: Extra Large

Since the variable number is assigned the value 49, it matches case 49, executing the corresponding
block and setting size to Extra Large. If number were 29, the output would be Small, and if number
were an unmatched value, the default case would execute, displaying Unknown.

193
The Complete Java Training Imran Afzal
7.3.4- Key Concepts Demonstrated

1. Multiple Case Matching: The cases 48, 49, and 50 are grouped together to avoid
redundancy. If number matches any of these values, the same block executes.

2. Break Statement: Used after each case to prevent fall-through to the next case.

3. Default Case: Acts as a fallback when none of the defined cases match the given value.

By using the switch statement, the program efficiently assigns and prints the appropriate size category.

7.4- Traditional vs Enhanced Switch Statement

The switch statement has undergone significant enhancements since its inception. Java introduced an
enhanced switch statement to improve readability, reduce errors, and eliminate unnecessary syntax
such as break statements. Understanding both traditional and enhanced switch statements is crucial
for writing efficient and maintainable code, especially when working with different versions of Java.

7.4.1- Differences Between Traditional and Enhanced Switch Statements

In the traditional switch statement, each case label is followed by a colon (:) and requires explicit break
statements to prevent fall-through. In the enhanced switch, case labels use an arrow (->) instead of
colons, eliminating the need for break. Additionally, multiple cases can be combined using a comma-
separated list, making the code more concise and readable.

7.4.2- Java Program Comparing Traditional and Enhanced Switch Statements

// 7.4.2 - Java Program Comparing Traditional and Enhanced Switch Statements

public class SwitchComparison {

public static String getDays(String dayName) {

// Traditional Switch

switch (dayName) {

194
The Complete Java Training Imran Afzal
case "Sunday":

case "Monday":

case "Tuesday":

return "First Three days of Week";

case "Wednesday":

case "Thursday":

case "Friday":

return "Second Three days of Week";

case "Saturday":

return "Last Day of Week";

return "Nothing"; // Default case

public static String getDaysEnhanced(String dayName) {

// Enhanced Switch

return switch (dayName) {

195
The Complete Java Training Imran Afzal
case "Sunday", "Monday", "Tuesday" -> "First Three days of Week";

case "Wednesday", "Thursday", "Friday" -> "Second Three days of Week";

case "Saturday" -> "Last Day of Week";

default -> "Nothing"; // Required in enhanced switch

};

public static void main(String[] args) {

String day = "Tuesday";

System.out.println(day + " is the " + getDays(day)); // Traditional Switch

System.out.println(day + " is the " + getDaysEnhanced(day)); // Enhanced Switch

7.4.3- Program Execution and Output

• Tuesday is the First Three days of Week


• Tuesday is the First Three days of Week
• If the day variable is changed to "Saturday", the output will be:
• Saturday is the Last Day of Week
• Saturday is the Last Day of Week

196
The Complete Java Training Imran Afzal
7.5- Enhanced Switch Statement Exercises Part 1

The enhanced switch statement in Java simplifies code by eliminating the need for break statements
and allowing switch expressions that return values directly. This example demonstrates an enhanced
switch statement that determines a person’s expertise level based on an integer input.

7.5.1- Implementation of Expertise Level Using Enhanced Switch

The program assigns an integer value representing the expertise level (1 for Beginner, 2 for
Intermediate, 3 for Expert) and uses an enhanced switch to determine the corresponding level.

7.5.2- Java Program for Expertise Level Classification

class EnhanceSwitch{

public static void main(String[] args) {

String levelString;

int level = 3;

levelString = switch (level) {

case 1 -> "Beginner";

case 2 -> "Intermediate";

case 3 -> "Expert";

default -> "Failed to Learn";

};

197
The Complete Java Training Imran Afzal
System.out.println("Your Level is: " + levelString);

7.5.3- Program Execution and Output

• Your Level is: Expert


• If the level variable is changed to 2, the output will be:
• Your Level is: Intermediate
• For an invalid input (e.g., 5), the output will be:
• Your Level is: Failed to Learn

7.6- Enhanced Switch Statement Exercises Part 2

The enhanced switch statement simplifies conditional logic by allowing multiple case values in a
single line, eliminating break statements, and supporting switch expressions that return values
directly. This example demonstrates an enhanced switch statement to classify characters as vowels or
consonants and to compute areas of different geometric shapes. Implementation of Vowel and
Consonant Classification Using Enhanced Switch The program checks whether a given character
(ch) is a vowel or consonant using a switch expression. If ch matches any vowel (a, e, i, o, u in
uppercase or lowercase), the output is "Vowel"; otherwise, it is "Consonant".

7.6.1- Java Program for Vowel and Consonant Classification

public class EnhanceSwitchTwo {

public static void main(String[] args) {

String type;

char ch = 'z';

198
The Complete Java Training Imran Afzal
type = switch (ch) {

case 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U' -> "Vowel";

default -> "Consonant";

};

System.out.println("This is a " + type);

7.6.2- Program Execution and Output

• This is a Consonant
• If ch = 'a', the output would be:
• This is a Vowel

7.6.3- Understanding Yield in Enhanced Switch

If a default case includes a code block, the yield keyword must be used instead of return.

default -> {

String response = "Consonant";

yield response;

The yield keyword was introduced in Java 14 to return values from a switch expression inside a block.
Implementation of Geometric Area Calculation Using Enhanced Switch This program
calculates the area of an ellipse, trapezoid, or parallelogram based on a user-defined shape.

199
The Complete Java Training Imran Afzal
7.6.4- Java Program for Area Calculation

public class EnhanceSwitch {

public static void main(String[] args) {

String shape = "Ellipse";

double majorAxis = 11, minorAxis = 16;

double topBase = 0, bottomBase = 0, height = 0;

double base = 0, heightPara = 0;

double areaResult;

areaResult = switch (shape) {

case "Ellipse" -> 3.14 * majorAxis * minorAxis;

case "Trapezoid" -> ((topBase + bottomBase) / 2) * height;

case "Parallelogram" -> base * heightPara;

default -> 0;

};

System.out.println(shape + " : " + areaResult);

200
The Complete Java Training Imran Afzal
7.6.5- Program Execution and Output

Ellipse: 552.64

If shape = "Parallelogram", base = 11, heightPara = 14, the output would be:

Parallelogram: 154.0

7.6.6- Implementation of User Authentication Using Enhanced Switch

This program matches a username to a predefined password using a switch expression. If the
username is found, the corresponding password is returned; otherwise, an error message is returned.

7.6.7- Java Program for User Authentication

public class Test {

public static void main(String[] args) {

String username = "John";

String password = getPassword(username);

System.out.println(username + " : " + password);

public static String getPassword(String username){

String password;

password = switch (username) {

201
The Complete Java Training Imran Afzal
case "Doe" -> "A123";

case "John" -> "F143";

case "Jane" -> "B122";

default -> {

password = "Invalid User Name";

yield password;

};

return password;

7.6.8- Program Execution and Output

• John: F143
• If username = "Mike", the output would be:
• Mike: Invalid User Name

7.7- Nested Switch Statement

The nested switch statement allows a switch statement to be placed inside another switch statement,
making it useful for handling complex conditions where multiple variables influence decision-making.
It is an efficient way to manage multiple related conditions while keeping the code more readable and
structured.

7.7.1- Implementation of Nested Switch Statement

A nested switch statement consists of an outer switch that evaluates an initial expression and
determines which case to execute. Inside a specific case, another inner switch evaluates a second
expression, leading to more specific cases based on additional conditions.

202
The Complete Java Training Imran Afzal
7.7.2- Syntax of Nested Switch Statement

switch (expression1) {

case value1:

// do something

switch (expression2) {

case value2:

// do something

break;

case value3:

// do something

break;

default:

// do something

break;

case value4:

203
The Complete Java Training Imran Afzal
// do something

break;

default:

// do something

7.7.3- Benefits of Using Nested Switch Statements

• Improves Readability: By breaking complex logic into multiple levels, nested switch
statements make the code easier to read and understand.

• Simplifies Complex Logic: Multiple related conditions can be evaluated separately, reducing
the complexity of large if-else blocks.

• Reduces Code Duplication: If different conditions share common logic, nested switch
statements can help avoid redundant code.

• Enhances Performance: Switch statements generally perform better than long if-else chains,
reducing conditional checks.

• More Modular Code: Separating logic into different levels makes the code easier to maintain
and modify.

7.7.4- Limitations of Nested Switch Statements

• Readability Issues: Excessive nesting can make the code difficult to follow and maintain.

• Code Duplication Risks: Improper use can lead to unnecessary repetition of logic.

• Limited Efficiency: In some cases, using polymorphism or conditional expressions might be


a more efficient approach.

204
The Complete Java Training Imran Afzal
• Restricted Data Types: Switch statements only support certain data types like integers,
characters, and enumerations.

• Limited Scalability: Managing a large number of nested conditions can become complex and
unwieldy.

7.7.5- Java Program for Nested Switch Statement

This program categorizes students into different grades and then further categorizes their subjects
using a nested switch statement.

public class NestedSwitchExample {

public static void main(String[] args) {

int grade = 2;

String subject = "Mathematics";

switch (grade) {

case 1:

System.out.println("Grade: First Year");

switch (subject) {

case "Mathematics":

System.out.println("Subject: Calculus");

break;

case "Physics":

205
The Complete Java Training Imran Afzal
System.out.println("Subject: Mechanics");

break;

default:

System.out.println("Unknown Subject");

break;

break;

case 2:

System.out.println("Grade: Second Year");

switch (subject) {

case "Mathematics":

System.out.println("Subject: Algebra");

break;

case "Physics":

System.out.println("Subject: Thermodynamics");

break;

default:

206
The Complete Java Training Imran Afzal
System.out.println("Unknown Subject");

break;

break;

default:

System.out.println("Invalid Grade");

break;

7.7.6- Program Execution and Output

• Grade: Second Year


• Subject: Algebra
• If grade = 1 and subject = "Physics", the output would be:
• Grade: First Year
• Subject: Mechanics

This example demonstrates nested decision-making, where the outer switch determines the grade
level, and the inner switch further classifies the subject.

207
The Complete Java Training Imran Afzal
7.8- Traditional Nested Switch Statement Exercises

7.8.1- Understanding Nested Switch Statements

A nested switch statement in Java allows a switch statement to be placed inside another switch
statement. This approach is useful when multiple conditions need to be evaluated in a hierarchical
manner. The outer switch evaluates one condition, and depending on the result, the inner switch
evaluates another condition, providing more specific control over the program’s execution.

7.8.2- Example 1: Evaluating Two Integer Variables

This program evaluates two integer variables, x and y, using nested switch statements. The outer switch
checks the value of x, and the inner switch checks y to determine a corresponding message.

7.8.3- Java Program for Nested Switch Using Integer Variables

// Online Java Compiler

// Use this editor to write, compile and run your Java code online

class TraditionalSwitchExample{

public static void main(String[] args) {

int x = 2, y = 3;

switch (x) {

case 1:

switch (y) {

case 2:

208
The Complete Java Training Imran Afzal
System.out.println("x is 1 and y is 2");

break;

case 3:

System.out.println("x is 1 and y is 3");

break;

break;

case 2:

switch (y) {

case 2:

System.out.println("x is 2 and y is 2");

break;

case 3:

System.out.println("x is 2 and y is 3");

break;

default:

209
The Complete Java Training Imran Afzal
System.out.println("x is 1 and y is 3");

break;

break;

7.8.4- Program Execution and Output

x is 2 and y is 3

Since x = 2 and y = 3, the program first evaluates x in the outer switch. Since x matches case 2, the
inner switch evaluates y, and because y = 3, it prints "x is 2 and y is 3".

7.8.5- Example 2: Printing Elective Courses Based on Year and Branch

This program determines a student's elective courses based on their year of study and branch using
nested switch statements.

7.8.6- Java Program for Nested Switch Using Year and Branch

class HelloWorld {

public static void main(String[] args) {

String Branch = "CSE";

int year = 2;

210
The Complete Java Training Imran Afzal
switch (year) {

case 1:

System.out.println("elective courses : Advance english, Algebra");

break;

case 2:

switch (Branch) {

case "CSE":

case "CCE":

System.out.println("elective courses : Machine Learning, Big Data");

break;

case "ECE":

System.out.println(

"elective courses : Antenna Engineering");

break;

default:

System.out.println("Elective courses : Optimization");

break;

211
The Complete Java Training Imran Afzal
}}

7.8.7- Program Execution and Output

Elective Courses: Machine Learning, Big Data

Since year = 2 and branch = "CSE", the outer switch matches case 2, and the inner switch evaluates
the branch. Since the branch is "CSE", it prints "Elective Courses: Machine Learning, Big Data".

7.8.8- Example 3: Selecting Pizza Size and Toppings

This program takes user input for pizza size (S, M, L) and toppings (P, C, V) and uses nested switch
statements to determine the final order.

7.8.9- Java Program for Pizza Selection Using Nested Switch

public class PizzaOrder {

public static void main(String[] args) {

char size = 'L', toppings = 'C';

switch (size) {

case 'S':

switch (toppings) {

212
The Complete Java Training Imran Afzal
case 'P':

System.out.println("Small pizza with Pepperoni topping");

break;

case 'C':

System.out.println("Small pizza with Cheese topping");

break;

case 'V':

System.out.println("Small pizza with Vegetable topping");

break;

default:

System.out.println("Invalid topping!");

break;

break;

case 'M':

switch (toppings) {

213
The Complete Java Training Imran Afzal
case 'P':

System.out.println("Medium pizza with Pepperoni topping");

break;

case 'C':

System.out.println("Medium pizza with Cheese topping");

break;

case 'V':

System.out.println("Medium pizza with Vegetable topping");

break;

default:

System.out.println("Invalid topping!");

break;

break;

case 'L':

switch (toppings) {

214
The Complete Java Training Imran Afzal
case 'P':

System.out.println("Large pizza with Pepperoni topping");

break;

case 'C':

System.out.println("Large pizza with Cheese topping");

break;

case 'V':

System.out.println("Large pizza with Vegetable topping");

break;

default:

System.out.println("Invalid topping!");

break;

break;

default:

System.out.println("Invalid pizza size!");

215
The Complete Java Training Imran Afzal
break;

7.8.10- Program Execution and Output

Large pizza with Cheese topping

Since size = 'L' and toppings = 'C', the program first evaluates the outer switch for size. Since it
matches case 'L', the inner switch evaluates the toppings. Since toppings = 'C', it prints "Large pizza
with Cheese topping".

Each example here demonstrates different use cases of nested switch statements, showing how
they help organize code effectively.

7.9- Introduction to Loops in Java

7.9.1- Overview of Loops in Java

Loops are fundamental control structures in Java that allow code execution multiple times based on a
specified condition. Java provides three types of loops: for loop, while loop, and do-while loop.
These looping mechanisms reduce code redundancy and enhance efficiency by automating repetitive
tasks.

7.9.2- Types of Loops in Java

1. For Loop - Used when the number of iterations is known beforehand. It consists of
initialization, condition checking, and increment/decrement steps.

2. While Loop - Used when the number of iterations is unknown; the loop continues executing
as long as the condition remains true.

216
The Complete Java Training Imran Afzal
3. Do-While Loop - Similar to the while loop but executes at least once before checking the
condition.

7.9.3- Advantages of Loops in Java

• Code Reusability: Allows execution of repetitive tasks without manually writing multiple
lines.

• Efficiency: Reduces the complexity of the code by minimizing redundancy.

• Flexibility: Loops execute code dynamically based on user input or runtime conditions.

• Scalability: Enables processing of large datasets efficiently.

• Improved Readability: Makes the code more structured and easier to maintain.

7.9.4- Limitations of Loops in Java

• Performance Issues: Inefficient loop design can consume excessive memory and CPU
cycles.

• Infinite Loops: Improper condition handling may cause loops to run indefinitely, leading to
program crashes.

• Complexity: Nested loops or complex conditions can make code difficult to debug.

• Code Readability: Excessive use of loops can reduce code clarity if not well-structured.

7.9.5- For Loop in Java

The for loop executes a block of code for a fixed number of times, defined by initialization, condition
checking, and an update statement.

7.9.6- Syntax of For Loop

for (initialization; condition; increment/decrement) {

// Code to be executed

217
The Complete Java Training Imran Afzal
}

7.9.7- Example: Printing Numbers from 0 to 9 Using For Loop

public class ForLoopExample {

public static void main(String[] args) {

for (int i = 0; i < 10; i++) {

System.out.println(i);

7.9.8- Execution Flow of For Loop

1. Initialization: Executes once before the loop starts (int i = 0).

2. Condition Checking: If i < 10 is true, the loop body executes.

3. Code Execution: The statement inside the loop executes.

4. Increment/Decrement: Updates the loop counter (i++).

5. Repeat: Steps 2–4 repeat until the condition becomes false.

Output

218
The Complete Java Training Imran Afzal
4

7.9.9- While Loop in Java

The while loop continues executing as long as its condition remains true. Unlike the for loop, it does
not require initialization or increment within its declaration.

7.9.10- Syntax of While Loop

while (condition) {

// Code to be executed

7.9.11- Example: Printing Numbers from 0 to 9 Using While Loop

public class WhileLoopExample {

public static void main(String[] args) {

int i = 0;

while (i < 10) {

System.out.println(i);

i++; // Incrementing i to avoid infinite loop

219
The Complete Java Training Imran Afzal
}

7.9.12- Execution Flow of While Loop

1. Condition Checking: If i < 10 is true, the loop body executes.

2. Code Execution: The statement inside the loop executes.

3. Increment/Decrement: Updates the loop counter (i++).

4. Repeat: Steps 1–3 repeat until the condition becomes false.

Output

7.9.13- Do-While Loop in Java

The do-while loop guarantees at least one execution of the loop body before condition checking.

220
The Complete Java Training Imran Afzal
7.9.14- Syntax of Do-While Loop

do {

// Code to be executed

} while (condition);

7.9.15- Example: Executing Loop Once Before Checking Condition

public class DoWhileExample {

public static void main(String[] args) {

int i = 1;

do {

System.out.println(i);

i++;

} while (i <= 5); // Loop runs while i is less than or equal to 5

7.9.16- Execution Flow of Do-While Loop

1. Code Execution: Executes the loop body at least once.

2. Condition Checking: After execution, the condition i > 5 is checked.

3. Termination: Since i = 1 and i > 5 is false, the loop terminates.

221
The Complete Java Training Imran Afzal
Output

7.9.17- Key Differences Between For, While, and Do-While Loops

Feature For Loop While Loop Do-While Loop

Use Case When the number of When the number of When at least one
iterations is known iterations is unknown execution is required

Condition Check Before execution Before execution After execution

Execution May not execute May not execute Executes at least once
Guarantee

Initialization & Inside loop statement Outside loop Outside loop


Update

7.9.18- Best Practices for Using Loops in Java

• Use meaningful variable names for loop counters (i, j, etc.).

• Avoid infinite loops by ensuring conditions eventually become false.

• Optimize performance by minimizing redundant calculations inside loops.

• Use enhanced loops (for-each) for iterating collections instead of traditional loops.

• Prefer while loops when the number of iterations is unpredictable.

• Use break and continue wisely to control loop execution efficiently.

Loops are essential in Java for handling repetitive tasks efficiently. Understanding the differences
between for, while, and do-while loops helps in writing optimized and maintainable code.

7.10- For Loop

The for loop is one of the most commonly used loop structures in Java. It is used when the number
of iterations is known beforehand. The for loop consists of three main components: initialization,

222
The Complete Java Training Imran Afzal
condition checking, and increment/decrement. These components control the loop execution
and determine how many times the loop will iterate.

The for loop executes a block of code repeatedly until a specified condition is met. This makes it
useful for tasks that require counting or performing an operation a known number of times.

7.10.1- For Loop Syntax in Java

for (initialization; condition; increment/decrement) {

// Code to be executed

• Initialization: Executes once before the loop starts. It initializes the loop control variable.

• Condition: Checked before each iteration. If true, the loop executes; otherwise, it terminates.

• Increment/Decrement: Updates the loop variable after each iteration.

7.10.2- Example 1: Printing Numbers from 1 to 5 Using For Loop

public class ForLoop {

public static void main(String[] args) {

for (int i = 1; i <= 5; i++) {

System.out.println(i);

223
The Complete Java Training Imran Afzal
Execution Flow

1. The loop starts with i = 1.

2. The condition i <= 5 is checked. If true, the loop executes.

3. The value of i is printed.

4. The value of i is incremented using i++.

5. Steps 2–4 repeat until i becomes 6, at which point the condition becomes false, and the loop
terminates.

Output

7.10.3- Example 2: Skipping Number 5 and Exiting at Number 8

public class ForLoop1 {

public static void main(String[] args) {

for (int i = 1; i <= 10; i++) {

if (i == 5) {

continue; // Skip number 5

224
The Complete Java Training Imran Afzal
if (i == 8) {

break; // Exit loop when i is 8

System.out.println(i);

Execution Flow

1. The loop starts with i = 1.

2. If i == 5, the continue statement skips the current iteration.

3. If i == 8, the break statement exits the loop entirely.

4. The remaining numbers are printed.

Output

225
The Complete Java Training Imran Afzal
7.10.4- Example 3: Checking Prime Numbers Using For Loop

What is a Prime Number?

A prime number is a number that is divisible only by 1 and itself. Examples: 2, 3, 5, 7, 11, 13, 17, 19,
23. A number that is divisible by any other number apart from 1 and itself is not a prime number.

Algorithm to Find a Prime Number

1. If the number is less than 2, it is not prime.

2. If the number is exactly 2, it is prime.

3. Check divisibility for numbers from 2 to n/2. If any number divides n completely, it is not
prime.

Java Program to Check If a Number is Prime

Part 1

class HelloWorld {

public static void main(String[] args) {

System.out.println("0 is " + (isPrime(0) ? "" : "Not ") + "a prime number.");

System.out.println("1 is " + (isPrime(1) ? "" : "Not ") + "a prime number.");

System.out.println("2 is " + (isPrime(2) ? "" : "Not ") + "a prime number.");

System.out.println("3 is " + (isPrime(3) ? "" : "Not ") + "a prime number.");

System.out.println("8 is " + (isPrime(8) ? "" : "Not ") + "a prime number.");

System.out.println("17 is " + (isPrime(17) ? "" : "Not ") + "a prime number.");

226
The Complete Java Training Imran Afzal
System.out.println("21 is " + (isPrime(21) ? "" : "Not ") + "a prime number.");

public static boolean isPrime(int wholeNumner) {

if(wholeNumner <= 2){

return (wholeNumner == 2);

for (int divisor = 2; divisor < wholeNumner; divisor++) {

if (wholeNumner % divisor == 0) {

return false;

return true;

227
The Complete Java Training Imran Afzal
}

Part 2

// Online Java Compiler

// Use this editor to write, compile and run your Java code online

class HelloWorld {

public static void main(String[] args) {

System.out.println("0 is " + (isPrime(0) ? "" : "Not ") + "a prime number.");

System.out.println("1 is " + (isPrime(1) ? "" : "Not ") + "a prime number.");

System.out.println("2 is " + (isPrime(2) ? "" : "Not ") + "a prime number.");

System.out.println("3 is " + (isPrime(3) ? "" : "Not ") + "a prime number.");

System.out.println("8 is " + (isPrime(8) ? "" : "Not ") + "a prime number.");

System.out.println("17 is " + (isPrime(17) ? "" : "Not ") + "a prime number.");

System.out.println("21 is " + (isPrime(21) ? "" : "Not ") + "a prime number.");

228
The Complete Java Training Imran Afzal
}

public static boolean isPrime(int wholeNumner) {

if(wholeNumner <= 2){

return (wholeNumner == 2);

for (int divisor = 2; divisor < wholeNumner; divisor++) {

if (wholeNumner % divisor == 0) {

return false;

return true;

229
The Complete Java Training Imran Afzal
Execution Flow

1. If number <= 2, return true for 2, false otherwise.

2. Loop from 2 to number-1, checking divisibility.

3. If number % divisor == 0, return false.

4. If no divisibility is found, return true.

Output

0 is not a prime number

1 is not a prime number

2 is a prime number

3 is a prime number

8 is not a prime number

17 is a prime number

21 is not a prime number

7.10.5- Optimization: Reducing Iterations to Half

Instead of checking all numbers up to n-1, we can reduce iterations by half since any factor greater
than n/2 will already have a smaller corresponding factor.

Optimized Code Using n/2 Approach

public class OptimizedPrimeCheck {

public static boolean isPrime(int number) {

if (number <= 2) {

return number == 2;

230
The Complete Java Training Imran Afzal
}

for (int divisor = 2; divisor <= Math.sqrt(number); divisor++) {

if (number % divisor == 0) {

return false;

return true;

public static void main(String[] args) {

int[] testNumbers = {0, 1, 2, 3, 8, 17, 21};

for (int num : testNumbers) {

System.out.println(num + " is " + (isPrime(num) ? "" : "not ") + "a prime number");

231
The Complete Java Training Imran Afzal
Output remains the same, but with improved performance.

7.11- For Loop Exercise

7.11.1- Checking Odd Numbers and Summing Odd Numbers in a Given Range

The for loop can be used to iterate through a range of numbers and perform operations such as
checking for odd numbers and summing them. This program consists of two main methods:

1. CheckOdd – Determines if a number is odd.

2. sumOdd – Sums all odd numbers within a given range.

7.11.2- Creating the CheckOdd Method

public class SumOddNumber {

public static boolean CheckOdd(int number) {

if (number <= 0) {

return false;

return (number % 2 == 1);

• If number is less than or equal to zero, it returns false, as odd numbers are always positive.

• The modulus operator (% 2 == 1) checks if the number has a remainder when divided by 2,
meaning it is odd.

232
The Complete Java Training Imran Afzal
7.11.3- Creating the sumOdd Method

public static int sumOdd(int startNumner, int endNumner) {

if (startNumner <= 0 || endNumner <= 0 || endNumner < startNumner) {

return -1;

int sum = 0;

for (int i = startNumner; i <= endNumner; i++) {

if (CheckOdd(i)) {

sum += i;

return sum;

• The method first checks if the input values are valid. If startNumber or endNumber is
negative or zero or endNumber is smaller than startNumber, it returns -1 (invalid input).

• The for loop iterates through the given range (startNumber to endNumber), calling
CheckOdd(i).

• If i is odd, it is added to the sum.

7.11.4- Calling the Methods in the Main Method

public static void main(String[] args) {

int startNumner = 1;

int endNumner = 100;

int sum = sumOdd(startNumner, endNumner);

233
The Complete Java Training Imran Afzal
if (sum == -1) {

System.out.println("Invalid input");

} else {

System.out.println("The sum of odd numbers between " + startNumner + " and " +
endNumner + " is " + sum);

• sumOdd(1, 100) calculates the sum of all odd numbers from 1 to 100.

• If the sum returns -1, it prints "Invalid input". Otherwise, it displays the result.

Output

The sum of odd numbers between 1 and 100 is 2500

7.11.5- Multiplication Table Generator Using For Loop

A multiplication table can be easily generated using a for loop. The loop iterates from 1 to 10,
multiplying a given number num by the loop index i.

7.11.6- Creating the Multiplication Table Program

public class TableOfFive {

public static void main(String[] args) {

int num = 5;

for (int i = 1; i <= 10; i++) {

System.out.println(num + " x " + i + " = " + (num * i));

234
The Complete Java Training Imran Afzal
}

• The loop runs 10 times, from 1 to 10.

• Each iteration multiplies num by i and prints the result in the format "num × i = result".

Output (Table of 5)

5×1=5

5 × 2 = 10

5 × 3 = 15

5 × 4 = 20

5 × 5 = 25

5 × 6 = 30

5 × 7 = 35

5 × 8 = 40

5 × 9 = 45

5 × 10 = 50

Modifying num = 11 will print the multiplication table of 11.

7.12- Introduction to Debugger

7.12.1- Understanding the Debugger and Its Importance

A debugger is a software tool used by programmers to identify and fix errors in their code. It allows
developers to step through their program, inspect variables and memory, set breakpoints, and track

235
The Complete Java Training Imran Afzal
the flow of execution. Debuggers play a crucial role in software development by helping developers
detect and correct bugs, errors, and performance issues that could affect the program's quality and
functionality.

There are two main types of debuggers:

1. Source-Level Debuggers – Used to debug code written in high-level programming languages


like Java or Python.

2. Machine-Level Debuggers – Used for debugging at a lower level, such as assembly language,
to inspect memory and processor instructions.

7.12.2- Key Features of a Debugger

A debugger provides several features that simplify error detection and correction:

• Breakpoints – Developers can pause the program at specific points to examine variable states.

• Stepping – Developers can execute code line by line to observe how it runs.

• Variable Inspection – Enables checking of variable values at any execution point.

• Memory Inspection – Helps inspect memory usage and allocation.

• Call Stack Inspection – Allows viewing of function calls and returns.

• Watch Expressions – Monitors variables and expressions over time.

7.12.3- Debugger vs. Debugging

A debugger is a tool used in the debugging process. Debugging is the practice of identifying,
analyzing, and fixing issues in code, while a debugger is a tool that assists in this process. Debugging
can be done manually, using print statements, or by using debugging tools.

7.12.4- Debugging Process

The debugging process typically follows a structured approach:

1. Reproduce the Error – Identify and recreate the error in a controlled environment.

236
The Complete Java Training Imran Afzal
2. Identify the Cause – Analyze the code using breakpoints, logging, and tracing to pinpoint
the issue.

3. Develop a Fix – Modify the code to resolve the issue.

4. Test the Fix – Validate that the issue is resolved and does not introduce new errors.

5. Deploy the Fix – Apply the fix to the production environment.

6. Monitor the Fix – Ensure the fix remains effective over time.

7.12.5- Common Coding Errors That Require Debugging

Several types of coding errors necessitate debugging:

• Syntax Errors – Occur when code violates the programming language rules (e.g., missing
semicolons, typos).

• Logical Errors – Happen when the code runs but does not produce the expected results due
to incorrect logic.

• Runtime Errors – Errors that occur while the program is running, such as division by zero
or accessing invalid memory addresses.

• Memory Leaks – Caused when allocated memory is not released, leading to slow
performance or crashes.

• Deadlocks – Occur when multiple processes or threads are blocked waiting for each other,
causing a system freeze.

• Race Conditions – Happen when multiple threads access shared data, leading to
unpredictable behavior.

• Input/Output Errors – Occur when the program cannot read/write data due to issues like
incorrect file permissions or network failures.

7.12.6- Limitations of Using Debuggers

While debuggers are invaluable in software development, they have some limitations:

237
The Complete Java Training Imran Afzal
• Overreliance on Debuggers – Relying too much on debuggers can limit a developer’s
problem-solving and coding skills.

• Performance Overhead – Debuggers can slow down program execution, making them
unsuitable for performance-critical applications.

• Complexity – Debugging tools can be challenging to use, especially for beginners.

• Incomplete or Inaccurate Information – Debugging tools may not always provide full
insights, particularly when external factors like network issues affect the program.

• Security Risks – Attackers can use debugging tools to exploit software vulnerabilities, making
security precautions essential when debugging production software.

7.13- Debugger in IntelliJ IDEA

7.13.1- Using Debugger in IntelliJ IDEA

A debugger is an essential tool for finding and fixing errors in code. IntelliJ IDEA provides a built-in
debugger for Java that allows developers to analyze and troubleshoot their programs efficiently. The
debugging process involves setting breakpoints, inspecting variables, and stepping through code
execution to understand its behavior.

7.13.2- Creating a Program for Debugging

To demonstrate how debugging works, a program is written that loops through numbers from 1 to
1500, finds numbers divisible by both 7 and 11, prints them, and stops when four such numbers are
found.

Setting Up the Program

1. Create a new class in IntelliJ IDEA named ForLoopExample.

2. Write the main method using the shortcut psvm.

3. Initialize variables:

int count = 0;

238
The Complete Java Training Imran Afzal
The count variable keeps track of how many numbers meet the condition.

4. Implement a for loop that iterates from 1 to 1500:

for (int i = 1; i <= 1500; i++) {

• i = 1 initializes the loop variable.


• i <= 1500 ensures the loop runs up to 1500.
• i++ increments the variable in each iteration.

5. Check divisibility by 7 and 11:

if (i % 7 == 0 && i % 11 == 0) {

• % 7 == 0 checks divisibility by 7.
• % 11 == 0 checks divisibility by 11.
• Both conditions must be true for a number to be considered.

6. Print matching numbers and count occurrences:

System.out.println("Number divisible by 7 and 11: " + i);

count++;

o The number is printed if it meets the criteria.


o count++ increments the counter.

7. Break out of the loop after finding four numbers:


if (count == 4) {

break;

• Once four numbers are found, the loop terminates.

239
The Complete Java Training Imran Afzal
Running the Program

After executing the program, the output is:

Number divisible by 7 and 11: 77

Number divisible by 7 and 11: 154

Number divisible by 7 and 11: 231

Number divisible by 7 and 11: 308

These four numbers meet the condition and are printed before the loop terminates.

7.13.3- Understanding Breakpoints in IntelliJ IDEA

What Are Breakpoints?

Breakpoints are special markers that pause program execution at specific lines in the code. This allows
developers to examine variable values, step through execution, and understand program behavior.

Setting a Breakpoint in IntelliJ IDEA

1. Find the line number where execution should pause.

2. Click in the gray margin next to the line number.

3. A red circle appears, indicating a breakpoint.

For this program, logical places to add breakpoints include:

• The start of the loop (to analyze iteration behavior).

• The condition checking (if (i % 7 == 0 && i % 11 == 0)).

• The count incrementing line (count++).

• The break statement (to observe loop termination).

240
The Complete Java Training Imran Afzal
Running the Debugger

To run the code in debug mode:

1. Right-click on the editor and select Debug instead of Run.

2. Click the debug icon (right next to the run icon).

3. Use Shift + F9 (shortcut for debugging).

4. The debug tool window appears, showing breakpoints, variable states, and the execution
flow.

Inspecting Variables in the Debugger

• The middle panel displays method arguments and local variables.

• The right panel allows adding watch expressions to track values dynamically.

• i % 7 and i % 11 can be added to the watch list to monitor remainder values.

Before the loop starts, i does not exist in memory. Once the loop begins, i updates on each iteration,
and the debugger tracks changes in real-time.

Using Debugger Controls

Several debugger controls help navigate the program:

• Resume Program – Continues execution until the next breakpoint.

• Pause Program – Halts execution at any point.

• Step Over – Executes one line at a time without entering method calls.

• Step Into – Moves inside method calls for deeper analysis.

• Step Out – Exits the current method and returns to the caller.

Stepping Through the Code

1. Step Over (F8) is used to move line by line through the loop.

241
The Complete Java Training Imran Afzal
2. When i == 7, only i % 7 == 0 evaluates to true, while i % 11 == 0 is false.

3. When i == 77, both conditions become true, and the number is printed.

4. count++ increments the counter, tracking found numbers.

5. The loop continues until count == 4, where break stops execution.

Modifying Breakpoints for Efficiency

Initially, the breakpoint is set at loop start. Later, it is moved to:

• Line 5 – To pause before checking conditions.

• Line 7 – To pause when a matching number is found.

• Line 9 – To pause before the loop exits.

With these modifications, execution only stops when significant conditions are met.

Final Debugging Insights

• 308 iterations are needed to find four matching numbers.

• Debugging reveals variable changes at each iteration.

• Breakpoints allow efficient tracking of specific conditions.

• The debugger helps visualize program flow and logic errors.

By strategically setting breakpoints and using stepping functions, the debugger simplifies error
identification, making troubleshooting more effective.

7.14- For Loop Quick Summary

7.14.1- Understanding the For Loop Structure

A for loop consists of three main parts:

1. Initialization – Defines and initializes a loop control variable.

242
The Complete Java Training Imran Afzal
2. Condition – Determines when the loop should continue or terminate.

3. Increment/Decrement – Updates the loop control variable after each iteration.

Each execution of the loop is known as an iteration. The loop will keep executing until the condition
evaluates to false. The loop body, enclosed in curly braces {}, contains the code that executes in each
iteration. If the loop body consists of multiple statements, they all execute in every iteration.

7.14.2- Example of a For Loop

The following for loop prints values of myNumber in increments of 3, starting from 0, and stopping
before 12.

for(int myNumber = 0; myNumber < 12; myNumber += 3){

System.out.println(myNumber);

7.14.3- Breaking Down the Code Execution

• Initialization: myNumber = 0 (First step, executed only once).

• Condition Check: myNumber < 12. If true, the loop executes; otherwise, it terminates.

• Loop Execution: The statement inside {} executes, printing the current value of myNumber.

• Increment: myNumber increases by 3 (myNumber += 3).

7.14.4- Step-by-Step Execution of the Loop

Iteration 1:

• Initialization: myNumber = 0

• Condition Check: 0 < 12 → true

• Execution: Prints myNumber = 0

243
The Complete Java Training Imran Afzal
• Increment: myNumber = 0 + 3 = 3

Iteration 2:

• Condition Check: 3 < 12 → true

• Execution: Prints myNumber = 3

• Increment: myNumber = 3 + 3 = 6

Iteration 3:

• Condition Check: 6 < 12 → true

• Execution: Prints myNumber = 6

• Increment: myNumber = 6 + 3 = 9

Iteration 4:

• Condition Check: 9 < 12 → true

• Execution: Prints myNumber = 9

• Increment: myNumber = 9 + 3 = 12

Iteration 5 (Loop Exit Condition Met):

• Condition Check: 12 < 12 → false

• Loop Terminates: Execution moves beyond the closing curly brace {}.

Loop Output

myNumber = 0

myNumber = 3

myNumber = 6

myNumber = 9

244
The Complete Java Training Imran Afzal
7.14.5- Key Takeaways

• The for loop executes a block of code repeatedly while the condition is true.

• The loop control variable updates after each iteration.

• The loop terminates when the condition becomes false.

• The loop skips numbers that do not match the increment step (e.g., 1, 2, 4, 5, 7, 8, 10, 11 are
not printed because the increment step is +3).

By understanding these steps, for loops become an essential tool in Java for executing repetitive tasks
efficiently.

7.15- The While Statements

7.15.1- Understanding the While Loop in Java

The while loop is used when the number of iterations is unknown. It continues executing a block of
code as long as its condition remains true. The loop consists of:

1. Initialization – Declaring and initializing a control variable (done before the loop).

2. Condition – A Boolean expression that determines if the loop continues.

3. Loop Body – The block of code that executes repeatedly.

4. Update (Increment/Decrement) – A way to modify the loop variable inside the loop to
eventually make the condition false.

7.15.2- Example: Printing Numbers from 10 to 1 using a While Loop

class Whileloop{

public static void main(String[] args) {

int number = 10;

while (number >= 1) {

System.out.println(number);

245
The Complete Java Training Imran Afzal
number--;

Execution Process

1. number starts at 10.

2. The condition number >= 1 is checked.

3. If true, number is printed, then decremented by 1.

4. The loop continues until number < 1.

5. When number becomes 0, the condition fails, and the loop exits.

Output

10

246
The Complete Java Training Imran Afzal
7.15.3- Handling Infinite Loops

If the loop condition is always true, it results in an infinite loop, continuously printing numbers
indefinitely.

while (true) {

if (number < 1) {

break; // Terminates the loop

System.out.println(number);

number--;

7.15.4- Example: Finding Even and Odd Numbers in a Given Range

public class EvenOdd {

public static void main(String[] args) {

int start = 5, end = 11;

while(start <= end){

checkEvenOdd(start);

start++;

247
The Complete Java Training Imran Afzal
}

public static void checkEvenOdd(int num) {

if(num % 2 == 0) {

System.out.println(num + " is even");

else {

System.out.println(num + " is odd");

Execution Process

1. The loop runs from start = 5 to end = 11.

2. checkEvenOdd() determines if the number is even or odd.

3. The loop increments start until start > end.

Output

5 is odd

248
The Complete Java Training Imran Afzal
6 is even

7 is odd

8 is even

9 is odd

10 is even

11 is odd

7.15.5- Example: Sum of First and Last Digit of a Number

public class SumofDigits {

public static int sumOfFirstAndLastDigit(int number) {

if (number < 0) {

return -1;

int lastDigit = number % 10;

int firstDigit = number;

while (firstDigit >= 10) {

firstDigit /= 10;

249
The Complete Java Training Imran Afzal
return firstDigit + lastDigit;

public static void main(String[] args) {

int number = 123456;

int sum = sumOfFirstAndLastDigit(number);

System.out.println("Sum of first and last digit of " + number + " is: " + sum);

number = -123;

sum = sumOfFirstAndLastDigit(number);

System.out.println("Sum of first and last digit of " + number + " is: " + sum);

Execution Process

1. Extract the last digit using % 10.

2. Extract the first digit by dividing the number by 10 repeatedly.

3. Return the sum of first and last digits.

250
The Complete Java Training Imran Afzal
Output

Sum of first and last digit of 123456 is: 7

Sum of first and last digit of -123 is: -1

7.15.6- Key Takeaways

• The while loop executes as long as the condition is true.

• Initialization is done before the loop, and update logic inside the loop.

• Avoid infinite loops by ensuring the condition eventually becomes false.

• The break statement can exit a loop manually when needed.

• The while loop is useful for cases where iterations depend on user input, calculations,
or unknown conditions.

7.16- The While Statements Exercise Part 1

7.16.1- Checking for a Palindrome Number

A palindrome number is a number that reads the same forward and backward. For example, 12321
is a palindrome because reversing its digits gives 12321.

7.16.2- Example: Check if a Number is a Palindrome

public class PalindromeChecker {

public static void main(String[] args) {

int num = 16461;

if (CheckPalindrome(num)) {

System.out.println(num + " is a palindrome number");

} else {
251
The Complete Java Training Imran Afzal
System.out.println(num + " is not a palindrome number");

public static boolean CheckPalindrome(int number) {

int palindrome = number;

int reverse = 0;

while (palindrome != 0) {

int remainder = palindrome % 10;

reverse = reverse * 10 + remainder;

palindrome = palindrome / 10;

return (number == reverse);

252
The Complete Java Training Imran Afzal
Execution Process

1. Extract each digit using % 10.

2. Append digits to reverse by multiplying reverse by 10 and adding the extracted digit.

3. Remove the last digit from palindrome using /= 10.

4. Compare the reversed number with the original.

Output

16461 is a palindrome number.

Changing the number to 16460 produces:

16460 is not a palindrome number.

7.16.3- Summing Even Digits in a Number

A method that calculates the sum of all even digits in a given number. If the number is negative, it
returns -1 as an invalid input.

Example: Sum of Even Digits

public class EvenNumSum {

public static int EvenNumSum(int number) {

if (number < 0) {

return -1; // return -1 if number is negative

253
The Complete Java Training Imran Afzal
int sum = 0;

while (number > 0) {

int digit = number % 10;

if (digit % 2 == 0) {

sum += digit; // add even digits to sum

number /= 10;

return sum;

public static void main(String[] args) {

int number = 2468;

int evenSum = EvenNumSum(number);

System.out.println("The sum of even digits in " + number + " is " + evenSum);

int negativeNumber = -1234;

254
The Complete Java Training Imran Afzal
int negativeEvenSum = EvenNumSum(negativeNumber);

System.out.println("The sum of even digits in " + negativeNumber + " is " + negativeEvenSum);

Execution Process

1. Extract each digit using % 10.

2. Check if the digit is even (% 2 == 0).

3. Add even digits to sum.

4. Remove the last digit using /= 10.

5. Return sum or -1 for negative input.

Output

The sum of even digits in 2468 is: 20

The sum of even digits in -1234 is: -1

7.16.4- Finding Factors of a Number

A factor of a number is an integer that divides it completely without leaving a remainder.

Example: Factors of 10 are 1, 2, 5, and 10 because:

• 10 / 1 = 10

• 10 / 2 = 5

• 10 / 5 = 2

• 10 / 10 = 1

255
The Complete Java Training Imran Afzal
7.16.5- Example: Finding Factors of a Number

public class FactorPrinter {

public static void main(String[] args) {

GetFactors(10); // Example usage

public static void GetFactors(int number) {

if (number < 1) {

System.out.println("Invalid Value");

return;

int i = 1;

System.out.print("Factors of " + number + ": ");

while (i <= number) {

if (number % i == 0) {

256
The Complete Java Training Imran Afzal
System.out.print(i + " ");

i++;

System.out.println();

Execution Process

1. Start from 1 and loop until number.

2. Check if number % i == 0 (i.e., i is a factor).

3. Print all numbers that divide number without a remainder.

4. Return "Invalid Value" if input is less than 1.

Output

Factors of 10 are: 1 2 5 10

Factors of 20 are: 1 2 4 5 10 20

7.16.6- Key Takeaways

• The while loop runs as long as its condition is true.

• Palindrome check reverses the number and compares it with the original.

257
The Complete Java Training Imran Afzal
• Summing even digits extracts and sums even numbers in a loop.

• Finding factors iterates from 1 to number and checks divisibility.

• Handling invalid input is crucial to prevent runtime errors.

These exercises help in understanding looping, conditional checks, and modulus operations in
Java efficiently.

7.17- The While Statements Exercise

7.17.1- Finding the Greatest Common Divisor (GCD) in Java

The Greatest Common Divisor (GCD) of two numbers is the largest integer that divides both
numbers without leaving a remainder.

Example

• GCD of 12 and 18 is 6 because 6 is the largest number that divides both 12 and 18 without
leaving a remainder.

7.17.2- Euclidean Algorithm for GCD

1. Divide the larger number by the smaller number.

2. Take the remainder of the division.

3. Replace the larger number with the smaller number.

4. Replace the smaller number with the remainder.

5. Repeat the process until the remainder becomes zero.

6. The last non-zero remainder is the GCD.

7.17.3- Example: Find the Greatest Common Divisor in Java

public class GCD {

public static int CheckGreatestCommonDivisor(int first, int second) {

258
The Complete Java Training Imran Afzal
if (first < 10 || second < 10) {

return -1;

while (second != 0) {

int temp = second;

second = first % second;

first = temp;

return first;

public static void main(String[] args) {

int a = 24;

int b = 36;

int result = CheckGreatestCommonDivisor(a, b);

System.out.println("Greatest common divisor of " + a + " and " + b + " is " + result);

259
The Complete Java Training Imran Afzal
}

Execution Process

1. Check if any number is less than 10. If so, return -1 to indicate an invalid input.

2. Loop while second is not 0

• Store second in temp.


• Compute second = first % second.
• Assign temp to first.

3. Return first as the GCD once the loop exits.

Output

Greatest common divisor of 24 and 36 is 12

Changing a = 81 and b = 54:

Greatest common divisor of 81 and 54 is 27

This program efficiently calculates the Greatest Common Divisor (GCD) using the while loop and
modulus operator (%), making it an optimal and structured approach in Java.

7.18- Do-While Statement

7.18.1- Introduction to Do-While Loop in Java

A do-while loop is similar to a while loop, but with one key difference:

• It executes at least once before checking the loop condition.

• The condition is evaluated after the loop body, ensuring that the loop runs at least once even
if the condition is false.

260
The Complete Java Training Imran Afzal
7.18.2- Common Use Cases of Do-While Loop

1. Reading User Input – Ensures that user input is processed at least once.

2. File or Database Processing – Reads records until no more data is available.

3. Performing Calculations – Continues calculations until a specific condition is met.

4. Iterating Through Collections – Ensures at least one iteration even if the collection is
empty.

5. Implementing Game Logic – Repeats actions like user choices until a stopping condition is
met.

7.18.3- Example 1: Do-While Loop Executes Once

class HelloWorld {

public static void main(String[] args) {

int i = 6;

do {

System.out.println(i);

i++;

} while (i < 5);

Explanation

1. The do block runs once, printing 6.

261
The Complete Java Training Imran Afzal
2. The condition i < 5 is false (since i = 7 after incrementing).

3. The loop does not iterate again, stopping execution.

Output

Since the condition is false, the loop executes only once before stopping.

7.18.4- Example 2: Calculating the Sum of Integers in a Given Range

public class WhileLoop6 {

public static void main(String[] args) {

int x = 21, sum = 0;

do {

sum += x; // Add current value of x to sum

x--; // Decrease x by 1

} while (x > 10); // Loop until x becomes 10 or less

System.out.println("Summation: " + sum);

Execution Process

1. Initialize variables: x = 21, sum = 0.

2. First execution: sum = sum + 21, x-- (now x = 20).

3. Repeat until x > 10 is false (when x = 10, the loop exits).

4. The sum of numbers from 21 to 11 is calculated and printed.

262
The Complete Java Training Imran Afzal
Output

Summation: 176

Breakdown of Summation Calculation

21 + 20 + 19 + 18 + 17 + 16 + 15 + 14 + 13 + 12 + 11 = 176

Review of the Program

• Step 1: Initialize variables x = 21 and sum = 0.

• Step 2: Enter do-while loop.

• Step 3: Add x to sum, then decrement x.

• Step 4: Continue the loop until x <= 10.

• Step 5: Print the final sum after exiting the loop.

This example effectively demonstrates how a do-while loop ensures execution at least once and
is useful in cases where an action needs to be performed before checking a condition.

7.19- While & Do-While Statements Quick Summary

7.19.1- Overview of While and Do-While Loops

Last time we explored while and do-while loops, learning how they function and their practical use
cases. This summary provides a comparison between while and do-while loops and highlights key
concepts such as continue and break statements.

7.19.2- While Loop Syntax and Functionality

while (condition) {

// Code block

263
The Complete Java Training Imran Afzal
7.19.3- Key Characteristics

• The while loop executes as long as the condition remains true.

• The condition is checked before execution of the loop body.

• If the condition is false from the start, the loop will not execute at all.

• If incrementing a variable is necessary, it must be done manually inside the loop.

• Any variable used in the condition must be declared outside the loop.

7.19.4- Example: Using While Loop

int count = 1; // Initialization

while (count <= 5) { // Condition

System.out.println("Count: " + count);

count++; // Iteration

Output

Count: 1

Count: 2

Count: 3

Count: 4

Count: 5

7.19.5- Do-While Loop Syntax and Functionality

do {

// Code block

} while (condition);
264
The Complete Java Training Imran Afzal
7.19.6- Key Characteristics:

• The do-while loop executes at least once before checking the condition.

• The condition is evaluated after executing the loop body.

• A semicolon is required at the end of the while statement.

• Useful when at least one execution of the loop is required, such as reading user input.

7.19.7- Example: Using Do-While Loop

int num = 10;

do {

System.out.println("Number: " + num);

num--;

} while (num > 7);

Output

Number: 10

Number: 9

Number: 8

Since the condition is checked after execution, the loop runs at least once even if num was initially
less than 7.

7.19.8- Comparison of While and Do-While Loops

Feature While Loop Do-While Loop

Condition Check Before loop execution After first iteration

265
The Complete Java Training Imran Afzal
Guaranteed Execution No Yes (at least once)

Semicolon Requirement No semicolon after condition Requires semicolon after condition

7.19.9- Comparison of While Loop and For Loop

Both while and for loops achieve similar logic, but their syntax is different.

While Loop Example

int count = 1; // Initialization

while (count <= 5) { // Condition

System.out.println("Count: " + count);

count++; // Iteration

For Loop Equivalent

for (int count = 1; count <= 5; count++) {

System.out.println("Count: " + count);

Differences

• While Loop: Requires separate initialization, condition, and iteration steps.

• For Loop: Combines initialization, condition, and iteration in a single line.

266
The Complete Java Training Imran Afzal
7.19.10- Using Continue and Break in While Loops

Break Statement

• Exits the loop when a certain condition is met.

Example: Using Break

int num = 1;

while (num <= 10) {

if (num == 5) {

break; // Loop stops when num is 5

System.out.println(num);

num++;

Output

The loop terminates immediately when num == 5.

267
The Complete Java Training Imran Afzal
7.19.11- Continue Statement

• Skips the current iteration and proceeds to the next.

Example: Using Continue

int num = 0;

while (num < 5) {

num++;

if (num == 3) {

continue; // Skips number 3

System.out.println(num);

Output

Number 3 is skipped, but the loop continues.

7.19.12- Summary of Key Takeaways

• While Loop: Executes only if the condition is true from the start.

• Do-While Loop: Executes at least once, then checks the condition.

268
The Complete Java Training Imran Afzal
• For Loop: Has a more compact syntax than a while loop.

• Break Statement: Exits the loop completely.

• Continue Statement: Skips the current iteration and moves to the next.

Each loop serves a different purpose, and choosing the right one depends on the specific use case
and condition evaluation needs.

7.20- Introduction to Nested Loops in Java

7.20.1- Understanding Nested Loops

A nested loop is a loop placed inside another loop. It is used when a task needs to be repeated
multiple times, and each repetition requires another set of repetitions. In Java, nested loops can be
implemented using any type of loop, such as for, while, or do-while.

7.20.2- Basic Structure of a Nested Loop (Using For Loop)

for (int i = 0; i < outerLimit; i++) {

// Code inside outer loop

for (int j = 0; j < innerLimit; j++) {

// Code inside inner loop

7.20.3- How Nested Loops Work:

1. The outer loop initializes a variable i and runs as long as i < outerLimit.

2. Inside the outer loop, there is an inner loop, which initializes another variable j and runs as
long as j < innerLimit.

269
The Complete Java Training Imran Afzal
3. Each time the inner loop completes its cycle, the control returns to the outer loop, which
increments i and re-executes the inner loop from the start.

4. The outer loop continues until its condition is false, at which point the nested loop
terminates.

7.20.4- Example: Multiplication Table Using Nested Loops

Program

for (int i = 1; i <= 10; i++) {

for (int j = 1; j <= 10; j++) {

System.out.print(i*j + "\t");

System.out.println();

How It Works

1. Outer Loop (i)

• Runs from 1 to 10.


• Represents each row of the multiplication table (i.e., tables 1 to 10).

2. Inner Loop (j)

• Runs from 1 to 10 inside the outer loop.


• Multiplies i * j and prints the result, separated by a tab (\t).

3. New Line after Inner Loop

• After each full iteration of the inner loop, a new line (System.out.println();) is printed to
start a new row.

270
The Complete Java Training Imran Afzal
Program Output (Multiplication Table 1-10)

1 2 3 4 5 6 7 8 9 10

2 4 6 8 10 12 14 16 18 20

3 6 9 12 15 18 21 24 27 30

4 8 12 16 20 24 28 32 36 40

5 10 15 20 25 30 35 40 45 50

6 12 18 24 30 36 42 48 54 60

7 14 21 28 35 42 49 56 63 70

8 16 24 32 40 48 56 64 72 80

9 18 27 36 45 54 63 72 81 90

10 20 30 40 50 60 70 80 90 100

Step-by-Step Execution

1. Outer Loop (i = 1 to 10)

• Starts with i = 1.
• The inner loop runs from j = 1 to 10 to calculate the multiplication table for 1.
• Once the inner loop completes, System.out.println(); prints a new line.

2. Inner Loop (j = 1 to 10)

• For each value of i, the inner loop runs ten times (j = 1 to 10).
• Each iteration prints i * j followed by a tab (\t).

271
The Complete Java Training Imran Afzal
3. Continuing Execution

• The outer loop increments i (i = 2).


• The inner loop resets j to 1 and repeats the process for the next table.

4. Final Iteration (i = 10)

• The last row (10 * j) is printed.


• The outer loop condition (i <= 10) fails, and the program ends.

By understanding nested loops, you can efficiently handle multi-dimensional operations and
complex repetitive tasks in Java.

7.21- Nested Loop Exercise Part 1

7.21.1- Finding the Largest Prime Factor

This program defines a method to find the largest prime factor of a given number. If the number is
negative or does not have any prime factors, the method returns -1 to indicate an invalid value.

7.21.2- Understanding the Logic

• Prime Factors: A prime factor is a factor of a number that is also a prime number.

• Largest Prime Factor: The biggest prime number that can divide a given number without
leaving a remainder.

Example Calculation

• For 10 → Prime factors: 2, 5 → Largest prime factor = 5


• For 15 → Prime factors: 3, 5 → Largest prime factor = 5
• For 29 → Prime factor: 29 (itself a prime) → Largest prime factor = 29
• For -5, 0 → No prime factors → Return -1.

7.21.3- Java Program: Finding the Largest Prime Factor

public class LargestPrime {

272
The Complete Java Training Imran Afzal
public static void main(String[] args) {

System.out.println(LargestPrime.findLargestPrime(10)); // 5

System.out.println(LargestPrime.findLargestPrime(15)); // 5

System.out.println(LargestPrime.findLargestPrime(29)); // 29

System.out.println(LargestPrime.findLargestPrime(-5)); // -1

System.out.println(LargestPrime.findLargestPrime(0)); // -1

public static int findLargestPrime(int number) {

if (number < 2) {

return -1;

int largestPrime = -1;

for (int i = 2; i <= number; i++) {

while (number % i == 0) {

largestPrime = i;

273
The Complete Java Training Imran Afzal
number /= i;

return largestPrime;

7.21.4- How It Works

1. Invalid Input Check: If number < 2, return -1.

2. Looping Through Factors:

• Starts with i = 2 and goes up to number.


• If number is divisible by i, update largestPrime = i and divide number by i (remove factor).

3. Returning the Largest Prime Factor:

• After the loop, largestPrime contains the largest prime factor of the input.

Program Output

29

-1

-1

274
The Complete Java Training Imran Afzal
7.21.5- Printing a Square Pattern of Stars with Diagonals

This program prints a square pattern with diagonals using nested loops. If the given number is less
than 5, it prints "Invalid Value".

Understanding the Logic

• The pattern should be a square with number × number size.

• It should contain:

o Borders (edges) of stars (*).

o Diagonals (from top-left to bottom-right and bottom-left to top-right).

• Example for number = 5:

*****

***

***

***

*****

Java Program: Printing a Star Square

public class Stars {

public static void main(String[] args) {

SquareStar(11);

public static void SquareStar(int number) {

275
The Complete Java Training Imran Afzal
if (number < 5) {

System.out.println("Invalid Value");

return;

for (int row = 1; row <= number; row++) {

for (int col = 1; col <= number; col++) {

if (row == 1 || row == number || col == 1 || col == number || row == col || col ==


(number - row + 1)) {

System.out.print("*");

} else {

System.out.print(" ");

System.out.println();

276
The Complete Java Training Imran Afzal
How It Works

1. Invalid Input Check:

• If number < 5, print "Invalid Value" and exit.

2. Nested Loop for Row and Column:

• Outer loop (row) iterates over rows.


• Inner loop (col) iterates over columns.

3. Checking Conditions for Printing *:

• First (row == 1) and Last (row == number) rows → Print *.


• First (col == 1) and Last (col == number) columns → Print *.
• Main diagonal (row == col) → Print *.
• Opposite diagonal (col == number - row + 1) → Print *.

4. Printing New Line:

• After each row, print System.out.println(); to move to the next line.

Program Output for SquareStar(11)

***********

** **

** **

* * * *

* ** *

* * *

* ** *

277
The Complete Java Training Imran Afzal
* * * *

** **

** **

***********

By understanding nested loops and conditional logic, you can efficiently implement complex
patterns and mathematical computations in Java.

7.22- Nested Loop Exercise Part 2

7.22.1- Converting Numbers to Words using Nested Loops in Java

This lecture explores how to use nested loops in Java to convert a given number into words. The
program extracts each digit of the number and prints its corresponding word representation.

Problem Definition

The program will define a method named numberToWords, which takes an integer as input and prints
the number in words. It will follow these conditions:

• If the number is negative, print "Invalid Value".

• If the number is zero, print "Zero".

• Convert each digit of a positive number into its word representation.

Example Output

Input Output

12345 One Two Three Four Five

0 Zero

278
The Complete Java Training Imran Afzal
-123 Invalid Value

Java Implementation: Number to Words Conversion

public class NumberToWords {

public static void numberToWords(int number) {

if (number < 0) {

System.out.println("Invalid Value");

return;

if (number == 0) {

System.out.println("Zero");

return;

String result = "";

int digit, temp;

279
The Complete Java Training Imran Afzal
// loop through each digit of the number

for (int i = 1, j = 10; number > 0; i *= 10, j *= 10) {

temp = number % j;

digit = temp / i;

// loop through each possible digit value

for (int k = 0; k < 10; k++) {

if (digit == k) {

String word = "";

switch (digit) {

case 0:

word = "Zero";

break;

case 1:

word = "One";

break;

case 2:

280
The Complete Java Training Imran Afzal
word = "Two";

break;

case 3:

word = "Three";

break;

case 4:

word = "Four";

break;

case 5:

word = "Five";

break;

case 6:

word = "Six";

break;

case 7:

word = "Seven";

break;

281
The Complete Java Training Imran Afzal
case 8:

word = "Eight";

break;

case 9:

word = "Nine";

break;

result = word + " " + result;

break;

number -= temp;

System.out.println(result.trim());

282
The Complete Java Training Imran Afzal
public static void main(String[] args) {

numberToWords(12345);

numberToWords(0);

numberToWords(-12345);

How the Program Works

1- Handling Special Cases

• If the number is negative, it prints "Invalid Value" and exits the method.

• If the number is zero, it prints "Zero" and exits.

2- Extracting Digits

• A for loop extracts each digit from right to left.

• temp = number % j; → Extracts the rightmost digit.

• digit = temp / i; → Determines the actual digit value.

3- Using a Switch Statement

• The switch statement maps each digit (0-9) to its word equivalent.

• The words are stored in the result variable.

4- Constructing the Final Output

• Since digits are extracted in reverse order, words are concatenated in reverse to maintain
correct sequence.

• result.trim() removes any leading or trailing spaces.

283
The Complete Java Training Imran Afzal
Program Output

One Two Three Four Five

Zero

Invalid Value

7.23- Local Variables

7.23.1- Understanding Local Variables and Their Scope in Java

What Are Local Variables?

Local variables are variables declared within a block of code, such as a method, loop, or conditional
statement. Their scope is limited to the block in which they are declared, meaning they can only be
accessed within that block. Once the block exits, the variable is no longer accessible.

Local variables are primarily used for:

• Storing temporary data or intermediate results within a method.

• Passing arguments to a method.

• Keeping track of iterations inside loops.

Scope of Local Variables

Scope defines where a variable can be accessed in a program. Local variables are in scope within the
block they are declared in, and out of scope outside of that block.

A local variable inside a method is accessible to:

• The method block in which it is declared.


• Any nested blocks within that method.

However, it cannot be accessed:

• Outside the method.

284
The Complete Java Training Imran Afzal
• In another method or class unless passed as an argument.

7.23.2- Example: Local Variable Scope

int firstNumber = 11;

int secondNumber = 20;

if(firstNumber > 1){

System.out.println(secondNumber);

• firstNumber is accessible throughout the main method.


• secondNumber is only accessible inside the if block.

7.23.3- Best Practices for Declaring Local Variables

• Declare and initialize variables together whenever possible for clarity.

• Use the narrowest possible scope to avoid unnecessary memory usage.

• For loop variables should be declared inside the loop if they are only used within it.

285
The Complete Java Training Imran Afzal
7.23.4- Local Variables in Loops

For Loop Example

public class LoopExample {

public static void main(String[] args) {

for (int i = 1; i <= 5; i++) { // 'i' is a local variable inside the for loop

System.out.println("Iteration: " + i);

// 'i' is not accessible outside the loop, so no access is made here

The variable i is only available inside the for loop and cannot be used afterward.

While Loop Example

public class WhileLoopExample {

public static void main(String[] args) {

int count = 0; // Declared outside the loop for broader scope

while (count < 3) {

int temp = count * 2; // Local variable only for this loop block

System.out.println("Temporary Value: " + temp);

286
The Complete Java Training Imran Afzal
count++;

// 'temp' is not accessible outside the loop, so no access is made here

count is accessible throughout the method because it is declared outside the loop.
temp is only available inside the while loop.

7.23.5- Local Variables in If-Else Statements

public class IfStatementExample {

public static void main(String[] args) {

int number = 10;

if (number > 5) {

int value = number * 2; // Local variable for the if block

System.out.println("Value: " + value);

// 'value' is not accessible outside the if block, so no access is made here

287
The Complete Java Training Imran Afzal
The variable value only exists inside the if block.

7.23.6- Scope of Variables in a Switch Statement

The switch statement differs from if-else statements in terms of variable scope.
Variables declared in one case can be accessed in later case blocks, but not before their declaration.

public class SwitchExample {

public static void main(String[] args) {

int choice = 2;

switch (choice) {

case 1:

// 'x' is not declared in this case, so no access here

break;

case 2:

int x = 10; // Local variable declared in case 2

System.out.println("Value of x: " + x);

break;

case 3:

// 'x' is not accessible here because it's only declared in case 2

break;

288
The Complete Java Training Imran Afzal
default:

System.out.println("Invalid choice");

Variables declared inside a case block are available only for subsequent case blocks.
If a variable needs to be used across multiple case blocks, declare it before the switch statement.

7.24- Class and Object

7.24.1- Understanding Classes and Objects

A class in Java can be thought of as a custom data type or a template that defines the structure of
objects. It contains fields (attributes) and methods (behaviors) related to that class. A class does
not store actual data, but instead, it serves as a blueprint for creating objects.

An object is an instance of a class, meaning it is created based on the structure defined in a class and
holds actual data. Each object has its own unique values assigned to the class fields.

7.24.2- Analogy of Classes and Objects

Consider a class as a form that has predefined fields for name, address, and phone number. The
object is an actual filled-out form, where these fields contain real data. Another analogy is a cookie
cutter (class) and the cookies (objects) that are created using it.

Each object has unique data, but all objects follow the same structure set by the class. The process
of creating an object from a class is called instantiation.

289
The Complete Java Training Imran Afzal
7.24.3- Creating Objects in Java

The new keyword is used to create an object from a class. The newly created object is assigned to a
variable, which holds the memory reference to that object.

7.24.4- Example: Creating an Object in Java

public class Car {

String brand;

int speed;

public void displayInfo() {

System.out.println("Brand: " + brand + ", Speed: " + speed);

public static void main(String[] args) {

Car myCar = new Car(); // Creating an object of the Car class

myCar.brand = "Toyota"; // Assigning values to fields

myCar.speed = 120;

myCar.displayInfo(); // Calling a method on the object

The class Car defines attributes (brand, speed) and a behavior (displayInfo()).
The object myCar is created using the new keyword.

290
The Complete Java Training Imran Afzal
The values Toyota and 120 are assigned to the object's fields.
The method displayInfo() is called on myCar, printing the object's details.

7.24.5- Static vs. Instance Fields in a Class

There are two types of fields in Java classes:

1. Static Fields (Class-Level)

• Declared using the static keyword.


• Shared among all instances of the class.
• Stored in a special memory area, not in individual objects.
• Can be accessed using the class name instead of an object reference.

2. Instance Fields (Object-Level)

• Declared without using the static keyword.


• Unique to each object.
• Exist only when an object is created and are stored in the object’s memory.
• Accessed using an object reference.

7.24.6- Example: Static vs. Instance Fields

public class Example {

static int staticVar = 100; // Static field

int instanceVar = 50; // Instance field

public static void main(String[] args) {

Example obj1 = new Example();

Example obj2 = new Example();

obj1.instanceVar = 200; // Changing instance variable for obj1

291
The Complete Java Training Imran Afzal
obj2.instanceVar = 300; // Changing instance variable for obj2

System.out.println("Static Variable: " + Example.staticVar); // Accessed using class name

System.out.println("Instance Variable (obj1): " + obj1.instanceVar);

System.out.println("Instance Variable (obj2): " + obj2.instanceVar);

staticVar remains the same for all objects because it belongs to the class.
instanceVar is different for each object because it belongs to the individual instance.

7.24.7- Static vs. Instance Methods

Java methods can also be static or instance-based:

Static Methods

• Declared using the static keyword

• Belong to the class rather than an instance

• Can be called using the class name

• Cannot access instance fields or methods directly

Instance Methods

• Do not use the static keyword

• Belong to an object

• Can access both instance fields and static fields

292
The Complete Java Training Imran Afzal
7.24.8- Example: Static vs. Instance Methods

public class Demo {

static void staticMethod() {

System.out.println("Static method called");

void instanceMethod() {

System.out.println("Instance method called");

public static void main(String[] args) {

Demo.staticMethod(); // Calling a static method using class name

Demo obj = new Demo(); // Creating an object

obj.instanceMethod(); // Calling an instance method using an object

staticMethod() is called using the class name.


instanceMethod() requires an object to be called.

7.24.9- Understanding String Objects

Strings are special in Java because they can be created:

293
The Complete Java Training Imran Afzal
• Using string literals: String text = "Hello";
• Using the new keyword: String text = new String("Hello");

Example: Creating String Objects

public class StringExample {

public static void main(String[] args) {

String str1 = "Hello"; // Using string literal

String str2 = new String("Hello"); // Using new keyword

System.out.println(str1.toUpperCase()); // Converts string to uppercase

String literals are stored in a special memory area called the string pool.
toUpperCase() is an instance method that modifies the string.

7.24.10- Wrapper Classes and Static Methods

Wrapper classes allow primitive data types to be used as objects and provide useful static methods.

Example: Using Wrapper Class Methods

public class WrapperExample {

public static void main(String[] args) {

int max = Integer.MAX_VALUE; // Static field

int min = Integer.MIN_VALUE; // Static field

294
The Complete Java Training Imran Afzal
int parsedNumber = Integer.parseInt("123"); // Static method

System.out.println("Max Value: " + max);

System.out.println("Min Value: " + min);

System.out.println("Parsed Number: " + parsedNumber);

• MAX_VALUE and MIN_VALUE are static fields of Integer.


• parseInt() is a static method that converts a string to an integer.

Java classes and objects form the foundation of object-oriented programming, allowing data and
behaviors to be encapsulated, reused, and efficiently managed.

7.25- Parsing Values and User Input

7.25.1- Parsing Data in Java

Parsing in Java refers to converting a string representation of a value into an actual value of a particular
data type. This is essential when working with user inputs, reading data from files, or handling dynamic
data formats.

Example: Parsing String to Integer

To understand parsing, consider a scenario where a person's age is calculated using the current year
and birth year, both stored as strings.

class HelloWorld {

public static void main(String[] args) {

295
The Complete Java Training Imran Afzal
int currentYear = 2023;

String BirthYear = "1992";

int myBirthYear = Integer.parseInt(BirthYear);

System.out.println("Total Age: " + (currentYear - myBirthYear));

7.25.2- Common Parse Methods in Java

Java provides various parsing methods for different data types:

• Integer.parseInt(String str): Converts a string to an integer.

• Double.parseDouble(String str): Converts a string to a double.

• Float.parseFloat(String str): Converts a string to a float.

• Long.parseLong(String str): Converts a string to a long.

• Short.parseShort(String str): Converts a string to a short.

• Byte.parseByte(String str): Converts a string to a byte.

• Boolean.parseBoolean(String str): Converts a string to a boolean.

• Character.toString(char ch): Converts a character to a string.

7.25.3- Wrapper Classes and Parsing

Java wrapper classes (Integer, Double, Boolean, etc.) allow conversions between strings and primitive
data types.

int myBirthYear = Integer.parseInt("1992"); // Converts string to int

296
The Complete Java Training Imran Afzal
double price = Double.parseDouble("15.99"); // Converts string to double

boolean status = Boolean.parseBoolean("true"); // Converts string to boolean

7.25.4- Reading User Input in Java

User input can come from multiple sources such as files, console input, or UI forms. In Java, different
approaches can be used for reading input:

• System.in: Reads input directly from the console.

• System.console(): Provides an easy way to read input from the console.

• Scanner class: Allows reading input efficiently.

Example: Using System.console()

public class ParseValue{

public static void main(String[] args) {

FromConsole();

public static void FromConsole() {

String name = System.console().readLine("What is your Name? ");

System.out.println("Your Name is: " + name);

297
The Complete Java Training Imran Afzal
This method prompts the user to enter their name, then prints it. However, System.console() does
not work within IDEs like IntelliJ and must be executed in a terminal.

7.25.5- Running the Program in IntelliJ Terminal

Since IntelliJ disables System.console(), running this program in the terminal is necessary.

1. Open IntelliJ's Terminal tab.

2. Navigate to the source file’s location.

3. Compile and run using:

javac ParseValues.java

java ParseValues

4. Enter a name when prompted. The output will display:

What is your name? John

Your name is: John

By using parsing methods and user input handling techniques, Java programs can efficiently process
data from different sources.

7.26- Introduction to Scanner

7.26.1- Introduction to the Scanner Class

The Scanner class in Java is a part of the java.util package and is used for reading user input from
various sources, such as the console, files, or strings. It provides several methods for parsing different
data types, including integers, floating-point numbers, characters, and strings.

298
The Complete Java Training Imran Afzal
7.26.2- Commonly Used Methods of the Scanner Class

• next(): Reads the next token from the input as a string.

• nextInt(): Reads the next token as an integer. Throws an exception if the token cannot be
parsed as an integer.

• nextLine(): Reads the entire line of input as a string.

• hasNext(): Returns true if there is another token available in the input stream.

• useDelimiter(String pattern): Sets the delimiter pattern for the Scanner. Default is whitespace.

• hasNextInt(): Checks if the next token can be parsed as an integer.

• nextDouble(): Reads the next token as a double.

• nextBoolean(): Reads the next token as a boolean value.

• nextByte(), nextShort(), nextLong(): Reads the next token as a byte, short, or long respectively.

• close(): Closes the Scanner and releases system resources.

7.26.3- Instantiating the Scanner Class

To use the Scanner class, an instance must be created. The new keyword is used for this purpose.

• Reading from Console:

Scanner scanner = new Scanner(System.in);

• Reading from a File:

Scanner scanner = new Scanner(new File("data.txt"));

The Scanner class is part of the java.util package, so it must be imported at the beginning of the Java
program.

7.26.4- Example: Taking User Input for Name and Age

This program prompts the user to enter their name and age, then displays them.

299
The Complete Java Training Imran Afzal
import java.util.Scanner;

public class ScannerExample {

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

System.out.print("Enter your name: ");

String name = scanner.nextLine();

System.out.print("Enter your age: ");

int age = scanner.nextInt();

System.out.println("Your name is " + name + " and you are " + age + " years old");

scanner.close();

Explanation

1. A Scanner object named scanner is created to read input from System.in.

2. The nextLine() method reads a full line of input for the user's name.

3. The nextInt() method reads an integer input for the user’s age.

4. The program prints the name and age using System.out.println().

300
The Complete Java Training Imran Afzal
5. The scanner.close() method is called to release system resources.

7.26.5- Example: Checking for Shared Digits Between Two Numbers

This program checks whether two integers in the range of 10-99 share at least one digit.

import java.util.Scanner;

public class SharedDigit {

public static boolean sharedDigit(int num1, int num2) {

if ((num1 < 10 || num1 > 99) || (num2 < 10 || num2 > 99)) {

return false;

int digit1 = num1 % 10;

int digit2 = num1 / 10;

int digit3 = num2 % 10;

int digit4 = num2 / 10;

return (digit1 == digit3 || digit1 == digit4 || digit2 == digit3 || digit2 == digit4);

301
The Complete Java Training Imran Afzal
public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

System.out.print("Enter the first number (between 10-99): ");

int num1 = scanner.nextInt();

System.out.print("Enter the second number (between 10-99): ");

int num2 = scanner.nextInt();

scanner.close();

if (sharedDigit(num1, num2)) {

System.out.println("There is a shared digit between " + num1 + " and " + num2);

} else {

System.out.println("There is no shared digit between " + num1 + " and " + num2);

Explanation

1. The sharedDigit() method checks if two numbers share at least one digit.

2. If the numbers are out of the range 10-99, it returns false.

302
The Complete Java Training Imran Afzal
3. It extracts the ones and tens digits of both numbers using modulus (%) and division (/).

4. If any digit in the first number matches any digit in the second number, it returns true.

5. The main method reads two integer inputs using Scanner.

6. It calls sharedDigit() to determine whether the numbers have a shared digit and prints the
result.

Example Runs

Input 1:

Enter the first number (between 10 to 99): 12

Enter the second number (between 10 to 99): 23

Output 1:

There is a shared digit between 12 and 23

Input 2:

Enter the first number (between 10 to 99): 13

Enter the second number (between 10 to 99): 26

Output 2:

There is no shared digit between 13 and 26

This program checks if two numbers between 10-99 share at least one digit. It first verifies that both
numbers fall within the valid range. The ones and tens digits of each number are extracted and
compared. If a shared digit is found, the method returns true; otherwise, it returns false. The Scanner
class is used to take input from the user

7.27- Input with Scanner Exercise Part 1


7.27.1- Converting Kilobytes to Megabytes
This program takes a user input in kilobytes and converts it to megabytes and remaining kilobytes.
The conversion formula is:
1 MB = 1024 KB
303
The Complete Java Training Imran Afzal
To calculate:

• Megabytes = Kilobytes ÷ 1024

• Remaining Kilobytes = Kilobytes % 1024

Implementing the Program

Step 1: Create a Java Class

Create a Java class named MegaBytesConverter and import the Scanner class.

import java.util.Scanner;

public class MegaBytesConverter {

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

System.out.print("Enter the number of kilobytes: ");

int kiloBytes = scanner.nextInt();

MegaBytesKiloBytes(kiloBytes);

public static void MegaBytesKiloBytes(int kiloBytes) {

if (kiloBytes < 0) {

304
The Complete Java Training Imran Afzal
System.out.println("Invalid input. Please enter a non-negative number.");

} else {

int megaBytes = kiloBytes / 1024;

int remainingKiloBytes = kiloBytes % 1024;

System.out.println(kiloBytes + " KB = " + megaBytes + " MB and " + remainingKiloBytes +


" KB");

Explanation

1. MegaBytesKiloBytes(int kiloBytes):

• Checks if the input is negative. If so, prints an error message.


• Converts kilobytes to megabytes and calculates remaining kilobytes.
• Displays the result in the format: XX KB = YY MB and ZZ KB

2. main(String[] args):

• Uses Scanner to take user input.


• Calls MegaBytesKiloBytes() to perform the conversion.
• Closes the scanner to free resources.

Example Runs

Input 1:

Enter the number of kilobytes: 4080

305
The Complete Java Training Imran Afzal
Output 1:

4080 KB = 3 MB and 1008 KB

Input 2:

Enter the number of kilobytes: -5

Output 2:

Invalid input. Please enter a non-negative number.

This program takes an input value in kilobytes and converts it into megabytes and remaining kilobytes.
It first checks if the input is valid and performs the calculations if the value is non-negative. The results
are displayed in a formatted message. The Scanner class is used to read input from the user.

7.28- Input with Scanner Exercise Part 2

7.28.1- Checking for Teen Numbers

This program takes three integer inputs from the user and checks if at least one of them falls within
the range of 13 to 19, which is considered a teen number.

Implementing the Program

Step 1: Create a Java Class

Create a Java class named TeenNumberChecker and import the Scanner class.

import java.util.Scanner;

public class TeenNumberChecker {

306
The Complete Java Training Imran Afzal
public static boolean FindTeen(int num1, int num2, int num3) {

boolean isTeen = false;

if (isInRange(num1) || isInRange(num2) || isInRange(num3)) {

isTeen = true;

return isTeen;

public static boolean isInRange(int num) {

return num >= 13 && num <= 19;

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

System.out.print("Enter the first number: ");

307
The Complete Java Training Imran Afzal
int num1 = scanner.nextInt();

System.out.print("Enter the second number: ");

int num2 = scanner.nextInt();

System.out.print("Enter the third number: ");

int num3 = scanner.nextInt();

boolean isTeen = FindTeen(num1, num2, num3);

if (isTeen) {

System.out.println("At least one number is a teen.");

} else {

System.out.println("No number is a teen.");

Explanation

1. FindTeen(int num1, int num2, int num3):

• Calls the helper method isInRange() to check if any number is between 13 and 19.

308
The Complete Java Training Imran Afzal
• If at least one number is in the range, it returns true; otherwise, it returns false.

2. isInRange(int num):

• Returns true if num is between 13 and 19 (inclusive).

3. main(String[] args):

• Uses Scanner to take three integer inputs from the user.


• Calls FindTeen() and checks whether at least one number is a teen number.
• Displays the appropriate message based on the result.

Example Runs

Input 1:

Enter the first number: 13

Enter the second number: 26

Enter the third number: 27

Output 1:

At least one number is a teen.

Input 2:

Enter the first number: 30

Enter the second number: 33

Enter the third number: 40

Output 2:

No number is a teen.

This program takes three integer inputs and checks if at least one of them is a teen number (13-19). It
uses a helper method isInRange() to check if a number is in the specified range. The FindTeen()

309
The Complete Java Training Imran Afzal
method calls isInRange() for all three numbers and returns true if any of them is a teen. The program
then prints whether at least one number is a teen or if none of them are.

7.29- Input with Scanner Exercise Part 3

7.29.1- Wake Up Decision Based on Cat Yowling

This program determines whether the user should wake up based on whether a cat is yowling and the
current time of day. The decision is made based on two inputs:

1. Yowl (Boolean): Whether the cat is currently yowling (true or false).

2. Hour of Day (Integer): The current hour in a 24-hour format (0-23).

The user should wake up if the cat is yowling before 8 AM or after 10 PM. Otherwise, they can
continue sleeping. If the input for the hour is invalid (less than 0 or greater than 23), the program
returns false.

Implementing the Program

Step 1: Create a Java Class

Create a Java class named CatAlarm and import the Scanner class.

import java.util.Scanner;

public class CatAlarm {

public static boolean WakeUp(boolean yowl, int hourOfDay) {

if (hourOfDay < 0 || hourOfDay > 23) {

310
The Complete Java Training Imran Afzal
return false;

if (yowl && (hourOfDay < 8 || hourOfDay > 22)) {

return true;

} else {

return false;

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

System.out.print("Is the cat yowling? (true/false): ");

boolean yowl = scanner.nextBoolean();

System.out.print("What is the hour of the day? (0-23): ");

311
The Complete Java Training Imran Afzal
int hourOfDay = scanner.nextInt();

boolean shouldWakeUp = WakeUp(yowl, hourOfDay);

if (shouldWakeUp) {

System.out.println("Wake up!");

} else {

System.out.println("Keep sleeping.");

Explanation

1. WakeUp(boolean yowl, int hourOfDay):

• Returns false if hourOfDay is not between 0 and 23 (invalid input).


• If the cat is yowling (yowl == true) and the hour is before 8 AM or after 10 PM, it returns
true (wake up).
• Otherwise, it returns false (keep sleeping).

2. main(String[] args):

• Uses Scanner to take user input for yowl and hourOfDay.


• Calls the WakeUp() method and stores the result in shouldWakeUp.
312
The Complete Java Training Imran Afzal
• If shouldWakeUp is true, the program prints "Wake up!"; otherwise, it prints "Keep
sleeping."

Example Runs

Input 1:

Is the cat yowling? (true/false): true

What is the hour of the day (0 to 23)?: 4

Output 1:

Wake up!

Explanation:

The cat is yowling, and the time is before 8 AM, so the program advises the user to wake up.

Input 2:

Is the cat yowling? (true/false): false

What is the hour of the day (0 to 23)?: 5

Output 2:

Keep sleeping.

Explanation:

The cat is not yowling, so the program advises the user to keep sleeping.

Input 3:

Is the cat yowling? (true/false): true

What is the hour of the day (0 to 23)?: 14

313
The Complete Java Training Imran Afzal
Output 3:

Keep sleeping.

Explanation:

The cat is yowling, but the time is not before 8 AM or after 10 PM, so the program advises the user
to keep sleeping.

Summary

• The program takes two inputs:

o A Boolean (true/false) indicating whether the cat is yowling.

o An integer (0-23) representing the current hour.

• If the cat is yowling before 8 AM or after 10 PM, the program returns "Wake up!"

• If the cat is not yowling or the time is outside the wake-up range, it returns "Keep sleeping."

• If an invalid hour is entered (less than 0 or greater than 23), the program automatically returns
false.

This simple program demonstrates the use of Boolean logic, conditional statements, and user
input handling with Scanner.

314
The Complete Java Training Imran Afzal
Chapter 8

Object Oriented Programming –


Inheritance

315
The Complete Java Training Imran Afzal
8.1- Classes and Objects

8.1.1- What is Object-Oriented Programming?

Object-Oriented Programming (OOP) is a programming paradigm where real-world entities are


modeled as software objects. These objects encapsulate both data (state) and code (behavior). In Java,
this paradigm is primarily implemented using classes, which serve as blueprints for creating objects.

Classes define the structure and behavior of objects through fields (attributes) and methods
(functions). Objects, which are instances of classes, hold specific values for the fields and can perform
actions defined by the methods.

8.1.2- Real-World Analogy

Consider the objects around you—a computer, keyboard, microphone, door, etc. Each has a state
(e.g., a computer's RAM, operating system) and behavior (e.g., a computer can shut down or display
output). This duality is captured in programming objects using fields (state) and methods (behavior).

8.1.3- Classes as Blueprints

In Java, a class acts as a blueprint. It defines what fields (data) and methods (behaviors) an object will
have. When an object is created from a class, it inherits all the characteristics defined in the class.

Each field in a class may either be:

• Static: Shared among all objects of the class.

• Instance: Unique to each object.

8.1.4- Creating a Class and Object in IntelliJ

1. Create a project named ClassesAndObject.

2. Inside the src folder, create a new class file named CentralClass.

3. Inside CentralClass, use the psvm shortcut to create a main method.

316
The Complete Java Training Imran Afzal
Then create another class named Pen. Java naming conventions suggest class names should begin with
an uppercase letter.

public class CentralClass {

public static void main(String[] args) {

Pen pen = new Pen();

pen.describePen();

public class Pen {

private String manufacturer = "Edison";

private String color = "Blue";

private String type = "Jell";

public void describePen() {

System.out.println(manufacturer + " manufacturer " + color + " " + type + " ");

317
The Complete Java Training Imran Afzal
This code defines a class Pen with three fields and a method. We create an object from the class and
call its method from main.

8.1.5- Understanding Access Modifiers

Access modifiers control visibility:

• public: Accessible from any class.

• (no modifier): Package-private (accessible only within the same package).

• private: Accessible only within the same class.

• protected: Accessible within the same package and by subclasses.

8.1.6- Adding Fields to a Class

Define the following fields in the Pen class:

private String manufacturer;

private String color;

private String type;

These are instance fields because they are not marked static and will hold different values for each
object created from the Pen class.

8.1.7- Creating a Method

Add a method to describe the pen:

public void describePen() {

System.out.println(manufacturer + " manufacturer " + color + " " + type + " ");

318
The Complete Java Training Imran Afzal
This method is public and non-static because it uses instance fields.

8.1.8- Creating an Object from the Pen Class

Go back to CentralClass.java and create an object:

Pen pen = new Pen();

pen.describePen();

Since no values are assigned to the fields, the output will show null values for each.

8.1.9- Default Values in Java

• Primitive numeric types: 0

• boolean: false

• Object types (like String): null

Assign default values in the class:

private String manufacturer = "Edison";

private String color = "Blue";

private String type = "Jell";

Now running the code will print:

Edison manufacturer, Blue, Jell

8.1.10- Encapsulation

Encapsulation is the practice of hiding fields and allowing access only through methods. Fields are
declared private and accessed using getter and setter methods.

319
The Complete Java Training Imran Afzal
8.1.11- Creating Getters and Setters

Create a getter method manually:

public String getManufacturer() {

return manufacturer;

Use IntelliJ to generate the rest:

1. Place the cursor below the first getter.

2. Go to Code > Generate > Getter.

3. Select color and type, and press OK.

Generated methods:

public String getColor() {

return color;

public String getType() {

return type;

320
The Complete Java Training Imran Afzal
8.1.12- Using Getters in the Main Method

In CentralClass.java, update your output statements:

System.out.println("Manufacturer = " + pen.getManufacturer());

System.out.println("Color = " + pen.getColor());

System.out.println("Type = " + pen.getType());

This removes direct access to private fields and adheres to the encapsulation principle.

8.1.13- Why Not Access Fields Directly?

Attempting to access or modify private fields directly:

pen.manufacturer = "Garwood";

pen.color = "Black";

pen.type = "Fountain";

...will result in compiler errors due to the fields being private.

8.2- Using Getter and Setters


In the previous section, getter methods were set up for all private attributes in the Pen class,
establishing the practice of keeping fields private and providing controlled access through methods.
This is a core concept of encapsulation in object-oriented programming. It allows internal changes to
a class without affecting external code relying on its public interface.

8.2.1- Creating Setter Methods

Setter methods are used to assign values to private fields. To create a setter method for the
manufacturer field in the Pen class, the following code is used:

321
The Complete Java Training Imran Afzal
public void setManufacturer(String manufacturer) {

manufacturer = manufacturer;

This code initially causes issues. IntelliJ flags it with warnings because the method parameter
manufacturer is being assigned to itself, not to the class field. The confusion arises due to both the
parameter and the class field having the same name.

8.2.2- Using the this Keyword

To differentiate between the method parameter and the class field, the this keyword is used:

this.manufacturer = manufacturer;

The this keyword refers to the current instance of the class and allows access to its fields. By using
this.manufacturer, the field belonging to the current object is specified, eliminating all warnings.

8.2.3- Calling the Setter Method

In the Main class, the setter can now be called to set the value of manufacturer:

pen.setManufacturer("Garwood");

This updates the manufacturer field to the specified value instead of using the default.

8.2.4- Generating Additional Setters

Instead of writing each setter manually, IntelliJ's code generation can be used. After placing the cursor
after the last method (e.g., setManufacturer), navigate to Code → Generate → Setter and select the desired
fields. This generates methods like setColor and setType automatically.

Example usage in the main class:

322
The Complete Java Training Imran Afzal
pen.setColor("Black");

pen.setType("Fountain");

Running this updates the pen object with the specified values.

8.2.5- Adding Validation in Setters

To ensure data integrity, validation logic can be added within setter methods. Here's how validation is
applied in the setManufacturer method:

public void setManufacturer(String manufacturer) {

if (manufacturer == null) {

manufacturer = "Unknown";

String lowerCaseManufacturer = manufacturer.toLowerCase();

switch (lowerCaseManufacturer) {

case "franklin":

case "schon":

case "noodler":

this.manufacturer = manufacturer;

323
The Complete Java Training Imran Afzal
break;

default:

this.manufacturer = "Unsupported";

Explanation of the Validation Logic

• If the input is null, it's set to "Unknown".

• The string is converted to lowercase for consistent comparison.

• The switch statement checks if the value matches supported manufacturers: "Franklin",
"Schon", or "Noodler".

• If it matches, the value is assigned; otherwise, the field is set to "Unsupported".

Testing Validation

Changing the manufacturer to an unsupported value like "Conklin" results in the manufacturer being
set to "Unsupported":

pen.setManufacturer("Conklin");

This enforces business rules and prevents invalid data from being set on the object.

8.2.6- Importance of Setters and Encapsulation

By using setter methods, rules related to valid data can be embedded directly into the class. This
ensures that any object created from the class remains valid and consistent. The concept of
encapsulation protects the internal state of objects by forcing external code to interact through
controlled methods, such as getters and setters.

324
The Complete Java Training Imran Afzal
8.3- Classes, Object, Getter and Setter Exercise Part 1

This program demonstrates the use of classes, objects, getters, and setters by creating an Employee
class that models employee data with private fields and public methods for access and modification.
It also includes a method to print the object's data in a formatted string.

8.3.1- Project Setup

Create a new project named ObjectOrientedPractice. Within this project:

1. Create a class file named MainClass and add the main method using the shortcut psvm.

2. Create another class file named Employee.

8.3.2- Defining the Employee Class

In the Employee class, define the following private variables:

private int eId;

private String eName;

private String eDesignation;

private String eCompany;

These private instance variables represent:

• eId: Unique ID of the employee.

• eName: Name of the employee.

• eDesignation: Job title or designation.

• eCompany: Company where the employee works.

Declaring these fields as private ensures encapsulation. External classes cannot directly access or
modify them, helping maintain data integrity.

325
The Complete Java Training Imran Afzal
8.3.3- Creating Getter and Setter Methods

To allow controlled access and modification, define public getter and setter methods for each field.

public int getEmpId()

return eId;

public void setEmpId(int eId)

this.eId = eId;

This allows retrieving and setting the value of eId while distinguishing between the parameter and the
class field using the this keyword.

Similarly, create getter and setter methods for the remaining fields:

public String getEmpName()

return eName;

public void setEmpName(String eName)

326
The Complete Java Training Imran Afzal
{

this.eName = eName;

public String getEmpDesignation()

return eDesignation;

public void setEmpDesignation(String eDesignation)

this.eDesignation = eDesignation;

public String getEmpCompany()

return eCompany;

public void setEmpCompany(String eCompany)

327
The Complete Java Training Imran Afzal
{

this.eCompany = eCompany;

8.3.4- makeString Method

Define a method to format and return employee details:

public String toString()

String str = "Employee: ID = " + getEmpId() + ", Name = " + getEmpName() + ", Designation
= " + getEmpDesignation() + ", Company = " + getEmpCompany() + "";

return str;

This method uses getter methods to access each field and concatenates them into a readable string.

8.3.5- Main Class Implementation

In MainClass.java, create and initialize an object of the Employee class:

Employee emp = new Employee();

emp.setEmpId(107);

328
The Complete Java Training Imran Afzal
emp.setEmpName("John");

emp.setEmpDesignation("Software Developer");

emp.setEmpCompany("Nixware Consulting");

These lines use setter methods to assign values to the private fields of the emp object.

To display the employee's details:

System.out.println(emp.makeString());

Output

Employee: ID = 71, Name = John, Designation = Software Developer, Company = Nixware


Consulting

Encapsulation is maintained by using private fields with public accessors, ensuring that internal data
cannot be directly modified from outside the class. This structure supports better data validation and
maintainability in object-oriented programming.

8.4- Classes, Object, Getter and Setter Exercise Part 2

This program demonstrates a practical example of using classes, objects, getters, and setters by
building a calculator that performs basic arithmetic operations using encapsulated data.

8.4.1- Project Setup

Create a new project named ObjectOrientedPractice1. Within the project:

1. Create a class file named MainClass and add the main method using the shortcut psvm.

2. Create another class file named MyCalculator.

8.4.2- Defining the MyCalculator Class

Inside the MyCalculator class, define the following private instance variables:
329
The Complete Java Training Imran Afzal
private double firstNumber;

private double secondNumber;

Here is the code for that:

class MyCalculator {

private double firstNumber;

private double secondNumber;

These variables are declared private to ensure encapsulation, meaning they cannot be accessed or
modified directly from outside the class. The double data type allows storing floating-point numbers
with precision.

8.4.3- Getter and Setter Methods

To allow controlled access to these private variables, define public getter and setter methods.

public double getFirstNumber() {

return firstNumber;

public double getSecondNumber() {

return secondNumber;

330
The Complete Java Training Imran Afzal
public void setFirstNumber(double firstNumber) {

this.firstNumber = firstNumber;

public void setSecondNumber(double secondNumber) {

this.secondNumber = secondNumber;

These methods allow setting and retrieving the values of firstNumber and secondNumber. The this
keyword is used to distinguish between the class fields and the method parameters.

8.4.4- Arithmetic Operation Methods

Define instance methods to perform the four basic operations:

public double getAdditionResult() {

return firstNumber + secondNumber;

public double getSubtractionResult() {

return firstNumber - secondNumber;

331
The Complete Java Training Imran Afzal
}

public double getMultiplicationResult() {

return firstNumber * secondNumber;

public double getDivisionResult() {

if (secondNumber == 0) {

return 0;

} else {

return firstNumber / secondNumber;

These methods return the result of addition, subtraction, multiplication, and division respectively. The
division method includes a condition to avoid division by zero, returning 0 in such a case.

8.4.5- Main Class Implementation

In MainClass.java, create and use an object of MyCalculator:

332
The Complete Java Training Imran Afzal
class MainClass{

public static void main(String[] args){

MyCalculator calculator = new MyCalculator();

calculator.setFirstNumber(10);

calculator.setSecondNumber(5);

These lines create an instance of MyCalculator and assign values to firstNumber and secondNumber
through setter methods.

8.4.6- Performing Calculations and Displaying Results

Call the arithmetic methods and store their results:

double additionResult = calculator.getAdditionResult();

double subtractionResult = calculator.getSubtractionResult();

double multiplicationResult = calculator.getMultiplicationResult();

double divisionResult = calculator.getDivisionResult();

Print the results:

System.out.println("Addition result: " + additionResult);

System.out.println("Subtraction result: " + subtractionResult);

System.out.println("Multiplication result: " + multiplicationResult);

333
The Complete Java Training Imran Afzal
System.out.println("Division result: " + divisionResult);

Output

Addition result: 15.0

Subtraction result: 5.0

Multiplication result: 50.0

Division result: 2.0

The MainClass creates an instance of MyCalculator, sets the values, performs calculations, and prints
the results. This example demonstrates encapsulation, reusability, and method-based logic in object-
oriented programming.

8.5- Classes, Object, Getter and Setter Exercise Part 3

This exercise demonstrates the use of getter and setter methods with validation and conditional logic
in a Person class. It also covers object manipulation and string handling using object-oriented
programming principles in Java.

8.5.1- Project Setup

Create a new project named ObjectOrientedPractice2. Within the project:

1. Create a class file named MainClass and add the main method using the shortcut psvm.

2. Create another class file named Person.

334
The Complete Java Training Imran Afzal
8.5.2- Defining the Person Class

Inside the Person class, declare the following private instance variables:

private String firstName;

private String lastName;

private int age;

Here is the code for that:

class Person {

private String firstName;

private String lastName;

private int age;

These fields store the personal information of a person. The String fields hold names, and int age
stores the person's age. Keeping these fields private ensures encapsulation.

8.5.3- Getter Methods

Define the getter methods to retrieve the values of the fields:

public String getFirstName() {

return firstName;

335
The Complete Java Training Imran Afzal
public String getLastName() {

return lastName;

public int getAge() {

return age;

These methods allow external access to the field values without exposing them directly.

8.5.4- Setter Methods

Define the setter methods to update the field values:

public void setFirstName(String firstName) {

this.firstName = firstName;

public void setLastName(String lastName) {

this.lastName = lastName;

336
The Complete Java Training Imran Afzal
public void setAge(int age) {

if (age < 0 || age > 100) {

this.age = 0;

} else {

this.age = age;

The setAge method includes validation logic to ensure that the age remains within a valid range (0–
100). If an invalid age is provided, it defaults to 0.

8.5.5- Boolean Method: isTeen

Add a method to determine if a person is a teenager:

public boolean isTeen() {

return (age > 12 && age < 20);

This returns true only if the age is between 13 and 19.

8.5.6- Method: getFullName

Implement logic to handle name formatting:

337
The Complete Java Training Imran Afzal
public String getFullName() {

if (firstName.isEmpty() && lastName.isEmpty()) {

return "";

} else if (lastName.isEmpty()) {

return firstName;

} else if (firstName.isEmpty()) {

return lastName;

} else {

return firstName + " " + lastName;

This method returns a person's full name with conditions:

• If both names are empty, returns an empty string.

• If one name is empty, returns the other.

• If both are present, returns them combined with a space.

8.5.7- Main Class Implementation

In MainClass.java, create and initialize a Person object:

338
The Complete Java Training Imran Afzal
Person person = new Person();

person.setFirstName("John");

person.setLastName("Doe");

person.setAge(25);

class MainClass

public static void main(String[] args){

Person person = new Person();

person.setFirstName("John");

person.setLastName("Doe");

person.setAge(25);

These method calls assign values to the Person object.

8.5.8- Access and Display Information

Call the methods to display data:

System.out.println(person.getFirstName());

System.out.println(person.getLastName());

System.out.println(person.getAge());

339
The Complete Java Training Imran Afzal
System.out.println(person.isTeen());

System.out.println(person.getFullName());

Output

John

Doe

25

false

John Doe

In the MainClass, an object of the Person class is created and manipulated through these methods.
This example emphasizes encapsulation, data validation, conditional logic, and the use of object-
oriented techniques for reusable and clean code structure.

8.6- Introduction to Constructors

A constructor is a special type of method used to initialize objects in Java. It shares its name with the
class and does not have a return type—not even void. Constructors are automatically called when an
object is created, and they are responsible for setting up the initial state of the object.

Constructors can include any necessary initialization code. Access modifiers like public, private, or
protected can also be used to control the visibility and accessibility of the constructor.

340
The Complete Java Training Imran Afzal
8.6.1- Purpose of Constructors

Constructors are used for:

• Object Initialization: Setting default or specified values for object fields.

• Encapsulation: Protecting the internal state of the object by only allowing controlled access.

• Default Values: Providing default initialization when no specific values are supplied.

• Method Overloading: Allowing multiple constructors with different parameter lists for
flexibility.

• Inheritance: Allowing child classes to inherit initialization behavior from parent classes.

8.6.2- Types of Constructors

Java supports two primary types of constructors:

1. Default Constructor
A default constructor is a constructor that does not accept any parameters. It can be:

• Explicitly defined by the programmer.

• Implicitly created by the compiler if no constructor is defined.

Example of an explicit default constructor:

public class Person {

private String name;

private int age;

// Explicit default constructor

public Person() {

341
The Complete Java Training Imran Afzal
// Manually setting default values

name = ""; // Empty string as default name

age = 0; // Default age set to 0

In this example:

• name is set to null (default for String).

• age is set to 0 (default for int).

• The constructor has no parameters and is used to assign default values to the instance
variables.

If no constructor is defined, Java automatically provides an implicit default constructor, which does
the same: initializes fields to their default values.

public class Person {

private String name;

private int age;

// No constructor declarations provided, so the compiler

// generates an implicit default constructor

342
The Complete Java Training Imran Afzal
Creating an object:

Person person = new Person();

Since no constructor is provided, the compiler adds an implicit default constructor, and name and age
will be initialized to null and 0, respectively.

2. Parameterized Constructor

A parameterized constructor allows you to initialize object fields with specific values at the time of
object creation.

Example:

public class Person {

private String name;

private int age;

public Person(String name, int age) {

this.name = name;

this.age = age;

Object creation using parameterized constructor:

Person person = new Person("John", 30);

343
The Complete Java Training Imran Afzal
Here, the Person object is created with name = "John" and age = 30.

8.6.3- Constructor Overloading

Java allows multiple constructors within the same class, as long as they have different parameter lists.
This enables flexibility in object creation.

Example:

public class Person {

private String name;

private int age;

public Person() {

name = ""; // Assigning an empty string instead of null

age = 0;

public Person(String name, int age) {

this.name = name;

this.age = age;

344
The Complete Java Training Imran Afzal
// Getter methods

public String getName() {

return name;

public int getAge() {

return age;

// Setter methods

public void setName(String name) {

this.name = name;

public void setAge(int age) {

this.age = age;

345
The Complete Java Training Imran Afzal
}

8.6.4- Considerations When Using Constructors

• Constructors should focus solely on initializing object state.

• Avoid using constructors for complex logic; consider using factory methods when
appropriate.

• Excessive parameters in constructors can lead to tight coupling and make maintenance
difficult.

• In inheritance, the constructor of the parent class is automatically called before the child class
constructor. Unexpected behavior can occur if parent constructors perform actions that don't
align with the child class.

This foundational concept of constructors is essential for effective class design and object creation in
Java.

8.7- Constructors Exercise Part 1

This exercise demonstrates how to define and use constructors in a class, along with getter and setter
methods to initialize, access, and modify the values of private instance variables.

8.7.1- Project Setup

Create a new project named ObjectOrientedConstructor. Within this project:

1. Create a class file named MainClass and use the shortcut psvm to generate the main method.

2. Create another class file named Library.

8.7.2- Defining the Library Class

Inside the Library class, declare the following private instance variables:

346
The Complete Java Training Imran Afzal
class Library {

private String name;

private int numBooks;

private String address;

• name stores the name of the library.

• numBooks holds the number of books in the library.

• address represents the location of the library.

Declaring these variables as private ensures that they cannot be accessed directly from outside the
class, maintaining encapsulation.

8.7.3- Constructor Definition

Create a parameterized constructor to initialize the variables:

// Constructor

public Library(String name, int numBooks, String address) {

this.name = name;

this.numBooks = numBooks;

this.address = address;

This constructor accepts three arguments and assigns them to the respective instance variables using
this keyword.

347
The Complete Java Training Imran Afzal
8.7.4- Getter Methods

Define getter methods to retrieve the values:

// Getter for name

public String getName() {

return name;

// Getter for numBooks

public int getNumBooks() {

return numBooks;

// Getter for address

public String getAddress() {

return address;

These methods allow controlled access to the private fields.

348
The Complete Java Training Imran Afzal
8.7.5- Setter Methods

Define setter methods to modify the values:

// Setter for name

public void setName(String name) {

this.name = name;

// Setter for numBooks

public void setNumBooks(int numBooks) {

this.numBooks = numBooks;

// Setter for address

public void setAddress(String address) {

this.address = address;

349
The Complete Java Training Imran Afzal
These methods provide a safe way to update the internal state of the object.

8.7.6- Main Class Implementation

In MainClass.java, create and initialize an object of the Library class:

class MainClass {

public static void main(String[] args) {

Library myLibrary = new Library("Central Library", 5000, "123 Main Street");

Use getter methods to display the initial values:

// Access instance variables using getter methods

System.out.println(myLibrary.getName());

System.out.println(myLibrary.getNumBooks());

System.out.println(myLibrary.getAddress());

Use setter methods to update the values:

// Modify instance variables using setter methods

myLibrary.setName("Downtown Library");

myLibrary.setNumBooks(10000);

myLibrary.setAddress("456 Elm Street");

Display the updated values using getters:

350
The Complete Java Training Imran Afzal
// Access modified instance variables using getter methods

System.out.println(myLibrary.getName());

System.out.println(myLibrary.getNumBooks());

System.out.println(myLibrary.getAddress());

Output

Central Library

5000

123 Main Street

Downtown Library

10000

456 Elm Street

The first set of values reflects those provided via the constructor, and the second set reflects the
updates made using setter methods.

8.8- Constructors Exercise Part 2

This exercise demonstrates how to use constructors along with getter and setter methods to initialize,
access, and update object fields. The example involves a Car class with private attributes and user
interaction via console input.

8.8.1- Project Setup

Create a new project named ObjectOrientedConstructor1. Within the project:

351
The Complete Java Training Imran Afzal
1. Create a class file named MainClass and use the shortcut psvm to generate the main method.

2. Create another class file named Car.

8.8.2- Defining the Car Class

Inside the Car class, declare the following private instance variables:

private String make;

private String model;

private int year;

import java.util.Scanner;

class Car {

private String make;

private String model;

private int year;

• make: Represents the manufacturer (e.g., Toyota).

• model: Represents the specific car model (e.g., Corolla).

• year: Represents the model year of the car.

8.8.3- Constructor Definition

Define a parameterized constructor to initialize the instance variables:

// Constructor

public Car(String make, String model, int year) {

this.make = make;

this.model = model;

352
The Complete Java Training Imran Afzal
this.year = year;

This constructor assigns the values received via parameters to the object's fields using the this
keyword.

8.8.4- Getter Methods

Define the following getter methods to access field values:

// Getter methods

public String getMake() {

return make;

public String getModel() {

return model;

public int getYear() {

return year;

353
The Complete Java Training Imran Afzal
8.8.5- Setter Methods

Define setter methods to update the field values:

// Setter methods

public void setMake(String make) {

this.make = make;

public void setModel(String model) {

this.model = model;

public void setYear(int year) {

this.year = year;

8.8.6- Main Class Implementation

In MainClass.java, begin by importing the Scanner class:

import java.util.Scanner;

354
The Complete Java Training Imran Afzal
Inside the main method, create a Scanner object and prompt the user for input:

class MainClass {

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

System.out.print("Enter the car's make: ");

String make = scanner.nextLine();

System.out.print("Enter the car's model: ");

String model = scanner.nextLine();

System.out.print("Enter the car's year: ");

int year = scanner.nextInt();

Use the input values to create a Car object:

Car myCar = new Car(make, model, year);

Display the initial values using getter methods:

System.out.println("Make: " + myCar.getMake());

355
The Complete Java Training Imran Afzal
System.out.println("Model: " + myCar.getModel());

System.out.println("Year: " + myCar.getYear());

Prompt the user to update the year:

System.out.print("Enter the updated year: ");

int updatedYear = scanner.nextInt();

myCar.setYear(updatedYear);

Print the updated year:

System.out.println("Updated year: " + myCar.getYear());

8.8.7- Sample Interaction and Output

Enter the car's make:

Toyota

Enter the car's model:

Corolla

Enter the car's year:

2022

Make: Toyota

Model: Corolla

356
The Complete Java Training Imran Afzal
Year: 2022

Enter the updated year:

2023

Updated year: 2023

In the MainClass, user input is collected using the Scanner class. The data is used to initialize a Car
object, which is then displayed and updated, showcasing the role of constructors and access methods
in object-oriented design.

8.9- Clear Understanding of Reference, Object and Instance

In Java programming, the terms reference, object, and instance are used frequently and sometimes
interchangeably, which can lead to confusion. This lecture clarifies the distinction between them using
conceptual explanation and practical examples.

8.9.1- Understanding the Class as a Blueprint

A class is like a blueprint. It defines the structure and behavior of objects but doesn't represent any
actual object itself. For example, a blueprint for an office building does not represent a real office; it's
simply a plan. When you create an actual office based on the blueprint, you are creating an object, also
known as an instance of that class. In programming terms, when you use the new keyword with a
class, you're creating a new instance (or object) of that class.

So, a class is the blueprint, an object is the real structure created from it, and an instance is another
word for an object.

8.9.2- What is a Reference?

A reference is like the address of an object. Think of it as a piece of paper with the location of the
office written on it. You can copy the reference (i.e., write the address on another paper), but it still
points to the same object. You’re not creating a new object; you’re just creating another reference to
the same one. In Java, objects are accessed indirectly via references. You never work with objects
directly, only through reference variables.

357
The Complete Java Training Imran Afzal
Consider this example:

Office myOffice = new Office("Nixware");

This line creates a new object of the Office class with the name "Nixware" and assigns the reference
of this object to the variable myOffice.

Office anotherOffice = myOffice;

Now, anotherOffice is a second reference to the same object. Both myOffice and anotherOffice refer
to the same memory location.

If you modify the object using either reference:

anotherOffice.setName("Nixware Consulting Company");

The change will be reflected through both references because they point to the same object.

System.out.println("Office name: " + myOffice.getName()); //Nixware Consulting Company

System.out.println("Office name: " + anotherOffice.getName()); //Nixware Consulting


Company

Creating a new object like this:

Office newOffice = new Office("Again Nixware");

Creates a completely separate object in memory. Now, myOffice and anotherOffice still point to one
object, while newOffice points to another.

If we assign:

anotherOffice = newOffice;

358
The Complete Java Training Imran Afzal
Then anotherOffice no longer points to the original object shared with myOffice. Instead, it now
points to the object newOffice refers to. After this assignment, both anotherOffice and newOffice
refer to the same object with the name "Again Nixware", and myOffice continues to point to the
object named "Nixware Consulting Company".

System.out.println("Office name: " + myOffice.getName()); //Nixware Consulting Company

System.out.println("Office name: " + newOffice.getName()); //Again Nixware

System.out.println("Office name: " + anotherOffice.getName()); //Again Nixware

In short, a reference is a variable that points to an object or an instance of a class. It holds the
memory address of the object, not the object itself. An object is an instance of a class that is created
at runtime. It occupies memory and has state and behavior. An instance is a specific occurrence of an
object created using the new keyword.

In Java, you always work with references to access and manipulate objects in memory.

8.10- Static versus Instance Variables and Methods


8.10.1- Static Variables

Static variables are declared using the static keyword and are shared across all instances of a class.
When a static variable is modified by one object, all other objects of that class will see the change.
These variables represent a shared property or data across the class.

359
The Complete Java Training Imran Afzal
8.10.2- Best Practices
It is recommended to access static variables using the class name rather than through an object
reference to clarify that the variable belongs to the class, not any specific object.

Example:
Consider a Cat class with a static variable name. The constructor sets the static name variable, and a
static method printName is used to print it. If two objects are created with the names "Maine Coon"
and "Ragdoll," calling printName on both objects will output "Ragdoll" because the static variable is
shared among all instances.

This highlights a potential issue: when using a static variable for something that logically should belong
to each individual object, like a cat's name, the behavior might be incorrect. It would be more
appropriate to use an instance variable in this case.

8.10.3- Instance Variables


Instance variables, unlike static variables, do not use the static keyword and are unique to each instance
of the class. Each object of the class has its own copy of the instance variables, and changes made to
one object's instance variables do not affect others.

Example:
If we modify the Cat class to use instance variables for the name, each object will maintain its own
version. Thus, creating two objects with the names "Maine Coon" and "Ragdoll" will correctly print
each name when printName is called on each object.

class Cat{

private String name;

public Cat(String name){

this.name = name;

360
The Complete Java Training Imran Afzal
}

public void printName(){

System.out.println("Name = " + name);

class MainClass{

public static void main(String[] args){

Cat MaineCoon = new Cat("Maine Coon");

Cat Ragdoll = new Cat("Ragdoll");

MaineCoon.printName(); // Prints Maine Coon

Ragdoll.printName(); // Prints Ragdoll

361
The Complete Java Training Imran Afzal
8.10.4- Static Methods

Static methods are declared with the static keyword and can only access static variables or other static
methods. They do not depend on any specific instance of the class and cannot use instance variables
or the this keyword.

Example:
A static method like main is invoked by the Java Virtual Machine when the program starts, and it
typically doesn't use instance data. Similarly, a method MyCompany might print company details, and
it can be called directly using the class name without creating an object.

class Cat{

private static String name;

public Cat(String name){

Cat.name = name;

public void printName(){

System.out.println("Name = " + name);

362
The Complete Java Training Imran Afzal
class MainClass{

public static void main(String[] args){

Cat MaineCoon = new Cat("Maine Coon");

Cat Ragdoll = new Cat("Ragdoll");

MaineCoon.printName(); // Prints Ragdoll

Ragdoll.printName(); // Prints Ragdoll

Static methods are typically used for operations that do not require access to instance-specific data.

8.10.5- Instance Methods

Instance methods belong to specific objects and can access both instance variables and static variables.
To invoke an instance method, an object of the class must be created.

Example:
In the MyCompany class, an instance method like CompanyName requires an object reference to be
called. The method can then access both instance and static variables or methods.

class MyCompany{

public void CompanyName(String name, String location){

363
The Complete Java Training Imran Afzal
System.out.println("Company Name is: " + name + " & Company Location is: " + location);

class MainClass{

public static void main(String[] args){

MyCompany Company = new MyCompany();

Company.CompanyName("Nixware","New York");

8.10.6- Deciding Between Static and Instance Methods

When deciding whether to use a static or instance method, consider the method's behavior:

• If the method does not rely on any instance data, make it static.

• If the method needs to interact with specific object data, use an instance method.

364
The Complete Java Training Imran Afzal
• If both static and instance data are required, use a static method and pass the instance as a
parameter.

Static methods and variables improve performance and reduce memory usage when data or operations
do not depend on specific instances. However, when object-specific behavior is needed, instance
methods and variables are the correct choice.

8.11- Exception Handling

8.11.1- Understanding Exceptions


An exception is an abnormal condition that arises during the execution of a program, commonly
referred to as a run-time error. In programming languages that lack built-in exception handling,
developers must manually check for and respond to errors using error codes or other methods. Java
simplifies this process by integrating exception handling into its object-oriented framework, enhancing
the structure and reliability of error management.

In Java, an exception is represented as an object that signifies an error condition occurring in the code.
When such a condition is encountered, the corresponding exception object is created and thrown by
the method where the error occurred. The method may either handle the exception or pass it to the
calling method for handling. Java exceptions may originate from the runtime system or be explicitly
thrown using custom logic.

8.11.2- Exception Handling Mechanism


Java manages exception handling using five main keywords: try, catch, throw, throws, and finally.

• try Block: Encloses code that might generate an exception. This block is monitored, and if an
exception occurs, it is caught by a subsequent catch block.

• catch Block: Catches and processes the exception. It contains the handling logic based on
the type of exception caught.

• throw Keyword: Explicitly throws an exception from within a method or block of code.

• throws Keyword: Declares exceptions that a method might throw, delegating their handling
to the calling method.

365
The Complete Java Training Imran Afzal
• finally Block: Contains code that executes regardless of whether an exception occurred. It is
typically used for releasing resources like files, connections, or sockets.

8.11.3- Syntax of try-catch Block


A try block surrounds the code that may cause an exception—such as file access, database
connectivity, or user input parsing. If an exception arises, control is transferred to the corresponding
catch block, which handles it. The catch block defines the type of exception to handle, for example:

try {

// code that may throw an exception

} catch (Exception e) {

// code to handle the exception

The object e represents the exception and can provide details such as the error message or stack trace.
If no exception is thrown, the catch block is skipped, and execution continues normally.

8.11.4- Multiple Catch Blocks


Java supports multiple catch blocks to handle different types of exceptions separately. For example:

try {

// code that may throw an exception

} catch (FileNotFoundException e) {

// code to handle file not found exception

} catch (IOException e) {

366
The Complete Java Training Imran Afzal
// code to handle input/output exception

} catch (Exception e) {

// code to handle all other exceptions

Each catch block addresses a specific type of exception, allowing targeted handling. If no exception
occurs, all catch blocks are bypassed.

8.11.5- Syntax of try-catch-finally Block


The finally block executes whether or not an exception is thrown. It ensures important cleanup
operations are performed:

try {

// code that may throw an exception

} catch (Exception e) {

// code to handle the exception

} finally {

// code to be executed regardless of whether an exception was thrown or not

The finally block executes after the try block finishes and after any relevant catch block, if an exception
occurred.

367
The Complete Java Training Imran Afzal
8.11.6- Benefits of Exception Handling in Java

• Improved Reliability: Anticipating and managing errors prevents program crashes and
ensures consistent behavior.

• Readable Code: Separating normal logic from error-handling logic improves code clarity.

• Maintainable Structure: Dedicated blocks for exception handling simplify updates and
modifications.

• Enhanced Debugging: Exception messages and stack traces help quickly locate and fix
issues.

• Support for Recovery: Allows handling of recoverable errors so the program can continue
executing.

8.11.7- Limitations of Exception Handling

• Performance Overhead: Handling exceptions consumes system resources, especially when


they occur frequently.

• Increased Complexity: Excessive use of exceptions can obscure program flow and make
logic harder to follow.

• Misuse: Using exceptions for flow control rather than genuine error management leads to
poor coding practices.

• Security Risks: Unhandled exceptions may expose sensitive information through stack traces
or logs.

• Memory Usage: Exception objects consume memory, especially if many are created or
retained in memory.

8.11.8- Example Without Exception Handling

class Exce {

public static void main(String args[]) {

368
The Complete Java Training Imran Afzal
int d = 0;

int a = 42 / d; // This will throw ArithmeticException at runtime

This code throws an ArithmeticException due to division by zero. Since there's no exception handling,
the program terminates and displays an error message.

8.11.9- Handled Example Using try-catch

class Exce {

public static void main(String args[]) {

try {

int d = 0;

int a = 42 / d;

} catch (ArithmeticException e) {

System.out.println("Error: Cannot divide by zero.");

369
The Complete Java Training Imran Afzal
In this version, the exception is caught and handled gracefully. Instead of crashing, the program
outputs an error message, allowing for a controlled response to the error.

8.12- Exception Handling Exercise

8.12.1- Program Overview


This exercise demonstrates exception handling in Java by creating a program that accepts two integers
from the user—a numerator and a denominator—and performs division. The program utilizes a try-
catch block to handle potential ArithmeticException scenarios, such as division by zero.

8.12.2- Creating the Project and Class


A new project named exceptionhandling is created. Within it, a class file named DivideException is
defined. To take user input, the Scanner class is used, and it is imported at the top of the program
using:

import java.util.Scanner;

8.12.3- Defining the Division Method


The division logic is encapsulated in an instance method:

import java.util.Scanner;

class DivideException {

public void doDivide(int a, int b){

float result = a / b;

System.out.println("Division result of " + a + " / " + b + " = " + result);

370
The Complete Java Training Imran Afzal
The doDivide method receives two integers as parameters and performs the division. It prints the
result in a formatted message that includes the numerator, denominator, and the result.

However, if the denominator is zero, an ArithmeticException will occur. Therefore, exception


handling should be added using a try-catch block.

8.12.4- Main Method and Input Handling


The main method is defined using the standard shortcut:

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

System.out.print("Please enter first number(numerator): ");

int numerator = scanner.nextInt();

System.out.print("Please enter second number(denominator): ");

int denominator = scanner.nextInt();

DivideException de = new DivideException();

de.doDivide(numerator,denominator);

}
371
The Complete Java Training Imran Afzal
}

Here, a Scanner object is created to read inputs from the console. The user is prompted to enter the
numerator and denominator. These values are passed to the doDivide method of the DivideException
object.

8.12.5- Initial Execution and Unhandled Exception


When the program is run with a denominator of 0, Java throws an ArithmeticException because
division by zero is undefined. The program crashes with a message like:

Exception in thread "main" java.lang.ArithmeticException: / by zero

This occurs because the exception is not handled in the doDivide method.

8.12.6- Adding Exception Handling


To manage this scenario, the division logic is modified to include a try-catch block:

public void doDivide(int a, int b){

try{

float result = a / b;

System.out.println("Division result of " + a +" / " + b + " = " + result);

}catch(ArithmeticException e){

System.out.println("Exception is Handled");

372
The Complete Java Training Imran Afzal
System.out.println("After try catch blocks");

The try block wraps the division operation, while the catch block captures any ArithmeticException
that may occur. If the denominator is zero, the catch block executes and prints an appropriate message.
Regardless of whether an exception is thrown, the message "After try catch blocks" is displayed
afterward.

8.12.7- Test Run with Zero Denominator


When the program is executed and the user inputs 12 as the numerator and 0 as the denominator, the
output is:

• Exception is Handled
• After try catch blocks

The division operation fails, but instead of crashing, the program handles the exception gracefully.
The message in the catch block is displayed, followed by the final statement outside the exception-
handling structure.

This implementation demonstrates effective use of exception handling to maintain program stability
and provide user-friendly error messages.

8.13- Introduction to Inheritance

8.13.1- What is Inheritance?


Inheritance is a foundational concept in object-oriented programming that enables code reuse and
logical class organization. It allows one class (child or subclass) to inherit fields and methods from
another class (parent or superclass), reducing redundancy and supporting hierarchical class
relationships.

373
The Complete Java Training Imran Afzal
8.13.2- Vehicle Hierarchy Example
A vehicle classification provides a clear example of inheritance. A generic class Vehicle sits at the top
of the hierarchy, and subclasses like Car or Bus inherit its properties and behaviors. In Java, each class
can extend only one direct parent, but it also indirectly inherits from that parent’s ancestors.

8.13.3- Creating the Base Class Vehicle


A new Java class Vehicle is created with three fields: make, model, and year.

public class Vahicle {

private String make;

private String model;

private int year;

A constructor is generated to initialize these fields. A toString method is also generated to print these
attributes in a formatted manner. This method uses the @Override annotation to indicate that it
overrides a method from the superclass (in this case, from Object). The toString method enables any
object of the class to be printed meaningfully using System.out.println().

8.13.4- Adding Methods to Vehicle


Two behavior methods are added:

public void move(String speed){

System.out.println(model + " moves " + speed);

public void service(int month){

System.out.println(model + " needs servicing every " + month + " months");

374
The Complete Java Training Imran Afzal
}

8.13.5- Creating the Subclass Car


The Car class is defined to inherit from Vehicle using the extends keyword:

public class Car extends Vehicle { }

Since Vehicle lacks a default constructor, one must be added:

public Vehicle() { }

The Car class also adds its own default constructor:

public Car() {

super();

The super() call invokes the parent constructor and must be the first statement in any constructor.

8.13.6- Creating a Shared Method for Vehicles


A static method doVehicleStuff is defined in the Main class to accept any Vehicle object and call its
methods:

public static void doVahicleStuff(Vahicle vahicle, String speed, int month){

vahicle.service(month);

vahicle.move(speed);

System.out.println(vahicle);
375
The Complete Java Training Imran Afzal
System.out.println("____");

8.13.7- Instantiating and Using Vehicle and Car


A Vehicle object is created and passed to doVehicleStuff:

public class MainClass {

public static void main(String[] args) {

Vahicle vahicle = new Vahicle("Toyota", "Camry",2023);

doVahicleStuff(vahicle,"Fast",1);

Then a Car object is created using the default constructor:

Car car = new Car();

doVahicleStuff(car,"Fast",2);

Since the constructor doesn't set any values, the printed fields appear as null or 0.

8.13.8- Parameterized Constructor in Car


A new constructor is added in Car to pass values directly to Vehicle:

public Car() {

super("Honda", "Civic", 2022);

376
The Complete Java Training Imran Afzal
Now, running the main method shows the fields with correct values.

8.13.9- Adding Car-Specific Attributes


Fields specific to Car are introduced:

public Car(String make, int year, int numberofDoors, int horsePower) {

A new constructor is created using IntelliJ's generate option, which also utilizes the parent constructor.
The model value is derived using a nested ternary operator based on the year:

super(make, year > 2022 ? make + " Latest Model" : (year < 2020 ? make+ " Previous Model" :
make + " Old Model"), year);

After the super() call, the new fields are initialized.

8.13.10- Constructor Overloading


An overloaded constructor simplifies object creation:

public Car(String make, int year){

this(make, year, 4, 9);

This constructor uses default values for numberOfDoors and horsepower, and chains to the four-
argument constructor.

8.13.11- Printing Car Attributes


The toString method in Car is overridden and includes a call to super.toString() to include Vehicle
fields:

@Override

377
The Complete Java Training Imran Afzal
public String toString() {

return "Car{" +

"NumberofDoors=" + NumberofDoors +

", horsePower=" + horsePower +

"}

8.13.12- Using the Enhanced Car Constructor


In the Main class, more Car instances are created and passed to doVehicleStuff:

Car audi = new Car("Audi", 2019);

doVahicleStuff(audi, "Fast", 2);

Car tesla = new Car("Tesla", 2023, 4, 1000);

doVahicleStuff(tesla, "Super Fast", 1);

The output includes all inherited and subclass-specific fields.

8.13.13- Demonstrating Polymorphism


Even though doVehicleStuff accepts a Vehicle object, it correctly calls the Car implementation of
toString. This is because Java supports runtime polymorphism, allowing overridden methods in
subclasses to be called even when referenced by a superclass type.

8.13.14- Overriding Methods in Subclass


The Car class overrides the service method to customize its behavior:

378
The Complete Java Training Imran Afzal
@Override

public void service(int month)

// customized behavior or even an empty implementation

Using IntelliJ's generate tool, the move method is also overridden:

@Override

public void move(String speed) {

super.move(speed);

System.out.println("Car Starts, run on road and work their engine");

This is an example of extending behavior: executing the superclass method first, then adding subclass-
specific functionality.

8.14- Inheritance Exercise, Part 3

8.14.1- Rectangle Class Implementation


A program is developed to calculate the volume of cuboid-shaped swimming pools by using
inheritance.

A class named Rectangle is created with two private instance variables:

379
The Complete Java Training Imran Afzal
private double width;

private double length;

These variables represent the width and length of a rectangle. The constructor for this class takes two
parameters and ensures both dimensions are non-negative:

public Rectangle(double width, double length) {

this.width = width < 0 ? 0 : width;

this.length = length < 0 ? 0 : length;

To allow default object creation when extended, a no-argument constructor is also defined:

public Rectangle() { }

Getter methods are implemented to retrieve values:

public double getWidth()

return width;

public double getLength()

380
The Complete Java Training Imran Afzal
return length;

public double getArea()

return width * length;

The getArea method calculates and returns the area using the stored dimensions.

8.14.2- Cuboid Class Implementation


A new class Cuboid is created as a subclass of Rectangle:

public class Cuboid extends Rectangle

private double height;

public Cuboid(double width, double length, double height)

super(width, length);

this.height = height < 0 ? 0 : height;

381
The Complete Java Training Imran Afzal
}

public double getHeight()

return height;

public double getVolume()

return getArea() * height;

The Cuboid class introduces an additional dimension—height. It uses the super keyword to call the
parent constructor for initializing width and length. The height is validated similarly to ensure it's non-
negative. The getVolume method calculates the volume of the cuboid by multiplying the base area
(from getArea) with the height.

8.14.3- Main Class Execution


In the MainClass, an object of Cuboid is created:

Cuboid pool = new Cuboid(10.0, 20.0, 5.0);

382
The Complete Java Training Imran Afzal
double volume = pool.getVolume();

System.out.println("The volume of the pool is " + volume);

This code initializes a cuboid with a width of 10.0, length of 20.0, and height of 5.0. It then computes
the volume and prints:

The volume of the pool is 1000.0

8.15- The Plain Old Java Object

8.15.1- Understanding POJO (Plain Old Java Object)


A POJO is a standard Java class used to encapsulate data and business logic without being bound to
any specific Java framework or enterprise APIs like EJB (Enterprise JavaBeans). It does not require
inheritance from special classes or implementation of framework-specific interfaces. POJOs use only
core Java (JDK) classes and are typically composed of private fields with public getters and setters, a
default constructor, and optional parameterized constructors.

A POJO:

• Is a simple, standalone Java class

• Can extend or implement any class or interface from the JDK

• Should not depend on external frameworks or libraries

8.15.2- Examples of POJO Classes

// Valid POJO (JDK only)

public class Test

383
The Complete Java Training Imran Afzal
// Valid POJO (Extends JDK class)

public class Test extends Thread

// Valid POJO (Implements JDK interface)

public class Test implements Runnable

// Not a POJO (Implements external framework interface)

public class Demo implements javax.servlet.Servlet

// Not a POJO (Indirectly depends on external API)

384
The Complete Java Training Imran Afzal
public class MyApp extends Demo

8.15.3- Exception Case

public class MyJDBC

void someJDBCMethod()

// JDBC logic requiring external library

Although MyJDBC is a POJO in structure, it uses code that requires external libraries to compile,
making it an exceptional case.

8.15.4- Use and Purpose of POJO


POJOs are frequently used to:

• Hold and transfer data

• Represent database records (entities)

• Serve as Data Transfer Objects (DTOs)

• Simplify working with frameworks like Hibernate or Jackson

385
The Complete Java Training Imran Afzal
8.15.5- Java Beans and DTOs
A Java Bean is essentially a POJO that follows stricter conventions (must be serializable, have a no-
arg constructor, and follow naming conventions for getters/setters). A DTO (Data Transfer Object)
is often a POJO used specifically to carry data across processes or layers in an application.

8.15.6- Creating a POJO Example: Employee Class

public class Employee {

private String id;

private String name;

private String dateOfBirth;

private String comanyName;

public Employee(String id, String name, String dateOfBirth, String comanyName) {

this.id = id;

this.name = name;

this.dateOfBirth = dateOfBirth;

this.comanyName = comanyName;

@Override

public String toString() {

return "Employee{" +

"id='" + id + '\'' +

", name='" + name + '\'' +

386
The Complete Java Training Imran Afzal
", dateOfBirth='" + dateOfBirth + '\'' +

", comanyName='" + comanyName + '\'' +

'}';

8.15.7- Creating Multiple Employee Instances Using Loop and Switch

public class MainClass {

public static void main(String[] args) {

for(int i = 1; i <=5; i++){

Employee e = new Employee("EN" + i,

switch (i){

case 1 -> "Bob";

case 2 -> "John";

case 3 -> "Imran";

case 4 -> "Kashif";

case 5 -> "Anderson";

default -> "Anonymous";

},

"07-01-1980",

387
The Complete Java Training Imran Afzal
"Nixware");

System.out.println(e);

This loop dynamically generates employees with unique IDs and names using a switch expression.

8.15.8- Introduction to Record in Java (JDK 16+)


Java introduced the record type to eliminate the boilerplate code often written in POJOs. A record:

• Automatically generates private final fields, constructors, accessors, and toString, equals, and
hashCode methods

• Is immutable (fields cannot be changed after creation)

• Is ideal for read-only data containers and DTOs

8.15.9- Creating a Record for Employee

public record NCEmployee(String id, String name, String dateOfBirth, String companyName) { }

Using Record in Main Class

NCEmployee e = new NCEmployee("EN1", "Ivan", "11th February 1982", "Nixware");

System.out.println(e); // Automatically calls generated toString

Comparing POJO vs. Record POJO:

• Requires manually writing or generating constructors, getters, and toString

• Can be mutable

388
The Complete Java Training Imran Afzal
• Flexible for logic and field updates

Record:

• Immutable by default

• Auto-generates common methods

• Ideal for simple data carriers

Accessing Record Fields

System.out.println(recordEmployee.name() + " is part of " + recordEmployee.companyName());

Note: Accessor methods in records match the field names directly and do not use get prefixes.

8.15.10- When to Use Records vs. POJOs


Use a record when:

• You need a data container with fixed, read-only fields

• You want to avoid boilerplate code

Use a POJO when:

• You need mutable fields

• Your class requires business logic or custom behavior

8.16- The Plain Old Java Object Exercise Part 1

8.16.1- Class Design: Wall

A class named Wall is created to demonstrate the concept of a Plain Old Java Object (POJO). This
class represents a wall with two essential attributes: width and height.

public class Wall {

private double width;

389
The Complete Java Training Imran Afzal
private double height;

8.16.2- Declaration of Fields

These two private fields encapsulate the dimensions of the wall, ensuring that they cannot be accessed
or modified directly from outside the class.

8.16.3- Constructors

public Wall() {

this.width = 20;

this.height = 30;

This no-argument constructor initializes the dimensions to zero by default.

if (width < 0) {

this.width = 0;

} else {

this.width = width;

if (height < 0) {

this.height = 0;

} else {

390
The Complete Java Training Imran Afzal
this.height = height;

The parameterized constructor allows setting custom values for width and height. If a negative value
is passed, it sets the respective field to 0 to ensure valid dimensions.

8.16.4- Getter and Setter Methods

public double getWidth() {

return width;

public void setWidth(double width) {

if (width < 0) {

this.width = 0;

} else {

this.width = width;

public double getHeight() {

return height;

391
The Complete Java Training Imran Afzal
}

public void setHeight(double height) {

if (height < 0) {

this.height = 0;

} else {

this.height = height;

These getter and setter methods follow JavaBean conventions:

• Accessors (getters) allow reading the values of width and height.

• Mutators (setters) allow modifying them, with validation to ensure only non-negative values
are accepted.

8.16.5- Area Calculation Method

public double getArea() {

return width * height;

This method calculates and returns the area by multiplying width and height.

392
The Complete Java Training Imran Afzal
8.16.7- Main Class: Testing the Wall Class

In the MainClass, the Wall class is tested through object creation and method invocation.

8.16.8- Creating an Object Using the No-Argument Constructor

public class MainClass {

public static void main(String[] args) {

Wall wall1 = new Wall();

System.out.println("Wall1 width: " + wall1.getWidth());

System.out.println("Wall1 height: " + wall1.getHeight());

System.out.println("Wall1 area: " + wall1.getArea());

This creates a Wall object with default values. Output:

Wall1 width: 0.0

Wall1 height: 0.0

Wall1 area: 0.0

8.16.9- Creating an Object Using the Parameterized Constructor

Wall wall2 = new Wall(5.0, 3.0);

System.out.println("Wall2 width: " + wall2.getWidth());

System.out.println("Wall2 height: " + wall2.getHeight());

System.out.println("Wall2 area: " + wall2.getArea());

393
The Complete Java Training Imran Afzal
}

This creates a wall with specific dimensions.

Output:

Wall2 width: 5.0

Wall2 height: 3.0

Wall2 area: 15.0

8.17- The Plain Old Java Object Exercise Part 2

8.17.1- Problem Statement


A carpet company needs a program that calculates the price of carpeting for rectangular rooms. The
price is calculated by multiplying the floor area (width × length) by the cost per square meter of the
carpet.

8.17.2- Class Design: Floor


This class represents a floor with width and length.

public class Floor {

private double width;

private double length;

public Floor(double width, double length) {

394
The Complete Java Training Imran Afzal
if (width < 0) {

this.width = 0;

} else {

this.width = width;

if (length < 0) {

this.length = 0;

} else {

this.length = length;

public double getArea() {

return width * length;

395
The Complete Java Training Imran Afzal
• width and length are private fields to ensure encapsulation.

• The constructor ensures no negative values are assigned.

• getArea() returns the area by multiplying width and length.

8.17.3- Class Design: Carpet


This class represents the cost of the carpet per square meter.

public class Carpet {

private double cost;

public Carpet(double cost) {

if (cost < 0) {

this.cost = 0;

} else {

this.cost = cost;

public double getCost() {

return cost;

396
The Complete Java Training Imran Afzal
}

• The cost is validated and encapsulated.

• getCost() returns the cost per square meter.

8.17.4- Class Design: Calculator


This class calculates the total cost based on the floor area and carpet cost.

public class Calculator {

private Floor floor;

private Carpet carpet;

public Calculator(Floor floor, Carpet carpet) {

this.floor = floor;

this.carpet = carpet;

public double getTotalCost() {

return floor.getArea() * carpet.getCost();

397
The Complete Java Training Imran Afzal
}

• The Calculator holds a Floor and Carpet object.

• getTotalCost() returns the total carpeting cost using their methods.

Main Class for Execution

class MainClass{

public static void main(String[] args) {

Floor floor = new Floor(12, 10);

Carpet carpet = new Carpet(8);

Calculator calculator = new Calculator(floor, carpet);

double totalCost = calculator.getTotalCost();

System.out.println("The total cost is $" + totalCost);

Execution Summary

• A Floor object is created with a width of 12 and length of 10 meters.

• A Carpet object is created with a cost of 8 dollars per square meter.

• A Calculator object uses both to calculate the total cost:


12 * 10 = 120 square meters × $8 = $960.0

398
The Complete Java Training Imran Afzal
Console Output

The total cost is $960.0

This exercise demonstrates how to use basic POJO structures and object composition to solve a real-
world scenario in a structured, maintainable way.

8.18- The Plain Old Java Object Exercise Part 3

8.18.1- Class Design: ComplexNumber

The ComplexNumber class is designed to represent complex numbers of the form a + bi, where:

• a is the real part

• b is the imaginary part

8.18.2- Fields

public class ComplexNumber {

private double real;

private double imaginary;

These are private fields to encapsulate the data of a complex number.

8.18.3- Constructor

public ComplexNumber(double real, double imaginary) {

this.real = real;

this.imaginary = imaginary;

399
The Complete Java Training Imran Afzal
The constructor initializes the real and imaginary parts of the complex number.

8.18.4- Getter Methods

public double getReal() {

return this.real;

public double getImaginary() {

return this.imaginary;

These methods allow controlled access to the real and imaginary fields.

8.18.5- Addition Methods

public void add(double real, double imaginary) {

this.real += real;

this.imaginary += imaginary;

public void add(ComplexNumber other) {

this.real += other.getReal();

this.imaginary += other.getImaginary();

400
The Complete Java Training Imran Afzal
These methods support adding either:

• two separate values (real and imaginary), or

• another ComplexNumber object

8.18.6- Subtraction Methods

public void subtract(double real, double imaginary) {

this.real -= real;

this.imaginary -= imaginary;

public void subtract(ComplexNumber other) {

this.real -= other.getReal();

this.imaginary -= other.getImaginary();

These methods allow subtracting values or another ComplexNumber from the current instance.

8.18.7- Main Method Implementation (Testing)

class MainClass{

public static void main(String[] args) {

401
The Complete Java Training Imran Afzal
ComplexNumber c1 = new ComplexNumber(5, 3);

ComplexNumber c2 = new ComplexNumber(4, 2);

c1.add(c2);

System.out.println(c1.getReal() + " + " + c1.getImaginary() + "i"); // prints "9.0 + 5.0i"

ComplexNumber c3 = new ComplexNumber(3, 1);

ComplexNumber c4 = new ComplexNumber(-1, 2);

c3.subtract(c4);

System.out.println(c3.getReal() + " + " + c3.getImaginary() + "i"); // prints "2.0 + -1.0i"

Output

9.0 + 5.0i

2.0 + 3.0i

Explanation

1. c1 (5 + 3i) + c2 (4 + 2i)
→ Real: 5 + 4 = 9
→ Imaginary: 3 + 2 = 5
Result: 9.0 + 5.0i

2. c3 (3 + 1i) + c4 (-1 + 2i)


→ Real: 3 + (-1) = 2
→ Imaginary: 1 + 2 = 3
Result: 2.0 + 3.0i

402
The Complete Java Training Imran Afzal
This example successfully applies POJO design principles to model and manipulate complex numbers.

8.19- The Plain Old Java Object Exercise Part 4

8.19.1- Class: Point

This class models a point in a 2D coordinate system.

8.19.2- Fields

public class Point {

private int x;

private int y;

These fields store the x and y coordinates of the point and are kept private for encapsulation.

8.19.3- Constructors

public Point() {

this.x = 0;

this.y = 0;

This no-argument constructor initializes the point to the origin (0,0).

public Point(int x, int y) {

this.x = x;

this.y = y;

403
The Complete Java Training Imran Afzal
}

This constructor initializes the point to the specified coordinates.

8.19.4- Getters and Setters

// Getter for x

public int getX() {

return this.x;

// Getter for y

public int getY() {

return this.y;

// Setter for x

public void setX(int x) {

this.x = x;

404
The Complete Java Training Imran Afzal
// Setter for y

public void setY(int y) {

this.y = y;

These methods allow external classes to read or modify the coordinates in a controlled way.

8.19.5- Method: distance()

// Distance between this Point and Point 0,0

public double distance() {

int x1 = 0;

int y1 = 0;

return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));

This method returns the distance between the current point and the origin (0,0).

8.19.6- Method: distance(int x, int y)

// Distance between this Point and Point x,y

public double distance(int x, int y) {

return Math.sqrt((this.x - x) * (this.x - x) + (this.y - y) * (this.y - y));

405
The Complete Java Training Imran Afzal
}

This method returns the distance between the current point and a point at the specified (x, y)
coordinates.

8.19.7- Method: distance(Point another)

// Distance between this Point and another Point

public double distance(Point another) {

return Math.sqrt((this.x - another.getX()) * (this.x - another.getX()) + (this.y - another.getY()) *


(this.y - another.getY()));

This method returns the distance between the current point and another Point object.

8.19.8- Main Class: MainClass

class MainClass{

public static void main(String[] args){

// Creating a new Point object

Point p1 = new Point();

// Setting the x and y values of p1

p1.setX(3);

p1.setY(4);

406
The Complete Java Training Imran Afzal
// Getting the x and y values of p1

System.out.println("p1.x = " + p1.getX());

System.out.println("p1.y = " + p1.getY());

// Creating a new Point object with x = 5, y = 6

Point p2 = new Point(5, 6);

// Getting the distance between p1 and (0,0)

System.out.println("Distance between p1 and (0,0): " + p1.distance());

// Getting the distance between p1 and (1,1)

System.out.println("Distance between p1 and (1,1): " + p1.distance(1, 1));

// Getting the distance between p1 and p2

System.out.println("Distance between p1 and p2: " + p1.distance(p2));

Sample Output

p1.x = 3

p1.y = 4

407
The Complete Java Training Imran Afzal
Distance between p1 and (0,0): 5.0

Distance between p1 and (1,1): 3.605551275463989

Distance between p1 and p2: 2.8284271247461903

This practice reinforces the use of POJO classes to perform fundamental data operations and
showcases method overloading with real-world logic.

8.20- Introduction to java.lang.Object

Java uses inheritance as a core concept in its class hierarchy. Every class in Java implicitly extends a
built-in class named Object, which resides in the java.lang package. This class serves as the root of the
entire class hierarchy. All classes, even arrays, directly or indirectly inherit from it, giving them access
to predefined methods that can be used or overridden.

For example, even a custom class like MainClass implicitly extends Object, unless explicitly extended
from another class. Java simplifies development by providing this implicit inheritance and by
automatically including core libraries like java.lang, so classes such as Object can be referenced directly
without needing import statements.

8.20.1- Exploring the Object Class in Java


To explore this concept practically, create a Java project in IntelliJ named JavaLangObject, and then
create a class named MainClass. Inside, define the main method using the shortcut psvm. Although
no extends keyword is visible, MainClass is still inheriting from Object.

Using IntelliJ’s code generation tools, go to Generate > Override Methods. A list of methods
appears—these are all inherited from the Object class. The class MainClass could explicitly extend
java.lang.Object, but it’s unnecessary due to Java’s implicit behavior.

Right-clicking on Object in the code and navigating to its declaration shows the source of Object.java.
Among its methods are hashCode(), equals(Object obj), and toString(). These methods are inherited
by all classes and can be overridden as needed.

408
The Complete Java Training Imran Afzal
8.20.2- Demonstrating Method Overriding

To see the impact of overriding, define another class named Employee:

class Employee {

private String name;

private int age;

Employee(String name, int age) {

this.name = name;

this.age = age;

Now, create an instance of Employee in the main method:

Employee employee = new Employee("John", 30);

System.out.println(employee.toString());

Running this prints something like Employee@1b28cdfa. This output comes from the default
toString() method in Object, which returns the class name followed by the @ symbol and the hash
code of the object instance.

This isn’t very informative. Override the toString() method in Employee to provide more meaningful
output:

@Override

public String toString() {

409
The Complete Java Training Imran Afzal
return name + " is " + age;

Now, calling System.out.println(employee) prints John is 30. This demonstrates how overriding the
toString() method enhances object representation.

Java also implicitly calls the toString() method when an object is passed to System.out.println(), so
writing System.out.println(employee) is functionally equivalent to
System.out.println(employee.toString()).

8.20.3- Inheritance and Overriding in Java

To explore inheritance further, define a new class ExperiencedEmployee that extends Employee:

class experiencedEmployee extends Employee{

private String employeeId;

experiencedEmployee(String name, int age, String employeeId){

super(name, age);

this.employeeId = employeeId;

Create an instance of ExperiencedEmployee:

ExperiencedEmployee experienced = new ExperiencedEmployee("Anderson", 45, "EN109");

System.out.println(experienced);

Initially, this uses the toString() method from Employee, printing Anderson is 45.

Override toString() in ExperiencedEmployee to include the employeeId:

410
The Complete Java Training Imran Afzal
@Override

public String toString() {

return employeeId + " Experienced Employee " + super.toString();

This now prints EN109 Experienced Employee Anderson is 45. Here, super.toString() refers to the
overridden method in Employee, not the original one in Object.

8.20.4- Java’s Single Inheritance

Java supports single inheritance only. For example, trying to extend both Employee and Object
directly will result in a compilation error. However, because Employee itself extends Object, the class
ExperiencedEmployee inherits members from both.

If Employee had not overridden toString(), then calling super.toString() in ExperiencedEmployee


would reference the method from Object. Since it does, the reference chain flows from
ExperiencedEmployee to Employee.

All classes that don’t explicitly extend another class automatically extend Object, which provides a set
of default methods like toString(), equals(), and hashCode(). These methods can be overridden to fit
the needs of your specific class implementations.

8.21- Summary of This and Super Keyword

The keywords this and super are fundamental in Java for referencing class members and constructors,
particularly in object-oriented programming involving inheritance and constructor chaining.

411
The Complete Java Training Imran Afzal
8.21.1- Understanding super and this

In Java, both this and super are keywords used to access class members, but they serve different
purposes:

• this refers to the current class — it's used to access fields, methods, or constructors of the
same class.

• super refers to the parent class — it's used to access inherited fields, methods, or constructors
from a superclass.

8.21.2- Using this Keyword to Avoid Confusion

We often use the this keyword in constructors or setters to resolve name conflicts between class fields
and parameters. Here's an example:

public class Building {

private String address;

public Building(String address) {

this.address = address; // 'this' resolves variable conflict

public String getAddress() {

return address; // 'this' is optional here

public String setAddress(String address) {

this.address = address; // again, 'this' clarifies usage

412
The Complete Java Training Imran Afzal
}

In the constructor and setter, this.address ensures we're referring to the instance variable and not the
parameter. In the getter, there's no conflict, so this is optional.

8.21.3- Using super to Access Parent Class Members

When a subclass overrides a method, and you still want to call the parent class version of that method,
you can use super.

Here is an example:

class ParentClass {

public void displayMessage() {

System.out.println("Message displayed in ParentClass.");

class ChildClass extends ParentClass {

@Override

public void displayMessage() {

super.displayMessage(); // Call to superclass method

System.out.println("Message displayed in ChildClass.");

413
The Complete Java Training Imran Afzal
}

class MainClass {

public static void main(String[] args) {

ChildClass c = new ChildClass();

c.displayMessage();

Without super.displayMessage();, the overridden method would call itself recursively, causing a stack
overflow.

8.21.4- Calling Constructors: this() and super()

In Java, you can call one constructor from another within the same class using this() — this technique
is known as constructor chaining. Similarly, you can call a parent class constructor using super().

• this(): Used to call another constructor in the same class.

• super(): Used to call a constructor from the parent class.

Only one of them can be used in a constructor, and it must be the first statement.

Here is an example of a bad constructor:

class Triangle {

414
The Complete Java Training Imran Afzal
private int y;

private int z;

private double base;

private double height;

public Triangle() {

this.y = 0;

this.z = 0;

this.base = 0;

this.height = 0;

public Triangle(double base, double height) {

this.y = 0;

this.z = 0;

this.base = base;

this.height = height;

415
The Complete Java Training Imran Afzal
}

public Triangle(int y, int z, double base, double height) {

this.y = y;

this.z = z;

this.base = base;

this.height = height;

This approach repeats code across constructors. It’s hard to maintain and error-prone.

Here is an example of good constructor using this () constructor chaining:

class Triangle {

private int y;

private int z;

private double base;

private double height;

416
The Complete Java Training Imran Afzal
public Triangle() {

this(0, 0);

public Triangle(double base, double height) {

this(0, 0, base, height);

public Triangle(int y, int z, double base, double height) {

this.y = y;

this.z = z;

this.base = base;

this.height = height;

In this improved version, only the final constructor performs the initialization. The others simply
delegate to it using this(). This ensures that all object creation flows through a single path, making the
code more maintainable and consistent.

417
The Complete Java Training Imran Afzal
8.22- Summary of Method Overloading and Overriding

Method overloading and method overriding are both techniques that allow Java developers to define
methods with the same name but different behavior, depending on the context. Despite their
similarities in name, these two concepts are fundamentally different in purpose and application.

8.22.1- Method Overloading

Method overloading occurs when two or more methods in the same class share the same name but
have different parameter lists. The parameters must differ in type, number, or order. The return type
may or may not be different, but it does not affect the method signature when determining
overloading.

Overloading allows method reuse while providing flexibility to handle different data types or scenarios.
It improves code readability and reduces the need for creating multiple method names for similar
functionality.

Overloaded methods can be either static or instance methods. Java resolves overloaded methods at
compile time based on the method name and the argument list, which is why overloading is referred
to as compile-time polymorphism.

While method overloading usually occurs within a single class, it can also occur in subclasses, which
may define additional overloaded versions of an inherited method.

8.22.2- Rules for Method Overloading:

• Methods must have the same name.

• Methods must have different parameter lists.

• Methods may have different return types.

• Methods may have different access modifiers.

• Methods may throw different exceptions.

418
The Complete Java Training Imran Afzal
8.22.3- Method Overriding

Method overriding allows a subclass to provide a specific implementation of a method that is already
defined in its superclass. The method in the subclass must have the same name, return type, and
parameters as the method in the parent class.

Overriding is used to achieve runtime polymorphism, also known as dynamic method dispatch,
because the method to execute is determined at runtime based on the object type.

Only instance methods can be overridden; static methods, constructors, and private methods cannot
be overridden. The @Override annotation is recommended above the method declaration to ensure
the method is correctly overriding one from the superclass. If the method does not properly override,
the compiler will generate an error when the annotation is used.

8.22.4- Rules for Method Overriding:

• Must have the same method name and parameters.

• The return type must be the same or a subclass (known as covariant return type).

• The access modifier of the overriding method cannot be more restrictive than the method
being overridden.

• Only inherited methods can be overridden.

• Final methods cannot be overridden.

• Constructors and private methods cannot be overridden.

8.22.5- Using super with Overridden Methods

When a subclass overrides a method, it can still access the parent class's version of the method using
the super keyword:

public class Cat {

public void Voice() {

419
The Complete Java Training Imran Afzal
System.out.println("Meow");

public class Ragdoll extends Dog {

@Override

public void Voice() {

System.out.println("Meow Meow");

This allows both the original and overridden behavior to be combined.

8.22.6- Overloading Method

public class Cat {

public void Voice() {

System.out.println("Meow");

420
The Complete Java Training Imran Afzal
public void Voice(int number) {

for (int i = 0; i < number; i++) {

System.out.println("Meow");

8.22.7- Recap of Overloading vs Overriding:

• Overloading:

o Happens within a class (or between a class and its subclass).

o Requires different parameter lists.

o May have different return types.

o Can apply to static and instance methods.

o Determined at compile time.

• Overriding:

o Happens between superclass and subclass.

o Requires the same method signature.

o Return type must be the same or covariant.

o Applies only to instance methods.

o Determined at runtime.

421
The Complete Java Training Imran Afzal
8.22.8- Covariant Return Type

Covariant return type means that when a method is overridden, the return type in the subclass can be
a subclass of the return type declared in the superclass.

Example:

public class Animal {

public Animal reproduce() {

return new Animal();

public class Dog extends Animal {

@Override

public Dog reproduce() {

return new Dog();

In this example, the method reproduce() in the Dog class returns a Dog object, which is a subtype of
Animal, making it a valid covariant return type. This provides more precise type control when
overriding methods.

422
The Complete Java Training Imran Afzal
Understanding method overloading and overriding is essential for implementing polymorphism,
reducing code duplication, and designing flexible object-oriented systems in Java.

8.23- Introduction to Text Block

Text blocks are a newer feature in Java, introduced officially in JDK 15, designed to simplify the
handling of multi-line string literals. A text block is a new syntax for writing strings that span multiple
lines, enhancing code readability and reducing the need for escape sequences and concatenation.

8.23.1- Traditional Multiline String Handling

Prior to JDK 15, handling multiline strings in Java required concatenation and escape sequences.

For Example:

String RightArrow = "Welcome to our:"+

"\U+279E Company"+

"\U+279E Nixware";

System.out.println(RightArrow);

This uses the + operator to join multiple string literals and includes escape sequences like \n (new
line), \t (tab), and Unicode characters like \u279E for a right arrow symbol.

While functional, this approach becomes hard to read and maintain as strings grow in complexity.

8.23.2- Using Text Blocks


Text blocks solve this problem by allowing multiline strings to be written directly in source code using
triple double quotes:

String text block = "Welcome to our:"+

"\U+279E Company"+

423
The Complete Java Training Imran Afzal
"\U+279E Nixware";

int number = 100;

System.out.printf("Your Number is: %d",number);

This provides an exact visual representation of the intended output. The syntax uses three double
quotes to open and close the string. The first triple quotes must appear on their own line. The content
between them is preserved as written, including whitespace, new lines, and special characters.

8.23.3- Benefits of Text Blocks

• Improved Readability: Code becomes easier to read and understand when large strings
match the output format.

• Reduced Code Noise: No need for escape sequences or concatenation symbols.

• Whitespace Preservation: Text blocks maintain exact indentation and formatting.

• Simplified Embedding: Ideal for embedding JSON, XML, HTML, or SQL into Java code.

• Cleaner Syntax: Helps keep source code visually aligned with output.

8.23.4- Limitations of Text Blocks

• Version Compatibility: Available only from Java 15 onward.

• Memory Usage: Can slightly increase memory usage due to multiline formatting.

• Performance: No notable runtime performance gain over regular strings.

• Learning Curve: New syntax may initially confuse developers unfamiliar with the feature.

• Formatting Restrictions: While it preserves indentation, it doesn’t provide fine-grained


formatting control beyond spacing and line breaks.

424
The Complete Java Training Imran Afzal
8.23.5- Formatting Output with printf and format

Besides System.out.println, Java offers System.out.printf and System.out.format for formatted output.
Both methods behave similarly, using format specifiers to control output formatting.

Example:

int number = 100;

System.out.printf("Your number is: %d", number);

In this case, %d is a format specifier for a decimal integer, and the value of number replaces it during
execution.

Another example using multiple variables:

int sum = number + 40;

System.out.printf("Number = %d, Sum = %d", number, sum);

Here, two format specifiers are used and must be matched with two arguments. This outputs: Number
= 100, Sum = 140.

8.23.6- Common Format Specifiers

• %d: Decimal integer (int, long, etc.)

• %f: Floating-point number (float, double)

• %n: Platform-independent line separator (preferred over \n for portability)

Java provides a comprehensive Formatter class that supports a wide range of formatting symbols and
options, including dates, times, and localized text.

Text blocks and formatted output in Java significantly enhance the ability to produce and manage
readable, maintainable, and well-structured textual data in code. Text blocks eliminate the clutter of

425
The Complete Java Training Imran Afzal
escape sequences, and formatting methods like printf allow for precise control over output structure.
These tools are particularly useful for working with structured text, generating templates, or creating
visually clean CLI outputs.

8.24- String Methods Part 1

In Java, strings are objects that represent sequences of characters, and the String class provides a wide
range of built-in methods for inspecting, manipulating, and comparing these sequences. These
methods are essential for performing common operations on text data.

Strings in Java are indexed starting from 0, which means the first character of a string has index 0, the
second character has index 1, and so on. For example, in the string "Hello World", the character 'H'
is at index 0 and 'W' is at index 6. The total length is 11, and the last index is 10.

String methods can be categorized into three main types:

• Inspection Methods – provide information about the string.


• Comparison Methods – compare values of strings.
• Manipulation Methods – change or transform the string’s content.

This part focuses on inspection and basic manipulation methods.

8.24.1- length() Method


Returns the number of characters in a string.

String str = "Hello, World!";

int length = str.length();

System.out.println("Length of the string: " + length);

426
The Complete Java Training Imran Afzal
Output:
Length of the string: 13

8.24.2- charAt(int index) Method


Returns the character at the specified index.

String str = "Hello, World!";

char firstChar = str.charAt(0);

char fifthChar = str.charAt(4);

char lastChar = str.charAt(str.length() - 1);

System.out.println("The first character is " + firstChar);

System.out.println("The fifth character is " + fifthChar);

System.out.println("The last character is " + lastChar);

Output:
The first character is H
The fifth character is o
The last character is !

8.24.3- substring(int beginIndex, int endIndex) Method


String str = "Hello, World!";

String sub1 = str.substring(0, 5);

String sub2 = str.substring(7);

427
The Complete Java Training Imran Afzal
String sub3 = str.substring(7, 12);

System.out.println("Sub1: " + sub1);

System.out.println("Sub2: " + sub2);

System.out.println("Sub3: " + sub3);

Output:
Sub1: Hello
Sub2: World!
Sub3: World

8.24.4- toLowerCase() Method


Converts all characters to lowercase.

String str = "Hello, World!";

String lowerCaseStr = str.toLowerCase();

System.out.println("Original string: " + str);

System.out.println("Lowercase string: " + lowerCaseStr);

Output:
Original string: Hello, World!
Lowercase string: hello, world!

428
The Complete Java Training Imran Afzal
8.24.5- toUpperCase() Method
Converts all characters to uppercase.

String str = "Hello, World!";

String upperCaseStr = str.toUpperCase();

System.out.println("Original string: " + str);

System.out.println("Uppercase string: " + upperCaseStr);

Output:
Original string: Hello, World!
Uppercase string: HELLO, WORLD!

8.24.6- trim() Method


Removes whitespace from the beginning and end of a string.

String str = " Hello, World! ";

String trimmedStr = str.trim();

System.out.println("Original string: \"" + str + "\"");

System.out.println("Trimmed string: \"" + trimmedStr + "\"");

Output:
Original string: ' Hello, World! '
Trimmed string: 'Hello, World!'

429
The Complete Java Training Imran Afzal
8.24.7- indexOf() Method
Returns the index of the first occurrence of a character or substring.

String str = "Hello, World!";

int index1 = str.indexOf('o');

int index2 = str.indexOf("World");

System.out.println("Index of 'o': " + index1);

System.out.println("Index of \"World\": " + index2);

Output:
Index of o: 4
Index of World: 7

8.24.8- replace() Method


Replaces occurrences of a character or substring with another.

String str = "Hello, World!";

String replacedStr = str.replace('o', '0');

System.out.println("Original string: " + str);

System.out.println("Replaced string: " + replacedStr);

430
The Complete Java Training Imran Afzal
Output:
Original string: Hello, World!
Replaced string: Hell0, W0rld!

8.24.9- isEmpty() Method


Checks if the string is empty (i.e., has zero characters).

String str1 = "";

String str2 = "Hello";

if (str1.isEmpty()) {

System.out.println("String 1 is empty.");

} else {

System.out.println("String 1 is not empty.");

if (str2.isEmpty()) {

System.out.println("String 2 is empty.");

} else {

System.out.println("String 2 is not empty.");

}
431
The Complete Java Training Imran Afzal
Output:
String 1 is empty: true
String 2 is empty: false

8.24.10- isBlank() Method


Checks if the string is blank (i.e., contains only whitespace or is empty).

String str1 = "";

String str2 = " ";

String str3 = "Hello";

if (str1.isBlank()) {

System.out.println("String 1 is blank.");

} else {

System.out.println("String 1 is not blank.");

if (str2.isBlank()) {

System.out.println("String 2 is blank.");

} else {

System.out.println("String 2 is not blank.");


432
The Complete Java Training Imran Afzal
}

if (str3.isBlank()) {

System.out.println("String 3 is blank.");

} else {

System.out.println("String 3 is not blank.");

Output:
String 1 is blank: true
String 2 is blank: true
String 3 is blank: false

These methods form the foundation for working with strings in Java. They help in tasks such as
validating input, formatting output, parsing data, and modifying textual content.

8.25- String Methods Part 2

8.25.1- String Comparison Methods

Java provides several methods to compare strings for equality or order. These methods are useful for
determining if two strings have the same content, or if one string comes before or after another
lexicographically.

equals() Method

The equals() method compares two strings for exact equality. It returns true if the character sequences
in both strings match exactly, otherwise it returns false.

433
The Complete Java Training Imran Afzal
Example

String str1 = "hello";

String str2 = "hello";

String str3 = "world";

if (str1.equals(str2)) {

System.out.println("str1 and str2 are equal.");

if (str1.equals(str3)) {

System.out.println("str1 and str3 are equal.");

} else {

System.out.println("str1 and str3 are not equal.");

str1 and str2 are equal.

str1 and str3 are not equal.

equalsIgnoreCase() Method

The equalsIgnoreCase() method compares two strings for equality while ignoring case differences. It
returns true if the strings are equal when case is not considered.

Example

String str1 = "hello";

String str2 = "Hello";


434
The Complete Java Training Imran Afzal
System.out.println(str1.equalsIgnoreCase(str2)); // true

contentEquals() Method

The contentEquals() method compares the content of a StringBuffer or StringBuilder with a String or
any CharSequence. It returns true if the contents match, otherwise false.

Note: StringBuffer and StringBuilder will be discussed in later sections.

compareTo() Method

The compareTo() method compares two strings lexicographically and returns:

• 0 if both strings are equal

• A negative value if the first string is lexicographically less than the second

• A positive value if the first string is lexicographically greater than the second

Example:

String str1 = "apple";

String str2 = "banana";

int result = str1.compareTo(str2);

if (result == 0) {

System.out.println("The strings are equal.");

} else if (result < 0) {

System.out.println("The first string is lexicographically less than the second string.");

435
The Complete Java Training Imran Afzal
} else {

System.out.println("The first string is lexicographically greater than the second string.");

The first string is lexicographically less than the second string.

8.25.2- String Manipulation Methods

String manipulation methods are grouped into two categories:

• Methods that clean or format text without changing its actual meaning.
• Methods that transform or modify the content of the string.

8.25.3- Case and Whitespace Management

• trim(): Removes leading and trailing whitespace.

• strip(): Similar to trim(), but supports Unicode.

• indent(): Adds or removes spaces for indenting.

• toLowerCase(): Converts all characters to lowercase.

• toUpperCase(): Converts all characters to uppercase.

8.25.4- concat() Method

The concat() method joins one string to the end of another and returns a new string.

Example

String str1 = "Hello";

String str2 = "world";

436
The Complete Java Training Imran Afzal
String str3 = str1.concat(str2);

System.out.println(str3); // Output: HelloWorld

String str1 = "Hello";

String str2 = "world";

String str3 = str1 + str2;

System.out.println(str3); // Output: HelloWorld

The + operator can also be used to concatenate strings, but concat() is more efficient in some
scenarios.

8.25.5- join() Method

The join() method combines multiple strings into one, using a specified delimiter.

Example

String joined = String.join("-", "Java", "is", "fun");

// Result: "Java-is-fun"

Note: Arrays and lists will be discussed in upcoming lectures.

437
The Complete Java Training Imran Afzal
8.25.6- repeat() Method

The repeat() method creates a new string by repeating the current string a specified number of times.
It was introduced in Java 11.

Example

String str = "abc";

String repeated = str.repeat(3);

System.out.println(repeated); // Output: abcabcabc

For earlier Java versions, similar behavior can be achieved using loops or StringBuilder.

8.25.7- replace() Method

The replace() method substitutes all occurrences of a character or substring with another character or
substring.

String str = "Hello, world!";

String newStr = str.replace(",", ";");

System.out.println(newStr); // Output: Hello; world!

String str = "the quick brown fox jumps over the lazy dog";

438
The Complete Java Training Imran Afzal
String newStr = str.replace("fox", "cat");

System.out.println(newStr); // Output: the quick brown cat jumps over the lazy dog

8.25.8- replaceAll() Method

The replaceAll() method replaces all matches of a regular expression with a specified replacement.

Example

String str = "Hello, world!";

String newStr = str.replaceAll("[aeiou]", "");

System.out.println(newStr); // Output: Hll, wrld!

8.25.9- replaceFirst() Method

The replaceFirst() method replaces only the first match of a regular expression.

Example

String str = "Hello, world!";

String newStr = str.replaceFirst("[aeiou]", "");

System.out.println(newStr); // Output: Hllo, world!

8.25.10- subSequence() Method

The subSequence() method returns a subsequence from the string. It takes two arguments: startIndex
and endIndex, and returns characters from startIndex to endIndex - 1.

439
The Complete Java Training Imran Afzal
Example

String str = "Hello, world!";

CharSequence subStr = str.subSequence(0, 5);

System.out.println(subStr); // Output: Hello

8.26- String and String Builder Class

8.26.1- Comparison Between String and StringBuilder

Both String and StringBuilder are classes in Java used to represent sequences of characters. Despite
this similarity, they differ in several important aspects:

8.26.1- Immutability

• String objects are immutable. Once created, their value cannot be changed. Any modification
operation on a String results in the creation of a new String object.

• StringBuilder objects are mutable. Their contents can be modified directly without creating a
new object.

8.26.2- Performance

• Because strings are immutable, repeated modifications (such as concatenation in a loop) can
result in multiple intermediate objects, which is inefficient.

• StringBuilder is more efficient for operations that involve frequent modifications to the
character sequence, as it modifies the same object repeatedly.

Java provides the StringBuilder class to allow text values to be updated or extended without creating
new objects. While strings are usually created using literals or by concatenation, StringBuilder offers
four overloaded constructors to create new objects:

440
The Complete Java Training Imran Afzal
1. No arguments (default)

2. A String as an argument

3. An integer indicating initial capacity

4. Another character sequence

8.26.3- Example: Comparing String and StringBuilder

To demonstrate the differences, consider an example using method overloading to accept both String
and StringBuilder parameters.

Overloaded Methods

public static void printInfo(String myString){

System.out.println("String = " + myString);

System.out.println("Length = " + myString.length());

public static void printInfo(StringBuilder myBuilder){

System.out.println("StringBuilder = " + myBuilder);

System.out.println("Length = " + myBuilder.length());

441
The Complete Java Training Imran Afzal
These methods display the value and length of the input, utilizing the length() method available in
both classes.

Creating Objects and Calling Methods

public class Tess {

public static void main(String[] args){

String message = "I love " + "programming";

message.concat(" in Java");

StringBuilder messageBuilder = new StringBuilder("I love " + "programming");

messageBuilder.append(" in Java");

printInfo(message);

printInfo(messageBuilder);

Directly assigning a string literal to a StringBuilder variable does not compile. Instead, a StringBuilder
object must be created using one of its constructors.

Mutability Demonstration

Using concat with String

message.concat(" in Java");

442
The Complete Java Training Imran Afzal
This does not change the original message string. A new string is created, but since the result is not
stored in a variable, it is discarded.

Using append with StringBuilder

messageBuilder.append(" in Java");

This modifies the original StringBuilder object directly. Its length increases and the new content is
visible without reassigning.

Internal Behavior

When using concat on a string, memory is allocated for:

• The original string "I love programming"

• The additional string " in Java"

• A new string resulting from the concatenation

However, the message variable still refers to the original object, unless the result is explicitly stored in
a new variable.

In contrast, StringBuilder holds only one object. After calling append, the same object is updated with
the new content. No additional reference assignment is required. This illustrates the mutability and
efficiency of StringBuilder.

Method Chaining with StringBuilder

StringBuilder methods return a reference to the same object, allowing method chaining:

messageBuilder.append(" in Java").append(" with OOP").append(" principles");

This ability is due to the self-reference returned by StringBuilder methods, which simplifies complex
string operations without intermediate variables.

443
The Complete Java Training Imran Afzal
8.27- Inheritance Section Summary

8.27.1- Classes and Objects

A class is a blueprint or template for creating objects. It defines the properties (fields) and behaviors
(methods) that the objects of that class will have. A class encapsulates data and the functions related
to that data, making it a user-defined data type.

An object is an instance of a class. It represents a specific realization of the class's blueprint and
contains its own state and behavior. For example, the Car class may define general properties like
color, make, and model, while an object of that class will have specific values for those properties.

Classes and objects help in organizing code, promoting abstraction and encapsulation, and building
reusable and maintainable applications.

8.27.2- Encapsulation

Encapsulation is the practice of bundling data and the methods that operate on that data within a
single unit, typically a class and restricting access to some of the object's components.

Benefits of encapsulation:

• Information hiding: Internal details are hidden from the outside world.

• Modularity: Objects become self-contained, making code easier to test and reuse.

• Flexibility: Internal implementations can change without affecting external code.

• Security: Prevents external code from modifying internal data directly.

8.27.3- Getters and Setters

Getters (accessor methods) and Setters (mutator methods) allow controlled access to private fields
in a class.

• A getter returns the value of a private field.

• A setter sets or updates the value of a private field.

444
The Complete Java Training Imran Afzal
Using getters and setters supports encapsulation by hiding internal implementation details while still
allowing access and updates through controlled methods.

8.27.4- Constructors

A constructor is a special method used to initialize objects. It has the same name as the class and no
return type.

There are two main types:

• Default constructor: Takes no parameters and sets default values.

• Parameterized constructor: Takes arguments to initialize fields with specific values.

Constructors ensure that an object starts in a valid state. Java provides a default constructor if none is
defined. If any constructor is explicitly defined, the default is not automatically generated.

8.27.5- Reference, Object, and Instance

• A reference is a variable that holds the memory address of an object.

• An object is an actual entity created from a class.

• An instance is a specific occurrence of a class, created at runtime.

When an object is created using new, it is stored in memory, and a reference is returned and stored in
a variable. This reference is used to access the object's members.

8.27.6- Static and Instance Variables and Methods

• A static variable is shared among all instances of a class. It belongs to the class itself.

• An instance variable is unique to each object of the class.

• A static method belongs to the class and can be called using the class name. It cannot access
instance variables or methods directly.

• An instance method belongs to an object and can access the object's data.

445
The Complete Java Training Imran Afzal
Example

A bank class may have a static variable for interestRate (shared by all accounts), while each account
object has its own balance as an instance variable.

8.27.7- Exception Handling

Exception handling allows a program to deal with unexpected runtime errors gracefully.

• An exception is an event that disrupts normal program flow (e.g., divide by zero, file not
found).

• In Java, exceptions are objects of the Exception class or its subclasses.

Java uses try-catch blocks to handle exceptions:

• Code that might throw an exception is placed in a try block.

• Code that handles the exception is placed in a catch block.

Multiple catch blocks can handle different exception types.

8.27.8- Inheritance

Inheritance allows one class (subclass) to inherit fields and methods from another class (superclass).

• The subclass is a specialized version of the superclass.

• Java uses the extends keyword to implement inheritance.

Example

void eat() {

System.out.println("Animal is eating.");

446
The Complete Java Training Imran Afzal
}

class Dog extends Animal {

void bark() {

System.out.println("Dog is barking.");

8.27.9- Benefits of Inheritance

• Code reuse: Common functionality can be shared across classes.

• Easy maintenance: Changes in the superclass propagate to subclasses.

• Encapsulation: Access to protected and public members, while private members remain
hidden.

• Modeling real-world relationships: Supports hierarchical class structures that reflect real-
world entities.

8.27.10- Method Overloading and Overriding

Method Overloading

Method overloading allows multiple methods in a class to have the same name but different
parameters (type, number, or order).

Example

public class Calculator {

447
The Complete Java Training Imran Afzal
public int addNumbers(int num1, int num2) {

return num1 + num2;

public double addNumbers(double num1, double num2) {

return num1 + num2;

The compiler distinguishes overloaded methods using the method signature.

Method Overriding

Method overriding allows a subclass to provide a specific implementation of a method defined in its
superclass.

To override a method:

• The method in the subclass must have the same name, return type, and parameters as the
superclass method.

Example

public class Animal {

public void makeSound() {

System.out.println("The animal makes a sound");

448
The Complete Java Training Imran Afzal
}

public class Dog extends Animal {

@Override

public void makeSound() {

System.out.println("The dog barks");

When makeSound() is called on a Dog object, the overridden version is executed. Overriding supports
dynamic method dispatch and runtime polymorphism.

449
The Complete Java Training Imran Afzal
Chapter 9

Pillars of OOP (Composition,


Encapsulation & Polymorphism)

450
The Complete Java Training Imran Afzal
9.1- Introduction to Composition

9.1.1- Understanding Composition in Java

Composition is a fundamental concept in object-oriented programming that involves building


complex types by combining objects of other types. In Java, this is done by including one or more
objects inside another class, establishing a HAS-A relationship.

To grasp the idea of composition, it helps to compare it with inheritance. Inheritance represents an
IS-A relationship. For example, a house is a type of building—this is inheritance. Conversely, if we
say a house has a kitchen, we describe a HAS-A relationship, which is composition.

Another example involves cars. If Honda is a car, it means Honda and Car have an IS-A relationship.
However, saying Honda has an engine implies a HAS-A relationship, illustrating the concept of
composition.

9.1.2- Inheritance and Composition in Practice

To understand these concepts programmatically, consider creating a class Person that includes fields
for name and address. We will then derive two subclasses, Student and Professor, from Person. Each
subclass introduces additional properties—rollNumber for students and salary for professors. The
address will be modeled as a separate class and included within the Person class to demonstrate
composition.

9.1.3- Creating the Project Structure

Create a new Java project named Composition. Inside this project, create the following class files:

1. Person Class

public class Person {

private String name;

private String street;

451
The Complete Java Training Imran Afzal
private String city;

private String state;

public Person(String name, String street, String city, String state) {

this.name = name;

this.street = street;

this.city = city;

this.state = state;

public String getName() {

return name;

public String getStreet() {

return street;

452
The Complete Java Training Imran Afzal
The Person class contains a name and an Address object. Instead of including address-related fields
directly, we encapsulate them in a separate class, forming a HAS-A relationship.

2. Address Class

public class Address {

private String city;

private String state;

public Address(String city, String state) {

this.city = city;

this.state = state;

public String getCity() {

return city;

public String getState() {

return state;

453
The Complete Java Training Imran Afzal
}

The Address class encapsulates details about the address and provides a constructor and getter
methods to access its data.

3. Student Class

class Student extends Person{

private String rollNumber;

public Student(String name, String street, String city, String state, String rollNumber) {

super(name, street, city, state);

this.rollNumber = rollNumber;

public String getRollNumber() {

return rollNumber;

454
The Complete Java Training Imran Afzal
The Student class extends Person, inheriting its fields and methods, and introduces a new field,
rollNumber. The constructor uses super to call the parent constructor.

4. Professor Class

class Professor extends Person{

private String salary;

public Professor(String name, String street, String city, String state, String salary) {

super(name, street, city, state);

this.salary = salary;

public String getSalary() {

return salary;

Similarly, the Professor class extends Person and adds a unique salary field.

5. Main Class

public class MainClass {

455
The Complete Java Training Imran Afzal
public static void main(String[] args) {

Student student = new Student("John Doe", "5th Ave 32nd floor", "New York",

"United States","A201");

System.out.println("Student RollNo: " + student.getRollNumber() + "\nName : "

+ student.getName() + "\nCity: " + student.getCity() + "\nStreet: "

+ student.getStreet() + "\nState: " + student.getState());

System.out.println("---------------");

Professor professor = new Professor("John Doe", "5th Ave 32nd floor", "New York",

"Amercia","10000$");

System.out.println("Professor Salary: " + professor.getSalary() + "\nName : "

+ professor.getName() + "\nCity: " + professor.getCity() + "\nStreet: "

+ professor.getStreet() + "\nState: " + professor.getState());

456
The Complete Java Training Imran Afzal
In the MainClass, two objects of type Address are created and passed to the Student and Professor
constructors. This demonstrates the use of composition where both subclasses have a HAS-A
relationship with the Address class via inheritance from Person.

This structure shows the power of combining inheritance and composition. Inheritance establishes an
IS-A relationship, while composition models the HAS-A relationship, promoting modular, reusable,
and maintainable code design.

9.2- Composition Exercise Part 1

9.2.1- Implementing Composition through a Room Setup Example

This exercise demonstrates composition by modeling a bedroom using multiple related classes. Each
part of the bedroom like lamp, bed, ceiling, and walls is represented by its own class. These parts are
then composed together inside the Bedroom class to form a complete unit.

Lamp Class

class Lamp {

private String style;

private boolean battery;

private int globRating;

public Lamp(String style, boolean battery, int globRating) {

this.style = style;

this.battery = battery;

this.globRating = globRating;

457
The Complete Java Training Imran Afzal
}

public void turnOn() {

System.out.println("The lamp is being turned on.");

public String getStyle() {

return style;

public boolean isBattery() {

return battery;

public int getGlobRating() {

return globRating;

458
The Complete Java Training Imran Afzal
}

The Lamp class models a lamp with style, power source, and brightness rating. It includes behavior
for turning on the lamp and methods to access its attributes.

Bed Class

class Bed {

private String style;

private int pillows;

private int height;

private int sheets;

private int quilt;

public Bed(String style, int pillows, int height, int sheets, int quilt) {

this.style = style;

this.pillows = pillows;

this.height = height;

this.sheets = sheets;

this.quilt = quilt;

459
The Complete Java Training Imran Afzal
public void make() {

System.out.println("The bed is being made.");

public String getStyle() {

return style;

public int getPillows() {

return pillows;

public int getHeight() {

return height;

460
The Complete Java Training Imran Afzal
public int getSheets() {

return sheets;

public int getQuilt() {

return quilt;

The Bed class includes physical details of a bed and a method to simulate making the bed.

Ceiling Class

class Ceiling {

private int height;

private int paintedColor;

public Ceiling(int height, int paintedColor) {

this.height = height;

this.paintedColor = paintedColor;

461
The Complete Java Training Imran Afzal
}

public int getHeight() {

return height;

public int getPaintedColor() {

return paintedColor;

The Ceiling class provides attributes like height and painted color.

Wall Class

class Wall {

private String direction;

public Wall(String direction) {

this.direction = direction;

462
The Complete Java Training Imran Afzal
}

public String getDirection() {

return direction;

The Wall class models a wall in a specific direction.

Bedroom Class

class Bedroom {

private String name;

private Wall wall1;

private Wall wall2;

private Wall wall3;

private Wall wall4;

private Ceiling ceiling;

private Bed bed;

private Lamp lamp;

463
The Complete Java Training Imran Afzal
public Bedroom(String name, Wall wall1, Wall wall2, Wall wall3, Wall wall4,

Ceiling ceiling, Bed bed, Lamp lamp) {

this.name = name;

this.wall1 = wall1;

this.wall2 = wall2;

this.wall3 = wall3;

this.wall4 = wall4;

this.ceiling = ceiling;

this.bed = bed;

this.lamp = lamp;

public Lamp getLamp() {

return lamp;

464
The Complete Java Training Imran Afzal
public void makeBed() {

System.out.println("The bed is being made.");

bed.make();

The Bedroom class brings all components together. It includes methods to access the lamp and make
the bed by calling respective methods from composed objects.

Main Class

public class Main {

public static void main(String[] args) {

// Create a Lamp object

Lamp lamp = new Lamp("modern", true, 5);

// Turn the lamp on

lamp.turnOn();

// Get the style of the lamp

System.out.println("Lamp style: " + lamp.getStyle());

System.out.println("Lamp has battery: " + lamp.isBattery());

System.out.println("Lamp glob rating: " + lamp.getGlobRating());

System.out.println("------------");

465
The Complete Java Training Imran Afzal
// Create a Bed object

Bed bed = new Bed("rustic", 2, 60, 2, 1);

// Make the bed

bed.make();

// Get the style of the bed

System.out.println("Bed style: " + bed.getStyle());

System.out.println("Number of pillows on the bed: " + bed.getPillows());

System.out.println("Bed height: " + bed.getHeight());

System.out.println("Number of sheets on the bed: " + bed.getSheets());

System.out.println("Bed quilt value: " + bed.getQuilt());

// Create a Ceiling object

Ceiling ceiling = new Ceiling(100, 2);

// Get the height of the ceiling

System.out.println("Ceiling height: " + ceiling.getHeight());

System.out.println("Ceiling painted color: " + ceiling.getPaintedColor());

// Create a Wall object

Wall wall = new Wall("north");

466
The Complete Java Training Imran Afzal
// Get the direction of the wall

System.out.println("Wall direction: " + wall.getDirection());

// Create a Bedroom object

Bedroom bedroom = new Bedroom("Master Bedroom", new Wall("north"), new Wall("south"),

new Wall("east"), new Wall("west"), new Ceiling(100, 2), new Bed("rustic", 2, 60, 2, 1),

new Lamp("modern", true, 5));

Lamp bedroomLamp = bedroom.getLamp();

bedroomLamp.turnOn();

bedroom.makeBed();

9.2.2- Demonstrating Composition

The Bedroom class clearly illustrates composition by including instances of Wall, Ceiling, Bed, and
Lamp. Each object contributes to forming a complete Bedroom object.

There are no inheritance (IS-A) relationships in this program. All class relationships are HAS-A,
forming a clean example of object composition in Java.

9.3- Composition Exercise Part 2

9.3.1- Modeling Hierarchical and Compositional Relationships

This program demonstrates both IS-A (inheritance) and HAS-A (composition) relationships using a
set of interrelated classes: Bird, Penguin, Beak, Tongue, and MuscularOrgan.

A Penguin is-a Bird, which has-a Beak, which has-a Tongue, and Tongue is-a Muscular Organ.

467
The Complete Java Training Imran Afzal
MuscularOrgan Class

// Define the MuscularOrgan class as the superclass

class MuscularOrgan {

private String name;

MuscularOrgan(String name) {

this.name = name;

public String getName() {

return name;

The MuscularOrgan class models generic muscular body parts and contains a single attribute name.

Tongue Class

// Define the Tongue class as a subclass of MuscularOrgan

class Tongue extends MuscularOrgan {

Tongue(String name) {

468
The Complete Java Training Imran Afzal
super(name);

Tongue is a subclass of MuscularOrgan, inheriting its properties and behaviors without adding
anything extra for now.

Beak Class

// Define the Beak class as a component of Bird

class Beak {

private Tongue tongue;

Beak(Tongue tongue) {

this.tongue = tongue;

public Tongue getTongue() {

return tongue;

469
The Complete Java Training Imran Afzal
Beak is a composite class that includes a Tongue object, representing a HAS-A relationship.

Bird Class

// Define the Bird class as the superclass

class Bird {

private String name;

private int age;

Bird(String name, int age) {

this.name = name;

this.age = age;

public String getName() {

return name;

public int getAge() {

470
The Complete Java Training Imran Afzal
return age;

Bird is the base class with common properties such as name and age.

Penguin Class

class Penguin extends Bird {

private Beak beak;

Penguin(String name, int age, Beak beak) {

super(name, age);

this.beak = beak;

public void swim() {

System.out.println(getName() + " is swimming...");

471
The Complete Java Training Imran Afzal
public Beak getBeak() {

return beak;

Penguin extends Bird (IS-A relationship) and includes a Beak object (HAS-A relationship). The swim
method represents specific behavior for Penguin.

TestBird (Main Class)

This class ties everything together. It demonstrates object creation and composition, followed by
method calls to display information.

// Main class to test the relationships

public class TestBird {

public static void main(String[] args) {

Tongue tongue = new Tongue("Tongue");

Beak beak = new Beak(tongue);

Penguin penguin = new Penguin("Penguin", 3, beak);

472
The Complete Java Training Imran Afzal
penguin.swim();

System.out.println(penguin.getName() + " is " + penguin.getAge() + " years old.");

System.out.println(penguin.getName() + " has a " + penguin.getBeak().getTongue().getName()


+ " tongue.");

9.3.2- Relationship Overview

• Penguin is-a Bird → Inheritance

• Penguin has-a Beak → Composition

• Beak has-a Tongue → Composition

• Tongue is-a MuscularOrgan → Inheritance

The output of the program confirms the relationships:

• Penguin is swimming

• Penguin is 3 years old

• Penguin has a Tongue tongue

This program effectively models a real-world biological structure using object-oriented principles. It
demonstrates both inheritance and composition in a clear hierarchy. The structure shows how
complex objects can be built from simpler, reusable components.

473
The Complete Java Training Imran Afzal
9.4- Composition Exercise Part 3

9.4.1- Modeling a Car Engine System Using Inheritance and Composition

This program demonstrates both IS-A (inheritance) and HAS-A (composition) relationships using
the classes: Car, Engine, Piston, ConnectingRod, and Crankshaft.

A Car is-a Engine, and an Engine has-a Piston, Connecting Rod, and Crankshaft.

Piston Class

// Piston.java

public class Piston {

private String material;

public Piston(String material) {

this.material = material;

public String getMaterial() {

return material;

474
The Complete Java Training Imran Afzal
The Piston class models a piston component with a specific material.

ConnectingRod Class

public class ConnectingRod {

private String type;

public ConnectingRod(String type) {

this.type = type;

public String getType() {

return type;

The ConnectingRod class stores the type of the rod used in the engine.

Crankshaft Class

public class Crankshaft {

private String manufacturer;

475
The Complete Java Training Imran Afzal
public Crankshaft(String manufacturer) {

this.manufacturer = manufacturer;

public String getManufacturer() {

return manufacturer;

The Crankshaft class holds the manufacturer's name of the crankshaft component.

Engine Class

// Engine.java

public class Engine {

private int horsepower;

private Piston piston;

private ConnectingRod rod;

private Crankshaft shaft;

476
The Complete Java Training Imran Afzal
public Engine(int horsepower, Piston piston, ConnectingRod rod, Crankshaft shaft) {

this.horsepower = horsepower;

this.piston = piston;

this.rod = rod;

this.shaft = shaft;

public int getHorsepower() {

return horsepower;

public Piston getPiston() {

return piston;

public ConnectingRod getRod() {

return rod;

477
The Complete Java Training Imran Afzal
}

public Crankshaft getCrankshaft() {

return shaft;

The Engine class encapsulates the functionality and properties of an engine by using composition with
Piston, ConnectingRod, and Crankshaft objects.

Car Class

public class Car extends Engine {

private String model;

public Car(String model, int horsepower, Piston piston, ConnectingRod rod, Crankshaft shaft) {

super(horsepower, piston, rod, shaft);

this.model = model;

public String getModel() {

478
The Complete Java Training Imran Afzal
return model;

The Car class extends Engine, establishing an IS-A relationship. It also introduces its own attribute:
model.

Main Class

public class MainClass {

public static void main(String[] args) {

Piston piston = new Piston("Aluminum");

ConnectingRod rod = new ConnectingRod("H-Beam");

Crankshaft shaft = new Crankshaft("Eagle");

Car car = new Car("Tesla Model S", 500, piston, rod, shaft);

System.out.println("Car model: " + car.getModel());

System.out.println("Engine horsepower: " + car.getHorsepower());

System.out.println("Piston material: " + car.getPiston().getMaterial());

System.out.println("Connecting rod type: " + car.getRod().getType());

479
The Complete Java Training Imran Afzal
System.out.println("Crankshaft manufacturer: " + car.getCrankshaft().getManufacturer());

This class creates all components of the engine and combines them into a Car object. Each attribute
is accessed using getter methods, showcasing the relationships between the objects.

9.4.2- Relationship Summary

• Car is-a Engine → Inheritance

• Engine has-a Piston → Composition

• Engine has-a ConnectingRod → Composition

• Engine has-a Crankshaft → Composition

Output

• Car model: Tesla Model S

• Engine horsepower: 500

• Piston material: Aluminum

• Connecting rod type: H-Beam

• Crankshaft manufacturer: Eagle

This program effectively demonstrates the design of a modular car engine system using object-
oriented programming concepts like inheritance and composition.

9.5- Introduction to Encapsulation

9.5.1- Understanding Encapsulation

Encapsulation is the principle of restricting direct access to some of an object's components and only
allowing access through public methods. In Java, encapsulation is implemented using the private

480
The Complete Java Training Imran Afzal
access modifier to hide fields, and public getter and setter methods to provide controlled access to
those fields.

Encapsulation is used to:

• Simplify class interfaces by hiding unnecessary details

• Protect the integrity of object data

• Allow internal implementation to change without affecting external code

• Ensure only valid data is accepted by an object

The public members of a class form its interface, often referred to as its API (Application
Programming Interface), which defines how other classes should interact with it. Internal details, such
as the names or types of fields, are kept private and hidden from the outside.

9.5.2- Non-Encapsulated Example: MarksCalculation

public class MarksCalculation {

public int mathMarks;

public int scienceMarks;

public int percentage;

public String grade;

public void calculatePercentage() {

percentage = ((mathMarks + scienceMarks) / 200.0f) * 100.0f;

public void calculateGrade() {


481
The Complete Java Training Imran Afzal
if (percentage >= 90 && percentage <= 100) {

grade = "A";

} else if (percentage >= 80 && percentage <= 89) {

grade = "B";

} else if (percentage >= 70 && percentage <= 79) {

grade = "C";

} else if (percentage >= 60 && percentage <= 69) {

grade = "D";

} else {

grade = "F";

9.5.3- Usage in Main Class

public class MainClass {

public static void main(String[] args) {

MarksCalculation calculation = new MarksCalculation();

482
The Complete Java Training Imran Afzal
calculation.mathMarks = 80;

calculation.scienceMarks = 90;

calculation.calculatePercentage();

calculation.calculateGrade();

calculation.mathMarks = 150;

calculation.scienceMarks = 190;

System.out.println("Percentage: " + calculation.percentage);

System.out.println("Grade: " + calculation.grade);

This implementation exposes all the fields as public, allowing external code to modify data directly.
This can bypass validation, lead to incorrect calculations, and make the code harder to maintain.

For example, if field names are changed, external code must also be updated. Without encapsulation,
there’s no control over the order or validity of data initialization.

483
The Complete Java Training Imran Afzal
9.5.4- Improved Example with Encapsulation

public class EncapsulateCalculation {

private int mathMarks;

private int scienceMarks;

private float percentage;

private String grade;

public EncapsulateCalculation(int mathMarks, int scienceMarks) {

if(mathMarks < 0){

this.mathMarks = 0;

} else if (scienceMarks < 0) {

this.scienceMarks = 0;

} else if (mathMarks > 100) {

this.mathMarks = 100;

} else if (scienceMarks > 100) {

this.scienceMarks = 100;

} else{

484
The Complete Java Training Imran Afzal
this.scienceMarks = scienceMarks;

this.mathMarks = mathMarks;

private float calculatePercentage() {

return ((this.mathMarks + this.scienceMarks) / 200.0f) * 100.0f;

public String calculateGrade() {

this.percentage = calculatePercentage();

if (this.percentage >= 90 && this.percentage <= 100) {

this.grade = "A";

} else if (this.percentage >= 80 && this.percentage <= 89) {

this.grade = "B";

} else if (this.percentage >= 70 && this.percentage <= 79) {

this.grade = "C";

485
The Complete Java Training Imran Afzal
} else if (this.percentage >= 60 && this.percentage <= 69) {

this.grade = "D";

} else {

this.grade = "F";

return "Percentage: " + this.percentage + "\nGrade: " + this.grade;

9.5.5- Usage in Main Class

public class MainClass {

public static void main(String[] args) {

EncapsulateCalculation ec = new EncapsulateCalculation(70,80);

String result = ec.calculateGrade();

System.out.println(result);

9.5.6- Key Benefits of Encapsulation

• Data Validation: Constructor validation ensures only valid scores are accepted.

486
The Complete Java Training Imran Afzal
• Restricted Access: Fields are private and cannot be accessed or modified directly.

• Safe Refactoring: Changing internal field names doesn’t affect external code.

• Controlled Behavior: Logic to calculate percentage and grade is internal and cannot be
bypassed.

• Cleaner Interfaces: Only necessary methods are exposed; everything else remains hidden.

9.5.7- Refactoring with Encapsulation

Using an IDE like IntelliJ, private fields can be safely renamed using refactor tools without affecting
other code. For instance, renaming mathScore to englishScore is contained within the class. No
external class is affected because direct access to the field was never exposed.

9.5.8- Encapsulation Best Practices

• Use private for fields.

• Use constructors to ensure valid object initialization.

• Provide only essential get or set methods.

• Avoid exposing internal class details directly.

Encapsulation enforces a clean separation between how an object is used and how it works internally.
It ensures that the internal state of an object is protected from unauthorized access and unintended
changes.

9.6- Encapsulation Exercise

9.6.1- Introduction to the Printer Class

The Printer class is designed to demonstrate the concept of encapsulation in Java. It includes three
private member variables:

• tonerLevel (int): represents the current toner level in the printer.

• pagesPrinted (int): keeps track of the total number of pages printed.

487
The Complete Java Training Imran Afzal
• duplex (boolean): indicates whether the printer supports double-sided printing.

These variables are private, meaning they cannot be accessed directly from outside the class. This
encapsulation ensures controlled access and modification, maintaining a consistent internal state.

9.6.2- Constructor for Initialization

The constructor accepts two parameters—tonerLevel and duplex. It follows these rules:

public class Printer {

private int tonerLevel;

private int pagesPrinted;

private boolean duplex;

public Printer(int tonerLevel, boolean duplex) {

if (tonerLevel >= 0 && tonerLevel <= 100) {

this.tonerLevel = tonerLevel;

} else {

this.tonerLevel = -1;

this.duplex = duplex;

488
The Complete Java Training Imran Afzal
this.pagesPrinted = 0;

• tonerLevel is validated to ensure it’s between 0 and 100 inclusive. If it isn’t, it’s set to -1.

• duplex is directly assigned.

• pagesPrinted is initialized to 0.

9.6.3- Method: addToner

This method adds toner to the printer, validating the input to avoid exceeding maximum capacity:

public int addToner(int tonerAmount) {

if (tonerAmount > 0 && tonerAmount <= 100) {

if (tonerLevel + tonerAmount > 100) {

return -1;

tonerLevel += tonerAmount;

return tonerLevel;

return -1;

• Validates tonerAmount is between 1 and 100.

489
The Complete Java Training Imran Afzal
• Ensures total toner does not exceed 100.

• Returns the updated toner level or -1 if the input is invalid.

9.6.4- Method: printPages

This method simulates printing and returns the number of pages actually printed:

public int printPages(int pages) {

int pagesToPrint = pages;

if (duplex) {

pagesToPrint = (int) Math.ceil(pages / 2.0);

pagesPrinted += pagesToPrint;

return pagesToPrint;

• If duplex is true, the pages are halved and rounded up.

• pagesPrinted is incremented by pagesToPrint.

• Returns the number of sheets used based on duplex capability.

9.6.5- Method: getPagesPrinted

Returns the total number of printed pages:

public int getPagesPrinted() {

490
The Complete Java Training Imran Afzal
return pagesPrinted;

This getter method allows external classes to check how many pages have been printed so far.

9.6.6- Testing the Printer Class

In the Main class, we test the functionality of the Printer class:

public class Main {

public static void main(String[] args) {

Printer myPrinter = new Printer(50, true);

System.out.println("Initial pages printed: " + myPrinter.getPagesPrinted());

int pagesToPrint = 10;

int pagesPrinted = myPrinter.printPages(pagesToPrint);

System.out.println("Pages printed: " + pagesPrinted);

System.out.println("Total pages printed: " + myPrinter.getPagesPrinted());

491
The Complete Java Training Imran Afzal
int tonerAdded = myPrinter.addToner(30);

System.out.println("Toner added. New toner level: " + tonerAdded);

pagesToPrint = 15;

pagesPrinted = myPrinter.printPages(pagesToPrint);

System.out.println("Pages printed: " + pagesPrinted);

System.out.println("Total pages printed: " + myPrinter.getPagesPrinted());

tonerAdded = myPrinter.addToner(100);

if (tonerAdded == -1) {

System.out.println("Invalid toner amount");

} else {

System.out.println("Toner added. New toner level: " + tonerAdded);

492
The Complete Java Training Imran Afzal
9.6.7- Explanation of Output

• Initial pages printed: 0, since no print jobs have been executed yet.

• Printing 10 pages: With duplex enabled, only 5 sheets are used.

• Toner addition: 30 units are added to an initial 50, resulting in a level of 80.

• Printing 15 pages: With duplex, 8 sheets are used, increasing total to 13.

• Invalid toner addition: Trying to add 100 units fails since it would exceed the maximum
level, resulting in an error message.

This encapsulation exercise illustrates how private fields and controlled access via public methods can
maintain object integrity and encapsulate behavior logically within a class.

9.7- Introduction to Polymorphism

9.7.1- Understanding Polymorphism in Java

Polymorphism in object-oriented programming allows different classes to respond to the same


method call in their own way. It enables one interface to be used for a general class of actions, while
the specific action is determined by the exact nature of the situation.

There are two common types of polymorphism:

• Method Overloading: Multiple methods in the same class with the same name but different
parameters.

• Method Overriding: A subclass provides its own implementation of a method declared in its
superclass.

This flexibility supports code extensibility and reusability, making it easier to maintain complex
applications.

9.7.2- Practical Use of Polymorphism


Polymorphism lets developers call a method on a parent class reference, and the appropriate

493
The Complete Java Training Imran Afzal
overridden method in the subclass will execute, based on the actual object type at runtime. This relies
on inheritance to establish the relationship between the declared type and the runtime type.

9.7.3- Creating the Fruit Class Hierarchy

A base class Fruit is created with a private field name and a method isRipe.

public class Fruit {

private String name;

public Fruit(String name) {

this.name = name;

public void isRipe(String check){

String instanceType = this.getClass().getSimpleName();

System.out.println(name + " is a " + instanceType + " and " + check);

public static Fruit getFruit(String type, String name){

return switch (type.toUpperCase().charAt(0)){

case 'A' -> new Apple(name);

case 'B' -> new Banana(name);

default -> new Fruit(name);

494
The Complete Java Training Imran Afzal
};

• getClass().getSimpleName() is used to get the actual class name at runtime.

• The isRipe method prints the name and type of the fruit along with a custom message.

9.7.4- Creating Subclasses: Apple and Banana

Apple class

class Apple extends Fruit{

public Apple(String name) {

super(name);

@Override

public void isRipe(String check) {

super.isRipe(check);

System.out.println(check + " Apple may lower your risk for cancer, diabetes, and heart
disease.");

public void EatApple(){

System.out.println("Eating this delicious Apple");

495
The Complete Java Training Imran Afzal
}

Banana class

class Banana extends Fruit{

public Banana(String name) {

super(name);

@Override

public void isRipe(String check) {

super.isRipe(check);

System.out.println(check + " Banana May support digestive health ");

public void EatBanana(){

System.out.println("Eating this delicious Banana");

• Both subclasses override the isRipe method.

• Each provides additional output specific to that fruit type.

496
The Complete Java Training Imran Afzal
• super.isRipe(check) calls the base class method first.

9.7.5- Using Polymorphism in Main Method

In the Main class:

public class MainClass {

public static void main(String[] args) {

Fruit fruit = Fruit.getFruit("Apple","Honey Crisp");

fruit.isRipe("Ripped");

// Scanner sc = new Scanner(System.in);

// while(true){

// System.out.print("Enter Type (A for Apple, B For Banana, or Q to quit): ");

// String type = sc.nextLine();

// if("Qq".contains(type)){

// break;

// }

// System.out.print("Enter the fruit Title: ");

// String name = sc.nextLine();

497
The Complete Java Training Imran Afzal
// Fruit fruit = Fruit.getFruit(type, name);

// fruit.isRipe("Ripped");

// }

Although the reference type is Fruit, the actual object is Apple. At runtime, the Apple version of isRipe
executes, showing polymorphism in action.

9.7.6- Implementing a Factory Method

To simplify object creation and support polymorphism, a static factory method was implemented in
the Fruit class earlier (see section 9.7.3). This method uses input to decide which subclass (Apple,
Banana, or Fruit) to instantiate.

// Refer to full implementation in section 9.7.3

public static Fruit getFruit(String type, String name)

This factory method:

• Returns a Fruit reference (enabling polymorphism)

• Allows switching behavior dynamically at runtime

• Hides subclass details from the client code

9.7.7- Interactive User Input with Scanner

Interactive input is added using Scanner:

498
The Complete Java Training Imran Afzal
import java.util.Scanner;

public class Main

public static void main(String[] args)

Scanner sc = new Scanner(System.in);

while (true)

System.out.print("Enter type (A for Apple, B for Banana, or Q to quit): ");

String type = sc.nextLine();

if ("Qq".contains(type))

break;

499
The Complete Java Training Imran Afzal
System.out.print("Enter the Fruit Name: ");

String name = sc.nextLine();

Fruit fruit = Fruit.getFruit(type, name);

fruit.isRipe("Ripe");

sc.close();

• Prompts the user for the fruit type and name.

• Uses the factory method to create the correct instance.

• Executes isRipe, which behaves according to the actual object type.

9.7.8 – Demonstrating Polymorphism, Casting, and Type Inference

public class MainClassTwo {

public static void main(String[] args) {

Fruit fruit = Fruit.getFruit("B", "Cavendish Banana");

500
The Complete Java Training Imran Afzal
fruit.isRipe("Ripped");

// Banana CavendishBanana = (Banana) Fruit.getFruit("A", "Cavendish Banana");

// CavendishBanana.isRipe("Ripped");

Object apple = Fruit.getFruit("A", "Granny Smith");

Apple appleFruit = (Apple) apple;

appleFruit.EatApple();

var honeycrisp = Fruit.getFruit("A","Honeycrisp");

honeycrisp.isRipe("Ripped");

var plantain = new Banana("plantain");

plantain.isRipe("Ripped");

This setup encapsulates object creation and behavioral differences within the class hierarchy, allowing
each object to respond appropriately at runtime through overridden methods. It simplifies logic in the
main program, promotes clean design through abstraction, and clearly demonstrates the flexibility and
power of polymorphism.
501
The Complete Java Training Imran Afzal
9.8- Polymorphism Exercise Part 1

9.8.1- Creating the Car Class and Its Members

• The base class Car contains four private member variables:

• engine (Boolean): Indicates if the car has an engine.

• cylinders (int): Represents the number of engine cylinders.

• name (String): Stores the name of the car.

• wheels (int): Represents the number of wheels on the car.

public class Car {

private boolean engine;

private int cylinders;

private String name;

private int wheels;

public Car(int cylinders, String name) {

this.engine = true;

this.cylinders = cylinders;

this.name = name;

this.wheels = 4;

502
The Complete Java Training Imran Afzal
public String startEngine() {

return "The " + name + " car's engine is starting.";

public String accelerate() {

return "The " + name + " car is accelerating.";

public String brake() {

return "The " + name + " car is braking.";

public int getCylinders() {

return cylinders;

503
The Complete Java Training Imran Afzal
public String getName() {

return name;

This class sets up the basic structure for a vehicle and includes methods that simulate common
behaviors like starting, accelerating, and braking. Getter methods are provided for accessing the
cylinders and name fields.

9.8.2- Creating the Mitsubishi Class

The Mitsubishi class is a subclass of Car and overrides three methods to provide behavior specific to
this car type.

class Mitsubishi extends Car {

public Mitsubishi(int cylinders, String name) {

super(cylinders, name);

@Override

public String startEngine() {

return "The " + getName() + " Mitsubishi's engine is starting.";

504
The Complete Java Training Imran Afzal
@Override

public String accelerate() {

return "The " + getName() + " Mitsubishi is accelerating.";

@Override

public String brake() {

return "The " + getName() + " Mitsubishi is braking.";

9.8.3- Creating the Holden Class

The Holden class extends Car and overrides the same three methods to personalize its behavior.

class Holden extends Car {

public Holden(int cylinders, String name) {

super(cylinders, name);

505
The Complete Java Training Imran Afzal
@Override

public String startEngine() {

return "The " + getName() + " Holden's engine is starting.";

@Override

public String accelerate() {

return "The " + getName() + " Holden is accelerating.";

@Override

public String brake() {

return "The " + getName() + " Holden is braking.";

506
The Complete Java Training Imran Afzal
9.8.4- Creating the Ford Class

The Ford class is another subclass of Car and customizes the same behaviors.

public class Ford extends Car {

public Ford(int cylinders, String name) {

super(cylinders, name);

@Override

public String startEngine() {

return "The " + getName() + " Ford's engine is starting.";

@Override

public String accelerate() {

return "The " + getName() + " Ford is accelerating.";

507
The Complete Java Training Imran Afzal
@Override

public String brake() {

return "The " + getName() + " Ford is braking.";

9.8.5- Testing the Classes in the Main Method

The main method demonstrates polymorphism by creating objects from different subclasses but
interacting with them through a common Car reference.

public class Ford extends Car {

public Ford(int cylinders, String name) {

super(cylinders, name);

@Override

public String startEngine() {

return "The " + getName() + " Ford's engine is starting.";

508
The Complete Java Training Imran Afzal
@Override

public String accelerate() {

return "The " + getName() + " Ford is accelerating.";

@Override

public String brake() {

return "The " + getName() + " Ford is braking.";

Output

The Generic Car's engine is starting.

The Generic Car is accelerating.

The Generic Car is braking.

-----

The Mitsubishi Car's engine is starting.

The Mitsubishi Car is accelerating.

The Mitsubishi Car is braking.

-----

509
The Complete Java Training Imran Afzal
The Holden Car's engine is starting.

The Holden Car is accelerating.

The Holden Car is braking.

-----

The Ford Mustang's engine is starting.

The Ford Mustang is accelerating.

The Ford Mustang is braking.

9.9- Polymorphism Exercise Part 2

9.9.1- Building the Hamburger Class

The Hamburger class defines the basic structure of a customizable burger.

public class Hamburger {

private String name;

private String meat;

private double price;

private String breadRollType;

private String addition1;

private double addition1Price;

private String addition2;

private double addition2Price;

510
The Complete Java Training Imran Afzal
private String addition3;

private double addition3Price;

private String addition4;

private double addition4Price;

These fields represent the name, type of meat, price, and type of bread roll for the burger, along with
up to four possible additions.

9.9.2- Constructor

public Hamburger(String name, String meat, double price, String breadRollType) {

this.name = name;

this.meat = meat;

this.price = price;

this.breadRollType = breadRollType;

This constructor initializes the basic details of the burger.

9.9.3- Methods for Additions

public void addAddition1(String name, double price) {

this.addition1 = name;

this.addition1Price = price;

511
The Complete Java Training Imran Afzal
}

public void addAddition2(String name, double price) {

this.addition2 = name;

this.addition2Price = price;

public void addAddition3(String name, double price) {

this.addition3 = name;

this.addition3Price = price;

public void addAddition4(String name, double price) {

this.addition4 = name;

this.addition4Price = price;

Each method allows for adding a named topping with a specific price.

512
The Complete Java Training Imran Afzal
9.9.4- Itemizing the Hamburger

public double itemizeHamburger() {

double hamburgerPrice = this.price;

System.out.println(this.name + " hamburger on a " + this.breadRollType + " roll with " +


this.meat + ", price is " + this.price);

if (this.addition1 != null) {

hamburgerPrice += this.addition1Price;

System.out.println("Added " + this.addition1 + " for an extra " + this.addition1Price);

if (this.addition2 != null) {

hamburgerPrice += this.addition2Price;

System.out.println("Added " + this.addition2 + " for an extra " + this.addition2Price);

if (this.addition3 != null) {

hamburgerPrice += this.addition3Price;

System.out.println("Added " + this.addition3 + " for an extra " + this.addition3Price);

if (this.addition4 != null) {

513
The Complete Java Training Imran Afzal
hamburgerPrice += this.addition4Price;

System.out.println("Added " + this.addition4 + " for an extra " + this.addition4Price);

return hamburgerPrice;

This method prints an itemized receipt including additions and returns the total price.

9.9.5- DeluxeBurger Class

class DeluxeBurger extends Hamburger {

public DeluxeBurger() {

super("Deluxe", "Beef", 19.10, "Sesame seed");

super.addAddition1("Chips", 2.75);

super.addAddition2("Drink", 1.81);

@Override

public void addAddition1(String name, double price) {

514
The Complete Java Training Imran Afzal
System.out.println("Cannot add additional items to a deluxe burger");

@Override

public void addAddition2(String name, double price) {

System.out.println("Cannot add additional items to a deluxe burger");

@Override

public void addAddition3(String name, double price) {

System.out.println("Cannot add additional items to a deluxe burger");

@Override

public void addAddition4(String name, double price) {

System.out.println("Cannot add additional items to a deluxe burger");

515
The Complete Java Training Imran Afzal
}

The deluxe burger includes chips and a drink by default and blocks additional customization.

9.9.6- HealthyBurger Class

class HealthyBurger extends Hamburger {

private String addition5Name;

private double addition5Price;

private String addition6Name;

private double addition6Price;

public HealthyBurger(String meat, double price) {

super("Healthy Burger", meat, price, "Brown rye");

public void addAddition5(String name, double price) {

this.addition5Name = name;

this.addition5Price = price;

516
The Complete Java Training Imran Afzal
public void addAddition6(String name, double price) {

this.addition6Name = name;

this.addition6Price = price;

@Override

public double itemizeHamburger() {

double totalPrice = super.itemizeHamburger();

if (this.addition5Name != null) {

System.out.println("Added " + this.addition5Name + " for an extra " + this.addition5Price);

totalPrice += this.addition5Price;

if (this.addition6Name != null) {

System.out.println("Added " + this.addition6Name + " for an extra " + this.addition6Price);

totalPrice += this.addition6Price;

517
The Complete Java Training Imran Afzal
}

return totalPrice;

The HealthyBurger class allows two additional healthy toppings and overrides the itemizing method
to include them.

9.9.7- Testing in Main Class

public class MainClass {

public static void main(String[] args) {

// Create a regular hamburger and add some additions

Hamburger hamburger = new Hamburger("Classic", "Beef", 10.50, "White");

hamburger.addAddition1("Lettuce", 0.50);

hamburger.addAddition2("Cheese", 1.00);

hamburger.itemizeHamburger();

System.out.println("----------");

518
The Complete Java Training Imran Afzal
// Create a deluxe hamburger (which already has 2 additions) and try to add more

DeluxeBurger deluxeBurger = new DeluxeBurger();

deluxeBurger.addAddition1("Lettuce", 0.50);

deluxeBurger.itemizeHamburger();

System.out.println();

// Create a healthy hamburger and add some additions

HealthyBurger healthyBurger = new HealthyBurger("Chicken", 8.50);

healthyBurger.addAddition1("Tomato", 0.75);

healthyBurger.addAddition5("Avocado", 1.50);

healthyBurger.itemizeHamburger();

9.9.8- Output Summary

1. Classic Hamburger Output

• Displays the base burger with White roll and Beef meat.

• Shows added items: Lettuce and Cheese.

519
The Complete Java Training Imran Afzal
• Calculates total price.

2. Deluxe Burger Output

• Ignores any new additions.

• Shows pre-included Chips and Drink.

• Returns total price including those fixed additions.

3. Healthy Burger Output

• Includes a healthy bread type (Brown rye).

• Shows additions including a regular (Tomato) and a healthy (Avocado) one.

• Returns updated total price.

This example demonstrates the use of inheritance and method overriding to enforce different rules
for burger customization using polymorphism.

9.10- Polymorphism Exercise Part 3

9.10.1- Creating the Movie Superclass

The Movie class represents a generic movie with two private attributes:

public class Movie {

private String title;

private String director;

These variables store the title and the director's name and are made private to follow encapsulation
principles.

9.10.2- Constructor

public Movie(String title, String director) {

520
The Complete Java Training Imran Afzal
this.title = title;

this.director = director;

The constructor initializes the title and director fields using the provided arguments.

9.10.3- Play Method

public void play() {

System.out.println("Playing " + title + " directed by " + director);

This method prints a message indicating that the movie is playing, along with its title and director.

9.10.4- Getters

public String getTitle() {

return title;

public String getDirector() {

return director;

521
The Complete Java Training Imran Afzal
These methods return the values of the title and director, allowing access to the private fields from
subclasses.

9.10.5- Creating the ActionMovie Class

The ActionMovie class extends the Movie class and customizes the play method:

class ActionMovie extends Movie {

public ActionMovie(String title, String director) {

super(title, director);

@Override

public void play() {

System.out.println("Playing action movie " + getTitle() + " directed by " + getDirector());

The overridden play method uses the getters from the Movie class to print a message specific to an
action movie.

9.10.6- Creating the ComedyMovie Class

The ComedyMovie class also extends Movie and provides its own play implementation:

class ComedyMovie extends Movie {

522
The Complete Java Training Imran Afzal
public ComedyMovie(String title, String director) {

super(title, director);

@Override

public void play() {

System.out.println("Playing comedy movie " + getTitle() + " directed by " + getDirector());

This class customizes the message format to reflect a comedy genre.

9.10.7- Creating the HorrorMovie Class

The HorrorMovie class inherits from Movie and overrides the play method:

class HorrorMovie extends Movie {

public HorrorMovie(String title, String director) {

super(title, director);

523
The Complete Java Training Imran Afzal
@Override

public void play() {

System.out.println("Playing horror movie " + getTitle() + " directed by " + getDirector());

This class is structured similarly to the others, but tailors its message for the horror genre.

9.10.8- Using Polymorphism in the Main Class

public class MainClass {

public static void main(String[] args) {

Movie actionMovie = new ActionMovie("Die Hard", "John McTiernan");

Movie comedyMovie = new ComedyMovie("Bridesmaids", "Paul Feig");

Movie horrorMovie = new HorrorMovie("The Shining", "Stanley Kubrick");

actionMovie.play();

comedyMovie.play();

horrorMovie.play();

524
The Complete Java Training Imran Afzal
}

In this code:

• Objects of the ActionMovie, ComedyMovie, and HorrorMovie classes are created and
assigned to Movie type references.

• The overridden play method is invoked on each object.

• Despite all variables being of type Movie, Java executes the appropriate subclass version of
play due to polymorphism.

Output

Playing action movie Die Hard directed by John McTiernan

Playing comedy movie Bridesmaids directed by Paul Feig

Playing horror movie The Shining directed by Stanley Kubrick

This output demonstrates runtime polymorphism. Each subclass customizes its behavior while
sharing the same interface (play method) from the base class Movie.

9.11- Polymorphism Exercise Part 4

9.11.1- Developer Class

// Developer.java

public class Developer {

private String name;

private int age;

private String specialization;

525
The Complete Java Training Imran Afzal
public Developer(String name, int age, String specialization) {

this.name = name;

this.age = age;

this.specialization = specialization;

public String getName() {

return name;

public int getAge() {

return age;

public String getSpecialization() {

return specialization;

526
The Complete Java Training Imran Afzal
}

The Developer class contains the name, age, and area of specialization. It uses a constructor to
initialize values and includes getter methods for all three fields.

9.11.2- ProgrammingLanguage Class

// ProgrammingLanguage.java

public class ProgrammingLanguage {

private String name;

private int year;

private Developer developer;

public ProgrammingLanguage(String name, int year, Developer developer) {

this.name = name;

this.year = year;

this.developer = developer;

527
The Complete Java Training Imran Afzal
public String getName() {

return name;

public int getYear() {

return year;

public Developer getDeveloper() {

return developer;

public void printInfo() {

System.out.println("Name: " + name);

System.out.println("Year: " + year);

System.out.println("Developer: " + developer.getName() + " (" + developer.getSpecialization()


+ ")");

528
The Complete Java Training Imran Afzal
}

This class contains the programming language name, the year it was created, and a Developer object.
It provides a printInfo method to display the language details along with the developer information.

9.11.3- Java Class

// Java.java

class Java extends ProgrammingLanguage {

private String platform;

public Java(String name, int year, Developer developer, String platform) {

super(name, year, developer);

this.platform = platform;

public String getPlatform() {

return platform;

529
The Complete Java Training Imran Afzal
@Override

public void printInfo() {

super.printInfo();

System.out.println("Platform: " + platform);

public void compile() {

System.out.println("Compiling Java code...");

public void compile(String filename) {

System.out.println("Compiling " + filename + "...");

The Java class extends ProgrammingLanguage, adds a platform field, overrides printInfo, and includes
overloaded compile methods.

9.11.4- CPP Class

// Cpp.java

530
The Complete Java Training Imran Afzal
class Cpp extends ProgrammingLanguage {

private String extension;

public Cpp(String name, int year, Developer developer, String extension) {

super(name, year, developer);

this.extension = extension;

public String getExtension() {

return extension;

@Override

public void printInfo() {

super.printInfo();

System.out.println("Extension: " + extension);

531
The Complete Java Training Imran Afzal
}

public void compile() {

System.out.println("Compiling C++ code...");

public void compile(String filename) {

System.out.println("Compiling " + filename + "...");

The CPP class introduces an extension attribute and also overrides printInfo. It contains compile
methods similar to the Java class but tailored for C++.

9.11.5- Python Class

// Python.java

class Python extends ProgrammingLanguage {

private String version;

532
The Complete Java Training Imran Afzal
public Python(String name, int year, Developer developer, String version) {

super(name, year, developer);

this.version = version;

public String getVersion() {

return version;

@Override

public void printInfo() {

super.printInfo();

System.out.println("Version: " + version);

public void interpret() {

System.out.println("Interpreting Python code...");

533
The Complete Java Training Imran Afzal
}

public void interpret(String filename) {

System.out.println("Interpreting " + filename + "...");

The Python class adds a version field, overrides printInfo, and provides methods to simulate
interpretation of Python code.

9.11.6- Main Class

public class MainClass {

public static void main(String[] args) {

Developer john = new Developer("John", 25, "Web Development");

Java java = new Java("Java", 1995, john, "JVM");

Cpp cpp = new Cpp("C++", 1983, john, ".cpp");

Python python = new Python("Python", 1991, john, "3.9");

// Call the printInfo() method of each object

534
The Complete Java Training Imran Afzal
java.printInfo();

cpp.printInfo();

python.printInfo();

// Call the compile() and compile(String filename) methods of the Java and Cpp objects

java.compile();

java.compile("Main.java");

cpp.compile();

cpp.compile("main.cpp");

// Call the interpret() and interpret(String filename) methods of the Python object

python.interpret();

python.interpret("script.py");

This code demonstrates polymorphism by creating instances of each subclass and invoking both
inherited and unique methods. The output confirms proper method overriding and subclass-specific
behavior.

535
The Complete Java Training Imran Afzal
9.11.7- Output

Name: Java

Year: 1995

Developer: John (Web Development)

Platform: JVM

Name: C++

Year: 1983

Developer: John (Web Development)

Extension: .cpp

Name: Python

Year: 1991

Developer: John (Web Development)

Version: 3.9

Compiling Java code...

Compiling Main.java...

Compiling C++ code...

Compiling main.cpp...

536
The Complete Java Training Imran Afzal
Interpreting Python code...

Interpreting script.py...

This output shows how polymorphism and inheritance work together to allow flexible and extensible
design, where each subclass extends the functionality of a shared base class while maintaining its
unique behaviors.

9.12- Polymorphism Exercise Part 5

9.12.1- Monster Class

public class Monster {

protected String name;

public Monster(String name) {

this.name = name;

public String attack() {

return this.name + " I don't know how to attack!";

537
The Complete Java Training Imran Afzal
The Monster class defines a protected name field, a constructor to initialize it, and a generic attack
method. The method returns a message indicating that the monster does not know how to attack.
This method is intended to be overridden by subclasses.

9.12.2- FireMonster Class

class FireMonster extends Monster {

public FireMonster(String name) {

super(name);

@Override

public String attack() {

return super.name + " Attack with fire!";

The FireMonster subclass extends Monster and overrides the attack method to provide a custom
behavior. It uses the name field inherited from the superclass and appends a specific attack message.

9.12.3- WaterMonster Class

class WaterMonster extends Monster {

538
The Complete Java Training Imran Afzal
public WaterMonster(String name) {

super(name);

@Override

public String attack() {

return super.name + " Attack with water!";

The WaterMonster subclass defines its unique implementation of the attack method, indicating an
attack with water.

9.12.4- StoneMonster Class

class StoneMonster extends Monster {

public StoneMonster(String name) {

super(name);

@Override

public String attack() {

return super.name +" Attack with stones!";

539
The Complete Java Training Imran Afzal
}

The StoneMonster subclass overrides the attack method to simulate an attack using stones.

9.12.5- TestMonster Class (Main)

public class TestMonster {

public static void main(String[] args) {

Monster m1 = new FireMonster("r2u2");

Monster m2 = new WaterMonster("u2r2");

Monster m3 = new StoneMonster("r2r2");

System.out.println(m1.attack());

System.out.println(m2.attack());

System.out.println(m3.attack());

m1 = new StoneMonster("a2b2");

540
The Complete Java Training Imran Afzal
System.out.println(m1.attack());

Monster m4 = new Monster("u2u2");

System.out.println(m4.attack());

This class demonstrates the use of polymorphism by assigning subclass objects to superclass
references and calling overridden methods. The program creates instances of each subclass, calls the
attack method on each, and shows how dynamic method dispatch works in Java.

Output

R2u2 Attack with fire

U2r2 Attack with water

R2r2 Attack with stones

A2b2 Attack with stones

U2u2 I don't know how to attack!

This output reflects the runtime behavior of polymorphic method calls. Subclass instances override
the attack method to provide specific messages, while the base class Monster uses the default
implementation when no override is present. The final example confirms that a superclass reference
can refer to subclass objects, and the correct overridden method is invoked based on the actual object
type.

541
The Complete Java Training Imran Afzal
9.13- Introduction to Java Packages

9.13.1- Understanding Java Packages

A package in Java is a namespace that organizes a set of related classes and interfaces. Packages help
prevent naming conflicts, control access with protected and default access levels, and make it easier
to locate and use classes. Packages are often structured hierarchically, and in Java development, it's
standard to use all lowercase letters for package names. The structure of a package is typically indicated
by periods separating different levels, for example, java.util.

Packages such as java.lang and java.util are part of Java’s standard library. For example, Scanner and
Random are part of java.util. To use these classes, import statements are used at the top of your source
file:

import java.util.Scanner;

public class MainClass {

public static void main(String[] args) {

Scanner sc = new Scanner(System.in);

Alternatively, the wildcard asterisk (*) can be used to import all classes in a package:

import java.util.*

public class Main {

public static void main(String[] args) {

542
The Complete Java Training Imran Afzal
Scanner sc = new Scanner(System.in);

Date date = new Date();

This simplifies access but is less common in modern development due to better clarity with explicit
imports.

9.13.2- Package and Import Statement Rules

• The package statement (if used) must be the first line in a source file, before any import
statements.

• Only one package statement is allowed per file.

• Multiple import statements are allowed.

• A fully qualified class name (FQCN) can be used in place of an import statement.

Example

package nixware.consulting.package;

public class MainClass {

public static void main(String[] args) {

java.util.Scanner scanner = new java.util.Scanner(System.in);

543
The Complete Java Training Imran Afzal
9.13.3- Naming Convention for Custom Packages

When creating your own packages, it's common to use a reverse domain name to ensure uniqueness.
For a company nixware.com, a typical package structure would be:

com.nixware.product

com.nixware.service

Each part of the hierarchy corresponds to a folder in the file system.

9.13.4- Creating Packages in IntelliJ

In IntelliJ IDEA:

1. Right-click the src folder > New > Package.

2. Enter the full package name (e.g., com.nixware.consulting).

3. IntelliJ creates the necessary folder structure.

4. Right-click on the package > New > Java Class to add a class.

You can also type the full package path when creating a class name, and IntelliJ will generate the
folders and class for you.

9.13.5- Example: Using Packages

Product Class

package com.nixware.first;

public class Product {

private String course;

544
The Complete Java Training Imran Afzal
public Product(String course) {

this.course = course;

@Override

public String toString() {

return "Product{" +

"course='" + course + '\'' +

'}';

Main Class

package nixware.consulting;

import com.nixware.first.*;

545
The Complete Java Training Imran Afzal
public class mainClass {

public static void main(String[] args) {

Product product = new Product("Java");

System.out.println(product);

When the code runs, the output is:

Course = Java

This setup demonstrates:

• Creating packages

• Using import statements

• Accessing a class across packages

9.13.7- Using Fully Qualified Names

Instead of importing the class, you can reference it directly using its fully qualified name:

com.nixware.first.Product product = new com.nixware.first.Product("Java");

System.out.println(product);

This eliminates the import statement but becomes cumbersome if used frequently.

546
The Complete Java Training Imran Afzal
9.13.8- Using Wildcard Import

You can modify the import line as follows:

import com.nixware.first.*;

This allows access to all classes within the com.nixware.first package. Although this works, it's
generally clearer to import only the classes being used.

Java packages provide structure and prevent naming conflicts. Using import statements, fully qualified
class names, and package organization in IntelliJ helps manage codebases as they grow. While using
wildcards is allowed, modern practices favor explicit imports for readability and clarity.

547
The Complete Java Training Imran Afzal
Chapter 10

Java Arrays

548
The Complete Java Training Imran Afzal
10.1- Introduction to Arrays

Arrays are a powerful tool in Java that allow developers to store multiple values of the same type in a
single data structure. They are especially useful when working with large sets of related data such as
temperatures, names, or scores.

Unlike creating separate variables for each value, arrays provide a more concise and manageable
solution. For instance, in a game that tracks scores for multiple players, using an array to store those
scores simplifies code management and enables collective operations like calculating averages or
finding the highest score.

10.1.1- Declaring and Initializing Arrays

Arrays are declared by specifying the type of the elements followed by square brackets. The square
brackets can appear after the data type or the variable name, though placing them after the data type
is more common. To instantiate an array, the new keyword is used along with the type and the size of
the array inside square brackets.

For example:

int[] scores = new int[5];

This creates an array that can hold five integers. Arrays in Java do not use parentheses during
instantiation—doing so results in a compile-time error.

For example,

Invalid Array Creation - Compile Error because of ()

int[] myArray = new int[5]();

Each element in an array is indexed, starting at 0. For example, in a string array of four names, the
first element is accessed with index 0, and the last element is at index 3.

549
The Complete Java Training Imran Afzal
Example 1

Array Variable Declaration

int[] RollNoList;

String[] NameList;

String SubjectList[];

These are valid ways to declare arrays. The first two use the preferred format (square brackets after
the type), while the third uses an alternative style (square brackets after the variable name), which is
also legal in Java.

Example 2

// Array creation

int[] myArray = new int[5]; // Creates an array of 5 integers

// Object creation

StringBuilder builder = new StringBuilder(); // Creates a StringBuilder object

Although arrays are objects in Java, their creation syntax is slightly different from regular object
instantiation.

10.1.2- Types of Arrays

Single-Dimensional Arrays
A single-dimensional array stores a sequence of values in a single row. It is a linear collection where
each value is accessed using a single index starting from 0.

550
The Complete Java Training Imran Afzal
Arrays can be declared like this:

int[] RollNoList;

String[] NameList;

String SubjectList[];

These are all single-dimensional array declarations. Each one can store a list of values of the same type
(e.g., integers, strings).
To initialize them, you can assign values directly:

int[] ages = {21, 25, 30, 35};

Multi-Dimensional Arrays
A multi-dimensional array stores values in a table-like format using rows and columns. Each element
is accessed using a pair of indices.

int[][] matrix = new int[3][3];

You can also declare multi-dimensional arrays using this general syntax:

dataType[][] arrayName;

int[][] myArray;

Accessing individual elements is done using two indices:

arrayName[rowIndex][columnIndex];

10.1.3- Advantages of Using Arrays in Java

• Efficient Memory Management: Arrays allocate a fixed block of memory, which makes
them efficient for predictable data storage.

551
The Complete Java Training Imran Afzal
• Easy Data Manipulation: Arrays allow for direct access and modification of elements, and
loops can be used to process all elements efficiently.

• Quick Element Access: Accessing elements by index is fast and simple.

• Support for Multidimensional Structures: Arrays can store data in multiple dimensions,
making them suitable for more complex data organization.

• Integration with Other Structures: Arrays can be used alongside other Java data structures
like lists, stacks, and queues.

10.1.4- Limitations of Arrays in Java

• Fixed Size: Once created, the size of an array cannot be changed. To adjust size, a new array
must be created and data copied over.

• Inefficient for Dynamic Data: Arrays do not resize automatically, making them unsuitable
for applications that require dynamic memory allocation.

• Single Data Type Restriction: Arrays can only store values of one data type. Storing mixed
data types requires separate arrays.

• No Built-In Methods: Unlike some Java collections, arrays lack built-in methods for
common operations such as searching or inserting.

• Potential for Memory Leaks: Improper memory management with arrays can lead to
memory leaks, negatively impacting application performance.

Arrays form a fundamental part of Java programming, offering an efficient and structured way to
handle large amounts of data of the same type. Understanding how to declare, initialize, and work
with both single and multi-dimensional arrays is essential for building scalable and organized Java
applications.

10.2- Arrays Exercise Part 1

To begin working with arrays in Java, start by creating a new project in IntelliJ named arrays. Within
the src folder, create a package named nixware.consulting. Inside this package, create a new Java class
named MainClass. Within MainClass, generate the main method using the psvm shortcut.

552
The Complete Java Training Imran Afzal
10.2.1- Creating and Initializing Arrays

Create an integer array named numberArray with five elements using the new keyword. Arrays in Java
are fixed in size, so once declared, their length cannot change.

int[] numberArray = new int[4]; // Creates an array with 4 elements

numberArray[2] = 30; // Assigns value 30 to index 2

numberArray[0] = 4; // Correct type assignment (must be int)

numberArray[1] = 30;

10.2.2- Working with Double Arrays

Arrays can store other primitive types like double.

double[] decimalArray = new double[4];

decimalArray[1] = 11.5;

System.out.println(decimalArray[1]); // Output: 11.5

10.2.3- Type Safety in Arrays

Invalid Examples:

numberArray[0] = 7.5; // Compile error: assigning double to int

numberArray[1] = "12"; // Compile error: assigning String to int

Make sure values assigned match the array's data type.

10.2.4- Using Array Initializers

Instead of assigning values one by one, Java supports array initializers using curly braces.

553
The Complete Java Training Imran Afzal
int[] SixNumbers = {1, 2, 3, 4, 5, 6};

System.out.println("First = " + SixNumbers[0]);

int arrayLength = SixNumbers.length;

System.out.println("Length = " + arrayLength);

System.out.println("Last = " + SixNumbers[arrayLength - 1]);

Using SixNumbers[SixNumbers.length] will throw an ArrayIndexOutOfBoundsException.

10.2.5- Anonymous Array Initializers

Anonymous arrays can only be used at the time of declaration.

int[] anotherArray = {1, 2, 3, 4}; // Valid

For reassignment (long form):

int[] anotherArray;

anotherArray = new int[] {1, 2, 3, 4};

10.2.6- Iterating Through Arrays with a For Loop

General Syntax:

for (initialization; condition; increment) {

// block of code

554
The Complete Java Training Imran Afzal
Example:

for (int i = 0; i < anotherArray.length; i++) {

System.out.print(anotherArray[i] + " ");

10.2.7- Using Enhanced For Loop (For-Each Loop)

General Syntax:

for (type variable : array) {

// block of code

Example:

for (int element : anotherArray) {

System.out.print(element + " ");

Type Checking Arrays with instanceof

You can check if an object is an array using instanceof.

Object arrayRef = anotherArray;

if (arrayRef instanceof int[]) {

555
The Complete Java Training Imran Afzal
System.out.println("arrayRef is an int array");

Storing different types in an Object[] array:

Object[] objectArray = new Object[5];

objectArray[0] = anotherArray;

objectArray[1] = "Nixware";

objectArray[2] = new StringBuilder("World");

10.3- Arrays Exercise Part 2

This exercise demonstrates how to read input values from the user, store them in an array, and then
calculate the sum and average of those values.

10.3.1- Creating the Program Structure

Start by creating a class named ArrayAverage inside the nixware.consulting package. Then, add the
main method:

import java.util.Scanner;

public class ArrayAverage {

public static void main(String[] args) {

This is the basic structure where the program logic will reside.

556
The Complete Java Training Imran Afzal
10.3.2- Reading Input Using Scanner

Create a Scanner object to capture user input from the console and ask the user for the size of the
array:

Scanner input = new Scanner(System.in);

System.out.print("Enter the limit of the array: ");

int limit = input.nextInt();

This reads an integer from the user and stores it in the limit variable, which will be used to determine
the size of the array.

10.3.3- Declaring the Array and Variables

Now declare an integer array a with the given limit and a variable sum to store the total:

int[] a = new int[limit];

int sum = 0;

This sets up the required storage for the user inputs and the accumulator for summing the values.

10.3.4- Filling the Array with User Input

Use a for loop to read values into the array:

for(int i = 0; i < limit; i++) {

System.out.print("Enter value for element " + (i + 1) + ": ");

a[i] = input.nextInt();

557
The Complete Java Training Imran Afzal
}

This loop prompts the user to enter values for each element and stores them in the array ‘a’.

10.3.5- Calculating the Sum of Array Elements

After the array is filled, calculate the sum using another loop:

for(int i = 0; i < limit; i++) {

sum += a[i];

This loop adds each element of the array to sum.

10.3.6- Calculating the Average

Now compute the average by casting the sum to double and dividing by the limit:

double average = (double) sum / limit;

This ensures you get decimal accuracy in the result.

10.3.7- Printing the Results

Finally, display the sum and average to the user:

System.out.println("Sum of array elements is: " + sum);

System.out.println("Average of array elements is: " + average);

10.3.8- Example Execution

If the user inputs:

558
The Complete Java Training Imran Afzal
• Limit: 3

• Values: 13, 14, 15

The program will output:

Sum of array elements is: 42

Average of array elements is: 14.0

10.3.9- Summary of Logic

• The Scanner reads the limit and values.

• An array of specified size is created.

• Values are stored via user input.

• The sum is calculated through a loop.

• The average is computed and displayed.

This example emphasizes the basics of array usage with input handling, summation, and average
calculation in Java.

10.4- Arrays Exercise Part 3

This example demonstrates how to read an array from user input and display its elements in reverse
order.

10.4.1- Creating the Program Structure

Create a class named ReverseArrayExample inside the nixware.consulting package. Then define the
main method:

import java.util.Scanner;

559
The Complete Java Training Imran Afzal
class ReverseArrayExample {

public static void main(String[] args) {

10.4.2- Reading Input Using Scanner

Create a Scanner object to read user input:

Scanner scanner = new Scanner(System.in);

Prompt the user to enter the array size (limit):

System.out.print("Enter the array limit: ");

int limit = scanner.nextInt();

This stores the number of elements the array should contain in the variable limit.

10.4.3- Declaring the Array

Create an integer array arr with a size equal to the limit:

int[] arr = new int[limit];

This creates an empty array of limit size. All elements are initialized to 0 by default.

10.4.4- Filling the Array with Input Values

Use a for loop to prompt the user for values and store them in the array:

for (int i = 0; i < limit; i++) {

System.out.printf("Enter element at index %d: ", i);

560
The Complete Java Training Imran Afzal
arr[i] = scanner.nextInt();

Each iteration:

• Prompts the user to enter a value for the current index

• Stores the input value into the corresponding position in the array

10.4.5- Displaying Elements in Reverse Order

Print a message before showing the reversed output:

System.out.println("\nDisplaying array elements in reverse order...\n");

for (int i = limit - 1; i >= 0; i--) {

System.out.println(arr[i]);

Explanation of the loop:

• Starts at limit - 1, which is the index of the last element

• Decrements i until it reaches 0

• Each iteration prints the current element

561
The Complete Java Training Imran Afzal
10.4.6- Example Execution

If the user inputs the following:

• Limit: 4

• Values: 12, 11, 15, 9

Then the reverse output will be:

15

11

12

10.4.7- Summary of Logic

• A Scanner object captures user input

• The user defines the size of the array

• A for loop fills the array with user-provided integers

• Another for loop prints the array elements in reverse order using backward indexing

These skills are fundamental for working with arrays in Java and are especially useful for scenarios like
reversing data, validating input, or processing collections of items.

10.5- Arrays Exercise Part 4

10.5.1- Finding the Smallest Element in an Array

Create a class file named SmallestElement in the nixware.consulting package and add a main method.
Begin by initializing an integer array with fixed values:

public class SmallestElementInArray {

562
The Complete Java Training Imran Afzal
public static void main(String[] args) {

int[] arr = {10, 5, 20, 3, 8};

This array arr contains five integers. The goal is to determine the smallest value among them.

Next, initialize a variable min with the first element of the array:

int min = arr[0];

This acts as a starting reference point to compare other elements in the array.

Use a for loop to iterate through the remaining elements:

for(int i = 1; i < arr.length; i++) {

if(arr[i] < min) {

min = arr[i];

This loop starts from index 1 and compares each element with the current minimum. If a smaller
value is found, min is updated.

After the loop completes, print the result:

System.out.println("The smallest element in the array is: " + min);

563
The Complete Java Training Imran Afzal
10.5.2- How the Program Works

• The array is initialized with the values: 10, 5, 20, 3, 8.

• min is initialized to 10 (first element).

• On each iteration:

o Index 1: 5 is less than 10 → min becomes 5.

o Index 2: 20 is not less than 5 → min remains 5.

o Index 3: 3 is less than 5 → min becomes 3.

o Index 4: 8 is not less than 3 → min remains 3.

• After the loop ends, min holds the smallest value: 3.

The output is:

The smallest element in the array is: 3

10.5.3- Modifying the Program to Find the Largest Element

To find the largest element instead of the smallest, make the following changes:

• Rename the variable from min to max to better reflect its purpose.

• Change the comparison condition from < to >.

Revised loop:

int max = arr[0];

for (int i = 1; i < arr.length; i++)

if (arr[i] > max)

564
The Complete Java Training Imran Afzal
{

max = arr[i];

System.out.println("The largest element in the array is: " + max);

10.5.4- Example: Updated Array

If arr is changed to {100, 150, 20, 300, 8}:

• max starts at 100.

• Index 1: 150 > 100 → max becomes 150.

• Index 2: 20 < 150 → max remains 150.

• Index 3: 300 > 150 → max becomes 300.

• Index 4: 8 < 300 → max remains 300.

Final output:

The largest element in the array is: 300

This exercise demonstrates how to:

• Initialize arrays with known values.

• Use loops and conditional statements to search for minimum or maximum values.

• Efficiently compare and update variables based on array contents.

• Adapt logic for different requirements with minimal code changes.

565
The Complete Java Training Imran Afzal
10.6- Introduction to Java Util Arrays Class

Java provides a utility class named java.util.Arrays that offers various static methods for working with
arrays. These methods help with common array operations such as sorting, copying, filling, and
searching. Rather than being called on the array instances directly, these methods are called through
the helper class Arrays.

Some of the most commonly used methods in the Arrays class include:

• sort()

• copyOf()

• fill()

• binarySearch()

• equals()

• toString()

This section focuses on the sort, copyOf, and fill methods.

10.6.1- Using the sort() Method

The Arrays.sort() method sorts the elements of an array in ascending order. It modifies the original
array directly.

There are several overloaded versions of the method:

• sort(int[] a) – Sorts an integer array

• sort(long[] a) – Sorts a long array

• sort(float[] a) – Sorts a float array

• sort(double[] a) – Sorts a double array

• sort(Object[] a) – Sorts an array of objects using natural ordering

566
The Complete Java Training Imran Afzal
10.6.2- Example: Sorting an Integer Array

int[] arr = {5, 3, 9, 1, 7};

Arrays.sort(arr);

System.out.println(Arrays.toString(arr));

After sorting, the array becomes [1, 3, 5, 7, 9].


Arrays.toString() converts the array into a readable string format for output.

Note: This sorting operation is in-place, meaning the original array is modified. To keep the original
unchanged, copy it before sorting.

10.6.3- Example: Attempting to Sort an Object Array

Object[] arr = {5, "apple", true, 3.14, "orange"};

Arrays.sort(arr); // Throws runtime error

System.out.println(Arrays.toString(arr));

When attempting to sort a mixed Object[] array containing integers, strings, booleans, and doubles,
Arrays.sort() will throw a ClassCastException at runtime. This is because Java cannot compare
different object types that do not implement a shared Comparable strategy.

567
The Complete Java Training Imran Afzal
Explanation:

• Arrays.sort() requires all elements in the array to be mutually comparable.

• When types like Integer, String, Boolean, and Double are mixed, Java does not know how to
order them.

Output (runtime error):

Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class


java.lang.Integer

To sort an Object array successfully:

• Ensure all elements are of the same type and implement Comparable, or

• Provide a custom Comparator for sorting

10.6.4- Using the copyOf() Method

The Arrays.copyOf() method creates a copy of an array with a specified length.

Syntax:

int[] copy = Arrays.copyOf(originalArray, newLength);

• If newLength is greater than the original, the extra elements are filled with default values (0
for integers).

• If newLength is smaller, only the first newLength elements are copied.

10.6.5- Example: Copying an Array

int[] original = {1, 2, 3, 4, 5};

int[] copy = Arrays.copyOf(original, 3);

568
The Complete Java Training Imran Afzal
System.out.println(Arrays.toString(original)); // prints [1, 2, 3, 4, 5]

System.out.println(Arrays.toString(copy)); // prints [1, 2, 3]

[1, 2, 3, 4, 5]

[1, 2, 3]

This shows how a new array with selected elements can be created from an existing one.

10.6.6- Using the fill() Method

The Arrays.fill() method fills an entire array with a specific value.

Syntax:

Arrays.fill(array, value);

This method replaces all elements in the array with the given value.

10.6.7- Example: Filling an Array with a Value

int[] arr = new int[5];

Arrays.fill(arr, 0);

System.out.println(Arrays.toString(arr)); // prints [0, 0, 0, 0, 0]

569
The Complete Java Training Imran Afzal
[0, 0, 0, 0, 0]

In this case, all five elements of the arr array are set to 0. This is useful when initializing an array with
default or placeholder values.

These static methods provided by java.util.Arrays simplify many common array operations and make
code more concise and efficient.

10.7- Java Util Arrays Class Method Exercise Part 1

10.7.1- Sorting a Numeric Array Without Using Arrays.sort()

Create a class named ArraySortWithoutSortMethod in the nixware.consulting package and define the
main method. Inside the method, initialize an array of integers:

public class ArraySortWithoutSortMethod {

public static void main(String[] args) {

int[] myArray = { 13, 7, 6, 45, 21, 9, 101, 102 };

Declare a temporary variable for use during swapping:

int temp = 0;

Print the original array elements before sorting:

System.out.println("Before sorting:");

for (int i = 0; i < myArray.length; i++) {

System.out.print(myArray[i] + " ");

570
The Complete Java Training Imran Afzal
Use a nested for loop to sort the array in ascending order:

for (int i = 0; i < myArray.length; i++) {

for (int j = i + 1; j < myArray.length; j++) {

if (myArray[i] > myArray[j]) {

temp = myArray[i];

myArray[i] = myArray[j];

myArray[j] = temp;

This sorting technique compares each element with all subsequent elements and swaps them if out of
order.

Print the sorted array:

System.out.println("\nAfter sorting:");

for (int i = 0; i < myArray.length; i++) {

System.out.print(myArray[i] + " ");

571
The Complete Java Training Imran Afzal
}

10.7.2- Step-by-Step Example of First Two Iterations

• Initial array: {13, 7, 6, 45, 21, 9, 101, 102}

• First iteration (i=0, j=1):

o Compare 13 and 7 → swap → {7, 13, 6, 45, 21, 9, 101, 102}

• Next (i=0, j=2):

o Compare 7 and 6 → swap → {6, 13, 7, 45, 21, 9, 101, 102}

The process continues until the array is sorted in ascending order.

10.7.3- Note on Efficiency


This manual sorting method is simple but not optimal for large arrays. As array size increases, the
number of iterations grows significantly, making it time-consuming. For efficiency, Java provides the
Arrays.sort() method.

10.7.4- Sorting Using Arrays.sort() Method

Create a new class named ArraySortWithSortMethod in the same package and define the main
method.

import java.util.Arrays;

public class ArraySortWithSortMethod {

public static void main(String[] args) {

int[] myArray = {13, 7, 6, 45, 21, 9, 101, 102};

Print the array before sorting using Arrays.toString():

572
The Complete Java Training Imran Afzal
System.out.println("Before sorting: " + Arrays.toString(myArray));

Sort the array in ascending order using the Arrays.sort() method:

Arrays.sort(myArray);

Print the sorted array:

System.out.println("After sorting: " + Arrays.toString(myArray));

The output is:

Before sorting: [13, 7, 6, 45, 21, 9, 101, 102]

After sorting: [6, 7, 9, 13, 21, 45, 101, 102]

The manual method helps understand how sorting works internally, but for performance and cleaner
code, the built-in method is recommended.

10.8- Java Util Arrays Class Method Exercise Part 2

10.8.1- Copying and Modifying an Array Using Arrays.copyOf()

Create a class file named CopyArray in the nixware.consulting package and define the main method.

import java.util.Arrays;

public class CopyArray {

573
The Complete Java Training Imran Afzal
public static void main(String[] args) {

int[] originalArray = {1, 2, 3, 4, 5};

Create a copy of the array using the Arrays.copyOf() method:

int[] copiedArray = Arrays.copyOf(originalArray, originalArray.length);

This method takes the original array and its length as arguments and returns a new array with the same
elements. copiedArray now holds a separate copy of originalArray.

Modify the copied array:

copiedArray[0] = 10;

This sets the first element of copiedArray to 10. Since it’s a copy, originalArray remains unchanged.

Print both arrays:

System.out.println("Original array: " + Arrays.toString(originalArray));

System.out.println("Copied array: " + Arrays.toString(copiedArray));

Output

Original array: [1, 2, 3, 4, 5]

Copied array: [10, 2, 3, 4, 5]

This confirms that the arrays are distinct and changes to one do not affect the other.

574
The Complete Java Training Imran Afzal
10.8.2- Filling an Array Using Arrays.fill()

Create another class file named FillArray in the same package and define the main method and declare
a new integer array with a length of 5:

import java.util.Arrays;

public class FillArray {

public static void main(String[] args) {

int[] myArray = new int[5];

At this point, all elements are set to 0 by default.

Fill the array with a specific value using the Arrays.fill() method:

Arrays.fill(myArray, 10);

This replaces every element of myArray with the value 10.

Print the filled array:

System.out.println("Filled array: " + Arrays.toString(myArray));

Output

Filled array: [10, 10, 10, 10, 10]

575
The Complete Java Training Imran Afzal
The fill() method is useful for initializing or resetting an array to a uniform value. So, both methods
simplify common tasks in array manipulation and help write cleaner, more efficient code.

10.9- Java Util Arrays Class Method Exercise Part 3

10.9.1- Binary Search Using Arrays.binarySearch()

This exercise demonstrates how to implement binary search using the built-in method provided by
the java.util.Arrays class. Binary search is an efficient algorithm for finding elements in a sorted array
by repeatedly dividing the search interval in half.

Creating the Program

Create a class named BinarySearchExample in the nixware.consulting package. Inside the main
method, start by reading user input:

import java.util.Arrays;

import java.util.Scanner;

public class BinarySearchExample {

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

System.out.print("Enter the size of the array: ");

int size = scanner.nextInt();

This sets up a Scanner object and prompts the user to enter the desired size of the array.

576
The Complete Java Training Imran Afzal
Initializing the Array

Create a new array of the specified size:

int[] myArray = new int[size];

Then prompt the user to enter values for the array elements:

System.out.println("Enter the elements of the array:");

for (int i = 0; i < size; i++) {

myArray[i] = scanner.nextInt();

Each entered integer is stored in the array one by one.

Reading the Target Value

After populating the array, ask the user to enter the value they want to search for:

System.out.print("Enter the target value: ");

int target = scanner.nextInt();

Sorting the Array

Before applying binary search, sort the array using the Arrays.sort() method:

Arrays.sort(myArray);

Sorting ensures that binary search works correctly, as it only functions on sorted arrays.

577
The Complete Java Training Imran Afzal
Performing Binary Search

Use Arrays.binarySearch() to search for the target value in the sorted array:

int position = Arrays.binarySearch(myArray, target);

This returns the index of the target value if found, or a negative number if not found.

Displaying the Result

Check the result using an if-else block:

if (position >= 0) {

System.out.println("Target value found at position " + position);

} else {

System.out.println("Target value not found");

If the result is 0 or higher, the value was found at that index. If the result is negative, the target value
is not present in the array.

Example Execution

Input:

Enter the size of the array:

578
The Complete Java Training Imran Afzal
Enter the elements of the array:

12

15

Enter the target value:

Sorted array becomes: [6, 12, 15]


Binary search finds the value 6 at index 0.

Output
Target value found at position 0

10.9.2- How It Works

• Binary search begins by examining the middle element.

• If the target is less than the middle, the left half is searched.

• If the target is greater, the right half is searched.

• This continues until the element is found or the interval is empty.

Notes

• The array must be sorted for binary search to work.

• If there are duplicate values, the returned index may not always be the first occurrence.

• If the array contains non-comparable elements, a custom comparator must be used.

Binary search is a fast and effective way to locate elements in large sorted arrays, and Java’s built-in
method makes it easy to implement.

579
The Complete Java Training Imran Afzal
10.10- Java dot Util dot Arrays Class Method Exercise Part 4

10.10.1- Generating a Random Integer Array

Let’s begin by creating the class and writing the method that sorts an array in descending order. The
program consists of two main methods:

• generateRandomNumbers(int length): Returns an integer array of the specified length with


randomly generated numbers between 0 and 100.

• sortIntegers(int[] numbers): Sorts the given integer array in descending order.

To begin, a class file named RandomArray is created in the nixware.consulting package. The main
method is added using psvm.

import java.util.Arrays;

import java.util.Random;

public class SortingProgram {

public static void main(String[] args) {

int[] unsortedNumbers = generateRandomNumbers(5);

System.out.println(Arrays.toString(unsortedNumbers));

int[] sortedNumbers = sortIntegers(new int[] {20, 50, 10});

System.out.println(Arrays.toString(sortedNumbers));

}.

580
The Complete Java Training Imran Afzal
10.10.2- Generating Random Numbers

The method generateRandomNumbers(int length) returns an integer array filled with randomly
generated numbers:

public static int[] generateRandomNumbers(int length) {

Random random = new Random();

int[] randomNumbers = new int[length];

for (int i = 0; i < length; i++) {

randomNumbers[i] = random.nextInt(100);

return randomNumbers;

• The array randomNumbers is initialized with the specified length.

• A Random instance is used to generate numbers.

• The nextInt(100) function ensures numbers are between 0 and 99.

• The filled array is returned.

581
The Complete Java Training Imran Afzal
10.10.3- Using the Generated Random Numbers in the Main Method

In the main method, the generateRandomNumbers method is called, and the generated array is printed
using Arrays.toString():

public static int[] generateRandomNumbers(int length) {

Random random = new Random();

int[] randomNumbers = new int[length];

for (int i = 0; i < length; i++) {

randomNumbers[i] = random.nextInt(100);

return randomNumbers;

• The method generates an array of 5 random numbers.

• Arrays.toString() converts the array to a printable format.

10.10.4- Sorting an Array in Descending Order

The method sortIntegers(int[] numbers) sorts an integer array in descending order:

private static int[] sortIntegers(int[] numbers) {

582
The Complete Java Training Imran Afzal
System.out.println(Arrays.toString(numbers));

int[] sortedNumbers = Arrays.copyOf(numbers, numbers.length);

boolean flag = true;

int temp;

while (flag) {

flag = false;

for (int i = 0; i < sortedNumbers.length - 1; i++) {

if (sortedNumbers[i] < sortedNumbers[i + 1]) {

temp = sortedNumbers[i];

sortedNumbers[i] = sortedNumbers[i + 1];

sortedNumbers[i + 1] = temp;

flag = true;

System.out.println("-->" + Arrays.toString(sortedNumbers));

583
The Complete Java Training Imran Afzal
}

return sortedNumbers;

• Arrays.copyOf(numbers, numbers.length) creates a copy of the original array.

• A while loop iterates until the array is sorted.

• A for loop compares adjacent elements and swaps them if needed.

• The flag ensures sorting continues until no swaps are required.

• The updated array is printed after each iteration.

10.10.5- Sorting a Given Array in the Main Method

The sortIntegers(int[]) method was already demonstrated in section 10.10.1, where we sorted a
predefined array. Here’s a reminder of how it was used:

int[] sortedNumbers = sortIntegers(new int[] {20, 50, 10});

System.out.println(Arrays.toString(sortedNumbers)); // Output: [50, 20, 10]

You can reuse this same approach with any array of integers. The sortIntegers() method will sort the
array in descending order using a reverse operation after applying Arrays.sort().

Program Execution and Output

The output demonstrates the sorting process:

[20, 50, 10]

584
The Complete Java Training Imran Afzal
--> [50, 20, 10]

--> [50, 20, 10]

--> [50, 20, 10]

• The original array [20, 50, 10] is printed.

• The program swaps 20 and 50 in the first iteration.

• No further swaps are needed, so the final sorted order is [50, 20, 10].

10.11- Arrays Revision

Arrays are a data structure that allows storing multiple values of the same type in a single variable. The
default values of numeric array elements are set to 0. Arrays use zero-based indexing, meaning an array
with N elements is indexed from 0 to N-1.

For example, an array with six elements has an index range from 0 to 5. If an attempt is made to access
an index out of this range, Java throws an ArrayIndexOutOfBoundsException, indicating that the
index is out of range. This happens when trying to access an element outside the defined array
boundaries.

To access array elements, square brackets ([]) are used, which are known as the array access operator.

10.11.1- Declaring and Initializing Arrays

Arrays can be created using different methods:

• Using the new keyword:

int[] array = new int[6]; // Creates an array of 6 elements initialized to 0

Here, new int[6] specifies that the array contains six elements, and all are initialized to 0 since it is an
integer array.

585
The Complete Java Training Imran Afzal
• Using an array initializer block:

int[] myNumbers = {1, 2, 3, 4, 5, 6};

This method initializes the array in-line using curly braces {} with values separated by commas. This
way of initializing an array is known as an anonymous array.

10.11.2- Understanding Arrays as Memory Slots

An array can be visualized as a collection of memory slots or boxes where each box contains a value.
Each slot has an index, which allows accessing its value using square brackets.

For example, in the following array:

int[] myArray = {11, 40, 25, 33, 50, 60};

Each element is assigned a specific index:

• myArray[0] = 11

• myArray[1] = 40

• myArray[2] = 25

• myArray[3] = 33

• myArray[4] = 50

• myArray[5] = 60

This means myArray[0] refers to the first element (11), while myArray[5] refers to the last element
(60).

10.11.3- Common Errors in Arrays

1. Array Index Out of Bounds Exception

int[] myArray = {20, 30, 40, 17, 80};

586
The Complete Java Training Imran Afzal
myArray[6] = 50; // Error! Index 6 does not exist (valid range: 0-4)

This occurs when trying to access an index that does not exist in the array. Since arrays start from
index 0, an array of six elements has a valid index range of 0-5.

• Negative indices are not allowed in Java, though an element's value can be negative.

2. Looping Mistakes in Traditional for Loops

for (int i = 1; i < myArray.length; i++)

System.out.println(myArray[i]);

In this case, the loop starts from index 1, skipping the first element (index 0). To correctly iterate over
all elements, the loop should start at 0:

for (int i = 0; i < myArray.length; i++)

System.out.println(myArray[i]);

Arrays provide an efficient way to store and manipulate multiple values, but understanding their
indexing and common pitfalls is crucial to avoiding errors.

587
The Complete Java Training Imran Afzal
10.12- References and Value Types

10.12.1- Value Types and Reference Types in Java

In Java, variables can hold either a value type or a reference type. A value type variable stores the
actual value, whereas a reference type variable stores a reference to an object.

When working with arrays, whether they are value or reference types depends on the array's data type:

• An array of primitive types (such as int, double, or boolean) is a value type. The array
variable holds the actual values, and modifications to one variable do not affect others that
reference the same array.

• An array of objects (such as String or Object) is a reference type. The array variable holds a
reference to the object, and any changes made to it affect all references pointing to the same
object.

10.12.2- Example: Value Type Array

int[] values = new int[] {1, 2, 3, 4};

int[] copy = Arrays.copyOf(values, values.length);

values[0] = 5;

System.out.println(Arrays.toString(values)); // output: [5, 2, 3, 4]

System.out.println(Arrays.toString(copy)); // output: [1, 2, 3, 4]

int[] values = {1,2,3};

boolean values1 = {true, false};

Here, copy holds a reference to values, meaning that modifying values[0] also affects copy[0].

588
The Complete Java Training Imran Afzal
10.12.3- Example: Reference Type Array

String[] names = new String[] {"Alice", "Bob", "Charlie"};

String[] copy = names;

names[0] = "Dave";

System.out.println(Arrays.toString(names)); // output: ["Dave", "Bob", "Charlie"]

System.out.println(Arrays.toString(copy)); // output: ["Dave", "Bob", "Charlie"]

Both names and copy refer to the same array in memory, so changes to names affect copy.

10.12.4- Understanding References in Arrays

When an object (like an array) is assigned to a variable, the variable becomes a reference to that object.
This is especially important for arrays of objects, where each element is also a reference. The following
example shows how modifying the array through either reference (array1 or array2) affects the same
underlying data structure:

public static void main(String[] args) {

int[] array1 = new int[5];

int[] array2 = array1;

System.out.println("Array1 = " + Arrays.toString(array1));

System.out.println("Array2 = " + Arrays.toString(array2));

589
The Complete Java Training Imran Afzal
array2[0] = 1;

modify(array1);

System.out.println("After modifying Array1 = " + Arrays.toString(array1));

System.out.println("After modifying Array2 = " + Arrays.toString(array2));

private static void modify(int[] array) {

array[1] = 2;

• Both array1 and array2 refer to the same array object in memory.

• Changing array2[0] also changes array1[0].

• The modify() method takes the array reference as a parameter, so changing array[1] affects
both variables.

10.12.5- Explanation in Memory

• The statement int[] array1 = new int[5]; creates a new array in memory with five elements
initialized to 0.
• array2 = array1; does not create a new array; instead, it makes array2 point to the same
memory location.

590
The Complete Java Training Imran Afzal
• Changing array2[0] = 1; modifies the same memory block that array1 references.

10.12.6- Key Takeaways

• Primitive type arrays store values, while object arrays store references.

• Assigning one array variable to another does not create a new array, just another reference.

• Changes made to a reference-type array affect all references pointing to that array.

• The new keyword creates a new object in memory, while assignment (=) simply points to an
existing object.

Understanding this distinction between value types and reference types is crucial for handling arrays
efficiently in Java.

10.13- Runtime Arguments

In Java, the main method serves as the entry point of a program and can accept command-line
arguments as input. These arguments are passed as an array of strings.

The syntax for the main method with command-line arguments is:

public static void main(String[] args)

Here, args is an array of strings containing the command-line arguments. The length of this array
corresponds to the number of arguments passed, and each element holds a separate argument.

10.13.1- Example: Using Command-Line Arguments

public class MyClass {

public static void main(String[] args) {

System.out.println("Number of arguments: " + args.length);

for (int i = 0; i < args.length; i++) {

591
The Complete Java Training Imran Afzal
System.out.println("Argument " + i + ": " + args[i]);

If the program is executed with the following command:

java MyClass arg1 arg2 arg3

The output will be:

Number of arguments: 3

Argument 0: arg1

Argument 1: arg2

Argument 2: arg3

This demonstrates how to access and use command-line arguments in a Java program.

10.13.2- Alternative Syntax for the Main Method

The main method parameter is an array of strings, which means an array of values can be passed when
calling this method. Java also allows an alternative syntax using varargs, which enables passing a
variable number of arguments to the method.

10.13.3- Varargs Syntax


Instead of using the traditional square bracket array declaration, Java allows replacing String[] args
with String... args. This approach automatically packs the arguments into an array when the method is
called.

592
The Complete Java Training Imran Afzal
Example:

public class Arguments {

public static void main(String... args) {

System.out.println("Hello Nixware");

This syntax functions the same way as the traditional array format.

10.13.4- Creating a Method with Varargs

public class VarargsExample {

public static void printValues(String... values) {

for (String value : values) {

System.out.println(value);

public static void main(String[] args) {

printValues("World", "With", "Peace");

593
The Complete Java Training Imran Afzal
}

10.13.5- Explanation

1. Method Definition:

• public static void printValues(String... values) defines a method that accepts a variable number
of String arguments.

• These arguments are automatically packed into an array.

2. Looping Through Values:

• The for-each loop iterates through the values array and prints each argument separately.

3. Calling the Method:

• printValues("World", "With", "Peace"); passes three string values to the method.

• The loop iterates three times, printing each value on a new line.

Output

World

With

Peace

The varargs method can accept any number of arguments, including none.

10.14- Array Summary Exercise Part 1

10.14.1- Creating the Minimum Finder Program

To practice working with arrays, we create a program that reads a list of numbers from user input,
stores them in an array, and finds the minimum value. The program is structured with separate
methods to handle reading input and finding the minimum value.

Start by creating a class named MinimumFinder in the nixware.and.consulting package. Inside this
class, create the main method using public static void main(String[] args).

594
The Complete Java Training Imran Afzal
private static int[] readNumbers() {

Scanner scanner = new Scanner(System.in);

System.out.print("Enter a list of numbers, separated by commas: ");

String input = scanner.nextLine();

String[] splits = input.split(",");

int[] numbers = new int[splits.length];

for (int i = 0; i < splits.length; i++) {

numbers[i] = Integer.parseInt(splits[i].trim());

return numbers;

This method prompts the user to enter comma-separated numbers. The input string is split using the
split(",") method. An integer array numbers is created with the same length as the resulting split strings.
The for loop converts each trimmed string to an integer using Integer.parseInt() and stores it in the
numbers array.
595
The Complete Java Training Imran Afzal
10.14.3- Finding the Minimum Value

Define a method called findMinimum:

private static int findMinimum(int[] numbers) {

int minimum = Integer.MAX_VALUE;

for (int number : numbers) {

if (number < minimum) {

minimum = number;

return minimum;

This method initializes a variable minimum with the highest possible integer value. Then, it uses a for-
each loop to iterate through the numbers array. If an element is smaller than the current minimum, it
updates the minimum value. After the loop, it returns the smallest number.

10.14.4- Main Method Logic

In the main method, the following code executes the logic:

596
The Complete Java Training Imran Afzal
int[] numbers = readNumbers();

System.out.println(Arrays.toString(numbers));

int minimum = findMinimum(numbers);

System.out.println("The minimum number is " + minimum);

First, it calls readNumbers() to get the user's input as an integer array. Then, it prints the array using
Arrays.toString(). Next, it calls findMinimum() to determine the smallest number in the array and
prints the result.

10.14.5- Example Execution

When the user enters 10, 5, 20, 6, the program output will be:

[10, 5, 20, 6]

The minimum number is: 5

This structure helps break the problem into manageable parts using methods, arrays, and loops.

10.15- Array Summary Exercise Part 2

This program demonstrates how to reverse an array using custom methods in Java. It involves taking
input from the user to define the size and elements of the array, reversing the array, and printing the
reversed array.
10.15.1- Creating the Java Class and Main Method
A class named ReverseArray is created inside the nixware.and.consulting package. The main method
is defined using public static void main(String[] args).

597
The Complete Java Training Imran Afzal
10.15.2- Defining the inputArray Method
The method inputArray is defined with the signature:

public static int[] inputArray() {

Inside this method:


Scanner input = new Scanner(System.in);

A Scanner object is created to read user input.


System.out.print("Enter the size of the array: ");

int size = input.nextInt();

This prompts the user to enter the size of the array, which is stored in the size variable.
int[] myArray = new int[size];

An integer array myArray is created with a length equal to the input size.
A for loop is used to fill the array:

for (int i = 0; i < size; i++) {

System.out.print("Enter the element at index["+ i +"]: ");

myArray[i] = input.nextInt();

This loop prompts the user to enter each element of the array and stores it at the corresponding index.
return myArray;

}
The method returns the fully initialized array to the calling function.

598
The Complete Java Training Imran Afzal
10.15.3- Defining the reverseArray Method
The method reverseArray is defined with the signature:

public static int[] reverseArray(int[] myArray) {

Inside this method:


int[] reversedArray = new int[myArray.length];

A new integer array reversedArray is created with the same length as myArray.
A for loop is used to copy the elements from myArray into reversedArray in reverse order:

for (int i = 0; i < myArray.length; i++) {

reversedArray[i] = myArray[myArray.length - 1 - i];

This loop calculates the reverse index using myArray.length - 1 - i and assigns the
corresponding value from myArray to reversedArray.
return reversedArray;

}
The reversed array is returned to the main method.

10.15.4- Using the Methods in the Main Method


Inside the main method:
import java.util.Scanner;

public class ReverseArray {

599
The Complete Java Training Imran Afzal
public static void main(String[] args) {

int[] myArray = inputArray();

This line calls the inputArray method to get user input.


int[] reversedArray = reverseArray(myArray);

This line calls the reverseArray method to reverse the user-provided array.
System.out.print("Array in reverse Order: ");

This line prints the label for the reversed array output.
A for loop is used to print the elements of the reversed array:

for (int i = 0; i < reversedArray.length; i++) {

System.out.print(reversedArray[i] + " ");

This prints each element in the reversedArray separated by spaces.

10.15.5- Example Execution


If the user inputs a size of 3, and elements 13, 2, and 3, then the input array is [13, 2, 3].
The reverseArray method calculates the reverse as follows:

• i = 0: reversedArray[0] = myArray[2] = 3

• i = 1: reversedArray[1] = myArray[1] = 2

600
The Complete Java Training Imran Afzal
• i = 2: reversedArray[2] = myArray[0] = 13
The reversed array becomes [3, 2, 13] and is printed as: Array in reverse Order: 3 2 13

10.16- Introduction to Two Dimensional Arrays

10.16.1- Understanding Array Types

Arrays are data structures used to store multiple values of the same data type in a continuous block of
memory.

There are different types of arrays:

• 1D Arrays: Store values in a single row (linear format).

• 2D Arrays: Store values in rows and columns (matrix-like format).

• 3D Arrays and Beyond: Used for complex, multi-level data representations.

What is a 2D Array?

A 2D array is a collection of elements arranged in rows and columns. Each element is accessed using
two indices: one for the row and one for the column. This format is similar to a matrix used in
mathematics.

For example, in a 3x5 matrix:

• There are 3 rows and 5 columns.

• Each value resides in a "cell" like a spreadsheet.

• To locate a value in row 2, column 3, you write (2, 3).

In Java, such a structure is represented as a two-dimensional array.

Declaration of a 2D Array

To declare a 2D array in Java:

601
The Complete Java Training Imran Afzal
int[][] arr = new int[4][5];

Here, dataType defines the type of data the array holds, and rows and columns specify the dimensions.

Visualizing 2D Arrays

A 1D array can be visualized as a single row of elements:

int[] oneDArray = new int[5];

Visual:

[ _ _ _ _ _ ] // 5 elements in one row

A 2D array adds another dimension:

int[][] twoDArray = new int[4][5];

Visual:

[ _ _ _ _ _ ],

[ _ _ _ _ _ ],

[ _ _ _ _ _ ],

[_____]

This creates a matrix with 4 rows and 5 columns, making a total of 4 × 5 = 20 elements.

602
The Complete Java Training Imran Afzal
Calculating Total Elements and Memory

To find the total number of elements in a 2D array:

rows × columns

Example: A 4x5 array contains 4 * 5 = 20 elements.

To calculate memory (assuming each element is 4 bytes):

total elements × size of each element = 20 × 4 = 80 bytes

Initializing a 2D Array

Method 1: Inline Initialization

int[][] array = {

{1, 2, 3},

{4, 5, 6}

};

• This defines a 2-row, 3-column array.

• Indices start from 0: rows (0,1), columns (0,1,2)

Element Mapping:

• array[0][0] = 1

• array[0][1] = 2

• array[0][2] = 3

• array[1][0] = 4

• array[1][1] = 5

603
The Complete Java Training Imran Afzal
• array[1][2] = 6

Method 2: Clearer Row-wise Initialization

int[][] array = {

{1, 2, 3},

{4, 5, 6}

};

• Each inner array represents a row.

• Easier to visualize as a matrix.

Accessing 2D Array Elements

To access a specific element, use:

array[rowIndex][columnIndex];

Example:

array[0][1]; // Accesses element in 1st row, 2nd column

Correct Interpretation:
If the array was:

int[][] array = {

{1, 2, 3},

{4, 5, 6}

};

604
The Complete Java Training Imran Afzal
Then array[0][1] gives you 2.

Printing 2D Array Elements

Printing a 2D array requires nested loops:

• Outer loop runs through rows.

• Inner loop runs through columns in each row.

for (int i = 0; i < 2; i++) {

for (int j = 0; j < 3; j++) {

System.out.print(array[i][j] + " ");

This is correct for printing a 2x3 matrix, where:

int[][] array = {

{1, 2, 3},

{4, 5, 6}

};

Step-by-step execution of the nested loop:

1. i = 0, j = 0 → print array[0][0] → 1

2. j = 1 → print array[0][1] → 2

3. j = 2 → print array[0][2] → 3

4. i = 1, j = 0 → print array[1][0] → 4

605
The Complete Java Training Imran Afzal
5. j = 1 → print array[1][1] → 5

6. j = 2 → print array[1][2] → 6

Output:

123456

Summary of Key Concepts

• Declaration: Define the size and type of the array.

• Visualization: Understand 2D arrays as a matrix (rows × columns).

• Initialization: Use curly braces to assign values row-wise.

• Accessing: Use [row][column] indices.

• Printing: Use nested for loops to traverse each element.

10.16.2- Advantages of 2D Arrays

• Tabular Representation: Ideal for spreadsheets, game boards, etc.

• Efficient Access: Stored in contiguous memory for quick access.

• Easy Traversal: Nested loops allow easy iteration over elements.

• Flexible Sizing: Dimensions can be defined at runtime.

• Type Safety: Elements are strictly typed, reducing runtime errors.

10.16.3- Limitations of 2D Arrays

• Fixed Size: Size is static after creation.

• High Memory Use: Large arrays consume significant memory.

• Limited Flexibility: Difficult to dynamically insert/delete rows/columns.

• Index Complexity: Requires careful handling of row and column indices.

606
The Complete Java Training Imran Afzal
10.16.4- 1D vs 2D Arrays Comparison

Aspect 1D Array 2D Array

Structure Linear Tabular (matrix-like)

Element Access Single index Two indices (row and column)

Memory Less More (depends on dimensions)

Flexibility Easier to resize Fixed after creation

Code Complexity Simpler More complex (requires nested loops)

Usage Simple lists Tables, grids, matrices

Type Safety Yes Yes

10.17- Two Dimensional Arrays Exercise

10.17.1- Creating and Accessing a 2D Array

Begin by creating a class named TwoDArray in the nixware.consulting package. Inside this class,
declare and initialize a 2D array of integers as follows:

int[][] numbers = {

{1, 2, 3, 4},

{5, 6, 7, 8}

};

This 2D array has:

• 2 rows

• 4 columns

• A total of 8 elements

607
The Complete Java Training Imran Afzal
Accessing Specific Elements

System.out.print(numbers[0][0] + " ");

System.out.println(numbers[0][3]);

System.out.print(numbers[1][1] + " ");

System.out.println(numbers[1][2]);

This will print:

14

67

Printing the Entire 2D Array Using Nested Loops

Use nested loops to display the entire array in matrix format:

for (int i = 0; i < numbers.length; i++) {

for (int j = 0; j < numbers[i].length; j++) {

System.out.print(numbers[i][j] + " ");

System.out.println();

Output

1234

608
The Complete Java Training Imran Afzal
5678

10.17.2- Conway’s Game of Life – Implementation

Create a class named GameOfLife in the nixware.consulting package.

Class Declaration & Package

package nixware.consulting;

import java.util.Random;

public class GameOfLife {

start by declaring the class and importing Random for random grid generation.

Class Variables and Constants

private static int ROWS = 20;

private static int COLS = 20;

private static int[][] NEIGHBOR_OFFSETS = {

{-1, -1}, {-1, 0}, {-1, 1},

{0, -1}, {0, 1},

{1, -1}, {1, 0}, {1, 1}

};

private int[][] grid;

609
The Complete Java Training Imran Afzal
private Random random;

This section sets the dimensions and rules for neighbor detection in the Game of Life.

Constructor and Grid Initialization

public GameOfLife() {

grid = new int[ROWS][COLS];

random = new Random();

initializeGrid();

This constructor prepares the grid and fills it with random values using initializeGrid().

Initializing the Grid

private void initializeGrid() {

for (int row = 0; row < ROWS; row++) {

for (int col = 0; col < COLS; col++) {

grid[row][col] = random.nextInt(2); // 0 or 1

This method fills the grid with random live (1) or dead (0) cells.

610
The Complete Java Training Imran Afzal
Counting Live Neighbors

private int countLiveNeighbors(int row, int col) {

int liveNeighbors = 0;

for (int[] offset : NEIGHBOR_OFFSETS) {

int neighborRow = row + offset[0];

int neighborCol = col + offset[1];

if (neighborRow >= 0 && neighborRow < ROWS &&

neighborCol >= 0 && neighborCol < COLS &&

grid[neighborRow][neighborCol] == 1) {

liveNeighbors++;

return liveNeighbors;

This method counts the number of live neighbors around a given cell using the predefined offset
array.

611
The Complete Java Training Imran Afzal
Updating the Grid

public void update() {

int[][] newGrid = new int[ROWS][COLS];

for (int row = 0; row < ROWS; row++) {

for (int col = 0; col < COLS; col++) {

int liveNeighbors = countLiveNeighbors(row, col);

if (grid[row][col] == 1) {

if (liveNeighbors < 2 || liveNeighbors > 3) {

newGrid[row][col] = 0;

} else {

newGrid[row][col] = 1;

} else {

if (liveNeighbors == 3) {

newGrid[row][col] = 1;

612
The Complete Java Training Imran Afzal
} else {

newGrid[row][col] = 0;

grid = newGrid;

This implements Conway’s rules:

• Any live cell with <2 or >3 live neighbors dies.

• A dead cell with exactly 3 neighbors becomes alive.

Printing the Grid

public void printGrid() {

for (int row = 0; row < ROWS; row++) {

for (int col = 0; col < COLS; col++) {

if (grid[row][col] == 1) {

System.out.print("*");

} else {

613
The Complete Java Training Imran Afzal
System.out.print(".");

System.out.println();

Main Method

public static void main(String[] args) {

GameOfLife game = new GameOfLife();

for (int i = 0; i < 10; i++) {

game.printGrid();

game.update();

This runs the Game of Life for 10 generations, printing the grid before each update.

Output Example

*.*..*.*..*...

...**..*..*.*.

614
The Complete Java Training Imran Afzal
... (20 lines per generation)

• Live cells are shown as *

• Dead cells are shown as .

• Each generation shows a new grid based on the rules

10.18- Multi Dimensional Arrays

10.18.1- Visualizing Multi-Dimensional Arrays

A 2D array can be visualized as a matrix with rows and columns. For example, a 3×3 2D array looks
like this:

[_, _, _],

[_, _, _],

[_, _, _]

When we add another dimension, it becomes a 3D array - —a stack of 2D arrays (or matrices). A
3D array with dimensions [3][3][3] means:

• 3 layers (depth)

• Each layer contains 3 rows

• Each row contains 3 columns

• Total elements: 3 × 3 × 3 = 27

To access an element in a 3D array, use:

615
The Complete Java Training Imran Afzal
array[depth][row][column];

For instance, array[0][0][2] accesses the element in the first 2D array, first row, third column.

array[0][1][2]; // Refers to: 1st layer, 2nd row, 3rd column

10.18.2- Initializing a 3D Array

There are two methods to initialize a 3D array:

• Single-line Initialization: Assign all values in sequence. This is compact but hard to visualize.

• Structured Initialization: Use nested curly braces to clearly separate layers and rows:

int[][][] array = {

{1, 2, 3},

{4, 5, 6}

},

{7, 8, 9},

{10, 11, 12}

};

Each outer set of braces {} defines a layer (2D array), and each nested pair represents a row in that
layer.

616
The Complete Java Training Imran Afzal
10.18.3- Printing Elements of a 3D Array

To print a 3D array, we use three nested loops:

• Outer loop → layers (depth)

• Middle loop → rows

• Inner loop → columns

Example:

for (int i = 0; i < depth; i++) {

for (int j = 0; j < rows; j++) {

for (int k = 0; k < columns; k++) {

System.out.print(array[i][j][k] + " ");

System.out.println(); // End of row

System.out.println(); // End of layer

10.18.4- Simulating Loop Execution

Let’s understand how the nested loops work in 3D arrays:

• i tracks the current layer 2D layer

• j tracks the row within that layer

617
The Complete Java Training Imran Afzal
• k tracks the column in the current row

For every value of i:

• All j and k combinations are printed.

• A new line after every row, and a blank line after each layer.

10.18.5- Practical Example in IntelliJ

Create a class named ThreeDArrayExample in the nixware.consulting package.

This class:

• Declares a 3D array

• Fills it with values using a formula

• Prints all elements layer by layer

public class ThreeDArrayExample {

public static void main(String[] args) {

int[][][] threeDArray = new int[3][3][3];

// Assigning values to the 3D array using i + j + k

for (int i = 0; i < 3; i++) {

for (int j = 0; j < 3; j++) {

for (int k = 0; k < 3; k++) {

threeDArray[i][j][k] = i + j + k;

618
The Complete Java Training Imran Afzal
// Printing the 3D array layer by layer

for (int i = 0; i < 3; i++) {

for (int j = 0; j < 3; j++) {

for (int k = 0; k < 3; k++) {

System.out.print(threeDArray[i][j][k] + " ");

System.out.println(); // End of row

System.out.println(); // Separation between layers

10.18.6- Output Structure

After executing the program, the console displays the contents of the 3D array as blocks of 2D arrays.
Each layer is printed row by row, with a blank line separating each layer for clarity.

Here’s what the output looks like when the array is filled using i + j + k:

012

123

234

619
The Complete Java Training Imran Afzal
123

234

345

234

345

456

Each block represents a 2D layer of the 3D array. Each row contains 3 values (columns), and each
line of values is printed using nested loops. A blank line is added between layers to distinguish them
visually.

10.18.7- Explanation of Printed Values

Let’s understand where these printed numbers come from.

In your code, you filled the array with:

threeDArray[i][j][k] = i + j + k;

So each element is the sum of its depth index i, row index j, and column index k.

Breakdown

• threeDArray[0][0][0] = 0 + 0 + 0 = 0

• threeDArray[0][0][1] = 0 + 0 + 1 = 1

• threeDArray[0][0][2] = 0 + 0 + 2 = 2

620
The Complete Java Training Imran Afzal
• ...

• threeDArray[2][2][2] = 2 + 2 + 2 = 6

As a result:

• The first layer (i = 0) starts with lower values.

• The second layer (i = 1) adds 1 to all previous values.

• The third layer (i = 2) adds 2 to all previous values.

Each layer appears like a 2D matrix that increases progressively.

621
The Complete Java Training Imran Afzal
Chapter 11

ArrayList & LinkedList

622
The Complete Java Training Imran Afzal
11.1- Introduction to List

11.1.1- Arrays vs Lists


An array in Java allows storage of multiple items of the same type. It was a significant improvement
when fixed-size data collections were needed. However, arrays have limitations, especially when it
comes to resizing. Once the size is set, it cannot be changed. To overcome these limitations, Java
provides a powerful library called Collections, which includes data structures like lists that are much
more flexible.

11.1.2- Understanding the List Interface


A List in Java represents an ordered collection of elements. Unlike arrays, Lists are dynamic in size,
allowing elements to be added or removed at any time. This makes Lists ideal for situations where the
number of items changes frequently.

For instance, consider managing a user’s to-do list. A List can easily accommodate adding new tasks
or removing completed ones without needing to worry about size limitations.

11.1.3- How Lists Work in Java


A List is an interface, and several Java classes implement it, including:

• AbstractList

• ArrayList

• CopyOnWriteArrayList

• LinkedList

• Stack

Among these, ArrayList is one of the most widely used implementations. It functions like a resizable
array. Internally, an ArrayList maintains an array that's larger than needed to allow future additions. It
keeps track of two main aspects:

• Capacity: Total size of the internal array.

• Size: Number of actual elements stored.

623
The Complete Java Training Imran Afzal
When more elements are added than the current capacity, the ArrayList automatically increases its size
behind the scenes.

11.1.4- Practical Example


Imagine a class of 11 students. Initially, an array is created to store their roll numbers. Mid-year, two
new students join, but arrays can’t be resized. You’d need to create a new array, copy the old data, and
then add the new roll numbers — a process that's time-consuming and inefficient.

Using a List simplifies this. You can add the two new roll numbers directly, and the List resizes itself
automatically. Internally, the ArrayList creates a larger array, copies existing elements, and adds new
ones. If needed, it also shrinks the internal array when items are removed.

11.1.5- Memory Allocation in Heap


Objects like ArrayList and its internal array are stored in heap memory. Heap is where Java dynamically
allocates memory at runtime. It's divided into different regions such as the young generation and old
generation to manage object lifecycle and memory usage efficiently.

11.1.6- Major Operations of ArrayList

• Adding Elements:
Use the add() method to insert elements at the end of the list.
Example: myArrayList.add(1);

• Removing Elements:
Use remove(index) or remove(object) to delete elements.
This operation adjusts the size of the list accordingly.

• Getting Elements:
Use get(index) to retrieve an element from a specific position.

• Updating Elements:
Use set(index, value) to change the value at a given index.

• Iterating Over Elements:


Commonly performed using loops to process each element one by one.

624
The Complete Java Training Imran Afzal
11.1.7- ArrayList in Java Collections Framework
ArrayList belongs to the java.util package, a part of the Java Collections Framework. It provides an
efficient and flexible way to store and manipulate data. To use it, you must import it at the beginning
of your program:

import java.util.ArrayList;

11.1.8- Handling Primitive Data Types in ArrayList


Java’s ArrayList cannot directly store primitive data types like int or double. Instead, it stores objects.
Wrapper classes like Integer, Double, and Boolean are used to wrap primitive values so they can be
stored in an ArrayList.

11.1.9- Syntax and Initialization Example

To declare and initialize an ArrayList of integers:

ArrayList<Integer> myArrayList = new ArrayList<Integer>();

• ArrayList<Integer>: Declares a list to store Integer objects.

• myArrayList: Variable name.

• new ArrayList<Integer>(): Creates a new ArrayList with default capacity.

The default capacity is 10. You can also explicitly set the initial capacity:

ArrayList<Integer> myArrayList = new ArrayList<Integer>(10);

11.1.10- Adding Elements to the ArrayList

myArrayList.add(1);

myArrayList.add(2);

myArrayList.add(3);

625
The Complete Java Training Imran Afzal
After adding these, the ArrayList will contain [1, 2, 3].

11.2- Array List Exercise Part 1

11.2.1- Creating and Using ArrayLists with Different Data Types


To practice working with ArrayLists, consider a program that demonstrates the use of ArrayLists with
three different data types: Integer, Double, and Boolean. The program involves creating an ArrayList
of each type, adding values to them, and printing their contents.

11.2.2- Setting Up the Project


Create a new Java project named ArrayList. Inside it, create a package named nixware.consulting, and
add a class called ArrayListExample. Inside this class, define the main method.

import java.util.ArrayList;

public class ArrayListExample {

public static void main(String[] args) {

11.2.3- Using ArrayList with Integer Type

ArrayList<Integer> intList = new ArrayList<Integer>();

This statement creates a new ArrayList named intList to store elements of the Integer type. The
diamond operator <> helps infer the type automatically.

Adding elements to the list:

intList.add(10);

intList.add(20);

626
The Complete Java Training Imran Afzal
intList.add(30);

System.out.println("Integers: " + intList);

Output:

Integers: [10, 20, 30]

11.2.4- Using ArrayList with String Type

ArrayList<String> fruits = new ArrayList<String>();

This creates a new ArrayList called fruits that can store String elements.

Adding items to the list:

fruits.add("Apple");

fruits.add("Banana");

fruits.add("Orange");

fruits.add("Mango");

Displaying contents:

System.out.println("ArrayList before removing an element:");

System.out.println(fruits);

Output:

ArrayList before removing an element

[Apple, Banana, Orange, Mango]

627
The Complete Java Training Imran Afzal
11.2.5- Removing an Element

fruits.remove("Orange");

System.out.println("ArrayList after removing an element:");

System.out.println(fruits);

This removes "Orange" from the list.

Output:

ArrayList after removing an element

[Apple, Banana, Mango]

11.2.6- Accessing Elements

String firstFruit = fruits.get(0);

System.out.println("First fruit in the ArrayList: " + firstFruit);

Retrieves the first element in the list.

Output:

First fruit in the ArrayList: Apple

11.2.7- Updating Elements

fruits.set(1, "Kiwi");

628
The Complete Java Training Imran Afzal
System.out.println("ArrayList after updating an element:");

System.out.println(fruits);

Replaces the element at index 1 (which is "Banana") with "Kiwi".

Output:

ArrayList after updating an element

[Apple, Kiwi, Mango]

This example demonstrates how to use ArrayList in Java to add, remove, access, and update elements
of various data types using built-in methods such as add(), remove(), get(), and set().

11.3- Array List Exercise Part 2

11.3.1- Storing Student Information Using ArrayList

This example demonstrates how to create a program that stores student information such as name,
age, and courses using an ArrayList of custom Student objects. The courses for each student are stored
using a String array. The toString method is customized to display course lists as comma-separated
values.

11.3.2- Defining the Student Class

Create a new class named Student in the nixware.consulting package. Define the following instance
variables:

private String name;

629
The Complete Java Training Imran Afzal
private int age;

private String[] courses;

These variables represent the student's name, age, and the array of enrolled courses.

11.3.3- Constructor for Student Class

Add a constructor to initialize these variables:

public Student(String name, int age, String[] courses) {

this.name = name;

this.age = age;

this.courses = courses;

11.3.4- Overriding the toString Method

Generate and override the toString() method to print student details clearly. Use Arrays.toString() to
display the courses array in a readable format:

@Override

public String toString() {

return "Name: " + name + ", Age: " + age + ", Courses: " + Arrays.toString(courses);

630
The Complete Java Training Imran Afzal
This converts the array of courses into a string like [Math, Science, English].

11.3.5- Creating the SchoolApp Class

In the same file, create another class named SchoolApp, and define the main method:

class SchoolApp {

public static void main(String[] args) {

ArrayList<Student> students = new ArrayList<>();

Adding Student Data

Create arrays for each student's courses and add Student objects to the ArrayList:

String[] courses1 = {"Math", "Science", "English"};

students.add(new Student("John", 16, courses1));

String[] courses2 = {"History", "Geography"};

students.add(new Student("Alice", 15, courses2));

String[] courses3 = {"Art", "Music"};

students.add(new Student("Bob", 17, courses3));

Printing Student Records

Use a for-each loop to iterate through the students list and print each student's details:

631
The Complete Java Training Imran Afzal
System.out.println("Students:");

for (Student student : students) {

System.out.println(student);

Expected Output

The output displays each student’s name, age, and courses in a clear format:

Students:

Name: John, Age: 16, Courses: [Math, Science, English]

Name: Alice, Age: 15, Courses: [History, Geography]

Name: Bob, Age: 17, Courses: [Art, Music]

11.4- Array List Exercise Part 3

11.4.1- Student Grades Program Using ArrayList

This example demonstrates a Java program that allows a user to interactively manage student grades
using an ArrayList. The user can add grades, remove grades by index, view the average, and exit the
program via a menu-driven interface. Input handling and grade storage are managed using Java utilities
such as Scanner and ArrayList.

Import and Scanner Setup

import java.util.ArrayList;
632
The Complete Java Training Imran Afzal
import java.util.Scanner;

public class StudentGrades {

private static Scanner scanner = new Scanner(System.in);

A static Scanner object is used to read user input from the console.

Menu Display Method

private static void printActions() {

String textBlock = """

Available actions:

0 - to shutdown

1 - to add a grade

2 - to remove a grade by index

633
The Complete Java Training Imran Afzal
3 - to display the average grade

Enter a number for which action you want to do:""";

System.out.print(textBlock + " ");

This method prints a user-friendly menu outlining the available options.

Add Grade Method

private static void addGrade(ArrayList<Integer> grades) {

System.out.print("Enter the grade to add: ");

int grade = Integer.parseInt(scanner.nextLine());

grades.add(grade);

Prompts the user to enter a grade and appends it to the grades list.

Remove Grade by Index Method

private static void removeGrade(ArrayList<Integer> grades) {

634
The Complete Java Training Imran Afzal
System.out.print("Enter the index of the grade to remove: ");

int index = Integer.parseInt(scanner.nextLine());

grades.remove(index);

Allows the user to remove a grade by specifying its index in the grades list.

Display Average Grade Method

private static void displayAverage(ArrayList<Integer> grades) {

if (grades.size() == 0) {

System.out.print("No grades entered yet.");

} else {

int sum = 0;

for (int grade : grades) {

sum += grade;

double average = (double) sum / grades.size();

System.out.printf("Average grade: %.2f\n", average);

635
The Complete Java Training Imran Afzal
}

Calculates and displays the average of all grades currently stored in the grades list. If no grades have
been entered, it displays an appropriate message.

Main Method and Program Logic

public static void main(String[] args) {

ArrayList<Integer> grades = new ArrayList<>();

boolean flag = true;

while (flag) {

printActions();

int choice = Integer.parseInt(scanner.nextLine());

switch (choice) {

case 1 -> addGrade(grades);

case 2 -> removeGrade(grades);

case 3 -> displayAverage(grades);

default -> flag = false;

System.out.println(grades);

636
The Complete Java Training Imran Afzal
• Initializes an empty ArrayList named grades.

• A while loop runs until the user chooses to exit.

• Inside the loop, the program prompts the user for an action.

• Based on the user's choice, the program calls the corresponding method.

• After each action, the current state of the grades list is printed.

Sample Interaction

• User chooses 1 and adds a grade 85:


Output → [85]

• User chooses 1 again and adds 92:


Output → [85, 92]

• User adds another grade 78:


Output → [85, 92, 78]

• User chooses 3 to calculate the average:


Output → Average grade: 85.00
Current grades → [85, 92, 78]

• User chooses 0 to exit the program.

This implementation effectively demonstrates how to use an ArrayList to manage dynamic data entries
and operations in an interactive Java application.

11.5- Summary of Array and ArrayList

11.5.1- Difference Between Array and ArrayList


Arrays support primitive data types directly, while ArrayLists do not. Instead, ArrayLists work with
primitive wrapper classes such as Integer, Double, and Boolean. Both Arrays and ArrayLists are
indexed and maintain the order of elements based on their index. They allow duplicate and null values
and both inherit from java.lang.Object. Arrays are immutable in size, while ArrayLists are resizable.

637
The Complete Java Training Imran Afzal
11.5.2- Creating Arrays and ArrayLists

String[] myArray = new String[5];

ArrayList myArrayList = new ArrayList<>();

• String[] myArray creates an array of 5 strings.

• ArrayList creates a resizable list without predefined size.

Note: It’s recommended to use generics with ArrayList for type safety:

ArrayList<String> myArrayList = new ArrayList<>();

11.5.3- Initializing Arrays and ArrayLists


String[] myArray1 = new String[] {"Nixware", "Consulting", "Company"};

String[] myArray2 = {"Nixware", "Consulting", "Company"};

ArrayList<String> myArrayList = new ArrayList<>(List.of("Nixware", "Consulting", "Company"));

• Arrays can be initialized using curly braces {} either explicitly or anonymously.

• ArrayLists can be initialized using List.of(...) for a fixed-size list.

List.of(...) returns an immutable list. If you need a modifiable one:

new ArrayList<>(List.of(...))

11.5.4- Accessing Elements


Arrays:

System.out.println(myArray[0]);

int length = myArray.length;

638
The Complete Java Training Imran Afzal
ArrayLists:

System.out.println(myArrayList.get(0));

int size = myArrayList.size();

• Arrays use length, ArrayLists use .size().

• Access is via [index] for arrays and .get(index) for ArrayLists.

11.5.5- Printing Elements


Array:

System.out.println(Arrays.toString(myArray));

ArrayList:

System.out.println(myArrayList);

• Arrays must be printed using Arrays.toString(...).

• ArrayLists support direct printing using System.out.println(...).

11.5.6- Multidimensional Arrays and ArrayLists


String[][] twoDArray = {

{"Nixware", "Consulting", "Company"},

{"New York", "City"}

};

ArrayList<ArrayList<String>> multiDiaList = new ArrayList<>();

639
The Complete Java Training Imran Afzal
System.out.println(multiDiaList);

• Arrays of arrays → multidimensional arrays

• Nested ArrayLists → lists of lists

To print multidimensional arrays:

System.out.println(Arrays.deepToString(twoDArray));

To print nested ArrayLists:

System.out.println(multiDiaList);

11.5.7- Searching Elements


int index = Arrays.binarySearch(myArray, "Consulting");

boolean exists = myArrayList.contains("Consulting");

boolean allExist = myArrayList.containsAll(List.of("Nixware", "Consulting"));

int firstIndex = myArrayList.indexOf("Consulting");

int lastIndex = myArrayList.lastIndexOf("Consulting");

• Arrays.binarySearch() works only on sorted arrays.

• contains(), containsAll(), indexOf(), lastIndexOf() are built-in in ArrayList.

640
The Complete Java Training Imran Afzal
11.5.8- Sorting Elements
Code Example for Arrays:

String[] myArray = {"Nixware", "Consulting", "Company"};

Arrays.sort(myArray);

System.out.println(Arrays.toString(myArray));

Code Example for ArrayLists:

ArrayList<String> arrayList = new ArrayList<>(List.of("Nixware", "Consulting", "Company"));

arrayList.sort(Comparator.naturalOrder()); // Ascending

System.out.println(arrayList);

arrayList.sort(Comparator.reverseOrder()); // Descending

System.out.println(arrayList);

11.6- Introduction to Linked List in Relation to an Array and an ArrayList

11.6.1- Overview of LinkedList and Memory Storage


The LinkedList class in Java implements many of the same methods as the ArrayList, but its internal
structure and memory handling differ significantly. Understanding these differences starts with how
Java stores data for arrays, array lists, and linked lists.

Memory Allocation in Arrays


For arrays of primitive types, Java allocates memory contiguously. Each element is stored one after

641
The Complete Java Training Imran Afzal
the other in memory, which allows fast access using the index. For example, if the first element is
stored at memory address 101 and each integer takes 4 bytes, the next element is at 105, then 109, and
so on. Java calculates the address of any element using simple arithmetic based on the base address
and index.

For reference types like Strings or objects, the array stores memory addresses (references) rather than
actual values. These references are also stored contiguously.

10.6.2- ArrayList and Internal Implementation


ArrayList in Java is internally backed by an array. Although objects stored in an ArrayList are stored
contiguously in memory, inserting or deleting elements involves shifting references or reallocating a
larger array when capacity is exceeded. This makes insertion and deletion relatively expensive
operations when the list is large.

When an ArrayList is created, its capacity can be specified. For example, initializing an ArrayList with
capacity 7 means the internal array has space for seven elements. Adding elements beyond this capacity
forces Java to allocate a new, larger array and copy the existing elements—an operation that can be
expensive in terms of time and memory.

Example:

ArrayList myList = new ArrayList(7);

for (int i = 0; i < 5; i++) {

myList.add((i + 2) * 5);

}
642
The Complete Java Training Imran Afzal
myList.add(35);

myList.add(40);

myList.add(45);

10.6.3- Understanding Amortized Cost


While individual additions to an ArrayList are usually fast, resizing the internal array when capacity is
exceeded is costly. Java does not specify the growth policy, but it ensures that the average time per
add() operation remains constant. This is called amortized time complexity—typically O(1),
although occasional operations may cost O(n) during resizing.

10.6.4- Big O Notation and Operation Costs


Big O notation describes the performance of algorithms as the input size increases.

• O(1): Constant time – operation cost does not increase with input size.

• O(n): Linear time – cost increases directly with the number of elements.

• O(n²), O(2ⁿ): Quadratic or exponential time – cost increases rapidly as the input grows.

For example, traversing an entire list to find an element using the contains() method has a worst-case
complexity of O(n), because it may need to check every element.

10.6.5- LinkedList Structure


In contrast to arrays and array lists, a LinkedList is not indexed and does not store elements in
contiguous memory. Each element (called a node) holds a reference to the next and previous elements.
This forms a doubly linked list—a chain of elements linked in both directions.

643
The Complete Java Training Imran Afzal
• The start of the list is the head.

• The end is the tail.

• Because there's no index, retrieving an element requires traversal from the head or tail.

10.6.6- Retrieval vs. Insertion in LinkedList


Retrieving a specific element by index is more expensive in a linked list because it involves traversing
the chain one element at a time. For example, finding the fifth element requires visiting the first four.

However, inserting or removing elements is faster than in an ArrayList. It only involves updating a
few references (breaking and re-linking nodes). There is no need to shift elements or reallocate
memory.

10.6.7- Efficiency Trade-offs Between ArrayList and LinkedList

• ArrayList: Efficient for random access and reading elements. Costly for inserting and deleting
elements in the middle due to shifting and resizing.

• LinkedList: Efficient for inserting or deleting elements at the beginning or end. Less efficient
for retrieving elements due to traversal.

10.6.8- Choosing Between ArrayList and LinkedList

• Use an ArrayList when you mostly need to access or modify elements and know the number
of items in advance.

• Use a LinkedList when you frequently add or remove items, especially at the start or end,
and the number of items may vary greatly.

10.6.9- Analogy Example


Imagine a grocery list:

• If you just read and occasionally update items, use an ArrayList—like flipping through pages
of a book.

644
The Complete Java Training Imran Afzal
• If you're always adding or removing items, use a LinkedList—like stringing or unstringing
beads on a necklace.

10.6.10- Scalability Considerations


For a known maximum number of elements, an ArrayList with a fixed capacity is more efficient. If
the list size is unpredictable or could be large, a LinkedList may offer better performance for frequent
insertions and deletions.

11.7- Linked List Exercise Part 1

11.7.1- Purpose of the Exercise


This exercise demonstrates the performance difference between ArrayList and LinkedList in Java.

The task compares the time it takes to retrieve or remove elements from each list type, helping to
understand which list is more efficient in specific scenarios.

11.7.2- Architecture Review

LinkedList Components

• Nodes: The core building blocks, each holding a value and reference(s) to other nodes.

• Head: The first node in the list, used to start traversal.

• Tail: The last node in the list, useful for fast append operations.

• Size: Represents the number of nodes in the list.

• Operations: Includes insert, delete, and search operations.

LinkedLists come in several types:

• Singly Linked List: Nodes link to the next node only.

• Doubly Linked List: Nodes link to both next and previous nodes.

• Circular Linked List: The last node links back to the head.

645
The Complete Java Training Imran Afzal
ArrayList Architecture
ArrayList is backed by a dynamic array. It grows as elements are added. The default initial capacity is
10 unless specified. When full, a new array of double size is created and elements are copied over. On
element removal, shifting happens to maintain the order.

11.7.3- Step-by-Step Program Description

1. Creating the Class and Lists

A class named CombinedExample is created in the nixware.consulting package with the main method.

ArrayList<Integer> arrayList = new ArrayList<>();

LinkedList<Integer> linkedList = new LinkedList<>();

Both lists are initialized to hold Integer values.

2. Populating the Lists

for (int i = 0; i < 1000000; i++) {

arrayList.add(i);

linkedList.add(i);

Each list is filled with 1 million integers from 0 to 999999.

3. Retrieving the 500,000th Element and Measuring Time

long startTime = System.nanoTime();

int elementAL = arrayList.get(500000);

646
The Complete Java Training Imran Afzal
long endTime = System.nanoTime();

long durationAL = endTime - startTime;

startTime = System.nanoTime();

int elementLL = linkedList.get(500000);

endTime = System.nanoTime();

long durationLL = endTime - startTime;

Each retrieval is timed using System.nanoTime(), providing high-resolution time in nanoseconds.

4. Displaying Results

System.out.println("Element retrieved from ArrayList: " + elementAL);

System.out.println("Element retrieved from LinkedList: " + elementLL);

System.out.println("Time taken by ArrayList: " + durationAL + " nanoseconds");

System.out.println("Time taken by LinkedList: " + durationLL + " nanoseconds");

Results show:

• ArrayList: Fast due to direct index access (O(1))

• LinkedList: Slower due to traversal (O(n))

647
The Complete Java Training Imran Afzal
Performance Insight

Example Output:

• ArrayList time: 17500 nanoseconds

• LinkedList time: 13000800 nanoseconds

This illustrates how ArrayList is significantly faster for random access.

Demonstrating LinkedList Advantage

5. Removing the First Element

arrayList.remove(0);

linkedList.remove(0);

Removing the first element helps observe where LinkedList performs better.

• ArrayList: Needs to shift all elements left (O(n))

• LinkedList: Simply updates the head pointer (O(1))

6. Measuring Time for Removal

startTime = System.nanoTime();

linkedList.remove(0);

endTime = System.nanoTime();

long durationLL = endTime - startTime;

System.out.println("Time taken by ArrayList: " + durationAL + " nanoseconds");

648
The Complete Java Training Imran Afzal
System.out.println("Time taken by LinkedList: " + durationLL + " nanoseconds");

Example Output:

• ArrayList: 25417900 nanoseconds

• LinkedList: 37300 nanoseconds

This confirms LinkedList is more efficient when removing elements from the beginning.

11.8- Linked List Exercise Part 2

11.8.1- Overview of Singly and Doubly Linked Lists

A singly linked list is a linear data structure where each element (node) contains a value and a reference
to the next node in the sequence. The final node’s next reference is set to null, indicating the end of
the list. Common operations include inserting nodes at the end, deleting nodes, and traversing the list.
Since each node refers only to the next one, backward traversal is not possible.

In contrast, a doubly linked list allows traversal in both directions, as each node keeps references to
both the next and previous nodes. While this provides more flexibility, it increases memory usage and
requires more pointer adjustments when modifying the list.

11.8.2- Creating a Node Class

To manually implement a singly linked list, a Node class is defined with two fields: an integer value
and a reference to the next node. The constructor initializes the node’s value and sets the next pointer
to null.

public class Node {

649
The Complete Java Training Imran Afzal
int value;

Node next;

public Node(int value) {

this.value = value;

this.next = null;

Explanation

• value stores the data.

• next is a pointer to the next node.

• The constructor sets the value and initializes next to null, indicating no link yet.

11.8.3- Implementing the LinkedList Class

The LinkedList class manages the linked list, starting with a head reference to the first node. Initially,
the list is empty, so head is set to null.

class LinkedList {

Node head;

public LinkedList() {

650
The Complete Java Training Imran Afzal
this.head = null;

Explanation

• head points to the first node in the list.

• If head is null, the list is empty.

11.8.4- Inserting Nodes into the List

The insert method is responsible for adding a new node at the end of the list. A new node is created
with the given value. If head is null, the new node becomes the head. Otherwise, the list is traversed
from the head using a current node variable until the last node is found (where next is null). The new
node is then linked as the next node of the last element.

public void insert(int value) {

Node newNode = new Node(value);

if (head == null) {

head = newNode;

} else {

Node current = head;

while (current.next != null) {

current = current.next;

}
651
The Complete Java Training Imran Afzal
current.next = newNode;

11.8.5- Displaying the Linked List

The display method prints all elements of the list. Starting from the head, each node’s value is printed
followed by a space. The loop continues until the end of the list is reached. A newline is added after
displaying all values for clean output.

public void display() {

Node current = head;

while (current != null) {

System.out.print(current.value + " ");

current = current.next;

System.out.println();

11.8.6- Testing the Implementation

A main class is created to test the linked list functionality. A LinkedList object is instantiated. The
insert method is called three times to add the values 5, 10, and 15. The display method is used to
output the list: 5 10 15.

652
The Complete Java Training Imran Afzal
class Main {

public static void main(String[] args) {

LinkedList list = new LinkedList();

list.insert(5);

list.insert(10);

list.insert(15);

list.display(); // Output: 5 10 15

11.9- Introduction to Queue and Stack in Linked List

ArrayList is implemented using arrays, while LinkedList is implemented as a doubly linked list. Both
implement all standard list methods, but LinkedList also supports queue and stack operations.

11.9.1- Queue in Linked List

A queue is based on the First-In, First-Out (FIFO) principle. In a queue, elements are added at the
end and removed from the front. The methods used are enqueue to add an element to the back and
dequeue to remove an element from the front. A single-ended queue processes elements only from
the front, while a double-ended queue allows insertion and removal from both ends. A linked list
can serve as the underlying structure for a double-ended queue.

When implementing a queue using a linked list:

• The enqueue operation creates a new node and attaches it to the end of the list. If the list is
empty, the new node becomes the first node.

653
The Complete Java Training Imran Afzal
• The dequeue operation removes the node at the front by updating the head reference to the
next node and returns the removed value. If the list is empty, an appropriate error message is
returned.

Advantages

• The size of the queue is dynamic, allowing elements to be added or removed without
predefined size limitations.

• Enqueue and dequeue operations have O(1) time complexity, as only the front and rear
pointers need to be updated.

• Memory usage is efficient since it grows with the number of elements.

• The java.util.LinkedList class provides additional flexibility, such as easy modification, support
for different data types, and built-in methods for managing the structure without manually
handling the node-level logic.

11.9.2- Stack in Linked List

A stack, in contrast, follows the Last-In, First-Out (LIFO) principle. Elements are added and
removed from the top of the stack. This can be visualized as a pile where the most recently added
element is on top. Adding an element is referred to as a push, and removing it is referred to as a pop.
The term remove is preferred over get to emphasize the operation's effect of modifying the stack by
removing the top element rather than merely accessing it. Using get could cause confusion because it
implies accessing an element without altering the stack, which contradicts the LIFO behavior where
the top element must be removed when accessed.

11.9.3- Advantages of Stack in Linked List:

• Like in the queue, the stack implemented using a linked list allows dynamic sizing.

• Push and pop operations are performed in O(1) time since only the head of the list needs to
be updated.

• Memory usage remains proportional to the number of elements.

654
The Complete Java Training Imran Afzal
• The java.util.LinkedList class also supports stack-like operations and adds flexibility by
offering more built-in methods, iterators, and the ability to easily adjust the implementation as
needed. This simplifies development and enhances functionality without the overhead of
implementing the underlying structure manually.

11.10- Queue and Stack Exercise

11.10.1- Queue Implementation Using LinkedList

A Java program can be developed to implement a queue using a linked list. The Queue class includes
two main methods:

• enqueue, which adds a value to the end of the queue

• dequeue, which removes and returns the value from the front of the queue

If the queue is empty, it prints "Queue is empty" and returns -1. The class contains a private
LinkedList<Integer> instance for storing elements. This list is initialized in the constructor as a new
linked list.

import java.util.LinkedList;

class Queue {

private LinkedList<Integer> list;

public Queue() {

list = new LinkedList<Integer>();

public void enqueue(int value) {

list.addLast(value); // Adds value at the end

655
The Complete Java Training Imran Afzal
}

public int dequeue() {

if (list.isEmpty()) {

System.out.println("Queue is empty");

return -1;

} else {

return list.removeFirst(); // Removes value from the front

The enqueue method uses addLast(value) to add elements to the back of the queue, maintaining the
First-In-First-Out (FIFO) order. The dequeue method checks if the list is empty using isEmpty().
If it is empty, an error message is printed, and -1 is returned. Otherwise, the first element is removed
and returned using removeFirst().

11.10.2- Queue Execution Flow

In the main method, a Queue object is created. Three values—1, 2, and 3—are added using
the enqueue method.

class MainClass {

public static void main(String[] args) {

Queue queue = new Queue();

queue.enqueue(1);

656
The Complete Java Training Imran Afzal
queue.enqueue(2);

queue.enqueue(3);

System.out.println(queue.dequeue()); // 1

System.out.println(queue.dequeue()); // 2

System.out.println(queue.dequeue()); // 3

System.out.println(queue.dequeue()); // Queue is empty → -1

• Enqueue adds 1, 2, and 3.

• The first three dequeue() calls remove and print each value in order.

• The fourth call shows the behavior when the queue is empty.

11.10.3- Implementation Using LinkedList

To understand stack behavior using similar logic, a separate Stack class is created. The code from the
Queue class is reused with some changes. The constructor name is updated to match the new class.
Unlike queues, a stack follows Last-In-First-Out (LIFO) behavior, where the last element added is
the first to be removed.

import java.util.LinkedList;

class Stack {
657
The Complete Java Training Imran Afzal
private LinkedList<Integer> list;

public Stack() {

list = new LinkedList<Integer>();

public void push(int value) {

list.addLast(value); // Adds value at the end (top of stack)

public int pop() {

if (list.isEmpty()) {

System.out.println("Stack is empty");

return -1;

} else {

return list.removeLast(); // Removes from the end (top)

658
The Complete Java Training Imran Afzal
}

The method enqueue is renamed to push, and dequeue is renamed to pop. In the pop method,
removeLast() is used instead of removeFirst() to remove the last (topmost) element.

11.10.4- Stack Execution Flow

In this main() method, we test the Stack class. Values are added using a loop and then removed in
reverse order using another loop.

class MainClass {

public static void main(String[] args) {

Stack stack = new Stack();

// Push values: 10, 20, 30

for (int i = 1; i <= 3; i++) {

stack.push(i * 10);

// Pop values: 30, 20, 10, then try popping from empty stack

for (int i = 1; i <= 4; i++) {

659
The Complete Java Training Imran Afzal
System.out.println(stack.pop());

The first three pops return 30, 20, and 10. The fourth pop finds the stack empty, prints "Stack is
empty", and returns -1.

The queue example illustrates FIFO behavior using a linked list, while the stack example demonstrates
LIFO behavior. Both use the same data structure but differ in logic for adding and removing elements.

11.11- Introduction to Iterators

An iterator is a tool provided in Java to traverse elements in a collection, offering an alternative to


traditional for loops. While for loops use index values or the enhanced for-each loop to iterate through
collections, Java provides two interfaces specifically designed for traversal: Iterator and ListIterator.
These allow step-by-step access to each element without directly managing index values.

11.11.1- How an Iterator Works

An iterator functions like a cursor moving through a collection. This is similar to browsing books on
a shelf where each book represents an element in the collection. You begin at the first item and move
forward one by one using the iterator. The current position of the iterator points to the element being
accessed, and each call to its methods progresses the cursor through the structure.

11.11.2- Using an Iterator with Collections


To use an iterator, first call the iterator() method on a collection such as an ArrayList. Once an iterator
object is obtained, use hasNext() to check if more elements exist and next() to retrieve the next element
in the sequence. The next() method returns the current element and moves the iterator to the following
one. If next() is called when no elements are left, it throws an exception. The hasNext() method returns
true if elements remain and false otherwise.

660
The Complete Java Training Imran Afzal
11.11.3- Example: Iterating Over an ArrayList

A common use case for iterators is looping through a collection like an ArrayList. Below is an example
where we create a list of fruit names and use an Iterator to print each one:

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

public class IteratorExample {

public static void main(String[] args) {

List<String> myList = new ArrayList<>();

myList.add("apple");

myList.add("banana");

myList.add("orange");

Iterator<String> iterator = myList.iterator();

while (iterator.hasNext()) {

String element = iterator.next();

System.out.println(element);

661
The Complete Java Training Imran Afzal
}

• myList.iterator() returns an iterator for the list.

• hasNext() checks if there are more elements in the list.

• next() retrieves the current element and advances the iterator.

This loop continues until all elements are processed individually.

11.11.4- Advantages of Using Iterators

• The main benefits of using an iterator in Java include safe removal of elements during iteration
using the remove() method, which is not possible with a basic for loop.

• Iterators support multiple operations and work across different collection types like ArrayList
and LinkedList, enhancing code flexibility.

• They provide efficient, lazy evaluation by retrieving elements only when requested via next().

• The uniform interface of Iterator simplifies the process of writing generic code that can
operate on various collection types.

11.11.5- Limitations of Iterators

However, iterators have limitations:

• They offer only basic traversal capabilities and cannot modify the underlying collection
structure while iterating unless using special methods.

• They may introduce performance overhead due to frequent method calls. Modifying a
collection directly while iterating over it can result in a ConcurrentModificationException, so
proper synchronization or use of concurrent collections is necessary.

662
The Complete Java Training Imran Afzal
• Iterators are also not suitable for all structures, such as arrays, and their use can increase code
complexity in cases where simple iteration would suffice.

11.12- Linked List and Iterators Exercise

This program manages an itinerary of fruits using a LinkedList. It includes methods to add, remove,
retrieve, and print elements, while also demonstrating Queue, Stack, and iterator functionalities.

11.12.1- Adding Elements to the LinkedList

A LinkedList<String> named fruitsToVisit is created. Elements like "Apple" and "Banana" are added
using add() with and without index. To add more fruits, a method addMoreFruits is defined that uses
addFirst, addLast, offer, offerFirst, offerLast, and push to insert elements at different positions. These
methods insert items at the start, end, or treat the list as a stack to add elements on top.

var fruitsToVisit = new LinkedList<String>();

fruitsToVisit.add("Apple");

fruitsToVisit.add(0, "Banana");

System.out.println(fruitsToVisit);

addMoreFruits Method

private static void addMoreFruits(LinkedList<String> list) {

list.addFirst("Pineapple");

list.addLast("Mango");

663
The Complete Java Training Imran Afzal
// Queue methods

list.offer("Orange");

list.offerFirst("Papaya");

list.offerLast("Kiwi");

// Stack method

list.push("Grapes");

11.12.2- Removing Elements from the LinkedList

The removeFruits method uses various removal techniques: remove(int index) removes an element at
a specified index, remove(Object o) removes a specific value, remove() and removeFirst() delete the
first element, removeLast() deletes the last, poll(), pollFirst(), and pollLast() remove and return head
or tail elements based on the method called. These demonstrate both queue and stack removal
operations.

private static void removeFruits(LinkedList<String> list) {

list.remove(4);

list.remove("Papaya");

String s1 = list.remove();

664
The Complete Java Training Imran Afzal
String s2 = list.removeFirst();

String s3 = list.removeLast();

String p1 = list.poll();

String p2 = list.pollFirst();

String p3 = list.pollLast();

list.push("Apple");

list.push("Papaya");

list.push("Banana");

String p4 = list.pop();

11.12.3- Demonstrating Stack Behavior with push() and pop()

To re-add elements, push() is used to insert fruits like "Apple", "Papaya", and "Banana" at the top of
the list. The pop() method is used to remove and return the top element, confirming stack behavior
by showing Last-In-First-Out ordering.

list.push("Apple");

665
The Complete Java Training Imran Afzal
list.push("Papaya");

list.push("Banana");

String p4 = list.pop(); // Removes "Banana"

• Stack follows Last-In-First-Out (LIFO).

• push() adds items to the front (top).

• pop() removes the topmost item.

11.12.4- Retrieving Elements from the LinkedList

System.out.println("Retrieved Fruit = " + list.get(4));

System.out.println("First Fruit = " + list.getFirst());

System.out.println("Last Fruit = " + list.getLast());

System.out.println("Pineapple is at position: " + list.indexOf("Pineapple"));

System.out.println("Orange is at position: " + list.lastIndexOf("Orange"));

System.out.println("Element from element() = " + list.element());

• get(index), getFirst(), and getLast() retrieve elements by index or position.

• indexOf() and lastIndexOf() return element positions.

• element() returns the head (like a queue), without removal.

666
The Complete Java Training Imran Afzal
11.12.5- Viewing Elements Without Removal (Peek Methods)

Stack-related methods like peek(), peekFirst(), and peekLast() are also demonstrated to view elements
without removing them.

System.out.println("Element from peek() = " + list.peek());

System.out.println("Element from peekFirst() = " + list.peekFirst());

System.out.println("Element from peekLast() = " + list.peekLast());

11.12.6- Printing the Itinerary with a For Loop, Enhanced Loop and ListIterator

The printItinerary method uses getFirst() and getLast() to mark the start and end, and a traditional for
loop to display transitions between each pair of fruits.

public static void printItinerary(LinkedList<String> list) {

System.out.println("Starts Eating " + list.getFirst());

for (int i = 1; i < list.size(); i++) {

System.out.println("--> From: " + list.get(i - 1) + " to " + list.get(i));

System.out.println("Ends Eating " + list.getLast());

printItinerary2 demonstrates the same using an enhanced for loop. A variable previousFruit tracks the
prior item to show transitions.

public static void printItinerary2(LinkedList<String> list) {

667
The Complete Java Training Imran Afzal
System.out.println("Starts Eating " + list.getFirst());

String previousFruit = list.getFirst();

for (String fruit : list) {

System.out.println("--> From: " + previousFruit + " to " + fruit);

previousFruit = fruit;

System.out.println("Ends Eating " + list.getLast());

printItinerary3 replaces the for loop with a ListIterator.

public static void printItinerary3(LinkedList<String> list) {

System.out.println("Starts Eating " + list.getFirst());

String previousFruit = list.getFirst();

ListIterator<String> iterator = list.listIterator(1);

while (iterator.hasNext()) {

var fruit = iterator.next();

System.out.println("--> From: " + previousFruit + " to " + fruit);

previousFruit = fruit;

668
The Complete Java Training Imran Afzal
}

System.out.println("Ends Eating " + list.getLast());

The while loop and hasNext()/next() methods iterate through the list. Initially, it starts from the first
element, resulting in a redundant line from the first item to itself. This is improved by calling
listIterator(1) to begin iteration from index 1, avoiding the duplication and starting directly with the
second element.

This exercise shows how LinkedList can be manipulated using list, queue, stack, and iterator methods,
offering a rich set of functionalities for sequential data management in Java.

11.13- Introduction to Autoboxing and Unboxing

Let’s revisit primitive data types and their wrapper classes in Java, with a focus on how they relate to
collections like LinkedList and ArrayList. While some object-oriented languages treat everything as an
object, Java supports both primitive types and objects. Primitive types are closely tied to how data is
stored and processed in the operating system and offer performance advantages in terms of memory
and speed, especially when dealing with large datasets. Java allows mixing primitive types with objects,
but collections such as LinkedList and ArrayList cannot hold primitive types directly. Instead, they
require the use of wrapper classes.

11.13.1- Need for Wrapper Classes in Collections

Java provides wrapper classes like Integer, Double, and Character for each primitive type. These allow
primitive values to be treated as objects and stored in collections.

11.13.2- Boxing and Unboxing in Java

The conversion from a primitive to its wrapper class is called boxing, while the reverse is called
unboxing. The recommended way to manually box a primitive is using the valueOf() static factory

669
The Complete Java Training Imran Afzal
method provided by each wrapper class. Although earlier versions of Java used constructors like new
Integer(10), this approach is now deprecated due to its inefficiency.

11.13.3- Auto-boxing and Auto-unboxing

Auto-boxing simplifies the process by automatically converting a primitive to its wrapper class when
needed. For instance, assigning int x = 10 to an Integer variable automatically boxes the value.
Conversely, unboxing extracts the primitive value from its wrapper. This too can be done manually
using methods like intValue() or automatically when assigning a wrapper object to a primitive variable.

11.13.4- Examples of Boxing and Unboxing

An example of boxing and unboxing starts by using Integer.valueOf(10) to box a primitive value.
Another example creates a new Integer with new Integer(20) and then calls intValue() to unbox it.
With auto-boxing and unboxing, these conversions happen implicitly. For instance, assigning int y =
boxedInt2 automatically unboxes the value.

11.13.5- Difference Between Boxing and Casting

Boxing and unboxing are different from casting. While boxing involves converting between primitive
types and wrapper classes, casting is used to convert between different object types or between
primitive types of different kinds. Casting doesn’t convert between primitives and wrapper classes
directly.

11.13.6- Advantages of Boxing and Unboxing

Advantages of boxing and unboxing include convenience, support for generics, interoperability with
libraries that require objects, minimal performance overhead in most cases, and compatibility with
collections that require object types.

11.13.7- Disadvantages of Boxing and Unboxing

However, disadvantages include performance overhead due to object creation, increased memory
consumption, and the risk of incorrect comparisons when using == instead of equals() for wrapper
objects.

670
The Complete Java Training Imran Afzal
11.14- Auto Boxing and Un boxing Exercise

Let’s explore how Java automatically converts between primitive data types and their corresponding
wrapper classes using autoboxing and unboxing. It begins with examples using Integer and then
explores a custom class Fraction to demonstrate conversion and method handling with primitive and
object types.

11.14.1- Auto Boxing and Unboxing with Integer

Autoboxing is the process of converting a primitive value (like int) into its wrapper object (like
Integer), while unboxing is the reverse. Java handles these conversions automatically when needed.

public class AutoboxingExample {

public static void main(String[] args) {

// autoboxing - converting int to Integer

int i = 10;

Integer integerObj = i;

System.out.println("The value of integerObj: " + integerObj);

// unboxing - converting Integer to int

Integer integerObj2 = new Integer(20);

int j = integerObj2;

671
The Complete Java Training Imran Afzal
System.out.println("The value of j: " + j);

Output:

The value of integerObj: 10

The value of j: 20

In this example, i is automatically boxed into an Integer object integerObj. Then, integerObj2 is
explicitly created with a constructor and automatically unboxed when assigned to the int variable j.

11.14.2- Using a Custom Fraction Class

This part demonstrates how a custom object can store values and return primitive types using method
calls, mimicking unboxing. The Fraction class models a simple mathematical fraction.

class Fraction {

private int numerator;

private int denominator;

public Fraction(int numerator, int denominator) {

this.numerator = numerator;

this.denominator = denominator;

672
The Complete Java Training Imran Afzal
}

@Override

public String toString() {

return numerator + "/" + denominator;

public double doubleValue() {

return (double) numerator / denominator;

class MainClass {

public static void main(String[] args) {

// autoboxing - converting int to Integer

int numerator = 5;

int denominator = 2;

673
The Complete Java Training Imran Afzal
Fraction fractionObj = new Fraction(numerator, denominator); // create Fraction object

System.out.println("The value of fractionObj: " + fractionObj);

// unboxing - converting Fraction to double

double decimalValue = fractionObj.doubleValue();

System.out.println("The decimal value of fractionObj: " + decimalValue);

Output:

The value of fractionObj: 5/2

The decimal value of fractionObj: 2.5

Here, numerator and denominator are primitive integers, passed to the Fraction constructor. The
toString() method outputs the fraction format, while doubleValue() returns the decimal representation,
demonstrating unboxing logic.

11.15- Introduction to the Enumeration

Java provides a special data type called enum to represent a fixed set of named constants. Unlike lists
and arrays where elements can change at runtime, enums define a constant group of values that cannot
be modified. These values are known at compile time and are used when a variable must hold one of
only a few possible values.

674
The Complete Java Training Imran Afzal
11.15.1- What is an Enum?
Enums are declared using the enum keyword. Each enum constant is a static final instance of the
enum type, and by convention, their names are written in uppercase. Enums offer type safety and can
replace integer or string constants in code, making the application more readable and maintainable.

enum EnumName {

CONSTANT1,

CONSTANT2,

// ...

CONSTANTN

An enum is similar to a class and can include fields, constructors, and methods. However, enums are
immutable, meaning once the constants are defined, they cannot be changed. For example, to
represent animals in a zoo, you could create an enum with constants like LION, TIGER, BEAR,
ELEPHANT, and GIRAFFE. Similarly, months of the year can be represented using constants like
JANUARY, FEBRUARY, and so on. These are ideal cases for enum usage where the possible values
are well-defined and unlikely to change.

enum Animal {

LION,

TIGER,

BEAR,

ELEPHANT,

GIRAFFE

675
The Complete Java Training Imran Afzal
11.15.2- Benefits of Using Enums

• Enums provide advantages such as type safety, improved code readability, easy maintenance,
simpler syntax, and compatibility with switch statements.

• With enums, variables can only hold predefined values, which helps prevent logical errors in
programs.

• They also make code self-documenting by using meaningful names instead of arbitrary integers
or strings.

11.15.3- Limitations of Enums

Despite these benefits, enums have some limitations:

• They are not flexible for dynamic values, they can consume more memory compared to
primitive constants, and they cannot be extended or modified once defined.

• In situations where the set of constants may evolve, enums might require changes throughout
the codebase, making them less adaptable.

• Overuse of enums can also lead to overly rigid or cluttered code when simpler alternatives
may be more appropriate.

11.15.4- Enum Syntax and Basic Example

The syntax of an enum involves listing constants separated by commas within an enum declaration.
For example:

enum Animal {

LION, TIGER, BEAR

To access a specific constant, you use dot notation like Animal.LION.

Enums can also contain fields, constructors, and methods. For example:

676
The Complete Java Training Imran Afzal
enum Animal {

DOG("Fido", 4),

CAT("Fluffy", 4),

BIRD("Tweety", 2);

private String name;

private int numLegs;

Animal(String name, int numLegs) {

this.name = name;

this.numLegs = numLegs;

public String getName() {

return name;

677
The Complete Java Training Imran Afzal
public int getNumLegs() {

return numLegs;

In this example, each enum constant represents an animal with a name and a number of legs. The
enum has a constructor to initialize these fields and getter methods to retrieve them. You can call
methods like getName() and getNumLegs() on enum constants to get their properties, such as
Animal.DOG.getName() which returns "Fido".

Enums in Java are powerful tools for grouping related constants and providing additional structure
and behavior where needed. They are widely used in applications where controlled, predefined values
are essential.

11.16- Enumeration Exercise

A Java enumeration (enum) allows you to define a fixed set of named constants that can be used in
place of primitive or string values.

11.16.1- Declaring an Enum for Months

Enums in Java are declared using the enum keyword. Each enum constant is implicitly public, static,
and final. Enums are ideal for representing a fixed set of constants such as months of the year.

// Months.java

public enum Months {

JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC

678
The Complete Java Training Imran Afzal
This declaration defines an enum named Months with twelve constants representing the months of
the year. These constants follow the naming convention of being uppercase and do not require
semicolons unless fields or methods are added to the enum.

11.16.2- Using Enum in MainClass

To demonstrate how to use an enum, a MainClass is created. It accesses and prints enum values using
a loop and a custom method that selects a random month.

// MainClass.java

package nixware.consulting;

import java.util.Random;

public class MainClass {

public static void main(String[] args) {

Months monthOfYear = Months.JAN;

System.out.println(monthOfYear);

for (int i = 0; i < 10; i++ ) {

monthOfYear = getRandomMonth();

System.out.printf("Name is %s, Ordinal Value = %d%n",

679
The Complete Java Training Imran Afzal
monthOfYear.name(), monthOfYear.ordinal());

if (monthOfYear == Months.JUN) {

System.out.println("Found the month of June!!!");

public static Months getRandomMonth() {

int randomInteger = new Random().nextInt(12);

var allMonths = Months.values();

return allMonths[randomInteger];

• The name() method returns the name of the enum constant.

• The ordinal() method returns its position in the declaration (starting from 0).

• The method getRandomMonth() returns a randomly selected month from the enum by using
Random().nextInt() and indexing into the array returned by Months.values().

680
The Complete Java Training Imran Afzal
11.16.3- Defining an Enum Inside a Class

Java allows enums to be defined within classes as nested types. This helps to scope them appropriately
and prevent naming conflicts.

// Animal.java

package nixware.consulting;

public class Animal {

public enum AnimalType {

DOG, CAT, BIRD, FISH

public static void main(String[] args) {

AnimalType animal = AnimalType.DOG;

switch (animal) {

case DOG -> System.out.println("The dog barks.");

case CAT -> System.out.println("The cat meows.");

case BIRD -> System.out.println("The bird sings.");

681
The Complete Java Training Imran Afzal
case FISH -> System.out.println("The fish swims.");

default -> System.out.println("Unknown animal type.");

• The enum AnimalType defines four constants representing types of animals.

• A switch statement is used to check the value of the animal variable and execute different
blocks based on the matched case.

• This approach provides clear, readable control logic based on fixed constant values.

Enums in Java provide a powerful and readable way to define and manage constant sets. They support
iteration, type safety, and integration with control structures like switch statements, enhancing both
code clarity and maintainability.

682
The Complete Java Training Imran Afzal
Chapter 12

Abstract Classes & Interfaces

683
The Complete Java Training Imran Afzal
12.1- Introduction to Abstraction

Abstraction and generalization are techniques in Java that minimize code duplication while enhancing
flexibility. These concepts support the development of extensible applications, those that are easy to
modify or extend in the future with minimal changes.

12.1.1- Modeling Real-World Entities with Abstraction

To model real-world entities efficiently, a base class is created to capture shared characteristics among
related types. For example, a Vehicle represents the general concept of transportation. A car or
motorcycle has specific behaviors, but both share core features such as movement and engines.

abstract class Vehicle {}

Here, Vehicle is declared as an abstract class. It cannot be instantiated directly but serves as a blueprint
for more specific types like Car or Motorcycle.

12.1.2- Java Mechanisms for Abstraction

Java enables abstraction through abstract classes and interfaces. An abstract class may include methods
without implementations, known as abstract methods, which must be implemented by subclasses.

abstract class Vehicle {

public void startEngine();

This abstract class includes an abstract method startEngine(). It only defines the method signature,
leaving the actual implementation to the subclasses. The purpose is to enforce that all vehicles must
define how their engines start, even if they differ in behavior.

Note: This code will result in a compilation error. In Java, abstract methods must use the abstract
keyword and must not have a method body. The correct form is:

684
The Complete Java Training Imran Afzal
abstract class Vehicle {

public abstract void startEngine();

12.1.3- Example: Shape Abstraction and Polymorphism

Consider a Shape abstraction. While a Rectangle, Circle, and Triangle differ in behavior, they all can
calculate area and perimeter. These behaviors can be declared as abstract methods within an abstract
Shape class. Subclasses would then provide specific implementations. This supports polymorphism—
enabling a single interface to handle different shapes appropriately.

12.1.4- Abstract vs Concrete Methods

Abstract classes serve as a base from which other classes inherit. While abstract methods define
required behavior without implementation, concrete subclasses must provide the implementation.

class Car extends Vehicle {

@Override

public void startEngine() {

System.out.println("Car engine started.");

class Motorcycle extends Vehicle {

685
The Complete Java Training Imran Afzal
@Override

public void startEngine() {

System.out.println("Motorcycle engine started.");

Both Car and Motorcycle extend the abstract class Vehicle and provide specific implementations of
the startEngine() method. This shows how concrete subclasses implement abstract behavior
differently, depending on their context.

Attempting to create an instance of the abstract class directly will result in a compilation error:

Vehicle v = new Vehicle(); // Error: Cannot instantiate abstract class

This line is invalid because abstract classes cannot be instantiated. You can only create instances of
concrete subclasses like Car or Motorcycle.

12.1.5- Importance of Implementing Abstract Methods

Declaring an abstract method in a class or interface ensures that all subtypes implement that method.
This guarantees that code relying on the abstract type can invoke the method, confident that an
implementation exists. This principle also applies to concrete methods that are overridden.

12.1.6- Advantages of Abstract Classes in Java

• Reusability: Abstract classes allow shared fields and methods across multiple subclasses.

• Encapsulation: Implementation details can be hidden while exposing the intended behaviors.

• Polymorphism: Abstract classes enable polymorphic behavior when working with related
types.

686
The Complete Java Training Imran Afzal
• Flexibility: They support a design where required behaviors are enforced, but implementation
can vary.

• Future-proofing: Abstract classes act as contracts, ensuring consistency for future subclass
implementations.

12.1.7- Limitations of Abstract Classes in Java

• Cannot be instantiated: While this enforces their design purpose, it also limits direct use.

• Tight coupling: Subclasses depend closely on the abstract class, so changes can break them.

• Inflexible hierarchy: Adding new abstract methods forces all existing subclasses to be
updated.

• Limited reusability: Java supports only single inheritance for classes, limiting shared abstract
functionality.

• Difficult to test: Since abstract classes can't be instantiated, testing them requires concrete
subclasses.

12.2- Abstraction Exercise Part 1

12.2.1- Creating the Abstract Vehicle Class

Create a program to demonstrate abstraction using an abstract base class Vehicle and two concrete
subclasses Car and Bike.

Start by creating a project named VehicleStarter and a package nixware.consulting. Inside this package,
create an abstract class named Vehicle.

abstract class Vehicle {

protected String model;

687
The Complete Java Training Imran Afzal
public Vehicle(String model) {

this.model = model;

public abstract void start();

public void stop() {

System.out.println(model + " has stopped.");

The Vehicle class contains a protected field model, an abstract method start(), and a concrete method
stop() that prints a message. Since it's abstract, it cannot be instantiated directly.

12.2.2- Implementing the Car Subclass

Next, create a Car class that extends Vehicle.

class Car extends Vehicle {

private int numDoors;

public Car(String model, int numDoors) {

688
The Complete Java Training Imran Afzal
super(model);

this.numDoors = numDoors;

public void start() {

System.out.println(model + " with " + numDoors + " doors has started.");

The Car class provides its own implementation of the start() method and includes an additional
numDoors field. The constructor uses super(model) to initialize the inherited model field.

12.2.3- Implementing the Bike Subclass

Then, create a Bike class that also extends Vehicle.

class Bike extends Vehicle {

private boolean isElectric;

public Bike(String model, boolean isElectric) {

super(model);

this.isElectric = isElectric;

689
The Complete Java Training Imran Afzal
}

public void start() {

System.out.println(model + " with " + (isElectric ? "electric" : "petrol") + " engine has started.");

The Bike class has an isElectric field and its own implementation of start(), using a ternary operator
to print engine type.

12.2.4- Testing with the MainClass

Now create a MainClass with the main method to test both classes.

public class AbstractionExample {

public static void main(String[] args) {

Car car = new Car("Toyota Corolla", 4);

Bike bike = new Bike("Hero Splendor", true);

car.start();

bike.start();

690
The Complete Java Training Imran Afzal
car.stop();

bike.stop();

This code creates a Car and a Bike object, calls their start() and stop() methods, and prints output to
the console.

The output is:

Toyota Corolla with 4 doors has started.

Hero Splendor with electric engine has started.

Toyota Corolla has stopped.

Hero Splendor has stopped.

This program effectively demonstrates abstraction by using an abstract class with shared behavior
(stop()), abstract behavior (start()), and subclass-specific implementations.

12.3- Abstraction Exercise Part 2

12.3.1- Library Catalog System – Java Abstraction Exercise

This program simulates a library system using Java abstraction concepts. The goal is to apply abstract
classes and interfaces in a practical way to build a structured library catalog system.

12.3.2- Defining the Abstract Item Class

To begin, create a class named Item and mark it as abstract using the abstract keyword. This class acts
as a blueprint for all item types in the library catalog.

691
The Complete Java Training Imran Afzal
public abstract class Item {

protected String title;

protected String type;

public Item(String title, String type) {

this.title = title;

this.type = type;

public abstract void display();

public abstract void borrow();

public abstract void returnItem();

The Item class contains two protected fields: title and type. It also defines three abstract methods that
every concrete subclass must implement: display(), borrow(), and returnItem().

12.3.4- Creating Concrete Subclass: Book

Now create a class named Book that extends the abstract class Item. This class implements all three
abstract methods defined in Item.

692
The Complete Java Training Imran Afzal
class Book extends Item {

public Book(String title, String type) {

super(title, type);

public void display() {

System.out.println("Type: " + type);

System.out.println("Title: " + title);

public void borrow() {

System.out.println("Borrowing book: " + title);

public void returnItem() {

System.out.println("Returning book: " + title);

693
The Complete Java Training Imran Afzal
}

The Book class includes its own constructor and overrides the methods to display, borrow, and return
a book.

12.3.5- Creating Concrete Subclass: DVD

Create another subclass of Item named DVD, following the same structure as Book.

class DVD extends Item {

public DVD(String title, String type) {

super(title, type);

public void display() {

System.out.println("Type: " + type);

System.out.println("Title: " + title);

public void borrow() {

System.out.println("Borrowing DVD: " + title);

694
The Complete Java Training Imran Afzal
public void returnItem() {

System.out.println("Returning DVD: " + title);

The DVD class implements custom behavior for DVD items. It reuses the inherited fields title and
type.

12.3.6- Defining the Abstract Person Class

Create another abstract class named Person to model the people involved in the library system.

abstract class Person {

protected String name;

protected int age;

public Person(String name, int age) {

this.name = name;

this.age = age;

695
The Complete Java Training Imran Afzal
public abstract void display();

public abstract void borrow(Item item);

public abstract void returnItem(Item item);

This class provides a structure with abstract methods for person-specific behaviors and includes
protected fields name and age.

12.3.7- Creating Concrete Subclass: Member

Create a Member class that extends Person and implements all abstract methods.

class Member extends Person {

private ArrayList<Item> borrowedItems;

public Member(String name, int age) {

super(name, age);

borrowedItems = new ArrayList<Item>();

public void display() {

System.out.println("Member: " + name + " (" + age + ")");

696
The Complete Java Training Imran Afzal
}

public void borrow(Item item) {

if (!borrowedItems.contains(item)) {

System.out.println("Borrowing item: " + item.title);

item.borrow();

borrowedItems.add(item);

} else {

System.out.println("Item " + item.title + " is already borrowed.");

public void returnItem(Item item) {

if (borrowedItems.contains(item)) {

System.out.println("Returning item: " + item.title);

item.returnItem();

borrowedItems.remove(item);

697
The Complete Java Training Imran Afzal
} else {

System.out.println("Item " + item.title + " is not borrowed.");

The Member class includes a list of borrowed items and provides logic to borrow and return them,
ensuring no duplicates.

12.3.8- Library Catalog System: Main Method

Now implement the main execution logic in a class named MainClass.

public class MainClass {

public static void main(String[] args) {

ArrayList<Person> members = new ArrayList<Person>();

members.add(new Member("John Doe", 25));

ArrayList<Item> items = new ArrayList<Item>();

items.add(new Book("The Great Gatsby", "Book"));

items.add(new DVD("The Shawshank Redemption", "DVD"));

698
The Complete Java Training Imran Afzal
members.add(new Member("Jane Smith", 30));

items.add(new Book("To Kill a Mockingbird", "Novel"));

items.add(new DVD("The Godfather", "Blu-ray"));

int i = 0;

for (Person member : members) {

member.display();

for (Item item : items) {

if (i == 2) {

i = 0;

break;

item.display();

i++;

699
The Complete Java Training Imran Afzal
}

This loop structure ensures that each member sees a pair of items. The loop uses a counter to control
the number of items shown per person.

This complete abstraction example showcases how abstract classes and methods allow us to define a
shared structure for multiple types of objects. Each subclass provides its specific implementation while
reusing a consistent interface, enhancing maintainability and scalability.

12.4- Abstraction Exercise Part 3

12.4.1- Banking System – Java Abstraction Exercise

This program simulates a simple banking system using Java’s abstraction concept. It includes abstract
and concrete classes, transaction handling, and account management through a central Banking class.

Creating the Abstract Account Class

The Account class serves as the base abstract class for all types of bank accounts. It defines common
attributes and behaviors shared across savings and checking accounts.

public abstract class Account {

private String name;

private String accountNumber;

private double balance;

public Account(String name, String accountNumber, double balance) {

700
The Complete Java Training Imran Afzal
this.name = name;

this.accountNumber = accountNumber;

this.balance = balance;

public String getName() {

return name;

public String getAccountNumber() {

return accountNumber;

public double getBalance() {

return balance;

701
The Complete Java Training Imran Afzal
public void setBalance(double balance) {

this.balance = balance;

public abstract void deposit(double amount);

public abstract void withdraw(double amount);

public abstract void display();

This class includes private fields, getter and setter methods, and three abstract methods: deposit(),
withdraw(), and display(). These must be implemented in all subclasses.

Implementing the SavingsAccount Class

The SavingsAccount class extends Account and includes logic specific to savings accounts, such as
rejecting withdrawals that lead to a negative balance.

class SavingsAccount extends Account {

public SavingsAccount(String name, String accountNumber, double balance) {

super(name, accountNumber, balance);

702
The Complete Java Training Imran Afzal
@Override

public void deposit(double amount) {

setBalance(getBalance() + amount);

@Override

public void withdraw(double amount) {

if (getBalance() - amount < 0) {

System.out.println("Insufficient funds");

} else {

setBalance(getBalance() - amount);

@Override

public void display() {

703
The Complete Java Training Imran Afzal
System.out.println("Savings account details:");

System.out.println("Name: " + getName());

System.out.println("Account number: " + getAccountNumber());

System.out.println("Balance: " + getBalance());

This class uses the inherited methods to manipulate the balance and display account details, enforcing
a zero-balance rule.

Implementing the CheckingAccount Class

The CheckingAccount class allows an overdraft of up to -500. It extends the base Account class and
implements custom withdrawal logic.

class CheckingAccount extends Account {

public CheckingAccount(String name, String accountNumber, double balance) {

super(name, accountNumber, balance);

@Override

704
The Complete Java Training Imran Afzal
public void deposit(double amount) {

setBalance(getBalance() + amount);

@Override

public void withdraw(double amount) {

if (getBalance() - amount < -500) {

System.out.println("Overdraft limit reached");

} else {

setBalance(getBalance() - amount);

@Override

public void display() {

System.out.println("Checking account details:");

System.out.println("Name: " + getName());

705
The Complete Java Training Imran Afzal
System.out.println("Account number: " + getAccountNumber());

System.out.println("Balance: " + getBalance());

This class overrides the withdraw() method to allow limited overdrafts and displays account
information accordingly.

Creating the Transaction Class

Each transaction is represented by the Transaction class, which captures the amount, date, and account
details.

import java.util.Date;

class Transaction {

private double amount;

private Date date;

private Account account;

public Transaction(double amount, Date date, Account account) {

this.amount = amount;

706
The Complete Java Training Imran Afzal
this.date = date;

this.account = account;

public void display() {

System.out.println("Transaction details:");

System.out.println("Amount: " + amount);

System.out.println("Date: " + date);

System.out.println("Account name: " + account.getName());

System.out.println("Account number: " + account.getAccountNumber());

System.out.println("New balance: " + account.getBalance());

The display() method outputs the transaction data including account and date information.

Creating the Banking Class

The Banking class manages accounts and performs transactions by instantiating the Transaction class.

import java.util.ArrayList;

707
The Complete Java Training Imran Afzal
import java.util.Date;

import java.util.List;

class Banking {

private List<Account> accounts;

public Banking() {

accounts = new ArrayList<>();

public void addAccount(Account account) {

accounts.add(account);

public void removeAccount(Account account) {

accounts.remove(account);

708
The Complete Java Training Imran Afzal
public void processTransaction(Account account, double amount) {

Date date = new Date();

Transaction transaction = new Transaction(amount, date, account);

transaction.display();

This class provides methods to add, remove, and process transactions for accounts, encapsulating
business logic related to banking operations.

Main Execution: MainClass

Finally, the main class creates instances of all necessary objects, performs transactions, displays results,
and simulates a basic banking workflow.

public class MainClass {

public static void main(String[] args) {

Banking banking = new Banking();

SavingsAccount savingsAccount = new SavingsAccount("John Smith", "123456789", 1000.0);

CheckingAccount checkingAccount = new CheckingAccount("Jane Doe", "987654321", 500.0);

709
The Complete Java Training Imran Afzal
banking.addAccount(savingsAccount);

banking.addAccount(checkingAccount);

banking.processTransaction(savingsAccount, 500.0);

banking.processTransaction(checkingAccount, 1000.0);

savingsAccount.display();

checkingAccount.display();

banking.removeAccount(savingsAccount);

This simulation showcases creating account instances, depositing/withdrawing amounts, processing


transactions, and printing account data. It also demonstrates abstraction in action by using base class
references and subclass-specific implementations.

Program Output Explanation

• A transaction of 500.0 is processed for John Smith’s savings account.

• A transaction of 1000.0 is processed for Jane Doe’s checking account.

710
The Complete Java Training Imran Afzal
• Details of both transactions are displayed with current date and updated balances.

• The display() method outputs account information for both accounts.

• John Smith’s account is removed from the banking system.

This program showcases how abstraction allows a clean and extendable design by enforcing method
implementations and isolating responsibilities across classes.

12.5- Introduction to Interface

12.5.1- Understanding Java Interfaces

An interface in Java is a reference type, similar to a class, that can contain only constants, method
signatures, default methods, static methods, and nested types. Interfaces are used to achieve
abstraction and multiple inheritance in Java. All methods in interfaces are implicitly public and abstract
(except static and default methods).

Interfaces are implemented by classes using the implements keyword. Any class that implements an
interface must provide concrete implementations for all of its abstract methods.

12.5.2- Declaring an Interface

An interface can be declared with abstract method signatures. You do not need to explicitly use the
abstract keyword for methods or public static final for fields as they are implicit.

public interface MyInterface {

public abstract void method1(); // abstract is optional

public void method2(); // also abstract

In this example, MyInterface defines two abstract methods. Any class that implements MyInterface
must provide definitions for both method1() and method2().

711
The Complete Java Training Imran Afzal
12.5.3- Using an Interface as a Type

An interface type can be used to refer to objects of any class that implements it. This allows for
polymorphism, where the interface type is used to interact with objects in a generic way.

public interface Shape {}

public class Triangle implements Shape {}

Shape shape = new Triangle(); // Shape is an interface reference

In this case, Triangle implements the Shape interface. The variable shape can point to any object of a
class that implements Shape, providing a flexible reference mechanism.

12.5.4- Defining and Implementing a Functional Interface

A functional interface is one that contains only a single abstract method. These are especially useful
in lambda expressions but can also be implemented by regular classes.

public interface Drawable {

void draw(); // abstract and public by default

An interface like Drawable defines the contract for drawing something.

12.5.5- Implementing an Interface in a Class

A class can implement one or more interfaces. When implementing an interface, the class must
override all the abstract methods defined in that interface.

public class Circle implements Drawable {

private int radius;

712
The Complete Java Training Imran Afzal
public Circle(int radius) {

this.radius = radius;

@Override

public void draw() {

System.out.println("Drawing a circle with radius " + radius);

Here, the Circle class implements the Drawable interface. It provides an actual implementation for
the draw() method.

12.5.6- Executing Interface Behavior with Main Method

The following is the main class that demonstrates the interface in use:

class MainClass {

public static void main(String[] args) {

Drawable circle = new Circle(5);

circle.draw();

713
The Complete Java Training Imran Afzal
This program creates a Circle object with a radius of 5 and uses the draw() method through the
Drawable interface reference. This illustrates polymorphism, where the interface type is used to invoke
the method.

12.5.7- Clarification on Interface Fields and Methods

Java interfaces cannot have concrete method implementations unless they are marked default or static.
Additionally, you cannot define a method as public static final unless it has a body.

Incorrect Syntax Example:

public interface Drawable {

public static final void draw(); // Incorrect - interfaces cannot have static final methods without a
body

Correct Way:

public interface Drawable {

void draw(); // Abstract by default

This ensures the interface remains a blueprint for behavior, and the implementation is deferred to the
implementing class.

12.5.8- Comparing Abstract Classes and Interfaces

Feature Abstract Class Interface

Instantiation Cannot be instantiated Cannot be instantiated

714
The Complete Java Training Imran Afzal
Methods Can have both abstract and Only abstract methods (until Java 8+, which
concrete methods allows default and static methods)

Variables Can have instance variables Only public static final constants

Constructors Can have constructors Cannot have constructors

Inheritance Single inheritance Multiple interface implementation

Purpose Partial implementation for Define a common contract across unrelated


related classes classes

Abstract classes are suitable for related objects needing shared code, while interfaces are better for
defining behavior that can be shared across unrelated classes.

12.5.9- Advantages of Using Interfaces

• Abstraction: Define behavior without enforcing how it’s implemented.

• Multiple Inheritance: A class can implement multiple interfaces, enhancing flexibility.

• Polymorphism: Interface references can point to different implementing objects.

• API Design: Interfaces help in designing clear, implementation-independent APIs.

1. Testing: Interfaces make it easier to create mock implementations for unit testing.

12.5.10- Limitations of Interfaces

• Complexity: Multiple interfaces can make the code harder to understand.

• Over-abstraction: Too much abstraction may reduce code clarity.

• Retrofit Challenges: Adding an interface to existing classes requires modifying them.

• No Implementation: Interfaces cannot offer default behavior (prior to Java 8).

715
The Complete Java Training Imran Afzal
• Frequent Changes: Changes to interfaces may lead to widespread updates in implementing
classes.

12.6- Interface Exercise Part 1

12.6.1- Building a Movie Review and Playback System with Interfaces

This program demonstrates how to use interfaces in Java to model behavior like playing and reviewing
movies. It uses abstract and concrete classes alongside multiple interfaces to organize shared and
specific functionalities.

Project Setup

Create a project named Movie Review Play and a package named nixware.consulting. Inside this
package, define the following components:

Interface: Playable

// Playable interface

interface Playable {

void play();

The Playable interface defines a play() method that must be implemented by any class that can be
"played", such as movies.

Interface: Reviewable

// Reviewable interface

interface Reviewable {

void leaveReview(String review);

716
The Complete Java Training Imran Afzal
void rate(double rating);

The Reviewable interface defines two behaviors: receiving a textual review and assigning a numerical
rating. These are abstract methods, and any class implementing Reviewable must provide concrete
implementations.

Abstract Class: Movie

// Abstract Movie class

abstract class Movie {

protected String title;

protected int year;

public Movie(String title, int year) {

this.title = title;

this.year = year;

public String getTitle() {

return title;

717
The Complete Java Training Imran Afzal
}

public int getYear() {

return year;

// Abstract method for getting the genre of the movie

public abstract String getGenre();

The Movie class acts as a blueprint for all movie types. It holds common attributes like title and year,
and enforces that all subclasses must implement the getGenre() method.

Class: ActionMovie (Extends Movie, Implements Playable)

// ActionMovie class

class ActionMovie extends Movie implements Playable {

public ActionMovie(String title, int year) {

super(title, year);

718
The Complete Java Training Imran Afzal
@Override

public void play() {

System.out.println("Playing " + title);

@Override

public String getGenre() {

return "Action";

ActionMovie extends Movie and implements Playable. It defines its genre and playback behavior,
printing out the movie’s title when played.

Class: ComedyMovie (Extends Movie, Implements Playable and Reviewable)

// ComedyMovie class

class ComedyMovie extends Movie implements Playable, Reviewable {

private double rating;

private String review;

719
The Complete Java Training Imran Afzal
public ComedyMovie(String title, int year) {

super(title, year);

@Override

public void play() {

System.out.println("Playing " + title);

@Override

public void leaveReview(String review) {

this.review = review;

@Override

public void rate(double rating) {

this.rating = rating;

720
The Complete Java Training Imran Afzal
}

public double getRating() {

return rating;

public String getReview() {

return review;

@Override

public String getGenre() {

return "Comedy";

ComedyMovie is a concrete class that supports both playback and user feedback through interfaces.
It tracks a rating and review for each instance.

721
The Complete Java Training Imran Afzal
Main Class with Execution Logic

// Main program

public class MovieProgram {

public static void main(String[] args) {

ActionMovie actionMovie = new ActionMovie("Die Hard", 1988);

actionMovie.play();

System.out.println("Genre: " + actionMovie.getGenre());

ComedyMovie comedyMovie = new ComedyMovie("Bridesmaids", 2011);

comedyMovie.play();

comedyMovie.leaveReview("Hilarious movie!");

comedyMovie.rate(4.5);

System.out.println("Genre: " + comedyMovie.getGenre());

System.out.println("Rating: " + comedyMovie.getRating());

System.out.println("Review: " + comedyMovie.getReview());

722
The Complete Java Training Imran Afzal
This code initializes one ActionMovie and one ComedyMovie, plays them, and interacts with the
review features of the comedy movie. It demonstrates the power of interfaces in allowing different
classes to commit to specific behaviors.

Program Output

Playing Die Hard

Genre: Action

Playing Bridesmaids

Genre: Comedy

Rating: 4.5

Review: Hilarious movie!

This output confirms that both classes have correctly implemented their required methods, and that
interfaces have successfully enabled consistent interaction with different movie types.

This exercise showcases how Java interfaces provide a powerful way to enforce a consistent API across
unrelated classes, while abstract classes help structure and reuse common functionality.

12.7- Interface Exercise Part 2

12.7.1- Introduction to JSON in Java

JSON (JavaScript Object Notation) is a widely adopted data-interchange format known for its
simplicity and readability. It uses key-value pairs and supports data structures such as arrays and nested
objects, making it ideal for representing structured data.

In Java, JSON is commonly used for data exchange and is supported by libraries such as Gson. Gson
is a powerful tool from Google used to convert Java objects into JSON format and vice versa.

723
The Complete Java Training Imran Afzal
Here is an example of a JSON object:

"name": "John Doe",

"age": 30,

"isMarried": false,

"grades": [85, 90, 78, 92],

"address": {

"street": "123 Main St",

"city": "Exampleville",

"country": "USA"

},

"spouse": null

12.7.2- Creating the MapMappable Interface

Define an interface called MapMappable with four abstract methods that classes must implement.

interface MapMappable {

String getLabel();

724
The Complete Java Training Imran Afzal
String getGeometry();

String getIcon();

String toJson();

These methods serve the following purposes:

• getLabel(): Returns the label or name of the map object.

• getGeometry(): Indicates the spatial type (e.g., Point, Line).

• getIcon(): Returns the icon used to represent the object on the map.

• toJson(): Converts the object into a JSON string.

12.7.3- Implementing the Interface in MyMapObject

Create a class MyMapObject that implements MapMappable. This class holds label, geometry type,
and icon details and uses the Gson library to support JSON conversion.

import com.google.gson.Gson;

class MyMapObject implements MapMappable {

private String label;

private String geometry;

private String icon;

725
The Complete Java Training Imran Afzal
public MyMapObject(String label, String geometry, String icon) {

this.label = label;

this.geometry = geometry;

this.icon = icon;

@Override

public String getLabel() {

return label;

@Override

public String getGeometry() {

return geometry;

@Override

726
The Complete Java Training Imran Afzal
public String getIcon() {

return icon;

@Override

public String toJson() {

Gson gson = new Gson();

return gson.toJson(this);

To use Gson, make sure to import the library and add it to your project dependencies.

Main Class to Demonstrate Functionality

Create a MapMainClass to test the interface and implementation.

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

727
The Complete Java Training Imran Afzal
import com.google.gson.Gson;

public class MapMainClass {

public static void main(String[] args) {

// Create an instance of MyMapObject

MyMapObject mapObject = new MyMapObject("My Object", "Point", "Black Pushpin");

// Accessing interface methods

System.out.println("Label: " + mapObject.getLabel());

System.out.println("Geometry: " + mapObject.getGeometry());

System.out.println("Icon: " + mapObject.getIcon());

// Convert object to JSON

String json = mapObject.toJson();

System.out.println("JSON: " + json);

// Working with List of MapMappable objects

728
The Complete Java Training Imran Afzal
List<MapMappable> mapObjects = new ArrayList<>();

mapObjects.add(mapObject);

String objectsJson = new Gson().toJson(mapObjects);

System.out.println("Objects JSON: " + objectsJson);

// Working with Map of MapMappable objects

Map<String, MapMappable> mapObjectMap = new HashMap<>();

mapObjectMap.put("object1", mapObject);

String mapJson = new Gson().toJson(mapObjectMap);

System.out.println("Map JSON: " + mapJson);

12.7.5- Expected Output

Label: My Object

Geometry: Point

Icon: Black Pushpin

JSON: {"label":"My Object","geometry":"Point","icon":"Black Pushpin"}

729
The Complete Java Training Imran Afzal
Objects JSON: [{"label":"My Object","geometry":"Point","icon":"Black Pushpin"}]

Map JSON: {"object1":{"label":"My Object","geometry":"Point","icon":"Black Pushpin"}}

Each output block represents:

• Direct property access using implemented methods

• JSON representation of a single object

• JSON representation of a list of objects

• JSON representation of a map of objects

This structured implementation highlights the power of interfaces to create standardized behaviors
and the usefulness of JSON serialization for real-world applications like mapping systems.

12.8- Interface and Abstract Class Summary

This summary clarifies the main differences between abstract classes and interfaces in Java and
explains when each should be used. Both abstract classes and interfaces are tools for abstraction and
cannot be instantiated directly. They may contain abstract methods, which lack implementations, and
concrete methods, which do have method bodies. Abstract classes can declare instance fields and
static final constants. They allow all four access modifiers for concrete methods, and all except private
for abstract methods. An abstract class can extend only one parent class but can implement multiple
interfaces. When a class extends an abstract class, it must implement all abstract methods, or it must
also be declared abstract.

12.8.1- Characteristics and Use Cases of Abstract Classes

Abstract classes are useful when you want to share code among several closely related classes. For
example, if you're building a game with different character types—warriors, mages, archers—each may
share attributes like name, level, and health, which are defined in an abstract base class. Use an abstract
class when you need to define non-static or non-final fields like color or size and want to provide
methods like getColor() or setSize() to manipulate object state. Abstract classes are also ideal when
you want some default method implementations and the flexibility for subclasses to override others.
730
The Complete Java Training Imran Afzal
12.8.2- Characteristics and Use Cases of Interfaces

In contrast, an interface is purely a contract for behavior without concern for implementation. It
defines operations that a class promises to perform. All methods in an interface are public and abstract
by default unless they are static, default, or private. Interfaces decouple what a class can do from how
it does it. They are useful when unrelated classes should implement the same behavior. For example,
the interfaces Comparable and Cloneable are implemented by many unrelated classes in Java.

12.8.3- Modern Features in Interfaces (Java 8 and Beyond)

Interfaces promote flexibility and extensibility, making them ideal for defining APIs. They are
foundational to many Java features such as lambda expressions (introduced in Java 8) and JDBC (Java
Database Connectivity), where JDBC drivers implement interfaces defined by the JDBC API, allowing
code to work across different database systems without modification.

Since Java 8, interfaces can contain default and static methods, and since Java 9, they can also have
private methods. Default methods provide a way to evolve interfaces without breaking existing
implementations. Static methods in interfaces are accessible at the interface level, and private methods
help reuse common code among default methods.

12.8.4- Guidelines for Choosing Between Abstract Classes and Interfaces

Use interfaces when you expect unrelated classes to implement them, when you want to specify
behavior without dictating implementation, or when you want to decouple components. Use abstract
classes when you need shared code, state, or logic, or when you want to define common functionality
for closely related classes.

Abstract classes allow constructors, which can be called by subclasses, and participate in inheritance
through the extends clause. Interfaces, on the other hand, do not participate in inheritance but are
implemented through the implements clause by classes, records, or enums. Abstract classes support
instance fields, while interfaces only support public static final fields, which are implicitly defined and
should not be redundantly declared.

731
The Complete Java Training Imran Afzal
While both abstract classes and interfaces support abstraction, their design goals and capabilities differ.
Abstract classes provide a foundation for related classes with common behavior and state, while
interfaces define a shared contract for diverse classes to follow. Understanding these differences allows
developers to design flexible, reusable, and maintainable code structures.

732
The Complete Java Training Imran Afzal
Chapter 13

Introduction to Generics

733
The Complete Java Training Imran Afzal
13.1- Introduction to Generics

13.1.1- Understanding Generics in Java

Generics in Java are a powerful feature that enable developers to create reusable, type-safe code by
abstracting over types. They allow the definition of classes, interfaces, and methods that can work
with various data types while ensuring compile-time type checking. This abstraction makes the code
more flexible, maintainable, and less prone to runtime errors.

13.1.2- Purpose of Generics

The main purpose of generics is to generalize class and method definitions so they can handle multiple
data types. This eliminates the need to write duplicate code for each specific type. Java provides syntax
using angle brackets (< >) and type parameters—commonly denoted by uppercase letters like T, E,
or K—to define generic types.

13.1.3- Generic Class Concept

A generic class allows the creation of objects that operate on different data types. A type parameter
serves as a placeholder that gets replaced with a concrete type when an instance of the class is created.
This ensures type safety and flexibility at the same time.

For example, consider a generic class MyGenericClass<T>. Here, T is a type parameter. When you
create an object of MyGenericClass<Integer>, T gets replaced with Integer, making the class
specialized for handling integers.

13.1.4- Syntax Comparison: Regular vs Generic Class

A regular class is defined with:

public class MyClass {

// Class members and methods

734
The Complete Java Training Imran Afzal
A generic class introduces a type parameter:

public class MyGenericClass<T> {

// Class members and methods

The use of the type parameter T allows the class to work with any object type, depending on what is
specified during instantiation.

13.1.5- Detailed Example of a Generic Class

public class MyGenericClass<T> {

private T value;

public void setValue(T value) {

this.value = value;

public T getValue() {

return value;

735
The Complete Java Training Imran Afzal
Usage in the main Method

public static void main(String[] args) {

MyGenericClass<Integer> integerInstance = new MyGenericClass<>();

integerInstance.setValue(10);

int intValue = integerInstance.getValue();

System.out.println("Integer value: " + intValue);

MyGenericClass<String> stringInstance = new MyGenericClass<>();

stringInstance.setValue("Hello");

String stringValue = stringInstance.getValue();

System.out.println("String value: " + stringValue);

Expected Output

Integer value: 10

String value: Hello

13.1.6- Advantages of Generic Classes

• Reusability: Generic classes support code reusability by enabling the same class to work with
different data types without rewriting code.

• Type Safety: Compile-time type checking ensures that the type specified matches the intended
usage, helping prevent type-related runtime errors.

• Code Clarity and Readability: Generic code tends to be cleaner and easier to understand. It
eliminates unnecessary type casting and improves readability.

736
The Complete Java Training Imran Afzal
• Compile-Time Type Checking: Errors are caught during compilation, reducing the chances of
bugs that would otherwise surface at runtime.

• Enhanced Error Detection: When type mismatches occur, the compiler provides detailed and
specific error messages that help in identifying the problem quickly.

• Collection Type Safety: Collections like ArrayList, LinkedList, and HashMap make extensive
use of generics to ensure that only objects of the specified type can be stored and retrieved,
minimizing runtime issues.

13.1.7- Limitations of Generic Classes

• Complexity: For beginners, generics can be difficult to grasp. Concepts like type parameters,
wildcards, and bounds can add complexity to the code.

• Limited Type Constraints: Generics allow constraints using extends, but other advanced
constraints (e.g., requiring specific methods) are not supported.

• Type Erasure: Java erases generic type information at runtime, which means you cannot
retrieve or check the type of a generic at runtime.

• Inflexible Type Handling: Generics only work with reference types. Primitive types (like
int, char) must be wrapped using their respective wrapper classes (Integer, Character), which
may lead to performance overhead.

• Compatibility Issues: Integrating generics with legacy code or libraries that do not use
generics may require workarounds.

• Increased Compilation Time: Large or complex generic code can lead to longer compilation
times due to increased type-checking operations performed by the compiler.

Generics are a cornerstone of modern Java programming. Despite some limitations, they offer
significant benefits in terms of code reusability, type safety, and maintainability.

13.2- Generics Exercise Part 1

This exercise demonstrates how to use generics in Java by developing a program that models a football
team. The focus is on creating a generic class that can store and manage data about team members.

737
The Complete Java Training Imran Afzal
Creating the Project Structure

Start by creating a Java project named Generics. Inside this project, add a package named
nixware.consulting. Within this package, create two Java classes: FootballTeam (containing the main
method) and TeamMember (the generic class).

Defining the Generic Class

The TeamMember class is declared with a generic type parameter T, enabling it to work with different
data types for the member's name.

// Generic class to represent a football team member

class TeamMember<T> {

private T name;

private int jerseyNumber;

public TeamMember(T name, int jerseyNumber) {

this.name = name;

this.jerseyNumber = jerseyNumber;

public T getName() {

return name;

738
The Complete Java Training Imran Afzal
}

public int getJerseyNumber() {

return jerseyNumber;

public void setName(T name) {

this.name = name;

public void setJerseyNumber(int jerseyNumber) {

this.jerseyNumber = jerseyNumber;

@Override

public String toString() {

return "Name: " + name + ", Jersey Number: " + jerseyNumber;

739
The Complete Java Training Imran Afzal
}

• The variable name is declared as type T, allowing the name to be of any data type specified
when the object is instantiated.

• The variable jerseyNumber is an integer representing the player's jersey number.

• Constructor and getter/setter methods handle the initialization and updating of these values.

• The toString method provides a string representation of the object's state.

Using the Generic Class in the Main Method

In the FootballTeam.java file, create and manipulate instances of the TeamMember class:

// Main program

public class FootballTeam {

public static void main(String[] args) {

// Creating team members

TeamMember<String> player1 = new TeamMember<>("John", 10);

TeamMember<String> player2 = new TeamMember<>("David", 7);

TeamMember<String> player3 = new TeamMember<>("Michael", 5);

// Printing team members

System.out.println(player1);

740
The Complete Java Training Imran Afzal
System.out.println(player2);

System.out.println(player3);

// Modifying team member details

player1.setName("Chris");

player2.setJerseyNumber(9);

// Printing updated team members

System.out.println(player1);

System.out.println(player2);

• Three instances of TeamMember are created with String as the type for the name.

• player1 is initialized with the name "John" and jersey number 10.

• player2 is initialized with "David" and 7.

• player3 is initialized with "Michael" and 5.

• The System.out.println() statements display the details using the overridden toString() method.

• The setName() method updates player1's name to "Chris".

• The setJerseyNumber() method changes player2's jersey number to 9.

741
The Complete Java Training Imran Afzal
Output of the Program

Initial details:

Name: John, Jersey Number: 10

Name: David, Jersey Number: 7

Name: Michael, Jersey Number: 5

Updated details after modification:

Name: Chris, Jersey Number: 10

Name: David, Jersey Number: 9

This implementation demonstrates the flexibility of generics in Java, allowing the TeamMember class
to work with different data types and simplifying code reusability while maintaining type safety.

13.3- Generics Exercise Part 2

In this exercise we’ll manage cricket teams using Java generics, records, and interfaces. This includes
creating players, managing teams, and simulating matches between them.

Project Structure

Create a Java project named Generics1, and inside it, create a package named nixware.consulting. In
this package, define several classes and records: MainClass, NewCricketTeam, CricketTeams<T
extends NewPlayer>, NewPlayer (interface), and NewCricketPlayer (record).

Interface and Record: Representing Players

Code

public interface NewPlayer {

742
The Complete Java Training Imran Afzal
String getName();

public record NewCricketPlayer(String name, String role) implements NewPlayer {

public String getName() {

return name;

NewPlayer is an interface that requires a method getName() which returns the name of the player.
NewCricketPlayer is a record (a concise way to store data) that has two fields: name and role (e.g.,
"Batsman"). It implements the NewPlayer interface and overrides getName().

Class: Managing a Regular Cricket Team

import java.util.ArrayList;

import java.util.List;

public class NewCricketTeam {

private String teamName;

private List<NewCricketPlayer> teamMembers = new ArrayList<>();

private int totalWins = 0;

743
The Complete Java Training Imran Afzal
private int totalLosses = 0;

private int totalTies = 0;

public NewCricketTeam(String teamName) {

this.teamName = teamName;

public void addTeamMember(NewCricketPlayer player) {

if (!teamMembers.contains(player)) {

teamMembers.add(player);

public void listTeamMembers() {

System.out.println(teamName + " Roster:");

for (NewCricketPlayer player : teamMembers) {

System.out.println(player);

744
The Complete Java Training Imran Afzal
}

public void setScore(int ourScore, int theirScore) {

if (ourScore > theirScore) {

totalWins++;

} else if (ourScore == theirScore) {

totalTies++;

} else {

totalLosses++;

public int getRanking() {

return (totalWins * 2) + totalTies + 1;

745
The Complete Java Training Imran Afzal
@Override

public String toString() {

return teamName + " (Ranked " + getRanking() + ")";

This class helps manage a cricket team:

• Stores players using a list.

• Tracks total wins, losses, and ties.

• Adds new players, avoiding duplicates.

• Calculates ranking: 2 points for a win, 1 for a tie, and always adds 1 to ensure rank is never
zero.

Generic Class: Flexible Team Management

import java.util.ArrayList;

import java.util.List;

public class CricketTeams<T extends NewPlayer> {

private String teamName;

private List<T> teamMembers = new ArrayList<>();

746
The Complete Java Training Imran Afzal
public CricketTeams(String teamName) {

this.teamName = teamName;

public void addTeamMember(T player) {

if (!teamMembers.contains(player)) {

teamMembers.add(player);

public void listTeamMembers() {

System.out.println(teamName + " Roster:");

for (T player : teamMembers) {

System.out.println(player.getName());

747
The Complete Java Training Imran Afzal
@Override

public String toString() {

return teamName;

This generic class can manage teams made of any type that implements NewPlayer. It:

• Stores team name and player list.

• Allows adding and displaying players.

• Works with custom or anonymous player implementations.

Match Logic: Handling Match Results

private static String getMessage(int ourScore, int theirScore) {

if (ourScore > theirScore) {

return "beat";

} else if (ourScore == theirScore) {

return "tied with";

} else {

return "lost to";

748
The Complete Java Training Imran Afzal
}

public static void scoreResult(NewCricketTeam team1, int t1_score, NewCricketTeam team2, int
t2_score) {

team1.setScore(t1_score, t2_score);

team2.setScore(t2_score, t1_score);

System.out.printf("%s %s %s%n", team1, getMessage(t1_score, t2_score), team2);

public static void scoreResult(CricketTeams<? extends NewPlayer> team1, int t1_score,


CricketTeams<? extends NewPlayer> team2, int t2_score) {

System.out.printf("%s %s %s%n", team1, getMessage(t1_score, t2_score), team2);

• getMessage() decides whether the result is a win, tie, or loss.

• scoreResult() updates match stats for regular teams.

• Another overloaded scoreResult() prints outcomes for generic teams.

Main Execution: Creating Teams and Running Matches

public class MainClass {

public static void main(String[] args) {

749
The Complete Java Training Imran Afzal
NewCricketTeam australia = new NewCricketTeam("Australia");

NewCricketTeam india = new NewCricketTeam("India");

scoreResult(australia, 220, india, 180);

CricketTeams<NewCricketPlayer> australiaTeam = new CricketTeams<>("Australia");

CricketTeams<NewCricketPlayer> indiaTeam = new CricketTeams<>("India");

scoreResult(australiaTeam, 220, indiaTeam, 180);

var smith = new NewCricketPlayer("Steve Smith", "Batsman");

var warner = new NewCricketPlayer("David Warner", "Batsman");

australiaTeam.addTeamMember(smith);

australiaTeam.addTeamMember(warner);

var kohli = new NewCricketPlayer("Virat Kohli", "Batsman");

indiaTeam.addTeamMember(kohli);

indiaTeam.listTeamMembers();

750
The Complete Java Training Imran Afzal
CricketTeams<NewPlayer> england = new CricketTeams<>("England");

england.addTeamMember(new NewPlayer() {

@Override

public String getName() {

return "Joe Root";

});

england.listTeamMembers();

CricketTeams<NewPlayer> pakistan = new CricketTeams<>("Pakistan");

pakistan.addTeamMember(new NewPlayer() {

@Override

public String getName() {

return "Babar Azam";

});

pakistan.listTeamMembers();

751
The Complete Java Training Imran Afzal
scoreResult(pakistan, 250, england, 230);

Explanation

• Creates teams using both concrete and generic types.

• Adds players using regular records and anonymous interface implementations.

• Simulates match results and prints outcomes.

• Shows how generics allow flexibility with team member types.

Output

Australia (Ranked 3) beat India (Ranked 1)

Australia beat India

India Roster:

Virat Kohli

England Roster:

Joe Root

Pakistan Roster:

Babar Azam

752
The Complete Java Training Imran Afzal
Pakistan beat England

This output confirms match outcomes and shows the rosters for India, England, and Pakistan.
Rankings are based on match outcomes and calculated using the defined formula.

13.4- Introduction to Comparable

The Comparable interface in Java allows objects to be compared with each other using their natural
ordering. It belongs to the java.lang package and contains the method compareTo(T o).

Declaration of the Comparable Interface

public interface Comparable<T> {

public int compareTo(T o);

The compareTo method compares the current object (this) with the object passed as a parameter (o).
It returns:

• A negative integer if the current object is less than the other.

• Zero if the two objects are equal.

• A positive integer if the current object is greater than the other.

This interface is essential for sorting objects, using ordered collections, and performing binary
searches.

13.4.1- Purpose of Comparable

The main goal of implementing the Comparable interface is to enable automatic sorting and ordering
of custom objects. It is especially useful when working with:

• Sorting algorithms like Collections.sort()

753
The Complete Java Training Imran Afzal
• Ordered collections such as TreeSet or TreeMap

• Searching methods like Collections.binarySearch()

13.4.2- Example: Implementing Comparable in a Class

public class Person implements Comparable<Person> {

private String name;

private int age;

public Person(String name, int age) {

this.name = name;

this.age = age;

@Override

public int compareTo(Person other) {

return Integer.compare(this.age, other.age);

754
The Complete Java Training Imran Afzal
The Person class implements Comparable<Person>, which means two Person objects can be
compared based on their age.

The compareTo method uses Integer.compare to compare the age field of the current object with
another Person's age.

The fields name and age are encapsulated in the class, and although not shown in this snippet, getters
and setters are assumed for accessing and modifying them.

13.4.3- Practical Usage of compareTo()

Integer number = 5;

Integer[] numbers = {1, 10, 3, -50, 100};

for (Integer n : numbers) {

int val = number.compareTo(n);

System.out.printf("%d %s %d: compareTo=%d%n", number,

(val == 0 ? "==" : (val < 0) ? "<" : ">"), n, val);

This snippet compares an integer (number = 5) with each value in the array numbers. It prints whether
the number is greater than, equal to, or less than each element using the compareTo() method.

13.4.4- Comparing Strings with compareTo()

String word = "apple";

String[] words = {"carrot", "broccoli", "tomato", "CUCUMBER"};

755
The Complete Java Training Imran Afzal
for (String w : words) {

int val = word.compareTo(w);

System.out.printf("%s %s %s: compareTo=%d%n", word,

(val == 0 ? "==" : (val < 0) ? "<" : ">"), w, val);

The compareTo() method is also used with String values to compare their lexicographical order
(dictionary order). Java uses the Unicode values of characters for this comparison.

13.4.5- Sorting with Arrays.sort()

Arrays.sort(words);

System.out.println(Arrays.toString(words));

When an array of strings is sorted using Arrays.sort(), it arranges the strings based on their natural
ordering defined by String.compareTo().

13.4.6- Understanding Unicode Character Order

System.out.println("A:" + (int) 'A' + " a:" + (int) 'a');

System.out.println("B:" + (int) 'B' + " b:" + (int) 'b');

System.out.println("P:" + (int) 'P' + " p:" + (int) 'p');

This part prints the Unicode values of uppercase and lowercase characters. It helps explain why
uppercase letters come before lowercase letters when sorted lexicographically.

13.4.7- Creating a Custom Comparable Class: Employee

class Employee implements Comparable {

756
The Complete Java Training Imran Afzal
private String employeeName;

public Employee(String name) {

this.employeeName = name;

@Override

public String toString() {

return employeeName;

@Override

public int compareTo(Object o) {

Employee other = (Employee) o;

if (employeeName.equals("Tim") && other.employeeName.equals("Ann")) {

return -1;

} else if (employeeName.equals("Ann") && other.employeeName.equals("Tim")) {

757
The Complete Java Training Imran Afzal
return 1;

} else {

return employeeName.compareTo(other.employeeName);

The Employee class implements Comparable (raw type, not parameterized). It defines a custom
ordering where:

• "Tim" is always less than "Ann".

• "Ann" is always greater than "Tim".

• For all other cases, it falls back to the default string comparison.

13.4.8- Using Custom Comparable with Arrays.sort()

Employee employee1 = new Employee("Tim");

Employee[] employeeArray = {

new Employee("Zach"), new Employee("Tim"),

new Employee("Ann"), new Employee("John")

};

758
The Complete Java Training Imran Afzal
Arrays.sort(employeeArray);

System.out.println(Arrays.toString(employeeArray));

This block sorts the Employee array using the custom comparison logic defined in compareTo(). The
overridden rules affect how "Tim" and "Ann" are positioned relative to each other.

13.4.9- Comparing Employee with a Raw String (Not Recommended)

System.out.println("result = " + employee1.compareTo("Mary"));

This line compares an Employee object to a raw string "Mary" which leads to a ClassCastException
at runtime. It highlights why generics and type safety are important when implementing
Comparable<T>.

13.4.10- Common Use Cases for Comparable

• Sorting collections like arrays and lists using Collections.sort() or Arrays.sort().

• Maintaining ordered collections like TreeSet or TreeMap.

• Binary Search, which depends on comparison logic to function correctly.

• Consistency in ordering, ensuring a reliable and reusable way to compare objects.

13.4.11- Advantages of Implementing Comparable

• Natural Ordering: Objects can be sorted in a natural and predictable way.

• Simplicity: You only need to implement a single method.

• Better Readability: It is clear to other developers how objects will be ordered.

• Code Reusability: Once implemented, the object can be used in sorted collections and
algorithms without additional comparator logic.

• Standardized Behavior: Provides a consistent rule across the application for object
comparison.

759
The Complete Java Training Imran Afzal
13.4.12- Limitations of Comparable

• Inflexibility: Only one comparison logic can be defined. If you need multiple sorting options
(e.g., by name or age), you must use Comparator.

• Tight Coupling: Comparison logic is part of the class, which might not be desirable.

• Encapsulation Issues: Sometimes forces exposure of internal fields.

• Inconsistent Comparisons: Can be tricky when comparing heterogeneous object types.

• Retroactive Support: Adding Comparable later can break existing systems.

• Limited Customization: Modifying the comparison behavior without changing the class is
not straightforward.

13.4.13- When Not to Use Comparable

• When you need multiple comparison strategies

• When comparison logic should be separate from the class definition

• When working with third-party or already compiled classes

In such cases, consider using the Comparator interface, which allows flexible and reusable comparison
logic.

The Comparable interface is a powerful feature that simplifies the sorting and comparison of objects.
It is especially useful for defining the default way objects should be ordered based on inherent
properties such as age, name, or ID. However, when flexibility is needed, using Comparator is the
preferred approach.

13.5- Interface Comparable Exercise

This exercise demonstrates the use of the Comparable interface by implementing a program that sorts
an array of Employee objects based on their names and compares an employee object with a string
value.

760
The Complete Java Training Imran Afzal
13.5.1- Creating the Employee Class

public class Employee implements Comparable{

private String employeeName;

public Employee(String name) {

this.employeeName = name;

@Override

public String toString() {

return employeeName;

@Override

public int compareTo(Object o) {

if (o instanceof Employee) {

Employee other = (Employee) o;

761
The Complete Java Training Imran Afzal
if (employeeName.equals("Tim") && other.employeeName.equals("Ann")) {

return -1;

} else if (employeeName.equals("Ann") && other.employeeName.equals("Tim")) {

return 1;

} else {

return employeeName.compareTo(other.employeeName);

} else if (o instanceof String) {

String other = (String) o;

return employeeName.compareTo(other);

} else {

throw new IllegalArgumentException("Cannot compare Employee with object of type " +


o.getClass().getName());

• The class includes a private field employeeName of type String.

• A constructor is used to initialize the employeeName.

762
The Complete Java Training Imran Afzal
• The toString() method returns the name of the employee.

• The class implements the Comparable interface to define custom sorting logic in the
compareTo() method.

13.5.2- Logic in compareTo() Method

o If the argument is an Employee object:

o Returns -1 if current name is "Tim" and other name is "Ann"

o Returns 1 if current name is "Ann" and other name is "Tim"

o Otherwise, compares using the built-in String.compareTo() method

• If the argument is a String, compares the employee name with that string

• If the argument is of any other type, throws an IllegalArgumentException

13.5.3- Creating the Main Class

import java.util.Arrays;

public class MainClass {

public static void main(String[] args) {

Employee employee1 = new Employee("Tim");

Employee[] employeeArray = {new Employee("Zach"), new Employee("Tim"),

new Employee("Ann"), new Employee("John")};

Arrays.sort(employeeArray);

System.out.println(Arrays.toString(employeeArray));

763
The Complete Java Training Imran Afzal
System.out.println("result = " + employee1.compareTo("Mary"));

• An Employee object named employee1 is created with the name "Tim".

• An array employeeArray is initialized with four Employee objects: "Zach", "Tim", "Ann", and
"John".

• The array is sorted using Arrays.sort(), which relies on the custom compareTo() logic.

• The sorted array is printed using Arrays.toString().

• The result of comparing employee1 with the string "Mary" is printed using compareTo().

Output Explanation

[Ann, John, Tim, Zach]

result = 7

• After sorting, the array is ordered based on the defined rules and string comparison.

• "Ann" appears before "Tim" due to the explicit condition.

• The comparison between "Tim" and "Mary" returns a positive integer (7), indicating "Tim" is
greater than "Mary" in lexicographical order.

This exercise demonstrates how implementing the Comparable interface in a custom class allows for
flexible and meaningful object comparisons and sorting behavior.

764
The Complete Java Training Imran Afzal

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy