E-Book+-+1.9
E-Book+-+1.9
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Learners dive into Java's collection framework, starting with ArrayList and LinkedList. It includes
comparisons, exercises, and introductions to Queue, Stack, Iterators, and Enumeration.
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.
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.
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
Introduction
1
The Complete Java Training Imran Afzal
1.1- What is 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.
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
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.
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.
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.
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.
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
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
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
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)
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).
• 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.
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.
To understand OOP better, let’s compare it with procedural (sequential) programming, which
follows a step-by-step approach to executing instructions.
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:
4. Fasten seatbelt
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
In OOP, we define an object and its related functions inside something called a class.
This revised approach reduces the number of lines in the program from 11 to just 5, making it easier
to read, maintain, and modify.
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).
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.
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.
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. 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.
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
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. 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.
5. Game Development: Many games leverage Java's portability. Examples include Minecraft,
RuneScape, Puzzle Games.
Figure – 1.4.4
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. 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.
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
C# was developed by Microsoft and is used extensively in Windows application development, gaming,
and enterprise software.
1.4.8- Applications of C#
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.
Ruby is known for its simplicity and readability, making it ideal for rapid web development and
automation tasks.
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.
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.
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.
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:
14
The Complete Java Training Imran Afzal
o Develop an authentication module for secure user login.
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.
• 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.
• 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
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.
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.
• 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.
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.
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.
• 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.
• 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.
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.
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.
• 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:
These tools offer code completion, debugging, refactoring, and profiling features that make Java
development efficient and adaptable.
Java has a large global community of developers who continuously contribute to its evolution. This
community-driven approach ensures:
• 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.
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:
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:
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:
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.
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.
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.
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.
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.
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.
• 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.
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.
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.
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 debugger
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.
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
1. Compilation
2. Execution
Step 1: Compilation
• 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.
• 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.
• 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.
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.
• If your system has enough resources (Memory, CPU, and Disk Space)
These are basic checks to ensure everything runs smoothly on your computer.
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.
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.
• Same as JDK (If your system meets the JDK requirements, JShell will work perfectly).
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.
• 4GB of RAM
Most modern computers will have these specifications, so you should be good to go.
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.
Once that’s done, both your Windows and Linux systems will be equipped with a powerful Java
environment.
If you're using Mac, Linux, or have already installed JDK on Windows, you can skip this lecture.
After the installation is complete, you need to set the JAVA_HOME environment variable to point
to the JDK installation folder.
33
The Complete Java Training Imran Afzal
1. Go to the Start menu and search for "Environment Variables."
4. Under the System variables section, scroll down and find the "Path" variable.
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.
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.
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.
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.
When you enter code at the JShell prompt, the tool immediately executes the code and prints the
results to the console.
2+2
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:
JShell has some limitations; it does not replace the need for an IDE but serves as a quick tool to
get started with Java.
1. Open the Windows terminal by typing "cmd" in the Windows Start search bar or by
pressing Windows + R keys.
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.
The /list command allows you to view the code snippets and commands entered in the current
JShell session.
Example:
1. Type:
2+2
3. Now type:
/list
36
The Complete Java Training Imran Afzal
3.4.4- Exiting JShell
/exit
2. Type:
jshell
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.
1. Open Notepad.
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.
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.
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.
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.
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 Java program must have a main method, which is the entry point of the program:
• 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.
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!");
jshell
System.out.println("Hello, World!");
Hello, World!
• 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
...>
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.
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.
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.
• 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.
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.
int age;
age = 25;
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.
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
System.out.print(age);
age = 30;
Types of Statements
int myVar;
45
The Complete Java Training Imran Afzal
int age;
Modify the myAge variable from 30 to 65 and print the new value.
myAge = 65;
System.out.print(myAge);
A variable’s value can change during program execution using the assignment operator =.
/list
CTRL + L
46
The Complete Java Training Imran Afzal
To print the value of myNumber:
System.out.print(myNumber);
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.
A literal value in an expression can have its result stored in a literal variable.
For example:
Here, (20 / 2) + (10 + 2) is a simple arithmetic expression in Java, combining division and
addition.
In Java:
47
The Complete Java Training Imran Afzal
Step-by-step evaluation:
(20 / 2) + (10 + 2)
(10) + (12)
22
1. Open JShell.
2. List all variables and their values using the /var command:
/var
3. Add literalVariable and secondVariable and store the result in a new variable sum:
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
secondVariable = secondVariable * 3;
These examples demonstrate how variables in Java can store mathematical expressions, be
modified, and be used for more complex operations.
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.
49
The Complete Java Training Imran Afzal
Long 64-bit Large integer values -9.223 × 10¹⁸ to 9.223 × 10¹⁸ long l =
10000L;
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.
Example:
byte b = 100;
If you try to assign 128, Java will return an error since the byte range is exceeded.
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.
• The most commonly used integer type due to its balance between range and memory
usage.
Example:
int i = 1000000;
int i1 = 500000;
int i2 = 250000;
Example:
long l = 10000L;
51
The Complete Java Training Imran Afzal
• Used for storing fractional values with less precision than double.
Example:
float f = 3.14f;
Example:
double d = 3.14;
Since double has higher precision than float, it is preferred when working with scientific
calculations.
Example:
char c = 'A';
Example:
52
The Complete Java Training Imran Afzal
Boolean values are useful in conditional statements (if, while, etc.).
Example of String:
String concatenation:
String s2 = "World!";
• Java has eight primitive data types: byte, short, int, long, float, double, char, and boolean.
• float is used for approximate decimal values, while double provides higher precision.
53
The Complete Java Training Imran Afzal
These fundamental data types form the building blocks of Java programs.
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.
The byte data type is an 8-bit signed integer that can hold values from -128 to 127.
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);
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.
54
The Complete Java Training Imran Afzal
Int 32-bit -2,147,483,648 to 2,147,483,647
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.
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);
• The result of x + y is stored as an int, since short cannot hold the operation's result.
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.
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.
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.
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;
short s = 100;
int j = s;
Since int has a larger range than short, this conversion happens safely without data loss.
char c = 'A';
intk;
int k = c;
Instead of storing 'A', k will store the ASCII value of 'A', which is 65.
ASCII is a character encoding standard that represents characters using 7-bit integers. Every
character has a corresponding ASCII value.
'A' 65
'B' 66
'a' 97
57
The Complete Java Training Imran Afzal
'0' 48
Narrowing casting is manually done when converting a larger data type into a smaller one.
Example
double pi = 3.14;
float f = 3.14f;
inta;
a = (int) f;
58
The Complete Java Training Imran Afzal
4.7.6- Important Notes on Type Casting
2. Java is statically typed, meaning the data type must be known at compile-time.
Summary
• Narrowing casting requires explicit conversion and may lead to data loss.
Understanding type casting helps control data conversion and prevent errors when working with
different data types.
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.
• sign bit
• 8 exponent bits
• 23 mantissa (fractional) bits
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.
System.out.println(num1);
System.out.println(num2);
60
The Complete Java Training Imran Afzal
4.8.5- Checking the Range of Float
When performing arithmetic operations with float, use the f suffix to avoid type incompatibility errors.
double is a 64-bit double-precision floating-point data type, typically used when higher precision is
required.
• 1 sign bit
• 11 exponent bits
• 52 mantissa bits
The d or D suffix is optional for double values since double is the default type for decimal numbers.
61
The Complete Java Training Imran Afzal
double total = price + (price * taxRate);
• 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.
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
Scientific notation is a way to represent very large or very small numbers in a compact form,
commonly used in mathematics, science, and engineering.
4.9.2- Format
• The exponent represents how many times the base should be multiplied by 10.
Example
• 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.
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
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.
25000000000.0
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.
2.68584E20
double pi = 3.14;
64
The Complete Java Training Imran Afzal
double circumference = 2 * pi * radius;
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.
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.
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
The char data type represents a single 16-bit Unicode character in Java. It can store letters, digits,
punctuation marks, and other symbols.
char ch = 'A';
System.out.println(ch);
Output
Note: Single quotes ' ' are used for char, while double quotes " " are used for String.
The char data type is useful for storing and manipulating individual characters. It is often used
in:
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.
System.out.println(uniCode);
Output
Since char includes all ASCII characters, it can store integer values corresponding to ASCII
codes.
System.out.println(ascii);
Output
67
The Complete Java Training Imran Afzal
• Used for text processing and manipulation.
System.out.println(company);
Output
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.
Output
Memory Usage Requires 2 bytes per Uses more memory (depends on the length
character of the string)
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.
This lecture provides a summary of all Java primitive data types covered in previous lectures. Each
type has a specific size, purpose, and usage.
Short 16- -32,768 to 32,767 Handles larger values than byte, used
bit in binary data.
70
The Complete Java Training Imran Afzal
4.11.2- Detailed Summary of Each Primitive Type
Example
byte b = 100;
Example
short s = 1500;
Example
71
The Complete Java Training Imran Afzal
Example
Example
Example
double d = 3.1415926535;
Example
72
The Complete Java Training Imran Afzal
8. boolean (1-bit logical value)
Example
Although not a primitive type, String is commonly used for text storage.
Example
Example int a = 100; double d = 5.75; char c = 'A'; boolean isTrue = true;
By now, you should be familiar with all Java primitive data types.
An operator in Java is a symbol that performs a specific operation on one, two, or three operands
and produces a result.
d = a + b * c;
• Since * has higher precedence than +, b * c is evaluated first, then added to a, and finally
assigned to d.
For example:
5+2
• + is the operator.
• The result is 7.
74
The Complete Java Training Imran Afzal
int x = 10;
int y = 5;
• Integers (int)
• Characters (char)
• Strings (String)
Example
System.out.println(result);
Output
Hello World
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:
• Assignments (x = 15 + 12;)
• Control statements (if (x > y) { })
• Loops (while (x < 100) { })
• 6 + 7 is an expression.
/*
76
The Complete Java Training Imran Afzal
This is a multi-line comment.
*/
+ Addition 10 + 5 15
- Subtraction 10 - 5 5
* Multiplication 10 * 5 50
/ Division 10 / 2 5
% Modulus (Remainder) 10 % 3 1
int x = 10;
int y = 5;
int sum = x + y;
System.out.println(sum);
Output
15
int x = 10;
77
The Complete Java Training Imran Afzal
int y = 5;
int result = x - y;
System.out.println(result);
Output
int x = 10;
int y = 5;
int product = x * y;
System.out.println(product);
Output
50
int x = 10;
int y = 3;
int remainder = x % y;
System.out.println(remainder);
78
The Complete Java Training Imran Afzal
Output
Output
131
• 65 + 66 = 131.
Output
AB
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
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:
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 x = 5;
int x = 5;
81
The Complete Java Training Imran Afzal
int y = ++x; // x becomes 6, then y is assigned 6.
Another way to increment a value is with the compound assignment operator (+=).
int x = 10;
int y = 5;
x += y; // x becomes 15
int x = 5;
82
The Complete Java Training Imran Afzal
Example: Prefix Decrement (--x)
int x = 5;
The multiplication (*=) and division (/=) operators allow quick updates of variable values.
int x = 10;
int y = 5;
x *= y; // x becomes 50
83
The Complete Java Training Imran Afzal
Example: Division (/=)
int x = 10;
int y = 5;
x /= y; // x becomes 2
The compound assignment operator can also be used with floating-point numbers (double).
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
86
The Complete Java Training Imran Afzal
5.1- Introduction to 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.
An IDE is a software application designed to streamline the coding process by integrating essential
development tools within a single platform. These typically include:
• 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.
There are several IDEs available for Java development, each with unique features. Some widely used
Java IDEs include:
87
The Complete Java Training Imran Afzal
• IntelliJ IDEA – A feature-rich IDE that provides intelligent coding assistance and powerful
debugging tools.
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.
• Intelligent Code Completion – Suggests relevant variable names, methods, and classes,
accelerating coding speed and improving accuracy.
• 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.
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.
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.
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.
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.
IntelliJ IDEA is now successfully installed on Windows. The next step is exploring its features and
configuring it for Java development.
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.
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
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.
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.
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.
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."
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.
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.
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 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 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.
System.out.print("Hello World!");
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.
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.
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");
The println method moves the cursor to the next line after printing the message, whereas print does
not.
and running the code will display the updated message in the console.
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:
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:
System.out.print("Hello, Nixware");
System.out.print("American Company");
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.
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 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.
if (condition) {
• Conditional Execution: Ensures that certain statements execute only when a specified
condition is met, improving efficiency and adaptability.
• Better Error Handling: Helps detect invalid inputs and error conditions, preventing
unexpected crashes.
• 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.
if (flag) {
System.out.println("Flag is True");
Output
Flag is true
97
The Complete Java Training Imran Afzal
Example 2: Checking a Boolean Variable
if (user == true) {
System.out.println("It's True");
Output
It's true
int x = 5;
if (x > 0) {
System.out.println("X is Positive");
• Variable x is assigned 5.
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.
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.
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.
• Useful in control flow: if, else if, while, and for loops.
99
The Complete Java Training Imran Afzal
• Improve performance and logic structure in complex programs.
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.
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
System.out.println("Eligible");
• gender.equals("male") (true)
Since one of the conditions is false, the overall result is false, and the statement is not executed.
if (a < b && b == c) {
d = a + b + c;
In this case:
101
The Complete Java Training Imran Afzal
• a < b is true (10 < 20)
Both conditions are true, so the code inside the if block runs:
• d = 10 + 20 + 20 = 50
Using logical operators allows combining checks efficiently. They’re fundamental for building
intelligent, branching logic in Java programs.
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:
int x = 4, y = 5;
if (x > 4 || y > 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.
103
The Complete Java Training Imran Afzal
double examScore = 85;
Output
int num = 6;
if (num % 2 == 0 || num % 3 == 0) {
104
The Complete Java Training Imran Afzal
}
Output
• Concise and easy to read – Multiple conditions can be checked in a single line.
• Complexity – Checking many conditions can make the code harder to read.
• Lack of granularity – The OR operator only indicates if a condition is true, not which one
specifically.
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.
105
The Complete Java Training Imran Afzal
int num = 7;
if (!(num % 2 == 0)) {
Output
The condition num % 2 == 0 evaluates to false, but the NOT operator (!) inverts it to true, so the if
block executes.
• Error handling – Useful for checking conditions and handling errors efficiently.
• Complexity – Can make code harder to understand when combined with other logical
operators.
106
The Complete Java Training Imran Afzal
Both OR (||) and NOT (!) operators are essential for implementing decision-making logic
efficiently in Java programs.
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;
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.
107
The Complete Java Training Imran Afzal
5.9.4- Practical Examples of Assignment Operators
Create a class file named AssignOperator and add the following code:
int x = 10;
x += 5; // Equivalent to x = x + 5;
Output
Updated value of x: 15
Create a class file named ComparisonExample and implement the following code:
int x = 5, y = 5;
if (x == y) {
108
The Complete Java Training Imran Afzal
System.out.println("Both Are Equal");
Output
The == operator compares the values of x and y. Since both are equal, the condition evaluates to true,
and the message is printed.
boolean z = true;
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.
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.
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.
110
The Complete Java Training Imran Afzal
• Loop Control – Used in while, do-while, and for loops to define iteration conditions.
• Flexibility – Works with different data types (integers, floats, strings, objects).
• 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.
int x = 5, y = 3, z = 4;
111
The Complete Java Training Imran Afzal
}
• 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.
String grade;
grade = "A";
System.out.println(grade);
In this example:
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
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.
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
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
System.out.println(isAmerican);
In this example:
Note: It’s important to use .equals() when comparing string content instead of ==.
String status;
System.out.println(status);
Here:
114
The Complete Java Training Imran Afzal
• status is assigned "Senior".
• Output: Senior
String result;
System.out.println(result);
• Output: Pass
String result;
System.out.println(result);
115
The Complete Java Training Imran Afzal
5.11.3- Advantages of the Ternary Operator
Summary of Examples
The ternary operator streamlines conditional assignments and makes your code more concise and
readable.
Java provides various operators that perform different operations within a program. These include:
• 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:
Create a class file named ChallengeOne and implement the following code:
Output
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.
Create a class file named ChallengeTwo and implement the following code:
117
The Complete Java Training Imran Afzal
public class challangeTwo {
int val = 5;
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.
Create a class file named ChallengeThree and implement the following code:
118
The Complete Java Training Imran Afzal
if (num1 <= num2) {
Output
This uses the less than or equal to (<=) comparison operator. Since 10 <= 15, the condition
evaluates to true.
Create a class file named ChallengeFour and implement the following code:
int x = 25;
int y = 30;
int z = 35;
119
The Complete Java Training Imran Afzal
System.out.println("The condition is true.");
• x > 20 is true
Since both the main condition and the grouped condition are true, the message is printed.
Output
Create a class file named ChallengeFive and implement the following code:
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
122
The Complete Java Training Imran Afzal
6.1- Keywords and Expressions 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.
Corrected version:
IDEs such as IntelliJ IDEA will highlight such issues with red underlines, making them easy to identify
and correct.
Example:
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.
Here:
int x = 5;
124
The Complete Java Training Imran Afzal
if (x > 3) {
x = x + 10;
In this example:
Term Description
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
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.
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:
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.
• 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.
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:
127
The Complete Java Training Imran Afzal
public static void main(String[] args){
int x=10;
if(x>5){
int x = 10;
if (x > 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.
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 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.
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.
else {
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".
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.
if(letter == 'a' || letter == 'e' || letter == 'i' || letter == 'o' || letter == 'u') {
130
The Complete Java Training Imran Afzal
else {
Since the value assigned to letter is 'a', which is one of the vowels, the output will be "The Letter is a
Vowel".
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.
System.out.println("Grade A");
System.out.println("Grade B");
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.
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:
System.out.println("Child");
132
The Complete Java Training Imran Afzal
else if(age >= 18 && age <= 21){
System.out.println("Teenager");
System.out.println("Adult");
else {
System.out.println("Senior Citizen");
Since age is 30, the third condition matches, and the output is "Adult".
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".
if(day == "Monday") {
System.out.println("Monday Blues");
133
The Complete Java Training Imran Afzal
System.out.println("Tuesday");
System.out.println("Hump Day");
System.out.println("Almost Friday");
System.out.println("TGIF");
System.out.println("Weekend");
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 ==.
This example determines a tax bracket based on a given salary using ladder if-else statements.
System.out.println("Tax Bracket:Low");
System.out.println("Tax Bracket:Medium");
135
The Complete Java Training Imran Afzal
System.out.println("Tax Bracket:High");
else {
This program calculates BMI and categorizes it using a ladder if-else structure.
System.out.println("Under Weight");
System.out.println("Normal Weight");
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.
System.out.println("Excellent");
System.out.println("Good");
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.
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.
• If condition2 is true, a block of code executes. Otherwise, the inner else executes.
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) {
else {
else {
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.
139
The Complete Java Training Imran Afzal
if(purchaseAmount > 100) {
if(isMember == "yes") {
else {
else {
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"))
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.
• 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.
The main method is the entry point for any Java application and follows this syntax:
141
The Complete Java Training Imran Afzal
public static void printString() {
System.out.println("Hello World");
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.
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.
• 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.
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.
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.
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 {
calculateArea(10, 3.14);
The method calculateArea accepts radius and pi as parameters and prints the computed area directly.
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.
145
The Complete Java Training Imran Afzal
double result = radius * pi;
return result;
In this modified method, calculateArea returns a double value. The caller captures the result in a
variable named area and then prints it.
This example demonstrates how to define a method that adds two integers and returns the result using
the return statement.
return a + b;
146
The Complete Java Training Imran Afzal
}
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.
• 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.
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.
• 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 Parameters: Optional variables that the method accepts as input, enclosed in
parentheses. If no parameters are needed, an empty () is used.
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.
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.
To avoid such issues, ensure that all execution paths return a value:
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.
Void methods do not return data, but you can still use return; to exit early:
if (number <= 1) {
return;
149
The Complete Java Training Imran Afzal
}
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.
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.
Here, both methods share the same name but accept different parameters, making them valid
overloads.
The main method in Java serves as the program's entry point and has a strict signature that the JVM
recognizes:
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.
This program defines a method to check whether a given integer is even or odd.
if (number % 2 == 0) {
return true;
} else {
return false;
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"
This program calculates a student's position based on their marks and prints their name with the
assigned position.
152
The Complete Java Training Imran Afzal
return 0;
return 5;
return 4;
return 3;
return 2;
} else {
return 1;
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:
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:
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.
double result;
if (tempChar == 'F') {
} else {
155
The Complete Java Training Imran Afzal
result = 0;
return result;
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.
This program determines whether a given year is a leap year based on divisibility rules.
} else {
157
The Complete Java Training Imran Afzal
int checkYear = 2019;
checkLeapYear(checkYear);
• Divisible by 400, or
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.
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.
158
The Complete Java Training Imran Afzal
• printDistance(double firstValue, String valueType, double secondValue, String
convertedValue): Prints the conversion result.
PrintDistance(miles,"Miles",kilometers,"Kilometers");
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) {
public static void PrintDistance(double firstValue, String valueType, double secondValue, String
convertedValue ){
System.out.println(firstValue + " " + valueType + " is equal to " + secondValue + " " +
convertedValue);
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
This program calculates the perimeter and area of both rectangles and squares using methods for
structured computation.
161
The Complete Java Training Imran Afzal
// Method to calculate square perimeter
return 4 * side;
int rectangleLength = 5;
int squareSide = 5;
162
The Complete Java Training Imran Afzal
System.out.println("Rectangle Area: " + rectangleArea(rectangleLength, rectangleWidth));
Rectangle Perimeter: 30
Rectangle Area: 50
Square Perimeter: 20
Square Area: 25
The program efficiently calculates and prints the perimeter and area using methods.
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.
• 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.
add(10, 20);
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);
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.
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.
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.
166
The Complete Java Training Imran Afzal
}
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.
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.
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.
Create a class named ConcatenationStrings with a main method and two overloaded concatenate
methods:
return s1 + s2;
168
The Complete Java Training Imran Afzal
}
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.
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:
Create a class named LengthConverter with a main method and two overloaded convert methods:
170
The Complete Java Training Imran Afzal
public static double convert(double length, char unit) {
if (unit == 'f') {
} else {
return 0;
171
The Complete Java Training Imran Afzal
6.16.3- Program Execution and Output
This example demonstrates how method overloading allows flexibility in handling different input
types while maintaining a clear and readable code structure.
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.
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".
Create a class named TimeConverter with a main method and two overloaded timeConvert
methods:
172
The Complete Java Training Imran Afzal
public static String timeConvert(int seconds) {
return timeConvert(12350);
System.out.println(result);
173
The Complete Java Training Imran Afzal
6.17.3- Program Execution and Output
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.
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.
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.
Create a class named Car with a main method and three overloaded carFunctions methods:
class Car {
174
The Complete Java Training Imran Afzal
return "Mileage: " + (distance / fuel) + " km/l";
System.out.println(result);
175
The Complete Java Training Imran Afzal
6.18.3- Program Execution and Output
This program demonstrates method overloading by allowing the carFunctions method to handle
different input types while keeping the code structure clean and efficient.
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.
1. Personal Information Storage: Takes name, age, and address as parameters and returns a
formatted string containing personal 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 {
176
The Complete Java Training Imran Afzal
return "Person Information: \nName - " + name + "\nAge - " + age + " years \nAddress - "
+ address;
177
The Complete Java Training Imran Afzal
System.out.println(getInfo);
Person Information:
Name - John
Age - 35 years
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.
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.
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:
class Age {
public static int findAge(int birth_year, int current_year, int birth_month, int current_month, int
birth_day, int current_day) {
return ageInDays;
179
The Complete Java Training Imran Afzal
}
if (ch == 'y') {
} else {
return ageValue;
if (checkNegative < 0) {
checkNegative = -1 * checkNegative;
180
The Complete Java Training Imran Afzal
return checkNegative;
System.out.println(getYears + " Years " + getMonth + " Months " + getDays + " 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
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.
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.
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).
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.
• 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.
• 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 Short-Circuit Evaluation: All cases are evaluated, even if an earlier match is found, which
may impact performance.
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");
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.
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.
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
}
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.
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.
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");
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.
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.
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.
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.
class Main {
191
The Complete Java Training Imran Afzal
String size;
switch (number) {
case 29:
size = "Small";
break;
case 42:
size = "Medium";
break;
case 44:
size = "Large";
break;
192
The Complete Java Training Imran Afzal
case 48: case 49: case 50:
break;
default:
size = "Unknown";
break;
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.
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.
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.
// Traditional Switch
switch (dayName) {
194
The Complete Java Training Imran Afzal
case "Sunday":
case "Monday":
case "Tuesday":
case "Wednesday":
case "Thursday":
case "Friday":
case "Saturday":
// Enhanced Switch
195
The Complete Java Training Imran Afzal
case "Sunday", "Monday", "Tuesday" -> "First Three days 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.
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.
class EnhanceSwitch{
String levelString;
int level = 3;
};
197
The Complete Java Training Imran Afzal
System.out.println("Your Level is: " + levelString);
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".
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";
};
• This is a Consonant
• If ch = 'a', the output would be:
• This is a Vowel
If a default case includes a code block, the yield keyword must be used instead of return.
default -> {
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
double areaResult;
default -> 0;
};
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
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.
String password;
201
The Complete Java Training Imran Afzal
case "Doe" -> "A123";
default -> {
yield password;
};
return password;
• John: F143
• If username = "Mike", the output would be:
• Mike: Invalid User Name
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.
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
• 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.
• 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.
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.
This program categorizes students into different grades and then further categorizes their subjects
using a nested switch statement.
int grade = 2;
switch (grade) {
case 1:
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:
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;
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
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.
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.
// Use this editor to write, compile and run your Java code online
class TraditionalSwitchExample{
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:
break;
break;
case 2:
switch (y) {
case 2:
break;
case 3:
break;
default:
209
The Complete Java Training Imran Afzal
System.out.println("x is 1 and y is 3");
break;
break;
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".
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 {
int year = 2;
210
The Complete Java Training Imran Afzal
switch (year) {
case 1:
break;
case 2:
switch (Branch) {
case "CSE":
case "CCE":
break;
case "ECE":
System.out.println(
break;
default:
break;
211
The Complete Java Training Imran Afzal
}}
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".
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.
switch (size) {
case 'S':
switch (toppings) {
212
The Complete Java Training Imran Afzal
case 'P':
break;
case 'C':
break;
case 'V':
break;
default:
System.out.println("Invalid topping!");
break;
break;
case 'M':
switch (toppings) {
213
The Complete Java Training Imran Afzal
case 'P':
break;
case 'C':
break;
case 'V':
break;
default:
System.out.println("Invalid topping!");
break;
break;
case 'L':
switch (toppings) {
214
The Complete Java Training Imran Afzal
case 'P':
break;
case 'C':
break;
case 'V':
break;
default:
System.out.println("Invalid topping!");
break;
break;
default:
215
The Complete Java Training Imran Afzal
break;
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.
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.
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.
• Code Reusability: Allows execution of repetitive tasks without manually writing multiple
lines.
• Flexibility: Loops execute code dynamically based on user input or runtime conditions.
• Improved Readability: Makes the code more structured and easier to maintain.
• 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.
The for loop executes a block of code for a fixed number of times, defined by initialization, condition
checking, and an update statement.
// Code to be executed
217
The Complete Java Training Imran Afzal
}
System.out.println(i);
Output
218
The Complete Java Training Imran Afzal
4
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.
while (condition) {
// Code to be executed
int i = 0;
System.out.println(i);
219
The Complete Java Training Imran Afzal
}
Output
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);
int i = 1;
do {
System.out.println(i);
i++;
221
The Complete Java Training Imran Afzal
Output
Use Case When the number of When the number of When at least one
iterations is known iterations is unknown execution is required
Execution May not execute May not execute Executes at least once
Guarantee
• Use enhanced loops (for-each) for iterating collections instead of traditional loops.
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.
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.
// 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.
System.out.println(i);
223
The Complete Java Training Imran Afzal
Execution Flow
5. Steps 2–4 repeat until i becomes 6, at which point the condition becomes false, and the loop
terminates.
Output
if (i == 5) {
224
The Complete Java Training Imran Afzal
if (i == 8) {
System.out.println(i);
Execution Flow
Output
225
The Complete Java Training Imran Afzal
7.10.4- Example 3: Checking Prime Numbers Using For Loop
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.
3. Check divisibility for numbers from 2 to n/2. If any number divides n completely, it is not
prime.
Part 1
class HelloWorld {
226
The Complete Java Training Imran Afzal
System.out.println("21 is " + (isPrime(21) ? "" : "Not ") + "a prime number.");
if (wholeNumner % divisor == 0) {
return false;
return true;
227
The Complete Java Training Imran Afzal
}
Part 2
// Use this editor to write, compile and run your Java code online
class HelloWorld {
228
The Complete Java Training Imran Afzal
}
if (wholeNumner % divisor == 0) {
return false;
return true;
229
The Complete Java Training Imran Afzal
Execution Flow
Output
2 is a prime number
3 is a prime number
17 is a prime number
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.
if (number <= 2) {
return number == 2;
230
The Complete Java Training Imran Afzal
}
if (number % divisor == 0) {
return false;
return true;
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.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:
if (number <= 0) {
return false;
• 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
return -1;
int sum = 0;
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).
int startNumner = 1;
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
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.
int num = 5;
234
The Complete Java Training Imran Afzal
}
• 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
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.
2. Machine-Level Debuggers – Used for debugging at a lower level, such as assembly language,
to inspect memory and processor instructions.
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.
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.
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.
4. Test the Fix – Validate that the issue is resolved and does not introduce new errors.
6. Monitor the Fix – Ensure the fix remains effective over time.
• 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.
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.
• 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.
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.
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.
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.
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.
count++;
break;
239
The Complete Java Training Imran Afzal
Running the Program
These four numbers meet the condition and are printed before the loop terminates.
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.
240
The Complete Java Training Imran Afzal
Running the Debugger
4. The debug tool window appears, showing breakpoints, variable states, and the execution
flow.
• The right panel allows adding watch expressions to track values dynamically.
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.
• Step Over – Executes one line at a time without entering method calls.
• Step Out – Exits the current method and returns to the caller.
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.
With these modifications, execution only stops when significant conditions are met.
By strategically setting breakpoints and using stepping functions, the debugger simplifies error
identification, making troubleshooting more effective.
242
The Complete Java Training Imran Afzal
2. Condition – Determines when the loop should continue or terminate.
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.
The following for loop prints values of myNumber in increments of 3, starting from 0, and stopping
before 12.
System.out.println(myNumber);
• Condition Check: myNumber < 12. If true, the loop executes; otherwise, it terminates.
• Loop Execution: The statement inside {} executes, printing the current value of myNumber.
Iteration 1:
• Initialization: myNumber = 0
243
The Complete Java Training Imran Afzal
• Increment: myNumber = 0 + 3 = 3
Iteration 2:
• Increment: myNumber = 3 + 3 = 6
Iteration 3:
• Increment: myNumber = 6 + 3 = 9
Iteration 4:
• Increment: myNumber = 9 + 3 = 12
• 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 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.
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).
4. Update (Increment/Decrement) – A way to modify the loop variable inside the loop to
eventually make the condition false.
class Whileloop{
System.out.println(number);
245
The Complete Java Training Imran Afzal
number--;
Execution Process
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) {
System.out.println(number);
number--;
checkEvenOdd(start);
start++;
247
The Complete Java Training Imran Afzal
}
if(num % 2 == 0) {
else {
Execution Process
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
if (number < 0) {
return -1;
firstDigit /= 10;
249
The Complete Java Training Imran Afzal
return firstDigit + lastDigit;
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
250
The Complete Java Training Imran Afzal
Output
• Initialization is done before the loop, and update logic inside the loop.
• The while loop is useful for cases where iterations depend on user input, calculations,
or unknown conditions.
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.
if (CheckPalindrome(num)) {
} else {
251
The Complete Java Training Imran Afzal
System.out.println(num + " is not a palindrome number");
int reverse = 0;
while (palindrome != 0) {
252
The Complete Java Training Imran Afzal
Execution Process
2. Append digits to reverse by multiplying reverse by 10 and adding the extracted digit.
Output
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.
if (number < 0) {
253
The Complete Java Training Imran Afzal
int sum = 0;
if (digit % 2 == 0) {
number /= 10;
return sum;
254
The Complete Java Training Imran Afzal
int negativeEvenSum = EvenNumSum(negativeNumber);
Execution Process
Output
• 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
if (number < 1) {
System.out.println("Invalid Value");
return;
int i = 1;
if (number % i == 0) {
256
The Complete Java Training Imran Afzal
System.out.print(i + " ");
i++;
System.out.println();
Execution Process
Output
Factors of 10 are: 1 2 5 10
Factors of 20 are: 1 2 4 5 10 20
• 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.
These exercises help in understanding looping, conditional checks, and modulus operations in
Java efficiently.
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.
258
The Complete Java Training Imran Afzal
if (first < 10 || second < 10) {
return -1;
while (second != 0) {
first = temp;
return first;
int a = 24;
int b = 36;
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.
Output
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.
A do-while loop is similar to a while loop, but with one key difference:
• 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.
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.
class HelloWorld {
int i = 6;
do {
System.out.println(i);
i++;
Explanation
261
The Complete Java Training Imran Afzal
2. The condition i < 5 is false (since i = 7 after incrementing).
Output
Since the condition is false, the loop executes only once before stopping.
do {
x--; // Decrease x by 1
Execution Process
262
The Complete Java Training Imran Afzal
Output
Summation: 176
21 + 20 + 19 + 18 + 17 + 16 + 15 + 14 + 13 + 12 + 11 = 176
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.
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.
while (condition) {
// Code block
263
The Complete Java Training Imran Afzal
7.19.3- Key Characteristics
• If the condition is false from the start, the loop will not execute at all.
• Any variable used in the condition must be declared outside the loop.
count++; // Iteration
Output
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
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.
• Useful when at least one execution of the loop is required, such as reading user input.
do {
num--;
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.
265
The Complete Java Training Imran Afzal
Guaranteed Execution No Yes (at least once)
Both while and for loops achieve similar logic, but their syntax is different.
count++; // Iteration
Differences
266
The Complete Java Training Imran Afzal
7.19.10- Using Continue and Break in While Loops
Break Statement
int num = 1;
if (num == 5) {
System.out.println(num);
num++;
Output
267
The Complete Java Training Imran Afzal
7.19.11- Continue Statement
int num = 0;
num++;
if (num == 3) {
System.out.println(num);
Output
• While Loop: Executes only if the condition is true from the start.
268
The Complete Java Training Imran Afzal
• For Loop: Has a more compact syntax than a while loop.
• 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.
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.
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.
Program
System.out.print(i*j + "\t");
System.out.println();
How It Works
• 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
• 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.
• 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
By understanding nested loops, you can efficiently handle multi-dimensional operations and
complex repetitive tasks in Java.
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.
• 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
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
if (number < 2) {
return -1;
while (number % i == 0) {
largestPrime = i;
273
The Complete Java Training Imran Afzal
number /= i;
return largestPrime;
• 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".
• It should contain:
*****
***
***
***
*****
SquareStar(11);
275
The Complete Java Training Imran Afzal
if (number < 5) {
System.out.println("Invalid Value");
return;
System.out.print("*");
} else {
System.out.print(" ");
System.out.println();
276
The Complete Java Training Imran Afzal
How It Works
***********
** **
** **
* * * *
* ** *
* * *
* ** *
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.
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:
Example Output
Input Output
0 Zero
278
The Complete Java Training Imran Afzal
-123 Invalid Value
if (number < 0) {
System.out.println("Invalid Value");
return;
if (number == 0) {
System.out.println("Zero");
return;
279
The Complete Java Training Imran Afzal
// loop through each digit of the number
temp = number % j;
digit = temp / i;
if (digit == k) {
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;
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);
• If the number is negative, it prints "Invalid Value" and exits the method.
2- Extracting Digits
• The switch statement maps each digit (0-9) to its word equivalent.
• Since digits are extracted in reverse order, words are concatenated in reverse to maintain
correct sequence.
283
The Complete Java Training Imran Afzal
Program Output
Zero
Invalid Value
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.
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.
284
The Complete Java Training Imran Afzal
• In another method or class unless passed as an argument.
System.out.println(secondNumber);
• 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 (int i = 1; i <= 5; i++) { // 'i' is a local variable inside the for loop
The variable i is only available inside the for loop and cannot be used afterward.
int temp = count * 2; // Local variable only for this loop block
286
The Complete Java Training Imran Afzal
count++;
count is accessible throughout the method because it is declared outside the loop.
temp is only available inside the while loop.
if (number > 5) {
287
The Complete Java Training Imran Afzal
The variable value only exists inside the if block.
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.
int choice = 2;
switch (choice) {
case 1:
break;
case 2:
break;
case 3:
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.
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.
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.
String brand;
int speed;
myCar.speed = 120;
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.
291
The Complete Java Training Imran Afzal
obj2.instanceVar = 300; // Changing instance variable for obj2
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.
Static Methods
Instance Methods
• Belong to an object
292
The Complete Java Training Imran Afzal
7.24.8- Example: Static vs. Instance Methods
void instanceMethod() {
293
The Complete Java Training Imran Afzal
• Using string literals: String text = "Hello";
• Using the new keyword: String text = new String("Hello");
String literals are stored in a special memory area called the string pool.
toUpperCase() is an instance method that modifies the string.
Wrapper classes allow primitive data types to be used as objects and provide useful static methods.
294
The Complete Java Training Imran Afzal
int parsedNumber = Integer.parseInt("123"); // Static method
Java classes and objects form the foundation of object-oriented programming, allowing data and
behaviors to be encapsulated, reused, and efficiently managed.
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.
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 {
295
The Complete Java Training Imran Afzal
int currentYear = 2023;
Java wrapper classes (Integer, Double, Boolean, etc.) allow conversions between strings and primitive
data types.
296
The Complete Java Training Imran Afzal
double price = Double.parseDouble("15.99"); // Converts string to double
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:
FromConsole();
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.
Since IntelliJ disables System.console(), running this program in the terminal is necessary.
javac ParseValues.java
java ParseValues
By using parsing methods and user input handling techniques, Java programs can efficiently process
data from different sources.
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
• nextInt(): Reads the next token as an integer. Throws an exception if the token cannot be
parsed as an integer.
• 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.
• nextByte(), nextShort(), nextLong(): Reads the next token as a byte, short, or long respectively.
To use the Scanner class, an instance must be created. The new keyword is used for this purpose.
The Scanner class is part of the java.util package, so it must be imported at the beginning of the Java
program.
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;
System.out.println("Your name is " + name + " and you are " + age + " years old");
scanner.close();
Explanation
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.
300
The Complete Java Training Imran Afzal
5. The scanner.close() method is called to release system resources.
This program checks whether two integers in the range of 10-99 share at least one digit.
import java.util.Scanner;
if ((num1 < 10 || num1 > 99) || (num2 < 10 || num2 > 99)) {
return false;
301
The Complete Java Training Imran Afzal
public static void main(String[] args) {
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.
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.
6. It calls sharedDigit() to determine whether the numbers have a shared digit and prints the
result.
Example Runs
Input 1:
Output 1:
Input 2:
Output 2:
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
Create a Java class named MegaBytesConverter and import the Scanner class.
import java.util.Scanner;
MegaBytesKiloBytes(kiloBytes);
if (kiloBytes < 0) {
304
The Complete Java Training Imran Afzal
System.out.println("Invalid input. Please enter a non-negative number.");
} else {
Explanation
1. MegaBytesKiloBytes(int kiloBytes):
2. main(String[] args):
Example Runs
Input 1:
305
The Complete Java Training Imran Afzal
Output 1:
Input 2:
Output 2:
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.
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.
Create a Java class named TeenNumberChecker and import the Scanner class.
import java.util.Scanner;
306
The Complete Java Training Imran Afzal
public static boolean FindTeen(int num1, int num2, int num3) {
isTeen = true;
return isTeen;
307
The Complete Java Training Imran Afzal
int num1 = scanner.nextInt();
if (isTeen) {
} else {
Explanation
• 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):
3. main(String[] args):
Example Runs
Input 1:
Output 1:
Input 2:
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.
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:
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.
Create a Java class named CatAlarm and import the Scanner class.
import java.util.Scanner;
310
The Complete Java Training Imran Afzal
return false;
return true;
} else {
return false;
311
The Complete Java Training Imran Afzal
int hourOfDay = scanner.nextInt();
if (shouldWakeUp) {
System.out.println("Wake up!");
} else {
System.out.println("Keep sleeping.");
Explanation
2. main(String[] args):
Example Runs
Input 1:
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:
Output 2:
Keep sleeping.
Explanation:
The cat is not yowling, so the program advises the user to keep sleeping.
Input 3:
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
• 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
315
The Complete Java Training Imran Afzal
8.1- Classes and 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.
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).
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.
2. Inside the src folder, create a new class file named CentralClass.
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.
pen.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.
These are instance fields because they are not marked static and will hold different values for each
object created from the Pen class.
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.
pen.describePen();
Since no values are assigned to the fields, the output will show null values for each.
• boolean: false
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
return manufacturer;
Generated methods:
return color;
return type;
320
The Complete Java Training Imran Afzal
8.1.12- Using Getters in the Main Method
This removes direct access to private fields and adheres to the encapsulation principle.
pen.manufacturer = "Garwood";
pen.color = "Black";
pen.type = "Fountain";
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.
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.
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.
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.
322
The Complete Java Training Imran Afzal
pen.setColor("Black");
pen.setType("Fountain");
Running this updates the pen object with the specified values.
To ensure data integrity, validation logic can be added within setter methods. Here's how validation is
applied in the setManufacturer method:
if (manufacturer == null) {
manufacturer = "Unknown";
switch (lowerCaseManufacturer) {
case "franklin":
case "schon":
case "noodler":
this.manufacturer = manufacturer;
323
The Complete Java Training Imran Afzal
break;
default:
this.manufacturer = "Unsupported";
• The switch statement checks if the value matches supported manufacturers: "Franklin",
"Schon", or "Noodler".
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.
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.
1. Create a class file named MainClass and add the main method using the shortcut psvm.
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.
return 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:
return eName;
326
The Complete Java Training Imran Afzal
{
this.eName = eName;
return eDesignation;
this.eDesignation = eDesignation;
return eCompany;
327
The Complete Java Training Imran Afzal
{
this.eCompany = eCompany;
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.
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.
System.out.println(emp.makeString());
Output
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.
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.
1. Create a class file named MainClass and add the main method using the shortcut psvm.
Inside the MyCalculator class, define the following private instance variables:
329
The Complete Java Training Imran Afzal
private double firstNumber;
class MyCalculator {
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.
To allow controlled access to these private variables, define public getter and setter methods.
return firstNumber;
return secondNumber;
330
The Complete Java Training Imran Afzal
public void setFirstNumber(double firstNumber) {
this.firstNumber = firstNumber;
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.
331
The Complete Java Training Imran Afzal
}
if (secondNumber == 0) {
return 0;
} else {
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.
332
The Complete Java Training Imran Afzal
class MainClass{
calculator.setFirstNumber(10);
calculator.setSecondNumber(5);
These lines create an instance of MyCalculator and assign values to firstNumber and secondNumber
through setter methods.
333
The Complete Java Training Imran Afzal
System.out.println("Division result: " + divisionResult);
Output
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.
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.
1. Create a class file named MainClass and add the main method using the shortcut psvm.
334
The Complete Java Training Imran Afzal
8.5.2- Defining the Person Class
Inside the Person class, declare the following private instance variables:
class Person {
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.
return firstName;
335
The Complete Java Training Imran Afzal
public String getLastName() {
return lastName;
return age;
These methods allow external access to the field values without exposing them directly.
this.firstName = firstName;
this.lastName = lastName;
336
The Complete Java Training Imran Afzal
public void setAge(int age) {
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.
337
The Complete Java Training Imran Afzal
public String getFullName() {
return "";
} else if (lastName.isEmpty()) {
return firstName;
} else if (firstName.isEmpty()) {
return lastName;
} else {
338
The Complete Java Training Imran Afzal
Person person = new Person();
person.setFirstName("John");
person.setLastName("Doe");
person.setAge(25);
class MainClass
person.setFirstName("John");
person.setLastName("Doe");
person.setAge(25);
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.
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
• 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.
1. Default Constructor
A default constructor is a constructor that does not accept any parameters. It can be:
public Person() {
341
The Complete Java Training Imran Afzal
// Manually setting default values
In this example:
• 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.
342
The Complete Java Training Imran Afzal
Creating an object:
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:
this.name = name;
this.age = age;
343
The Complete Java Training Imran Afzal
Here, the Person object is created with name = "John" and age = 30.
Java allows multiple constructors within the same class, as long as they have different parameter lists.
This enables flexibility in object creation.
Example:
public Person() {
age = 0;
this.name = name;
this.age = age;
344
The Complete Java Training Imran Afzal
// Getter methods
return name;
return age;
// Setter methods
this.name = name;
this.age = age;
345
The Complete Java Training Imran Afzal
}
• 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.
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.
1. Create a class file named MainClass and use the shortcut psvm to generate the main method.
Inside the Library class, declare the following private instance variables:
346
The Complete Java Training Imran Afzal
class Library {
Declaring these variables as private ensures that they cannot be accessed directly from outside the
class, maintaining encapsulation.
// Constructor
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
return name;
return numBooks;
return address;
348
The Complete Java Training Imran Afzal
8.7.5- Setter Methods
this.name = name;
this.numBooks = numBooks;
this.address = address;
349
The Complete Java Training Imran Afzal
These methods provide a safe way to update the internal state of the object.
class MainClass {
System.out.println(myLibrary.getName());
System.out.println(myLibrary.getNumBooks());
System.out.println(myLibrary.getAddress());
myLibrary.setName("Downtown Library");
myLibrary.setNumBooks(10000);
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
Downtown Library
10000
The first set of values reflects those provided via the constructor, and the second set reflects the
updates made using setter methods.
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.
351
The Complete Java Training Imran Afzal
1. Create a class file named MainClass and use the shortcut psvm to generate the main method.
Inside the Car class, declare the following private instance variables:
import java.util.Scanner;
class Car {
// Constructor
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.
// Getter methods
return make;
return model;
return year;
353
The Complete Java Training Imran Afzal
8.8.5- Setter Methods
// Setter methods
this.make = make;
this.model = model;
this.year = year;
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 {
355
The Complete Java Training Imran Afzal
System.out.println("Model: " + myCar.getModel());
myCar.setYear(updatedYear);
Toyota
Corolla
2022
Make: Toyota
Model: Corolla
356
The Complete Java Training Imran Afzal
Year: 2022
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.
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.
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.
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:
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.
Now, anotherOffice is a second reference to the same object. Both myOffice and anotherOffice refer
to the same memory location.
The change will be reflected through both references because they point to the same object.
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".
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.
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.
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{
this.name = name;
360
The Complete Java Training Imran Afzal
}
class MainClass{
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{
Cat.name = name;
362
The Complete Java Training Imran Afzal
class MainClass{
Static methods are typically used for operations that do not require access to instance-specific data.
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{
363
The Complete Java Training Imran Afzal
System.out.println("Company Name is: " + name + " & Company Location is: " + location);
class MainClass{
Company.CompanyName("Nixware","New York");
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.
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.
• 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.
try {
} catch (Exception e) {
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.
try {
} catch (FileNotFoundException e) {
} catch (IOException e) {
366
The Complete Java Training Imran Afzal
// code to handle input/output exception
} catch (Exception e) {
Each catch block addresses a specific type of exception, allowing targeted handling. If no exception
occurs, all catch blocks are bypassed.
try {
} catch (Exception e) {
} finally {
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.
• 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.
class Exce {
368
The Complete Java Training Imran Afzal
int d = 0;
This code throws an ArithmeticException due to division by zero. Since there's no exception handling,
the program terminates and displays an error message.
class Exce {
try {
int d = 0;
int a = 42 / d;
} catch (ArithmeticException e) {
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.
import java.util.Scanner;
import java.util.Scanner;
class DivideException {
float result = a / b;
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.
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.
This occurs because the exception is not handled in the doDivide method.
try{
float result = a / b;
}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.
• 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.
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.
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().
374
The Complete Java Training Imran Afzal
}
public Vehicle() { }
public Car() {
super();
The super() call invokes the parent constructor and must be the first statement in any constructor.
vahicle.service(month);
vahicle.move(speed);
System.out.println(vahicle);
375
The Complete Java Training Imran Afzal
System.out.println("____");
doVahicleStuff(vahicle,"Fast",1);
doVahicleStuff(car,"Fast",2);
Since the constructor doesn't set any values, the printed fields appear as null or 0.
public Car() {
376
The Complete Java Training Imran Afzal
Now, running the main method shows the fields with correct values.
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);
This constructor uses default values for numberOfDoors and horsepower, and chains to the four-
argument constructor.
@Override
377
The Complete Java Training Imran Afzal
public String toString() {
return "Car{" +
"NumberofDoors=" + NumberofDoors +
"}
378
The Complete Java Training Imran Afzal
@Override
@Override
super.move(speed);
This is an example of extending behavior: executing the superclass method first, then adding subclass-
specific functionality.
379
The Complete Java Training Imran Afzal
private double width;
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:
To allow default object creation when extended, a no-argument constructor is also defined:
public Rectangle() { }
return width;
380
The Complete Java Training Imran Afzal
return length;
The getArea method calculates and returns the area using the stored dimensions.
super(width, length);
381
The Complete Java Training Imran Afzal
}
return 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.
382
The Complete Java Training Imran Afzal
double volume = pool.getVolume();
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:
A POJO:
383
The Complete Java Training Imran Afzal
// Valid POJO (Extends JDK class)
384
The Complete Java Training Imran Afzal
public class MyApp extends Demo
void someJDBCMethod()
Although MyJDBC is a POJO in structure, it uses code that requires external libraries to compile,
making it an exceptional case.
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.
this.id = id;
this.name = name;
this.dateOfBirth = dateOfBirth;
this.comanyName = comanyName;
@Override
return "Employee{" +
"id='" + id + '\'' +
386
The Complete Java Training Imran Afzal
", dateOfBirth='" + dateOfBirth + '\'' +
'}';
switch (i){
},
"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.
• Automatically generates private final fields, constructors, accessors, and toString, equals, and
hashCode methods
public record NCEmployee(String id, String name, String dateOfBirth, String companyName) { }
• Can be mutable
388
The Complete Java Training Imran Afzal
• Flexible for logic and field updates
Record:
• Immutable by default
Note: Accessor methods in records match the field names directly and do not use get prefixes.
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.
389
The Complete Java Training Imran Afzal
private double height;
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;
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.
return width;
if (width < 0) {
this.width = 0;
} else {
this.width = width;
return height;
391
The Complete Java Training Imran Afzal
}
if (height < 0) {
this.height = 0;
} else {
this.height = height;
• Mutators (setters) allow modifying them, with validation to ensure only non-negative values
are accepted.
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.
393
The Complete Java Training Imran Afzal
}
Output:
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;
395
The Complete Java Training Imran Afzal
• width and length are private fields to ensure encapsulation.
if (cost < 0) {
this.cost = 0;
} else {
this.cost = cost;
return cost;
396
The Complete Java Training Imran Afzal
}
this.floor = floor;
this.carpet = carpet;
397
The Complete Java Training Imran Afzal
}
class MainClass{
Execution Summary
398
The Complete Java Training Imran Afzal
Console Output
This exercise demonstrates how to use basic POJO structures and object composition to solve a real-
world scenario in a structured, maintainable way.
The ComplexNumber class is designed to represent complex numbers of the form a + bi, where:
8.18.2- Fields
8.18.3- Constructor
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.
return this.real;
return this.imaginary;
These methods allow controlled access to the real and imaginary fields.
this.real += real;
this.imaginary += imaginary;
this.real += other.getReal();
this.imaginary += other.getImaginary();
400
The Complete Java Training Imran Afzal
These methods support adding either:
this.real -= real;
this.imaginary -= imaginary;
this.real -= other.getReal();
this.imaginary -= other.getImaginary();
These methods allow subtracting values or another ComplexNumber from the current instance.
class MainClass{
401
The Complete Java Training Imran Afzal
ComplexNumber c1 = new ComplexNumber(5, 3);
c1.add(c2);
c3.subtract(c4);
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
402
The Complete Java Training Imran Afzal
This example successfully applies POJO design principles to model and manipulate complex numbers.
8.19.2- Fields
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.x = x;
this.y = y;
403
The Complete Java Training Imran Afzal
}
// Getter for x
return this.x;
// Getter for y
return this.y;
// Setter for x
this.x = x;
404
The Complete Java Training Imran Afzal
// Setter for y
this.y = y;
These methods allow external classes to read or modify the coordinates in a controlled way.
int x1 = 0;
int y1 = 0;
This method returns the distance between the current point and the origin (0,0).
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.
This method returns the distance between the current point and another Point object.
class MainClass{
p1.setX(3);
p1.setY(4);
406
The Complete Java Training Imran Afzal
// Getting the x and y values of p1
Sample Output
p1.x = 3
p1.y = 4
407
The Complete Java Training Imran Afzal
Distance between p1 and (0,0): 5.0
This practice reinforces the use of POJO classes to perform fundamental data operations and
showcases method overloading with real-world logic.
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.
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
class Employee {
this.name = name;
this.age = age;
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
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()).
To explore inheritance further, define a new class ExperiencedEmployee that extends Employee:
super(name, age);
this.employeeId = employeeId;
System.out.println(experienced);
Initially, this uses the toString() method from Employee, printing Anderson is 45.
410
The Complete Java Training Imran Afzal
@Override
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.
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.
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.
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.
We often use the this keyword in constructors or setters to resolve name conflicts between class fields
and parameters. Here's an example:
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.
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 {
@Override
413
The Complete Java Training Imran Afzal
}
class MainClass {
c.displayMessage();
Without super.displayMessage();, the overridden method would call itself recursively, causing a stack
overflow.
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().
Only one of them can be used in a constructor, and it must be the first statement.
class Triangle {
414
The Complete Java Training Imran Afzal
private int y;
private int z;
public Triangle() {
this.y = 0;
this.z = 0;
this.base = 0;
this.height = 0;
this.y = 0;
this.z = 0;
this.base = base;
this.height = height;
415
The Complete Java Training Imran Afzal
}
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.
class Triangle {
private int y;
private int z;
416
The Complete Java Training Imran Afzal
public Triangle() {
this(0, 0);
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.
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.
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.
• 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.
When a subclass overrides a method, it can still access the parent class's version of the method using
the super keyword:
419
The Complete Java Training Imran Afzal
System.out.println("Meow");
@Override
System.out.println("Meow Meow");
System.out.println("Meow");
420
The Complete Java Training Imran Afzal
public void Voice(int number) {
System.out.println("Meow");
• Overloading:
• Overriding:
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:
@Override
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.
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.
Prior to JDK 15, handling multiline strings in Java required concatenation and escape sequences.
For Example:
"\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.
"\U+279E Company"+
423
The Complete Java Training Imran Afzal
"\U+279E Nixware";
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.
• Improved Readability: Code becomes easier to read and understand when large strings
match the output format.
• Simplified Embedding: Ideal for embedding JSON, XML, HTML, or SQL into Java code.
• Cleaner Syntax: Helps keep source code visually aligned with output.
• Memory Usage: Can slightly increase memory usage due to multiline formatting.
• Learning Curve: New syntax may initially confuse developers unfamiliar with the feature.
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:
In this case, %d is a format specifier for a decimal integer, and the value of number replaces it during
execution.
Here, two format specifiers are used and must be matched with two arguments. This outputs: Number
= 100, Sum = 140.
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.
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.
426
The Complete Java Training Imran Afzal
Output:
Length of the string: 13
Output:
The first character is H
The fifth character is o
The last character is !
427
The Complete Java Training Imran Afzal
String sub3 = str.substring(7, 12);
Output:
Sub1: Hello
Sub2: World!
Sub3: World
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.
Output:
Original string: Hello, World!
Uppercase string: HELLO, WORLD!
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.
Output:
Index of o: 4
Index of World: 7
430
The Complete Java Training Imran Afzal
Output:
Original string: Hello, World!
Replaced string: Hell0, W0rld!
if (str1.isEmpty()) {
System.out.println("String 1 is empty.");
} else {
if (str2.isEmpty()) {
System.out.println("String 2 is empty.");
} else {
}
431
The Complete Java Training Imran Afzal
Output:
String 1 is empty: true
String 2 is empty: false
if (str1.isBlank()) {
System.out.println("String 1 is blank.");
} else {
if (str2.isBlank()) {
System.out.println("String 2 is blank.");
} else {
if (str3.isBlank()) {
System.out.println("String 3 is blank.");
} else {
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.
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
if (str1.equals(str2)) {
if (str1.equals(str3)) {
} else {
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
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.
compareTo() Method
• 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:
if (result == 0) {
435
The Complete Java Training Imran Afzal
} else {
• Methods that clean or format text without changing its actual meaning.
• Methods that transform or modify the content of the string.
The concat() method joins one string to the end of another and returns a new string.
Example
436
The Complete Java Training Imran Afzal
String str3 = str1.concat(str2);
The + operator can also be used to concatenate strings, but concat() is more efficient in some
scenarios.
The join() method combines multiple strings into one, using a specified delimiter.
Example
// Result: "Java-is-fun"
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
For earlier Java versions, similar behavior can be achieved using loops or StringBuilder.
The replace() method substitutes all occurrences of a character or substring with another character or
substring.
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
The replaceAll() method replaces all matches of a regular expression with a specified replacement.
Example
The replaceFirst() method replaces only the first match of a regular expression.
Example
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
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
To demonstrate the differences, consider an example using method overloading to accept both String
and StringBuilder parameters.
Overloaded Methods
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.
message.concat(" in Java");
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
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.
messageBuilder.append(" in Java");
This modifies the original StringBuilder object directly. Its length increases and the new content is
visible without reassigning.
Internal Behavior
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.
StringBuilder methods return a reference to the same object, allowing method chaining:
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
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.
Getters (accessor methods) and Setters (mutator methods) allow controlled access to private fields
in a class.
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.
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.
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.
• A static variable is shared among all instances of a class. It belongs to the class itself.
• 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.
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).
8.27.8- Inheritance
Inheritance allows one class (subclass) to inherit fields and methods from another class (superclass).
Example
void eat() {
System.out.println("Animal is eating.");
446
The Complete Java Training Imran Afzal
}
void bark() {
System.out.println("Dog is barking.");
• 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.
Method Overloading
Method overloading allows multiple methods in a class to have the same name but different
parameters (type, number, or order).
Example
447
The Complete Java Training Imran Afzal
public int addNumbers(int num1, int num2) {
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
448
The Complete Java Training Imran Afzal
}
@Override
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
450
The Complete Java Training Imran Afzal
9.1- Introduction to Composition
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.
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.
Create a new Java project named Composition. Inside this project, create the following class files:
1. Person Class
451
The Complete Java Training Imran Afzal
private String city;
this.name = name;
this.street = street;
this.city = city;
this.state = state;
return name;
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
this.city = city;
this.state = state;
return city;
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
public Student(String name, String street, String city, String state, String rollNumber) {
this.rollNumber = rollNumber;
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
public Professor(String name, String street, String city, String state, String salary) {
this.salary = salary;
return salary;
Similarly, the Professor class extends Person and adds a unique salary field.
5. Main Class
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("---------------");
Professor professor = new Professor("John Doe", "5th Ave 32nd floor", "New York",
"Amercia","10000$");
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.
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 {
this.style = style;
this.battery = battery;
this.globRating = globRating;
457
The Complete Java Training Imran Afzal
}
return style;
return battery;
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 {
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() {
return style;
return pillows;
return height;
460
The Complete Java Training Imran Afzal
public int getSheets() {
return sheets;
return quilt;
The Bed class includes physical details of a bed and a method to simulate making the bed.
Ceiling Class
class Ceiling {
this.height = height;
this.paintedColor = paintedColor;
461
The Complete Java Training Imran Afzal
}
return height;
return paintedColor;
The Ceiling class provides attributes like height and painted color.
Wall Class
class Wall {
this.direction = direction;
462
The Complete Java Training Imran Afzal
}
return direction;
Bedroom Class
class Bedroom {
463
The Complete Java Training Imran Afzal
public Bedroom(String name, Wall wall1, Wall wall2, Wall wall3, Wall wall4,
this.name = name;
this.wall1 = wall1;
this.wall2 = wall2;
this.wall3 = wall3;
this.wall4 = wall4;
this.ceiling = ceiling;
this.bed = bed;
this.lamp = lamp;
return lamp;
464
The Complete Java Training Imran Afzal
public void makeBed() {
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
lamp.turnOn();
System.out.println("------------");
465
The Complete Java Training Imran Afzal
// Create a Bed object
bed.make();
466
The Complete Java Training Imran Afzal
// Get the direction of the wall
new Wall("east"), new Wall("west"), new Ceiling(100, 2), new Bed("rustic", 2, 60, 2, 1),
bedroomLamp.turnOn();
bedroom.makeBed();
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.
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
class MuscularOrgan {
MuscularOrgan(String name) {
this.name = name;
return name;
The MuscularOrgan class models generic muscular body parts and contains a single attribute name.
Tongue Class
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
class Beak {
Beak(Tongue tongue) {
this.tongue = tongue;
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
class Bird {
this.name = name;
this.age = age;
return name;
470
The Complete Java Training Imran Afzal
return age;
Bird is the base class with common properties such as name and age.
Penguin Class
super(name, age);
this.beak = beak;
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.
This class ties everything together. It demonstrates object creation and composition, followed by
method calls to display information.
472
The Complete Java Training Imran Afzal
penguin.swim();
• Penguin is swimming
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
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
this.material = material;
return material;
474
The Complete Java Training Imran Afzal
The Piston class models a piston component with a specific material.
ConnectingRod Class
this.type = type;
return type;
The ConnectingRod class stores the type of the rod used in the engine.
Crankshaft Class
475
The Complete Java Training Imran Afzal
public Crankshaft(String manufacturer) {
this.manufacturer = manufacturer;
return manufacturer;
The Crankshaft class holds the manufacturer's name of the crankshaft component.
Engine Class
// Engine.java
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;
return horsepower;
return piston;
return rod;
477
The Complete Java Training Imran Afzal
}
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 Car(String model, int horsepower, Piston piston, ConnectingRod rod, Crankshaft shaft) {
this.model = model;
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
Car car = new Car("Tesla Model S", 500, piston, rod, shaft);
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.
Output
This program effectively demonstrates the design of a modular car engine system using object-
oriented programming concepts like inheritance and composition.
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.
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.
grade = "A";
grade = "B";
grade = "C";
grade = "D";
} else {
grade = "F";
482
The Complete Java Training Imran Afzal
calculation.mathMarks = 80;
calculation.scienceMarks = 90;
calculation.calculatePercentage();
calculation.calculateGrade();
calculation.mathMarks = 150;
calculation.scienceMarks = 190;
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
this.mathMarks = 0;
this.scienceMarks = 0;
this.mathMarks = 100;
this.scienceMarks = 100;
} else{
484
The Complete Java Training Imran Afzal
this.scienceMarks = scienceMarks;
this.mathMarks = mathMarks;
this.percentage = calculatePercentage();
this.grade = "A";
this.grade = "B";
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";
System.out.println(result);
• 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.
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.
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.
The Printer class is designed to demonstrate the concept of encapsulation in Java. It includes three
private member variables:
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.
The constructor accepts two parameters—tonerLevel and duplex. It follows these rules:
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.
• pagesPrinted is initialized to 0.
This method adds toner to the printer, validating the input to avoid exceeding maximum capacity:
return -1;
tonerLevel += tonerAmount;
return tonerLevel;
return -1;
489
The Complete Java Training Imran Afzal
• Ensures total toner does not exceed 100.
This method simulates printing and returns the number of pages actually printed:
if (duplex) {
pagesPrinted += pagesToPrint;
return pagesToPrint;
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.
491
The Complete Java Training Imran Afzal
int tonerAdded = myPrinter.addToner(30);
pagesToPrint = 15;
pagesPrinted = myPrinter.printPages(pagesToPrint);
tonerAdded = myPrinter.addToner(100);
if (tonerAdded == -1) {
} else {
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.
• 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.
• 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.
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.
A base class Fruit is created with a private field name and a method isRipe.
this.name = name;
494
The Complete Java Training Imran Afzal
};
• The isRipe method prints the name and type of the fruit along with a custom message.
Apple class
super(name);
@Override
super.isRipe(check);
System.out.println(check + " Apple may lower your risk for cancer, diabetes, and heart
disease.");
495
The Complete Java Training Imran Afzal
}
Banana class
super(name);
@Override
super.isRipe(check);
496
The Complete Java Training Imran Afzal
• super.isRipe(check) calls the base class method first.
fruit.isRipe("Ripped");
// while(true){
// if("Qq".contains(type)){
// break;
// }
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.
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.
498
The Complete Java Training Imran Afzal
import java.util.Scanner;
while (true)
if ("Qq".contains(type))
break;
499
The Complete Java Training Imran Afzal
System.out.print("Enter the Fruit Name: ");
fruit.isRipe("Ripe");
sc.close();
500
The Complete Java Training Imran Afzal
fruit.isRipe("Ripped");
// CavendishBanana.isRipe("Ripped");
appleFruit.EatApple();
honeycrisp.isRipe("Ripped");
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
this.engine = true;
this.cylinders = cylinders;
this.name = name;
this.wheels = 4;
502
The Complete Java Training Imran Afzal
public String startEngine() {
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.
The Mitsubishi class is a subclass of Car and overrides three methods to provide behavior specific to
this car type.
super(cylinders, name);
@Override
504
The Complete Java Training Imran Afzal
@Override
@Override
The Holden class extends Car and overrides the same three methods to personalize its behavior.
super(cylinders, name);
505
The Complete Java Training Imran Afzal
@Override
@Override
@Override
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.
super(cylinders, name);
@Override
@Override
507
The Complete Java Training Imran Afzal
@Override
The main method demonstrates polymorphism by creating objects from different subclasses but
interacting with them through a common Car reference.
super(cylinders, name);
@Override
508
The Complete Java Training Imran Afzal
@Override
@Override
Output
-----
-----
509
The Complete Java Training Imran Afzal
The Holden Car's engine is starting.
-----
510
The Complete Java Training Imran Afzal
private String addition3;
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
this.name = name;
this.meat = meat;
this.price = price;
this.breadRollType = breadRollType;
this.addition1 = name;
this.addition1Price = price;
511
The Complete Java Training Imran Afzal
}
this.addition2 = name;
this.addition2Price = price;
this.addition3 = name;
this.addition3Price = 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
if (this.addition1 != null) {
hamburgerPrice += this.addition1Price;
if (this.addition2 != null) {
hamburgerPrice += this.addition2Price;
if (this.addition3 != null) {
hamburgerPrice += this.addition3Price;
if (this.addition4 != null) {
513
The Complete Java Training Imran Afzal
hamburgerPrice += this.addition4Price;
return hamburgerPrice;
This method prints an itemized receipt including additions and returns the total price.
public DeluxeBurger() {
super.addAddition1("Chips", 2.75);
super.addAddition2("Drink", 1.81);
@Override
514
The Complete Java Training Imran Afzal
System.out.println("Cannot add additional items to a deluxe burger");
@Override
@Override
@Override
515
The Complete Java Training Imran Afzal
}
The deluxe burger includes chips and a drink by default and blocks additional customization.
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
if (this.addition5Name != null) {
totalPrice += this.addition5Price;
if (this.addition6Name != null) {
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.
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.addAddition1("Lettuce", 0.50);
deluxeBurger.itemizeHamburger();
System.out.println();
healthyBurger.addAddition1("Tomato", 0.75);
healthyBurger.addAddition5("Avocado", 1.50);
healthyBurger.itemizeHamburger();
• Displays the base burger with White roll and Beef meat.
519
The Complete Java Training Imran Afzal
• Calculates total price.
This example demonstrates the use of inheritance and method overriding to enforce different rules
for burger customization using polymorphism.
The Movie class represents a generic movie with two private attributes:
These variables store the title and the director's name and are made private to follow encapsulation
principles.
9.10.2- Constructor
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.
This method prints a message indicating that the movie is playing, along with its title and director.
9.10.4- Getters
return title;
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.
The ActionMovie class extends the Movie class and customizes the play method:
super(title, director);
@Override
The overridden play method uses the getters from the Movie class to print a message specific to an
action movie.
The ComedyMovie class also extends Movie and provides its own play implementation:
522
The Complete Java Training Imran Afzal
public ComedyMovie(String title, String director) {
super(title, director);
@Override
The HorrorMovie class inherits from Movie and overrides the play method:
super(title, director);
523
The Complete Java Training Imran Afzal
@Override
This class is structured similarly to the others, but tailors its message for the horror genre.
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.
• Despite all variables being of type Movie, Java executes the appropriate subclass version of
play due to polymorphism.
Output
This output demonstrates runtime polymorphism. Each subclass customizes its behavior while
sharing the same interface (play method) from the base class Movie.
// Developer.java
525
The Complete Java Training Imran Afzal
public Developer(String name, int age, String specialization) {
this.name = name;
this.age = age;
this.specialization = specialization;
return name;
return age;
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.
// ProgrammingLanguage.java
this.name = name;
this.year = year;
this.developer = developer;
527
The Complete Java Training Imran Afzal
public String getName() {
return name;
return year;
return developer;
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.
// Java.java
this.platform = platform;
return platform;
529
The Complete Java Training Imran Afzal
@Override
super.printInfo();
The Java class extends ProgrammingLanguage, adds a platform field, overrides printInfo, and includes
overloaded compile methods.
// Cpp.java
530
The Complete Java Training Imran Afzal
class Cpp extends ProgrammingLanguage {
this.extension = extension;
return extension;
@Override
super.printInfo();
531
The Complete Java Training Imran Afzal
}
The CPP class introduces an extension attribute and also overrides printInfo. It contains compile
methods similar to the Java class but tailored for C++.
// Python.java
532
The Complete Java Training Imran Afzal
public Python(String name, int year, Developer developer, String version) {
this.version = version;
return version;
@Override
super.printInfo();
533
The Complete Java Training Imran Afzal
}
The Python class adds a version field, overrides printInfo, and provides methods to simulate
interpretation of Python code.
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
Platform: JVM
Name: C++
Year: 1983
Extension: .cpp
Name: Python
Year: 1991
Version: 3.9
Compiling Main.java...
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.
this.name = name;
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.
super(name);
@Override
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.
538
The Complete Java Training Imran Afzal
public WaterMonster(String name) {
super(name);
@Override
The WaterMonster subclass defines its unique implementation of the attack method, indicating an
attack with water.
super(name);
@Override
539
The Complete Java Training Imran Afzal
}
The StoneMonster subclass overrides the attack method to simulate an attack using stones.
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());
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
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
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;
Alternatively, the wildcard asterisk (*) can be used to import all classes in a package:
import java.util.*
542
The Complete Java Training Imran Afzal
Scanner sc = new Scanner(System.in);
This simplifies access but is less common in modern development due to better clarity with explicit
imports.
• The package statement (if used) must be the first line in a source file, before any import
statements.
• A fully qualified class name (FQCN) can be used in place of an import statement.
Example
package nixware.consulting.package;
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
In IntelliJ IDEA:
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.
Product Class
package com.nixware.first;
544
The Complete Java Training Imran Afzal
public Product(String course) {
this.course = course;
@Override
return "Product{" +
'}';
Main Class
package nixware.consulting;
import com.nixware.first.*;
545
The Complete Java Training Imran Afzal
public class mainClass {
System.out.println(product);
Course = Java
• Creating packages
Instead of importing the class, you can reference it directly using its fully qualified name:
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
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.
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:
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,
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
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
// Object creation
Although arrays are objects in Java, their creation syntax is slightly different from regular object
instantiation.
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:
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.
You can also declare multi-dimensional arrays using this general syntax:
dataType[][] arrayName;
int[][] myArray;
arrayName[rowIndex][columnIndex];
• 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.
• 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.
• 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.
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.
numberArray[1] = 30;
decimalArray[1] = 11.5;
Invalid Examples:
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};
int[] anotherArray;
General Syntax:
// block of code
554
The Complete Java Training Imran Afzal
Example:
General Syntax:
// block of code
Example:
555
The Complete Java Training Imran Afzal
System.out.println("arrayRef is an int array");
objectArray[0] = anotherArray;
objectArray[1] = "Nixware";
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.
Start by creating a class named ArrayAverage inside the nixware.consulting package. Then, add the
main method:
import java.util.Scanner;
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:
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.
Now declare an integer array a with the given limit and a variable sum to store the total:
int sum = 0;
This sets up the required storage for the user inputs and the accumulator for summing the values.
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’.
After the array is filled, calculate the sum using another loop:
sum += a[i];
Now compute the average by casting the sum to double and dividing by the limit:
558
The Complete Java Training Imran Afzal
• Limit: 3
This example emphasizes the basics of array usage with input handling, summation, and average
calculation in Java.
This example demonstrates how to read an array from user input and display its elements in reverse
order.
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 {
This stores the number of elements the array should contain in the variable limit.
This creates an empty array of limit size. All elements are initialized to 0 by default.
Use a for loop to prompt the user for values and store them in the array:
560
The Complete Java Training Imran Afzal
arr[i] = scanner.nextInt();
Each iteration:
• Stores the input value into the corresponding position in the array
System.out.println(arr[i]);
561
The Complete Java Training Imran Afzal
10.4.6- Example Execution
• Limit: 4
15
11
12
• 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.
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:
562
The Complete Java Training Imran Afzal
public static void main(String[] args) {
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:
This acts as a starting reference point to compare other elements in the array.
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.
563
The Complete Java Training Imran Afzal
10.5.2- How the Program Works
• On each iteration:
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.
Revised loop:
564
The Complete Java Training Imran Afzal
{
max = arr[i];
Final output:
• Use loops and conditional statements to search for minimum or maximum values.
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()
The Arrays.sort() method sorts the elements of an array in ascending order. It modifies the original
array directly.
566
The Complete Java Training Imran Afzal
10.6.2- Example: Sorting an Integer Array
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
Note: This sorting operation is in-place, meaning the original array is modified. To keep the original
unchanged, copy it before sorting.
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:
• When types like Integer, String, Boolean, and Double are mixed, Java does not know how to
order them.
• Ensure all elements are of the same type and implement Comparable, or
Syntax:
• If newLength is greater than the original, the extra elements are filled with default values (0
for integers).
568
The Complete Java Training Imran Afzal
System.out.println(Arrays.toString(original)); // prints [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3]
This shows how a new array with selected elements can be created from an existing one.
Syntax:
Arrays.fill(array, value);
This method replaces all elements in the array with the given value.
Arrays.fill(arr, 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.
Create a class named ArraySortWithoutSortMethod in the nixware.consulting package and define the
main method. Inside the method, initialize an array of integers:
int temp = 0;
System.out.println("Before sorting:");
570
The Complete Java Training Imran Afzal
Use a nested for loop to sort the array in ascending order:
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.
System.out.println("\nAfter sorting:");
571
The Complete Java Training Imran Afzal
}
Create a new class named ArraySortWithSortMethod in the same package and define the main
method.
import java.util.Arrays;
572
The Complete Java Training Imran Afzal
System.out.println("Before sorting: " + Arrays.toString(myArray));
Arrays.sort(myArray);
The manual method helps understand how sorting works internally, but for performance and cleaner
code, the built-in method is recommended.
Create a class file named CopyArray in the nixware.consulting package and define the main method.
import java.util.Arrays;
573
The Complete Java Training Imran Afzal
public static void main(String[] args) {
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.
copiedArray[0] = 10;
This sets the first element of copiedArray to 10. Since it’s a copy, originalArray remains unchanged.
Output
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;
Fill the array with a specific value using the Arrays.fill() method:
Arrays.fill(myArray, 10);
Output
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.
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.
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;
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
Then prompt the user to enter values for the array elements:
myArray[i] = scanner.nextInt();
After populating the array, ask the user to enter the value they want to search for:
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:
This returns the index of the target value if found, or a negative number if not found.
if (position >= 0) {
} else {
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:
578
The Complete Java Training Imran Afzal
Enter the elements of the array:
12
15
Output
Target value found at position 0
• If the target is less than the middle, the left half is searched.
Notes
• If there are duplicate values, the returned index may not always be the first occurrence.
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
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:
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;
System.out.println(Arrays.toString(unsortedNumbers));
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:
randomNumbers[i] = random.nextInt(100);
return randomNumbers;
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():
randomNumbers[i] = random.nextInt(100);
return randomNumbers;
582
The Complete Java Training Imran Afzal
System.out.println(Arrays.toString(numbers));
int temp;
while (flag) {
flag = false;
temp = sortedNumbers[i];
sortedNumbers[i + 1] = temp;
flag = true;
System.out.println("-->" + Arrays.toString(sortedNumbers));
583
The Complete Java Training Imran Afzal
}
return sortedNumbers;
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:
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().
584
The Complete Java Training Imran Afzal
--> [50, 20, 10]
• No further swaps are needed, so the final sorted order is [50, 20, 10].
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.
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:
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.
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.
• 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).
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.
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:
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
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.
values[0] = 5;
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
names[0] = "Dave";
Both names and copy refer to the same array in memory, so changes to names affect copy.
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:
589
The Complete Java Training Imran Afzal
array2[0] = 1;
modify(array1);
array[1] = 2;
• Both array1 and array2 refer to the same array object in memory.
• The modify() method takes the array reference as a parameter, so changing array[1] affects
both variables.
• 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.
• 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.
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:
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.
591
The Complete Java Training Imran Afzal
System.out.println("Argument " + i + ": " + args[i]);
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.
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.
592
The Complete Java Training Imran Afzal
Example:
System.out.println("Hello Nixware");
This syntax functions the same way as the traditional array format.
System.out.println(value);
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.
• The for-each loop iterates through the values array and prints each argument separately.
• 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.
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() {
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
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.
596
The Complete Java Training Imran Afzal
int[] numbers = readNumbers();
System.out.println(Arrays.toString(numbers));
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.
When the user enters 10, 5, 20, 6, the program output will be:
[10, 5, 20, 6]
This structure helps break the problem into manageable parts using methods, arrays, and loops.
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:
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:
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:
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:
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.
599
The Complete Java Training Imran Afzal
public static void main(String[] args) {
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:
• 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
Arrays are data structures used to store multiple values of the same data type in a continuous block of
memory.
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.
Declaration of a 2D Array
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
Visual:
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
rows × columns
Initializing a 2D Array
int[][] array = {
{1, 2, 3},
{4, 5, 6}
};
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
int[][] array = {
{1, 2, 3},
{4, 5, 6}
};
array[rowIndex][columnIndex];
Example:
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.
int[][] array = {
{1, 2, 3},
{4, 5, 6}
};
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
606
The Complete Java Training Imran Afzal
10.16.4- 1D vs 2D Arrays Comparison
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}
};
• 2 rows
• 4 columns
• A total of 8 elements
607
The Complete Java Training Imran Afzal
Accessing Specific Elements
System.out.println(numbers[0][3]);
System.out.println(numbers[1][2]);
14
67
System.out.println();
Output
1234
608
The Complete Java Training Imran Afzal
5678
package nixware.consulting;
import java.util.Random;
start by declaring the class and importing Random for random grid generation.
};
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.
public GameOfLife() {
initializeGrid();
This constructor prepares the grid and fills it with random values using initializeGrid().
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
int liveNeighbors = 0;
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
if (grid[row][col] == 1) {
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;
if (grid[row][col] == 1) {
System.out.print("*");
} else {
613
The Complete Java Training Imran Afzal
System.out.print(".");
System.out.println();
Main Method
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)
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)
• Total elements: 3 × 3 × 3 = 27
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.
• 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},
};
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
Example:
617
The Complete Java Training Imran Afzal
• k tracks the column in the current row
• A new line after every row, and a blank line after each layer.
This class:
• Declares a 3D array
threeDArray[i][j][k] = i + j + k;
618
The Complete Java Training Imran Afzal
// Printing the 3D array layer by layer
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.
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:
621
The Complete Java Training Imran Afzal
Chapter 11
622
The Complete Java Training Imran Afzal
11.1- Introduction to List
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.
• 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:
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.
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.
• 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.
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;
The default capacity is 10. You can also explicitly set the initial capacity:
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].
import java.util.ArrayList;
This statement creates a new ArrayList named intList to store elements of the Integer type. The
diamond operator <> helps infer the type automatically.
intList.add(10);
intList.add(20);
626
The Complete Java Training Imran Afzal
intList.add(30);
Output:
This creates a new ArrayList called fruits that can store String elements.
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
fruits.add("Mango");
Displaying contents:
System.out.println(fruits);
Output:
627
The Complete Java Training Imran Afzal
11.2.5- Removing an Element
fruits.remove("Orange");
System.out.println(fruits);
Output:
Output:
fruits.set(1, "Kiwi");
628
The Complete Java Training Imran Afzal
System.out.println("ArrayList after updating an element:");
System.out.println(fruits);
Output:
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().
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.
Create a new class named Student in the nixware.consulting package. Define the following instance
variables:
629
The Complete Java Training Imran Afzal
private int age;
These variables represent the student's name, age, and the array of enrolled courses.
this.name = name;
this.age = age;
this.courses = courses;
Generate and override the toString() method to print student details clearly. Use Arrays.toString() to
display the courses array in a readable format:
@Override
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].
In the same file, create another class named SchoolApp, and define the main method:
class SchoolApp {
Create arrays for each student's courses and add Student objects to the ArrayList:
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:");
System.out.println(student);
Expected Output
The output displays each student’s name, age, and courses in a clear format:
Students:
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 java.util.ArrayList;
632
The Complete Java Training Imran Afzal
import java.util.Scanner;
A static Scanner object is used to read user input from the console.
Available actions:
0 - to shutdown
1 - to add a grade
633
The Complete Java Training Imran Afzal
3 - to display the average grade
grades.add(grade);
Prompts the user to enter a grade and appends it to the grades list.
634
The Complete Java Training Imran Afzal
System.out.print("Enter the index of the grade to remove: ");
grades.remove(index);
Allows the user to remove a grade by specifying its index in the grades list.
if (grades.size() == 0) {
} else {
int sum = 0;
sum += grade;
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.
while (flag) {
printActions();
switch (choice) {
System.out.println(grades);
636
The Complete Java Training Imran Afzal
• Initializes an empty ArrayList named grades.
• 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
This implementation effectively demonstrates how to use an ArrayList to manage dynamic data entries
and operations in an interactive Java application.
637
The Complete Java Training Imran Afzal
11.5.2- Creating Arrays and ArrayLists
Note: It’s recommended to use generics with ArrayList for type safety:
new ArrayList<>(List.of(...))
System.out.println(myArray[0]);
638
The Complete Java Training Imran Afzal
ArrayLists:
System.out.println(myArrayList.get(0));
System.out.println(Arrays.toString(myArray));
ArrayList:
System.out.println(myArrayList);
};
639
The Complete Java Training Imran Afzal
System.out.println(multiDiaList);
System.out.println(Arrays.deepToString(twoDArray));
System.out.println(multiDiaList);
640
The Complete Java Training Imran Afzal
11.5.8- Sorting Elements
Code Example for Arrays:
Arrays.sort(myArray);
System.out.println(Arrays.toString(myArray));
arrayList.sort(Comparator.naturalOrder()); // Ascending
System.out.println(arrayList);
arrayList.sort(Comparator.reverseOrder()); // Descending
System.out.println(arrayList);
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.
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:
myList.add((i + 2) * 5);
}
642
The Complete Java Training Imran Afzal
myList.add(35);
myList.add(40);
myList.add(45);
• 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.
643
The Complete Java Training Imran Afzal
• The start of the list is the head.
• Because there's no index, retrieving an element requires traversal from the head or tail.
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.
• 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.
• 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.
• 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.
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.
LinkedList Components
• Nodes: The core building blocks, each holding a value and reference(s) to other nodes.
• Tail: The last node in the list, useful for fast append operations.
• 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.
A class named CombinedExample is created in the nixware.consulting package with the main method.
arrayList.add(i);
linkedList.add(i);
646
The Complete Java Training Imran Afzal
long endTime = System.nanoTime();
startTime = System.nanoTime();
endTime = System.nanoTime();
4. Displaying Results
Results show:
647
The Complete Java Training Imran Afzal
Performance Insight
Example Output:
arrayList.remove(0);
linkedList.remove(0);
Removing the first element helps observe where LinkedList performs better.
startTime = System.nanoTime();
linkedList.remove(0);
endTime = System.nanoTime();
648
The Complete Java Training Imran Afzal
System.out.println("Time taken by LinkedList: " + durationLL + " nanoseconds");
Example Output:
This confirms LinkedList is more efficient when removing elements from the beginning.
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.
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.
649
The Complete Java Training Imran Afzal
int value;
Node next;
this.value = value;
this.next = null;
Explanation
• The constructor sets the value and initializes next to null, indicating no link yet.
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
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.
if (head == null) {
head = newNode;
} else {
current = current.next;
}
651
The Complete Java Training Imran Afzal
current.next = newNode;
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.
current = current.next;
System.out.println();
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 {
list.insert(5);
list.insert(10);
list.insert(15);
list.display(); // Output: 5 10 15
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.
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.
• 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.
• 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.
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.
• 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.
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.
A Java program can be developed to implement a queue using a linked list. The Queue class includes
two main methods:
• 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 {
public Queue() {
655
The Complete Java Training Imran Afzal
}
if (list.isEmpty()) {
System.out.println("Queue is empty");
return -1;
} else {
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().
In the main method, a Queue object is created. Three values—1, 2, and 3—are added using
the enqueue method.
class MainClass {
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
• The first three dequeue() calls remove and print each value in order.
• The fourth call shows the behavior when the queue is empty.
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() {
if (list.isEmpty()) {
System.out.println("Stack is empty");
return -1;
} else {
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.
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 {
stack.push(i * 10);
// Pop values: 30, 20, 10, then try popping from empty stack
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.
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.
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;
myList.add("apple");
myList.add("banana");
myList.add("orange");
while (iterator.hasNext()) {
System.out.println(element);
661
The Complete Java Training Imran Afzal
}
• 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.
• 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.
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.
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.
fruitsToVisit.add("Apple");
fruitsToVisit.add(0, "Banana");
System.out.println(fruitsToVisit);
addMoreFruits Method
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");
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.
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();
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");
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.
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.
printItinerary2 demonstrates the same using an enhanced for loop. A variable previousFruit tracks the
prior item to show transitions.
667
The Complete Java Training Imran Afzal
System.out.println("Starts Eating " + list.getFirst());
previousFruit = fruit;
while (iterator.hasNext()) {
previousFruit = fruit;
668
The Complete Java Training Imran Afzal
}
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
int i = 10;
Integer integerObj = i;
int j = integerObj2;
671
The Complete Java Training Imran Afzal
System.out.println("The value of j: " + j);
Output:
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.
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 {
this.numerator = numerator;
this.denominator = denominator;
672
The Complete Java Training Imran Afzal
}
@Override
class MainClass {
int numerator = 5;
int denominator = 2;
673
The Complete Java Training Imran Afzal
Fraction fractionObj = new Fraction(numerator, denominator); // create Fraction object
Output:
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.
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.
• 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.
The syntax of an enum involves listing constants separated by commas within an enum declaration.
For example:
enum Animal {
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);
this.name = name;
this.numLegs = numLegs;
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.
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.
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
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.
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;
System.out.println(monthOfYear);
monthOfYear = getRandomMonth();
679
The Complete Java Training Imran Afzal
monthOfYear.name(), monthOfYear.ordinal());
if (monthOfYear == Months.JUN) {
return allMonths[randomInteger];
• 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;
switch (animal) {
681
The Complete Java Training Imran Afzal
case FISH -> System.out.println("The fish swims.");
• 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
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.
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.
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.
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.
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 {
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.
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.
@Override
685
The Complete Java Training Imran Afzal
@Override
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:
This line is invalid because abstract classes cannot be instantiated. You can only create instances of
concrete subclasses like Car or Motorcycle.
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.
• 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.
• 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.
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.
687
The Complete Java Training Imran Afzal
public Vehicle(String model) {
this.model = model;
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.
688
The Complete Java Training Imran Afzal
super(model);
this.numDoors = numDoors;
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.
super(model);
this.isElectric = isElectric;
689
The Complete Java Training Imran Afzal
}
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.
Now create a MainClass with the main method to test both classes.
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.
This program effectively demonstrates abstraction by using an abstract class with shared behavior
(stop()), abstract behavior (start()), and subclass-specific implementations.
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.
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 {
this.title = title;
this.type = type;
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().
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 {
super(title, type);
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.
Create another subclass of Item named DVD, following the same structure as Book.
super(title, type);
694
The Complete Java Training Imran Afzal
public void returnItem() {
The DVD class implements custom behavior for DVD items. It reuses the inherited fields title and
type.
Create another abstract class named Person to model the people involved in the library system.
this.name = name;
this.age = age;
695
The Complete Java Training Imran Afzal
public abstract void display();
This class provides a structure with abstract methods for person-specific behaviors and includes
protected fields name and age.
Create a Member class that extends Person and implements all abstract methods.
super(name, age);
696
The Complete Java Training Imran Afzal
}
if (!borrowedItems.contains(item)) {
item.borrow();
borrowedItems.add(item);
} else {
if (borrowedItems.contains(item)) {
item.returnItem();
borrowedItems.remove(item);
697
The Complete Java Training Imran Afzal
} else {
The Member class includes a list of borrowed items and provides logic to borrow and return them,
ensuring no duplicates.
698
The Complete Java Training Imran Afzal
members.add(new Member("Jane Smith", 30));
int i = 0;
member.display();
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.
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.
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.
700
The Complete Java Training Imran Afzal
this.name = name;
this.accountNumber = accountNumber;
this.balance = balance;
return name;
return accountNumber;
return balance;
701
The Complete Java Training Imran Afzal
public void setBalance(double balance) {
this.balance = balance;
This class includes private fields, getter and setter methods, and three abstract methods: deposit(),
withdraw(), and display(). These must be implemented in all subclasses.
The SavingsAccount class extends Account and includes logic specific to savings accounts, such as
rejecting withdrawals that lead to a negative balance.
702
The Complete Java Training Imran Afzal
@Override
setBalance(getBalance() + amount);
@Override
System.out.println("Insufficient funds");
} else {
setBalance(getBalance() - amount);
@Override
703
The Complete Java Training Imran Afzal
System.out.println("Savings account details:");
This class uses the inherited methods to manipulate the balance and display account details, enforcing
a zero-balance rule.
The CheckingAccount class allows an overdraft of up to -500. It extends the base Account class and
implements custom withdrawal logic.
@Override
704
The Complete Java Training Imran Afzal
public void deposit(double amount) {
setBalance(getBalance() + amount);
@Override
} else {
setBalance(getBalance() - amount);
@Override
705
The Complete Java Training Imran Afzal
System.out.println("Account number: " + getAccountNumber());
This class overrides the withdraw() method to allow limited overdrafts and displays account
information accordingly.
Each transaction is represented by the Transaction class, which captures the amount, date, and account
details.
import java.util.Date;
class Transaction {
this.amount = amount;
706
The Complete Java Training Imran Afzal
this.date = date;
this.account = account;
System.out.println("Transaction details:");
The display() method outputs the transaction data including account and date information.
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 {
public Banking() {
accounts.add(account);
accounts.remove(account);
708
The Complete Java Training Imran Afzal
public void processTransaction(Account account, double amount) {
transaction.display();
This class provides methods to add, remove, and process transactions for accounts, encapsulating
business logic related to banking operations.
Finally, the main class creates instances of all necessary objects, performs transactions, displays results,
and simulates a basic banking workflow.
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);
710
The Complete Java Training Imran Afzal
• Details of both transactions are displayed with current date and updated balances.
This program showcases how abstraction allows a clean and extendable design by enforcing method
implementations and isolating responsibilities across classes.
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.
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.
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.
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.
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.
A class can implement one or more interfaces. When implementing an interface, the class must
override all the abstract methods defined in that interface.
712
The Complete Java Training Imran Afzal
public Circle(int radius) {
this.radius = radius;
@Override
Here, the Circle class implements the Drawable interface. It provides an actual implementation for
the draw() method.
The following is the main class that demonstrates the interface in use:
class MainClass {
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.
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.
public static final void draw(); // Incorrect - interfaces cannot have static final methods without a
body
Correct Way:
This ensures the interface remains a blueprint for behavior, and the implementation is deferred to the
implementing class.
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
Abstract classes are suitable for related objects needing shared code, while interfaces are better for
defining behavior that can be shared across unrelated classes.
1. Testing: Interfaces make it easier to create mock implementations for unit testing.
715
The Complete Java Training Imran Afzal
• Frequent Changes: Changes to interfaces may lead to widespread updates in implementing
classes.
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 {
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.
this.title = title;
this.year = year;
return title;
717
The Complete Java Training Imran Afzal
}
return year;
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.
// ActionMovie class
super(title, year);
718
The Complete Java Training Imran Afzal
@Override
@Override
return "Action";
ActionMovie extends Movie and implements Playable. It defines its genre and playback behavior,
printing out the movie’s title when played.
// ComedyMovie class
719
The Complete Java Training Imran Afzal
public ComedyMovie(String title, int year) {
super(title, year);
@Override
@Override
this.review = review;
@Override
this.rating = rating;
720
The Complete Java Training Imran Afzal
}
return rating;
return review;
@Override
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
actionMovie.play();
comedyMovie.play();
comedyMovie.leaveReview("Hilarious movie!");
comedyMovie.rate(4.5);
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
Genre: Action
Playing Bridesmaids
Genre: Comedy
Rating: 4.5
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.
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:
"age": 30,
"isMarried": false,
"address": {
"city": "Exampleville",
"country": "USA"
},
"spouse": null
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();
• getIcon(): Returns the icon used to represent the object on the map.
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;
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
return label;
@Override
return geometry;
@Override
726
The Complete Java Training Imran Afzal
public String getIcon() {
return icon;
@Override
return gson.toJson(this);
To use Gson, make sure to import the library and add it to your project dependencies.
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;
728
The Complete Java Training Imran Afzal
List<MapMappable> mapObjects = new ArrayList<>();
mapObjects.add(mapObject);
mapObjectMap.put("object1", mapObject);
Label: My Object
Geometry: Point
729
The Complete Java Training Imran Afzal
Objects JSON: [{"label":"My Object","geometry":"Point","icon":"Black Pushpin"}]
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.
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.
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.
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.
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
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.
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.
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.
734
The Complete Java Training Imran Afzal
A generic class introduces a type parameter:
The use of the type parameter T allows the class to work with any object type, depending on what is
specified during instantiation.
private T value;
this.value = value;
public T getValue() {
return value;
735
The Complete Java Training Imran Afzal
Usage in the main Method
integerInstance.setValue(10);
stringInstance.setValue("Hello");
Expected Output
Integer value: 10
• 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.
• 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.
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).
The TeamMember class is declared with a generic type parameter T, enabling it to work with different
data types for the member's name.
class TeamMember<T> {
private T name;
this.name = name;
this.jerseyNumber = jerseyNumber;
public T getName() {
return name;
738
The Complete Java Training Imran Afzal
}
return jerseyNumber;
this.name = name;
this.jerseyNumber = jerseyNumber;
@Override
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.
• Constructor and getter/setter methods handle the initialization and updating of these values.
In the FootballTeam.java file, create and manipulate instances of the TeamMember class:
// Main program
System.out.println(player1);
740
The Complete Java Training Imran Afzal
System.out.println(player2);
System.out.println(player3);
player1.setName("Chris");
player2.setJerseyNumber(9);
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.
• The System.out.println() statements display the details using the overridden toString() method.
741
The Complete Java Training Imran Afzal
Output of the Program
Initial details:
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.
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).
Code
742
The Complete Java Training Imran Afzal
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().
import java.util.ArrayList;
import java.util.List;
743
The Complete Java Training Imran Afzal
private int totalLosses = 0;
this.teamName = teamName;
if (!teamMembers.contains(player)) {
teamMembers.add(player);
System.out.println(player);
744
The Complete Java Training Imran Afzal
}
totalWins++;
totalTies++;
} else {
totalLosses++;
745
The Complete Java Training Imran Afzal
@Override
• Calculates ranking: 2 points for a win, 1 for a tie, and always adds 1 to ensure rank is never
zero.
import java.util.ArrayList;
import java.util.List;
746
The Complete Java Training Imran Afzal
public CricketTeams(String teamName) {
this.teamName = teamName;
if (!teamMembers.contains(player)) {
teamMembers.add(player);
System.out.println(player.getName());
747
The Complete Java Training Imran Afzal
@Override
return teamName;
This generic class can manage teams made of any type that implements NewPlayer. It:
return "beat";
} else {
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);
749
The Complete Java Training Imran Afzal
NewCricketTeam australia = new NewCricketTeam("Australia");
australiaTeam.addTeamMember(smith);
australiaTeam.addTeamMember(warner);
indiaTeam.addTeamMember(kohli);
indiaTeam.listTeamMembers();
750
The Complete Java Training Imran Afzal
CricketTeams<NewPlayer> england = new CricketTeams<>("England");
england.addTeamMember(new NewPlayer() {
@Override
});
england.listTeamMembers();
pakistan.addTeamMember(new NewPlayer() {
@Override
});
pakistan.listTeamMembers();
751
The Complete Java Training Imran Afzal
scoreResult(pakistan, 250, england, 230);
Explanation
Output
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.
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).
The compareTo method compares the current object (this) with the object passed as a parameter (o).
It returns:
This interface is essential for sorting objects, using ordered collections, and performing binary
searches.
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:
753
The Complete Java Training Imran Afzal
• Ordered collections such as TreeSet or TreeMap
this.name = name;
this.age = age;
@Override
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.
Integer number = 5;
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.
755
The Complete Java Training Imran Afzal
for (String w : words) {
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.
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().
This part prints the Unicode values of uppercase and lowercase characters. It helps explain why
uppercase letters come before lowercase letters when sorted lexicographically.
756
The Complete Java Training Imran Afzal
private String employeeName;
this.employeeName = name;
@Override
return employeeName;
@Override
return -1;
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:
• For all other cases, it falls back to the default string comparison.
Employee[] employeeArray = {
};
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.
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>.
• 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.
• Limited Customization: Modifying the comparison behavior without changing the class is
not straightforward.
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.
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
this.employeeName = name;
@Override
return employeeName;
@Override
if (o instanceof Employee) {
761
The Complete Java Training Imran Afzal
if (employeeName.equals("Tim") && other.employeeName.equals("Ann")) {
return -1;
return 1;
} else {
return employeeName.compareTo(other.employeeName);
return employeeName.compareTo(other);
} else {
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.
• If the argument is a String, compares the employee name with that string
import java.util.Arrays;
Arrays.sort(employeeArray);
System.out.println(Arrays.toString(employeeArray));
763
The Complete Java Training Imran Afzal
System.out.println("result = " + employee1.compareTo("Mary"));
• 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 result of comparing employee1 with the string "Mary" is printed using compareTo().
Output Explanation
result = 7
• After sorting, the array is ordered based on the defined rules and string comparison.
• 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