0% found this document useful (0 votes)
23 views

The Java Handbook

boost your coding

Uploaded by

chrissyndagi
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
23 views

The Java Handbook

boost your coding

Uploaded by

chrissyndagi
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 153

The Java Handbook – Learn Java

Programming for Beginners

Farhan Hasin Chowdhury


Java has been around since the 90s. And
despite its massive success in many areas,
this cross-platform, object-oriented
programming language is often maligned.
Regardless of how people feel about Java, I can tell you
from experience is that it is an excellent programming
language. After its first appearance back in 1995, it's
still widely-used – and chances are it's not going
anywhere anytime soon.

You can use Java to build servers, create desktop


applications, games, mobile applications and more.
There are also other JVM (we'll discuss what that means
very soon) languages such as Kotlin, Groovy, Scala,
and Clojure that you can use for different purposes.
Java is also cross-platform which means code you write
and compile on one platform can run on any other
platform that has Java installed on it. We'll discuss this
topic in much more detail later on.

For now, I can tell you that although Java has its fair
share of flaws, it also a has a lot to offer.

Table of Contents
 Prerequisites
 How to Write Hello World in Java
 What’s Going On in the Code?
 What is JVM?
 What is JRE and JDK?
 How To Setup Java on Your Computer?
 How To Install a Java IDE on Your Computer?
 How To Create a New Project on IntelliJ IDEA
 How to Work with Variables in Java
 What Are the Rules for Declaring Variables?
 What Are final Variables?
 What are the Primitive Data Types in Java?
 What is Type Conversion or Casting?
 What are Wrapper Classes in Java
 How to Use Operators in Java
 What Are the Arithmetic Operators?
 What Are the Assignment Operators?
 What Are the Relational Operators?
 What Are the Logical Operators?
 What Are the Unary Operators?
 How to Work with Strings in Java
 How to Format a String
 How to Get the Length of a String or Check if It's Empty
or Not
 How to Split and Join Strings
 How to Convert a String to Upper or Lowercase
 How to Compare Two Strings
 How to Replace Characters or Substring in a String
 How to Check If a String Contains a Substring or Not
 What Are the Different Ways of Inputting and
Outputting Data?
 How to Use Conditional Statements in Java
 What is a switch-case statement?
 What is Variable Scope in Java?
 What Are Default Values of Variables in Java?
 How to Work with Arrays in Java
 How to Sort an Array
 How to Perform Binary Search on an Array
 How to Fill an Array
 How to Make Copies of an Array
 How to Compare Two Arrays
 How to Use Loops in Java
 For Loop
 For-Each Loop
 While Loop
 Do-While Loop
 How to Work with Array Lists in Java
 How to Add or Remove Multiple Elements
 How to Remove Elements Based on a Condition
 How to Clone and Compare Array Lists
 How To Check if an Element Is Present or the Array List
Is Empty
 How to Sort an Array List
 How To Keep Common Elements From Two Array Lists
 How To Perform an Action on All Elements of an Array
List
 How To Work With Hash Maps in Java
 How To Put or Replace Multiple Elements in a Hash Map
 How To Check if a Hash Map Contains an Item or if It’s
Empty
 How To Perform an Action on All Elements of a Hash
Map
 Classes and Objects in Java
 What is a Method?
 What is Method Overloading?
 What are Constructors in Java?
 What Are Access Modifiers in Java?
 What Are the Getter and Setter Methods in Java?
 What is Inheritance in Java?
 How to Override a Method in Java
 Conclusion
Prerequisites
The only re-requisite for this course is familiarity with
any other programming language such as Python,
JavaScript, and so on.

Although I'll explain crucial programming concepts in


the context of Java, I'll not explain things like what a
variable is in the context of programming in general.
How to Write Hello World in Java
Ideally the first step should've been setting up Java on
your computer, but I don't want to bore you with
downloading and installing a bunch of software right at
the beginning. For this example, you'll
use https://replit.com/ as your platform.
First, head over to https://replit.com/ and create a new
account if you don't already have one. You can use your
existing Google/GitHub/Facebook account to login. Once
logged in, you'll land on your home. From there, use
the Create button under My Repls to create a new
repl.

In the Create a Repl modal,


choose Java as Template, set a descriptive Title such
as HelloWorld and hit the Create Repl button.
A code editor will show up with an integrated terminal
as follows:
On the left side is the list of files in this project, in the
middle is the code editor, and on the right side is the
terminal.

The template comes with some code by default. You


can run the code by hitting the Run button. Go ahead
and do that, run the program.
If everything goes fine, you'll see the words "Hello
world!" printed on the right side. Congratulations,
you've successfully run your first Java program.

What’s Going On in the Code?


The hello world program is probably the most basic
executable Java program that you can possibly write –
and understanding this program is crucial.

class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
Let's start with the first line:

class Main {
//...
}
This line creates a Main class. A class groups together a
bunch of related code within a single unit.
This is a public class, which means this class is
accessible anywhere in the codebase. One Java source
file (files with the .java extension) can contain only one
top level public class in it.
This top level public class has to be named exactly the
same as the source code filename. That's why the file
named Main.java contains the main class in this project.
To understand why, click on the three dots in the list of
files and click on the Show hidden files option.
This will unveil some new files within the project.
Among them is the Main.class file. This is called a
bytecode. When you hit the Run button, the Java
compiler compiled your code from the Main.java file into
this bytecode.
Now, modify the existing Hello World code as follows:

class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}

class NotMain {
public static void main(String[] args) {
System.out.println("Not hello world!");
}
}
As you can see, a new class called NotMain has been
added. Go ahead and hit the Run button once more
while keeping your eyes on the Files menu.
A new bytecode named NotMain.class has showed up.
This means that for every class you have within your
entire codebase, the compiler will create a separate
bytecode.
This creates confusion about which class is the entry-
point to this program. To solve this issue, Java uses the
class that matches the source code file name as the
entry-point to this program.

Enough about the class, now let's look at the function


inside it:

class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
The public static void main (String[] args) function is
special in Java. If you have experience with languages
like C, C++ or Go, you should already know that every
program in those languages has a main function. The
execution of the program begins from this main
function.
In Java, you have to write this function as exactly public
static void main (String[] args) otherwise it won't work.
In fact, if you change it even a little bit Java will start to
scream.

The return type has changed from void to int and the
function now returns 0 at the end. As you can see in the
console, it says:
Error: Main method must return a value of type void in class Main, please
define the main method as:
public static void main(String[] args)
Listen to that suggestion and revert your program back
to how it was before.
class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
The main method is a public method and
the static means, you can call it without instantiating its
class.
The void means that the function doesn't return any
value and the String[] args means that the function
takes an array of strings as an argument. This array
holds command line arguments passed to the program
during execution.
The System.out.println prints out strings on the terminal.
In the example above, "Hello world!" has been passed to
the function, so you get Hello world! printed on the
terminal.
In Java, every statement ends with a semicolon. Unlike
JavaScript or Python, semicolons in Java are
mandatory. Leaving one out will cause the compilation
to fail.
That's pretty much it for this program. If you didn't
understand every aspect of this section word by word,
don't worry. Things will become much clearer as you go
forward.

For now, remember that the top level public class in a


Java source file has to match the file name, and the
main function of any Java program has to be defined
as public static void main(String[] args).
What is JVM?
I've uttered the word "bytecode" a few times already in
the previous section. I've also said that Java is "cross-
platform" which means code written and compiled in
one platform can run on any platform that has Java
installed on it.

You see, your processor doesn't understand English. In


fact the only thing it understands are zeros and ones,
aka binary.

When you write and compile a C++ program it results


in a binary file. Your processor understands it and based
on the program's targeted platform, this file can be
different.

Take an AMD64 and an ARMv8-A processor for example.


These processors have different instruction sets. So in
order to run your program on these two different
platforms, you'll have to compile them separately.

But a Java program can be written once and run


anywhere. I hope you remember the bytecodes we
talked about in the previous section. When you compile
Java code it doesn't result in binary but rather in
bytecode.

This bytecode is not entirely binary but it's also not


human readable. In fact, your processor can't read it
either.

So instead of throwing this bytecode at the CPU, we


instead run it through the Java Virtual Machine or JVM
for short. JVM then reads and interprets the bytecode to
the CPU.
If you would like to understand the architecture of JVM
at a deeper level, I would suggest Siben Nayak's in-
depth article on the topic.
What is JRE and JDK?
JRE stands for Java Runtime Environment and JDK
stands for Java Development Kit.

The JRE or Java Runtime Environment packages


together an implementation of the JVM along with a set
of libraries required for running Java programs.

The JDK, on the other hand, packages the JRE along


with all the necessary libraries for developing Java
programs.

So if you want to run Java programs on your computer


you install the JRE. If you want to develop Java
programs yourself, you install the JDK. There are
multiple implementation of the JDK.

There is the Java SE (Standard Edition) Development


Kit from Oracle, then there is the OpenJDK, an official
reference implementation of Java SE (Standard Edition)
Development Kit.
As you can tell from the name of OpenJDK, it's open-
source. So there are multiple builds of it. If you're on a
Linux machine and use your distro's package manager
to install JDK, it's highly likely that you'll install an
OpenJDK build such as Adoptium, Microsoft Build of
OpenJDK and so on.
I hope that you understand that JRE is a superset of JVM
and JDK is a superset of JRE. Don't worry about different
implementations or builds at the moment, you'll get
your hands on them when the time comes.

How to Setup Java on Your Computer


First, head over
to https://www.oracle.com/java/technologies/downloads
/ and download the latest version of the Java SE
Development Kit according to the platform you're on:
Once the download has finished, start the installer and
go through the installation process by hitting the Next
buttons. Finish it by hitting the Close button on the last
page.

The installation process may vary on macOS and Linux


but you should be able to figure it out by yourself.
Once the installation has finished, execute the following
command on your terminal:

java --version

# java 18.0.2 2022-07-19


# Java(TM) SE Runtime Environment (build 18.0.2+9-61)
# Java HotSpot(TM) 64-Bit Server VM (build 18.0.2+9-61, mixed mode, sharing)
If it works, you've successfully install Java SE
Development Kit on your computer. If you want to use
OpenJDK instead, feel free to download Microsoft Build
of OpenJDK or Adoptium and go through the installation
process.
For the simple example programs that we're going to
write in this article, it won't matter which JDK you're
using. But in real life, make sure that your JDK version
plays nicely with the type of project you're working on.

How to Install a Java IDE on Your Computer


When it comes to Java, IntelliJ IDEA is undeniably the
best IDE out there. Even Google uses it as a base for
their Android Studio.
The ultimate version of the IDE can cost an individual
up-to $149.00 per year. But if you're student, you can
get educational licenses for all JetBrains products for
free.
There is also the completely free and open-source
community edition. This is the one we'll be using
throughout the entire book.

Head over to the IntelliJ IDEA download page, and


download the community edition for your platform.
Once the download finishes, use the installer to install
IntelliJ IDEA like any other software.

How to Create a New Project on IntelliJ IDEA


Use the shortcut from your start menu to start IntelliJ
IDEA. The following window will show up:
Use the New Project button and a New
Project window will show up:
Put a descriptive name for your project. Leave the rest
of the options as they are and press the Create button.
The project creation shouldn't take longer than a
moment and once it's done, the following window will
show up:
That's the project tool window on the left side. All your
source code will live inside that src folder.
Right click on the src folder and go to New > Java
Class.
In the next step, put a name, such as Main for your class
and make sure Class is highlighted as the type.

A new class will be created with a few lines of code.

Update the code as follows:


public class Main {
public static void main (String[] args) {
System.out.println("Hello World!");
}
}
To run this code, use the green play button on the right
side of the top bar.

The code will run and the output will be shown in the
integrated terminal at the bottom of the window.
Congratulations, you've successfully recreated the
previously discussed HelloWorld program in IntelliJ IDEA.
How to Work with Variables in Java
To work with different kinds of data in Java, you can
create variables of different types. For example, if you
want to store your age in a new variable, you can do so
like this:

public class Main {

public static void main(String[] args) {


// <type> <name>
int age;

}
You start by writing out the type of data or variable.
Since age is a whole number, its type will be integer
or int for short, followed by the name of the
variable age and a semicolon.
At the moment, you've declared the variable but you
haven't initialized it. In other words, the variable doesn't
have any value. You can initialize the variable as
follows:

public class Main {

public static void main(String[] args) {


// <type> <name>
int age;

// <name> = <value>
age = 27;
// prints the age on the terminal
System.out.println("I am " + age + " years old.");

}
When assigning a value, you start by writing the name
of the variable you want to initialize, followed by an
equal sign (it's called the assignment operator) then the
value you want to assign to the variable. And don't
forget the semicolon at the end.

The System.out.println(); function call will print the line I


am 27 years old. to the console. In case you're
wondering, using a plus sign is one of the many ways to
dynamically print out variables in the middle of a
sentence.
One thing that you have to keep in mind is you can not
use an uninitialized variable in Java. So if you comment
out the line age = 27 by putting two forward slashes in
front of it and try to compile the code, the compiler will
throw the following error message at you:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The local variable age may not have been initialized

at variables.Main.main(Main.java:13)
The line The local variable age may not have been
initialized indicates that the variable has not been
initialized.
Instead of declaring and initializing the variable in
different lines, you can do that in one go as follows:

public class Main {


public static void main(String[] args) {
// <type> <name> = <value>
int age = 27;

// prints the age on the terminal


System.out.println("I am " + age + " years old.");

}
The code should be back to normal again. Also, you can
change the value of a variable as many times as you
want in your code.

public class Main {

public static void main(String[] args) {


int age = 27;

// updates the value to be 28 instead of 27


age = 28;

System.out.println("I am " + age + " years old.");

}
In this code, the value of age will change from 27 to 28
because you're overwriting it just before printing.
Keep in mind, while you can assign values to a variables
as many times as you want, you can not declare the
same variable twice.

public class Main {


public static void main(String[] args) {
// <type> <name> = <value>
int age = 27;

int age = 28;

// prints the age on the terminal


System.out.println("I am " + age + " years old.");

}
If you try to compile this code, the compiler will throw
the following error message at you:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:


Duplicate local variable age

at variables.Main.main(Main.java:9)
The line Duplicate local variable age indicates that the
variable has already been declared.
Apart from variables, you may find the term "literal" on
the internet. Literals are variables with hardcoded
values.

For example, here, age = 27 and it's not dynamically


calculated. You've written the value directly in the
source code. So age is an integer literal.
What Are the Rules for Declaring Variables?
There are some rules when it comes to naming your
variables in Java. You can name it anything as long as it
doesn't start with a number and it can't contain any
spaces in the name.
Although, you can start a variable name with an
underscore (_) or a dollar sign ($), not being mindful of
their usage can make your code hard to read. Variable
names are also case sensitive. So age and AGE are two
different variables.
Another important thing to remember is you can not
use any of the keywords reserved by Java. There are
around 50 of them at present. You can learn about
these keywords from the official documentation but
don't worry about memorizing them.
As you keep practicing, the important ones will slip into
your neurons automatically. And if you still manage to
mess up a variable declaration, the compiler will be
there to remind you that something's wrong.

Apart from the rules, there are some conventions that


you should follow:

 Start your variable name with small letter and not any
special character (like an underscore or dollar sign).
 If the variable name has multiple words, use camel
case: firstName, lastName
 Don't use single letter names: f, l
As long as you follow these rules and conventions,
you're good to go. If you'd like to learn more about
naming conventions in general, checkout my article on
the topic.
What Are final Variables?
A final variable in Java can be initialized only once. So if
you declare a variable as final, you can not reassign it.
public class Main {

public static void main(String[] args) {


// final <type> <name> = <value>
final int age = 27;

age = 28;

System.out.println("I am " + age + " years old.");

}
Since the age variable has been declared as final, the
code will throw the following error message at you:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The final local variable age cannot be assigned. It must be blank and not
using a compound assignment

at variables.Main.main(Main.java:9)
However, if you leave the variable uninitialized while
declaring, the code will work:

public class Main {

public static void main(String[] args) {


// final <type> <name>
final int age;

age = 28;

// prints the age on the terminal


System.out.println("I am " + age + " years old.");

}
So, declaring a variable as final will limit your ability to
reassign its value. If you leave it uninitialized, you'll be
able to initialize it as usual.
What are the Primitive Data Types in Java?
At a high level, there are two types of data in Java.
There are the "primitives types" and the "non-primitive"
or "reference types".

Primitive types store values. For example, int is a


primitive type and it stores an integer value.
A reference type, on the other hand, stores the
reference to a memory location where a dynamic object
is being stored.

There are eight primitive data types in Java.

TYPE EXPLANATION
byte 8-bit signed integer within the range of -128 to 127
short 16-bit signed integer within the range of -32,768 to 32,767
32-bit signed integer within the range of -2147483648 to
int 2147483647
64-bit signed integer within the range of -
long 9223372036854775808 to 9223372036854775807
single-precision 32-bit floating point within the range of 1.4E-45
float to 3.4028235E38
double-precision 64-bit floating point within the range of 4.9E-
double 324 to 1.7976931348623157E308
boolean It can be either true or false

single 16-bit Unicode character within the range of \u0000 (or 0)


char to \uffff (or 65,535 inclusive)
Yeah yeah I know the table looks scary but don't stress
yourself. You don't have to memorize them.
You will not need to think about these ranges very
frequently, and even if you do, there are ways to print
them out within your Java code.

However, if you do not understand what a bit is, I would


recommend this short article to learn about binary.
You've already learned about declaring an integer in the
previous section. You can declare a byte, short,
and long in the same way.
Declaring a double also works the same way, except you
can assign a number with a decimal point instead of an
integer:
public class Main {

public static void main(String[] args) {


double gpa = 4.8;

System.out.println("My GPA is " + gpa + ".");

}
}
If you assign an int to the double, such as 4 instead
of 4.8, the output will be 4.0 instead of 4,
because double will always have a decimal point.
Since double and float are similar, you may think that
replacing the double keyword with float will convert this
variable to a floating point number – but that's not
correct. You'll have to append a f or F after the value:
public class Main {

public static void main(String[] args) {


float gpa = 4.8f;

System.out.println("My GPA is " + gpa + ".");


}
}
This happens because, by default, every number with a
decimal point is treated as a double in Java. If you do not
append the f, the compiler will think you're trying to
assign a double value to a float variable.
boolean data can hold either true or false values.
public class Main {

public static void main(String[] args) {


boolean isWeekend = false;

System.out.println(isWeekend); // false

}
}
As you can imagine, false can be treated as a no
and true can be treated as a yes.
Booleans will become much more useful once you've
learned about conditional statements. So for now, just
remember what they are and what they can hold.

The char type can hold any Unicode character within a


certain range.
public class Main {

public static void main(String[] args) {


char percentSign = '%';

System.out.println(percentSign); // %

}
}
In this example, you've saved the percent sign within
a char variable and printed it out on the terminal.
You can also use Unicode escape sequences to print out
certain symbols.

public class Main {

public static void main(String[] args) {


char copyrightSymbol = '\u00A9';

System.out.println(copyrightSymbol); // ©

}
}
The Unicode escape sequence for the copyright symbol,
for example, is \u00A9 and you can find more Unicode
escape sequences on this website.
Among these 8 types of data, you'll be working
with int, double, boolean, and char majority of the time.
What is Type Conversion or Casting?
Type conversion in Java can be either "implicit" or
"explicit". When the compiler converts a smaller type of
data to a larger one automatically, it's known as an
implicit or narrowing type conversion.

public class Main {

public static void main(String[] args) {


int number1 = 8;
double number2 = number1;

System.out.println(number2); // 8.0
}
}
Since a double is larger than an integer, the compiler
could easily perform the conversion. If you try to do the
reverse however, you'll face the following error from the
compiler:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:


Type mismatch: cannot convert from double to int

at operators.Main.main(Main.java:7)
When performing an implicit conversion, the flow of
conversion should be as follows:

You can of course go from a short to a double, for


example, skipping the others in between.
You can also go from smaller data types to larger ones.
That's called an explicit or widening type conversion.

package datatypes;

public class Main {

public static void main(String[] args) {


double number1 = 8.5;
int number2 = (int) number1;

System.out.println(number2); // 8
}

}
Previously you've seen that if you try to convert a larger
data type to a smaller one, the compiler complains. But
when you add the (int) cast operator explicitly, you
show the compiler who's boss.
In doing so, you lose a part of your data. If you change
the initial double number from 8.5 to just 8.0, you'll not
lose any information. So whenever you're performing an
explicit conversion, be careful.
You can also convert a char to an int as follows:
public class Main {

public static void main(String[] args) {


char character = 'F';
int number = character;

System.out.println(number); // 70
}

}
70 is the ASCII code for the character F – that's why the
output was like this. If you'd like to learn more about
ASCII codes, my colleague Kris Koishigawa has
written an excellent article on the topic.
The flow of conversion in this case will be the opposite
of what you've seen already.

I'd suggest you to experiment by converting various


values from one type to another and see what happens.
This will deepen your understanding and make you
confident.
What are Wrapper Classes in Java?
Wrapper classes can wrap around primitive datatypes
and turn them into reference types. Wrapper classes
are available for all eight primitive data types.

Primitive Wrapper
Type Class
int Integer

long Long

short Short

byte Byte

boolean Boolean

char Character

float Float

double Double

You can use these wrapper classes as follows:

public class Main {


public static void main (String[] args) {
Integer age = 27;
Double gpa = 4.8;

System.out.println(age); // 27
System.out.println(gpa); // 4.8
}
}
All you have to do is replace the primitive data type
with the equivalent wrapper class. These reference
types also have methods for extracting the primitive
type from them.

For example, age.intValue() will return the age as a


primitive integer and the gpa.doubleValue() will return the
GPA in a primitive double type.
There are such methods for all eight datatypes.
Although you'll use the primitive types most of the time,
these wrapper classes will be handy in some scenarios
we'll discuss in a later section.

How to Use Operators in Java


Operators in programming are certain symbols that tell
the compiler to perform certain operations such as
arithmetic, relational, or logical operations.

Although there are six types of operators in Java, I won't


talk about bitwise operators here. Discussing bitwise
operators in a beginner guide can make it intimidating.

What Are the Arithmetic Operators?


Arithmetic operators are the ones that you can use to
perform arithmetic operations. There are five of them:

OPERATO
R OPERATION
+ Addition
- Subtraction
* Multiplication
/ Division
Remainder
% (Modulo/Modulus)
Addition, subtraction, multiplication, and division
operations are pretty self-explanatory. Have a look at
the following code example to understand:

public class Main {

public static void main(String[] args) {


int number1 = 10;
int number2 = 5;

System.out.println(number1 + number2); // 15
System.out.println(number1 - number2); // 5
System.out.println(number1 * number2); // 50
System.out.println(number1 / number2); // 2
System.out.println(number1 % number2); // 0

}
Outputs from the first four operations need no
explanation. In the last operation, you've performed a
modulo/modulus operation using the % symbol. The
result is 0 because if you divide 10 by 2, there'll be
nothing left (no remainder).
Addition and multiplication operations are quite simple.
But, when performing a subtraction, if the first operand
is larger than the second operand, the result will be a
negative number, just like in real life.

The type of data you're working with makes a difference


in the result of division and modulo operations.

public class Main {

public static void main(String[] args) {


int number1 = 8;
int number2 = 5;

System.out.println(number1 / number2); // 1
}

}
Although the result of this operation should've been 1.6
it didn't happen because in Java, if you divide an integer
by another integer, the result will be an integer. But if
you change both or one of them to a float/double,
everything will be back to normal.

public class Main {

public static void main(String[] args) {


double number1 = 8;
double number2 = 5;

System.out.println(number1 / number2); // 1.6


}

}
This principle applies to the modulo operations as well.
If both or one of the operands are a float/double, the
result will be a float/double.

What Are the Assignment Operators?


You've already worked with the assignment operator in
a previous section.

public class Main {

public static void main(String[] args) {


// <type> <name> = <value>
int age = 27;

// prints the age on the terminal


System.out.println(age);

}
}
When you use the = symbol to assign a value to a
variable, it works as an assignment operator. But, this is
not the only form of this operator.
Combining the regular assignment operator with the
arithmetic operators, you can achieve different results.

OPERATO OPERATIO EQUIVALENT


R N TO
+= a += b a = a + b

-= a -= b a = a - b

*= a *= b a = a * b

/= a /= b a = a / b

%= a %= b a = a % b

The following code example should make things clearer:

package operators;

public class Main {

public static void main(String[] args) {


double number1 = 10;
double number2 = 5;

number1 += number2;

System.out.println(number1); // 15
}

}
The other operators work the same. They operate and
then assign the resultant value to the left operand.
I could demonstrate the other ones using code but I
think if you try them out yourself, you'll get a better
understanding. After all, experimentation and practice
are the only ways to solidify your knowledge.

What Are the Relational Operators?


Relational operators are used to check the relation
between operands. Such as whether an operand is
equal to another operand or not.

These relational operators return


either true or false depending on the operation you've
performed.
There are six relational operators in Java.

OPERATO
R EXPLANATION USAGE
5 ==
== Is Equal To 8 returns false

5 !=
!= Is Not Equal To 8 returns true

5 >
> Is Greater Than 8 returns false

5 <
< Is Less Than 8 returns true

Greater Than or 5 >=


>= Equal To 8 returns false

Less Than or Equal 5 <=


<= To 8 returns true

The following code example demonstrates the usage of


these operators:

public class Main {

public static void main(String[] args) {


double number1 = 10;
double number2 = 5;

System.out.println(number1 == number2); // false


System.out.println(number1 != number2); // true
System.out.println(number1 > number2); // true
System.out.println(number1 < number2); // false
System.out.println(number1 >= number2); // true
System.out.println(number1 <= number2); // false
}

}
Practical usage of these operators will become much
apparent to you once you've learned about conditional
statements in a later section.

You can also use these operators with characters.

public class Main {

public static void main(String[] args) {


char smallLetter = 'a';
char capitalLetter = 'A';

System.out.println(smallLetter > capitalLetter); // ???


}

}
What do you think the output of this code will be? Find
out for yourself. Remember the ASCII values of the
characters? They play a role in the output of this
program.

What Are the Logical Operators?


Imagine a scenario where a program you've made can
only be used by people who are 18 and up but not over
40 years old. So the logic should be as follows:

can run the program if ->


age >= 18 and age <= 40
Or in another scenario, a user has to be a student of
your school or member of the library to borrow books.
In this case the logic should be as follows:

can borrow books if ->


isSchoolStudent or isLibraryMember
These logical decisions can be made using logical
operators. There are three such operators in Java.

OPERA EXPLANAT
TOR USAGE ION
Evaluates
to true,
only if
Logical both
age >= 18
And && age <= conditions
(&&) 40 are true
Evalu
ates
to
true if
one
of the
two
or
both
condi
tions
Logical `isSchool isLibraryM are
Or (` `) Student ember` true
Not (!) ! Evaluates
isLibraryMe
mber to false if
OPERA EXPLANAT
TOR USAGE ION
the inner
condition
evaluates
to true
and vise
versa
Let's see these operators in code. First, the
logical and operator:
public class Main {

public static void main(String[] args) {


int age = 20;

System.out.println(age >= 18 && age <= 40); // true


}

}
In this case, there are two conditions on either side of
the && operator. If and only if both conditions evaluate
to true, the and operation evaluates to true.
If the first condition evaluates to false, the computer
will not evaluate the rest of the conditions and
return false. Because if the first one evaluates to false,
then there is no way for the entire operation to evaluate
to true.
The logical or operator works similarly, but in this case,
if any of the conditions are true then the entire
operation will evaluate to true:
public class Main {

public static void main(String[] args) {


boolean isSchoolStudent = true;
boolean isLibraryMember = false;
System.out.println(isSchoolStudent || isLibraryMember); // true
}

}
If the first condition of a logical or operation evaluates
to true, the computer will not evaluate the rest of the
conditions and return true. Because if the first condition
evaluates to true the operation will evaluate
to true regardless of what the other conditions evaluate
to.
Finally the not operator evaluates to the opposite of
whatever its condition evaluates to. Take a look at the
following code example:
public class Main {

public static void main(String[] args) {


boolean isLibraryMember = true;

System.out.println(isLibraryMember); // true
System.out.println(!isLibraryMember); // false
}

}
As you can see, the not operator returns the opposite of
the given boolean value. The not operator is a unary
operator, meaning it operates on a single operand.
public class Main {

public static void main(String[] args) {


boolean isLibraryMember = true;
boolean isSchoolStudent = false;

System.out.println(!isSchoolStudent || isLibraryMember); // true


}
}
In this example, the not operator
turns isSchoolStudent into true, so the operation
evaluates to true. However, if you modify the code as
follows:
public class Main {

public static void main(String[] args) {


boolean isLibraryMember = true;
boolean isSchoolStudent = false;

System.out.println(!(isSchoolStudent || isLibraryMember)); // false


}

}
First, the logical or operation will take place and
evaluate to true. The not operator will turn it into false.
Although you've used two operands with each operator,
you can use as many as you want. You can also mix and
match multiple operators together.

public class Main {

public static void main(String[] args) {


boolean isSchoolStudent = true;
boolean isLibraryMember = false;
int age = 10;

System.out.println(isSchoolStudent || isLibraryMember && age > 18);


// ???
}

}
What do you think the output of this code will be? I'd
recommend you find out by yourself. :)

What Are the Unary Operators?


There are some operators that are used with one
operand at a time and these are called the unary
operators. Although there are five of them, I'll only
discuss two.

OPERATOR EXPLANATION
Increment Increments a given
(++) value by 1
Decrement Decrements a given
(--) value by 1
The following code example will demonstrate them
nicely:

public class Main {

public static void main(String[] args) {


int score = 95;
int turns = 11;

score++;
turns--;

System.out.println(score); // 96
System.out.println(turns); // 10
}

}
You can also use the operators as prefixes:

public class Main {


public static void main(String[] args) {
int score = 95;
int turns = 11;

++score;
--turns;

System.out.println(score); // 96
System.out.println(turns); // 10
}

}
So far this is simple. But there are some slight
differences between the postfix and prefix syntaxes
that you need to understand. Look at the following
code:

package operators;

public class Main {

public static void main(String[] args) {


int score = 95;

System.out.println(++score); // 96
System.out.println(score); // 96
}

}
This is expected behavior. The prefix decrement
operator will work the same. But look what happens if
you switch to the postfix version:

package operators;
public class Main {

public static void main(String[] args) {


int score = 95;

System.out.println(score++); // 95
System.out.println(score); // 96
}

}
Confusing, isn't it? What do you think is the actual value
of the variable right now? It's 96. Let me explain.

When using the postfix syntax within a print function,


the print function encounters the variable first and then
increments it. That's why the second line prints out the
newly updated value.

In case of the prefix syntax, the function encounters the


increment operator first and performs the operation.
Then it goes on to printing the updated value.

This little difference may catch you off guard if you're


not careful. Or you try to avoid incrementing or
decrementing within function calls.

How to Work with Strings in Java


The String type in Java is one of the most commonly
used reference types. It's a collection of characters that
you can use to form lines of text in your program.
There are two ways of creating new strings in Java. The
first one is the literal way:
public class Main {
public static void main(String[] args) {
String name = "Farhan";

System.out.println("My name is " + name + ".");


}

}
As you can see, declaring and using a String this way is
not very different from declaring the primitive types in
Java.
The second way to create a new String is by using
the new operator.
public class Main {
public static void main(String[] args) {
// <type> <name> = new <type>(<value>)
String name = new String("Farhan");

System.out.println("My name is " + name + ".");


}

}
This program will work exactly like the previous one but
there's a slight difference between the two.

The JVM maintains a portion of your computer's


memory for storing strings. This portion is called the
string pool.

Whenever you create a new String in the literal way, the


JVM first checks if that String already exists in the pool.
If it does, JVM will reuse it. If it doesn't, then the JVM will
create it.
On the other hand, when you use the new operator, the
JVM will always create a new String object no matter
what. The following program demonstrates this concept
clearly:
public class Main {

public static void main(String[] args) {


String literalString1 = "abc";
String literalString2 = "abc";

String objectString1 = new String("abc");


String objectString2 = new String("abc");

System.out.println(literalString1 == literalString2);
System.out.println(objectString1 == objectString2);

}
As you may already know, the == operator is used for
checking equality. The output of this program will be:
true
false
Since abc was already in the string pool,
the literalString2 variable reuses that. In case of the
object strings however, both of them are different
entities.
How to Format a String
You've already seen the usage of the + operator to sew
strings together or format them in a specific way.
That approach works until you have a lot of additions to
a string. It's easy to mess up the placements of the
quotation marks.
A better way to format a string is
the String.format() method.
public class Main {
public static void main(String[] args) {
String name = "Farhan";
int age = 27;

String formattedString = String.format("My name is %s and I'm %d years


old.", name, age);

System.out.println(formattedString);
}

}
The method takes a string with format specifiers as its
first argument and arguments to replace those
specifiers as the later arguments.

In the code above, the %s, and %d characters are format


specifiers. They're responsible for telling the compiler
that this part of the string will be replaced with
something.
Then the compiler will replace the %s with the name and
the %d with the age. The order of the specifiers needs to
match the order of the arguments and the arguments
need to match the type of the specifier.
The %s and %d are not random. They are specific for
string data and decimal integers. A chart of the
commonly used specifiers are as follows:
Specifi
er Data Type
%b, %B Boolean
%s, %S String
%c, %C Unicode Character
Specifi
er Data Type
%d Decimal Integer
Floating Point
%f Numbers
There is also %o for octal integers, %x or %X for
hexadecimal numbers, and %e or %E for scientific
notations. But since, we won't discuss them in this
book, I've left them out.
Just like the %s and %d specifiers you saw, you can use
any of these specifiers for their corresponding data
type. And just in case you're wondering, that %f specifier
works for both floats and doubles.
How to Get the Length of a String or Check if It's Empty or
Not
Checking the length of a string or making sure its not
empty before performing some operation is a common
task.

Every string object comes with a length() method that


returns the length of that string. It's like
the length property for arrays.
public class Main {
public static void main(String[] args) {
String name = "Farhan";

System.out.println(String.format("Length of this string is: %d.",


name.length())); // 6
}

}
The method returns the length as an integer. So you
can freely use it in conjunction with the integer format
specifier.
To check if a string is empty or not, you can use
the isEmpty() method. Like the length() method, it also
comes with every string object.
public class Main {
public static void main(String[] args) {
String name = "Farhan";

if (name.isEmpty()) {
System.out.println("There is no name mentioned here");
} else {
System.out.println(String.format("Okay, I'll take care of %s.", name));
}
}

}
The method returns a boolean value so you can use it
directly in if statements. The program checks if the
name is empty or not and prints out different responses
based off of that.

How to Split and Join Strings


The split() method can split a string based on a regular
expression.
import java.util.Arrays;

public class Main {


public static void main(String[] args) {
String name = "Farhan Hasin Chowdhury";

System.out.println(Arrays.toString(name.split(" ")));
}

}
The method returns an array of strings. Each string in
that array will be a substring from the original string.
Here for example, you're breaking the string Farhan Hasin
Chowdhury at each space. So the output will be [Farhan,
Hasin, Chowdhury].
Just a reminder that arrays are collections of multiple
data of the same type.

Since the method takes a regex as argument, you can


use regular expressions to perform more complex split
operations.

You can also join this array back into a string like this:

public class Main {


public static void main(String[] args) {
String name = "Farhan Hasin Chowdhury";

String substrings[] = name.split(" ");

String joinedName = String.join(" ", substrings);

System.out.println(joinedName); // Farhan Hasin Chowdhury


}

}
The join() method can also help you in joining multiple
strings together outside of an array.
public class Main {
public static void main(String[] args) {
System.out.println(String.join(" ", "Farhan", "Hasin", "Chowdhury")); //
Farhan Hasin Chowdhury
}

How to Convert a String to Upper or Lowercase


Converting a string to upper or lower case is very
straightforward in Java. There are the aptly
named toUpperCase() and toLowerCase() methods to
perform the tasks:
public class Main {
public static void main(String[] args) {
String name = "Farhan Hasin Chowdhury";

System.out.println(name.toUpperCase()); // FARHAN HASIN CHOWDHURY

System.out.println(name.toLowerCase()); // farhan hasin chowdhury


}

How to Compare Two Strings


Since strings are reference types, you can not compare
them using the = operator.
The equals() method checks whether two strings are
equal or not and the equalsIgnoreCase() method ignores
their casing when comparing.
public class Main {
public static void main(String[] args) {
String name = "Farhan Hasin Chowdhury";
String nameUpperCase = name.toUpperCase();

System.out.println(name.equals(nameUpperCase)); // false

System.out.println(name.equalsIgnoreCase(nameUpperCase)); // true
}

How to Replace Characters or Substrings in a String


The replace() method can replace characters or entire
substrings from a given string.
package strings;

public class Main {


public static void main(String[] args) {
String loremIpsumStd = "Sed ut perspiciatis unde omnis iste natus error sit
voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae
ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.";

System.out.println(String.format("Standard lorem ipsum text: %s",


loremIpsumStd));

String loremIpsumHalfTranslated = loremIpsumStd.replace("Sed ut


perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque
laudantium", "But I must explain to you how all this mistaken idea of denouncing
pleasure and praising pain was born and I will give you a complete account of the
system");

System.out.println(String.format("Translated lorem ipsum text: %s",


loremIpsumHalfTranslated));
}

}
Here, the loremIpsumStd string contains a portion of the
original lorem ipsum text. Then you're replacing the
first line of that string and saving the new string in
the loremIpsumHalfTranslated variable.
How to Check If a String Contains a Substring or Not
The contains() method can check whether a given string
contains a certain substring or not.
public class Main {
public static void main(String[] args) {
String lyric = "Roses are red, violets are blue";

if (lyric.contains("blue")) {
System.out.println("The lyric has the word blue in it.");
} else {
System.out.println("The lyric doesn't have the word blue in it.");
}
}

}
The method returns a boolean value, so you can use the
function in any conditional statement.

There were some of the most common string methods.


If you'd like to learn about the other ones, feel free to
consult the official documentation.
What Are the Different Ways of Inputting and
Outputting Data?
So far you've learned about
the System.out.println() method to print out information
on the terminal. You've also learned about
the String.format() method in a previous section.
In this section, you'll learn about some siblings of
the System.out.println() method. You'll also learn about
taking input from the user.
Taking input from user is extremely easy in languages
like Python. However in Java, it takes a few more lines
of code.

import java.util.Scanner;

public class Main {

public static void main(String[] args) {


Scanner scanner = new Scanner(System.in);

System.out.print("What's your name? ");


String name = scanner.nextLine();
System.out.printf("So %s. How old are you? ", name);
int age = scanner.nextInt();

System.out.printf("Cool! %d is a good age to start programming.", age);

scanner.close();

}
The java.util.Scanner class is necessary for taking user
inputs. You can bring the class to your program using
the import keyword.
Then, you'll need to create a new instance of
the Scanner class using the new keyword. While creating
the new instance, you'll have to let it know your desired
input stream.
You may want to take input from the user or from a file.
Whatever it is, you'll have to let the compiler know
about it. The System.in stream is the standard input and
output stream.
The scanner object has methods like nextLine() for
taking string input, nextInt() for taking integer
input, nextDouble() for taking double input and so on.
In the code above, the scanner.nextLine() method will ask
for a string from the user and return the given input
with a newline character appended.
Then the scanner.nextInt() method will ask for an integer
and return the given number from the user.
You may be seeing the System.out.printf() method for
the first time here. Well, apart from
the System.out.println() method, there is also
the System.out.print() method that prints out a given
string without appending a newline character to it.
The System.out.printf() is kind of a combination of
the System.out.print() and String.format() methods. You
can use the previously discussed format specifiers in
this method as well.
Once you're done with taking input, you'll need to close
the scanner object. You can do that by simply calling
the scanner.close() method.
Simple right? Let me complicate it a bit.

import java.util.Scanner;

public class Main {

public static void main(String[] args) {


Scanner scanner = new Scanner(System.in);

System.out.print("What's your name? ");


String name = scanner.nextLine();

System.out.printf("So %s. How old are you? ", name);


int age = scanner.nextInt();

System.out.printf("Cool! %d is a good age to start programming. \nWhat


language would you prefer? ", age);
String language = scanner.nextLine();

System.out.printf("Ah! %s is a solid programming language.", language);

scanner.close();

}
I've added a new scanner.nextLine() statement after
the scanner.nextInt() method call. Will it work?
No, it won't. The program will simply skip the last input
prompt and print out the last line. This behavior is not
exclusive to just scanner.nextInt(). If you
use scanner.nextLine() after any of the
other nextWhatever() methods, you'll face this issue.
In short, this happens because when you press enter on
the scanner.nextInt() method, it consumes the integer
and leaves the newline character in the input buffer.
So when scanner.nextLine() is invoked, it consumes that
newline character as the end of the input. The easiest
solution to this problem is writing an
additional scanner.nextLine() call after the other scanner
method calls.
import java.util.Scanner;

public class Main {

public static void main(String[] args) {


Scanner scanner = new Scanner(System.in);

System.out.print("What's your name? ");


String name = scanner.nextLine();

System.out.printf("So %s. How old are you? ", name);


int age = scanner.nextInt();

// consumes the dangling newline character


scanner.nextLine();

System.out.printf("Cool! %d is a good age to start programming. \nWhat


language would you prefer? ", age);
String language = scanner.nextLine();
System.out.printf("Ah! %s is a solid programming language.", language);

scanner.close();

}
There is another way of solving this problem. But I won't
get into that here. If you're interested, checkout my
article on this topic.
How to Use Conditional Statements in Java
You use conditional statements for making decision
based on conditions.

It's done using the if statement as follows:


public class Main {

public static void main(String[] args) {


int age = 20;

// if (condition) {...}
if (age >= 18 && age <= 40) {
System.out.println("you can use the program");
}

}
The statement starts with an if and then there is the
condition inside a pair of parenthesis. If the condition
evaluates to true, the code within the curly braces will
be executed.
Code enclosed between a set of curly braces is known
as a code block.
If you change the value of age to 50 the print statement
will not be executed and there'll be no output on the
console. For these kind of situations where the
condition evaluates to false, you can add an else block:
public class Main {

public static void main(String[] args) {


int age = 20;

if (age >= 18 && age <= 40) {


System.out.println("you can use the program");
} else {
System.out.println("you can not use the program");
}

}
Now if the condition evaluates to false, the code within
the else block will execute and you'll see you can not use
the program printed on your terminal.
You can also have multiple conditions within an if-else
if-else ladder:
public class Main {

public static void main(String[] args) {


int age = 50;
boolean isSchoolStudent = true;
boolean isLibraryMember = false;

// if (condition) {...}
if (age >= 18 && age <= 40) {
System.out.println("you can use the program");
} else if (isSchoolStudent || isLibraryMember) {
System.out.println("you can use the program for a short time");
} else {
System.out.println("you can not use the program");
}

}
Now, if the first condition evaluates to false then the
second condition will be tested. If the second one
evaluates to true then the code within curly braces will
be executed. If the conditions in both if statements
evaluate to false, then the else block will be executed.
You can also nest if statements within
other if statements as follows:
package operators;

public class Main {

public static void main(String[] args) {


int age = 20;

if (age >= 18 && age <= 40) {


boolean isSchoolStudent = true;
boolean isLibraryMember = false;

if (isSchoolStudent || isLibraryMember) {
System.out.println("you can use the program");
}
} else {
System.out.println("you can not use the program");
}

}
In this case, only if the first if statement evaluates to
true will the inner if statement be tested.
What is a switch-case statement?
Apart from the if-else blocks, there are also switch
cases where you can define multiple cases based on a
single switch.

import java.util.Scanner;

public class Main {

public static void main(String[] args) {


Scanner scanner = new Scanner(System.in);

System.out.print("What is the first operand? ");


int a =scanner.nextInt();

// consumes the dangling newline character


scanner.nextLine();

System.out.print("What is the second operand? ");


int b = scanner.nextInt();

// consumes the dangling newline character


scanner.nextLine();

System.out.print("What operation would you like to perform? ");


String operation = scanner.nextLine();

switch (operation) {
case "sum":
System.out.printf("%d + %d = %d", a, b, a+b);
break;
case "sub":
System.out.printf("%d - %d = %d", a, b, a-b);
break;
case "mul":
System.out.printf("%d * %d = %d", a, b, a*b);
break;
case "div":
if (b == 0) {
System.out.print("Can't divide by zero!");
} else {
System.out.printf("%d / %d = %d", a, b, a / b);
}
break;
default:
System.out.printf("Invalid Operation!");
}

scanner.close();

}
This is a very simple calculator program. The program
prompts the user for two numbers and then asks what
operation they would like to perform.

Every switch-case statement will have one switch and


multiple cases. When you say case "sum", the program
checks whether the value of the switch or
the operation variable in this is sum or not.
If it matches, the case body will execute. If none of the
cases match, the default case will be executed.
And about that break statement. It does what it sounds
like: stops the program from going into the next case.
If you remove the break statements, all the cases will be
executed one after the other until the default case has
been reached.
What is Variable Scope in Java?
Scope is the lifetime and accessibility of a variable.
Depending on where you declare a variable you may or
may not be able to access it from other places.

Take the following code snippet as an example:

public class Main {

public static void main(String[] args) {


int age = 20;

if (age >= 18 && age <= 40) {


// age variable is accessible here
// booleans are not accessible here

boolean isSchoolStudent = true;


boolean isLibraryMember = false;

if (isSchoolStudent || isLibraryMember) {
// booleans are accessible here
// age variable is accessible here

System.out.println("you can use the program");


}
} else {
// age variable is accessible here
// booleans are not accessible here

System.out.println("you can not use the program");


}

}
Here, the age variable is declared within the class code
block. That means you can access this variable within
the entire class without any issue. Since the variable is
accessible in the entire class instance, it's an instance
variable.
However,
the isSchoolStudent and isLibraryMember variables have
been declared within the first if statement code block.
So it'll not be accessible outside of that code block.
But, it'll be accessible within any nested code block
inside the first if block. These are called local variables.
There are also class variables declared using
the static keyword but you'll learn about them in the
object-oriented programming sections.
So for now, the rule of thumb is, a variable will be
accessible within the code block it was declared at and
any other code block nested inside the parent block.

What Are Default Values of Variables in Java?


You've learned that in Java, you need to initialize a
variable after declaring it. Otherwise, you'll not be able
to use that. Well that's not true in all cases.

If you declare a variable in the class level, that variable


will be assigned a default value by the compiler.

public class Main {

// gets 0 as the value by default


static int age;

public static void main(String[] args) {


System.out.println(age); // 0
}
}
Since the main method is static, it can only
access static variables in the class level. I'll
discuss static in greater detail in the object-oriented
programming section.
But if you move the variable declaration inside
the main method, it becomes local to that method and
doesn't get any default value.
public class Main {
public static void main(String[] args) {
// remains uninitialized
int age;

System.out.println(age); // fails to compile


}
}
This code will throw the The local variable age may not
have been initialized error on compilation.
Variables get their default values based on their type. In
most cases, it'll be 0 or null. I'm giving a list of all the
primitive types and their default values:
Data Default
Type Value
byte 0

short 0

int 0

long 0L

float 0.0f

double 0.0d

char '\u0000'

boolean false

Any reference type will be assigned the value null by


default. We'll discuss reference types, classes, and
objects in the object-oriented programming sections.
How to Work with Arrays in Java
You've already learned about declaring single variables
and using them in your program. This is where arrays
come in.

Arrays are data structures containing multiple value sof


the same data type in sequential memory locations.
Arrays can be of any primitive or non-primitive data
type.

You can create an array in Java as follows:

public class Main {

public static void main(String[] args) {


// <type> <name>[] = new <type>[<length>]
char vowels[] = new char[5];

}
}
You start by typing out the type of data you want to
hold in the array, char in this case. Then you write out
the name of the array, vowels followed by a pair of
square braces here. This pair of braces tells Java that
you're declaring an array of characters and not a
regular character variable.
Then you put an equal sign followed by the new operator
used for creating new objects in Java. Since arrays are
reference types in Java, new is required to create new
instances.
You finish off the declaration by writing out the type
again, followed by another pair of square braces
enclosing the length of the array. Here, 5 means the
array will hold five elements and not more than that.
When working with a single variable, you can refer to
the variable simply by its name. But in case of an array,
each element will have an index and arrays are zero-
based. This means the first element in an array will
have 0 as its index and not 1.
To access an element in an array, you start by writing
out the name of the array – in this case vowels followed
by a pair of square braces enclosing your desired index.
So if you want to access the first element in the array,
you can do so as follows:
public class Main {

public static void main(String[] args) {


char vowels[] = new char[5];

vowels[0] = 'a';
}
}
At this point, vowels[0] is similar to a regular character
variable. You can print it out, assign new value to it,
perform calculations in case of number types, and so
on.
Since the array is empty at this moment, I'm assigning
the character a to the first index. You can assign the
rest of the vowels to the rest of the indices as follows:
public class Main {

public static void main(String[] args) {


char vowels[] = new char[5];

vowels[0] = 'a';
vowels[1] = 'e';
vowels[2] = 'i';
vowels[3] = 'o';
vowels[4] = 'u';

}
}
Since the indices start at 0 they'll end at the length of
the array - 1, which in this case is 4. If you try to assign
another element to the array like vowels[4] = 'x'; the
compiler will throw the following error:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5
out of bounds for length 5
at arrays.Main.main(Main.java:18)
Arrays can not be printed like regular variables. You'll
have to use a loop or you'll have to convert the array
into a string. Since, I haven't discussed loops yet, I'll use
the second method.

import java.util.Arrays;

public class Main {

public static void main(String[] args) {


char vowels[] = new char[5];

vowels[0] = 'a';
vowels[1] = 'e';
vowels[2] = 'i';
vowels[3] = 'o';
vowels[4] = 'u';

System.out.println("These are the vowels: " + Arrays.toString(vowels));

}
}
You'll need to first import the java.util.Arrays; and use
the Arrays.toString() method to convert the array to a
string. This class has a bunch of other interesting
methods, but before discussing them, I'd like to show
you how you can declare and initialize an array in one
go.
import java.util.Arrays;

public class Main {

public static void main(String[] args) {


char vowels[] = {'a', 'e', 'i', 'o', 'u'};

System.out.println("These are the vowels: " + Arrays.toString(vowels));

}
}
The left side of the declaration syntax remains
unchanged. However, after the assignment operator,
instead of using new you write out the individual array
elements separated by comma and enclosed within a
pair of curly braces.
In this case the compiler will count the number of
elements in the array and use that as the length of the
array.

If you do not know the length of an array, you can take


a look at the length property.
In this case, vowels.length will be 5 since there are five
elements in the array. The length property is an integer
and is present in every array in Java.
Arrays can also be multidimensional. So far you've
worked with arrays that look like this:
Single dimensional arrays like this are perfect when you
want to store a series of values. But imagine someone's
daily medicine routine in the form of a table:

The first row represents the seven days in a week and


the columns represent how many times the patient
should take their medicine out of three times a
day. 0 means no and 1 means yes.
You can map this routine using a multidimensional
array in your program:

import java.util.Arrays;

public class Main {

public static void main(String[] args) {


int medicineRoutine[][] = {
{1, 2, 3, 4, 5, 6, 7},
{0, 1, 1, 0, 1, 1, 0},
{1, 0, 1, 0, 1, 0, 0},
{0, 0, 1, 1, 0, 1, 0},
};

System.out.println(Arrays.deepToString(medicineRoutine)); // [[1, 2, 3, 4, 5, 6,
7], [0, 1, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 0], [0, 0, 1, 1, 0, 1, 0]]

}
}
Multidimensional arrays can not be printed out using
the regular Arrays.toString() method, you have to dig
deeper.
Although the output doesn't look anything like the
table, you can make it look like a table using some
clever programming:

import java.util.Arrays;

public class Main {

public static void main(String[] args) {


int medicineRoutine[][] = {
{1, 2, 3, 4, 5, 6, 7},
{0, 1, 1, 0, 1, 1, 0},
{1, 0, 1, 0, 1, 0, 0},
{0, 0, 1, 1, 0, 1, 0},
};

System.out.println(Arrays.deepToString(medicineRoutine).replace("], ", "]\


n"));
}
}

// [[1, 2, 3, 4, 5, 6, 7]
// [0, 1, 1, 0, 1, 1, 0]
// [1, 0, 1, 0, 1, 0, 0]
// [0, 0, 1, 1, 0, 1, 0]]
You've already learned about the replace() method for
strings. You're just replacing the ending square brace in
each line with a square brace and a newline character.
The first row represents the 7 weekdays and the rest of
the rows are medicine routines for each day. Each line
in this table represents an array.

To access a single value from a multidimensional array,


you'll need two indices. The first index determines the
row and the second one determines the column.

So medicineRoutine[2][3] will select the element in


index 3 of the third array. That element will be 0.
Working with multidimensional array can seem a bit
tricky but practicing will make it much easier.
Since you can create arrays of any type in Java, why
don't you try creating some other types of arrays by
yourself, huh?

How to Sort an Array


One of the most common tasks that you will perform on
arrays is sorting them. The java.utils.Arrays comes with
the Arrays.sort() method to do just that:
import java.util.Arrays;

public class Main {

public static void main(String[] args) {


char vowels[] = {'e', 'u', 'o', 'i', 'a'};

Arrays.sort(vowels);
System.out.println("The sorted array: " + Arrays.toString(vowels)); // [a, e, i,
o , u]

}
}
The Arrays.sort() method takes the unsorted array as its
argument and sorts it in place. So instead of getting a
new sorted array in return, your original array itself will
be sorted in ascending order.
By default, the method treats the first index of the array
as its starting index and the length of the array as its
ending index.

You can specify these two indices manually. For


example, if you want to sort only u, o, i in ascending
order and leave e, a as is, you can do so as follows:
import java.util.Arrays;

public class Main {

public static void main(String[] args) {


char vowels[] = {'e', 'u', 'o', 'i', 'a'};

int startIndex = 1;
int endIndex = 4;

Arrays.sort(vowels, startIndex, endIndex);

System.out.println("The sorted array: " + Arrays.toString(vowels)); // [e, i, o,


u , a]

}
}
This time, the method takes the array as the first
parameter, the starting index as the second parameter,
and the ending index as the third parameter. The rest
of the behaviors stay the same as before.

How to Perform Binary Search on an Array


Searching for values within a sorted value is another
common task. The Arrays.binarySearch() method lets you
search for items in a sorted array using the binary
search algorithm.
public class Main {

public static void main(String[] args) {


char vowels[] = {'a', 'e', 'i', 'o', 'u'};

char key = 'i';

int foundItemIndex = Arrays.binarySearch(vowels, key);

System.out.println("The vowel 'i' is at index: " + foundItemIndex); // 2

}
}
The Arrays.binarySearch() method takes an array as its
first parameter and the the search key (aka the item
you're looking for) as its second parameter. It'll return
the index of the found item as an integer.
You can store that index in an int and use that to
access the element from the array
as vowels[foundItemIndex].
Note that the array has to be sorted in ascending order.
If you're unsure of the array's ordering, use
the Arrays.sort() method to sort it first.
By default, the method treats the first index of the array
as its starting index and the length of the array as its
ending index. But you can also specify those indices
manually.

For example, if you want the search to take place from


index 2 to index 4, you can do so as follows:
import java.util.Arrays;

public class Main {

public static void main(String[] args) {


char vowels[] = {'a', 'e', 'i', 'o', 'u'};

char key = 'i';


int startIndex = 2;
int endIndex = 4;

int foundItemIndex = Arrays.binarySearch(vowels, startIndex, endIndex,


key);

System.out.println("The vowel 'i' is at index: " + foundItemIndex); // 2

}
}
This time the method takes the array you want to
search on as the first parameter, the starting index as
the second parameter, the ending index as the third
parameter, and the search key as the fourth parameter.

Now the search will take place within i, o, and u. So if


you look for a, it'll not be found. In cases where the
given item is not found, you'll get a negative index. The
resultant negative index will vary based on a number of
factors but I won't get into those here. If you're
interested in learning more, check out my article on the
topic.
How to Fill an Array
You've already learned about initializing an array with
values but you may sometimes want to fill an entire
array with the same value. The Arrays.fill() method
can do that for you:
import java.util.Arrays;

public class Main {

public static void main(String[] args) {


char vowels[] = {'e', 'u', 'o', 'i', 'a'};

Arrays.fill(vowels, 'x');

System.out.println("The filled array: " + Arrays.toString(vowels)); // [x, x, x, x,


x]

}
}
Like the Arrays.sort() method, Arrays.fill() also
performs its operation in place. It takes your array as
the first parameter, the value you want to fill the array
with as the second parameter, and updates the original
array in place.
This method also treats the first index as the starting
index and the length of the array as the ending index.
You can specify these indices manually as follows:

import java.util.Arrays;

public class Main {

public static void main(String[] args) {


char vowels[] = {'e', 'u', 'o', 'i', 'a'};
int startIndex = 1;
int endIndex = 4;

Arrays.fill(vowels, startIndex, endIndex, 'x');

System.out.println("The filled array: " + Arrays.toString(vowels)); // [e, x, x, x,


a]

}
}
This time the method takes your array as the first
argument, the starting index as the second argument,
the ending index as the third argument, and the filler as
the fourth argument.

How to Make Copies of an Array


Since arrays in Java are of reference types, copying
them using the assignment operator can cause some
unexpected behavior.

import java.util.Arrays;

public class Main {

public static void main(String[] args) {


int oddNumbers[] = {1, 3, 5};
int copyOfOddNumbers[] = oddNumbers;

Arrays.fill(oddNumbers, 0);

System.out.println("The copied array: " + Arrays.toString(copyOfOddNumbers));


// [0, 0, 0]

}
}
Although you've made changes to the source array, the
copy reflects them as well. This happens because when
you use the assignment operator to copy an array, the
copy references the original array in the memory.

To properly copy an array, you can use


the Arrays.copyOf() method as follows:
import java.util.Arrays;

public class Main {

public static void main(String[] args) {


int oddNumbers[] = {1, 3, 5};
int copyOfOddNumbers[] = Arrays.copyOf(oddNumbers, oddNumbers.length);

Arrays.fill(oddNumbers, 0);

System.out.println("The copied array: " + Arrays.toString(copyOfOddNumbers));


// [1, 3, 5]

}
}
The method takes the source array as its first argument
and the desired length of the new array as the second
argument. If you want the length to be the same,
simply pass the length of the original array using
the length property.
If you put a smaller length, any value after that will be
cut off and if you put a larger length, the new indices
will be filled with the default value of the array data
type.

There is another method Arrays.copyOfRange() that can


copy a portion of an array to a new one:
import java.util.Arrays;

public class Main {

public static void main(String[] args) {


int oddNumbers[] = {1, 3, 5, 7, 9, 11, 13, 15};

int startIndex = 2;
int endIndex = 7;

int copyOfOddNumbers[] = Arrays.copyOfRange(oddNumbers, startIndex,


endIndex);

System.out.println("The copied array: " + Arrays.toString(copyOfOddNumbers));


// [5, 7, 9, 11, 13]

}
}
This method takes the source array as its first
argument, then the start index and finally the end
index.

Keep in mind, the ending index is not inclusive. That's


why 15 is absent from the new array. But if you want to
include the last index of the array, use the length of the
original array as the ending index.

import java.util.Arrays;

public class Main {

public static void main(String[] args) {


int oddNumbers[] = {1, 3, 5, 7, 9, 11, 13, 15};

int startIndex = 2;
int endIndex = oddNumbers.length;

int copyOfOddNumbers[] = Arrays.copyOfRange(oddNumbers, startIndex,


endIndex);

System.out.println("The copied array: " + Arrays.toString(copyOfOddNumbers));


// [5, 7, 9, 11, 13, 15]

}
}
Now the new array will also include 15 in it. You can
also put a higher number than the length of the source
array. In that case the newly added indices will contain
the default value of the array data type.

How to Compare Two Arrays


If you try two check if two arrays are the same or not in
Java using the equal relational operator, you'll get some
unexpected results.

public class Main {

public static void main(String[] args) {


int oddNumbers1[] = {1, 3, 5, 7, 9, 11, 13, 15};
int oddNumbers2[] = {1, 3, 5, 7, 9, 11, 13, 15};

System.out.println(oddNumbers1 == oddNumbers2); // false


}
}
Even though the two arrays are identical, the output of
the program is false. Since arrays are reference types,
the relational operator will check whether they are the
same instance or not.
To compare two arrays in Java, you can use
the Arrays.equals() method:
import java.util.Arrays;

public class Main {

public static void main(String[] args) {


int oddNumbers1[] = {1, 3, 5, 7, 9, 11, 13, 15};
int oddNumbers2[] = {1, 3, 5, 7, 9, 11, 13, 15};

System.out.println(Arrays.equals(oddNumbers1, oddNumbers2)); // true


}
}
However, if you change even a single element in either
of these arrays, the output will be false since the arrays
will not remain identical anymore.
You can also compare multidimensional arrays, but for
that you'll have to use the Arrays.deepEquals() method
instead of the regular one.
import java.util.Arrays;

public class Main {

public static void main(String[] args) {


int medicineRoutine[][] = {
{1, 2, 3, 4, 5, 6, 7},
{0, 1, 1, 0, 1, 1, 0},
{1, 0, 1, 0, 1, 0, 0},
{0, 0, 1, 1, 0, 1, 0},
};

int medicineRoutine2[][] = {
{1, 2, 3, 4, 5, 6, 7},
{0, 1, 1, 0, 1, 1, 0},
{1, 0, 1, 0, 1, 0, 0},
{0, 0, 1, 1, 0, 1, 0},
};
System.out.println(Arrays.deepEquals(medicineRoutine, medicineRoutine2));
// true
}
}
This method calls itself each time it encounters a new
array inside the parent array.

These were some of the most common methods inside


the java.util.Arrays class. You can consult the official
documentation if you'd like to learn more.
How to Use Loops in Java
If you ever need to repeat a task for a set number of
times, you can use a loop. Loops can be of three types:
they are for loops, for...each loops, and while loops.
For Loop
For loops are probably the most common types of loops
that you'll see on the internet.

Every for loop consists of three parts. The initialization,


condition, and update expression. The looping happens
in multiple steps.
If you want to print the numbers from 0 to 10 using a
for loop, you can do so as follows:

public class Main {

public static void main(String[] args) {


for (int number = 0; number <= 10; number++) {
System.out.println(number);
}
}
}

// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
// 10
The flowchart for this loop looks like this:
1. In the beginning of the loop, you initialize a new integer
named number with the initial value of 0.
2. Then you check whether the number is less than or equal
to 10 or not.
3. If its less than or equal to 10, you execute the statement
inside the loop block and print out the number on the
terminal.
4. Then you update the number variable by incrementing
its value by 1.
5. The loop goes back to checking whether the value of
number is still less than or equal or not.
As long as the value of number remains less than or
equal to 10, the loop goes on. The moment the value of
the number variable becomes 11, the loop ends.
You can use a for loop to loop over an array as follows:

public class Main {

public static void main(String[] args) {


int fibonacciNumbers[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55};

for(int index = 0; index < fibonacciNumbers.length; index++) {


System.out.println(fibonacciNumbers[index]);
}
}
}
The flowchart for this loop will be as follows:
Since the last index of the array is one less than its
length, you run the loop as long as the index is less
than the array length. The moment the index becomes
equal to the length of the array, you exit the loop.

One of the fun things that you can do with loops is


printing out multiplication tables. For example, the
multiplication table for 5 will be as follows:

public class Main {


public static void main(String[] args) {
int number = 5;

for (int multiplier = 1; multiplier <= 10; multiplier++) {


System.out.println(String.format("%d x %d = %d", number, multiplier,
number * multiplier));
}
}
}

// 5 x 1 = 5
// 5 x 2 = 10
// 5 x 3 = 15
// 5 x 4 = 20
// 5 x 5 = 25
// 5 x 6 = 30
// 5 x 7 = 35
// 5 x 8 = 40
// 5 x 9 = 45
// 5 x 10 = 50
Loops can also be nested. Which means you can put
one loop inside another. You can print out the
multiplication table of all the numbers from 1 to 10
using nested loops:

public class Main {

public static void main(String[] args) {


for (int number = 1; number <= 10; number++) {
System.out.println(String.format("\nmultiplication table of %d", number));
for (int multiplier = 1; multiplier <= 10; multiplier++) {
System.out.println(String.format("%d x %d = %d", number,
multiplier, number * multiplier));
}
}
}
}
I wouldn't dare printing out the output here. Instead, try
out the code by yourself. Draw out each iteration of the
loop on a piece of paper so that you understand what's
happening on each step.

For-Each Loop
If you want to iterate over a collection like an array and
perform some operation on each element of that
collection, you can use a for...each loop.

public class Main {

public static void main(String[] args) {


int fibonacciNumbers[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55};

for(int number : fibonacciNumbers) {


System.out.println(number);
}
}
}
In the case of a for-each loop, the type of the item
needs to match the type of the collection you're
working with. Here, the array is of type integer so the
item in the loop is of type integer.

This does the same task as the previously shown for


loop. But in this one, you don't have to keep track of the
index or use the square braces to access the elements.
It looks cleaner and is less error prone.

While Loop
If you want to execute a bunch of code until a certain
condition is met, you can use a while loop.

There are no initialization or update steps in a while


loop. Whatever happens, happens within the loop body.
If you rewrite the program for printing out the
multiplication table of 5 using a while loop, it'll be as
follows:

public class Main {

public static void main(String[] args) {


int number = 5;
int multiplier = 1;

while (multiplier <= 10) {


System.out.println(String.format("%d x %d = %d", number,
multiplier, number*multiplier));
multiplier++;
}
}
}

// 5 x 1 = 5
// 5 x 2 = 10
// 5 x 3 = 15
// 5 x 4 = 20
// 5 x 5 = 25
// 5 x 6 = 30
// 5 x 7 = 35
// 5 x 8 = 40
// 5 x 9 = 45
// 5 x 10 = 50
Although, while loops are not as common as for loops in
the real world, learning about them is worth it.

Do-While Loop
The final type of loop you'll learn about is the do-while
loop. It kind of reverses the order of the regular while
loop – so instead of checking the condition before
executing the loop body, you execute the loop body
first and then check the condition.
The multiplication table code implemented using a do-
while loop will be as follows:

public class Main {

public static void main(String[] args) {


int number = 5;
int multiplier = 1;

do {
System.out.println(String.format("%d x %d = %d", number, multiplier,
number*multiplier));

multiplier++;
} while (multiplier <= 10);
}
}

// 5 x 1 = 5
// 5 x 2 = 10
// 5 x 3 = 15
// 5 x 4 = 20
// 5 x 5 = 25
// 5 x 6 = 30
// 5 x 7 = 35
// 5 x 8 = 40
// 5 x 9 = 45
// 5 x 10 = 50
Do-while loops are very useful when you need to
perform some operation until the user gives a specific
input. Such as, show a menu until user presses the "x"
key.

How to Work with Array Lists in Java


Arrays in Java are not resizable. Once you've set a
length for an array, you can not change it any way.
The ArrayList class in Java mitigates this limitation.
import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(1);
oddNumbers.add(3);
oddNumbers.add(5);
oddNumbers.add(7);
oddNumbers.add(9);

System.out.println(oddNumbers.toString()); // [1, 3, 5, 7, 9]
}
}
To create array lists, you'll need to import
the java.util.ArrayList class at the top of your source
file.
Then you start by writing ArrayList and then inside a
pair of less than-greater than signs, you'll write the data
type for the elements. Then you'll add the name of the
array list itself followed by the assignment operator
and new ArrayList<>().
You can not create array lists of primitive types, so
you'll have to use the corresponding wrapper class.

Although these elements have zero-based indices like


arrays, you can not use the square brace notation to
access them. Instead, you'll have to use
the get() method:
import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(1);
oddNumbers.add(3);
oddNumbers.add(5);
oddNumbers.add(7);
oddNumbers.add(9);

System.out.println(oddNumbers.get(2)); // 5
}
}
The get() method will get the value in the given index.
Just like get() you can use the set() method to update
the value of an element.
import java.time.LocalDate;
import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(1);
oddNumbers.add(3);
oddNumbers.add(5);
oddNumbers.add(7);
oddNumbers.add(9);

oddNumbers.set(2, 55);

System.out.println(oddNumbers.get(2)); // 55
}
}
The first parameter to the set() method is the index and
the second one is the updated value.
There is no length property like in an array but you can
use the size() method on any array list to find out its
length.
import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(1);
oddNumbers.add(3);
oddNumbers.add(5);
oddNumbers.add(7);
oddNumbers.add(9);

System.out.println(oddNumbers.size()); // 5
}
}
You can remove elements from an array list using the
remove method:

import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(1);
oddNumbers.add(3);
oddNumbers.add(5);
oddNumbers.add(7);
oddNumbers.add(9);

oddNumbers.remove(Integer.valueOf(7));
oddNumbers.remove(Integer.valueOf(9));

System.out.println(oddNumbers.toString()); // [1, 3, 5]
}
}
The remove() method can remove an element by value or
by index. If you pass a primitive integer value to the
method, it'll remove the element in the given index.
But if you pass an object like in this code, the method
will find and delete that given element.
The valueOf() method is present in all wrapper classes
and it can convert a primitive value to a reference type.
How to Add or Remove Multiple Elements
You've already seen examples of
the add() and remove() methods. There are two other
methods addAll() and removeAll() for working with
multiple elements.
import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(1);
oddNumbers.add(3);
oddNumbers.add(5);

ArrayList<Integer> moreOddNumbers = new ArrayList<>();

moreOddNumbers.add(7);
moreOddNumbers.add(9);
moreOddNumbers.add(11);

oddNumbers.addAll(moreOddNumbers); // [1, 3, 5, 7, 9, 11]

System.out.println(oddNumbers.toString());

oddNumbers.removeAll(moreOddNumbers);

System.out.println(oddNumbers.toString()); // [1, 3, 5]
}
}
Both methods accept collections as their parameter. In
the code above, you're creating two separate array lists
and joining them using the addAll() method.
Then you remove the elements from the second array
list using the removeAll() method and the array list goes
back to its original state.
You can also drop all elements from an array list using
the clear() method:
import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(1);
oddNumbers.add(3);
oddNumbers.add(5);

oddNumbers.clear();

System.out.println(oddNumbers.toString()); // []
}
}
The method doesn't require any parameter at all and
also doesn't return any value. It just empties your array
list in a single call.

How to Remove Elements Based on a Condition


The removeIf() method can remove elements from an
array list if they meet a certain condition:
import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> numbers = new ArrayList<>();

for (int i = 0; i <= 10; i++) {


numbers.add(i);
}

System.out.println(numbers.toString()); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

numbers.removeIf(number -> number % 2 == 1);


System.out.println(numbers.toString()); // [0, 2, 4, 6, 8, 10]
}
}
The method takes a lambda expression as a parameter.
Lambda expressions are like unnamed methods. They
can receive parameters and work with them.

Here, the removeIf() method will loop over the array list
and pass each element to the lambda expression as the
value of the number variable.
Then the lambda expression will check whether the
given number is divisible by 2 or not and
return true or false based on that.
If the lambda expression returns true,
the removeIf() method will keep the value. Otherwise the
value will be deleted.
How to Clone and Compare Array Lists
To make a duplicate of an array list, you can use
the clone() method.
import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> numbers = new ArrayList<>();

for (int i = 0; i <= 10; i++) {


numbers.add(i);
}

ArrayList<Integer> numbersCloned = (ArrayList<Integer>)numbers.clone();

System.out.println(numbersCloned.equals(numbers)); // true
}
}
The clone() method returns an object, so you'll have to
cast it to a proper array list manually. You can compare
two array lists using the equals() method just like in
arrays.
How to Check if an Element Is Present or the Array List Is
Empty
You can use the contains() method to check if an array
list contains a given element or not:
import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(1);
oddNumbers.add(3);
oddNumbers.add(5);
oddNumbers.add(7);
oddNumbers.add(9);

System.out.println(oddNumbers.isEmpty()); // false
System.out.println(oddNumbers.contains(5)); // true
}
}
If you want to check if an array list is empty or not, just
call the isEmpty() method on it and you'll get a boolean
in return.
How to Sort an Array List
You can sort an array list in different orders using
the sort() method:
import java.util.ArrayList;
import java.util.Comparator;
public class Main {
public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(5);
oddNumbers.add(7);
oddNumbers.add(1);
oddNumbers.add(9);
oddNumbers.add(3);

System.out.println(oddNumbers.toString()); [5, 7, 1, 9, 3]

oddNumbers.sort(Comparator.naturalOrder());

System.out.println(oddNumbers.toString()); [1, 3, 5, 7, 9]
}
}
The sort() method takes a comparator as its parameter.
A comparator imposes the order of sorting on the array
list.
You can sort the array list in reverse order just by
changing the passed comparator:

import java.util.ArrayList;
import java.util.Comparator;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(5);
oddNumbers.add(7);
oddNumbers.add(1);
oddNumbers.add(9);
oddNumbers.add(3);
System.out.println(oddNumbers.toString()); // [5, 7, 1, 9, 3]

oddNumbers.sort(Comparator.reverseOrder());

System.out.println(oddNumbers.toString()); // [9, 7, 5, 3, 1]
}
}
Comparators have other usages as well but those are
out of the scope of this book.

How to Keep Common Elements From Two Array Lists


Think of a scenario where you have two array lists. Now
you'll have to find out which elements are present in
both array lists and remove the rest from the first array
list.

import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(1);
oddNumbers.add(3);
oddNumbers.add(5);

ArrayList<Integer> moreOddNumbers = new ArrayList<Integer>();

moreOddNumbers.add(5);
moreOddNumbers.add(7);
moreOddNumbers.add(9);

oddNumbers.retainAll(moreOddNumbers);
System.out.println(oddNumbers.toString()); // [5]
}
}
The retainAll() method can get rid of the uncommon
elements from the first array list for you. You'll need to
call the method on the array list you want to operate on
and pass the second array list as a parameter.
How to Perform an Action on All Elements of an Array List
You've already learned about looping in previous
sections. Well, array lists have a forEach() method of
their own that takes a lambda expression as parameter
and can perform an action on all the elements of the
array list.
import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
ArrayList<Integer> oddNumbers = new ArrayList<>();

oddNumbers.add(1);
oddNumbers.add(3);
oddNumbers.add(5);
oddNumbers.add(7);
oddNumbers.add(9);

oddNumbers.forEach(number -> {
number = number * 2;
System.out.printf("%d ", number); // 2 6 10 14 18
});

System.out.println(oddNumbers.toString()); // [1, 3, 5, 7, 9]
}
}
Last time, the lambda expression you saw was a single
line – but they can be bigger. Here,
the forEach() method will loop over the array list and
pass each element to the lambda expression as the
value of the number variable.
The lambda expression will then multiply the supplied
value by 2 and print it out on the terminal. However,
the original array list will be unchanged.

How to Work With Hash Maps in Java


Hash maps in Java can store elements in key-value
pairs. This collection type is comparable to dictionaries
in Python and objects in JavaScript.

import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

System.out.printf(prices.toString()); // {orange=1.8, banana=1.0, apple=2.0,


berry=2.5, guava=1.5}
}
}
To create hash maps, you'll first have to import
the java.util.HashMap class at the top of your source file.
Then you start by writing HashMap and then inside a pair
of less than-greater than signs, you'll write the data
type for the key and the value.
Here, the keys will be strings and values will be
doubles. After that the assignment operator, followed
by new HashMap<>().
You can use the put() method to put a record in the
hash map. The method takes the key as the first
parameter and its corresponding value as the second
parameter.
There is also the putIfAbsent() method that adds the
given element only if it already doesn't exist in the hash
map.
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

prices.putIfAbsent("guava", 2.9);

System.out.println(prices.toString()); // {orange=1.8, banana=1.0,


apple=2.0, berry=2.5, guava=1.5}
}
}
You can use the get() method to bring out a value from
the hash map. The method takes the key as its
parameter.
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

System.out.println(prices.get("banana")); // 1.000000
}
}
There is another variation of the this method.
The getOrDefault() method works like get() but if the
given key is not found, it'll return a specified default
value.
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

System.out.println(prices.getOrDefault("jackfruit", 0.0)); // 0.0


}
}
The default value has to match the type of the values in
the hash map. You can update a value in a hash map
using the replace() method:
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

prices.replace("berry", 2.8);

System.out.printf(prices.toString()); // {orange=1.8, banana=1.0, apple=2.0,


berry=2.8, guava=1.5}
}
}
For removing elements from a hash map, you can use
the aptly named remove() method:
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);
prices.remove("guava");

System.out.printf(prices.toString()); // {orange=1.8, banana=1.0, apple=2.0,


berry=2.5}
}
}
If you ever need to know how many entries are there in
a hash map, you can do so by using the size() method:
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

System.out.println(prices.size()); // 5
}
}
Finally if you want to clear a hash map in Java, you can
do so by using the clear() method.
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

prices.clear();

System.out.println(prices.toString()); // {}
}
}
Just like in the array lists, the method doesn't take any
argument or return any value.

How to Put in or Replace Multiple Elements in a Hash Map


If you want to put multiple elements into a hash map in
a single go, you can do so by using the putAll() method:
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

HashMap<String, Double> morePrices = new HashMap<>();

prices.put("jackfruit", 2.9);
prices.put("pineapple", 1.1);
prices.put("tomato", 0.8);

prices.putAll(morePrices);

System.out.println(prices.toString()); // {orange=1.8, banana=1.0,


apple=2.0, berry=2.5, pineapple=1.1, tomato=0.8, guava=1.5, jackfruit=2.9}
}
}
The method takes another hash map as its parameter
and adds its elements to the one the method has been
called upon.

You can also use the replaceAll() method to update


multiple values in a hash map.
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

prices.replaceAll((fruit, price) -> price * 2);

System.out.println(prices.toString()); // {orange=3.6, banana=2.0,


apple=4.0, berry=5.0, guava=3.0}
}
}
The replace all method iterates over the hashmap and
passes each key value pair to the lambda expression.

The first parameter to the lambda expression is the key


and the second one is the value. Inside the lambda
expression, you perform your actions.

How to Check if a Hash Map Contains an Item or if It’s Empty


You can use the
methods containsKey() and containsValue() for checking if
a hash map contains a value or not.
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

System.out.println(prices.containsKey("banana")); // true
System.out.println(prices.containsValue(2.5)); // true
}
}
Difference between the two methods is that
the containsKey() method checks if the given key exists
or not and the containsValue() method checks if the
given value exists or not.
And if you want to check if a hash map is empty or not,
you can do so by using the isEmpty() method:
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

System.out.println(prices.isEmpty()); // false
}
}
Since the method returns a boolean value, you can use
it within if-else statements.

How to Perform an Action on All Elements of a Hash Map


Like the array lists, hash maps also have their
own forEach() method that you can use to loop over the
hash map and repeat a certain action over each entry.
import java.util.HashMap;

public class Main {


public static void main (String[] args) {
HashMap<String, Double> prices = new HashMap<>();

prices.put("apple", 2.0);
prices.put("orange", 1.8);
prices.put("guava", 1.5);
prices.put("berry", 2.5);
prices.put("banana", 1.0);

System.out.println("prices after discounts");

prices.forEach((fruit, price) -> {


System.out.println(fruit + " - " + (price - 0.5));
});
}
}

// prices after discounts


// orange - 1.3
// banana - 0.5
// apple - 1.5
// berry - 2.0
// guava - 1.0
The method loops over each entry and passes the key
and value to the lambda expression. Inside the lambda
expression body, you can do whatever you want.

Classes and Objects in Java


Here's a helpful definition of object-oriented
programming:
OOP (Object-oriented programming) is a programming
paradigm based on the concept of "objects", which can
contain data and code: data in the form of fields (often
known as attributes or properties), and code, in the
form of procedures (often known as methods).
Imagine a library management system where members
of the library can log in, take a look at the books they
have already borrowed, request new ones, and so on.

In this system the users and the books can all be


objects. These objects will have their own properties
such as name and birthday (in case of a user) and title
and author in case of the books.

Classes in object oriented programming are blueprints


for the aforementioned objects. We've already
discussed the possible properties of the user and book
objects.

To create a new User class, right click on the src folder


once again. Then go to New > Java Class, name
it User, and hit enter.
Keeping the previously discussed properties in mind,
your code for the User class should be as follows:
import java.time.LocalDate;

public class User {


String name;
LocalDate birthDay;
}
The LocalDate is a reference data type that represents a
date. Now go back to the Main.java file and create a new
instance of this class:
import java.time.LocalDate;

public class Main {


public static void main (String[] args) {
User user = new User();

user.name = "Farhan";
user.birthDay = LocalDate.parse("1996-07-15");

System.out.printf("%s was born on %s.", user.name, user.birthDay.toString());


// Farhan was born on 15th July 1996.
}
}
Creating a new user is not very different from creating a
new string or array. You start by writing out the name of
the class then the instance or object name.

Then you put the assignment operator followed by


the new keyword and the constructor call. The
constructor is a special method that initializes the
object.
The constructor has initialized the object properties with
default values which is null for all these reference
types.
You can access the properties of the object by writing
out the name of the object followed by a dot and then
the name of the property.

The LocalDate.parse() method can parse a date from a


given string. Since the birthDay is a reference type,
you'll have to use the toString() method to print out on
the console.
What is a Method?
The variables or properties of a class describe the state
of its objects. Methods on the other hand describes the
behavior.

For example, you can have a method within


your User class that calculates the user's age.
import java.time.Period;
import java.time.LocalDate;

public class User {


String name;
LocalDate birthDay;

int age() {
return Period.between(this.birthDay, LocalDate.now()).getYears();
}
}
Here, the this keyword represents the current instance
of the class. You start by writing out the return type of
the method. Since the age of a user is an integer, the
return type of this method will be int.
After the return type, you write the name of the
method, followed by a pair of parenthesis.
Then you write the method body within a pair of curly
braces. The Period class in Java expresses a time frame
in the ISO-8601 calendar system.
The LocalDate.now() method returns the current date.
So the Period.between(this.birthDay,
LocalDate.now()).getYears() method call will return the
difference between the current date and the date of
birth in years.
Now back in the Main.java file, you can call this method
as follows:
import java.time.LocalDate;

public class Main {


public static void main (String[] args) {
User user = new User();

user.name = "Farhan";
user.birthDay = LocalDate.parse( "1996-07-15");

System.out.printf("%s is %s years old.", user.name, user.age()); // Farhan a 26


years old.
}
}
Methods can also accept parameters. For example, if
you want to create a method borrow() for inserting new
books into the list of borrowed books for this user, you
can do so as follows:
import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {


String name;
LocalDate birthDay;
ArrayList<String> borrowedBooks = new ArrayList<String>();

int age() {
return Period.between(this.birthDay, LocalDate.now()).getYears();
}

void borrow(String bookTitle) {


this.borrowedBooks.add(bookTitle);
}
}
Back in the Main.java file, you can call this method as
follows:
public class Main {
public static void main (String[] args) {
User user = new User();

user.name = "Farhan";

user.borrow("Carmilla");
user.borrow("Hard West");

System.out.printf("%s has borrowed these books: %s", user.name,


user.borrowedBooks.toString()); // Farhan has borrowed these books: [Carmilla, Hard
West]
}
}
Let's create a class for the books as well:

import java.util.ArrayList;

public class Book {


String title;
ArrayList<String> authors = new ArrayList<String>();
}
Books often have multiple authors. Now you can create
a new book instance back in the Main.java file.
import java.time.LocalDate;

public class Main {


public static void main (String[] args) {
User user = new User();
user.name = "Farhan";
user.birthDay = LocalDate.parse( "1996-07-15");

Book book = new Book();


book.title = "Carmilla";
book.authors.add("Sheridan Le Fanu");

System.out.printf("%s is written by %s", book.title, book.authors.toString());


// Carmilla is written by [Sheridan Le Fanu]

}
}
Now let's go back to the User.java file and create a
relationship between the users and the books:
import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {


String name;
LocalDate birthDay;

ArrayList<Book> borrowedBooks = new ArrayList<Book>();

int age() {
return Period.between(this.birthDay, LocalDate.now()).getYears();
}
void borrow(Book book) {
this.borrowedBooks.add(book);
}
}
Instead of using an array list of strings, you're now
using an array list of books to store the books borrowed
by this user.

Since the argument type of the method has changed,


you'll have to update the code in the Main.java file
accordingly:
import java.time.LocalDate;

public class Main {


public static void main (String[] args) {
User user = new User();
user.name = "Farhan";
user.birthDay = LocalDate.parse( "1996-07-15");

Book book = new Book();


book.title = "Carmilla";
book.authors.add("Sheridan Le Fanu");

user.borrow(book);

System.out.printf("%s has borrowed these books: %s", user.name,


user.borrowedBooks.toString()); // Farhan has borrowed these books: [Book@30dae81]
}
}
Everything works out fine except the fact that book
information has not been printed properly.

I hope you remember the toString() method. When you


call user.borrowedBooks.toString() the compiler realizes
that the items stored in the arraylist are objects or
reference types. So it starts calling
the toString() methods inside those items.
The problem is, there is no proper implementation
of toString() in your Book class. Open Book.java and
update its code as follows:
import java.util.ArrayList;

public class Book {


String title;
ArrayList<String> authors = new ArrayList<String>();

public String toString() {


return String.format("%s by %s", this.title, this.authors.toString());
}
}
The toString() method now returns a nicely formatted
string instead of the object reference. Run the code
once again and this time the output should be Farhan has
borrowed these books: [Carmilla by [Sheridan Le Fanu]].
As you can see, being able to design your software
around real-life entities makes it a lot more relatable.
Although there is just an array list and a bunch of
strings in play, it feels like as if a real book borrowing
operation is going on.

What is Method Overloading?


In Java, multiple methods can have the same name if
their parameters are different. This is called method
overloading.

One example can be the borrow() method on


the User class. Right now, it accepts a single book as its
parameter. Let's make an overloaded version which can
accept an array of books instead.
import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;
import java.util.Arrays;

public class User {


private String name;
private LocalDate birthDay;
private ArrayList<Book> borrowedBooks = new ArrayList<Book>();

public String getName() {


return this.name;
}

public void setName(String name) {


this.name = name;
}

public String getBorrowedBooks() {


return this.borrowedBooks.toString();
}

User (String name, String birthDay) {


this.name = name;
this.birthDay = LocalDate.parse(birthDay);
}

int age() {
return Period.between(this.birthDay, LocalDate.now()).getYears();
}

void borrow(Book book) {


borrowedBooks.add(book);
}

void borrow(Book[] books) {


borrowedBooks.addAll(Arrays.asList(books));
}
}
The return type and name of the new method is
identical to the previous one, but this one accepts an
array of Book objects instead of a single object.
Let's update the Main.java file to make use of this
overloaded method.
public class Main {
public static void main (String[] args) {
User user = new User("Farhan", "1996-07-15");

Book book1 = new Book("Carmilla", new String[]{"Sheridan Le Fanu"});


Book book2 = new Book("Frankenstein", new String[]{"Mary Shelley"});
Book book3 = new Book("Dracula", new String[]{"Bram Stoker"});

user.borrow(new Book[]{book1, book2});

user.borrow(book3);

System.out.printf("%s has borrowed these books: %s", user.getName(),


user.getBorrowedBooks());
}
}
As you can see, the borrow() method now accepts an
array of books or a single book object without any issue.
What are Constructors in Java?
Constructors are a special kind of method that exists in
every class, and whenever you create a new object
from a class, the compiler calls it.

Since the method is called during the construction of an


object, it's called a constructor. By default, a
constructor assigns default values to all its properties.
To override the default constructor, you need to create
a new method under your classes with the same name
as the class.

import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {


public String name;
public LocalDate birthDay;
public ArrayList<Book> borrowedBooks = new ArrayList<Book>();

User (String name, String birthDay) {


this.name = name;
this.birthDay = LocalDate.parse(birthDay);
}

int age() {
return Period.between(this.birthDay, LocalDate.now()).getYears();
}

void borrow(Book book) {


this.borrowedBooks.add(book);
}
}
Now that you have a constructor, instead of parsing the
date from a string in the Main.java file, you can do that
here.
This is because the format of the birthday is the
concern of the User class and the Main class doesn't need
to bother about it.
Same treatment for the book class as well:

import java.util.ArrayList;
import java.util.Arrays;

public class Book {


public String title;
public ArrayList<String> authors = new ArrayList<String>();

Book(String title, String[] authors) {


this.title = title;
this.authors = new ArrayList<String>(Arrays.asList(authors));
}

public String toString() {


return String.format("%s by %s", this.title, this.authors.toString());
}
}
Again, the type of the authors collection is not a concern
of the Main class. The most basic way of working with a
bunch of values in Java is an array.
So you'll receive the author names as an array from
the Main class and create an array list out of it in
the Book class.
Now you'll have to pass those parameters to the
constructor when creating a new user or book object in
the Main.java file.
import java.util.ArrayList;

public class Main {


public static void main (String[] args) {
User user = new User("Farhan", "1996-07-15");

Book book = new Book("Carmilla", new String[]{"Sheridan Le Fanu"});

user.borrow(book);
System.out.printf("%s has borrowed these books: %s", user.name,
user.borrowedBooks.toString()); // Farhan has borrowed these books: [Carmilla, Hard
West]
}
}
Look how much cleaner it already looks. But soon it'll
look better.

What Are Access Modifiers in Java?


You've already seen the keyword public multiple times.
This is one of the access modifiers in Java.
There are four access modifiers in Java:

Primitive
Type Wrapper Class
Default Accessible within the package
Public Accessible everywhere
Private Accessible within the class
Accessible within the class and
Protected subclasses
For now, I'll discuss the Default, Public and Private access
modifiers. Protected will be discussed in a later section.
You've already learned about classes. Packages are
collections of multiple classes separated by their
functionality.

For example, if you're making a game, you can put all


the physics-related classes in a separate package and
the graphics-related ones in a different one.

Packages are outside of the scope of this book, but as


you keep working on larger and larger projects, you'll
get the hang of them.
The Public access modifier is pretty self-explanatory.
These variables, methods, or classes are accessible
from any other class or package in your project.
The Private ones, on the other hand, are the opposite.
They're only available within their class.
Take the User class, for example. The name and
birthday of a user shouldn't be accessible from the
outside.
import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {


private String name;
private LocalDate birthDay;
private ArrayList<Book> borrowedBooks = new ArrayList<Book>();

User (String name, String birthDay) {


this.name = name;
this.birthDay = LocalDate.parse(birthDay);
}

int age() {
return Period.between(this.birthDay, LocalDate.now()).getYears();
}

void borrow(Book book) {


borrowedBooks.add(book);
}
}
That's better. Update the Book class as well to hide the
title and author information from the outside world.
import java.util.ArrayList;
import java.util.Arrays;
public class Book {
private String title;
private ArrayList<String> authors = new ArrayList<String>();

Book(String title, String[] authors) {


this.title = title;
this.authors = new ArrayList<String>(Arrays.asList(authors));
}

public String toString() {


return String.format("%s by %s", this.title, this.authors.toString());
}
}
Since the properties have become private now,
the System.out.println() line in the Main.java file will fail to
directly access them and will cause an issue.
The solution to this program is writing public methods
that other classes can use to access these properties.

What Are the Getter and Setter Methods in


Java?
Getters and setters are public methods in classes used
to read and write private properties.

import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {


private String name;
private LocalDate birthDay;
private ArrayList<Book> borrowedBooks = new ArrayList<Book>();

public String getName() {


return this.name;
}

public String getBirthDay() {


return this.birthDay.toString();
}

public String getBorrowedBooks() {


return this.borrowedBooks.toString();
}

User (String name, String birthDay) {


this.name = name;
this.birthDay = LocalDate.parse(birthDay);
}

int age() {
return Period.between(this.birthDay, LocalDate.now()).getYears();
}

void borrow(Book book) {


borrowedBooks.add(book);
}
}
The getName() and getBorrowedBooks() are responsible for
returning the value of
the name and borrowedBooks variables.
You never actually access the birthday variable out of
the age() method, so a getter is not necessary.
Since the type of the borrowedBooks variable is not a
concern of the Main class, the getter makes sure to
return the value in the proper format.
Now update the code in the Main.java file to make use of
these methods:
public class Main {
public static void main (String[] args) {
User user = new User("Farhan", "1996-07-15");

Book book = new Book("Carmilla", new String[]{"Sheridan Le Fanu"});

user.borrow(book);

System.out.printf("%s has borrowed these books: %s", user.getName(),


user.getBorrowedBooks());
}
}
Excellent. It has become even cleaner and easier to
read. Like getters, there are setters for writing values to
the private properties.

For example, you may want to allow the user to change


their name or birthday. The borrow() method already
works as a setter for the borrowedBooks array list.
import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

public class User {


private String name;
private LocalDate birthDay;
private ArrayList<Book> borrowedBooks = new ArrayList<Book>();

public String getName() {


return this.name;
}

public void setName(String name) {


this.name = name;
}

public void setBirthDay(String birthDay) {


this.birthDay = LocalDate.parse(birthDay);
}

public String getBorrowedBooks() {


return this.borrowedBooks.toString();
}

User (String name, String birthDay) {


this.name = name;
this.birthDay = LocalDate.parse(birthDay);
}

int age() {
return Period.between(this.birthDay, LocalDate.now()).getYears();
}

void borrow(Book book) {


borrowedBooks.add(book);
}
}
Now you can call the setName() method with whatever
name you want to set to the user. Similarly,
the setBirthDay() method can set the birthday.
You can implement some getters and setters for
the Book class as well.
import java.util.ArrayList;
import java.util.Arrays;

public class Book {


private String title;
private ArrayList<String> authors = new ArrayList<String>();

public String getTitle() {


return this.title;
}
public void setTitle(String title) {
this.title = title;
}

public String getAuthors() {


return this.authors.toString();
}

public void setTitle(String[] authors) {


this.authors = new ArrayList<String>(Arrays.asList(authors));
}

Book(String title, String[] authors) {


this.title = title;
this.authors = new ArrayList<String>(Arrays.asList(authors));
}

public String toString() {


return String.format("%s by %s", this.title, this.authors.toString());
}
}
Now you can not access those properties directly.
Instead you'll have to use one of the getters or setters.

What is Inheritance in Java?


Inheritance is another big feature of object oriented
programming. Imagine you have three kinds of books.
The regular ones, e-books, and audio books.

Although they have similarities such as title and author,


they also have some differences. For example, the
regular books and e-books have page count whereas
audio books have run time. The e-books also have
format such as PDF or EPUB.
So using the same class for all three of them is not an
option. That doesn't mean you'll have to create three
separate classes with minor differences, though. You
can just create separate classes for e-books and audio
books and make them inherit the properties and
methods from the Book class.
Let's begin by adding the page count in the Book class:
import java.util.ArrayList;
import java.util.Arrays;

public class Book {


private String title;
private int pageCount;
private ArrayList<String> authors = new ArrayList<String>();

Book(String title, int pageCount, String[] authors) {


this.title = title;
this.pageCount = pageCount;
this.authors = new ArrayList<String>(Arrays.asList(authors));
}

public String length() {


return String.format("%s is %d pages long.", this.title, this.pageCount);
}

public String toString() {


return String.format("%s by %s", this.title, this.authors.toString());
}
}
Since you're not going to use getters and setters in
these examples, cleaning up seemed like a good idea.
The length() method returns the length of the book as a
string.
Now create a new Java class named AudioBook and put
the following code in it:
public class AudioBook extends Book{
private int runTime;

AudioBook(String title, String[] authors, int runTime) {


super(title, 0, authors);

this.runTime = runTime;
}
}
The extends keyword lets the compiler know that this
class is a subclass of the Book class. This means that this
class inherits all the properties and methods from the
parent class.
Inside the AudioBook constructor method, you set the run
time for the audio book which is fine – but you'll also
have to manually call the constructor of the parent
class.
The super keyword in Java refers to the parent class,
so super(title, 0, authors) essentially calls the parent
constructor method with the necessary parameters.
Since the audio books don't have any pages, setting the
page count to zero can be an easy solution.

Or you can create an overloaded version of


the Book constructor method that doesn't require the
page count.
Next, create another Java class named Ebook with the
following code:
public class Ebook extends Book{
private String format;

Ebook(String title, int pageCount, String[] authors, String format) {


super(title, pageCount, authors);
this.format = format;
}
}
This class is largely identical to the Book class except the
fact that it has a format property.
public class Main {
public static void main (String[] args) {
Book book = new Book("Carmilla", 200, new String[]{"Sheridan Le Fanu"});
Ebook ebook = new Ebook("Frankenstein", 220, new String[]{"Mary
Shelley"}, "EPUB");
AudioBook audioBook = new AudioBook("Dracula", new String[]{"Bram
Stoker"}, 160);

System.out.println(book.toString()); // Carmilla by [Sheridan Le Fanu]


System.out.println(ebook.toString()); // Frankenstein by [Mary Shelley]
System.out.println(audioBook.toString()); // Dracula by [Bram Stoker]
}
}
So far everything is working fine. But do you remember
the length() method you wrote inside the Book class? It'll
work for the regular books but will break in the e-books.
That's because the page count property is marked
as private and no other class except Book will be able to
access it. The title is also a private property.
Open the Book.java file and mark
the title and pageCount properties as protected.
import java.util.ArrayList;
import java.util.Arrays;

public class Book {


protected String title;
protected int pageCount;
private ArrayList<String> authors = new ArrayList<String>();

Book(String title, int pageCount, String[] authors) {


this.title = title;
this.pageCount = pageCount;
this.authors = new ArrayList<String>(Arrays.asList(authors));
}

public String length() {


return String.format("%s is %d pages long.", this.title, this.pageCount);
}

public String toString() {


return String.format("%s by %s", this.title, this.authors.toString());
}
}
This'll make them accessible from the subclasses. The
audio books have another problem with
the length() method.
Audio books don't have a page count. They have run
times and this difference will break the length method.

One way to solve this problem is by overriding


the length() method.
How to Override a Method in Java
As the name suggests, overriding means cancelling the
effect of a method by replacing it with something else.

public class AudioBook extends Book{


private int runTime;

AudioBook(String title, String[] authors, int runTime) {


super(title, 0, authors);

this.runTime = runTime;
}
@Override
public String length() {
return String.format("%s is %d minutes long.", this.title, this.runTime);
}
}
You override a method from the parent class by
rewriting the method in the subclass.
The @Override keyword is an annotation. Annotations in
Java are metadata.
It's not mandatory to annotate the method like this. But
if you do, the compiler will know that the annotated
method overrides a parent method and will make sure
you're following all the rules of overriding.

For example, if you make a mistake in the method


name and it doesn't match any method from the
parent, the compiler will let you know that the method
is not overriding anything.

public class Main {


public static void main (String[] args) {
AudioBook audioBook = new AudioBook("Dracula", new String[]{"Bram
Stoker"}, 160);

System.out.println(audioBook.length()); // Dracula is 160 minutes long.


}
}
Cool, isn't it? Whenever overriding a method in Java,
keep in mind that both the original and overridden
method must have the same return type, same name,
and same parameters.

Conclusion
I would like to thank you from the bottom of my heart
for the time you've spent on reading this book. I hope
you've enjoyed your time and have learned all the
fundamental concepts of Java.

This handbook is not frozen in time. I'll keep working on


it and I'll update it with improvements, new content,
and more. You can provide anonymous opinions and
suggestion on the handbook in this form.
Apart from this one, I've written full-length handbooks
on other complicated topics available for free
on freeCodeCamp.
These handbooks are part of my mission to simplify
hard to understand technologies for everyone. Each of
these handbooks takes a lot of time and effort to write.

If you've enjoyed my writing and want to keep me


motivated, consider leaving starts on GitHub and
endorse me for relevant skills on LinkedIn.
I'm always open to suggestions and discussions
on Twitter or LinkedIn. Hit me up with direct messages.
In the end, consider sharing the resources with others,
because:

In open source, we feel strongly that to really do


something well, you have to get a lot of people
involved. — Linus Torvalds
Till the next one, stay safe and keep learning.

ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT

You might also like