Java Ranga
Java Ranga
app/
I am Ranga Karanam, and I have more than two decades of programming experience.
I love Programming. One of the aims I had when I started in28minutes was to make learning
programming easy. Thanks for helping us provide amazing courses to 300,000 learners across
the world.
At In28Minutes, we ask ourselves one question every day: "How do we create awesome
learning experiences?"
In this book, you will learn to write object oriented code with Java. You will be exposed to a lot
of examples, exercises and tips. We will take up a lot of examples, and try and see how to write
code for those in Java.
Help us improve this guide - Fork, Pull Requests, Shares and Likes are recommended!
Table of Contents
Our Approach
We did a study on why students give up on programming?
Put yourselves in the shoes of a beginner and look at this typical Java Hello World Example .
package com.in28minutes.firstjavaproject;
public class HelloWorld
{
public static void main(String[] args) {
System.out.println("Hello World");
}
}
A Programming Beginner will be overwhelmed by this. I remember how I felt when I saw this
almost 20 years back. Stunned.
Why?
• There are a number of keywords and concepts - package, public, class, static, void,
String[] and a lot more..
• What if the programmer makes a typo? Will he be able to fix it?
This is the approach we took to writing this guide and develop our introductory programming
courses for Java and Python.
Do you know? The first 3 hours of our Java Course is available here.
Once you use this stepwise approach to solve a few problems, it becomes a habit.
In this book, we will introduce you to Java programming by taking on a few simple problems to
start off.
Are you all geared up, to take on your first programming challenge? Yes? Let's get started
then!
Our first programming challenge aims to do, what every kid does in math class: reading out a
multiplication table.
The PMT-Challenge
5 * 1 = 5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
As part of solving the multiplication table problem, you will be introduced to:
• JShell
• Statements
• Expressions
• Variables
• Literals
• Conditionals
• Loops
• Methods
Summary
JShell is a programming tool, introduced in Java SE 9. JShell is a REPL interface. The term
REPL refers to this:
You can use https://tryjshell.org/ to run the code for the first 25 steps. Or you can Install Java
12+. Here's the troubleshooting section if you face problems.
in28minutes$>java -version
java version "x.0.1"
Java(TM) SE Runtime Environment (build x.0.1+11)
Java HotSpot(TM) 64-bit Server VM (build x.0.1+11, mixed mode)
in28minutes$>
A successful execution displays the version of Java installed your system. You need to have
atleast Java 9 to pursue this book.
in28minutes$>jshell
When run, this command displays basic information about the installed JShell program. A
jshell prompt then appears, which waits for your input.
The JShell command /help , with a parameter intro , gives you basic guidelines on how you
use the tool.
|
| intro
| The jshell tool allows you to execute Java code, getting immediate results.
| You can enter a Java definition (variable, method, class, etc), like: int x =8
| or a Java expression, like: x + x
| or a Java statement or import.
| These little chunks of Java code are called 'snippets'.
| There are also jshell commands that allow you to understand and
| control what you are doing, like: /list
|
| For a list of commands: /help
jshell>
jshell> 3 + 4
$1 ==> 7
jshell>
This was your first real REPL cycle! When you type in 3 + 4 , JShell evaluates it and prints the
result.
The entity $1 is actually a variable name assigned to the result. We will talk about this
later.
The /exit command terminates the JShell program, and we are back to the terminal
prompt.
jshell> /exit
| Goodbye
in28minutes$>
You can now effortlessly launch, feed code to, and exit from JShell !
in28minutes$> jshell
in28minutes$>
Summary
• How to launch JShell from our terminal, and run a few commands on it
• How to run Java code on the JShell prompt
5 * 1 = 5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
Summary
in28minutes$> jshell
| Error:
| ';' expected
| 5 X 3
| ^
| Error:
| not a statement
| 5 X 3
| ^
| Error:
| ';' expected
| 5 X 3
| ^
| Error:
| missing return statement
| 5 X 3
| ^---^
You probably look at the symbol 'X' as a multiplier, remembering your school days.
Java does not identify ' X ' as the multiplication operator! Java supports multiplication, but only
if you use its predefined operator, * .
jshell> 5 * 3
$1 ==> 15
jshell>
Success!
• 5 * 3 is an expression.
• 5 and 3 are operands. They are also called literals or constant values.
• * is an operator.
Java also has built-in operators to perform other numerical tasks, such as:
• Addition: +
• Subtraction: -
• Division: /
• Modulo arithmetic: %
jshell> 5 * 10
$2 ==> 50
jshell> 5 + 10
$3 ==> 15
jshell> 5 - 10
$4 ==> -5
jshell> 10 / 2
$5 ==> 5
jshell>
Your school math memories are still fresh, and the operators don't seem to disappoint either!
+ , - and / are your bread-and-butter operators.
% is the modulo operator, which gives you the remainder when integer division is performed.
jshell> 9 % 2
$6 ==> 1
jshell> 8 % 2
$7 ==> 0
jshell>
Java allows you to use more than one operator within an expression.
jshell> 5 + 5 + 5
$8 ==> 15
jshell> 5 + 10 - 15
$9 ==> 0
jshell> 5 * 5 + 5
$10 ==> 30
jshell> 5 * 15 / 3
$11 ==> 25
Summary
Solution 1
jshell> 60 * 24
$1 ==> 1440
Solution 2
$jshell>60 * 60 * 24
$1 ==> 86400
Invalid Operators
jshell> 5 ** 6
| Error:
| Illegal start of expression
| 5 ** 6
| ^
jshell> 5 $ 6
| Error:
| ';' expected
| 5 $ 6
| ^
| Error:
| not a statement
| 5 $ 6
| ^
| Error:
| ';' expected
| 5 $ 6
| ^
jshell> 5 */ 6
| Error:
| Illegal start of expression
| 5 */ 6
| ^
Java has a set of grammar rules, called its syntax. Code that does not follow these rules throw
errors. The compiler informs you by listing the errors, and also provides hints on how you can
correct them.
Operators in Java are all predefined, and limited in number. * is a valid operator, whereas **
and $ were rejected, with error messages.
jshell> 5 / 2
$1 ==> 2
jshell>
Read what follows, with the biggest magnifying lens you can find:
The result of an expression when evaluated, depends on the operator context. This context
is determined by the operands passed to it
There are two kinds of numbers typically used in programming : integer (1,2,3,...) and floating-
point (1.1,2.3, 56.7889 etc). These values are represented by different types in Java. Integers
are commonly of type int , and the floating-point numbers are double by default.
In the expression 5/2 , both 5 and 2 are of type int . So, the result is also of type int .
Both 5.0 and 2.0 are of type double , the result is double . We get a result of 2.5 , as
expected.
jshell> 5.0 / 2
$3 ==> 2.5
jshell>
Among the types int and double , double is considered to be a wider type. When you
perform a numeric operation between two types, the result will be of the wider type.
Let's look at few complex examples of expressions with more than one operator.
jshell> 5 + 5 * 6
$1 ==> 35
jshell> 5 - 2 * 2
$2 ==> 1
jshell> 5 - 2 / 2
$3 ==> 4
jshell>
Surprised with the results? You might expect 5 + 5 * 6 evaluate to 10 * 6 i.e. 60. Howeever, we
got 35 !
The basic rules for operator precedence are actually quite simple (we will look at other rules a
little later).
The operators in the set { * , / , % } have higher precedence than the operators in the set { + ,
- }.
Java provides syntax elements, called parentheses ( and ) , to group parts of an expression.
jshell> (5 - 2) * 2
$4 ==> 6
jshell> 5 - (2 * 2)
$5 ==> 1
jshell>
Parentheses lead to better readability as they reduce confusion and help avoid errors.
The old adage A stitch in time saves nine rings very true here. Use parentheses to make your
code easy to read, even when they might not be necessary.
Summary
How do we do this?
jshell> 5 * 3 = 15
| Error:
| unexpected type
| required: variable
| found: value
| 5 * 3 = 15
| ^---^
Hmm! Error.
Java has a built-in utility method called System.out.println() , that displays text on the
console.
jshell> System.out.println(3*4)
12
The syntax rules for method calls are quite strict, and all its parts are mandatory.
jshell> System.out.println3*4)
| Error:
| ';' expected
| System.out.println3*4)
|___________________^
| Error:
| cannot find symbol
| symbol: variable println3
| System.out.println3*4)
| ^---------------------^
What if we want to print an entry of the Multiplication Table, as part of our solution to PMT-
Challenge? In other words, how do we print the exact text 5 * 2 = 10 on the console?
You wanted to print 5 * 2 = 10 on the console. However, we see that we cannot pass 5 * 2
= 10 as an argument to System.out.println() .
Congratulations! You have now figured out how to display not just numbers on the console, but
text as well!
Summary
2. Print 5 * 3 , as is.
5. Do a syntax revision for the code that you write for each of the above exercises. In your
code, identify the following elements:
Solution 1
Solution 2
Solution 3
jshell> System.out.println(5 * 3)
15
Solution 4
Whitespace
jshell> System.out.println("HelloWorld")
HelloWorld
Case Sensitive
System.out.println() involve pre-defined Java elements : the System class name, the out
variable name,and the println method name. All are case-sensitive. If any character in these
names is used with a different case, you get an error.
jshell>
Inside a string literal, the case of characters do not cause errors. The literal will be taken in and
printed, as-is.
Escape Characters
An escape character is a special symbol, used with another regular symbol to form an escape
sequence. In Java, the ' \ ' (back-slash) character plays the role of an escape character. This
escape sequence changes the original usage of the symbols.
If you want to print the string delimiter, the " character, you need to escape it with a \ .
Without it, a " character within a string literal causes an error!
You would need to escape it with another \ . Printing \\ outputs the symbol \ to the
console.
Summary
You know how to invoke a method with a single argument, courtesy System.out.println(3*4) .
Let's check them out, using the built-in methods in Java Math class.
In method calls, parentheses are a necessary part of the syntax. Don't leave them out!
Math.random() prints a random real number in the range [0 .. 1] , a different one on each
call
jshell> Math.random
| Error:
| cannot find symbol
| symbol: Math.random
| Math.random
| ^------------- ^
jshell> Math.random()
$1 ==> 0.0424279106074651_
jshell> Math.random()
$2 ==> 0.8696879746593543
jshell> Math.random()
$3 ==> 0.8913591586787125
jshell> Math.min 23 45
| Error
| cannot find symbol
| symbol: variable min
| Math.min 23 45
| ^---------^
jshell> Math.min(23 45)
| Error
| ')' expected
| Math.min 23 45
| ---------------^
Math.min() returns the minimum of two given numbers. Math.max() returns the maximum of
two given numbers.
Summary
• Understood how zero, or multiple parameters are passed during a method call
To display the multiplication table for 5 with a calculated value, we need a way to print both
numbers and strings.
When System.out.printf() is called with a single string argument, it prints some illegible
information. For now, it suffices to know, that this information is about the built-in type
java.io.PrintStream .
The good news is, that if we call the println() method on this, the illegible stuff disappears.
• The first argument specifies the print format. This is a string literal, having zero or more
format specifiers. A format specifier is a predefined literal (such as %d ), that formats data
of a particular type ( %d formats data of type int ).
• The trailing arguments are expressions,
Let's try to display a calculated value. In the example below 5*7 is calculated as 35 .
Let's use this to print the output in the format that we want to use for multiplication table:
Exercise
Solution
In the example below, there are four format specifiers ( %d ) and only three value arguments 5,
6, 7 .
If the number of format specifiers is less than the number of trailing arguments, the compiler
simply ignores the excess ones.
Earlier, we used %d to print an int value. You cannot use %d to display floating point values.
Summary
If you carefully observe the code, these statements are very similar.
What's changing? The number in the third and fourth parameter slots changes from 1 to 4.
Welcome variables.
• number is a variable.
• The literal number is the variable name.
• The Java keyword int specifies the type of number .
• The literal 10 provided number with an initial value.
jshell> number = 11
number ==> 11
An assignment causes the value stored in the memory location to change. In this case, 10 is
replaced with the value 11 .
jshell> number
number ==> 11
jshell> number = 12
number ==> 12
jshell> number
number ==> 12
jshell>int i = 1
i ==> 1
jshell> i
i ==> 1
jshell> 5*i
$1 ==> 5
How do we print 5 * 2 = 10 ?
jshell> i = 2
i ==> 2
jshell> 5*i
$2 ==> 10
jshell> System.out.printf("%d * %d = %d", 5, i, 5*i).println()
5 * 2 = 10
jshell>
jshell> i = 3
i ==> 3
jshell> System.out.printf("%d * %d = %d", 5, i, 5*i).println()
5 * 3 = 15
jshell> i = 10
i ==> 10_
jshell> System.out.printf("%d * %d = %d", 5, i, 5*i).println()
5 * 10 = 50
jshell>
By varying the value of i , we are able to print different multiples of 5 with the same
statement.
Summary
Solution to PE-03
jshell>int a = 5
a ==> 5
jshell>int b = 7
b ==> 7
jshell>int c = 11
c ==> 11
jshell>newVariable
| Error:
| cannot find symbol
| symbol: newVariable
| newVariable
^------------^
The variable number is an integer, mathematically a number. The constant 5.5 is a number as
well.
5.5 is a floating-point number of type double . We are trying to store a double inside a
memory slot meant for int .
number is an int variable, and we are trying to store a String value "Hello World" . Not
allowed.
Summary
The statement int number = 5 combines a declaration and the initialization of number .
jshell> number2
number2 ==> 5
jshell>
The initial value for number2 is another variable, which is previously defined ( number ).
Here's what goes on behind the scenes with int number2 = number :
jshell> int a = 5
a ==> 5
jshell> int b = 10
b ==> 10
jshell> a = 100
a ==> 100
jshell> int c
c ==> 0
jshell> c = a + b
c ==> 110
jshell> a = c
a ==> 110
jshell> int d = b + c
d ==> 120
jshell>
• From a literal value, to a variable, having compatible types. The statement a = 100 is one
such example.
• From a variable to another variable, of compatible types.The statement a = c illustrates
this case.
• From a variable expression to a variable. This expression can be formed with variables and
literals of compatible types, and is evaluated before the assignment. The statement c = a
+ b below is an example of this.
jshell> 20 = var
| Error:
| unexpected type
| required : variable
| found : value
| 20 = var
| ^^
jshell>
Summary
In this step, we discussed how to provide variables with initial values and the basics of
assigment.
Let's now see what kinds of errors the compiler throws if you violate these naming rules.
| Error:
| ';' expected
| int test-test
| ^
jshell>
In Java, certain special words are called keywords . For example, some of the data types we
looked at - int , double . These keywords cannot be used as variable names.
| unexpected type
| required: value
| found: class
| int int
| ^--^
| Error:
| missing return statement
| int int
| ^------...
In a football scorecard application, using a name s for a score variable is vague. Something
like score carries more meaning, and is preferred.
jshell> int s
s ==> 0
jshell> int score
score ==> 0
jshell>
The Java community, with its wealth of experience, suggests that programmers follow
some naming conventions. Above examples is one of these. Violation of these rules does
not result in compilation errors. That's why, these are called conventions.
In Java, another convention is to use CamelCase when we have multiple words in variable
name.
But in Java, the convention followed is to start variable names with a lower case letter. So, for a
variable, noOfGoals is preferred.
Use the shortest meaningful and readable name possible for your variables.
Summary
Java Size
Type of
Primitive (in Range of Values Example
Values
Type bits)
Integral
byte 8 -128 to 127 byte b = 5;
Values
Integral short s =
short 16 -32,768 to 32,767
Values 128;
Java Size
Type of
Primitive (in Range of Values Example
Values
Type bits)
Integral int i =
int 32 -2,147,483,648 to 2,147,483,647
Values 40000;
approximately
Floating- ±1.79769313486231570E+308. NOT
double d =
Point double 64 very precise, but better than float
67.0;
Values (also avoid for financial/scientific
math)
Character char c =
char 16 '\u0000 to '\uffff
Values 'A';
boolean
Boolean
boolean 1 true or false isTrue =
Values
false;
Let's now look at how we create data of these types, and store them in memory.
Note: In the above examples, we used semi-colon ; character at the end. This is the Java
statement separator, which is not mandatory in JShell for single statements. However, it
is a must when you use code editors and IDE's, to write Java code.
Integer Types
The only difference among the integer types is their storage capacity.
jshell> byte b = 5
b ==> 5
jshell> short s = 128
s ==> 128
jshell> int i = 40000
i ==> 40000
jshell> long l = 2222222222
l ==> 2222222222
jshell>
double is the default type for floating type values with size 64 bits.
float occupies 32 bits. float literals need to have a suffix ' f ' (as in 4.0f ).
You can store float value in double . Remember, both types store similar kind of values and a
float value is only 32 bits where as double has 64 bits.
Character Type
The character type char can store a single character symbol. The symbol must be enclosed
within a pair of single quotes, ' and ' .
Following are a few char declaration errors: Not using single quotes and trying to store multiple
characters.
jshell> char ch = A
| Error:
| cannot find symbol
| symbol: variable A
| char ch = A
| ^
Boolean Type
The concept of a boolean type is rooted in mathematical logic. The data type can store two
possible values, true and false . Both are case-sensitive labels, but not enclosed in quotes.
boolean data are mostly used in expressions used to form logical conditions in your code. We
will talk more about this - when we explore Java conditionals.
Summary
Consider a sports broadcaster that writes a program to track football scores. In a football game
there are two teams playing. A game is generally 90 minutes long, with extra time pushing it to
120 minutes at the most. Practically speaking, the scorelines are never huge. From a practical
standpoint, the number of goals scored in a match would never exceed 7200 (the number of
seconds in 120 minutes). Playing it a bit safe, a short data type would be enough to store the
score of each side (though a byte would be more than enough).
We know that the global count of humans is well over 7 billion now, so the only integer data
type that can store it is a long .
Rainfall is usually measured in millimeters (mm), and we know computing an average over 30
days is very likely to yield a floating-point number. Hence we can use a float , or for more
accuracy, a double to store that average.
For a program that generates a grade report, a char type would be the best bet.
A boolean isNumEven is a good bet, with a default initial value of false . It can be changed to
true if the number turns out to be even.
Summary
We are allowed to use expressions to assign values to variables, using code such as c = a +
b.
jshell> int i = 10
i ==> 10
jshell> int j = 15
j ==> 15
jshell> i = j
i ==> 15
jshell>
jshell> i = i * 2
i ==> 30
jshell>
The same variable i appears on both the right-hand-side (RHS) and left-hand-side (LHS).
How would that make any sense?
The expression on the RHS is independently evaluated first. The value we get is next copied
into the memory slot of the LHS variable.
Using the logic above, the assignment i = i * 2 actually updates the value of i (initially
15 ) to 30 (doubles it).
Java also does the right thing when it encounters the puzzling statement i = i + i , where i
is all over the place! The code doubles the value of i , and this reflects on the JShell output.
jshell> i = i + i
i ==> 60
jshell> i = i - i
i ==> 0
jshell> int i = 0
i ==> 0
jshell> i = i + 1
i ==> 1
jshell> i = i + 1
i ==> 2
jshell> i = i + 1
i ==> 3
jshell>
Conversely, the statement i = i - 1 is called a variable decrement. This can also be done
repeatedly, and i changes value every time.
jshell> i = i - 1
i ==> 2
jshell> i = i - 1
i ==> 1
jshell> i = i - 1
i ==> 0
jshell>
Summary
There are two short-hand versions for increment. number++ denotes post-increment.
Conversely, ++number means pre-increment.
Operator ++ can be applied only to variables of integer types, which are byte , short , int
and long .
jshell> ++number
$2 ==> 7
jshell> number
number ==> 7
jshell>
jshell> number--
$3 ==> 7
jshell> number
number ==> 6
jshell> --number
$4 ==> 5
jshell> number
number ==> 5
jshell>
Although both prefix and postfix versions achieve the same visible result in above examples,
there is a slight difference. We will explore this later.
jshell> int i = 1
i ==> 1
jshell> i = i + 2
i ==> 3
jshell> i += 2
$1 ==> 5
jshell>
jshell> i
i ==> 5
jshell> i -= 1
$2 ==> 4
jshell> i
i ==> 4
jshell>
jshell> i *= 4
$3 ==> 20
jshell> i
i ==> 20
jshell>
jshell> i /= 4
$4 ==> 5
jshell> i
i ==> 5
jshell>
jshell> i %= 2
$5 ==> 1
jshell> i
i ==> 1
jshell>
Summary
Summary
The PMT-Challenge needs us to print a total of 10 table entries. The for loop is a suitable
iteration mechanism to get this done. The word loop means exactly what the English dictionary
says.
Logical Operators
Turns out Java has a class of logical operators, which can be used with operands within
logical expressions, evaluating to a boolean value.
jshell> int i = 10
i ==> 10
jshell> i == 10
$1 ==> true
jshell> i == 11
$2 ==> false
These are other comparison operators as well, such as < and > .
jshell> i < 5
$3 ==> false
jshell> i > 5
$4 ==> true
jshell> i <= 5
$4 ==> false
jshell> i <= 10
$5 ==> true
jshell> i >= 10
$6 ==> true
jshell>
We would want to execute specific lines of code only when a condition is true.
Enter if statement.
if (condition) {
statement;
}
jshell> int i = 10
i ==> 10
jshell> System.out.println("i is less than 5")
i is less than 5
jshell> if (i < 5)
...> System.out.println("i is less than 5");
jshell>
The condition i < 5 will evaluate to false , since i is currently 10 . Nothing is printed to
console.
jshell> i = 4
i ==> 4
jshell> if (i < 5)
...> System.out.println("i is less than 5");
i is less than 5
jshell>
By controlling the value stored in the variable i , we are able to control whether the statement
System.out.println("i is less than 5"); actually runs.
Just as we can compare a variable with a literal, it is possible to compare the values of two
variables. The same set of comparison operators, namely == , < , > , <= and >= can be
used.
jshell> number2 = 3
number2 ==> 3
jshell> if (number2 > number1)
...> System.out.println("number2 is greater than number1");
jshell>
Summary
1. Create four integer variables a , b , c and d . Write an if statement to print if the sum
of a and b is greater than the sum of c and d .
2. Store three numerical values as proposed angles of a triangle in integer variables angle1 ,
angle2 and angle3 . Create an if statement to state whether these three angles
together can form a triangle.
Hint: A triangle is a closed geometric figure with three angles, whose sum must exactly
equal 180 degrees .
3. Have a variable store an integer. Create an if statement to find out if it's an even number.
Solution 1
jshell> int a = 5
a ==> 5
jshell> int b = 7
b ==> 7
jshell> int c = 4
c ==> 4
jshell> int d = 3
d ==> 3
jshell> if (a + b > c + d)
...> System.out.println("a and b together tower above c plus d");
a and b together tower above c plus d
jshell>
Solution 2
jshell> angleThree = 75
angleOne ==> 55
jshell> if (angleOne + angleTwo + angleThree == 180)
...> System.out.println("The three angles together form a triangle");
jshell>
Solution 3
jshell> if (num % 2 == 0)
...> System.out.println("The number is even");
The number is even
jshell> num++
num ==> 11
jshell> if (num % 2 == 0)
...> System.out.println("The number is even");
jshell>
Snippet-1
jshell> int i = 5
i ==> 5
jshell> if (i == 5)
...> System.out.println("i is odd");
i is odd
jshell> if (i == 5)
...> System.out.println("i is odd"); System.out.println("i is prime");
i is odd
i is prime
jshell>
jshell> i = 6
i ==> 6
jshell> if (i == 5)
...> System.out.println("i is odd"); System.out.println("i is prime");
i is prime
jshell>
It does not control the execution of System.out.println("i is prime"); , which in fact runs
unconditionally, always.
Is there a way we can ensure conditional execution of a two statements? And more than two?
We seem to have pulled a rabbit out of the hat here, haven't we! The rabbit lies in the pair of
braces : ' { ' and ' } '.
They are used to group a sequence of statements together, into a block. Depending on the
condition value ( true or false ), either all the statements in this block are run, or none at all.
jshell> int i = 5
i ==> 5
jshell> if (i == 5) {
...> System.out.println("i is odd");
...> System.out.println("i is prime");
...> }
i is odd
i is prime
jshell> i = 6
i ==> 6
jshell> if (i == 5) {
...> System.out.println("i is odd");
...> System.out.println("i is prime");
...> }
jshell>
if (i == 5) {
System.out.println("i is odd");
}
A block can also consist of a single statement! This improves readability of code.
Summary
The word loop means exactly what the English dictionary says. As i varies from 1 through
10 , do some stuff involving i
• initialization : int i = 1
• condition : i <= 10
• update : i++
Let's now put them together to get the bigger picture. Here is how it might look:
This loop, when executed, prints the message "Hello World" on a separate line, for a total of
10 times.
We need to replace the "Hello World" message with the console print of a table entry. This print
is controlled by i taking values from 1 through 10 .
jshell> int i
i ==> 0
jshell> for (i=0; i<=10; i++) {
...> System.out.printf("%d * %d = %d", 5, i, 5*i).println();
...> }
5 * 1 = 5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 2
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
Snippet-1 Explained
Meanwhile, the Multiplication Table for 5 , for entries 1 through 10 has been displayed!
Let's pat ourselves on the back for having reached this stage of learning. This elegant, yet
powerful technique (loops) is used in almost every Java program that's written.
Summary
Solution 2
jshell> int i
i ==> 0
jshell> for (i=0; i<=10; i++) {
...> System.out.printf("%d * %d = %d", 6, i, 6*i).println();
...> }
6 * 1 = 6
6 * 2 = 12
6 * 3 = 18
6 * 4 = 24
6 * 5 = 30
6 * 6 = 36
6 * 7 = 42
6 * 8 = 48
6 * 9 = 54
6 * 10 = 60
jshell> i = 0
i ==> 0
jshell> for (i=0; i<=10; i++) {
...> System.out.printf("%d * %d = %d", 10, i, 10*i).println();
...> }
10 * 1 = 10
10 * 2 = 20
10 * 3 = 30
10 * 4 = 40
10 * 5 = 50
10 * 6 = 60
10 * 7 = 70
10 * 8 = 80
10 * 9 = 90
10 * 10 = 100
Solution 3
7
8
9
10
Solution 4
This is the first time we are using i-- . Isn't this interesting?
Solution 5
Solution 6
Solution 7
statement;
statement;
//...
statement;
}
It may surprise you that each one of initialization, condition, updation and statements block is
optional. They can all be left out individually, in combination, or all altogether!! Let's examine a
few interesting cases in code.
Increments the control variable in quick succession until the condition evaluates to false.
jshell> int i = 1
i ==> 1
jshell> for (; i<=10; i++);
jshell> i
i ==> 11
jshell>
jshell> int j
i ==> 11
jshell> for (i=1, j=2; i<=10; i++, j++);
jshell> i
i ==> 11
jshell> j
j ==> 12
jshell>
jshell> i
i ==> 11
jshell> j
j ==> -8
jshell>
3. Infinite Loop
An infinite loop is one where the condition is left empty. An empty condition always evaluates
to true . Since a for loop only terminates when the condition becomes false , such a loop
this never terminates.
^C
jshell>
As in case of the if conditional statement, we can have statement blocks in for loops as
well. As before, we enclose the statement set between braces (' { ' and ' } '). The statements
are executed repeatedly, in the same order as they appear in the block.
Summary
In this step, we saw that all components of a for loop are optional:
• initialization
• condition
• updation
• statement block
A computer is a machine that does a job for you and me. It is can be used to run tasks that we
find complicated, time-consuming, or even boring! For instance, a laptop can play music from a
CD, videos from the web, or fire a print job.
We have mobile phones around us, which are mini versions of computers. Then there are
others, ranging from blood-sugar monitors to weather-forecast systems. Computers surround
us, wherever we go!
• The hardware: Consists of all the electronic and mechanical parts of the computer,
including the electronic circuits.
• The software: Describes the instructions fed into the computer, which are stored and run
on its hardware.
• Its hardware would consist of the various organs (such as limbs, blood and heart)
• Its software would be the signals from the nervous system, which drive these organs.
Computer programming involves writing software instructions to run tasks on a computer. The
user who gives these instructions is called the programmer. Often, computer programming
involves solving challenging, and very interesting problems.
In the previous steps, we introduced you to the basic Java language concepts and constructs.
At each step, we applied fresh knowledge to enhance our solution to the PMT-Challenge
Hope you liked the journey thus far. Next sections delve deeper into Java features, using the
same Step by-step approach. We will catch up again soon, hopefully!
Understanding Methods
Feeling good about where you are right now? You just created an elegant, yet powerful solution
to the PMT-Challenge, using:
And guess what we ended up with? A good-looking display of a Multiplication Table! There's a
good chance people in your circles want to see it, use it and maybe share it among their
friends.
However, some might be unhappy that it works only for 5 , and not for 8 and 7 . Maybe it
would, but then they would need to type in those lines of code again. This might disappoint
Exist it does, and the mechanism to use it lies with Java methods. A method is a feature that
allows you to group together a set of statements, and give it a name. This name represents the
functionality of that set, which can be re-used when necessary.
A method is essentially a routine that performs a certain task, and can do it any number of
times it is asked to. It may also return a result for the task it performs. The syntax for a method
definition is along these lines:
ReturnType methodName () {
method-body
}
Where
Summary
When the code ran, it didn't exactly welcome us twice, did it? All we got was a boring
message from JShell , mumbling created method sayHelloWorldTwice() .
That's because defining a method does NOT execute its statement body.
Since the statement block is not stand-alone anymore, its functionality needs to be invoked.
This is done by writing a method call.
jshell> sayHelloWorldTwice
| Error:
| cannot find symbol
| symbol: variable sayHelloWorldTwice
| sayHelloWorldTwice
| ^----------------^
jshell> sayHelloWorldTwice()
Hello World
Hello World
The trailing ' ; ' can be left out in JShell , and is another example of syntax rules being
relaxed.
Summary
Exercise Set -5
1. Write and execute a method named sayHelloWorldThrice to print Hello World thrice.
2. Write and execute a method that prints the following four statements:
Solutions to PE-01
Solution-1
jshell> sayHelloWorldThrice()
Hello World
Hello World
Hello World
Solution-2
jshell> sayFourThings()
I've created my first variable
I've created my first loop
I've created my first method
I'm excited to learn Java
The /methods command lists out the methods defined in the current session.
jshell> /methods
| void sayHelloWorldTwice()
jshell>
The /edit command allows you to modify the method definition, in a separate editor window.
The /save method takes a file name as a parameter. When run, it saves the session method
definitions to a file.
in28minutes$>
Summary
In this step, we explored a few JShell tips that make life easier for you while defining methods
Imagine you're in the Java classroom, where your teacher wants to test your Java skills by
saying: "I want you to print Hello World an arbitrary number of times".
The thing to note is the word "arbitrary", which means the method body should have no clue!
This number has to come from outside the method, which can only happen with a method call.
To support arguments during a call, the concept of a method needs to be tweaked to:
method-body
ArgType argName
within the parentheses. argName represents the argument, and ArgType is its type. The next
example should clear the air for you.
jshell> sayHelloWorld()
| Error:
| method sayHelloWorld in class cannot be applied to given types;
| required : int
| found : no arguments
| reason : actual and formal argument lists differ in length
| sayHelloWorld(
|^-----------------^
jshell> sayHelloWorld(1)
jshell>
Method call must include the same number and types of arguments, that it is defined to have.
sayHelloWorld() is an error because sayHelloWorld is defined to accept one parameter.
sayHelloWorld(1) works.
The next example will show you how method body code can access arguments passed during a
call. Not only that, the argument values can be used during computations.
A method can be invoked many times within a program, and each time different values could be
passed to it. The following example will illustrate this fact.
jshell> sayHelloWorld(1)
1
jshell> sayHelloWorld(2)
2
jshell> sayHelloWorld(4)
4
jshell> /edit sayHelloWorld
| modified method sayHelloWorld(int)
jshell>
Code inside a method can be any valid Java code! For instance, you could write code for
iteration, such as a for loop.
In the above example, we printed numOfTimes a total of numOfTimes for each method call.
jshell> sayHelloWorld(2)
2
2
jshell> sayHelloWorld(4)
4
4
4
4
jshell>
We wanted to print "Hello World" multiple times. Let's update the method:
jshell> sayHelloWorld(1)
Hello World
jshell> sayHelloWorld(2)
Hello World
Hello World
jshell> sayHelloWorld(4)
Hello World
Hello World
Hello World
Hello World
jshell>
You can now proudly demonstrate this code to your Java instructor. Your program can print
"Hello World" an arbitrary number of times!
What started off giving you a headache, will probably keep you in her good books, for the rest
of your course!
Armed with this confidence booster, let's now see how Java treats mistakes you may make. .
Java is a strongly typed language, with strict rules laid out for type compatibility. We saw that
with variables, and how they play out with expressions and assignments. The same type
compatibility rules are enforced by the compiler, when it needs to match the arguments from
method calls with method definition.
jshell> sayHelloWorld("value")
| Error:
| incompatible types: java.lang.String cannot be converted to int
| sayHelloWorld("value")
| ^-----^
jshell> sayHelloWorld(4.5)
| Error:
| incompatible types: possibly lossy conversion from double to int
| sayHelloWorld(4.5)
| ^-^
jshell>
Summary
• Understood why Java supports method arguments, and how we may use them
• Observed how method arguments lead to convenience and reuse
• Decided to abide by type compatibility for actual arguments
Exercises
Solution-01
jshell>
Solution 2
Can we top-up the existing solution to the PMT-Challenge, with even more elegance? You're
right, we're talking about:
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
void printMultiplicationTable() {
for (int i=1; i<=10; i++) {
System.out.printf("%d * %d = %d", 5, i, 5*i).println();
}
}
jshell> printMultiplicationTable()
5 * 1 = 5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
jshell>
Summary
We can now print the multiplication table of any number, by calling the method
printMultiplicationTable(number) .
jshell> printMultiplicationTable(6)
6 * 1 = 6
6 * 2 = 12
6 * 3 = 18
6 * 4 = 24
6 * 5 = 30
6 * 6 = 36
6 * 7 = 42
6 * 8 = 48
6 * 9 = 54
6 * 10 = 60
jshell>
Nothing new to explain here. We have only combined stuff you have been learning so far, most
recently adding the flavor of methods to the existing code.
Overloaded Methods
It turns out we can call both versions of the method, printMultiplicationTable() and
printMultiplicationTable(5) :
jshell> printMultiplicationTable()
5 * 1 = 5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
jshell>
and
jshell> printMultiplicationTable(5)
5 * 1 = 5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
jshell>
You can have multiple methods with same name but different number of parameters. This is
called method overloading.
Summary
We have already seen how to call two in-built Java methods, Math.max and Math.min , which
accept 2 arguments each.
Time now to enrich our understanding, by writing one such method ourselves as well.
A method void sum(int, int) that computes the sum of two integers, and prints their output.
A method void sum(int, int, int) that computes the sum of three integers, and prints their
output.
There are overloaded methods. They have same name sum and have different number of
arguments.
Summary
A lot of built-in Java methods have return values, the most notable we have seen being
Math.min() and Math.max() .
The next example explains how you can collect a method's return value.
We could define our own method that returns a value, in a similar manner. In the method
sumOfTwoNumbers above, instead of displaying sum at once, we could return it to the calling-
code.
$2 => 11
When the code sum = sumOfTwoNumbers(1, 10) is run, sum collects the result returned by
sumOfTwoNumbers() . You could now print sum on the console, store it in another variable, or
even invoke a different method by passing it as an argument!
Summary
Exercises
Solution-01
Solution-02
• We understood that methods are routines that perform a unit of computation. They group a
set of statements together into a block, and need to be run by being invoked, through a
method call.
• We can define methods that accept no arguments, a single argument and multiple
arguments.
• A method can also be defined to return results of its computation, and this makes it your
program more flexible.
When we wrote our code in JShell, how was it converted to binary code?
We write our programs in a high-level language, such as Java, as it is easier to learn, remember
and maintain.
Typically, Compiler is a program which understands the syntax of your programming language
and converts it into binary code.
Java designers wanted it to be Platform independent. Compile the code once and run it
anywhere.
However, different Operating Systems have different instruction sets - different binary code.
The Java compiler translates Java source code to bytecode, which is stored as a .class file on
the computer.
Summary
A Country is a concept. India, USA and Netherlands are examples or instances of Country.
We will discuss more about class and object in the section on Object Oriented
Programming.
india , usa and netherlands are all different objects of type Country .
A class does often include both data (member variables) and method definitions. More on this
at a relevant time.
Planet is now a different template. planet , earth and venus are instances of Planet
class.
Summary
The class template got updated as a result of adding new method. Earlier instances
planet , earth and venus got reset to null as they are based on the old template
jshell> Planet.revolve();
| Error:
| non-static method revolve() cannot be referenced from a static context
| Planet.revolve();
|^-----------------^
jshell> earth.revolve();
Revolve
jshell> venus.revolve();
Revolve
jshell>
Our attempt to perform revolve() did not work with the syntax Planet.revolve() as it is not
a static method (more on that in a later section).
Summary
Step 04: Writing and Running Java Code in separate Source Files
So far, we have enjoyed creating classes and defining methods within them, all with our friendly
neighborhood JShell .
JShell kind of spoiled us kids, by relaxing some Java syntax rules, invoking the compiler on
our behalf, and also running the code like room service.
The time has come for us frog princes to outgrow the proverbial well. Yes, you're right! Time to
start writing code in a proper code editor, and smell the greener grass on that side.
Let's start with creating a new source code file - 'Planet.java'. Choose any folder on your hard-
disk and create the file shown below:
Planet.java
class Planet {
void revolve() {
System.out.println("Revolve");
}
}
You can get the code that we wrote earlier in JShell for Planet by running the command
/list Planet , and copying the code.
You can use any text editor of your choice, for now.
The source file name must match the class name it contains. Here, we must name the file
Planet.java .
The next course of action would be to compile this code (Stage 2). For that, exit out of JShell ,
and run back to your system terminal or command prompt. Now, see what plays out!
jshell> /exit
| Goodbye
in28minutes$> cd in28Minutes/git/JavaForBeginners/
command-prompt> ls
Planet.java
You can compile java code using javac Planet.java . You can see that a new file is created
Planet.class . This contains your bytecode .
Summary
Console Commands
command-prompt> ls
Planet.class Planet.java
command-prompt> java Planet
Error: Main method not found inside class Planet, please define the main method as
public static void main(String[] args)
or a JavaFX application class must extend javax.application.Application
command-prompt>
The code may have compiled without any errors, but what's the use if we cannot run the
program to see what stuff it's got!
The main() method is essential to run code defined within any class .
A few details:
Let's add one such definition within the Planet code. Open up your text editor and add the
main method as shown below.
Planet.java
class Planet {
void revolve() {
System.out.println("Revolve");
}
public static void main(String[] args) {
}
}
After changing your Java source file, you need to compile the code again to create a new class
file.
Planet.java
class Planet {
void revolve() {
System.out.println("Revolve");
}
public static void main(String[] args) {
Planet earth = new Planet();
earth.revolve();
}
}
Console Commands
command-prompt> ls
Planet.class Planet.java
command-prompt> java Planet
Revolve
command-prompt>
Summary
Below is the source code for Planet that worked so well for us:
Planet.java
class Planet {
void revolve() {
System.out.println("Revolve");
}
Planet.java
class Planet {
void revolve() {
System.out.println("Revolve");
}
Console Commands
Remove semicolons after statements Planet earth = new Planet() and earth.revolve() .
Planet.java
class Planet {
void revolve() {
System.out.println("Revolve");
}
Console Commands
It is not needed in JShell but mandatory when you write separate Java source files.
Planet.java
class Planet {
void revolve() {
System.out.println("Revolve");
}
Console Commands
We misspelled Planet as Plane , and the compiler trapped this error. It shows us a red flag
with a message to help us correct our mistake.
Planet.java
class Planet {
void revolve() {
System.out.println("Revolve");
}
Console Commands
earth.revolve1();
|_____^
symbol: method revolve1()
location: variable earth of type Planet
1 error
command-prompt>
We misspelled the name of the revolve() method, to revolve1() . Rightly, got rapped on our
knuckles by the compiler.
Summary
• Learned that the best way to learn about language features is to play around with working
code
• Observed that JShell simplifies coding for us, even relaxing some syntax rules
• Concluded that developing Java code in code-editors is an empowering feeling!
Apart from the fact that all start with a 'J', there are important differences in what they contain,
and what they do.
• Libraries are built-in Java utilities that can be used within any program you create.
System.out.println() was a method in java.lang , one such utility.
• Other Components include tools for debugging and code profiling (for memory
management and performance).
• JDK refers to the Java Development Kit. It's an acronym for the bundle needed to compile
(with the compiler) and run (with the JRE bundle) your Java program.
Scenario 1: You give Planet.class file to a friend using a different operating system. What does
he need to do to run it?
First, install JRE for his operating system. Run using java Planet on the terminal. He does not
need to run javac, as he already has the bytecode.
Scenario 2: You gave your friend the source file Planet.java. What does he need to do to run it?
Install JDK of a compatible version. Compile code using javac Planet.java. Run java Planet on
his terminal.
In summary
Summary
• Understood the differences in the scope-of-work done by the JVM, JRE and JDK
• Realized how each of them could be invoked by different terminal commands, such as
javac and java.
Eclipse
TODO - Need Revision
◦ Package name
◦ Public method stub : can be used to create a default public static void
main(String[] args) signature
◦ (Include Snapshots)
▪ Syntax Highlighting
▪ Keywords
▪ Built-in Types
▪ Constant literals: numbers, strings
▪ Auto-suggest feature of eclipse as code is being typed in
◦ The "Run as --> Java Application" Option to compile-and-run the source code
Execution of the Multiplication Table Program can be done Step by-step. That is, the entire
application dies not execute at one go, printing the final output onto the console. We can stall
its flow at several steps, by taking control of its execution using the Eclipse IDE. This is possible
in a special mode of Eclipse, called Debug Mode.
The process is called Debugging, because this mode is heavily used not just to have fun seeing
what happens, but also to detect and fix software bugs. A bug is a defect in the program being
written, that leads to its incorrect execution. The process of removing bugs is called
debugging. Humans being humans, programming errors are but natural, and a debug mode in
the IDE is worth its wight in gold to the programmer. Here is how Eclipse facilitates debugging
of Java applications.
• The overall state of the data in the suspended program is available to the programmer at
that particular break-point. He/she gets to specify one or more break-points in the
program, and when the execution is suspended, a list of possible actions can be
performed, including:
and others.
• The Eclipse IDE provides a very user-friendly and intuitive interface to the programmer for
controlling execution of a program in Debug Mode (Provide Snapshots of IDE at various
stages of Debug Mode execution).
jshell> int i = 2
$1 ==> 2
jshell> int i = 4
$2 ==> 4
jshell> long i = 1000
$3 ==> 1000
jshell>
When the following code (similar to the one given to JShell) is compiled manually or by IDEs
such as Eclipse, it will flag errors!
package com.in28minutes.firstjavaproject;
Let's have another look at where we have reached with our solution, to the PMT-Challenge
problem. Only now, let's change the code arrangement.
MultiplicationTable.java
package com.in28minutes.firstjavaproject;
public class MultiplicationTable {
public static void print() {
for(int i=1; i<=10;i++) {
System.out.printf("%d * %d = %d", 5, i, 5*i).println();
}
}
}
MultiplicationRunner.java
package com.in28minutes.firstjavaproject;
Console Output
5*1=5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
Snippet-01 Explained
Print-Multiplication-Table: Enhancements
• We now want to enhance our current solution to this challenge, even though we've come a
long way. "Once you get the whiff of success, sky is the limit!". Here is the changes we
need:
◦ Pass the number whose table needs to be printed, as an argument
◦ Pass the (continuous) range of numbers, whose index entries in the table are to be
printed. (For example, printing the table entries for 6 , with entries for indexes
between 15 to 30 ).
One way to achieve all this, would involve overloading the print() method. The next example
is one such attempt.
Overloading print() works for us, and we now support three ways in which to display any
table:
Console Output
5*1=5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
Console Output
8*1=8
8 * 2 = 16
8 * 3 = 24
8 * 4 = 32
8 * 5 = 40
8 * 6 = 48
8 * 7 = 56
8 * 8 = 64
8 * 9 = 72
8 * 10 = 80
Printing a table for any number, but with entries fixed between 1 and 1 .
Console Output
6 * 11 = 66
6 * 12 = 72
6 * 13 = 78
6 * 14 = 84
6 * 15 = 90
6 * 16 = 96
6 * 17 = 102
6 * 18 = 108
6 * 19 = 114
6 * 20 = 120
Displaying a table for any number, with entries for any range
MultiplicationTable.java
package com.in28minutes.firstjavaproject;
MultiplicationRunner.java
package com.in28minutes.firstjavaproject;
There is an issue with the code for class MultiplicationTable . In the final print format, what
if we are asked to replace the multiplication symbol ' * ' with an ' X ', so that school kids like it
better? Since there are three calls to System.out.printf() , one in each print() version, we
make three changes.
MultiplicationTable.java
package com.in28minutes.firstjavaproject;
Console Output
5X1=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
8*1=8
8 * 2 = 16
8 * 3 = 24
8 * 4 = 32
8 * 5 = 40
8 * 6 = 48
8 * 7 = 56
8 * 8 = 64
8 * 9 = 72
8 * 10 = 80
6 X 11 = 66
6 X 12 = 72
6 X 13 = 78
6 X 14 = 84
6 X 15 = 90
6 X 16 = 96
6 X 17 = 102
6 X 18 = 108
6 X 19 = 114
6 X 20 = 120
Snippet-3 Explained
• Humans make mistakes. The programmer missed out on changing the format symbol in the
code for void print(int, int) , but we don't recommend punishment. He could be
overworked, or could only be the maintainer of code someone else wrote years ago! The
point is not to blame human flaws (there are many), but to show how code duplication
causes errors. This issue arises frequently with overloaded methods, point blank.
• Instead of trying to change human nature, can we change our software? Let's have a closer
look at print() :
◦ The method void print() prints the multiplication table for 5 only, in the default
range 1 to 10 .
◦ The method void print(int) outputs the table for any number, but only in the fixed
range from 1 to 10 .
◦ The method void print(int,int,int) displays the table for any number, in any range
of entries.
There is something huge we observe in the previous example. All overloaded versions of
print() have nearly the same code!
• print(int) has wider usage potential than print() . The latter is a special case, as it
prints only the for 5 . We can achieve what print() does, by passing a fixed parameter
value of 5 to print(int) . It's simple: invoke print(int) within print() , by passing 5
to it.
• The point to note is, that a more specialized function can be implemented-in-terms-of a
more general function.
MultiplicationTable.java
MultiplicationRunner.java
package com.in28minutes.firstjavaproject;
Console Output
5*1=5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
8*1=8
8 * 2 = 16
8 * 3 = 24
8 * 4 = 32
8 * 5 = 40
8 * 6 = 48
8 * 7 = 56
8 * 8 = 64
8 * 9 = 72
8 * 10 = 80
Snippet-4 Explained
When we call a method inside another, the method call statement is replaced by its body (with
actual arguments replacing the formal ones). In the new definition of int print() above, the
code executed during the call will be:
MultiplicationTable.java
MultiplicationRunner.java
package com.in28minutes.firstjavaproject;
Console Output
8*1=8
8 * 2 = 16
8 * 3 = 24
8 * 4 = 32
8 * 5 = 40
8 * 6 = 48
8 * 7 = 56
8 * 8 = 64
8 * 9 = 72
8 * 10 = 80
6 * 11 = 66
6 * 12 = 72
6 * 13 = 78
6 * 14 = 84
6 * 15 = 90
6 * 16 = 96
6 * 17 = 102
6 * 18 = 108
6 * 19 = 114
6 * 20 = 120
Snippet-5 Explained
• This example merely extended what we did in the previous example. We will will take this
extension one level further now! Yes, you guessed right. We will implement print() in
terms of print(int,int,int) .
MultiplicationTable.java
MultiplicationRunner.java
package com.in28minutes.firstjavaproject;
Console Output
5*1=5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
6 * 11 = 66
6 * 12 = 72
6 * 13 = 78
6 * 14 = 84
6 * 15 = 90
6 * 16 = 96
6 * 17 = 102
6 * 18 = 108
6 * 19 = 114
6 * 20 = 120
Snippet-6 Explained
• By extending the same logic of code reuse, the method int print(int,int,int) can be
used to implement the logic of int print() . Just pass in a number parameter 5 , as well
as the fixed range parameters 1 and 10 , in a call to the former. int print() is thus
implemented-in-terms-of int print(int,int,int) .
MultiplicationTable.java
package com.in28minutes.firstjavaproject;
MultiplicationRunner.java:
package com.in28minutes.firstjavaproject;
Console Output
5*1=5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
5 * 10 = 50
8*1=8
8 * 2 = 16
8 * 3 = 24
8 * 4 = 32
8 * 5 = 40
8 * 6 = 48
8 * 7 = 56
8 * 8 = 64
8 * 9 = 72
8 * 10 = 80
6 * 11 = 66
6 * 12 = 72
6 * 13 = 78
6 * 14 = 84
6 * 15 = 90
6 * 16 = 96
6 * 17 = 102
6 * 18 = 108
6 * 19 = 114
6 * 20 = 120
Snippet-7 Explained
Neat, isn't it! To make our program school kid friendly, we just need to change one character in
the code, take a peek below.
MultiplicationTable.java:
package com.in28minutes.firstjavaproject;
Console Output
5X1=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
8X1=8
8 X 2 = 16
8 X 3 = 24
8 X 4 = 32
8 X 5 = 40
8 X 6 = 48
8 X 7 = 56
8 X 8 = 64
8 X 9 = 72
8 X 10 = 80
6 X 11 = 66
6 X 12 = 72
6 X 13 = 78
6 X 14 = 84
6 X 15 = 90
6 X 16 = 96
6 X 17 = 102
6 X 18 = 108
6 X 19 = 114
6 X 20 = 120
Snippet-8 Explained
Software Development is an iterative process. The best code that we want to write does not
happen at one go. It starts off at a certain level, and can always be improved. More importantly,
such improvements needs to be remembered, to be applied again at different points in the
same program, and across programs. This process is called Code Refactoring. Thought we'd
keep you posted.
Summary
Recommended Videos:
Let's say I've to take a flight from London to New York. This is how I would think:
Procedural programming is just a reflection of this thought process. A procedural program for
above process would look something like this:
takeACabToLondonAirport();
checkIn();
passSecurity();
boardPlane();
wishHostess();
takeOff();
cruiseMode();
land();
getOffPlane();
//...
Object Oriented Programming (OOP) brings in a new thought process around this.
How about thinking in terms of the different Actors? How about storing data related to each
actor right beside itself? How about giving them some responsiblity and let them do their own
actions?
Here's how our program would look like when we think in terms of different actors and give
them data and responsibilities
Person
name
boardFlight(Plane flight), wishHostess (Hostess hostess), getOffFlight(Plane flight)
AirPlane
altitude, pilot, speed, flightMode
takeOff(), cruiseMode(), land()
Hostess
welcome()
Do not worry about the implementation details. Focus on the difference in approaches.
We have encapsulated data and methods into these entities, which are now called objects. We
have defined object boundaries, and what it can (and cannot) do.
An object has
The position of an Airplane can change over time. The operations that can be performed on
an Airplane include takeOff() , land() and cruiseMode() . Each of these actions can
change its position . Therefore, an object's behavior can affects its own state.
It's now time to introduce you to some core OOP terms, which will make our future discussions
easier.
OOP Terminology
Let's visit and enhance the Planet example we had written a few sections ago. This time, let's
also explore the conceptual angle.
Planet
class Planet
name, location, distanceFromSun // data / state / fields
rotate(), revolve() // actions / behavior / methods
Fields are the elements that make up the object state. Object behavior is implemented through
Methods.
Summary
Exercises
In each of the following systems, identify the basic entities involved, and organize them using
object oriented terminology:
Customer
name, address
login(), logout(), selectProduct(Product)
ShoppingCart
items
addItem(), removeItem()
Product
name, price, quantityAvailable
order(), changePrice()
Solution-2: Person
Person
name, address, hobbies, work
walk(), run(), sleep(), eat(), drink()
In this series of examples, we want to model your pet mode of transport, a motorbike. We want
to create motorbike objects and play around with them.
• MotorBike.java, which contains the MotorBike class definition. This class will
encapsulate our motorbike state and behavior
• MotorBikeRunner.java, with class MotorBikeRunner holding a main method, our
program's entry point
MotorBike.java
package com.in28minutes.oops;
public class MotorBike {
//behavior
void start() {
System.out.println("Bike started!");
}
}
MotorBikeRunner.java
package com.in28minutes.oops;
Console Output
Bike started!
Bike started!
Snippet-1 Explained
We started off creating a simple MotorBike class with a start method. We created a couple
of instances and invoked the start method on them.
Summary
• Defined a MotorBike class allowing us to further explore OOP concepts in the next steps
Exercises
1. Write a small Java program to create a Book class , and then create instances to
represent the following book titles:
◦ "The Art Of Computer Programming"
◦ "Effective Java"
◦ "Clean Code"
Solution
Book.java
BookRunner.java
Console Output
Effective Java
Clean Code
State defines "the condition of the object at a given time". State is represented by member
variables.
In the MotorBike example, if we need a speed attribute for each MotorBike , here is how we
would include it.
MotorBike.java
package com.in28minutes.oops;
void start() {
System.out.println("Bike started!");
}
}
MotorBikeRunner.java
package com.in28minutes.oops;
ducati.speed = 100;
honda.speed = 80;
ducati.speed = 20;
honda.speed = 0;
}
}
Console Output
Bike started!
Bike started!
Snippet-4 Explained
It can be accessed within objects such as ducati and honda , by qualifying it with the object
name ( ducati or honda ).
ducati.speed = 100;
honda.speed = 80;
ducati has its own value for speed , and so does honda . These values are independent of
each other. Changing one does not affect the other.
1. Update the Book class created previously to include a member variable named
noOfCopies , and demonstrate how it can be set and updated independently for each of
the three titles specified earlier.
Solution
TODO
This did work fine, but it breaks the fundamental principle of encapsulation in OOP.
"A method of one object, should not be allowed to directly access the state of another object.
To do so, such an object should only be allowed to invoke methods on the target object".
In other words, a member variable should not be directly accessible from methods declared
outside its class .
MotorBike.java
package com.in28minutes.oops;
void start() {
System.out.println("Bike started!");
}
MotorBikeRunner.java
package com.in28minutes.oops;
ducati.setSpeed(100);
honda.setSpeed(80);
ducati.setSpeed(20);
honda.setSpeed(0);
}
}
Console Output
Bike started!
Bike started!
Snippet-3 Explained
By declaring speed as private , we provide MotorBike with something called access control.
Java keywords such as public and private are called access modifiers. They control what
external objects can access within a given object.
Code written earlier within MotorBikeRunner , such as ducati.speed = 100; would now result
in errors! The correct way to access and modify speed is to invoke appropriate methods such
as setSpeed() , on MotorBike objects.
1. Update the class Book to make sure it no longer breaks Encapsulation principles.
Solution
TODO
Summary
MotorBike.java
package com.in28minutes.oops;
int getSpeed() {
return this.speed;
}
}
MotorBikeRunner.java
package com.in28minutes.oops;
ducati.setSpeed(100);
honda.setSpeed(80);
System.out.printf("Current Ducati Speed is : %d", ducati.getSpeed()).println();
System.out.printf("Current Honda Speed is : %d", honda.getSpeed()).println();
}
}
Console Output
Bike started!
Bike started!
Snippet-4 Explained
Defining a method such as getSpeed() allows us to access the current speed of a MotorBike
object.
int getSpeed() {
return this.speed;
}
Eclipse has a very handy feature. When the state elements (member variables) of a class
have been defined, it can generate default get() and set() methods for each of them. You
would want to use this regularly, to save on time and typing effort. Right click on class
> Generate Source > Generate Getters and Setters
Summary
MotorBike.java
//Same as before
MotorBikeRunner.java
package com.in28minutes.oops;
Console Output
Snippet-6 Explained
When we instantiate an object, all its state elements are always initialized, to the default values
of their types. Inside MotorBike , speed is declared to be an int , and so is initialized to 0 .
This happens even before any method of MotorBike is called, including start() .
You can see that honda.getSpeed() printed 0 even though we did not explictly initialize it.
Default
Summary
MotorBike.java
package com.in28minutes.oops;
MotorBikeRunner.java
package com.in28minutes.oops;
Console Output
Bike started!
Snippet-01 Explained
For a motorbike, -100 might be an invalid speed. Currently, we do not have any validation.
Having a method setSpeed allows us to add validation.
MotorBike.java
package com.in28minutes.oops;
//Other code as is
MotorBikeRunner.java
//Same as before
Bike started!
Snippet-8 Explained
setSpeed() checks if(speed > 0) and does not update speed for negative values.
This is not perfect solution because the caller of the setSpeed method assumes that he
was successful. Ideally, we should throw an Exception indicating validation error. We will
talk about Exceptions later.
Summary
• Explored the first advantage of encapsulation - A provision for adding data validation
• Highlighted how such validation can be done, using the Motorbike example
Suppose at different points of time, we want to increase the speeds of both Honda and Ducati
bikes by a fixed amount, say 100 mph . The logic would be simple, right? Fetch the current
speed of each bike, increment that fetched value by 100 , and then set the new value back
into that bike's speed . The following example puts the above logic into action.
MotorBike.java
// No Change
MotorBikeRunner.java
package com.in28minutes.oops;
Bike started!
Bike started!
Snippet-1 Explained
Notice the repeated code within MotorBikeRunner ? The code for updating the speed of
ducati , is almost the same as that for honda . Remember at the start of this book, we said
something like this:
"The goal of any computer program is to make a task easier, less cumbersome and more
elegant for the programmer."
OOP achieves all thus through encapsulation! The idea is to encapsulate repeated logic within a
method, and pass object-specific information to it as arguments. The next example shows you
one way of doing it.
MotorBike.java
package com.in28minutes.oops;
MotorBikeRunner.java
package com.in28minutes.oops;
ducati.setSpeed(100);
ducati.increaseSpeed(100);
honda.increaseSpeed(100);
Bike started!
Bike started!
Snippet-2 Explained
The method increaseSpeed() has been added to MotorBike . It can be invoked on the ducati
and honda objects.
MotorBike.java
package com.in28minutes.oops;
MotorBikeRunner.java
package com.in28minutes.oops;
ducati.setSpeed(100);
System.out.printf("Earlier Ducati Speed is : %d", ducati.getSpeed()).println();
System.out.printf("Earlier Honda Speed is : %d", honda.getSpeed()).println();
ducati.increaseSpeed(100);
honda.increaseSpeed(100);
ducati.decreaseSpeed(50);
honda.decreaseSpeed(50);
ducati.decreaseSpeed(200);
honda.decreaseSpeed(200);
Bike started!
Bike started!
Snippet-3 Explained
The method decreaseSpeed() has been added to MotorBike . It can be invoked on the ducati
and honda objects inside the main() method of MotorBikeRunner .
On of the things you can observe again is Negative Speed Values. Our validation needs to be
improved.
MotorBike.java
package com.in28minutes.oops;
//Same as before
MotorBikeRunner.java
//Same as before
Bike started!
Bike started!
Snippet-4 Explained
We have achieved data validation, because attempts to decrease the speed of ducati and
honda below 0 are now ignored.
MotorBike.java
package com.in28minutes.oops;
MotorBikeRunner.java
//Same as before
Bike started!
Bike started!
Snippet-5 Explained
The idea behind this solution is, that an update is the same as a set() operation. Since
setSpeed() already has a validation check, it can be called inside both increaseSpeed() and
decreaseSpeed() with appropriate parameters.
In this way, the validation logic would be reused across update methods.
Summary
Exercises
1. Use an encapsulation technique to write methods for the Book class , that
◦ Increase the number of books
◦ Decrease the number of books
Solution
BookRunner.java
System.out.println(taocp.getTitle());
System.out.println(ej.getTitle());
System.out.println(cc.getTitle());
taocp.increaseCopies(10);
ej.increaseCopies(15);
cc.increaseCopies(20);
taocp.decreaseCopies(5);
ej.decreaseCopies(10);
cc.decreaseCopies(15);
System.out.println(taocp.getNumberOfCopies());
System.out.println(ej.getNumberOfCopies());
System.out.println(cc.getNumberOfCopies());
}
}
Book.java
Console Output
Effective Java
Clean Code
Suppose our whim is that a Ducati bike starts with 100 mph, and a Honda with 200 mph.
MotorBike.java
package com.in28minutes.oops;
MotorBike(int speed) {
if(speed > 0)
this.speed = speed;
}
//Same as before
MotorBikeRunner.java
package com.in28minutes.oops;
Bike started!
Bike started!
Snippet-1 Explained
We defined a single-argument constructor for MotorBike , whosw definition looks like this:
The constructor is a method, whose name is the same as the class name. All Java rules for a
method apply to constructors as well. Constructor cannot be directly called.
A constructor is always invoked when a class object is created, using the new keyword. A
constructor for a class could accept zero, one or more than one arguments. Let's next write
some full-blooded code for a MotorBike constructor.
Summary
Exercises
1. Rewrite the Book class solution by using a constructor, which accepts an integer
argument specifying the initial number of copies to be published:
◦ "The Art Of Computer Programming" : 100 copies
◦ "Effective Java" : 75 copies
◦ "Clean Code" : 60 copies
Solution To PE-OO-04
BookRunner.java
System.out.println(taocp.getTitle());
System.out.println(taocp.getNumberOfCopies());
System.out.println(ej.getTitle());`
System.out.println(ej.getNumberOfCopies());
System.out.println(cc.getTitle());
System.out.println(cc.getNumberOfCopies());
}
}
Book.java
Console Output
100
Effective Java
75
Clean Code
60
More on Constructors
We enjoyed defining the MotorBike class and checking out the behavior of its instances,
ducati and honda .
Did you notice something familiar? Doesn't the expression new MotorBike() look like a
constructor call?
When we define a class in Java (even a seemingly empty one), some behind-the-scene magic
happens. Consider one such class Cart :
class Cart {
};
This class has neither state, nor behavior. When we try to create instances of this "empty"
class :
class CartRunner {
public static void main(String[] args) {
Cart cart = new Cart();
}
}
The code seems to compile, execute and get initialized quite smoothly! What happens is, that
the compiler silently generates a default constructor for Cart .
A default constructor is one that accepts no arguments. It is as if the Cart class were
defined this way:
class Cart {
public Cart() {
}
};
Let's now try to create MotorBike instances with default initialization, just as we did with
Cart .
MotorBike.java
package com.in28minutes.oops;
MotorBike(int speed) {
if(speed > 0)
this.speed = speed;
}
}
MotorBikeRunner.java
package com.in28minutes.oops;
Console Output
Compiler Error
Snippet-2 Explained
No default constructor is generated here! If you provide any constructor definition in a class,
the compiler will not add a default constructor. Do them all yourself, if you don’t like what I do
for you! is what it yells back.
If you need the default constructor, you can explicitly add it.
MotorBike.java
package com.in28minutes.oops;
//behavior
MotorBike() {
this(5);
}
MotorBike(int speed) {
if(speed > 0)
this.speed = speed;
}
MotorBikeRunner.java
package com.in28minutes.oops;
Snippet-3 Explained
We have called the constructor MotorBike(int) , qualified with the this keyword, within
Motorbike() .
We used literal values, declared variables and formed expressions using them.
• Integer Types
◦ byte
◦ short
◦ int
◦ long
• Floating-Point Types
◦ float
◦ double
• Character Type
◦ char
• Logical Type
◦ boolean
In this section, let's play with each of these types to understand them further.
Java supports them with ease, and we have coded quite a few examples using them, already.
Java also has a wrapper class corresponding to each of them, which make their primitive
versions more versatile. The wrapper classes we are talking about are:
Look at all the information these tiny classes hold for you! As they say, "fore-warned is fore-
armed". Depending on the data (range and size) your program handles, you decide which
types to use, to store them.
jshell> Byte.SIZE
$1 ==> 8
jshell> Byte.BYTES
$2 ==> 1
jshell> Byte.MAX_VALUE
$3 ==> 127
jshell> Byte.MIN_VALUE
$4 ==> -128
jshell> Short.BYTES
$5 ==> 2
jshell> Integer.BYTES
$6 ==> 4
jshell> Long.BYTES
$7 ==> 8
jshell> Short.MAX_VALUE
$8 ==> 32767
jshell> Integer.MAX_VALUE
$8 ==> 2147483647
jshell> int i = 100000;
i ==> 100000
jshell> long l = 50000000000;
| Error:
| integer number too large: 50000000000
| long l = 50000000000;
|_________^
jshell> long l = 50000000000l;
l ==> 50000000000
jshell>
Problems will (and should) arise if we attempt to store large data into smaller bins. The
compiler warns the programmer about such issues by flagging errors. However, if the
programmer is aware of the risks and intends to go ahead, explicit casts are her tools to push
the code through.
Operations in the other direction (storing a smaller data value in a larger bin), is a piece of cake
for the compiler. Such a conversion is called an implicit cast.
Attempting to store a long value into an int variable will give us a compiler error. However,
you can use an explicit cast, such as i = (int) l; .
jshell> l = i;
l ==> -1539607552
jshell>
The compiler is not responsible for the type-safety of this statement. The onus is on me,
the programmer.
Remember our earlier statement on possible incorrect program behavior? As you can see, a
different value got stored in i .
Summary
• Explored the wrapper classes present for the primitive integer types
• Understood the different capacities and data ranges of these types
• Examined how to use explicit and implicit casts
Those familiar with number systems would know that decimal is not the only system that
computers understand.
Other number systems that the Java language supports are Octal (Base-8) and Hexadecimal
(Base-16).
In an Octal system, the allowed digits are 0 through 7 , and a value 8 is represented by
" 010 ". The leading 0 is added only to distinguish the octal format from the decimal one.
In a Hexadecimal system, the allowed digits are 0 through 9 , followed by a through f (or
A through F ). A value of 16 is represented by " 0x10 ". Here, a leading 0x is added to help
the compiler recognize Hexa decimal representation.
There are no number-system specific integer types in Java! The same int type is used to
store decimal, octal and hexadecimal values.
If we adhere to to number system conventions about valid digits and understand the compiler
hints, we get no surprises.
• Literal-to-variable assignment: With short s = 123456; , the data is clearly out of range
(this is known at compile-time). The compiler flags an error.
• Variable-to-variable assignment: Consider sh = in; . The value stored in int in at that
stage was 4567 , which is well within the range of the short type. The compiler chooses
not to take chances and flags an error. This can again be preempted with a explicit cast
sh = (short) in .
• +
• -
• *
• /
• %
• ++ (both prefix and post-fix increment)
• -- (both prefix and post-fix increment)
The increment and decrement operators are an interesting case, as they are actually short-
hands for multiple statements. When we use their prefix and post-fix versions, we need to look
out for side-effects.
With post-fix increment, such as in int j = i++; , the increment takes place after the
assignment is done. j gets the value before increment.
When prefix increment is involved, as with int n = ++m; , the increment is carried out before
the assignment. n gets the value after increment.
With post-fix decrement, as with int l = k--; , the decrement occurs after the assignment is
done. As far as prefix decrement is concerned, such as in int q = --p; , the decrement is
performed before the assignment.
Summary:
Exercise Set
1. Create a Java class BiNumber that stores a pair of integers, and has the following
functionality:
In short, we must be able to write code like this in the main method of our runner class:
Solution to CE-01
BiNumber.java
package com.in28minutes.primitive.datatypes;
number2 *= 2;
}
BiNumberRunner.java
package com.in28minutes.primitive.datatypes;
Console Output
• double : The default type for floating-point literals, with a capacity of 8 bytes
• float : A narrower, less precise representation for floating-point data. It has a capacity of
4 bytes.
Default floating point type in Java is double . A float literal must be accompanied with a
trailing f or F .
jshell> dbl++
$3 ==> 34.5678
jshell> dbl
dbl ==> 35.5678
jshell> dbl--
dbl ==> 35.5678
jshell> dbl % 5
dbl ==> 4.567799999999998
You would need an explicit cast to convert a float to an integer value int i = (int)f .
Summary
In fact, they are not used in computations that require high degrees for accuracy, such as
scientific experiments and financial applications. The next example shows you why.
The literal expression 34.56789876 + 34.2234 should evaluate to 68.79129876 . Above addition
is not really accurate.
This is due to a flaw in floating-point representations, used in the double and float types.
The BigDecimal class was introduced in Java to tide over these problems.
Accuracy of BigDecimal representation is retained only when String literals are used to build
it.
A BigDecimal type can be used to create only immutable objects. The values in an immutable
object cannot be changed after creation.
You can see that the value of number1 is not changed on executing number1.add(number2) . To
get the result of the sum, we create a new variable sum .
jshell> number1.add(number2);
$2 ==> 68.79129876
jshell> number1
number1 ==> 34.56789876
jshell> BigDecimal sum = number1.add(number2);
sum ==> 68.79129876
Summary
• Learned that double and float are not very precise data types
• Were introduced to the BigDecimal built-in data type
• Understood that BigDecimal is immutable
• Saw that accuracy is best achieved when you construct BigDecimal data using string
literals
jshell> int i = 5;
i ==> 5
jshell> number1.add(i);
| Error:
| incompatible types: int cannot be converted to java.math.BigDecimal
| number1.add(i);
|_____________^
Summary
Exercise-Set
Write a Program that does a Simple Interest computation for a Principal amount. Recall that the
formula for such a calculation is:
Total amount (TA) = Principal Amount (PA) + ( PA * Simple Interest (SI) Rate *
Duration In Years (N))
Solution To CE-02
SimpleInterestCalculatorRunner.java
package com.in28minutes.primitive.datatypes;
import java.math.BigDecimal;
SimpleInterestCalculator.java
package com.in28minutes.primitive.datatypes;
import java.math.BigDecimal;
Console Output:
6187.50000
The Java import statement is Required in each source file that uses a class from another
package . Hence, both SimpleInterestCalculator.java ( class definition) and
SimpleInterestCalculatorRunner.java (runner class definition) need to import class
java.math.BigDecimal .
The .* suffix indicates that all classes in the package java.lang are being imported.
Summary
labels are case-sensitive. We have seen examples of built-in comparison operators, such as
== , != and > , that evaluate expressions to give us boolean results. Let us do a quick recap
of some of these.
Relational Operators are used mainly for comparison. They accept operands of non- boolean
primitive data types, and return a boolean value.
jshell> int i = 7;
i ==> 7
jshell> i > 7;
$1 ==> false
jshell> i >= 7;
$2 ==> true
jshell> i < 7;
$3 ==> false
jshell> i <= 7;
$4 ==> true
jshell> i == 7;
$5 ==> true
jshell> i == 8;
$6 ==> false
jshell>
Logical Operators
Java also has logical operators that are used in expressions. Logical operators expect boolean
operands. Expressions involving these are used for forming boolean conditions in code,
including within if , for and while statements. The prominent logical operators are:
We would want to find if i is between 15 and 25 . An expression i >= 15 && i <= 25 can be
used. Details below.
An expression with && evaluates to true only if both its boolean operands evaluate to true .
Snippet-02 Explained
The next example helps us visualize truth tables for the prominent logical operators.
Summary
Why? Because i++ > 5 was not even evaluated. Why? && is lazy. It saw that j > 15 is false.
Irrespective of the result of second expression, the result of this && would be false. So, it does
NOT evaluate the second expression.
The expression j > 15 && i++ > 5 was scanned from left to right. As the first operand to &&
evaluated to false , the compiler got lazy. && avoids evaluating expressions that don't affect
the final result. The optimization has a name: Short-Circuit Evaluation, also called lazy
evaluation.
The logical operator & is another version of the logical AND operation, that does away with
lazy evaluation.
It is bad programming practice for our code to depend on the compiler's lazy evaluation. It
makes code less readable, and can hide difficult-to-fix software bugs. It obviously adds to the
code maintenance burden, so don't do it unless you like being in your peers' bad books.
Summary
Turns out Java supports a much larger family of character encoding sets, called Unicode. All
Unicode characters can be input to, understood and processed by, as well as output from your
code.
Not all Unicode characters can be input from your keyboard. But Java allows you to handle their
values from your code, if you deal correctly with their format.
Integer values can be stored in char variables. If the value is within a meaningful range, it also
corresponds to the ascii value of a keyboard character.
jshell> cn++
$1 ==> 'A'
jshell> cn
$2 ==> 'B'
jshell> ++cn
$3 ==> 'C'
jshell> cn + 5
$4 ==> 72
jshell> cn
cn ==> 'C'
jshell> (int)cn
cn ==> 67
jshell>
Summary
Exercise Set
1. Write a Java class MyChar that is a special type of char . An object of type MyChar is
created round an input char data element, and has operations that do the following:
◦ Check if the input character is a:
▪ Numeric Digit
▪ Letter of the Alphabet
▪ Vowel (Either upper-case or lower-case)
▪ Consonant (Either upper-case or lower-case) NOTE: A letter is a consonant if not a
vowel
◦ Print all the letters of the Alphabet in
▪ Upper-Case
▪ Lower-Case
In Essence, a runner class for MyChar would have its main method run code similar to:
myChar.printLowerCaseAlphabets();
myChar.printUpperCaseAlphabets();
package com.in28minutes.primitive.datatypes;
MyChar.java
package com.in28minutes.primitive.datatypes;
Console Output :
false
package com.in28minutes.primitive.datatypes;
MyChar.java
package com.in28minutes.primitive.datatypes;
return false;
}
Console Output :
false
false
package com.in28minutes.primitive.datatypes;
MyChar.java
package com.in28minutes.primitive.datatypes;
Console Output :
false
true
false
true
• We first got familiar with built-in wrappers for the integer types, that store useful type
information.
• Going from the integer to the floating-point types, we examined type compatibility, and
how the compiler warns you about common pitfalls. We used explicit casts to force type
conversions, and learned that implicit type conversions are quite common.
• We moved on to the BigDecimal class, a floating-point type with greater precision and
accuracy.
• Next in line was boolean , where we built on what we know of logical expressions. We
focused on the logical operators, more so on short-circuit evaluation of their expressions.
• We saw how dependency on side-effects, and on lazy evaluation, is not a good
programming practice.
• Finally, we got to the char type, and were pleasantly surprised to know, that the keyboard
is not the limit.
• if
• if - else
• if - else if - else
• switch
What better way to master these basics, than using them to solve a programming challenge?
Here we go!
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
The Result Is : 8
Summary
if(condition) {
<if-body>
}
An if - else statement improves over the plain if . We can now choose between executing
two pieces of code, depending on what condition evaluates to.
• "Do Something when condition is true , something else when condition is false "
if(condition) {
<if-body>
} else {
<else-body>
}
Snippet-01 : if behavior
In this example:
• we have used compound comparison operators such as <= and >= , which also evaluate
to boolean values.
• Also used, are logical operators such as && and || , which both accept and return values
of type boolean .
jshell> int i = 3;
i ==> 3
jshell> if(i==3) {
..>> System.out.println("True");
..>> }
True
jshell> if(i<2) {
..>> System.out.println("True");
..>> }
jshell> if(i==3) {
..>> System.out.println("True");
..>> } else {
..>> System.out.println("i is not 3");
..>> }
True
jshell> i = 5;
i ==> 5
jshell> if(i==3) {
..>> System.out.println("True");
..>> } else {
..>> System.out.println("i is not 3");
..>> }
i is not 3
jshell>
IfStatementRunner.java
com.in28minutes.ifstatement.examples;
Console Output
i != 25
IfStatementRunner.java
com.in28minutes.ifstatement.examples;
int i=25;
if(i == 25) {
System.out.println("i = 25");
}
if(i == 24) {
System.out.println("i = 24");
} else {
System.out.println("i != 25 and i != 24");
}
}
}
Console Output
i = 25
i != 25 and i != 24
Snippet-03 Explained
What just happened here? The value of i is set to 25 within the program, so like in the
previous example, only i = 25 should have been printed.
We started off trying to check fro 3 possibilities: * i being equal to 25 * i being equal to
24 * Neither of the above
Our decision making is not continuous. We need a tighter conditional to deal with more than
two alternatives. We will explore this topic in the next step.
Summary
The if - else if - else statement solves the issue we faced, while trying to test more than
two conditions.
IfStatementRunner.java
com.in28minutes.ifstatement.examples;
int i=25;
if(i == 25) {
System.out.println("i = 25");
} else if(i == 24) {
System.out.println("i = 24");
} else {
System.out.println("i != 25 and i != 24");
}
}
}
Console Output
i = 25
IfStatementRunner.java
com.in28minutes.ifstatement.examples;
// if i is 25, print i = 25
//if i is 24, print i = 24
//otherwise, print i != 25 and i != 24
//int i=25;
int i=24;
if(i == 25) {
System.out.println("i = 25");
} else if(i == 24) {
System.out.println("i = 24");
} else {
System.out.println("i != 25 and i != 24");
}
}
}
Console Output
i = 24
Snippet-02 Explained
Let's now try to get a match with the else clause, the only one unexplored so far.
IfStatementRunner.java
com.in28minutes.ifstatement.examples;
//int i=24;
int i=26;
if(i == 25) {
System.out.println("i = 25");
} else if(i == 24) {
System.out.println("i = 24");
} else {
System.out.println("i != 25 and i != 24");
}
}
}
Console Output
i != 25 and i != 24
Snippet-03 Explained
When i is given a value of 26 , the first two conditions are false. Hence, the code in the else
gets executed.
one, and only one clause among those present in an if - else if - else statement ever
evaluates to true . Also, the code block corresponding to the matched clause will get
executed. This is ensured even if conditions are duplicated, or overlap. In that scenario, only
the first such condition, downward from the if in sequence, will evaluate to true . The
remaining possible matches are not even checked.
Summary
Answer:
Answer:
l < 20
Who Am I?
Answer:
jshell> int i = 0;
i ==> 3
jshell> if(i) {
..>> System.out.println("i");
..>> }
| Error:
| incompatible types: int cannot be converted to boolean
| if(i)
|____^
jshell> if(i=1) {
..>> System.out.println("i");
..>> }
| Error:
| incompatible types: int cannot be converted to boolean
| if(i=1)
|____^-^
jshell> if(i==1) {
..>> System.out.println("i");
..>> }
jshell>
Answer:
Compiler Error
**Answer : **
In the absence of explicit blocks, Only the statement next to the if statement is considered
to be part of the if statement block.
Menu-Challenge
We have a good idea about how the if - else if - else conditional works.What we don't
know, however, is how a such a conditional would behave when fed with random input. To test
those scenarios out, the next step would be to learn how to take console input, from within our
code.
Java provides a built-in class named Scanner , to scan user input from the console. Roping in
this utility would require you, the programmer, to do the following:
• import the java.util.Scanner class within your code (here, we were writing code in
the Eclipse IDE)
• Create a scanner object which is of type Scanner . This involves invoking the Scanner
constructor through the new operator. You also need to pass System.in as a constructor
parameter, which ties scanner to the console input.
• To read integer input from the console, call the method scanner.nextInt() . The
keyboard's key needs to be pressed to complete user input. That number is passed on to
your code, where it can be passed around.
MenuScanner.java
package com.in28minutes.ifstatement.examples;
import java.util.Scanner;
Console Output
Enter Number1: 35
Summary
• Revisited our Menu-Challenge problem statement, and found we needed to read user input
• Explored the basic usage of the Scanner utility to fulfill this need
MenuScanner.java
package com.in28minutes.ifstatement.examples;
import java.util.Scanner;
System.out.println("2 - Subtract");
System.out.println("3 - Multiply");
System.out.println("4 - Divide");
System.out.print("Enter Choice: ");
int choice = Scanner.nextInt();
Console Output
Enter Number1: 25
Enter Number2: 50
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
Enter Choice: 4
Number1: 25
Number2: 50
Choice: 4
Snippet-01 Explained
Repeatedly calling scanner.nextInt() will keep reading in any number the user may input.
Also, the user needs to press the keyboard key to send each input.
We were not only able to read in all three values we need, but also wrote them out on the
console. The user can now see that we got his data!
Summary
• Figured out how to read more than one input from the console
• Demonstrated how values read in, can be preserved and used within the program
Our solution above does a very simple thing. It combines the mechanisms we learned for
reading input and testing conditions, to solve Menu-Challenge.
• To check for 4 favorable conditions (The choice values for the 4 supported operations).
One if and three else - if clauses do the stuff for us.
• the last default condition, which corresponds to a choice value not supported, is handled
by an else clause.
MenuScanner.java
package com.in28minutes.ifstatement.examples;
import java.util.Scanner;
System.out.println("3 - Multiply");
System.out.println("4 - Divide");
System.out.print("Enter Choice: ");
int choice = Scanner.nextInt();
if(choice == 1) {
System.out.println("Result = " + (number1 + number2));
} else if(choice == 2) {
System.out.println("Result = " + (number1 - number2));
} else if(choice == 3) {
System.out.println("Result = " + (number1 * number2));
} else if(choice == 4) {
System.out.println("Result = " + (number1 / number2));
} else {
System.out.println("Invalid Operation");
}
}
}
Console Output
Enter Number1: 23
Enter Number2: 10
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
Enter Choice: 2
Number1: 23
Number2: 10
Choice: 2
Result = 13
Console Output
Enter Number1: 25
Enter Number2: 35
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
Enter Choice: 5
Number1: 23
Number2: 10
Choice: 5
Invalid Operation
Summary
• Combined our knowledge of conditionals, with our recent learning on taking multiple
console inputs
• Ultimately solved the Menu-Challenge problem
the default possibility. Conceptually, a switch statement looks like the following:
switch(condition) {
case 1:
//<statements>
break;
case 2:
//<statements>
break;
//...
default:
//<statements>
break;
}
}
break; statement is used to break out of the switch after a successful match.
Using a switch leads to code that is quite readable, doesn't it? Compare it with the chained
if - else if - else statements we used in the previous step.
Leaving out the break statement from every case clause is a very common error. It leads to a
situation called switch-fall-through. Here, even if there is a match with a particular case
clause, all clauses following this one are executed in sequence, until a break is encountered
somewhere!
jshell> int i = 5;
i ==> 5
jshell> switch(i) {
..>> case 1 : System.out.println("1");
..>> case 5 : System.out.println("5");
..>> default : System.out.println("default");
..>> }
5
default
jshell> i = 1;
i ==> 1
jshell> switch(i) {
..>> case 1 : System.out.println("1");
..>> case 5 : System.out.println("5");
..>> default : System.out.println("default");
..>> }
1
5
default
jshell> switch(i) {
..>> case 1 : System.out.println("1"); break;
..>> case 5 : System.out.println("5"); break;
..>> default : System.out.println("default"); break;
..>> }
1
jshell>
MenuScanner.java
package com.in28minutes.ifstatement.examples;
import java.util.Scanner;
Console Output
Enter Number1: 23
Enter Number2: 10
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
Enter Choice: 2
Number1: 23
Number2: 10
Choice: 2
Result = 13
Console Output
Enter Number1: 25
Enter Number2: 35
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
Enter Choice: 5
Number1: 23
Number2: 10
Choice: 5
Invalid Operation
Summary
Puzzles On switch
Let's now have some fun with the various conditionals. These puzzles will not only ensure
you're still wide awake, they will also give you ample food for thought.
Answer:
default
Answer:
Number is 2 or 3
break;
default:
System.out.println("default");
break;
}
}
}
Answer:
default
Answer:
default
Answer:
Compiler Error
Answer:
Compiler Error
Let's compare and contrast these two approaches, to gain some experience on how to choose
a conditional.
OperatorChoiceRunner.java
package com.in28minutes.ifstatement.examples;
OperatorChoice.java
package com.in28minutes.ifstatement.examples;
if(choice == 1) {
System.out.printf("result : %d", number1 + number2).println();
} else if(choice == 2) {
System.out.printf("result : %d", number1 - number2).println();
} else if(choice == 3) {
Console Output
number1 : 5
number2 : 2
choice : 1
result : 7
OperatorChoiceRunner.java
package com.in28minutes.ifstatement.examples;
OperatorChoice.java
package com.in28minutes.ifstatement.examples;
switch(choice) {
case 1: System.out.printf("result : %d", number1 + number2).println();
break;
case 2: System.out.printf("result : %d", number1 - number2).println();
break;
case 3: System.out.printf("result : %d", number1 * number2).println();
break;
case 4: System.out.printf("result : %d", number1 / number2).println();
break;
default: System.out.printf("result : %d", number1 + number2).println();
break;
}
}
}
Console Output
number1 : 5
number2 : 2
choice : 1
result : 7
Summary
• Observed that the same conditional code could be written using an if -family conditional,
or the switch .
• Learned that an if family conditional is difficult to get wrong, as the rules for it are very
strict. It can be used to evaluate only boolean conditions. But it is verbose, and often less
readable.
• Came to know that a switch conditional can be used to check for only integer values. It is
very compact, and very readable. However, the relative order of case clauses and
default are not fixed, and the usage of break is optional. This can lead to subtle errors in
your program.
Solution to PE-03
CalendarSwitchRunner.java
package com.in28minutes.ifstatement.examples;
The ternary operator ?: is a logical operator, that works on three operands. Its functioning is
similar to an if - else statement (Checking for just 2 conditions). Exactly one of the two
expressions is guaranteed to match.
Summary
Loops
TODO
//<Statements Body>
The <Statements Body> inside the loop is executed so long as the condition is true . Let's
look at a few puzzles to explore how we can utilize them.
__...>> }
0 1 2 3 4 5 6 7 8 9 10
__...>> }
0 2 4 6 8 10
__...>> }
13579
__...>> }
__...>> i++;
__...>> }
11 12 13 14 15 16 17 18 19 20
i ==> 20
__...>> }
21 22 23 24 25 26 27 28 29 30
jshell>
Snippet-1 Explained
• initialziation
• condition
• update
Reference Types
new Planet() creates an object on the Heap. In above example Planet@3f49dace , the object
is stored on the Heap at address 3f49dace .
• Creates an object on the Heap new Planet() . In above example, object is created at Heap
location 31a5c39e
• Stores the reference of the object on the Heap in a variable jupiter . In above example,
31a5c39e is the value stored in variable jupiter . That's the memory location where the
new object was created.
Two new Animal objects are created on the Heap . Their memory locations ( references ) are
stored in the reference variables - dog and cat .
In Java, all classes are also called Reference Types. Except for primitive variable instances, all
the instances or objects are stored on the Heap. The references to the objects are stored in the
reference variables like jupiter , dog and cat .
Summary
References that are not initialized by the programmer, are initialized by the Java compiler to
null . null is a special value that stands for an empty location. In other words, the Animal
nothing refers to nothing!
Assigning the reference cat to nothing does what one would expect: it assigns the address
of the object created with new Animal(15) (stored in cat ), to the variable nothing .
nothing and cat are pointing to the same location on the 'Heap'. When we do nothing.id =
10 we are changing the id of the object pointed to by both nothing and cat .
You can nothing.id prints the value of the object referenced by dog because they are
• nothing = dog - Assignment between references does not copy the entire referenced
object. It only copies the reference. After an assignment, both reference variables point to
the same object.
• nothing.id = 10 - References can be used to modify the objects they reference.
j = i copies the value of i into j . Later, when value of j is changed, i is not affected.
jshell> i == j;
$4 ==> false
jshell> j = 5;
j ==> 5
jshell> i == j;
$5 ==> true
When we compare reference variables, we are still comparing values. But the values are
references - the address of memory locations where objects are stored. The values stored
inside the referenced objects are not used for comparison.
Both cat and ref reference a single Animal object created using new Animal(10) . ==
returns true
The comparison dog == dog2 evaluates to false since the references i.e. memory locations
pointed by these variables are different. They have the same values for id field ( 12 ). But,
that is not important!
Summary
• Understood the way reference variables behave during initialization and assignment
• Saw the relevance of a null value for references
• Observed how equality of references, is different from equality of values of primitive types
jshell> "Test".length()
$1 ==> 4
"Test" is a string literal, so the compiler internally creates an object, gives it the type String
and allocates memory for it. Since length() is a method of String , it can be invoked on this
"Test" object.
In the example below, str is a reference to a String object, containing the value "Test" .
Creating a String object is an exception to how we typically create objects in Java. You don't
need to make a String constructor call. Contrast this with how we would create a BigDecimal
object.
String indexes, like those of arrays, start at 0 . The charAt(int) method takes an index value
as its argument, and returns the character symbol present at that index.
jshell> str.charAt(0)
$2 ==> 'T'
jshell> str.charAt(2)
$3 ==> 's'
jshell> str.charAt(3)
$4 ==> 't'
The substring() method returns a String reference, and has overloaded versions:
• The substring(int, int) method returns the sequence of character symbols starting at
the lower index, and ending just before the upper index.
• The substring(int) method returns the sequence of character symbols starting from the
index to end of string.
jshell> biggerString.substring(5)
$5 ==> "is a lot of text"
jshell> biggerString.substring(5, 13)
$6 ==> "is a lot"
jshell> biggerString.charAt(13)
$7 ==> ' '
jshell>
Summary
Exercise
1. Write a method to print the individual characters of a given text string, separately.
Solution To PE-01
The String class is part of the built-in java.lang package. It provides several methods for
common text processing. Let's have a peek at some of them, shall we?
• indexOf() : Has two overloaded versions. indexOf(char) returns the position where a
character occurs in a string, the first time. indexOf(String) returns the starting position
where a string occurs within our string, the first time.
• lastIndexOf() : Similar in function to indexOf() , but replace "first time" with "final time"
in its description.
• startsWith() : Returns true if our string starts with the given prefix, false otherwise.
• endsWith() : Returns true if our string ends with the given suffix, false otherwise.
• isEmpty() : Returns true if our string is empty, false otherwise.
• equals() : Returns true if our string is identical to the argument, false otherwise.
• equalsIgnoreCase() : Returns true if our string is identical to the argument, ignoring the
case of its characters. Will return false otherwise.
Summary
The word "immutable" is related to the concept of "mutability", or the possibility of "mutating"
something.
"mutate" means "to change". "Immutable" refers to something that "cannot be changed".
String objects are immutable. You cannot change their value after they are created.
The method concat() joins the contents of two String objects into one.
However, the original value referred by str remains unchanged. The concat method create a
new String object.
jshell> str
str ==> "in28Minutes"
Just like concat() , other String methods such as toUpperCase() , toLowerCase() and
trim() return new String objects.
Summary
Heck, we even saw how to use it with the primitive numeric types, such as int , double and
jshell> 1 + 2
$1 ==> 3
jshell> "1" + "2"
$2 ==> "12"
jshell> "1" + 2
$3 ==> "12"
jshell> "1" + 23
$4 ==> "123"
jshell> 1 + 23
$5 ==> 24
jshell> "1" + 2 + 3
$6 ==> "123"
jshell> 1 + 2 + "3"
$7 ==> "33"
In the example below, a different value is printed when parentheses are used around i + 20 .
replace(String, String) replaces all occurrences of the first sub-string with the second one.
It also works with char data type.
Summary
• StringBuffer is thread safe. If you are writing a multi threaded program(more on this in a
later section), use StringBuffer .
Summary
Following is a list of built-in Java wrapper classes and their corresponding primitive types:
• byte : Byte
• short : Short
• int : Integer
• long : Long
• float : Float
• double : Double
• char : Character
• boolean : Boolean
Summary
• Were introduced to the wrapper classes available for the primitive types
• Learned about the advantages of having them around
You can also use valueOf() method within types such as Integer and Float to create a
wrapper object.
The Integer.valueOf() reuses existing Integer objects with same value on the heap. If an
object with same value is present in the heap, it returns a reference to existing object.
Otherwise, it returns a reference of a newly created Integer object.
Wrapper classes are immutable. Hence, above approach is efficient and accurate.
Summary
• Discovered that there are two ways to create a wrapper object for primitive data
• Learned that valueOf() takes advantage of immutability, to improve efficiency
Auto-boxing reuses the mechanism provided by Integer.valueOf() for Integer , Float and
others.
In the above example, Integer sevenToo = 7000 uses auto boxing. We are upgrading a int
literal to a Integer value. This is done implicitly by using Integer.valueOf method.
There are constants available on Wrapper classes print the size of their variables and the range
of values they can store.
jshell> Integer.MAX_VALUE
$2 ==> 2147483647
jshell> Integer.MIN_VALUE
$3 ==> -2147483648
jshell> Integer.SIZE
$4 ==> 32
jshell> Integer.BYTES
$5 ==> 4
jshell>
Summary
• Understood the mechanism of auto-boxing, which uses the assignment operator route
• Understood how auto-boxing internally makes use of valueOf() method
Before Java SE 8, there were a lot of practical issues regarding the interface and
implementation of the Date class.
From Java 8, Java provided an API for Date classes based on the Joda Date Framework.
The three most significant classes within this framework are : LocalDate , LocalTime and
LocalDateTime .
The commonly used utilities to access current values of date and time are:
Summary
• Were introduced to the Java Date API, available through the java.time package
• Saw how to use the LocalDate , LocalTime and LocalDateTime types
$9 ==> 28
LocalDate is also immutable. You can use different methods provided to add and subtract
days, months and years. Each of these return a new instance of LocalDate .
jshell> today.plusDays(100)
$10 ==> 2018-05-12
jshell> today.plusMonths(100)
$11 ==> 2026-06-01
jshell> today.plusYears(100)
$12 ==> 2118-02-01
jshell> today.minusYears(100)
$13 ==> 1918-02-01
jshell> LocalDate yesteryear = today.minusYears(100)
yesterYear ==> 1918-02-01
jshell> today
today ==> 2018-02-01
jshell>
LocalDateTime extends LocalDate and provides time component as well. You can access, and
perform arithmetic on the hours, minutes, seconds and nanoseconds values.
Summary
$3 ==> 2018-03-01
jshell> today.withDayOfYear(3)
$4 ==> 2018-04-30
You can also compare dates using the methods shown below:
jshell> today.isBefore(yesterday)
$5 ==> false
jshell> today.isAfter(yesterday)
$6 ==> true
jshell>
These methods are also available with LocalTime and LocalDateTime classes as well.
We would like to model a student report card in a Java program, which allows the user to do
stuff such as:
A data structure like array and ArrayList allow you to store multiple object of the same kind.
These are called aggregates.
Consider the example below. We are creating 3 marks and adding them.
If an additional mark component mark4 is added to the existing list of marks mark1 , mark2
and mark3 , the code for computing sum needs to change.
All these marks are similar. How about creating a group of marks and storing it as part of single
variable?
In above example
• marks is an array .
• marks stores multiple int values.
• marks array has 3 values
In an array, the index runs from 0 to (length of array - 1). In above example, the index runs from
0 to 2.
You can use an index to find the specific element from the array. It is done by using the
indexing operator, ' [] '. The expression marks[0] maps to the first array element stored at
index 0 of the array marks .
jshell> marks[0]
$20 ==> 75
jshell> marks[1]
$21 ==> 60
jshell> marks[2]
$22 ==> 56
mark is the loop control variable. Its type needs to match the type of an array elements, which
is int .
Above for loop can be used irrespective of the number of elements in marks array.
An array can also be created with new operator. You've to specify the size of the array.
jshell> marks2[0]
$1 ==> 0
jshell> marks2[0] = 10;
$2 ==> 10
jshell> marks2[0]
$3 ==> 10
jshell>
jshell> marks2
marks2 ==> int[5]{ 1,2,3,4,5 }
jshell> marks2.length
$6 ==> 5
jshell> int marks3 = {};
marks3 ==> int[0]{ }
jshell> marks3.length
$7 ==> 0
1. Write a program that creates an array marks to store 8 int values, and code to iterate
through marks using a for loop, printing out its values.
Solution To CE-AA-01
Below snippet shows how arrays with different types are initialized.
• The declaration part of an array definition must not include the dimension of the array
• The assignment part of an array definition must include the dimension of the array
• All the elements of an array must be of the same, homogeneous type
The name of an array is a reference variable that stores the address of where the array is
created on the heap.
The built-in method Arrays.toString can be used to print out elements of an array.
jshell> System.out.println(Arrays.toString(marks));
[1, 2, 3, 4, 5]
jshell>
..>> }
100
99
95
96
100
jshell> for(int i=0; i < marks.length; i++) {
..>> System.out.println(marks[i]);
..>> }
100
99
95
96
100
jshell>
Array.equals compares two given arrays, and returns a boolean value of true only if
jshell> Arrays.sort(array3);
jshell> array3
array3 ==> int[3]{ 2,3,3 }
jshell>
Exercise
Armed with the knowledge of Java arrays and their in-built utility methods, let's now solve the
challenge we started off with.
Solution To CE-AA-02
StudentRunner.java
package com.in28minutes.arrays;
Student.java
package com.in28minutes.arrays;
import java.math.BigDecimal;
Let's look at an example. The critical part is the parameter int... values .
A typical parameter would've been int values . This allows us to pass one parameter to the
method.
What difference does the three dots ... make in int... values ?
You can see that doSomething can be called with one, two and three parameters.
We created a sum method with a variable argument. Let's look at how to use it.
jshell> sum(1, 2)
3
jshell> sum(1, 2, 3)
6
jshell> sum(1, 2, 3, 4)
1
jshell> sum(1, 2, 3, 4, 5, 6)
21
jshell>
In this step, we took our first look at variable arguments. Variable arguments allow us to pass
variable number of arguments to a method.
Let's add a few methods to the Student class to accept variable arguments.
Student.java
package com.in28minutes.arrays;
import java.math.BigDecimal;
StudentRunner.java
package com.in28minutes.arrays;
public class StudentRunner {
public static void main(String[] args) {
//int[] marks = {99, 98, 100};
Student student = new Student("Ranga", 97, 98, 100);
student.addMark(35);
student.removeMarkAtIndex(5);
}
}
Quick Tip
The variable arguments list must always be at the end of the parameter list passed to a
method. The following method definition will not be accepted by the Java compiler:
When creating arrays of objects, the array ends up holding references to the created objects.
Exercises
Solutions To CE-AA-03
WeekRunner.java
package com.in28minutes.arrays;
We've implemented most of the features except for addMark() and removeMarkAtIndex() .
The size of an array is fixed at its compile-time definition. Which means that once we define an
array such as:
The size of the textValues array is fixed to 3. You an change values inside the array. But the
size cannot be changed.
• Create a fresh array with a few extra element slots to accommodate the additional
elements to be inserted
• Copy the existing array elements to the beginning of this new array
• Add the additional elements at the rear end of this array
jshell> newMarks
newMarks ==> int[4] { 12, 34, 45, 0 }
jshell> newMarks
newMarks ==> int[4] { 12, 34, 45, 100 }
As you can see, this can be very inefficient. How do we solve it?
jshell> arrayList.remove("Cat");
$4 ==> true
jshell> arrayList
arrayList ==> ["Apple", "Ball"]
The ArrayList instance arrayList can be used to store objects of pretty much any type,
even primitive types. Also, non-homogeneous types!
jshell> arrayList.add(1);
| Warning:
| unchecked call to add(E) as a member of the raw type java.util.ArrayList
| arrayList.add(1);
|_^---------------------^
$3 ==> true
jshell> arrayList
arrayList ==> ["Apple", "Ball", 1]
jshell>
Let's say we want to store only String values in an ArrayList . How do we do it?
In above snippet, we are creating an ArrayList that can hold String values.
jshell> items.add("Apple");
$1 ==> true
jshell> items
items ==> ["Apple"]
jshell> items.add(1);
| Error
| no suitable method found for add(int)
|...
jshell> items.add("Ball");
$2 ==> true
jshell> items.add("Cat");
$3 ==> true
jshell> items
items ==> ["Apple", "Ball", "Cat"]
jshell> items.remove("Cat");
$4 ==> true
jshell> items
items ==> ["Apple", "Ball"]
jshell> items.remove(0);
$5 ==> "Apple"
jshell> items
items ==> ["Ball"]
jshell>
StudentRunner.java
package com.in28minutes.arrays;
Student.java
package com.in28minutes.arrays;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
The Enhanced for loop works for ArrayList s as well, just like in the case of an array
The Collections.max() and Collections.min() methods can be used to find the maximum
and minimum value in an array.
package com.in28minutes.arrays;
student.addMark(35);
System.out.println(student);
student.removeMarkAtIndex(1);
System.out.println(student);
}
}
Student.java
package com.in28minutes.arrays;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
Console Output
Number of marks : 3
Sum of marks : 3
Average of marks : 3_
Maximum of marks : 3
Minimum of marks : 3
Ranga[97,98,100]
Ranga[97,98,100,35]
Ranga[97,100,35]
At any given time, values of these attributes defines the object's state.
In The MotorBike example, the attribute speed defines a MotorBike 's state. The speed of a
ducati defines its state.
How an object responds to an external event, or a message sent to it, defines its behavior.
Messages are delivered to an object using methods. Methods are used to implement an
object's behavior.
The methods setSpeed , increaseSpeed and decreaseSpeed have an effect on the observed
speed of the MotorBike s. The future state of a MotorBike depends on behavior and
current state .
MotorBikeRunner.java
package com.in28minutes.oops;
ystem.out.println(ducati.getSpeed());
System.out.println(honda.getSpeed());
System.out.println(yamaha.getSpeed());
ducati.increseSpeed(50);
yamaha.setSpeed(250);
honda.increaseSpeed(100);
yamaha.decreaseSpeed(50);
System.out.println(ducati.getSpeed());
System.out.println(honda.getSpeed());
System.out.println(yamaha.getSpeed());
}
}
MotorBike.java
package com.in28minutes.oops;
MotorBike(int speed) {
if(speed > 0)
this.speed = speed;
}
The above three major areas that correspond to its design could be as follows:
• State
◦ make
◦ radius
◦ color
◦ isOn
◦ speed
• Constructors
◦ Fan(String make, double radius, String color)
• Behavior
◦ void switchOn()
◦ void SwitchOff()
◦ void changeSpeed(int change)
◦ String toString()
Let's try to write a simple Fan class , that covers all these aspects.
Fan.java
package com.in28minutes.oops.level2;
//constructors
//methods
public String toString() {
return String.format("Make : %s, Radius : %f, Color : %s, Is On : %b, Speed : %d
make,
radius,
color,
isOn,
speed);
}
}
FanRunner.java
package com.in28minutes.oops.level2;
Console Output
The fields which were not set by the constructor, namely isOn and speed , assumed the
language default values for their data types, namely false (for booelan ) and 0 (for int ).
The default state attributes of the Fan class objects, namely make , color and radius are
fixed at manufacturing time, and cannot be altered by a user of this class 's instances.
The other two state attributes, isOn and speed need to be exposed to change by Fan object
users. We will offer methods that change them.
FanRunner.java
package com.in28minutes.oops.level2;
Fan.java
package com.in28minutes.oops.level2;
//constructors
public Fan(String make, double radius, String color) {
this.make = make;
this.radius = radius;
this.color = color;
}
//methods
public String toString() {
return String.format("Make : %s, Radius : %f, Color : %s, Is On : %b, Speed : %d
make,
radius,
color,
isOn,
speed);
}
//isOn
/*public void isOn(boolean isOn) {
this.isOn = isOn;
}*/
Console Output
Snippet-01 Explained
◦ A state modifier method such as public void isOn(boolean) is not preferred, even
though it does alter this attribute. This is because it is not intuitive from the class
user's perspective.
◦ Alternatively, methods such as public void switchOn() and public void
switchOff() not only toggle the attribute isOn , but are also intuitive and useful to the
Fan class users (Here, the FanRunner class).
◦ setSpeed is both intuitive as well as useful, so not much rethinking needed here
◦ speed needs to be affected by the operations switchOn() and switchOff() . We
have added calls to setSpeed() in these method definitions.
Summary
• State
◦ length
◦ width
• Constructors
• Behavior or Methods
Solution To PE-OOP-01
RectangleRunner.java
package com.in28minutes.oops.level2;
System.out.println(rectangle);
rectangle.setWidth(25);
System.out.println(rectangle);
rectangle.setLength(20);
System.out.println(rectangle);
}
}
Rectangle.java
package com.in28minutes.oops.level2;
//creation:
public Rectangle(int length, int width) {
this.length = length;
this.width = width;
}
//behaviors:
public int getLength() {
return length;
}
Console Output
Solution Explained
• A Rectangle object created without a specified length and width makes no practical
sense, therefore a default constructor is not provided.
Fan.java
package com.in28minutes.oops.level2;
//constructors
//methods
}
All member variables of 'Fan' class are primitive variables. Can we make it complex and include
other classes?
CustomerRunner.java
package com.in28minutes.oops.level2;
Address.java
package com.in28minutes.oops.level2;
//creation
//behaviors
}
Customer.java
package com.in28minutes.oops.level2;
//creation
//behaviors
}
Snippet-01 Explained
• name ,
• homeAddress , and
• workAddress .
String is a built-in type, and is simple. Address is a user defined type, and is composed of:
• doorNo ,
• streetInfo ,
• city , and
• zipCode
CustomerRunner.java
package com.in28minutes.oops.level2;
Address.java
package com.in28minutes.oops.level2;
//creation
public Address(String doorNo, String streetInfo, String city, String zipCode)
this.doorNo = doorNo;
this.streetInfo = streetInfo;
this.city = city;
this.zipCode = zipCode;
}
//behaviors
}
Customer.java
package com.in28minutes.oops.level2;
//creation
//workAddress not mandatory for creation
public Customer(String name, String homeAddress) {
this.name = name;
this.homeAddress = homeAddress;
}
//behaviors
}
CustomerRunner.java
package com.in28minutes.oops.level2;
Address.java
package com.in28minutes.oops.level2;
//creation
public Address(String doorNo, String streetInfo, String city, String zipCode)
super();
this.doorNo = doorNo;
this.streetInfo = streetInfo;
this.city = city;
this.zipCode = zipCode;
}
//behaviors
public String toString() {
return doorNo + ", " + streetInfo + ", " + city + " - " + zipCode;
}
}
Customer.java
package com.in28minutes.oops.level2;
//creation
//workAddress not mandatory for creation
public Customer(String name, String homeAddress) {
this.name = name;
this.homeAddress = homeAddress;
}
//behaviors
//certain components of homeAddress and workAddress can be modified, not the name
public void setHomeAddress(Address homeAddress) {
this.homeAddress = homeAddress;
}
Console Output
Customer [Ashwin Tendulkar] lives at [Flat No. 51, Hiranandani Gardens, Mumbai - 400076],
works at [null]
Customer [Ashwin Tendulkar] lives at [Flat No. 51, Hiranandani Gardens, Mumbai - 400076],
works at [Administrative Office, Western Block, Mumbai - 400076]
Exercises
• Book:
◦ Id
◦ Name
◦ Author
• Review:
◦ Id
◦ Description
◦ Rating
Book book = new Book(123, "Object Oriented Programming With Java", "Ranga");
book.addReview(new Review(10, "Great Book", 4));
book.addReview(new Review(101, "Awesome", 5));
System.out.println(book);
Solution To PE-OOP-02
BookReviewRunner.java
package com.in28minutes.oops.level2;
Book book = new Book(123, "Object Oriented Programming With Java", "Ranga"
book.addReview(new Review(10, "Great Book", 4));
book.addReview(new Review(101, "Awesome", 5));
System.out.println(book);
}
}
Review.java
package com.in28minutes.oops.level2;
Book.java
package com.in28minutes.oops.level2;
Console Output
Book-123, Object Oriented Programming With Java, Ranga, [(Review-10, Great Book", 4),
(Review-101, Awesome, 5)]
Person.java
package com.in28minutes.oops.level2.inheritance;
StudentWithoutInheritance.java
package com.in28minutes.oops.level2.inheritance;
• The member fields of Person , namely name , email and phoneNumber , are replicated in
Student .
• The setter and getter methods pertaining to the above fields of Person get repliated in
Student as well.
Every Student is a Person . What if we could extend Person class instead of duplicating
everything?
Enter Inheritance
Student is a Person . Java supports one of the basic Object Oriented Programming Paradigms
: Inheritance.
Student can inherit from Person , to model the fact that a Student is a Person .
This is accomplished by using the Java keyword extends , during class definition of Student .
Inheritance is a mechanism of code reuse. In this case, all the fields and methods previously
defined in Person are available for Student as well.
Let's now look at how we go about changing the Student class definition.
Person.java
package com.in28minutes.oops.level2.inheritance;
Student.java
package com.in28minutes.oops.level2.inheritance;
StudentRunner.java
package com.in28minutes.oops.level2.inheritance;
public class StudentRunner {
public static void main(String[] args)
Student student = new Student();
// < all setter() and getter() methods of Person and Student available >
student.setName("Ranga");
student.setEmail("in28minutes@gmail.com");
}
}
In the Java language, every class, whether an in-built Java library class, or a user-defined
class, implicitly inherits from the class Object .
This Object class is available in the Java system package java.lang . This class is at the root
of the Java class hierarchy. All classes, including arrays, implement/inherit the methods of this
class .
Person.java
package com.in28minutes.oops.level2.inheritance;
Student.java
package com.in28minutes.oops.level2.inheritance;
StudentRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
com.in28minutes.oops.level2.inheritance.Person@7a46a697
com.in28minutes.oops.level2.inheritance.Person@7a46a697
Snippet-01 Explained
Methods of the Object class such as toString() , hashCode() and notify() are available
to objects of class Person as default implementations.
These are of course, available for access (and modification), and invocation, respectively,
within the sub-class.
You can also override super class method implementations in a sub class - method overriding.
Person.java
package com.in28minutes.oops.level2.inheritance;
PersonRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
Snippet-01 Explained
By defining the method toString() within the Person sub- class , we are overriding the
default version provided by the Object super- class .
• Title
• Employer
• EmployeeGrade
• Salary
Create a method toString() within Employee to print all state attribute values, including those of
Person.
Person.java
package com.in28minutes.oops.level2.inheritance;
Employee.java
package com.in28minutes.oops.level2.inheritance;
import java.math.BigDecimal;
this.employeeGrade = employeeGrade;
}
EmployeeRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
Snippet-01 Explained
We have not printed the underlying Person object details in Employee.toString() method
overriding. Let's look to do that next.
Employee.java
package com.in28minutes.oops.level2.inheritance;
import java.math.BigDecimal;
EmployeeRunner.java
package com.in28minutes.oops.level2.inheritance;
employee.setPhoneNumber("123-456-7890");
employee.setTitle("Programmer Analyst");
employee.setEmployerName("In28Minutes");
employee.setEmployeeGrade('A');
employee.setSalary(new BigDecimal("50000"));
System.out.println(employee);
}
}
Console Output
The super keyword allows an sub-class to access the attributes present in the super-class.
Hence, we were able to invoke the getter methods of the Person object within the Employee
object, like this within Employee.toString() * super.getName() * super.getEmail() *
super.getPhoneNumber()
Sub-Class Contructor
What happens when a sub class object is created? Does the super class constructor get
called?
Person.java
package com.in28minutes.oops.level2.inheritance;
public Person() {
System.out.println("Inside Person Constructor");
}
Employee.java
package com.in28minutes.oops.level2.inheritance;
import java.math.BigDecimal;
public Employee() {
//super();
System.out.println("Inside Employee Constructor");
}
EmployeeRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
Snippet-02 Explained
The Java compiler inserts the code super(); (if it is not explicitly added by the programmer)
as the first statement in the body of the sub-class default constructor, here Employer() .
Let's remove the no argument constructor and add a one argument constructor to Person
class.
PersonRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
Snippet-03 Explained
When we added the constructor with one argument for class Employee , the existing code in
EmployeeRunner.java will cause a compilation error, because there is no longer any default
constructor for Person !
public Person() {
System.out.println("Inside Person Constructor");
}
But, it doesn't really make sense to create a Person without a name , does it?
The solution in this case would be to call the single-argument constructor Person(String) by
an invocation such as super(name);
EmployeeRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
Employee Name: Ranga, Email: null, Phone Number: null, Title: Programmer Analyst, Employer:
In28Minutes, Employee Grade: A, Salary: null
EmployeeRunner.java
package com.in28minutes.oops.level2.inheritance;
}
}
Console Output
Student.java
package com.in28minutes.oops.level2.inheritance;
StudentRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
class Dog extends Animal, Pet {} throws an error. You cannot extend two classes.
Inheritance Chains
• class C is a class B
• class B is a class A
`Dog **is a** Pet , Pet **is a** Animal```. This is an example of what is called an
inheritance hierarchy.
If you want, you can visualize in your mind as : Dog --> Pet --> Animal --> Object (Yes,
Object is sitting at the top of all inheritance hierarchies in Java!)
The statement Dog dog = new Dog(); sets off constructor invocations up the inheritance
hierarchy: * Dog() invokes Pet() * Pet() invokes Animal() * Animal() invokes Object()
The expression dog.toString() does a traversal up the inheritance hierarchy as well: * Since
Dog.toString() is not defined, the compiler looks for Pet.toString() * Since
Pet.toString() is not defined, the compiler looks for Animal.toString() * Since
Animal.toString() is not defined, the compiler looks for Object.toString() , which is always
provided as the default implementation for all sub-classes of class Object in Java.
jshell> dog.toString();
$1 ==> "Dog@23a6e47f"
jshell> dog.groom();
"Pet Groom"
The statement Pet pet = new Dog(); is really interesting. In Java, it is permitted for a super-
class reference variable to reference a sub-class object instance.
Through such a reference, method invocations are also permitted, and the correct thing gets
done. Hence, pet.groom(); causes the output " Pet Groom ".
However, the converse assignment is not allowed. A sub-class reference variable cannot
reference a super-class object instance. * The statement Dog dog = new Pet(); therefore,
causes a compiler error.
The instanceof operator is to find the relationship between an object and a class. If the object
is an instance of the class or its sub class, it returns true.
The instanceof operator throws an error if the object and class are unrelated.
The instanceof operator returns false if the object is an instance of a super class of the
class provided.
Here's how a typical class looks like. You can create methods inside the class and you can
create instances of the class.
Animal Bark
Consider a feast being prepared at a home, and several dishes are on the menu for the event.
Obviously, each dish would have certain procedure for it to be prepared. Cooking any dish
normally involves following a tried and tested recipe, and its preparation boils down to these
basic steps:
These steps would be different for each dish but the order of steps remain the same.
AbstractRecipe.java
package com.in28minutes.oops.level2;
We defined abstract methods for each of the steps and created an execute method calling
them. execute method ensures that the order of method call is followed.
CurryRecipe.java
package com.in28minutes.oops.level2;
@Override
void prepareIngredients() {
System.out.println("Get Vegetables Cut and Ready");
System.out.println("Get Spices Ready");
}
@Override
void cookRecipe() {
System.out.println("Steam And Fry Vegetables");
System.out.println("Cook With Spices");
System.out.println("Add Seasoning");
}
@Override
void cleanup() {
System.out.println("Discard unused Vegetables");
System.out.println("Discard unused Spices");
}
}
RecipeRunner.java
package com.in28minutes.oops.level2;
Console Output
Add Seasoning
Snippet-01 Explained
CurryRecipe defines what needs to be done in each step. When we invoke the execute
method, the steps are executed in order.
Snippet-02 : MicrowaveCurryRecipe
MicrowaveCurryRecipe.java
package com.in28minutes.oops.level2;
@Override
void prepareIngredients() {
System.out.println("Get Vegetables Cut and Ready");
System.out.println("Switch on Microwave");
}
@Override
void cookRecipe() {
System.out.println("Microwave Vegetables");
System.out.println("Add Seasoning");
}
@Override
void cleanup() {
System.out.println("Switch Off Microwave");
System.out.println("Discard unused Vegetables");
}
}
RecipeRunner.java
package com.in28minutes.oops.level2;
Console Output
Switch on Microwave
Microwave Vegetables
Add Seasoning
Snippet-02 Explained
MicrowaveCurryRecipe defines what needs to be done in each step. When we invoke the
execute method, the steps are executed in order.
Summary
This pattern is called a Template method pattern. You define an abstract class with the order of
steps defined. You leave the implementation of each of the steps to the sub classes.
An abstract class can be a sub class to create another abstract class , without overriding
any of the super-class abstract methods.
A device that has buttons or other controls on it, that enable us to play video games.
Who provides the implementation of what happens when a button is clicked? The game
implementor - for example, the implementor of Mario game or the Chess game.
GamingConsole.java
package com.in28minutes.oops.level2.interfaces;
Welcome MarioGame .
MarioGame.java
package com.in28minutes.oops.level2.interfaces;
@Override
public void down() {
System.out.println("Go into a hole");
}
@Override
public void left() {
}
@Override
public void right() {
System.out.println("Go Forward");
}
}
MarioGame provides a definition for all the methods declared in the interface GamingConsole .
Syntax is simple. class MarioGame implements GamingConsole and implement all the methods.
GameRunner.java
package com.in28minutes.oops.level2.interfaces;
Console Output
Jump
Go into a hole
Go forward
Snippet-01 Explained
The main advantage of having an interface is that it can be used to enforce a contract for its
implementors.
ChessGame.java
package com.in28minutes.oops.level2.interfaces;
@Override
public void up() {
System.out.println("Move Piece Up");
}
@Override
public void down() {
System.out.println("Move Piece Down");
}
@Override
public void left() {
System.out.println("Move Piece Left");
}
@Override
public void right() {
System.out.println("Move Piece Right");
}
}
Running it is simple. All that you need to do is to comment out MarioGame game = new
MarioGame(); and replace it with an implementation of ChessGame .
GameRunner.java
package com.in28minutes.oops.level2.interfaces;
Console Output
Move Piece Up
Snippet-2 explained
GameRunner.java
package com.in28minutes.oops.level2.interfaces;
game.up();
game.down();
game.left();
game.right();
}
}
Console Output
Move Piece Up
Explained
GameRunner.java
package com.in28minutes.oops.level2.interfaces;
Console Output
Jump
Go into a hole
Go forward
Snippet-04 Explained
You can replace GamingConsole game = new ChessGame() with GamingConsole game = new
MarioGame() and now the program runs the MarioGame . Isn't it awesome?
How do we ensure that the work done by both the teams remains compatible?
ComplexAlgorithm.java
package com.in28minutes.oops.level2.interfaces;
Now the teams can go on their merry way. Team A can create a stub for the interface
OneComplexAlgorithm and start working on their project.
OneComplexAlgorithm.java
package com.in28minutes.oops.level2.interfaces;
ActualComplexAlgorithm.java
package com.in28minutes.oops.level2.interfaces;
An implementation of interface should implement all its methods including the methods in its
super interfaces.
If a class is declared as abstract, it can skip providing implementations for all interface
methods.
interfaces cannot have member variables. An interface can only have declared constants
Starting from Java SE 8, an interface can provide a default implementation of the methods it
provides. It can be done by including the keyword default in the signature of that method,
and providing a body to that method in its definition.
No method declared inside an interface can be qualified with the private access specifier.
However, an abstract class can have private methods declared within.
}
}
No other code needs to immediately change, and specific implementation classes can override
this default version, as and when needed.
This is especially useful in building and extending frameworks. You are not breaking a user of
your framework interface when you add new methods to the interface.
interface
interface is a Contract.
An interface is primarily used when you have two software components that need to
communicate with each other, and there is a need to establish a contract.
Recall the following example: ComplexAlgorithm defines the interface which helps both the
teams involved.
package com.in28minutes.oops.level2.interfaces;
public interface ComplexAlgorithm {
int complexAlgorithm(int number1, int number2);
}
abstract class
An abstract class is primarily used when you want to generalize behavior by creating a super
class.
Syntactical Comparison
can extend only one interface , and a class or an abstract class can extend only one
class or abstract class .
Exercises
TODO
Solution To PE-OOP-03
Solution - 1
Solution - 2
Introducing Collections
Arrays are not dynamic data structures. They can store only a fixed maximum number of
elements.
Collections are in-built implementations available to support dynamic data structures in Java
programs. The commonly used collections are based on Lists, Trees and Hash Tables.
To make it simple to understand all collections, we will start with the interfaces - such as List ,
Set , Queue , Map and others.
After that, we will look at implementations of these interfaces, such as LinkedList , HashTable
and TreeMap , among others.
If an element's insertion position is not specified, it is added at the end of the List .
We can see examples of creating a list and accessing element data below:
List Immutability
jshell> words.add("Dog");
| java.lang.UnsupportedOperationExcetion thrown:
| at ImmutableCollections.uoe (ImmutableCollections.java:71)
| at ImmutableCollections$AbstractImmutableList.add (ImmutableCollections.java:77)
| at (#15:1)
jshell>
The way to create List data structures that can be updated over time, is to instantiate built-in
collection classes that implement the List interface.
jshell> wordsArrayList.add("Dog");
$1 ==> true
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Cat, Dog]
jshell>
ArrayList vs LinkedList
The data structure used to implement a LinkedList is of the type linked-list, which is a chain
of blocks of memory slots.
• Inserting and Deleting values is easy. This is because in a chain of blocks, each link is
nothing but a reference to the next block. Insertion only involves adjustment of these links
to accommodate new values, and does not require extensive copying and shifting of
existing elements.
• Positional access and modification of elements is less efficient than in an ArrayList ,
because access always involves traversal of links.
Optimization: the underlying data structure for a LinkedList is actually a doubly-linked list,
with each element having both forward and backward links to elements around it.
Vector vs ArrayList
Vector has been with Java since v1.0, whereas ArrayList was added later, in v1.2. Both of
them use an array as the underlying data structure.
Look at other thread safe List implementations as Vector has poor concurrency.
List Operations
We will examine common List operations on an ArrayList . Similar operations can also be
done on a LinkedList or a Vector .
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Ball, Cat, Dog, Elephant, Ball, Yak, Zebra]
jshell> wordsArrayList.set(6, "Fish");
$4 ==> "Ball"
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Ball, Cat, Dog, Elephant, **Fish**, Yak, Zebra]
jshell> wordsArrayList.remove(2);
$5 ==> "**Ball**"
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Cat, Dog, Elephant, Fish, Yak, Zebra]
jshell> wordsArrayList.remove("Dog");
$6 ==> true
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Cat, Elephant, Fish, Yak, Zebra]
jshell> wordsArrayList.remove("Dog");
$6 ==> false
jshell>
Using iterators:
Cat
Snippet-6
A List collection does not store primitives. It only stores object references. When we attempt
to store primitive types, such as those of int , char or double , they get implicitly converted
to their wrapper class types, namely Integer , Character and Double respectively. Primitive
data type elements get auto-boxed.
The ArrayList.indexOf() method is not overloaded, there is only one version. SO, when
passed an int argument, it is auto-boxed to an Integer object, and the method gets
executed.
jshell> numbersAL.remove(Integer.valueOf(101))
$7 ==> true
jshell> numbersAL
numbersAL ==> [102, 103, 104, 105]
jshell>
Sorting a List
The List obtained from List.of() is immutable, hence cannot be sorted itself. Hence, create
a mutable ArrayList out of it.
The ArrayList.sort() method requires the definition of a Comparator object. Easier option is
to use Collections.sort() instead.
Sorting List
Student.java
package collections;
StudentsCollectionRunner.java
package collections;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
Collections.sort(studentsAl);
System.out.println(studentsAl);
}
}
Console Output
COMPILER ERROR
Snippet-9 Explained
To the method Collections.sort() , only those List s can be passed, the type of whose
elements T , implements the Comparator<? super T> interface .
StudentsCollection.java
package collections;
import java.util.Comparable;
StudentsCollectionRunner.java
package collections;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
Console Output
Snippet-10 Explained
Integer.compare(x, y) returns the value 0 if x == y; a value less than 0 if x < y; and a value
greater than 0 if x > y.
@Override
public int compareTo(Student that) {
return Integer.compare(that.id, this.id);
}
What if there is a need for sorting Student's in multiple ways? What if we want to sort Students
in ascending order of id's in some situations and in descending order of id's in some other
situations?
Collections.sort has an overloaded version that has a signature that looks like:
@SuppressWarnings({"unchecked", "rawtypes" })
As you may have already guessed, we would need to define two different Comparator
implementations: one for ascending sort, and another for descending sort.
package collections;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
Console Output
You can call sort method on the List by passing it a Comparator implementation -
studentsAl.sort(new AscStudentComparator()) .
StudentsCollectionRunner.java
package collections;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
Console Output
• If object1.equals(object2) returns true , then only one of object1 and object2 have a
place in a Set implementation.
Snippet-13 : Set
The collection returned by Set.of() is immutable, hence does not support the add()
method.
Create a HashSet collection instead, which supports the add() , in order to test the
uniqueness property of a Set .
jshell> set.add("Apple");
| java.lang.UnsupportedOperationException thrown:
jshell> Set<String> hashSet = new HashSet<>(set);
hashSet ==> [Apple, Cat, Banana]
The HashSet.add() operation returns a false value, indicating that inserting a duplicate
"Apple" entry has failed.
jshell> hashSet.add("Apple");
$1 ==> false
jshell> hashSet
hashSet ==> [Apple, Cat, Banana]
Note that when the hashSet was constructed using the set , the order of the elements got
permuted/changed. Also originally, when we created the set from the Set.of() method, the
order printed was different from the order of initialization. This confirms the fact that a Set
collection does not give any importance to element order, and therefore, does not support
positional access. Hence, the compiler error on call to hashSet.add(int, String) .
Hash Table
• Hash Table: A data structure that attempts to combine the best of both worlds:
◦ Very efficient element access
◦ Very efficient element insertion and deletion
• Buckets : They are the slots in the hash table, into which elements are inserted and
chained together. As you can see, a hash table is effectively a large array of buckets, each
containing a small linked list.
• Hashing: A procedure called hashing used in the construction of the Hash Table. The term
hash generally means mix up the order of elements.
• Hashing Function: A formula to determine/compute which bucket a particular element gets
inserted into. For illustration: hash(elem) could compute elem mod 13 mathematically,
and uses that value to index into the table, to locate the bucket insertion. The
Object.hashCode() could be used as a hashing function.
• Collisions: Leads to chaining within a bucket, where a linked list grows. The larger the
table, and the cleverer the hashing function, the lesser the chance for collisions.
Tree
• Tree: Stores elements in sorted order. For every node in the tree, elements of all nodes in
its left sub-tree are lesser than its contained element. Conversely, elements of all nodes in
its right sub-tree are greater than its contained element.
• Insertions: Given any node, we know by comparing the new element with the node's
element, where to go about inserting it, to maintain sorted order.
• Access : More efficient than linked lists.
Set Implementations
• HashSet
• LinkedHashSet
• TreeSet
Snippet-14 : HashSet
In a HashSet , elements are neither stored in the order of insertion, nor in sorted order.
Snippet-15 : LinkedHashSet
jshell> numbers
numbers ==> [765432, 76543, 7654, 765, 76]
jshell> numbers.add(7654321);
$5 ==> true
jshell> numbers
numbers ==> [765432, 76543, 7654, 765, **7654321**]
jshell>
Snippet-16 : TreeSet
Exercise Set
Solution
SetRunner.java
package collections;
import java.util.List;
import java .util.Set;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;
Console Output
Solution Explained
In this example, the order to elements traversed in hashSetChars happened to be the same as
that in treeSetChars . Such an order is not always guaranteed, as we saw in the earlier
examples.
TreeSet In Depth
• Super-Interfaces
◦ Set
◦ NavigableSet
jshell> numbers.floor(34);
$2 ==> 34
jshell> numbers.lower(34);
$3 ==> 12
jshell> numbers.ceiling(36);
$4 ==> 54
jshell> numbers.ceiling(34);
$5 ==> 34
jshell> numbers.higher(34);
$6 ==> 54
subSet(Object, Object) method is only lower-inclusive. So, the left bound is like lower() ,
and right bound is like higher() . The overloaded version subSet(Object, boolean, Object,
boolean) can be used to configure lower- and upper inclusiveness of the returned subset.
headSet() returns the subset of elements preceding the given element value. tailsSet()
returns the subset of elements succeeding the given element value
jshell> numbers.headSet(50);
$12 ==> [12, 34]
jshell> numbers.tailSet(50);
$13 ==> [54, 65, 99]
jshell>
The PriorityQueue collection is a built-in Java class , that implements the Queue
interface . Elements are stored in a sorted natural order, by default. We can also specify a
different custom order, called the order of priority (customized by the programmer).
Snippet-17 : PriorityQueue
The PriorityQueue queue stores the strings in ascending alphabetic order by default.
jshell> queue
queue ==> [Cat, Monkey, Zebra]
jshell> queue.poll();
$5 ==> "Cat"
jshell> queue.poll();
$6 ==> "Monkey"
jshell> queue.poll();
$7 ==> "Zebra"
jshell> queue.poll();
$8 ==> null
jshell>
QueueRunner.java
package collections;
import java.util.Comparator;
import java.util.List;
import java.util.Queue;
import java.util.PriorityQueue;
Console Output
Cat
Zebra
Monkey
null
The Map interface specifies a contract to implement collections of elements, that are in the
form of (key, value) pairs.
Let's say you want to store how many times a character is repeated in a sequence of
characters.
• If the sequence inserted is: {'A', 'C', 'A', 'C', 'E', 'C', 'M', 'D', 'H', 'A'}
• The map contents would end up being: {('A', 3), ('C', 3), ('E', 1), ('M', 1), ('D',
1), ('H', 1)} .
Since the kind of elements stored in a map ( (key, value) pairs) are different from any other
collection categories in Java, the Map interface is unique. So unique, that it even does not
extend the Collection interface !
//Method Declarations
• The Java collection classes that implement the Map interface are:
◦ HashMap
◦ HashTable
◦ LinkedHashMap
◦ TreeMap
• HashMap
◦ Unordered
◦ Unsorted
◦ Key's hashCode() value is used in the hashing function
◦ Allows a key with a null value.
• HashTable
• LinkedHashMap
• TreeMap
The Map.of() method takes a sequence of objects, which are interpreted as being key and
value entries in alternation.
HashMap elements are not guaranteed to be stored either in inserted order, or in the sorted
order of keys.
TreeMap elements are stored in the natural sorted order of the keys.
$9 ==> null
jshell> treeMap.put("A", 15);
$10 ==> null
jshell> treeMap.put("F", 25);
$11 ==> null
jshell> treeMap.put("L", 250);
$12 ==> null
jshell> treeMap
treeMap ==> {A=15, F=25, L=250, Z=5}
jshell>
Exercise Set
1. Given the string: "This is an awesome occassion. This has never happened before." , do
the following processing on it:
◦ Find the number of occurrences of each unique character in this string
◦ Find the number of occurrences of each unique word in this string
Solution
MapRunner.java
package collections;
import java.util.Map;
import java.util.HashMap;
Revisiting TreeMap
Let's look at some more interesting operations of Data Structures based on TreeMap .
jshell> treeMap.higherKey("B");
$7 ==> "F"
jshell> treeMap.higherKey("C");
$8 ==> "F"
jshell> treeMap.ceilingKey("B");
$9 ==> "B"
jshell> treeMap.ceilingKey("C");
$10 ==> "F"
jshell> treeMap.lowerKey("B");
$11 ==> "A"
jshell> treeMap.floorKey("B");
$12 ==> "B"
jshell> treeMap.firstEntry();
$713 ==> A=15
jshell> treeMap.lastentry();
$14 ==> Z=5
jshell> treeMap.subMap("C", "Y");
$7 ==> {F=25, G=25, L=250}
jshell> treeMap.subMap("B", "Z");
$7 ==> {B=25, F=25, G=25, L=250}
jshell> treeMap.subMap("B", true, "Z", true);
$7 ==> {B=25, F=25, G=25, L=250, Z=5}
jshell>
The collection interfaces we have explored, as well as their implementation classes, are:
• List
• Set
• Queue
• Map
• When you use a "Hashed" Java collection (hash table based), such as HashMap or
HashSet , it will be unordered, unsorted and will iterate over its elements in no particular
order.
• When you encounter a "Linked" Java collection (linked list based), such as a
LinkedHashSet or a LinkedHashMap , it will maintain insertion order, but will be unsorted.
• When we make use of a "Tree"-based Java collection (stored in a tree data structure),
such as TreeSet or TreeMap it always maintains natural sorted order. It would implement
one of the navigable category of interfaces, such as NavigableSet or NavigableMap .
Introducing Generics
Recommended Videos
• Generics - https://www.youtube.com/watch?v=v4o0wyFPwEs
Let's look at a scenario where want to write a wrapper class around the ArrayList data
structure, maybe to do some better custom error-checking and stuff. For now, we will just look
at basic wrapper functionality, the error checking intent is just an excuse!
Snippet-1
GenericsRunner.java
package com.in28minutes.generics;
import com.in28minutes.generics.MyCustomList;
MyCustomList.java
package com.in28minutes.generics;
Snippet-1 Explained
The MyCustomList class is a wrapper for ArrayList of String s. Insertion and deletion of
elements into this data structure is straightforward.
Let's sy I would want to create MyCustomList for other types. Should we write additional
wrapper classes MyCustomList and so on?
MyCustomList.java
package com.in28minutes.generics;
GenericsRunner.java
package com.in28minutes.generics;
import com.in28minutes.generics.MyCustomList;
Console Output
[Element-1, Element-2]
[5, 9]
Snippet-2 Explained
The identifier T in the definition of the Generic class MyCustomList<T> is a placeholder for
the actual type of the container. It is this placeholder that truly converts the MyCustomList
class into a template.
The naming convention for these type placeholders is: * Always use UpperCase letters of the
alphabet (such as T , or S ), or * intuitive words such as TYPE
Exercise Set - 18
1. Write a method get inside the generic class MyCustomList , which returns the element at
a particular index (passed as argument) in its list storage.
Solution
MyCustomList.java
package com.in28minutes.generics;
GenericsRunner.java
package com.in28minutes.generics;
import com.in28minutes.generics.MyCustomList;
Console Output
Element-1
Solution Explained
We have defined a method MyCustomList<T>.get whose return type is generic as well. The
return type has the same placeholder T as the template in the definition of MyCustomList<T> .
We saw above that we could use MyCustomList<T> to be instantiated into data structures for
storing String s as well as for Integer s.
MyCustomList.java
package com.in28minutes.generics;
GenericsRunner.java
package com.in28minutes.generics;
import com.in28minutes.generics.MyCustomList;
Console Output
When we specify T extends Number as the type, we can use all the methods in the API of
class Number are available for use.
Generic Methods
GenericsRunner.java
package com.in28minutes.generics;
import com.in28minutes.generics.MyCustomList;
Console Output
[A, B, C, A, B, C]
[1, 2, 3, 1, 2, 3]
You can use wild card with generics too - ? extends Number
Snippet-5
GenericsRunner.java
package com.in28minutes.generics;
import com.in28minutes.generics.MyCustomList;
return sum;
}
Console Output
15.0
15.5
15.0
Snippet-5 Explained
The symbol ? in the definition of the method static double sumOfNumberList(List<? extends
Number> numbers) is the wild-card symbol. It denotes the fact that in order to be a valid
argument to sumOfNumberList , numbers can be a List of any elements, so long as all of them
are of type sub-classed from Number .
The generic wildcard we saw in the previous section is referred to as a Upper-Bounded Wild-
Card. It can be used to specify homogeneous types with a restriction. There is another
category of wild-cards called Lower-Bounded Wild-Card, which can be used with create
Heterogeneous types of elements , within the restriction. Here is an example.
GenericsRunner.java
package com.in28minutes.generics;
import com.in28minutes.generics.MyCustomList;
Console Output
• Part 1 - https://www.youtube.com/watch?v=aFCNPHfvqEU
• Part 2 - https://www.youtube.com/watch?v=5Xw1_IREXQs
FunctionalProgrammingRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
Console Output
Apple
Banana
Cat
Dog
Snippet-01 Explained
We looped around the list, accessed individual elements of a List and did
System.out.println() to print each element.
FunctionalProgrammingRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
for(String str:list) {
System.out.println(str);
}
}
Console Output
Apple
Banana_Cat
Dog
Snippet-02 Explained
1
4
7
9
jshell>
Snippet-01 Explained
elem -> System.out.println(elem) is a lambda expression. For each element in list stream,
execute the lambda expression.
package com.in28minutes.functionalprogramming;
import java.util.List;
}
}
Console Output
Bat
Cat
Snippet-02 Explained
In the above example, we are using lambda expression to define the same conditions.
Let's look at how to use reduce method to calculation the sum. FPNumberRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
Console Output
49
Snippet-01 Explained
The reduce() method acts on a pair of elements at a time. The initial-value is 0 . The lambda
expression (num1, num2) -> num1 + num2 is executed on the elements of the list, a pair at a
time.
Solution To CE-01
FPNumberRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
Console Output
Let's have a re-look at the FPNumberRunner.java program from the previous step. We wrote
two variants of the same task that computed the sum of a list of numbers:
Let's use them as benchmarks, to illustrate the core differences between SP and FP.
i. SP: Within basicSum() , the variable sum (a sort of worker variable) is initialized to 0 ,
and undergoes mutations across iterations of the for loop.
ii. FP: We just set up an initial value for reduce() to work with. We don't have any
mutation.
i. SP: We specify both what to do, and how to do it. The code loops through the list
elements using a for , while also updating the aggregate value in sum .
ii. FP: We are focused on what to do, and very little on the how part. reduce() takes care
of what numbers from the stream to add up, and you don't bother about how to select
them from the stream.
Lambda Expression
Why take our word for all this? Let's put this code into an IDE, and then run it, to see for
ourselves.
FPNumberRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
Console Output
04
46
10 8
18 13
31 3
34 15
49
Stream
A Stream is a sequence of elements. You can perform different kinds of operations on a stream.
• Intermediate Operations: An operation that takes a stream - for example, applies a lambda
expression - and produces another stream of elements as its result.
• Terminal Operations: A stream operation that takes a stream - for example, applies a
lambda expression - and returns a single result (A single primitive-value/object, or a single
• sorted()
• distinct()
• filter()
• map()
3
5
7
45
213
jshell> numbers.stream().distinct().map(num -> num*num).forEach(elem -> System.out.print
9
25
45369
2025
49
jshell>
Snippet-01 Explained
• sorted() preserves the elements of the consumed stream in the result, but also puts
them in natural sorted order (Increasing order for numbers, alphabetical order for strings).
• distinct() returns a stream retaining only the unique elements of the input stream. This
method maintains the relative order of retained elements.
• You can chain together more than one intermediate operation, such as sorted() followed
by distinct() above. Such code is sometimes called a pipeline.
• map() : Applies a lambda expression to compute new results from the input stream
elements. It then returns a stream of these results as output. In our example, map() takes
each element in the Stream object created by ```number.stream()``,` to its square value.
Exercises
Solutions To FP-PE-01
FPNumberRunner.java
package com.in28minutes.functionalprogramming;
public class FPNumberRunner {
public static void printFPSquares() {
IntStream.range(1, 11).
map(num -> num*num).
forEach(elem -> System.out.println(elem));
}
Console Output
16
25
36
49
64
81
100
apple
banana
cat
Solution Explained
• reduce()
• max() and min()
What if there are no numbers in the Stream? What should be returned? As a Java programmer,
we grew up to hate null . You don't want to return null back.
• You can query an Optional object to check if it contains a valid result, by invoking
isPresent() on it.
• You can get that result by calling get() on the same object.
collect() method can be called to collect the result of filter() into a list.
Collectors.toList() is the utility method used.
1. From a list of 23, 12, 34, 53, create a list of the even numbers in it.
2. Create a list of squares of the first 10 positive integers.
Solutions To FP-CE-02
FunctionalProgrammingRunner.java
package com.in28minutes.functionalprogramming;
Solution Explained
In order to get the result in a form you would appreciate, we modified this code to look like:
max() is a stream operation, that needs to consume a stream. It is possible that the input
stream is empty. In that case, the maximum value would be null . It is undesirable in FP to
encounter an exception during a stream operation. It is extremely inelegant if we are asked to
handle an exception in an FP code pipeline.
The Optional<T> class was introduced in Java SE 8, as a lifeline in such situations. It covers
the possibility of absence of (or null ) result from a terminal stream operation. The following
example illustrates how you can query, and access the result from, an Optional<T> object.
jshell> List.of(23, 45, 67, 12).stream().filter(num -> num % 2 == 0).max( (n1, n2) ->
$1 ==> Optional[12]
jshell> $1.get()
$2 ==> 12
jshell> $1.isPresent()
$3 ==> true
In case the result is empty, then the value stored in the result is not null , it is
Optional.empty .
jshell> List.of(23, 45, 67).stream().filter(num -> num % 2 == 0).max( (n1, n2) -> Inte
$4 ==> Optional.empty
jshell> $4.isPresent()
$5 ==> false
jshell> $4.orElse(0)
$6 ==> 0
You can provide a default value for the result using the method orElse() .
jshell> List.of(23, 45, 67).stream().filter(num -> num % 2 == 0).max( (n1, n2) -> Inte
$7 ==> 0
jshell> List.of(23, 45, 67, 34).stream().filter(num -> num % 2 == 0).max( (n1, n2) ->
$8 ==> 34
jshell>
LambdaBehindTheScenesRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
Console Output
34
36
48
Snippet-01 Explained
filter() accepts an object implementing the Predicate interface, as its argument. It returns
a stream, consisting of those elements from the input stream, that match this predicate.
The Predicate<T> interface is an example of a Functional Interface. This interface has one
method boolean test(T t) .
@FunctionalInterface
public interface Predicate<? super T> {
boolean test(T t) { /* */ }
//...
}
}
LambdaBehindTheScenesRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
import java.util.function.Predicate;
Console Output
34
36
48
Snippet-02 Explained
@FunctionalInterface
public interface Consumer<? super S> {
void accept(S s) { /* */ }
//...
The lambda expression used inside forEach() and other such stream operations, actually
represent a Consumer implementation.
Snippet-01
FunctionalProgrammingRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
import java.util.stream.Stream;
import java.util.function.Consumer;
import java.util.function.Predicate;
numbers.stream()
.filter(new EvenNumberPredicate())
.forEach(new SysOutConsumer());
}
}
Console Output
12
34
36
48
Snippet-01 Explained
• The code actually speaks for itself. The steps to customize a Consumer<S> implementation
are quote simple, and straightforward.
• The final code that involves both a custom Predicate<T> , and a custom Consumer<S> is
still quite compact and elegant!
package com.in28minutes.functionalprogramming;
import java.util.List;
import java.util.stream.Stream;
import java.util.function.Consumer;
import java.util.function.Predicate;
Console Output
1156
1296
2304
Snippet-01 Explained
@FunctionalInterface
public interface Function<T,R> {
R apply(T t);
}
The method apply() accepts a T object as argument, and returns another object of type R .
In effect, any Function implementation can map object of one type to another.
package com.in28minutes.functionalprogramming;
import java.util.List;
import java.util.stream.Stream;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Function;
@Override
public void accept(Integer num) {
System.out.println(num);
}
}
numbers.stream()
.filter(new EvenNumberPredicate())
.map(new NumberSquareMapper())
.forEach(new SysOutPredicate());
}
}
Console Output
1156
1296
2304
1156
1296
2304
MethodReferencesRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
Console Output
MethodReferencesRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
Let's define a static method print and use it using a method reference.
MethodReferencesRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
Instance method calls can also be replaced with their method references.
MethodReferencesRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
1. Using method references, write java functional code to determine the maximum even
number, in a given list of integers.
Solution To FP-CE-03
MethodReferencesRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
.orElse(0);
System.out.println(max);
int maximum = List.of(23, 45, 67, 34).stream()
.filter(MethodReferencesRunner::isEven)
.max(Integer::compare)
.orElse(0);
System.out.println(maximum);
}
Console Output
34
34
Summary
package com.in28minutes.functionalprogramming;
import java.util.List;
import java.util.function.Predicate;
numbers.stream()
.filter(evenPredicate)
.map(n -> n*n)
.forEach(e -> System.out.println(e));
}
}
Console Output
1156
1296
2304
import java.util.stream.Stream;
import java.util.function.Predicate;
numbers.stream()
//.filter(num -> num%2 == 0)
//.filter(evenPredicate)
.map(n -> n*n)
.forEach(e -> System.out.println(e));
}
}
Console Output
1156
1296
2304
Summary
So far, we've only seen programs that run like a single horse, running round a race course.
However, it often makes sense for a program's main task to be split into smaller ones (let's call
them sub-tasks).
Imagine an ant-colony, where a large number of worker ants toil together, to complete what the
Queen Ant tells them to do. Groups of ants that are part of separately tasks, work with a free
hand.
The concept of concurrency in programming is very similar to what an ant colony is in nature.
Snippet-1
ThreadBasicsRunner.java
//Task2
for(int i=201; i<=299; i++) {
System.out.print(i + " ");
}
System.out.println("Main Done");
}
}
Console Output
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
191 192 193 194 195 196 197 198 199
Task1 Done
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
286 287 288 289 290 291 292 293 294 295 296 297 298 299
Task2 Done
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
Task3 Done
Main Done
Snippet-1 Explained
As you can see, the execution of all three for loops (that really are independent tasks) is
sequential. This is how all our code so far has been running!
Thread Creation
There are two ways in which you can create a thread to represent a sub-task, within a program.
They are:
ThreadBasicsRunner.java
//Task2
for(int i=201; i<=299; i++) {
System.out.print(i + " ");
}
System.out.println("\nTask2 Done");
//Task3
for(int i=301; i<=399; i++) {
System.out.print(i + " ");
}
System.out.println("\nTask3 Done");
System.out.println("\nMain Done");
}
}
Console Output
Task1 Started
201 101 202 102 203 103 204 104 105 205 206 106 207 107 208 108 209 109 210 110 211 111
Task2 Done
301 199 302
Task1 Done
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
Task3 Done
Main Done
Snippet-01 Explained
We defined a Task1 class to denote our sub-task, with a run() method definition. However,
when we create such a thread within our main() method, we don't seem to be invoking run()
in any way! What's happening here?
A thread can be created and launched, by calling a generic method named start() . method.
Calling start() will invoke the individual thread’s run() method.
From the console output, we see that the output of Task1 overlaps with those of tasks labeled
Task2 and Task3. Task1 is running in parallel with main which is running (Task2, Task3).
Summary
In Step 01, we told you that there are two ways a thread could represent a sub-task, in a Java
program. One was by sub-classing a Thread , and the other way is to implement Runnable . We
saw the first way a short while ago, and it's time now to explore the second. The following
example will show you how it's done.
ThreadBasicsRunner.java
System.out.println("\nMain Done");
}
}
Console Output
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
Task3 Done
Main Done
Snippet-01 Explained
To run Task2 which is implementing Runnable interface, we used this code. We are using the
Thread constructor passing an instance of Task2 .
You can see from the output that all three tasks are running in parallel.
Summary
• NEW: A thread is in this state as soon as it's been created, but its start() method hasn't
yet been invoked.
• TERMINATED/DEAD: When all the statements inside a thread's run() method have been
been completed, that thread is said to have terminated.
A thread can be in any one of the remaining three states, after its start() method has been
invoked.
• RUNNABLE: If the thread is not currently running, but is ready to do so at any time.
• BLOCKED/WAITING: If the thread is not currently running on the processor, but is not
ready to execute either. This may be the case if it's waiting for an external resource (such
Summary
A request to change this priority is done by invoking the static setPriority(int) method,
available in the Thread class . This request may or may not be honored in response, so be
prepared for that!
Let consider the example from Step 02. We want to add a condition - Task3 should execute
only after Task1 terminates.
ThreadBasicsRunner.java
task1.join();
Snippet-5 Explained
task1.join() waits until task1 completes. So, the code after task1.join() will executed only
on completion of task1 .
If we want Task3 to be executed only after both Task1 and Task2 are done, the code in main()
needs to look as follows:
ThreadBasicsRunner.java
task1.join();
task2Thread.join();
Snippet-6 Explained
It is important to note that Task1 and Task2 are still independent sub-tasks. The thread
scheduler is free to interleave their executions. However, Task3 is kicked off only after both of
them terminate.
Summary
• public static native void sleep(int millis) : Calling this method will cause the thread
in question, to go into a blocked / waiting state for at least millis milliseconds.
• public static native void yield() : Request thread scheduler to execute some other
thread. The scheduler is free to ignore such requests.
jshell> Thread.sleep(1000)
jshell> Thread.sleep(10000)
jshell>
Snippet-7 Explained
• start()
• join()
• sleep()
• wait()
• No Fine-Grained Control: Suppose, for instance , we want Task3 to run after any one of
Task1 or Task2 is done. How do we do it?
• Difficult to maintain: Imagine managing 5-10 threads with code written in earlier
examples. It would become very difficult to maintain.
• NO Sub-Task Return Mechanism: With the Thread class or the Runnable interface ,
there is no way to get the result from a sub-task.
The ExecutorService is a framework you can use to manage threads in your code, better. It
has built-in ways to:
The ExecutorService provides utilities to achieve each one of these. Let's start with thread
creation, which the next example takes care of.
ExecutorServiceRunner.java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Console Output
Task1 Started
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
191 192 193 194 195 196 197 198 199
Task1 Done
Task2 Started
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
286 287 288 289 290 291 292 293 294 295 296 297 298 299
Task2 Done
Snippet-01 Explained
Console Output
Task1 Started
101
Task3 Kicked Off
102 301 103 302 104 303 105 304 106 305 107 306 108 109 110 111 112 307 113 114 115 116
Task1 Done
337 338 Task2 Started
339 201 202 203 204 205 206 207 340 341 208 209 210 211 212 213 214 215 216 217 218 219
Task3 Done
Main Done
292 293 294 295 296 297 298 299
Task2 Done
Snippet-02 Explained
The only order that we see in the resulting chaos is: Task2 starts execution only after Task1 is
done.
The following examples will show you how you can create thread pools of varying kinds, and of
course, of different sizes.
ExecutorServiceRunner.java
Console Output
Task1 Started
Main Done
184 185 279 280 281 282 283 186 187 284 285 286 188 189 287 288 289 190 191 290 192 291
Task2 Done
197 198 199
Task1 Done
Snippet-03 Explained
ExecutorServiceRunner.java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Snippet-04 Explained
}
}
Snippet-04 Explained
Summary
• Explored how one can create pools of threads of the same kind, using the
ExecutorService
So far, we have only seen sub-tasks that are largely independent, and which don't return any
result to the main program that launched them.
The next example tells you how to implement Callable<T> , and use it with ExecutorService .
ExecutorServiceRunner.java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "Hello " + name;
}
}
Console Output
Hello in28Minutes
Snippet-01 Explained
• public String call() throws Exception { - Implement call method and return a
String value back.
Summary
• Understood the need for a mechanism, to create sub-tasks that return values
• Discovered that Java has a Callable<T> interface to do exactly this
MultipleCallableRunner.java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "Hello " + name;
}
}
Console Output
Hello in28Minutes
Hello Ranga
Hello Adam
Snippet-01 Explained
The list of return values can be accessed only after all the threads are done, and have returned
their results.
This can be verified from the console output. all the planned welcome messages are printed in
one go, but only after a wait of at least 3000 milliseconds has been completed.
Let's now see what scenario would pan out with a larger thread pool size.
Console Output
Hello in28Minutes
Hello Ranga
Hello Adam
Snippet-02 Explained
The welcome messages all get printed in a batch again, but their collective wait gets shorter.
This is because:
• The thread pool size is now 3 , not 2 as earlier. This means all the three tasks can be put
in the RUNNABLE state at once.
• Then, they go into their BLOCKED state also almost simultaneously, which means their
collective wait time is much less than 3000 milliseconds. That's one advantage of a larger
thread pool!
Summary
• Learned that it's possible to collect the return values of a pool of Callable threads, at one
go.
• This is done using the invokeAll() method for their launch, and specifying a List of
Future objects to hold these results
• Changing the thread pool size for such scenarios can change response time dramatically
Console Output
Hello Ranga
Console Output
Hello in28Minutes
Console Output
Hello Ranga
Console Output
Hello Adam
Snippet-01 Explained
The method invokeAny() returns when the first of the sub-tasks is done. Also, the returned
value is not a Future object. It's the return value of the call() method.
We can see that over different executions, the order of console output changes. This is
because:
Summary
• Learned that ExecutorService has a way to return the first result, from a poll of Callable
threads
• https://www.youtube.com/watch?v=34ttwuxHtAE
• Compile-time Errors: Flagged by the compiler when it detects syntax or semantic errors.
• Run-time Errors: Detected by the run-time environment when executing code
• Running out of heap-memory for objects, or space for the method call stack
• Dividing a number by 0
• Trying to read from an unopened file
Exceptions are unexpected conditions; their occurrence does not make a programmer bad (or
good, for that matter). It is a programmer's responsibility to be aware of potential exceptional
conditions in her program, and use a mechanism to handle them effectively.
Want to see a live example of the Java run-time throwing an exception? The next example will
satisfy your thirst.
ExceptionHandlingRunner.java
package com.in28minutes.exceptionhandling;
Console Output
java.lang.NullPointerException
at com.in28minutes.exceptionhandling.ExceptionHandlingRunner.callMethod
(ExceptionHandlingRunner.java:8)
at com.in28minutes.exceptionhandling.ExceptionHandlingRunner.main
(ExceptionHandlingRunner.java:4)
Snippet-01 Explained
If an exception is not handled in the entire call chain, including main , the exception is thrown
out and the program terminates. System.out.println() statements after the exception are
never executed.
The runtime prints the call stack trace onto the console.
at Test.callThree(Test.java:13)
at Test.callTwo(Test.java:9)
at Test.callOne(Test.java:6)
at Test.main(Test.java:3)
Summary
ExceptionHandlingRunner.java
package com.in28minutes.exceptionhandling;
Console Output
method1() Done
main() Done
Snippet-01 Explained
We have handled an exception here with a try - catch block. Its syntax resembles this:
try {
//< program-logic code block >
} catch (Exception e) {
//< exception-handling code block >
}
The exception (the NullPointerException ) still occurs after adding this block, but now it's
actually caught. Although we did nothing with the caught exception, we did avoid a sudden end
of the program.
The statements following int len = str.length() in method2 are not executed.
However, all of method1() 's code was run (with the "method1 Done" message getting printed).
main() method also completed execution successfully.
The program thus terminated gracefully. method1() and main() are both unaware of the
NullPointerException occurring within method2() .
ExceptionHandlingRunner.java
package com.in28minutes.exceptionhandling;
Console Output
java.lang.NullPointerException
at com.in28minutes.exceptionhandling.ExceptionHandlingRunner.method2
(ExceptionHandlingRunner.java:14)
at com.in28minutes.exceptionhandling.ExceptionHandlingRunner.method1
(ExceptionHandlingRunner.java:8)
at com.in28minutes.exceptionhandling.ExceptionHandlingRunner.main
(ExceptionHandlingRunner.java:4)
method1() Done
main() Done
printStackTrace() is provided by the Exception class , which every exception inherits from.
This method prints the frozen call trace of the program when the exception occurred, but
without terminating the program. The code next continues to run.
The stack trace provides useful information to you, the programmer,to debug the exception
scenario.
Summary
• Were introduced to Java's basic mechanism to handle exceptions, the try - catch block
• Saw how a program runs and ends gracefully, when an exception is handled
• Observed how the printStackTrace method gives debug information to the programmer
You heard right! Java has a hierarchy of exception types, rooted at class Exception . For
instance,
Different branches of this inheritance-tree actually denote different exception categories. We'll
dwell on this topic a little later.
ExceptionHandlingRunner.java
package com.in28minutes.exceptionhandling;
method1();
System.out.println("main() Done");
}
Console Output
NullPointerException
method1() Done
main() Done
Snippet-01 Explained
Among all the catch clauses following the try , one and only one of them, may get executed.
The first catch clause to match, in serial order after the try , always gets executed. If none
match, the exception is not handled.
You need to order the catch blocks after a try , from more-specific to less-specific matches.
package com.in28minutes.exceptionhandling;
Console Output
ArrayIndexOutOfBoundsException : 3
at com.in28minutes.exceptionhandling.ExceptionHandlingRunner.method2
(ExceptionHandlingRunner.java:14)
at com.in28minutes.exceptionhandling.ExceptionHandlingRunner.method1
(ExceptionHandlingRunner.java:8)
at com.in28minutes.exceptionhandling.ExceptionHandlingRunner.main
(ExceptionHandlingRunner.java:4)
method1() Done
main() Done
Snippet-02 Explained
Summary
FinallyRunner.java
package com.in28minutes.exceptionhandling;
import java.util.Scanner;
}
}
Console Output
at com.in28minutes.exceptionhandling.FinallyRunner.main (FinallyRunner.java:8)
Snippet-01 Explained
This example makes use of Scanner object to read from console. Ideally a Scanner object
should be closed using scanner.close(); .
However, in our example, it is not called because a line before it threw an exception. ( int num
= numbers[5]; tries to access the 5th element of a 4-element array).
What this means, is a system resource that has been acquired, is never released.
It's important to ensure that any acquired resource is always released; whether on normal or
abrupt termination. Let's see how you do this while handling an exception.
FinallyRunner.java
package com.in28minutes.exceptionhandling;
import java.util.Scanner;
}
System.out.println("Before exiting main");
}
}
Console Output
ArrayIndexOutOfBoundsException
at com.in28minutes.exceptionhandling.FinallyRunner.main (FinallyRunner.java:10)
Snippet-02 Explained
Code in finally is almost always executed - even when there are exceptions.
We added a null check on scanner since scanner = new Scanner(System.in); could also
result in an exception.
} finally {
if(scanner != null) {
System.out.println("Before scanner close");
scanner.close();
}
}
Summary
Puzzle-01
• Answer
◦ Yes
◦ Yes
Puzzle-02
• Answer
◦ In case of statements within the same finally clause, preceding this code, throwing
an exception
◦ In case of a JVM crash. This can be simulated in some scenarios by calling
System.exit with an appropriate int argument, within an appropriate catch clause
of the same try - catch - finally clause.
Puzzle-03
connection.open();
try {
String str = null;
//str = "Hello";
str.toString();
return;
} finally {
connection.close();
}
}
• Answer : Yes
Puzzle-04
• Answer : No
CheckedExceptionRunner.java
package com.in28minutes.exceptionhandling;
Snippet-02 Explained
The reason we get flagged by a compiler error, lies in the signature of the sleep() method.
Here is its definition within the Thread class :
This declaration is a bit different from what you normally expect for a method, isn't it! It
contains a throws specification.
CheckedExceptionRunner.java
package com.in28minutes.exceptionhandling;
}
}
Snippet-03 Explained
throws is used to declare that a method might throw exceptions. This involves a throws
keyword, followed by a list of exception types.
package com.in28minutes.exceptionhandling;
Snippet-04 Explained
Here, we have removed the try - catch block within riskyMethod() , because we want to
follow another way of managing the exception. As an alternative, we added a throws
specification to riskyMethod() to make the code compile.
Summary
• Discovered that certain exceptions in Java do not force you to handle them
• Learned that all the rest must be managed/handled
• Observed that there are two ways to manage those "Checked" exceptions:
◦ Handling with a try - catch block
◦ Using a throws specification
Once an Error occurs, there is nothing a programmer could do. Examples include:
• RuntimeException and its sub-classes. These come under the category of unchecked
exceptions. Another example we've seen is NullPointerException , which inherits from
RuntimeException .
• The method call must be enclosed in a try - catch block for proper handling, or
• The caller must throw this exception out, to its own caller. Its signature must also be
enhanced using a throws specification.
A checked exception must be handled, whereas as unchecked exception may or may not be
handled.
Summary
• Discovered that within the Java exception hierarchy, there are two categories:
◦ Checked exceptions
◦ Unchecked exceptions
• There are different strategies to manage these two categories
ThrowingExceptionRunner.java
package com.in28minutes.exceptionhandling;
class Amounts {
private String currency;
private int amount;
Console Output
at com.in28minutes.exceptionhandling.ThrowingExceptionRunner.main
(ThrowingExceptionRunner.java:26)
Snippet-01 Explained
Since adding 10 USD and 20 EUR does not make sense in the real world, it's important to tell
the user that add() won't work for different currencies. The most direct way is to throw an
exception, which the code throw new RuntimeException("Currencies Don't Match"); does.
This thrown exception object can be handled inside main() . By calling printStackTrace() on
the caught exception reference, you get debug information like before.
ThrowingExceptionRunner.java
package com.in28minutes.exceptionhandling;
class Amounts {
private String currency;
private int amount;
Console Output
at com.in28minutes.exceptionhandling.ThrowingExceptionRunner.main
(ThrowingExceptionRunner.java:26)
Summary
• Learned that it is possible to throw an exception from code inside any method we write
• When a method throws checked exception, it should declare it.
ThrowingExceptionRunner.java
package com.in28minutes.exceptionhandling;
class Amounts {
private String currency;
private int amount;
Console Output
at com.in28minutes.exceptionhandling.ThrowingExceptionRunner.main
(ThrowingExceptionRunner.java:26)
Snippet-01 Explained
Summary
• Discovered that Java allows you to define your own custom exception classes
• Whether a custom exception is checked or unchecked, depends on which exception it sub-
classes.
Snippet-01: try-with-resources
package com.in28minutes.exceptionhandling;
import java.util.Scanner;
Snippet-01 Explained
The try -with-resources version was introduced in Java SE 7. It encloses the resource to be
managed within parentheses, along with the try keyword.
In this case, Scanner is compatible with such resource management, because it's defined to
implement the Closeable interface . This interface in turn, is a sub-class of the abstract
class AutoCloseable .
For try -with-resources, a catch and/or a finally clause are not mandatory.
Summary
• Discovered a veriant of the try - catch - finally block, called try -with-resources
• Saw that it can be used to manage resource cleanly, provided the resource implements the
AutoCloseable interface .
Puzzle-01
try {
AmountAdder.addAmounts(new Amount("RUPEE", 5), new Amount("RUPEE", 5);
String str = null;
str.toString();
} catch(CurrenciesDoNotMatchException ex) {
ex.printStackTrace();
}
• Answer : No
Puzzle-01 Explained
The exception thrown is a NullPointerException , whereas the one we are trying to catch is a
CurrenciesDoNotMatchException .
Puzzle-02
try {
AmountAdder.addAmounts(new Amount("RUPEE", 5), new Amount("RUPEE", 5);
String str = null;
str.toString();
} catch(Exception e) {
e.printStackTrace();
} catch(CurrenciesDoNotMatchException ex) {
ex.printStackTrace();
}
Answer : No
Puzzle-02 explained
The order of catch clauses for exceptions needs to be from less specific to more specific.
CurrenciesDoNotMatchException is a sub-class of Exception . Hence, error.
Puzzle-03
try {
• Answer : Yes
Puzzle-03 Explained
File Operations
We would be aware that any computer has a hard disk, on which information is stored. This
information is stored in units called files. For ease of access, file are grouped together and
organized into directories. The operating system has a sub-system called the file-system,
which has the responsibility of interfacing system and user programs, with files and directories
on disk.
The Java Runtime System also communicates with the native file-system, and makes use of its
services to provide a file programming interface to Java programmers.
In this section, we will explore a few basic ways in which programmers can interact with the
native file-system, through the platform independent Java file API.
When we develop a Java software project in the Eclipse IDE environment, the root folder of the
project has several interesting file and sub-folders contained within it. From now on, we use the
terms "folder" and "directory" interchangeably, as they have equivalent meanings.
Let's write a simple Java program to list the contents of one such project folder.
The java.nio.file system package has a bunch of utility class es and interface s to help
us navigate the native file system.
DirectoryScanRunner.java
package com.in28minutes.files;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
Console Output
./.classpath
./.project
./.DS_Store
./bin
./resources
./src
Snippet-1 Explained
A Path class is the entity used to denote a pathname in Java NIO Library. NIO stands for
"New I/O", which was introduced in Java SE, replacing the earlier, very awkward File I/O Library.
It is no longer new though, but that's another issue!
The Paths.get() method returns the path-name of the specified directory in a format that the
Files.get() method understands.
The Files.get() method does a lazy traversal of the directory it is provided with, in the sense
that:
The contents of the root directory are listed as path-names, with each path-name being
relative to the root directory.
DirectoryScanRunner.java
package com.in28minutes.files;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
}
}
Console Output
./.classpath
./.project
./.DS_Store
./bin
./bin/files
./resources
./src
./src/files
DirectoryScanRunner.java
package com.in28minutes.files;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
Console Output
./.classpath
./.project
./.DS_Store
./bin
./bin/files
./bin/files/DirectoryScanRunner.class
./resources
./src
./src/files
./src/files/DirectoryScanRunner.java
DirectoryScanRunner.java
package com.in28minutes.files;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Predicate;
import java.io.IOException;
//Files.walk(currentDirectory, 4).forEach(System.out::println);
Files.walk(currentDirectory, 4).filter(predicate)
.forEach(System.out::println);
}
}
Console Output
./src/files/DirectoryScanRunner.java
DirectoryScanRunner.java
package com.in28minutes.files;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Predicate;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.function.BiPredicate;
import java.io.IOException;
Console Output
./src/files/DirectoryScanRunner.java
DirectoryScanRunner.java
package com.in28minutes.files;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Predicate;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.function.BiPredicate;
import java.io.IOException;
Console Output
./bin
./bin/files
./resources/
./src/
./src/files
./resources/data.txt
123.122
asdfghjkl
Apple
Bat
Cat
FileReadRunner.java
package com.in28minutes.files;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
Console Output
./resources/data.txt
123.122
asdfghjkl
Apple
Bat
Cat
FileReadRunner.java
package com.in28minutes.files;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
Console Output
123.122
asdfghjkl
Apple
Bat
Cat
./resources/data.txt
123.122
asdfghjkl
Apple
Bat
Cat
FileReadRunner.java
package com.in28minutes.files;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
Console Output
123.122
asdfghjkl
apple
bat
cat
You can also filter file content using the filter method.
FileReadRunner.java
package com.in28minutes.files;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
.map(String::toLowerCase)
.filter(str -> str.contains("a"))
.forEach(System.out::println);
}
}
Console Output
asdfghjkl
apple
bat
cat
FileWriteScanner.java
package com.in28minutes.files;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
./resources/file-write.txt
Apple
Boy
Cat
Dog
Elephant
Counter.java
package com.in28minutes.concurrency;
ConcurrencyRunner.java
package com.in28minutes.concurrency;
Snippet-1 Explained
Quite straightforward!
• Step 1. Read the value of i from memory into the CPU registers
• Step 2. Increment the value in the CPU registers
• Step 3. Store the incremented value back into the memory location of i
Let's say the threads concurrently invoke the increment and getI .
Assume the initial value of i is 15 . Let's take a closer look at a possible scenario:
• T1 calls increment() first, and successfully completes Step 1 of i++ . Gets a value of 15.
• The scheduler switches threads to T2 .
• T2 calls increment() first, and successfully completes Step 1 of i++ .Gets a value of 15.
• The scheduler switches to T1 . T1 resumes execution of increment() , and completes
steps 2 and 3 of i++ . Completes execution of increment() . Value of i is over-written
with 16 .
• The scheduler switches to T2 . In it's CPU register context, i is 15 , not 16 . T2 resumes
execution of increment() , and completes steps 2 and 3 of i++ . Completes execution of
increment() .Value of i in the Counter instance is over-written with 16 .
Ideally, the final value of i after two increment s should have been 17 . This would result
when the operations run serially one after the other.
There is a popular English saying: "There is many a slip, between the cup and the lip". This
refers to the fact that anything can happen between the time when we hold a cup of tea in our
hands, and the time when we actually get to take a sip of the tea.
The increment operation is not actually not as smooth as it seems. because it is not atomic,
slip-ups can and will often occur. This brings us to the concept of Thread-Safety.
Adding the keyword synchronized to the signature of a class method makes it thread safe.
Snippet-2
Counter.java
package com.in28minutes.concurrency;
private int i = 0;
Snippet-2 Explained
After adding synchronized keyword to the method increment , only one thread will be able to
execute the method, at a time. Hence, race condition is avoided.
synchronized keyword make the code thread safe. However, it causes all other threads to wait.
This can result in performance issues. Let's look at an example:
BiCounter.java
package com.in28minutes.concurrency;
ConcurrencyRunner.java
package com.in28minutes.concurrency;
Snippet-3 Explained
Just imagine, if there are a total of 12 threads wanting to increment counter . When one
thread is running, the other 11 have to wait!
BiCounterWithLocks.java
package com.in28minutes.concurrency;
import java.util.concurrent.locks.ReentrantLock;
Snippet-4 Explained
i++ and j++ are the pieces of code to protect. We use two locks lockForI and lockForJ. If a
thread wants to execute i++ , it needs to first get a lock to it - implemented using
lockForI.lock() . Once it performs the operation, it can release the lock lockForI.unlock() .
The Lock s lockForI and lockForJ are totally independent of each other. Therefore, a thread
T2 can execute j++ within incrementJ() , at the same time that thread T1 is executing i++
within incrementI() .
Atomic Classes
The operation i++ , small though it might seem, gave us quite a bit headache!
If a seemingly minute operation might need so much worrying about, imagine writing
concurrent data structures that manipulate linked lists with multiple link operations!
It would be really nice if someone could take care of these tiny operations for us, otherwise we
would have hard time finding out what code to definitely lock, and what code need not be!
Java addresses this issue for basic data types, by providing a few classes that are inherently
thread-safe. A good example is AtomicInteger , which is a wrapper around the int primitive
type.
Snippet-5 : AtomicInteger
BiCounterWithAtomicInteger.java
package com.in28minutes.concurrency;
import java.util.concurrent.atomic.AtomicInteger;
Snippet-5 Explained
Concurrent Collections
Java gave us a ready-made solution for thread-safe primitive data, with wrappers such as
AtomicInteger . The reasons this approach worked for int were:
Java provides classes like Vector (synchronized version of ArrayList ) which can provide
thread safety. But, these inherit the problems with using synchronized.
The code within the for loop does a get and then a put . It is not thread safe.
MapRunner.java
package com.in28minutes.collections;
import java.util.HashMap;
V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction)
LongAdder provides an atomic increment method, which we are making use of to make the
code a little more thread safe. However, different threads could be executing
occurrences.get() and occurrences.put() in parallel. A race condition can still occur.
ConcurrentMapRunner.java
package com.in28minutes.concurrency;
import java.util.Map;
import java.util.HashTable;
import java.util.concurrent.atomic.LongAdder;
Console Output
ConcurrentMapRunner.java
package com.in28minutes.concurrency;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder;
for(char character:str.toCharArray()) {
occurances.computeIfAbsent(character, ch -> new LongAdder()).increment();
}
System.out.println(occurances);
}
}
Console Output
ConcurrentHashMap
In ConcurrentHashMap , data structure is organized into disjoint regions. Access methods use
different Locks for different regions, reducing performance impact during concurrent access.
Read operations are not synchronized. Only write operations are synchronized.
Copy on Write approach is used in scenarios where reads greatly out number write’s on a
collection.
Copy on Write collections are typically used in Subject – Observer scenarios, where the
observers very rarely change. Most frequent operations would be iterating around the
observers and notifying them.
Snippet-9 : CopyOnWriteArrayList
CopyOnWriteArrayListRunner.java
package com.in28minutes.concurrency;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
Snippet-9 Explained
The CopyOnWriteArrayList.get method is NOT synchronized , since on the array that the
reads work, there will be no direct write through an add() .
Copy-On-Write collections should only be used for the specific usage scenarios, viz., very
large number of data structure traversals (data element reads only), compared to mutations
(data element insertions/deletions/modifications). In this way, high concurrency is achieved for
traversals.