JavaProgrammingForBeginners CourseBook
JavaProgrammingForBeginners CourseBook
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?
We believe that there has to be a better way to learn programming.
Why don't we learn programming step by step?
Why should it not be a lot of fun?
Why don't we solve a lot of problems and learn programming as a result?
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.
Introduction to Programming with Print-Multiplication-Table
Step 01: First Challenge : The Print-Multiplication-Table (PMT-Challenge)
Learning to program is a lot like learning to ride a bicycle. The first few steps are the most challenging ones.
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.
Having fun along the way is what we will aim to do.
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
. Compute the multiplication table for 5 , with entries from 1 to 10 .
. Display this table.
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
In this step, we:
Stated our first programming challenge, PMT-Challenge
Identified basic Java concepts to learn, to solve this challenge
Step 02: Introducing JShell
JShell is a programming tool, introduced in Java SE 9. JShell is a REPL interface. The term REPL refers to this:
'R' stands for Read; Read the input Java code
'E' means Eval; Evaluate the source code
'P' translates to Print; Print out the result
'L' indicates Loop; Loop around, and wait for the next input
How about starting off exploring Java? Are you game?
Snippet-1: Check the Java programming environment
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.
Launch up command prompt or Terminal.
Let type in java -version on the terminal and press enter.
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.
Snippet-2: Launch JShell
You can launch JShell by typing jshell at your terminal.
in28minutes$>jshell
When run, this command displays basic information about the installed JShell program. A jshell prompt then
appears, which waits for your input.
Snippet-3: Sample JShell input, using a built-in command
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.
Snippet-5: Getting out of JShell
The /exit command terminates the JShell program, and we are back to the terminal prompt.
jshell> /exit
| Goodbye
in28minutes$>
in28minutes$> jshell
in28minutes$>
Summary
In this step, we learned:
How to launch JShell from our terminal, and run a few commands on it
How to run Java code on the JShell prompt
Step 03: Welcome to Problem Solving
Lets try to break down the PMT-Challenge problem to be able to solve it.
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
in28minutes$> jshell
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, * .
Let's type in code shown below:
jshell> 5 * 3
$1 ==> 15
jshell>
Success!
Let's look at some terminology:
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: %
The following examples illustrate how to use them.
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>
jshell> 5 + 5 + 5
$8 ==> 15
jshell> 5 + 10 - 15
$9 ==> 0
jshell> 5 * 5 + 5
$10 ==> 30
jshell> 5 * 15 / 3
$11 ==> 25
jshell> 60 * 24
$1 ==> 1440
Solution 2
60 (seconds in a minute) multipled by 60 (minutes in an hour) multipled by 24 (hours in a day)
$jshell>60 * 60 * 24
$1 ==> 86400
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
| ^
jshell> 5 / 2
$1 ==> 2
jshell>
Surprise, Surprise! JShell seems to evaluate 5 / 2 to 2 instead of 2.5 . Where did we go wrong?
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 .
Let's try with a few floating point numbers:
Both 5.0 and 2.0 are of type double , the result is double . We get a result of 2.5 , as expected.
Let's do a mixed operation - using a floating point number and integer.
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.
Understanding Precedence of Operators
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 !
We write English left-to-right, and carry this habit to calculations as well.
In expressions with multiple operators, the order of sub-expression evaluation depends on operator precedence.
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 { + , - }.
In the expression 5 + 5 * 6 : 5*6 is evaluated first. So, 5 + 5 * 6 becomes 5 + 30 i.e. 35.
5 - 2 * 2 and 5 - 2 / 2 are evaluated by following the same rules.
jshell> (5 - 2) * 2
$4 ==> 6
jshell> 5 - (2 * 2)
$5 ==> 1
jshell>
Hmm! Error.
How do we print text?
Java has a built-in utility method called System.out.println() , that displays text on the console.
jshell> System.out.println(3*4)
12
We formed an expression, 3*4 , and passed it to System.out.println() , which is a built-in Java method.
System.out.println(3*4) is an example of a statement. It is a method call.
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() .
5 * 2 = 10 is not a single value. It is a piece of text with numeric characters, * and = .
In Java, we represent text using String . A String literal is a sequence of characters, enclosed within double
quotes: " and " .
jshell> System.out.println("5 * 2 = 10")
| 5 * 2 = 10
Congratulations! You have now figured out how to display not just numbers on the console, but text as well!
Summary
In this step, we:
Were introduced to the System.out.println() method for console output
Used this utility to print a single PMT-Challenge table entry
Step 08: Programming Exercise PE-02
Try and solve the following exercises:
. Print Hello World onto the console.
. Print 5 * 3 , as is.
. Print the calculated value of 5 * 3 .
. Print the number of seconds in a day, using the System.out.println method.
. Do a syntax revision for the code that you write for each of the above exercises. In your code, identify the
following elements:
Numeric and string literals
Expressions
Operators
Operands
Method calls
Step 09: Solutions to PE-02
Solution 1
Solution 2
jshell> System.out.println(5 * 3)
15
Solution 4
Case Sensitive
Java is 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.
jshell> System.out.println("hello world")
hello world
jshell> System.out.println("HELLO WORLD")
HELLO WORLD
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
In this step, we:
Were introduced to method call syntax, with System.out.println()
Discovered the uses of whitespace characters
Learned about Java escape sequences
Step 11: More On Method Calls
Let's look at method calls with a few more examples.
You know how to invoke a method with a single argument, courtesy System.out.println(3*4) . Other scenarios do
exist, such as
Calling a method without any arguments
Calling a method with several arguments
Let's check them out, using the built-in methods in Java Math class.
Method without parameters
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
In this step, we:
Understood how zero, or multiple parameters are passed during a method call
Step 12: More Formatted Output
System.out.println() can accept one value as an argument at a maximum.
To display the multiplication table for 5 with a calculated value, we need a way to print both numbers and strings.
For this we would need to use another built-in method System.out.printf() .
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 .
jshell> System.out.printf("5 * 2 = 10")
5 * 2 = 10$1 ==> java.io.PrintStream@4e1d422d
jshell>
The good news is, that if we call the println() method on this, the illegible stuff disappears.
Let's try to display a calculated value. In the example below 5*7 is calculated as 35 .
jshell> System.out.printf("%d %d %d", 5, 7, 5*7).println()
5 7 35
Let's use this to print the output in the format that we want to use for multiplication table:
jshell> System.out.printf("%d * %d = %d", 5, 7, 5*7).println()
5 * 7 = 35
Solution
Earlier, we used %d to print an int value. You cannot use %d to display floating point values.
jshell> System.out.printf("%d + %d + %d", 5.5, 6.5, 7.5).println()
| java.util.IllegalFormatConversionException thrown: d != java.lang.Double
| at Formatter$FormatSpecifier.failedConversion(Formatter.java:4331)
| at Formatter$FormatSpecifier.printInteger(Formatter.java:2846)
| at Formatter$FormatSpecifier.print(Formatter.java:2800)
| at Formatter.format(Formatter.java:2581)
| at PrintStream.format(PrintStream.java:974)
| at PrintStream.print(PrintStream.java:870)
| at #(57:1)
Summary
In this step, we:
Discovered how to do formatted output, using System.out.printf()
Stressed on the number and sequence of arguments for formatting
Explored the built-in format specifiers for primitive types
Step 13: Introducing Variables
In the previous steps, we worked out how to print a calculated value as part of our multiplication table.
jshell> System.out.printf("%d * %d = %d", 5, 1, 5 * 1).println()
5 * 1 = 5
jshell> number = 11
number ==> 11
How do we print 5 * 2 = 10 ?
We update i to 2 and execute the same code as before.
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>
By varying the value of i , we are able to print different multiples of 5 with the same statement.
Congratulations! You made a major discovery. Variables.
Summary
In this step, we:
Understood the need for variables
Observed what different parts of a variable definition mean
Seen what goes on behind-the-scenes when a variable is defined
Step 14: Programming Exercise PE-03 (With solution)
. Create three integer variables a , b and c .
Write a statement to print the sum of these three variables.
Change the value of a , and then print this sum.
Then again, change the value of b and print this sum.
Solution to PE-03
jshell>int a = 5
a ==> 5
jshell>int b = 7
b ==> 7
jshell>int c = 11
c ==> 11
jshell>System.out.printf("a + b + c = %d", a+b+c).println()
a + b + c = 23
jshell>a = 2
a ==> 2
jshell>System.out.printf("a + b + c = %d", a+b+c).println()
a + b + c = 20
jshell>b = 9
b ==> 9
jshell>System.out.printf("a + b + c = %d", a+b+c).println()
a + b + c = 22
jshell>
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.
Why does it result in error?
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
In this step, we:
Observed how declaring a variable is important
Understood the compatibility rules for basic Java types
Step 16: Variables: Behind-The-Scenes
Giving a value to a variable, during its declaration, is called its initialization.
The statement int number = 5 combines a declaration and the initialization of number .
The next statement int number2 = number is a little different.
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 :
A memory slot is allocated for number2 with size of int .
Value stored in number 's slot is copied to number2 's slot.
In example below, a = 100 , c = a + b , a = c are called assignments .
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>
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.
Step 17: Naming Variables
The syntax rules for variable definition are quite strict. Do we have any freedom with the way we name variables?
The answer is "yes", to some extent.
Variable Name Rules
Here are the Java rules for variable names, in a nutshell:
A variable name can be a sequence, in any order, of
Letters [ A-Z, a-z ]
Numerical digits [ 0-9 ]
The special characters ' $ ' ("dollar") and ' _ ' ("underscore")
With the following exceptions:
The name cannot start with a numerical digit [ 0-9 ]
The name must not match with a predefined Java keyword
Let's now see what kinds of errors the compiler throws if you violate these naming rules.
Error : Using a invalid character -
jshell>
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.
* NOT RECOMMENDED*
jshell> int NoOfGoals
NoOfGoals ==> 0
Values 4.0f;
Let's now look at how we create data of these types, and store them in memory.
We choose type for data, based on:
The type of data we want to store
The range of values we would want to store
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>
float occupies 32 bits. float literals need to have a suffix ' f ' (as in 4.0f ).
If this suffix f is omitted, a floating-point number is assumed to be a double . You cannot store a 64 bit value into
32 bits.
jshell> float f2 = 4.0
| Error:
| incompatible types: possible lossy conversion from double to float
| float f2 = 4.0
| ^-^
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.
jshell> double d2 = 67.0f
d2 ==> 67.0
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
In this step, we:
Looked at the primitive data types provided in Java
Understood the categories into which these types are divided
Saw what kind of data can be stored in each primitive type
Step 19: Choosing A Data Type
How do we choose the data type for a variable? Let's look at a few examples.
Example 1 : Goals in a Football match
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).
Summary
In this step, we:
Understood how to think while choosing a data type
Explored cases for integer, floating-point, character and boolean data
Step 20: The Assignment Operator =
We were first exposed to the assignment operator while discussing variable assignment.
We are allowed to use expressions to assign values to variables, using code such as c = a + b .
Let's look at a few additional ways assignment can be done.
jshell> int i = 10
i ==> 10
jshell> int j = 15
j ==> 15
jshell> i = j
i ==> 15
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> 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
In this step, we:
Looked at the assignment operator = in a more formal way
Looked at heavily used scenarios for assignment, such as increment and decrement
Step 21: Assignment Puzzles, and PMT-Challenge revisited
Now that we understand how to define, assign to, and use variables in expressions, it's time to push the boundaries
a bit. The following examples explore how we could play around with variables in our code.
Snippet-1 : Pre- and Post- Increment and Decrement
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 .
number++ is equivalent to the statement number = number + 1
jshell> number--
$3 ==> 7
jshell> number
number ==> 6
jshell> --number
$4 ==> 5
jshell> number
number ==> 5
jshell>
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
In the step, we:
Looked at the built-in Java increment and decrement operators
Explored the side-effects of prefix and postfix versions of these operators
Seen the advantages of compound assignment
Step 22: Some JShell Usage Tips
Shortcuts
i. Toggling to limits of current prompt entry
' Ctrl+a ' : to start of line
' Ctrl+e ' : to end of line
ii. Moving up and down history of completed prompt inputs
up-arrow key : move back in time
down-arrow key : move forward in time
iii. Reverse-keyword-search
Search for a term, in reverse order of JShell prompt inputs history : input ' Ctrl+r '
Scrolling within matches (in reverse order of history) : repeatedly input ' Ctrl+r '
/exit : resets and exits the JShell session, clearing all stored variables values.
Turns out Java has a class of logical operators, which can be used with operands within logical expressions,
evaluating to a boolean value.
While in school, you probably used the = symbol to compare numbers.
The world of Java is a little different. = is assignment operator. == is the comparison operator.
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>
if (condition) {
statement;
}
jshell> int i = 10
i ==> 10
jshell> System.out.println("i is less than 5")
i is less than 5
Using the if statement, we can control the execution of System.out.println("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.
Let's change i to 4 and execute the if condition.
jshell> i = 4
i ==> 4
jshell> if (i < 5)
...> System.out.println("i is less than 5");
i is less than 5
jshell>
jshell> number2 = 3
number2 ==> 3
jshell> if (number2 > number1)
...> System.out.println("number2 is greater than number1");
jshell>
Summary
In this step, we:
Understood the need for a conditionals
Were introduced to the concept of logical expressions, and conditional operators
Explored usage of the Java if statement
Step 24: Programming Exercise PE-04
. 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 .
. 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 .
. Have a variable store an integer. Create an if statement to find out if it's an even number.
Hint: Use operator % .
Step 25: Solution to PE-04
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> int num = 10
num ==> 10
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>
jshell> int i = 5
i ==> 5
jshell> if (i == 5)
...> System.out.println("i is odd");
i is odd
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>
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>
It is considered good programming practice to always use blocks in an if conditional. Here is an example:
if (i == 5) {
System.out.println("i is odd");
}
A block can also consist of a single statement! This improves readability of code.
Summary
In this step, we:
Saw the importance of using statement blocks with if conditionals
Understood how control flow can be made more readable
Step 27: Introducing Loops: The for Statement
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. As i varies from 1 through 10 , do some stuff
involving i
for loop is built this way:
This loop, when executed, prints the message "Hello World" on a separate line, for a total of 10 times.
Snippet-1 : PMT-Challenge Solution
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
. The first step is the initialization: i = 1 .
. After this, the statement System.out.printf("%d * %d = %d", 5, i, 5*i).println(); is executed once.
. Then, the update occurs: i++ .
. Immediately after, the condition i<=10 is evaluated. It returns true . Progress happens. Since it is a for
loop, the statement is executed again.
. Thereafter, this sequence is executed until i gets a value of 11 (due to successive updates).
. The moment i reaches 11 , , the condition becomes false . The looping within the for terminates.
Meanwhile, the Multiplication Table for 5 , for entries 1 through 10 has been displayed!
Now, wasn't that really elegant? It sure was!
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
In this step, we:
Observed why we need a looping construct, such as the for
Understood the mechanism of a for loop
Used a for loop in iteration, to solve our PMT-Challenge
Step 28: Programming Exercise PE-05
. Repeat the entire process at arriving at the Multiplication Table Print problem, now for the number 7 . Start
with a fresh JShell session, if you're still using an existing one for quite some time (Rinse, and repeat!).
. Use the final solution to print Multiplication Tables for 6 and 10 .
. Print the integers from 1 to 10 .
. Print the integers from 10 to 1 .
. Print the squares of the integers from 1 to 10 .
. Print the squares of the first 10 even integers.
. Print the squares of the first 10 odd integers.
Step 29: Solution to PE-05
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
Solution 4
This is the first time we are using i-- . Isn't this interesting?
Solution 5
Solution 6
update of a for loop can do a lot of things. Here, we are using i += 2 .
Solution 7
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.
. Empty initialization, Empty Statement
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>
. 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>
Summary
In this step, we saw that all components of a for loop are optional:
initialization
condition
updation
statement block
Step 31: A Review Of Basic Concepts
Before we go further, let's quickly get a Big Picture of what we are doing here!
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!
Any computer is made up of two basic layers:
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.
If the human body were a computer,
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.
We solved a programming challenge, the PMT-Challenge using basic Java constructs.
We used JShell REPL to learn the following concepts, Step by-step:
Expressions, Operators and Statements
Variables and Formatted Output
Conditionals and Loops
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:
Operators, variables and expressions
Built-in formatted output
Conditionals for control-flow, and
Iteration through loops
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 them, and your new found popularity would
slide downhill.
Surely a more elegant solution exists, a pattern waiting to unravel itself.
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
methodName is the name of the routine
method-body is the set of statements
Summary
In this step, we:
Examined the need for labeling code, and its reuse
Learned that a Java method fits the bill
Saw how a method definition looks like, conceptually
Step 01 : Defining A Simple Method
We will start off, by writing a simple method that prints " Hello World " twice on your screen.
jshell> sayHelloWorldTwice
| Error:
| cannot find symbol
| symbol: variable sayHelloWorldTwice
| sayHelloWorldTwice
| ^----------------^
jshell> sayHelloWorldTwice()
Hello World
Hello World
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
Step 04: Methods with Arguments
We wrote the method sayHelloWorldTwice to say Hello World twice. In the programming exercise, we wanted to
print Hello World thrice and we wrote a new method sayHelloWorldThrice .
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".
Now, that probably would test your patience as well!
How to write a method 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.
External input to a method is given as a method argument, or parameter.
To support arguments during a call, the concept of a method needs to be tweaked to:
method-body
within the parentheses. argName represents the argument, and ArgType is its type. The next example should clear
the air for you.
Snippet-01: Method with an argument: Definition
Let's look at a method definition using an argument numOfTimes .
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.
However, the method does not do anything. Isn't it sad?
Snippet-02 : Passing and accessing a parameter
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.
jshell> sayHelloWorld(1)
1
jshell> sayHelloWorld(2)
2
jshell> sayHelloWorld(4)
4
jshell> /edit sayHelloWorld
| modified method sayHelloWorld(int)
jshell>
In the above example, we printed numOfTimes a total of numOfTimes for each method call.
We print 2 two times and 4 four times.
jshell> sayHelloWorld(2)
2
2
jshell> sayHelloWorld(4)
4
4
4
4
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. .
Snippet-5 : Parameter type mismatch
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
In this step, we:
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
Step 05: Exercise Set PE-02 (With Solutions)
Exercises
. Write a method printNumbers(int n) that prints all successive integers from 1 to n .
. Write a method printSquaresOfNumbers(int n) that prints the squares of all successive integers from 1 to
n.
Solution-01
jshell> void printNumbers(int n) {
...> for(int i=0; i< n; i++) {
...> System.out.println(i);
...> }
...> }
| created method printNumbers(int)
jshell>
Solution 2
void printMultiplicationTable() {
for (int i=1; i<=10; i++) {
System.out.printf("%d * %d = %d", 5, i, 5*i).println();
}
}
You can call it to print 5 table.
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
In this step, we:
Revisited the PMT-Challenge solution we had earlier
Enclosed its logic within a method definition, printMultiplicationTable()
Haven't fully explored its power yet!
Step 08: Methods With Arguments, And Overloading
The real power of a definition such as printMultiplicationTable() is when we arm it with arguments. Not verbal
arguments, but "value" ones. That's right!
We will modify printMultiplicationTable() to have it accept a parameter, which would make it more flexible.
void printMultiplicationTable(int number) {
for (int i=1; i<=10; i++) {
System.out.printf("%d * %d = %d", number, i, number*i).println();
}
}
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.
Soak in the power, simplicity and elegance of this program, folks!
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
In this step, we:
Enhanced the logic of printMultiplicationTable() by passing arguments
Added flexibility to the PMT-Challenge solution
Were introduced to the concept of method overloading
Step 09: Methods With Multiple Arguments
It is possible, and pretty useful, to define methods that accept more than one argument.
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
In this step, we:
Used our understanding of method overloading to define our own methods
Step 10: Returning From A Method
A method can also be coded to return the result of its computation. Such a result is called a return value.
A lot of built-in Java methods have return values, the most notable we have seen being Math.min() and
Math.max() .
Summary
In this step, we:
Understood the need for a return mechanism with methods
Explored the syntax for a return statement
Wrote our own method sumOfTwoNumbers() with a return value, and saw how it became more flexible
Step 11: Exercise-Set PE-04 (With Solutions)
Let's now try a few exercises on methods that return values.
Exercises
. Write a method that returns the sum of three integers.
. Write a method that takes as input two integers representing two angles of a triangle, and computes the third
angle. Hint: The sum of the three angles of a triangle is 180 degrees.
Solution-01
Solution-02
india , usa and netherlands are all different objects of type Country .
We actually left the contents of the class Country bare.
A class does often include both data (member variables) and method definitions. More on this at a relevant time.
Let's consider another example:
Summary
In this step, we:
Saw how class creation actually creates a new type
Understood how to create instances of classes - objects
Step 03: Adding A Method To A class
A method defined within a class , denotes an action than can be performed on objects of that class .
Let's define a method inside Planet - to revolve() !
jshell> class Planet {
...> void revolve() {
...> System.out.println("Revolve");
...> }
...> }
replaced class Planet
update replaced variable planet, reset to null
update replaced variable earth, reset to null
update replaced variable venus, reset to null
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).
Invoking revolve() through syntax such as earth.revolve() succeeds, because earth is an object of type
Planet .
Summary
In this step, we:
Learned how to add a method definition to an existing class
Discovered how to invoke it, on objects of the class
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
You can compile java code using javac Planet.java . You can see that a new file is created Planet.class . This
contains your bytecode .
command-prompt> javac Planet.java
command-prompt> ls
Planet.class Planet.java_
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 .
The definition of main() needs to have this exact synatx:
public static void main(String[] args) { /* <Some Code Goes In Here> */ }
A few details:
void is its return type
`public and static``` are reserved Java keywords. More on these later sections.
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) {
}
}
class Planet {
void revolve() {
System.out.println("Revolve");
}
public static void main(String[] args) {
Planet earth = new Planet();
earth.revolve();
}
}
Console Commands
class Planet {
void revolve() {
System.out.println("Revolve");
}
class Planet {
void revolve() {
System.out.println("Revolve");
}
Console Commands
class Planet {
void revolve() {
System.out.println("Revolve");
}
Console Commands
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.
Snippet-04: Messing-up main() - v4
Call a non existing method earth.revolve1(); .
Planet.java
class Planet {
void revolve() {
System.out.println("Revolve");
}
Console Commands
We misspelled the name of the revolve() method, to revolve1() . Rightly, got rapped on our knuckles by the
compiler.
Summary
In this step, we:
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!
Step 07: The JVM, JRE And JDK
What are JVM, JRE and the JDK?
Apart from the fact that all start with a 'J', there are important differences in what they contain, and what they do.
The following list gives you a bird's eye view of things.
The JVM runs your program bytecode.
JRE = JVM + Libraries + Other Components
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 = JRE + Compilers + Debuggers
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.
An interesting way to remember this organization is:
JDK is needed to Compile and Run Java programs
JRE is needed to Run Java Programs
JVM is needed to Run Bytecode generated from Java programs
Let's check a couple of scenarios to test your understanding.
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
Application Developers need ==> JDK
Application Users need ==> JRE
Summary
In this step, we:
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
Introducing Java Software Development with Eclipse
Features of the Eclipse IDE (Integrated Development Environment)
Installation & Configuration
Workspace Creation & Configuration
Project Creation & Organization
JRE Version Selection and Included Libraries
Eclipse Java Perspectives
Source Files Organization into Folders
Packages?
IDE User Interface Description
Perspective
Views
Console Content and Display Options
Creating a new Java class in Eclipse (And related source file)
Package name
A Java solution to solving a problem could be composed of several application components. It is
considered good programming arctice to identify a class for each distinct component. Package is the
Java way to organize classes in source code.
Analogy: Eatables in a Refrigerator (Freezer, Chill-Tray, Vegetable-Tray, Bottle-Rack)
Public method stub : can be used to create a default public static void main(String[] args)
signature
(Include Snapshots)
Class creation pop-up, with selected options
Default generated source code in editor window
Customizing the generated source code to our needs
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
Option is avaibale only for classes that have a main() method
Using Eclipse in Debug Mode
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.
Prior to debugging application, we need to set few Break-Points. A Break-Point is a statement in the source of
the program, before which execution under debug mode is suspended, and control is passed to the
programmer.
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:
Reading & modifying data variables
Resuming program execution
Re-executing current statement
Skipping all statements till the next break-point
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).
Setting and Removing a Break-point (also Toggling)
Stepping over a method call, or into and out of a method body (during a method call)
Stepping into int print() and int print(int) and int print(int,int,int) gives us interesting
infromation, but stepping into System.out.printf can freak you out! So, you may want to step over it.
For nested method calls, examine the method call Stack Trace (Call child, call parent relationship)
Example : int print() , int print(int) and int print(int,int,int) method call chain of class
MultiplicationTable .
Observe that execution exits from the for loop when the condition i<=10 is no longer true .
Re-executing a line of code
Skipping execution of all statements till next break-point
Ignoring all remaining breakpoints, resume execution of code till completion
Eclipse IDE Keyboard Shortcuts
Code Text Editor Shortcuts
New Project/Class Creation Shortcuts
Search for a Class
Differences between JShell and IDE
Variables just declared, but used without an initialization, will flag a compilation error in an IDE! But not so, it seems,
in JShell. In regular software, variable initialization at point of declaration (called a "defintion", as we already know)
is mandatory.
Snippet-1 : JShell redefines variables on demand
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;
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
We have now split the code into two source files:
MultiplicationTable.java: Houses the MultiplicationTable class definition, with some methods we
need.
MultiplicationRunner.java: Acts as the client of the MultiplicationTable class , to invoke its
functionality. It defines a main() method, that instantiates a MultiplicationTable object, and invokes its
print() method.
After this code was rearranged, we still got it to work!
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.
Snippet-02: print() overloaded
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
The default table for 5 , with entries fixed within 1 to 10 .
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
The full code:
MultiplicationTable.java
package com.in28minutes.firstjavaproject;
MultiplicationRunner.java
package com.in28minutes.firstjavaproject;
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.
A Solution: Code Reuse
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.
Let's now reorganize this part of the code.
Snippet-4: Code Reuse
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:
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) .
Snippet-6 : Extending code reuse (contd.)
MultiplicationTable.java
public static void print() {
print(5, 1, 10);
}
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) .
package com.in28minutes.firstjavaproject;
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.
Snippet-8 : Extending Code Reuse (Contd.)
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
In this step, we:
Explored how to reorganize the code for PMT-Challenge into a class
Understood that overloading works the same way for class methods
Observed that code reuse is possible across overloaded versions of a class method
Object Oriented Progamming (OOP)
How do you design great Object Oriented Programs?
Let's find out
Recommended Videos:
Object Oriented Progamming - Part 1 - https://www.youtube.com/watch?v=NOD802rMMCw
Object Oriented Progamming - Part 2 - https://www.youtube.com/watch?v=i6EztA-F8UI
Step 01: Object Oriented Progamming (OOP) - Basic Terminology
Let's consider a few examples before we get to Object Oriented Progamming.
Humans think in a step by step process.
Let's say I've to take a flight from London to New York. This is how I would think:
Take a cab to London Airport
Check in
Pass Security
Board the flight
Wish the Hostess
Take Off
Cruise
Land
Get off the plane
Take a cab to ..
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
State : Its data
Behavior : Its operations
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.
Each Planet has its own state:
name : "Earth", "Venus"
revolve() : They revolve round the sun in different orbits, at different speeds
Summary
In this step, we:
Understood how OOP is different from Prodedural Programming
Learned about a few basic OOP terms
Step 02: Programming Exercise PE-01
Exercises
In each of the following systems, identify the basic entities involved, and organize them using object oriented
terminology:
. Online Shopping System
. Person
Solution-1: Online Shopping System
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.
We will start with two java files:
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
Snippet-1: MotorBike Class
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.
We created two classes because we believe in Seperation of Concerns :
MotorBike class is responsible for all its data and behavior.
Summary
In this step, we:
Defined a MotorBike class allowing us to further explore OOP concepts in the next steps
Step 04: Programming Exercise OO-PE-02
Exercises
. 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
The Art Of Computer Programming
Effective Java
Clean Code
Step 05: MotorBike - Representing State
An object encapsulates both state and behavior.
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.
Snippet-1 : MotorBike with state variable speed
MotorBike.java
package com.in28minutes.oops;
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
int speed; within MotorBike , defines a member variable.
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.
Classroom Exercise CE-OO-01
. 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
Step 07: MotorBike - get() and set() methods
In the previous step. we were merrily modifying the speed attribute within the ducati and honda objects directly,
from within the main() program.
public class MotorBikeRunner {
public static void main(String[] args) {
//... Other code
ducati.speed = 100;
honda.speed = 0;
}
}
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 .
Snippet-3 : MotorBike class with private attributes
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.
Let's look at this.speed = speed; in the body of method setSpeed() :
An member variable always belongs to a specific instance of that class .
A method argument behaves just like a local variable inside that method.
To differentiate between the two, this is used. The expression this.speed refers to the member variable
speed of a Motorbike object. setSpeed() would be invoked on that very 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.
Classroom Exercise CE-OO-02
. Update the class Book to make sure it no longer breaks Encapsulation principles.
Solution
TODO
Summary
In this step, we:
Learned about the need for access control to implement encapsulation
Observed that Java provides access modifiers (such as public and private ) for such control
Understood the need for get() and set() methods to access object data
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!
Current Ducati Speed is : 100
Current Honda Speed is : 80
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
In this step, we:
Understood how access control forces us to provide get() methods as well
Explored a few Eclispe tips to generate get() and set() versions for each class attribute
Step 10: Default Object State
What happens if a data element inside an object is not initialized with a value?
Snippet-5 : Default Initialization of Object State
MotorBike.java
//Same as before
MotorBikeRunner.java
package com.in28minutes.oops;
Console Output
Current Honda Speed is : 0
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
In this step, we:
Learnt that default values are assigned to object member variables.
Step 10: Encapsulation: Its Advantages
At the heart of encapsulation, is the need to protect object's state. From whom? Other objects.
Let's look at an example to understand Encapsulation better.
Snippet-7 : Advantage of Encapsulation
MotorBike.java
package com.in28minutes.oops;
MotorBikeRunner.java
package com.in28minutes.oops;
Console Output
Bike started!
Current Ducati Speed is : -100
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.
package com.in28minutes.oops;
//Other code as is
MotorBikeRunner.java
//Same as before
MotorBikeRunner.java
package com.in28minutes.oops;
package com.in28minutes.oops;
MotorBikeRunner.java
package com.in28minutes.oops;
ducati.setSpeed(100);
ducati.increaseSpeed(100);
honda.increaseSpeed(100);
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);
package com.in28minutes.oops;
//Same as before
MotorBikeRunner.java
//Same as before
package com.in28minutes.oops;
MotorBikeRunner.java
//Same as before
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
The Art Of Computer Programming
Effective Java
Clean Code
5
5
5
Step 13: Introducing Constructors
When we create Ducati and Honda motorbikes, we may want to configure them with some start speeds.
Suppose our whim is that a Ducati bike starts with 100 mph, and a Honda with 200 mph.
Snippet-1: MotorBike Constructor
MotorBike.java
package com.in28minutes.oops;
MotorBike(int speed) {
if(speed > 0)
this.speed = speed;
}
//Same as before
MotorBikeRunner.java
package com.in28minutes.oops;
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
In this step, we were introduced to the concept of a class constructor
Programming Exercise PE-OO-04, And More On Constructors
Exercises
. 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
The Art Of Computer Programming
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 .
When we started off , we created instances of MotorBike classes using:
MotorBike ducati = new MotorBike();
Did you notice something familiar? Doesn't the expression new MotorBike() look like a constructor call?
As it turns out, it is a constructor call on MotorBike !
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() {
}
};
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
The compiler flags an error with MotorBikeRunner.java! MotorBike yamaha = new MotorBike(); is failing
compilation. Why?
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.
Snippet-3 : Programmer-defined default constructor
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;
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.
Step 01: The Integer Types
Integers are not much of a mystery, are they? They've been part of us since our school days, and we normally prefer
them over other numbers (they scare us less!).
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:
Byte : for byte
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>
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
In this step, we:
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
Step 02: Integer Representations, And Other Puzzles
In a decimal system, the allowed digits are 0 through 9 . When a value of 10 is encountered, the number of digits
increases by 1, and its representation is " 10 ".
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.
Let's see how Java supports these three number systems.
Snippet-01 : Storing Octal and Hexadecimal in Integer types
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.
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.
Snippet-03 : Increment & Decrement Operators
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.
jshell> int m = 10;
m ==> 10
jshell> int n = ++m;
n ==> 11
jshell> m
m ==> 11
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.
jshell> int k = 10;
k ==> 10
jshell> int l = k--;
l ==> 10
jshell> k
k ==> 9
jshell> int p = 10;
p ==> 10
jshell> int q = --p;
q ==> 9
jshell> p
p ==> 9
jshell>
Summary:
In this step, we:
Looked at the number-systems supported in Java for integers
Examined how prefix and post-fix versions work for increment and decrement operators
Solution to CE-01
BiNumber.java
package com.in28minutes.primitive.datatypes;
BiNumberRunner.java
package com.in28minutes.primitive.datatypes;
Console Output
5
6
4
6
Step 05: Floating-Point Types
You would recall there are two types in Java to support floating-point numbers:
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.
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 .
jshell> float f = 34.5678f;
f ==> 34.5678
jshell> int i = f;
| Error:
| incomaptible types: possible lossy conversion from float to int
| int i = f;
|_______^
jshell> int i = (int)f;
i ==> 34
jshell> float fl = i;
fl ==> 34.0
jshell>
Summary
In this step, we:
Saw how we create literals and variables of the floating-point types
Understood the differences between double and float
Step 06: Introducing BigDecimal
Compact though they are, double and float are not very precise representations of floating-point numbers.
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
In this step, we:
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
Step 06: BigDecimal Operations
Let's look at a few other operations defined in the BigDecimal class.
Snippet-01: Arithmetic Operations
All BigDecimal operations support only BigDecimal operands.
Summary
In this step, we:
Explored the BigDecimal methods for doing some basic arithmetic
Found that BigDecimal has constructors accepting most basic Java numeric types
Step 07: Classroom Exercise CE-02
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))
In essence, write a SimpleInterestCalculator class that can be used in the following manner:
Solution To CE-02
SimpleInterestCalculatorRunner.java
package com.in28minutes.primitive.datatypes;
import java.math.BigDecimal;
package com.in28minutes.primitive.datatypes;
import java.math.BigDecimal;
Console Output:
6187.50000
Tip: The import Statement
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 .
package java.lang.* is imported by default.
The .* suffix indicates that all classes in the package java.lang are being imported.
Summary
In this step, we:
Used BigDecimal values in a stand-alone Java program
Learnt how to make use of built-in Java packages, through the import statement
Step 08: boolean , Relational and Logical Operators
The Java boolean data type is one that holds only one of two values: true or false . Both 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.
Snippet-01 : Relational Operators : Recap
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:
|| : OR
! : NOT
An expression with && evaluates to true only if both its boolean operands evaluate to true .
jshell> true && true
$6 ==> true
jshell> true && false
$7 ==> false
jshell> false && true
$8 ==> false
jshell> false && false
$9 ==> false
jshell>
Snippet-02 Explained
The next example helps us visualize truth tables for the prominent logical operators.
jshell> true || true
$1 ==> true
jshell> true || false
$2 ==> true
jshell> false || true
$3 ==> true
jshell> false || false
$4 ==> false
jshell> true ^ true
$5 ==> false
jshell> true ^ false
$6 ==> true
jshell> false ^ true
$7 ==> true
jshell> false ^ false
$8 ==> false
jshell> !true
$9 ==> false
jshell> !false
$10 ==> true
jshell> int x = 6;
x ==> 6
jshell> !(x > 7)
$11 ==> true
jshell>
Summary
In this step, we:
We're introduced to the boolean primitive type
Understood where logical operators are used in Java programs
Explored the truth-tables of commonly used logical operators
Step 09: Short-Circuit Evaluation (With Puzzles)
Consider code below. The expression j > 15 && i++ > 5 evaluates to false as expected. j>15 is false as j
has a value of 15 .
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> char cn = 65;
cn ==> 'A'
Integer arithmetic can be performed on char data.
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
In this step, we:
Were introduced the the char data type
Learned that Unicode takes the character set beyond your keyboard
An ascii character is char value, encoded by an integer value
Step 11: Programming Exercise PE-02
Exercise Set
. 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:
package com.in28minutes.primitive.datatypes;
MyChar.java
package com.in28minutes.primitive.datatypes;
Console Output :
false
Step 13: Solution To PE-02, Part 2 - isDigit()
MyCharRunner.java
package com.in28minutes.primitive.datatypes;
MyChar.java
package com.in28minutes.primitive.datatypes;
Console Output :
false
false
Step 14: Solution To PE-02, Part 3 - Other Methods
MyCharRunner.java
package com.in28minutes.primitive.datatypes;
MyChar.java
package com.in28minutes.primitive.datatypes;
Console Output :
false
true
false
true
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
Step 15: The Primitive Types - A Review
In this section, we built on our knowledge of the primitive Java types.
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.
Introducing Conditionals - if, switch and more
Decision making is a part of our daily lives. Computers do tasks for humans, so even programs need to make
decisions. They do this by checking logical conditions, which evaluate to boolean values.
In the following steps, we will explore the following conditional statements:
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!
Programming Challenge : Design A Menu (The Menu-Challenge)
Ask the User for input:
Enter two numbers
Choose the arithmetic to do on those:
Add
Subtract
Multiply
Divide
Perform the Operation
Publish the Result
An example scenario could be:
Enter First Number
2
Enter Second Number
4
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
Enter your choice of operation
3
The Result Is : 8
Summary
In this step, we:
Discussed how input affects the control-flow of a program
Listed out the conditionals available with Java
Selected a programming challenge to help us learn about conditions
Step 01: The if and if - else Conditionals
An if statement is the most basic way to manage control-flow in a program. A boolean condition is tested, and if
found to be true , some code is executed. Otherwise, that code is not run.
"Do Something when condition is true "
Conceptually, the if statement looks like this:
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 "
An if - else statement boils down to the following:
if(condition) {
<if-body>
} else {
<else-body>
}
If condition is found to be true , the statement block <if-body> is executed, otherwise <else-body> is run.
Let's now look at a examples that make use of these conditionals.
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");
..>> }
com.in28minutes.ifstatement.examples;
Console Output
i != 25
Snippet-03 : chained if-else v2
We would want to test a number for 3 conditions - value is 24 , value is 25 or value is not 24 and 25 . Let's
consider the code from the example below.
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.
Why the extra print?
We started off trying to check fro 3 possibilities: * i being equal to 25 * i being equal to 24 * Neither of the
above
We get wrong output, because i == 25 in first if is independent from i == 24 in the if - else that follows it.
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
In this step, we:
Learned about the if and if - else conditionals
Tried out a few examples to see how they are used
Step 02: The if - else if - else Conditional
The if - else if - else statement solves the issue we faced, while trying to test more than two conditions.
Let's see an example.
Snippet-01 : Matching The if Clause
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
Let's now try giving i a different value, say 24 .
Snippet-02: Matching The else if Clause
IfStatementRunner.java
com.in28minutes.ifstatement.examples;
//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
With i holding 24 , a different condition (the one coresponding to i == 24 ) evaluates to true .
Let's now try to get a match with the else clause, the only one unexplored so far.
Snippet-03: Matching The else Clause
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
In this step, we:
Learned about the if - else if - else conditional
Found out how to test when each different clause gets executed
Understood the guarantees made by Java regarding the conditional's code execution
Step 03: Puzzles on if
Try and guess the outputs of the following puzzles.
Programming Puzzle PP-01
public class puzzleRunner {
Answer:
2
Programming Puzzle PP-02
Answer:
l < 20
Who Am I?
Programming Puzzle PP-03
Answer:
Nothing is printed. Because the code is structured this way.
if(m > 20) {
if(m < 20)
System.out.println("m > 20");
else
System.out.println("Who Am I?");
}
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
Programming Puzzle PP-05
**Answer : **
6
In the absence of explicit blocks, Only the statement next to the if statement is considered to be part of the if
statement block.
Step 04: Reading User Input
Look at the statement of Menu-Challenge once again:
Menu-Challenge
Ask the User for input:
Enter two numbers
Choose an Arithmetic Operation to perform on them:
Add
Subtract
Multiply
Divide
Perform the Operation
Publish the Result
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.
We will do just that, in our next example.
Snippet-01: Reading console input
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
The number you entered is: 35
Summary
In this step, we:
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
Step 05: Menu-Challenge : Reading More Input
The Menu-Challenge does not stop at a single user input. It requires a total of three numbers to be typed in at the
console. So how do we continue asking for input, and read them when they are given?
Snippet-01 : Implementing Menu-Challenge
MenuScanner.java
package com.in28minutes.ifstatement.examples;
import java.util.Scanner;
Console Output
Enter Number1: 25
Enter Number2: 50
Operation Choices Available
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
Enter Choice: 4
Your Inputs Are
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
In this step, we:
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
Step 06: Menu-Challenge - Reading Input, Computing Result, Displaying Output
We don't think that after the previous step, anything can stop you from completing the Menu-Challenge. Here is
one such way, from head-to-toe.
Snippet-01 : All computations
Our solution above does a very simple thing. It combines the mechanisms we learned for reading input and testing
conditions, to solve Menu-Challenge.
The if - else - else if statement has a total of 5 clauses:
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;
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
Operation Choices Available
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
Enter Choice: 2
Your Inputs Are
Number1: 23
Number2: 10
Choice: 2
Result = 13
Console Output
Enter Number1: 25
Enter Number2: 35
Operation Choices Available
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
Enter Choice: 5
Your Inputs Are
Number1: 23
Number2: 10
Choice: 5
Invalid Operation
Summary
In this step, we:
Combined our knowledge of conditionals, with our recent learning on taking multiple console inputs
Ultimately solved the Menu-Challenge problem
Step 07: Introducing switch
If you remember, our initial list of conditionals to manage control-flow, also had a switch statement. A switch
also tests multiple conditions, and just like an else clause, it can handle the default possibility. Conceptually, a
switch statement looks like the following:
switch(condition) {
case 1:
//<statements>
break;
case 2:
//<statements>
break;
//...
default:
//<statements>
break;
}
}
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
package com.in28minutes.ifstatement.examples;
import java.util.Scanner;
Console Output
Enter Number1: 23
Enter Number2: 10
Operation Choices Available
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
Enter Choice: 2
Your Inputs Are
Number1: 23
Number2: 10
Choice: 2
Result = 13
Console Output
Enter Number1: 25
Enter Number2: 35
Operation Choices Available
1 - Add
2 - Subtract
3 - Multiply
4 - Divide
Enter Choice: 5
Your Inputs Are
Number1: 23
Number2: 10
Choice: 5
Invalid Operation
Summary
In this step, we:
Explored the switch conditional
Saw how it could implement the same control-flow as the if family of conditionals
Learned the importance of coding it correctly, to enjoy Java control-flow guarantees
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.
Programming Puzzle PP-01
Answer:
2
3
default
Programming Puzzle PP-02
Answer:
Number is 2 or 3
Programming Puzzle PP-03
Answer:
default
Programming Puzzle PP-04
Answer:
default
Programming Puzzle PP-05
Answer:
Compiler Error
Programming Puzzle PP-06
Answer:
Compiler Error
Step 09: Comparing The if Family, And switch
Let's compare and contrast these two approaches, to gain some experience on how to choose a conditional.
First comes an example using the if - else if - else statement.
Snippet-01 : Formatted Output Using if
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) {
System.out.printf("result : %d", number1 * number2).println();
} else if(choice == 4) {
System.out.printf("result : %d", number1 / number2).println();
} else {
System.out.println("Invalid Operation");
}
}
}
Console Output
number1 : 5
number2 : 2
choice : 1
result : 7
Next in line, is an example involving a switch .
Snippet-02 : Formatted Output Using switch
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
In this step, we:
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.
package com.in28minutes.ifstatement.examples;
Summary
In this step, we:
Discovered a more compact Java conditional, the ?: operator
Saw that in most cases it behaves like an if - else construct
Loops
TODO
Revisiting Java loop constructs: for and while
If you may recall, the structure of a for loop is:
for(initialization; condition; update) {
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.
Snippet 1 : First for loop puzzle
jshell> for(int i=0; i <= 10; i++) {
__**...>>** System.out.print(i + " ");
__**...>>** }
0 1 2 3 4 5 6 7 8 9 10
jshell> for(int i=0; i <= 10; i = i+2) {
__**...>>** System.out.print(i + " ");
__**...>>** }
0 2 4 6 8 10
jshell> for(int i=1; i <= 10; i = i+2) {
__**...>>** System.out.print(i + " ");
__**...>>** }
13579
jshell> for(int i=11; i <= 10; i = i+2) {
__**...>>** System.out.print(i + " ");
__**...>>** }
jshell> for(int i=11; i <= 20;) {
__**...>>** System.out.print(i + " ");
__**...>>** i++;
__**...>>** }
11 12 13 14 15 16 17 18 19 20
jshell> int i = 20;
i ==> 20
jshell> for(i <= 30; i++) {
__**...>>** System.out.print(i + " ");
__**...>>** }
21 22 23 24 25 26 27 28 29 30
jshell>
Snippet-1 Explained
All the three control components of a for loop are dispensable
initialziation
condition
update
Reference Types
Step 01: Introducing Reference Types
What happens in the background when we create objects?
new Planet() creates an object on the Heap. In above example Planet@3f49dace , the object is stored on the
Heap at address 3f49dace .
jshell> Planet jupiter = new Planet();
jupiter ==> Planet@31a5c39e
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
In this step, we:
Looked at what references are
Had a look at what the contents of a reference variable look like
Step 02: References: Usage And Puzzles
Let's spend some time playing with reference variables.
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 .
jshell> Animal dog = new Animal(12);
dog ==> Animal@27c20538
jshell> Animal cat = new Animal(15);
cat ==> Animal@6e06451e_
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 .
jshell> nothing.id = 10;
$1 => 10
jshell> cat.id
$2 => 10
You can nothing.id prints the value of the object referenced by dog because they are pointing to the same
object.
Here are couple of important things to note:
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.
Comparing primitive variables compares their values.
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
jshell> cat == dog;
$6 ==> false
jshell> cat == ref;
$7 ==> 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!
jshell> dog == dog2;
$8 ==> false
jshell>
Summary
In this step, we:
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
Step 03: Introducing String
A sequence of characters, such as "Hello" , "qwerty" and "PDF" is very different from other pieces of data.
In Java, a sequence of characters is typically represented by String class .
String provides several built-in utility methods.
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.
jshell> String str = "Test";
str ==> "Test"
jshell> BigDecimal bd = new BigDecimal("1.0");
bd ==> 1.0
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> String biggerString = "This is a lot of text";
biggerString ==> "This is a lot of text"
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
In this step, we:
Were introduced to the String class, that represents sequences of characters
Explored a few utility methods of the String class
Step 04: Programming Exercise PE-01, And String Utilities
Exercise
. Write a method to print the individual characters of a given text string, separately.
Solution To PE-01
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.
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
In this step, we:
Used our String programming skills on a small challenge.
Explored a set of simple, yet useful String utilities.
Step 05: String Immutability
What picture forms in your mind on hearing the word "immutable"? Someone whose voice cannot be muted out?
Or is it the other way round?
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
In this step, we:
We understood that String objects are immutable
Observed how common String utilities return a new String.
Step 06: More String Utilities
The symbol + denotes addition, and addition only, right? Any school kid will tell you that, or laugh at you if you
disagree.
Heck, we even saw how to use it with the primitive numeric types, such as int , double and others related to it.
Java does not strictly follow your arithmetic text book.
+ can also be used as a String concatenation operator.
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 .
jshell> int i = 20;
i ==> 20
jshell> System.out.println("Value is " + i);
Value is 20
jshell> System.out.println("Value is " + i + 20);
Value is 2020
jshell> System.out.println("Value is " + (i + 20));
Value is 40
replace(String, String) replaces all occurrences of the first sub-string with the second one. It also works with
char data type.
jshell> "abcd".replace('a', 'z');
$9 ==> "zbcd"
jshell> "abcd".replace("ab", "xyz");
$10 ==> "xyzcd"
jshell>
Summary
In this step, we:
Learned that the + operator is overloaded for String concatenation
Observed how + interprets its operands, depending on the context
Noticed a few more String utility methods, such as join() and replace()
Step 07: Storing mutable text
StringBuffer and StringBuilder allow you to modify a string literal in-place.
Otherwise, use StringBuilder , since it offers better performance in both execution-time and memory usage.
Summary
In this step, we:
Learned about StringBuffer and StringBuilder
Step 08: Introducing Wrapper Classes
Each primitive type in Java has a corresponding built-in wrapper class. Wrapper classes are also immutable (As
well as final , more on this a little later).
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
You can also use valueOf() method within types such as Integer and Float to create a wrapper object.
Summary
In this step, we:
Discovered that there are two ways to create a wrapper object for primitive data
Learned that valueOf() takes advantage of immutability, to improve efficiency
Step 10: Auto-Boxing, And Some Wrapper Constants
Auto-Boxing is an example of syntactic sugar in the Java language. It does not provide new features but makes
code more readable.
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
In this step, we:
Understood the mechanism of auto-boxing, which uses the assignment operator route
Understood how auto-boxing internally makes use of valueOf() method
Step 11: The Java Date API
No discussion on the built-in Java primitive and reference types is complete without an exploration of the Date API.
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 .
Snippet-01 : java.time utilities
java.time.* is not imported automatically by Jshell. Let's import it in.
The commonly used utilities to access current values of date and time are:
LocalDate.now() : Returns the current date value in readable format
LocalDateTime.now() : Returns a combination of the current date and time values, in a readable format
Summary
In this step, we:
Were introduced to the Java Date API, available through the java.time package
Saw how to use the LocalDate , LocalTime and LocalDateTime types
Step 12: Playing With java.time.LocalDate
We've been looking at calendars right from childhood, haven't we! How about playing around with a digital
calendar?
LocalDate provides a number of methods to retrieve
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
In this step, we:
Saw common utilities that the LocalDate class provides
Learned that LocalDate , LocalTime and LocalDateTime are all immutable
Step 13: Comparing LocalDate Objects
You can take a look at additional utility methods in LocalDate in examples below:
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.
Arrays and ArrayList
We will use the following exercise to understand heavily used data structures of Java - Arrays and ArrayList .
We would like to model a student report card in a Java program, which allows the user to do stuff such as:
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?
Let's create an array - marks
In above example
marks is an array .
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
Above for loop can be used irrespective of the number of elements in marks array.
Step 02: Storing And Accessing Array Values
Let's dig deeper into arrays in this step.
An array can be used to store zero or more number of elements.
An array can also be created with new operator. You've to specify the size of the array.
jshell> int[] marks2 = new int[5];
marks2 ==> int[5]{ 0,0,0,0,0 }
jshell> marks2[0]
$1 ==> 0
jshell> marks2[0] = 10;
$2 ==> 10
jshell> marks2[0]
$3 ==> 10
jshell>
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>
Array.equals compares two given arrays, and returns a boolean value of true only if
Both arrays are of same length and
Elements at each corresponding index are equal, for all indexes
jshell> int[] array1 = {1, 2, 3};
array1 ==> int[3]{ 1,2,3 }
jshell> int[] array2 = {1, 2, 3};
array2 ==> int[3]{ 1,2,3 }
package com.in28minutes.arrays;
Student.java
package com.in28minutes.arrays;
import java.math.BigDecimal;
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 ?
jshell> Something thing = new Something();
thing ==> Something@2e465f6a
jshell> thing.doSomething(1);
[1]
jshell> thing.doSomething(1, 2);
[1, 2]
jshell> thing.doSomething(1, 2, 3);
[1, 2, 3]
jshell>
You can see that doSomething can be called with one, two and three parameters.
Let's look at another example:
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.
Step 09: Variable Argument Methods For Student
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:
package com.in28minutes.arrays;
The size of the textValues array is fixed to 3. You an change values inside the array. But the size cannot be
changed.
How to add an element to an array?
One of the options is
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
If elements need to be removed from an array:
Create a fresh array with correspondingly lesser element slots
Copy the existing array elements, excluding the ones to be removed, to the beginning of this new array
jshell> int[] marks = {12, 34, 45};
marks ==> int[3] { 12, 34, 45 }
jshell> newMarks
newMarks ==> int[4] { 12, 34, 45, 0 }
jshell> newMarks[3] = 100
$27 ==> 100
jshell> newMarks
newMarks ==> int[4] { 12, 34, 45, 100 }
As you can see, this can be very inefficient. How do we solve it?
Step 12: Introducing ArrayList
ArrayList is more dynamic than an array. It provides operations to add and remove elements.
Let's start with creating an ArrayList and add a few values.
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!
The warning message displayed is a hint to the programmer to discourage this.
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?
We can specify the type of elements that an ArrayList can contain.
In above snippet, we are creating an ArrayList that can hold String values.
You can add String value but not numbers.
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"]
package com.in28minutes.arrays;
Student.java
package com.in28minutes.arrays;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
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]
Object Oriented Programming (OOP) - Revisited
In this section, we revisit the principles of OOP, armed with the knowledge of
Arrays and their variants
Built-in Java classes and utilities, and
Conditionals and loops (normal and enhanced).
Step 01: Objects Revisited - State And Behavior
The attributes of an object determine what it is made up of. At different points in an object's lifetime, the value of
any of its attributes can change.
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;
}
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
Make : Fan-tastic, Radius : 0.45600, Color : GREEN, Is On : false, Speed : 0
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 ).
Step 03: Augmenting Fan With Behavior
We need to decide what kind of behavior should be provided by a Fan object.
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.
Snippet-01 : The Fan class - v4
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
Make : Fan-Tastic, Radius : 0.45600, Color : GREEN, Is On : false, Speed : 0
Make : Fan-Tastic, Radius : 0.45600, Color : GREEN, Is On : true, Speed : 1
Make : Fan-Tastic, Radius : 0.45600, Color : GREEN, Is On : true, Speed : 5
Make : Fan-Tastic, Radius : 0.45600, Color : GREEN, Is On : false, Speed : 0
Snippet-01 Explained
Regarding the state attribute isOn :
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).
Regarding the state attribute speed :
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
The best way to design a class is using an Outside In thought process:
Who all could possibly be using my class ?
What functionality would they absolutely require?
Step 04: Programming Exercise PE-OOP-01
. Write a simple Rectangle class , while covering the following constituents:
State
length
width
Constructors
Behavior or Methods
Solution To PE-OOP-01
RectangleRunner.java
package com.in28minutes.oops.level2;
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
Rectangle - length : 12, width : 23, area : 276, perimeter : 70
Rectangle - length : 12, width : 25, area : 300, perimeter : 74
Rectangle - length : 20, width : 25, area : 500, perimeter : 90
Solution Explained
A Rectangle object created without a specified length and width makes no practical sense, therefore a
default constructor is not provided.
Step 06: Understanding Object Composition
Let's take a re-look at the state attributes of the Fan class :
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?
Snippet-01 : Object composition - State
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
Customer customer is composed of:
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
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
}
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]
Step 07: Programming Exercise PE-OOP-02
Exercises
Write a program that manages Books and their Reviews:
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;
Review.java
package com.in28minutes.oops.level2;
Book.java
package com.in28minutes.oops.level2;
package com.in28minutes.oops.level2.inheritance;
StudentWithoutInheritance.java
package com.in28minutes.oops.level2.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.
In this Inheritance relationship, Person is called the super-class of Student . Likewise, Student is the sub-class
of Person .
Let's now look at how we go about changing the Student class definition.
Snippet-02 : Student inherits from Person - v1
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");
}
}
package com.in28minutes.oops.level2.inheritance;
Student.java
package com.in28minutes.oops.level2.inheritance;
public class Student extends Person {
private String collegeName;
private int year;
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.
package com.in28minutes.oops.level2.inheritance;
PersonRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
Person Ranga , Email : in28minutes@gmail.com, Phone Number : 9898989898
Person Ranga , Email : in28minutes@gmail.com, Phone Number : 9898989898
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 .
Step 11: Classroom Exercise CE-OOP-01
Create an Employee class extending Student Class with following attributes:
Title
Employer
EmployeeGrade
Salary
Create a method toString() within Employee to print all state attribute values, including those of Person.
Snippet-01 : Employee Inheritance
Person.java
package com.in28minutes.oops.level2.inheritance;
Employee.java
package com.in28minutes.oops.level2.inheritance;
import java.math.BigDecimal;
EmployeeRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
Employee Title: Programmer Analyst, Employer: In28Minutes, Employee Grade: A, Salary: 50000.0000
Snippet-01 Explained
We have not printed the underlying Person object details in Employee.toString() method overriding. Let's look
to do that next.
Step 12: Constructors, And Calling super()
The super keyword allows an sub-class to access the attributes present in the super-class.
Snippet-01 : Calling Person.toString()
Employee.java
package com.in28minutes.oops.level2.inheritance;
import java.math.BigDecimal;
EmployeeRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
Employee Name: Ranga, Email: in28minutes@gmail.com, Phone Number: 123-456-7890, Title: Programmer
Analyst, Employer: In28Minutes, Employee Grade: A, Salary: 50000.0000
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?
Snippet-7 : Person class
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
Inside Person Constructor
Inside Employee Constructor
Snippet-02 Explained
When a sub-class object is created
sub-class constructor is called and it implicitly invokes its super-class constructor.
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() .
The statement super(); is the invocation of the super-class default constructor.
Hence, the body of the super-class constructor is always invoked before the body of the sub-class constructor.
Snippet-3 : Person - Non-Default Constructor
Let's remove the no argument constructor and add a one argument constructor to Person class.
public Person(String name) {
this.name = name;
}
PersonRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
Person Ranga , Email : in28minutes@gmail.com, Phone Number : 123-456-7890
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);
public Employee(String name, String title, String employerName, char employeeGrade) {
super(name);
this.title = title;
this.employerName = employerName;
this.employeeGrade = employeeGrade;
}
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
Snippet-10 : EmployeeRunner complete
Let's provide setters to set the non mandatory attributes.
EmployeeRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
Employee Name: Ranga, Email: in28minutes@gmail.com, Phone Number: 123-456-7890, Title: Programmer
Analyst, Employer: In28Minutes, Employee Grade: A, Salary: 50000.0000
Snippet-10: Student updated
Let's add a two argument construtor to the Student class.
Student.java
package com.in28minutes.oops.level2.inheritance;
StudentRunner.java
package com.in28minutes.oops.level2.inheritance;
Console Output
Student : Ranga, College : IIT Bombay
Step 13: Multiple Inheritance, Reference Variables And instanceof
In other programming languages, Multiple Inheritance is allowed. A class can directly inherit from two or more
classes.
However, in Java, direct Multiple Inheritance is not allowed.
Snippet-01 : Multiple Inheritance
class Dog extends Animal, Pet {} throws an error. You cannot extend two classes.
Inheritance Chains
However you can create an inheritance chain.
class C is a class B
class B is a class A
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()
jshell> Dog dog = new Dog();
dog ==> Dog@23a6e47f
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"
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.
jshell> Pet pet = new Dog();
pet ==> Dog@22d37d54
jshell> pet.groom();
Pet Groom
jshell> Dog dog = new Pet();
| Error:
| incompatible types: Pet cannot be converted to Dog
| Dog dog = new Pet();
|___________^-------^
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.
jshell> pet instanceof Pet
$2 ==> true
jshell> pet instanceof Dog
$3 ==> true
The instanceof operator throws an error if the object and class are unrelated.
jshell> pet instanceof String
| Error:
| incompatible types: Pet cannot be converted to java.lang.String
| pet instanceof String
|_^-^
jshell> pet instanceof Animal
$4 ==> true
jshell> pet instanceof Object
$5 ==> true
The instanceof operator returns false if the object is an instance of a super class of the class provided.
jshell> Animal animal new Animal();
animal ==> Animal@3632be31
jshell> animal instanceof Pet
$6 ==> false
jshell> animal instanceof Dog
$7 ==> false
jshell> animal instanceof Object
$8 ==> true
jshell>
However, it can be sub-classed, creating inheritance hierarchies below it. A sub-class of an abstract class (often
called a concrete class) must override its abstract methods.
jshell> class Dog extends AbstractAnimal {
..>> }
| Error:
| Dog is not abstract and does not override abstract method bark() in AbstractAnimal
| class Dog extends AbstractAnimal {
|^----------------------------------...
jshell> class Dog extends AbstractAnimal {
..>> public void bark() {
..>> System.out.println("Bow Bow");
..>> }
..>> }
| created class Dog
jshell> Dog dog = new Dog();
dog ==> Dog@5a8e6209
jshell> dog.bark();
Bow Bow
package com.in28minutes.oops.level2;
public abstract class AbstractRecipe {
public void execute() {
prepareIngredients();
cookRecipe();
cleanup();
}
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.
You can define implementations implementing the abstract methods.
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
[Curry Preparation Method]
Get Vegetables Cut and Ready
Get Spices Ready
Steam And Fry Vegetables
Cook With Spices
Add Seasoning
Discard unused Vegetables
Discard unused Spices
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
We can easily create more recipes.
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;
An abstract class can be a sub class to create another abstract class , without overriding any of the super-
class abstract methods.
jshell> abstract class AbstractAlgorithm {
...> abstract void flowChart();
...> }
| creates abstract class AbstractAlgorithm
jshell> abstract class AlgorithmTypeOne extends AbstractAlgorithm {
...> }
| creates abstract class AlgorithmTypeOne
package com.in28minutes.oops.level2.interfaces;
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.
Let's look at how you can run these games.
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.
Snippet-02 : Code Reuse With Interfaces
Let's look at another example - ChessGame .
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
Move Piece Down
Move Piece Left
Move Piece Right
Snippet-2 explained
In the same GamerRunner class , if we now instantiate a ChessGame object instead of a MarioGame one, none of
the other code needs to change. This is because both MarioGame and ChessGame implement the same
interface , GamingConsole .
package com.in28minutes.oops.level2.interfaces;
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?
Step 18: Using Interfaces To Design APIs
Consider a Software Development project, which involves programming a fairly large and complex application.
Project team (Team A) decided to out-source part of this project to an external team (Team B). Let's say this
external team needs to implement a farily complex algorithm to achieve a specific task, and which needs to
interface with the rest of the application. Work on both parts of the application needs to proceed simultaneously.
Suppose the algorithm logic is implemented using a single method:
int complexAlgorithm(int number1, int number2);
How do we ensure that the work done by both the teams remains compatible?
They start with defining an interface.
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;
package com.in28minutes.oops.level2.interfaces;
An implementation of interface should implement all its methods including the methods in its super interfaces.
jshell> class Implementation implements InterfaceTwo {
...>}
| Error:
| Implementation is not abstract and does not implement abstract method methodTwo() of Interf
| class Implementation implements InterfaceTwo {
|^---------------------------------------------...
If a class is declared as abstract, it can skip providing implementations for all interface methods.
jshell> public abstract class AbstractImplementation implements InterfaceTwo {
...> public void methodOne() {}
...> }
| created class AbstractImplementation
interfaces cannot have member variables. An interface can only have declared constants
jshell> interface InterfaceThree {
...> int test;
...> }
| Error:
| = expected
| int test;
|_________^
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.
jshell> interface InterfaceFour {
...> public default void print() {
...> System.out.println("default print");
...> }
...> }
| created interface InterfaceFour
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.
Step 20: abstract class And interface : A Comparison
abstract class and interface are very different, except that they have a very similar syntax.
When would you want to use them in your application?
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.
Recall the following example we had discussed:
An interface cannot have declared member variables. An abstract class can have member variable
declarations.
A class or an abstract class can implement multiple interface s. But, an interface can extend only one
interface , and a class or an abstract class can extend only one class or abstract class .
List Immutability
Consider the List words we created in the last snippet.
List<String> words = List.of("Apple", "Bat", "Cat");
jshell> wordsArrayList.add("Dog");
$1 ==> true
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Cat, Dog]
jshell>
ArrayList vs LinkedList
ArrayList uses an array to store elements.
Positional access and modification of elements is very efficient, with constant-time algorithmic complexity.
Insertion and deletion of elements are expensive. In a 20 element list, to insert an element at first position, all
20 elements should be moved.
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.
Vector is thread-safe. In Vector , all methods are synchronized .
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:
jshell> Iterator wordsIterator = words.iterator();
wordsIterator ==> java.util.AbstractList$Itr@3712b94
jshell> while(wordsIterator.hasNext()) {
...> System.out.println(wordsIterator.next());
...> }
Apple
Bat
Cat
Sorting a List
Snippet-8 : List sort
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
Snippet-9 : Sorting List
Let's create a Student class and try to sort it using Collections.sort
Student.java
package collections;
StudentsCollectionRunner.java
package collections;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
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 .
Student does not implement the interface. Result - Compilatino error.
package collections;
import java.util.Comparable;
StudentsCollectionRunner.java
package collections;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
Console Output
[1 Ranga, 100 Adam, 2 Eve]
[1 Ranga, 2 Eve, 100 Adam]
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.
If you change the order of parameters:
@Override
public int compareTo(Student that) {
return Integer.compare(that.id, this.id);
}
@SuppressWarnings({"unchecked", "rawtypes" })
public static <T> sort(List<T> list, Comparator<? super T> c)
list.sort(c);
}
To be able to invoke this version of Collections.sort , we will need to define a suitable Comparator
implementation, that works with Student objects.
As you may have already guessed, we would need to define two different Comparator implementations: one for
ascending sort, and another for descending sort.
Snippet-11 : Implementing Student Comparators
package collections;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
You can call sort method on the List by passing it a Comparator implementation - studentsAl.sort(new
AscStudentComparator()) .
package collections;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
Console Output
[1 Ranga, 100 Adam, 2 Eve]
Asc : [1 Ranga, 2 Eve, 100 Adam]
Desc : [100 Adam, 2 Eve, 1 Ranga]
The Set interface
Mathematically, a set is a collection of unique items. Similarly, the Java Set interface also specifies a contract
for collections having unique elements.
If object1.equals(object2) returns true , then only one of object1 and object2 have a place in a Set
implementation.
There is no positional element access provided by the Set implementations.
Snippet-13 : Set
Let's look at a few examples:
The collection returned by Set.of() is immutable, hence does not support the add() method.
jshell> Set<String> set = Set.of("Apple", "Banana", Cat");
set ==> [Banana, Apple, Cat]
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) .
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
In a LinkedHashSet , elements are stored in the order of insertion.
Snippet-16 : TreeSet
Exercise Set
package collections;
import java.util.List;
import java .util.Set;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;
Console Output
Unique Characters: [A, B, F, Z]
Sorted Order: [A, B, F, Z]
Inserted Order: [A, Z, B, F]
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
Snippet-16 : NavigableSet Operations
Let's look at few operations:
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.
jshell> numbers.subSet(20, 80);
$7 ==> [34, 54, 65]
jshell> numbers.subSet(34, 54);
$8 ==> [34]
jshell> numbers.subSet(34, 65);
$9 ==> [34, 54]
jshell> numbers.subSet(34, true, 65, true);
$10 ==> [34, 54, 65]
jshell> numbers.subSet(34, false, 65, false);
$11 ==> [54]
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>
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
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 !
The interface definition looks something like:
//Method Declarations
The Java collection classes that implement the Map interface are:
HashMap
HashTable
LinkedHashMap
TreeMap
TreeMap elements are stored in the natural sorted order of the keys.
jshell> TreeMap<String, Integer> treeMap = new TreeMap<>();
treeMap ==> {}
jshell> treeMap.put("Z", 5);
$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
. 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 .
Snippet-13 : Treemap Operations
Entries in a TreeMap are always sorted.
Set
Queue
Map
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
Why do we need Generics?
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?
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
At the time of actual instantiation of MyCustomList inside GenericsRunner.main , this placeholder is substituted by
the actual type:
When MyCustomList<String> list is created, T is substituted by String
When MyCustomList<Integer> list2 is created, T is substituted by Integer
Exercise Set - 18
. 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
9
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> .
For MyCustomList<String> list , list.get returns a String
For MyCustomList<Integer> list2 , list2.get returns an Integer
Implementing Type Restrictions on Generics
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.
What if we wanted to to use MyCustomList<T> purely for storing numeric values?
Snippet-3 : Generic Type Restrictions
MyCustomList.java
package com.in28minutes.generics;
GenericsRunner.java
package com.in28minutes.generics;
import com.in28minutes.generics.MyCustomList;
Console Output
5
9
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
We can create generic methods as well. Let's look at a few examples:
Snippet-4 : Generic Method
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]
Generics And Wild-Cards
You can use wild card with generics too - ? extends Number
Snippet-5
GenericsRunner.java
package com.in28minutes.generics;
import com.in28minutes.generics.MyCustomList;
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 .
package com.in28minutes.generics;
import com.in28minutes.generics.MyCustomList;
Console Output
[1, 1, 1.0. 1.0]
Introduction to Functional Programming
What's all the fuss around Functional Programming about?
Let's find out.
Functional Programming Videos
Part 1 - https://www.youtube.com/watch?v=aFCNPHfvqEU
Part 2 - https://www.youtube.com/watch?v=5Xw1_IREXQs
Step 01: Introducing Functional Programming
Let's look at a typical program to loop around a list and print its content.
Snippet-01 : OOP List Traversal
FunctionalProgrammingRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
Console Output
Apple
Banana
Cat
Dog
Snippet-01 Explained
Above approach focuses on the how.
We looped around the list, accessed individual elements of a List and did System.out.println() to print each
element.
Functional Programming allows us to focus on the what.
Snippet-02 : printBasic() And printFunctional()
FunctionalProgrammingRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
Console Output
Apple
_Banana_Cat_
Dog
Snippet-02 Explained
list.stream().forEach(element -> System.out.println(element)) - for each element in list stream, print it.
element -> System.out.println(element) is called a lambda expression.
Snippet-01 Explained
elem -> System.out.println(elem) is a lambda expression. For each element in list stream, execute the lambda
expression.
Step 03: Filtering Results
A Stream is a sequence of values. The filter() method can be used to filter the Stream elements based on
some logic.
Snippet-01 : Using filter()
printBasicWithFiltering shows the usual approach of filtering. printFPWithFiltering shows the functional
approach.
package com.in28minutes.functionalprogramming;
import java.util.List;
}
}
Console Output
Bat
Cat
Snippet-02 : Printing even/odd numbers
Let's look at how to filter numbers.
Snippet-02 Explained
Typically, these are the conditions we write
num is odd : if(num % 2 == 1) { /* */ }
In the above example, we are using lambda expression to define the same conditions.
num is odd: num -> num%2 == 1
num is even: num -> num%2 == 0
Step 05: Streams - Aggregated Results
Sometimes we want to aggregate data into a single result. For example, we might want to add all the numbers
between 1 and 10 . Or we may want to calculate the average maximum temperature in our city over a month's
time.
Snippet-01 : Sum Of A Sequence
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.
package com.in28minutes.functionalprogramming;
import java.util.List;
Console Output
Even Numbers Sum: 18
Odd Numbers Sum: 31
Step 06: Functional Programming v Structured Programming
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:
basicSum() : that used the traditional approach
Why take our word for all this? Let's put this code into an IDE, and then run it, to see for ourselves.
Snippet-01 : Lambda Expression
FPNumberRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
distinct()
filter()
map()
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.
Step 09: Programming Exercise FP-PE-01
Exercises
. Write a program to print the squares of the first 10 positive integers.
. Create a list of the character strings "Apple", "Banana" and "Cat". Print all of them in lower-case.
. Create a list of the character strings "Apple", "Banana" and "Cat". Print the length of each string.
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
1
4
9
16
25
36
49
64
81
100
apple
banana
cat
5
6
3
Solution Explained
The map() method accepts a lambda expression.
Step 10: Terminal Operations
A terminal operation returns a single result (A single object/data-unit, or a single collection). It does not return an
output stream.
Commonly used instances of this are:
reduce()
Solution Explained
Solution to #1 is straightforward, given that we already know how to use filter() .
Solution #2 uses boxed() method to convert an IntPipeline to a Stream . After that, what follows is routine
stuff.
Step 12: The Optional<T> class
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) -> Integer
$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) -> Integer.com
$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) -> Integer.com
$7 ==> 0
jshell> List.of(23, 45, 67, 34).stream().filter(num -> num % 2 == 0).max( (n1, n2) -> Integer
$8 ==> 34
jshell>
package com.in28minutes.functionalprogramming;
import java.util.List;
Console Output
34
36
48
Snippet-01 Explained
The signature of filter() reads is Stream<T> java.util.stream.Stream.filter(Predicate<? super T>
predicate) . In this case, T is java.lang.Integer .
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.
Conventionally speaking, a predicate is a logical condition. This predicate is applied to each element, to determine if
it should be included in the output stream.
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) { /* */ }
//...
}
}
package com.in28minutes.functionalprogramming;
import java.util.List;
import java.util.function.Predicate;
Console Output
34
36
48
Snippet-02 Explained
EvenNumberPredicate implements the Predicate<Integer> interface, and overrides the test() method.
Something similar to EvenNumberPredicate is created when we use lambda expressions num -> num%2 == 0 .
Step 14: Functional Interfaces : Consumer
Let's look at another Functional Interface - Consumer<S> .
forEach() on a stream is actually defined as - forEach(Consumer<? super S> action)
@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
Let's implement a SysOutConsumer .
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!
Step 15: More Functional Interfaces
Let's now look at what happens behind the scenes, for the stream operation map() . Suppose we wanted to print
out the squares of all even numbers in a given list.
Snippet-01 : map() Behind The Scenes - v1
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
The signature of the map() intermediate stream operation is :
@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;
numbers.stream()
.filter(new EvenNumberPredicate())
.map(new NumberSquareMapper())
.forEach(new SysOutPredicate());
}
}
Console Output
1156
1296
2304
1156
1296
2304
Step 16: Introducing Method References
What is a method reference?
Let's look at an example.
Snippet-01: Method References - v1
MethodReferencesRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
Console Output
3
3
3
3
8
Snippet-02 : Method References - v2
Method references make it easy to create lambda expressions.
l -> System.out.println(l) can be replaced with System.out::println .
MethodReferencesRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
MethodReferencesRunner.java
package com.in28minutes.functionalprogramming;
import java.util.List;
package com.in28minutes.functionalprogramming;
import java.util.List;
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
In this step, we:
Understood what is a method reference
Learned that both built-in, and user defined class methods can be invoked using method references
Observed that method references work for static and non-static methods
Step 17: FP - Functions As First-Class Citizens
Are functions first class citizens in Java?
Here are few questions to think about?
Can you pass a function as an argument to a method?
Can you assign a function to a variable?
Can you obtain a function as a return value, from a method invocation?
Passing function as method argument
We looked at several examples of this earlier.
In the example below, num -> num % 2 == 0 is passed to filter method.
int max = List.of(23, 45, 67, 34).stream()
.filter(num -> num % 2 == 0)
.max( (n1, n2) -> Integer.compare(n1, n2) )
.orElse(0);
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
Returning functions from methods
createEvenPredicate and createOddPredicate are examples of methods returning functions.
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
In this step, we observed that the following is true for a function:
It can be passed as a method argument
It can be stored in a reference variable
It can be returned from a method
Threads and Concurrency
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.
Step 01: Concurrent Tasks: Extending Thread
In Java, you can run tasks in parallel using threads. Let's first write a simple program.
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:
Define your own thread class to sub-class the Thread class .
Define your own thread class to implement the Runnable interface .
In this step, we will focus on the first alternative.
Snippet-01 : A simple Java thread class
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 212 112 213 1
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 325 326 327 3
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 this step, we:
Discovered how to define a thread by sub-classing Thread
Demonstrated how to run a Thread
Step 02: Concurrent Tasks - Implementing Runnable
Snippet-01 : Implementing Runnable
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
Main Done
Snippet-01 Explained
In this example, we implemented Runnable by implementing the run() method from Runnable interface.
To run Task2 which is implementing Runnable interface, we used this code. We are using the Thread constructor
passing an instance of Task2 .
Task2 task2 = new Task2();
Thread task2Thread = new Thread(task2);
task2Thread.start();
You can see from the output that all three tasks are running in parallel.
Summary
In this step, we:
Explored another way to create threads, by implementing the Runnable interface
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:
Snippet-6 : Task3 after Task1 and Task2
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
In this step, we:
Understood the need for thread communication
Learned that Java provides mechanisms for threads to wait for each other
Observed how the join() method can be used to sequence thread execution
Step 07: synchronized Methods, And Thread Utilities
When a thread gets tired, you can put it to bed. Heck, you can do it even when it's fresh and raring to go! It's under
your control, remember?
The Thread class provides a couple of methods:
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.
Snippet-01 : Thread utilities
jshell> Thread.sleep(1000)
jshell> Thread.sleep(10000)
jshell>
Snippet-7 Explained
Thread.sleep(1000) causes the JShell prompt to appear after a delay of at least 1 second. This delay is
even more visible, when we execute Thread.sleep(10000) .
Step 08: Drawbacks of earlier approaches
We saw few of the methods for synchronization in the Thread class
start()
join()
sleep()
wait()
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
ExecutorService executorService = Executors.newSingleThreadExecutor() creates a single threaded executor
service. That's why the tasks ran serially, one after the other.
Snippet-02 : main runs in parallel
Let's update the main method to do more things:
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 117 118 119 1
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 220 342 221 2
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.
Threads managed by ExecutorService run in parallel with main method.
Step 10: Executor - Customizing Number Of Threads
With the ExecutorService , it is possible to create a pool of threads.
The following examples will show you how you can create thread pools of varying kinds, and of course, of different
sizes.
Snippet-03 : Executors for Concurrent threads
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 193 292 194 2
Task2 Done
197 198 199
Task1 Done
Snippet-03 Explained
We created ExecutorService by using Executors.newFixedThreadPool(2) . So, 'ExecutorService` uses two
parallel threads at a maximum.
Task1 and Task2 execute concurrently as part of the ExecutorService , and
The thread running main() executes concurrently with this thread pool, created by ExecutorService .
Snippet-04 : All-Executor Task Execution
Let's create a simple example to allow to play with 'ExecutorService'.
ExecutorServiceRunner.java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Snippet-04 Explained
Executors.newFixedThreadPool(2) - Two threads in parallel. The thread new Task(3) is executed only after any
one of new Task(1) and new Task(2) have completed their execution.
Snippet-04 : Larger Thread Pool Size
public class ExecutorServiceRunner {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.execute(new Task(1));
executorService.execute(new Task(2));
executorService.execute(new Task(3));
executorService.execute(new Task(4));
executorService.execute(new Task(5));
executorService.execute(new Task(6));
executorService.execute(new Task(7));
executorService.shutdown();
}
}
Snippet-04 Explained
We made the pool larger to 3:
Initially Tasks 1 , 2 , 3 are added to the ExecutorService and are started.
As soon as any one of them is terminated, another task from the thread pool is picked up for execution, and so
on. A classic case of musical chairs, with regard to the slots available in the ExecutorService thread pool, is
played out here!
Summary
In this step, we:
Explored how one can create pools of threads of the same kind, using the ExecutorService
Noted how one could specify the size of a thread pool
Step 11: ExecutorService : Returning Values From Tasks
Snippet-01: Returning a Future Object
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.
To be able to return a value from a Thread, Java provides a Callable<T> interface.
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;
}
}
public class CallableRunner {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<String> welcomeFuture = executorService.submit(new CallableTask("in28Minutes"));
System.out.println("CallableTask in28Minutes Submitted");
String welcomeMessage = welcomeFuture.get();
System.out.println(welcomeMessage);
executorService.shutdown();
}
}
Console Output
CallableTask in28Minutes Submitted
Hello in28Minutes
Snippet-01 Explained
class CallableTask implements Callable<String> - Implement Callable . Return type is String
public String call() throws Exception { - Implement call method and return a String value back.
executorService.submit(new CallableTask(String)) adds a callable task to its thread pool. This puts the
task thread in a RUNNABLE state. Subsequently, the program goes ahead to invoke its call() method.
welcomeFuture.get() - Ensures that the main thread waits for the result of the operation.
Summary
In this step, we:
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
Saw that the ExecutorService is capable of managing Callable threads
Step 11: Executors - Waiting For Many Callable Tasks To Complete
ExecutorService framework also allows you to create a pool of Callable threads. Not only that, you can collect
their return values together.
Snippet-01 : Waiting for Multiple Callable Threads
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;
}
}
public class MultipleCallableRunner {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
List<CallableTask> tasks = List.of(new CallableTask("in28Minutes"),
new CallableTask("Ranga"),
new CallableTask("Adam"));
List<Future<String>> welcomeAll = executorService.invokeAll(tasks);
for(Future<String> welcomeFuture : welcomeAll) {
System.out.println(welcomeFuture.get());
}
executorService.shutdown();
}
}
Console Output
Hello in28Minutes
Hello Ranga
Hello Adam
Snippet-01 Explained
The invokeAll() method of ExecutorService allows for a list of Callable tasks to be launched in the thread
pool. Also, a List of Future objects can be used to hold return values, one for each such Callable thread.
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.
Snippet-02 : List of Callable tasks with larger thread pool
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
In this step, we:
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
Step 12: Executor - Wait Only For The Fastest Task
Let's look at how you can wait for any of the three tasks to complete.
Snippet-01 : Wait only for fastest
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:
All three tasks are created together, in a thread pool of size 3 .
Therefore, these are independent threads, going into their RUNNABLE states almost at once.
Summary
In this step, we:
Learned that ExecutorService has a way to return the first result, from a poll of Callable threads
Introduction To Exception handling
Recommended Exception handling Videos
https://www.youtube.com/watch?v=34ttwuxHtAE
There are two kinds of errors a programmer faces:
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
Example runtime errors include:
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.
Handling an exception generally involves two important aims:
. Provide a useful message to the end user.
. Log enough information to help a programmer identify root cause.
Step 01: Introducing Exceptions
In the previous step, we gave you a few instances of exceptions, such as your program running out of memory, or
your code trying to divide a number by 0 .
Want to see a live example of the Java run-time throwing an exception? The next example will satisfy your thirst.
Snippet-1 : Exception Condition
ExceptionHandlingRunner.java
package com.in28minutes.exceptionhandling;
Console Output
java.lang.NullPointerException
Exception in thread "main" java.lang.NullPointerException
at com.in28minutes.exceptionhandling.ExceptionHandlingRunner.callMethod (ExceptionHandlingRunner.java:8)
at com.in28minutes.exceptionhandling.ExceptionHandlingRunner.main (ExceptionHandlingRunner.java:4)
Snippet-01 Explained
A java.lang.NullPointerException is thrown by the Java run-time when we called length() on the null
reference.
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.
For instance, consider this simple class Test :
However, the code name.length() used in callThree() caused a NullPointerException to be thrown. However,
this is not handled, and the console coughs up the following display:
Exception in thread "main" java.lang.NullPointerException
at Test.callThree(Test.java:13)
at Test.callTwo(Test.java:9)
at Test.callOne(Test.java:6)
at Test.main(Test.java:3)
This is nothing but a call trace of the stack, frozen in time.
Summary
In this step, we:
Saw a live example of an exception being thrown
Understood how a call trace on the stack appears when a exception occurs
Reinforced this understanding with another example
Step 02: Handling An Exception
In Java, exception handling is achieved through a try - catch block.
Snippet-01 : Handling An Exception
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() .
Snippet-02: Print Debug Information
Let's add ex.printStackTrace(); .
ExceptionHandlingRunner.java
package com.in28minutes.exceptionhandling;
Console Output
java.lang.NullPointerException
Exception in thread "main" 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
In this step, we:
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
Step 03: The Exception Hierarchy
The code in the try - catch block above does not work by black magic. From the call trace, it's clear that this
program encounters a NullPointerException in method1() . What's surprising, is that it was caught by a catch
clause meant for an Exception ! The reason this worked is because NullPointerException is-a Exception .
You heard right! Java has a hierarchy of exception types, rooted at class Exception . For instance,
NullPointerException is-a RuntimeException , and
RuntimeException is-a Exception .
package com.in28minutes.exceptionhandling;
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.
We placed an additional catch block within method2() , to handle NullPointerException . Typically, the most
specific exception class is matched. Hence the catch block for NullPointerException matches.
You need to order the catch blocks after a try , from more-specific to less-specific matches.
Snippet-02 : Catching another exception
package com.in28minutes.exceptionhandling;
public class ExceptionHandlingRunner {
public static void main(String[] args) {
method1();
System.out.println("main() Done");
}
Console Output
ArrayIndexOutOfBoundsException : 3
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
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
ArrayIndexOutOfBoundsException is-a IndexOutOfBoundsException , which is-a RuntimeException , which in
turn is-a Exception .
ArrayIndexOutOfBoundsException is not a sub class of NullPointerException . Hence, it does not match with the
first catch block. ArrayIndexOutOfBoundsException is a sub class of Exception . Hence, that catch block
matched, and the statement ex.printStackTrace(); within it ran.
If we omit the handler for Exception , the ArrayIndexOutOfBoundsException is not caught by this try - catch
block. Since it is not handled in method1() , or even later in main() , our program would have to stop suddenly.
Summary
In this step, we:
Learned that there is an exception hierarchy in Java, rooted at Exception
Looked at an example that could cause multiple exceptions to occur
Observed how a handler for Exception could match any exception
package com.in28minutes.exceptionhandling;
import java.util.Scanner;
Console Output
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
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.
Snippet-02 : Releasing Resources
FinallyRunner.java
package com.in28minutes.exceptionhandling;
import java.util.Scanner;
Console Output
ArrayIndexOutOfBoundsException
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
at com.in28minutes.exceptionhandling.FinallyRunner.main (FinallyRunner.java:10)
Before scanner close
Before exiting main
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
In this step, we:
Observed how exceptional conditions could result in resource leaks
Learned about the finally clause in a try - catch block
Step 05: Programming Puzzles - PP-01
Puzzle-01
Would the finally clause be executed if
The statement //str = "Hello"; remains as-is
The statement //str = "Hello"; has its comments removed?
Answer
Yes
Yes
Puzzle-02
When will code in a finally clause not get executed?
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
Will the following code, a try - finally without a catch ?
Answer : Yes
Puzzle-04
Will the following code, a try without a catch or a finally ?
Answer : No
Step 06: Handling Exceptions: Do We Have A Choice?
Sometimes, in Java you are forced to handle exceptions.
Snippet-02: Checked Exceptions - v1
CheckedExceptionRunner.java
package com.in28minutes.exceptionhandling;
Snippet-02 Explained
This program will not compile!
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.
When a method throws an Exception, the calling method should:
Handle it using try - catch block
Or declare throws in its signature
Let's use try - catch block to start off.
Snippet-03: Checked Exceptions - v2
CheckedExceptionRunner.java
package com.in28minutes.exceptionhandling;
Snippet-03 Explained
main() , which is the caller of sleep() , chooses the option of handling InterruptedException with a try -
catch block.
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.
We made main method handle the exception.
Summary
In this step, we:
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
Step 08: The Java Exception Hierarchy
Right at the root (top, we mean), the Java exception hierarchy looks like this:
Once an Error occurs, there is nothing a programmer could do. Examples include:
The JVM running out of heap memory space
An Exception can be handled. There are two types of Exceptions.
RuntimeException and its sub-classes. These come under the category of unchecked exceptions. Another
example we've seen is NullPointerException , which inherits from RuntimeException .
All other sub-classes of Exception , excluding the sub-tree rooted at RuntimeException , are called checked
exceptions. An instance we've encountered is InterruptedException .
If a method throws a checked exception is called, then either:
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.
If an unchecked exception is involved, then:
You have the options of handling with try - catch block.
It is not mandatory to handle it.
A checked exception must be handled, whereas as unchecked exception may or may not be handled.
Summary
In this step, we:
Discovered that within the Java exception hierarchy, there are two categories:
Checked exceptions
Unchecked exceptions
There are different strategies to manage these two categories
Step 09: Throwing an Exception
So far, we have seen how to handle an exception, that is thrown by a built-in Java method. Now, let's explore how
we can alert a user about exceptional conditions in our own code, by throwing exceptions.
Snippet-01 : Throwing An Exception
ThrowingExceptionRunner.java
package com.in28minutes.exceptionhandling;
class Amounts {
private String currency;
private int amount;
This thrown exception object can be handled inside main() . By calling printStackTrace() on the caught
exception reference, you get debug information like before.
Snippet-02 : Throwing a Checked Exception
Exception is a Checked Exception. If a method throws an instance of Exception class, it needs to declare it -
public void add(Amount that) throws Exception { .
ThrowingExceptionRunner.java
package com.in28minutes.exceptionhandling;
class Amounts {
private String currency;
private int amount;
Console Output
java.lang.RuntimeException:Currencies Don't Match : USD & EUR
Exception in thread "main" java.lang.RuntimeException:Currencies Don't Match
at com.in28minutes.exceptionhandling.ThrowingExceptionRunner.main (ThrowingExceptionRunner.java:26)
Exception is not a RuntimeException or one of its sub-classes, it is a checked exception. So, it needs to be
declared when it is thrown - public void add(Amount that) throws Exception .
Summary
In this step, we:
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.
Step 10: Throwing A Custom Exception
It is also possible for you to throw a custom exception. You can do this by defining your own exception class , only
that it must inherit from one of the built-in exception classes. Note that:
If you sub-class a checked exception, your exception also becomes checked.
If you sub-class an unchecked exception, your exception would be unchecked.
Snippet-01 : Throw a custom exception
ThrowingExceptionRunner.java
package com.in28minutes.exceptionhandling;
class Amounts {
private String currency;
private int amount;
Console Output
com.in28minutes.exceptionhandling.CurrenciesDoNotMatchException : Currencies Don't Match : USD & EUR
Exception in thread "main" com.in28minutes.exceptionhandling.CurrenciesDoNotMatchException : Currencies Don't
Match : USD & EUR
at com.in28minutes.exceptionhandling.ThrowingExceptionRunner.main (ThrowingExceptionRunner.java:26)
Snippet-01 Explained
The class CurrenciesDoNotMatchException clearly is a checked exception. Hence, rules that apply for throwing
and handling checked applications, apply to it as well.
If instead, CurrenciesDoNotMatchException were to sub-class RuntimeException , it would be an unchecked
exception. Adding the throws specification to the add() definition would not be needed. Also, no method in the
call sequence of add() is required to handle it.
Summary
In this step, we:
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.
Saw an example of how to raise and handle a custom exception
Step 11: Introducing try -With-Resources
try -with-resources makes managing resource in a try - catch - finally block. Let's look at an example.
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.
Also, the call to scanner.close is no longer required.
Summary
In this step, we:
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 .
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
Does the following code compile?
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
Does the following code compile?
try {
Answer : Yes
Puzzle-03 Explained
This feature was added in Java SE 7.
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.
Listing Directory Contents
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.
Snippet-1 : Listing Directory Contents
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;
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
Snippet-3 : Level-4 Traversal
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
Snippet-4 : Only list .java files during traversal
We use a predicate Files.walk(currentDirectory, 4).filter(predicate) to filter only Java files.
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
Snippet-5 : Filtered Traversal with find()
We can use a matcher - Files.find(currentDirectory, 4, matcher) which is configured to check the path
attribute for .java extension - (path, attributes) -> String.valueOf(path).contains(".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
Snippet-6 : Filtering directories
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
We are making use of a matcher checking attributes.isDirectory() .
Snippet-7 : Reading a File
./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]
Files.readAllLines(pathFileToRead) makes it easy to read content of a file to list of String values.
Snippet-8 : Streamed File Read
Files.readAllLines(pathFileToRead) makes it easy to read content of a file. However, streaming is a better
options when reading large files or when less memory is available.
./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
Files.lines(pathFileToRead) returns a stream which can be consumed as needed.
Snippet-9 : Printing file contents in lower-case
We can use map function to map to String::toLowerCase
./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
Snippet-9 : Filtering file contents
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;
Console Output
asdfghjkl
apple
bat
cat
Snippet-10 : Writing to a file
Files.write can be used to write to a file.
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
package com.in28minutes.concurrency;
ConcurrencyRunner.java
package com.in28minutes.concurrency;
Snippet-1 Explained
Quite straightforward!
Counter increment() method is NOT Atomic!
Let's look at the code. It seemingly involves just one operation.
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.
This scenario, where the result of a concurrent computation (involving a sequence of operations) depends on the
relative order of execution of those operations by the threads involved, is called a race condition.
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.
This definitely rings true here.
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.
A method is said to be thread-safe, if it can be run in a concurrent environment (involving several concurrent
invocations by independent threads) without race-conditions.
Revisited : The synchronized Keyword
Adding the keyword synchronized to the signature of a class method makes it thread safe.
Snippet-2
Counter.java
package com.in28minutes.concurrency;
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.
Snippet-3 : less concurrency
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;
public class ConcurrencyRunner {
public static void main(String[] args) {
BiCounter counter = new BiCounter();
counter.incrementI();
counter.incrementJ();
counter.incrementI();
System.out.println(counter.get());
}
}
Snippet-3 Explained
Both incrementI() and incrementJ() of class BiCounter are synchronized . Therefore, at any given time, at
most one thread can execute either of these methods! Which means that, while a thread T1 is executing
counter.incrementI() within ConcurrencyRunner.main() , another thread T2 is not allowed to execute
counter.incrementJ() !
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!
Synchronization With Locks
Let's look at another synchronization option - Locks
Snippet-4: BiCounter With Locks
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
incrementAndGet is atomic. So, BiCounterWithAtomicInteger does not need to worry about synchronization.
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:
Simple, small-sized underlying type
Wide-spread potential usage
How about collections?
Java provides classes like Vector (synchronized version of ArrayList ) which can provide thread safety. But,
these inherit the problems with using synchronized.
What are other options?
Snippet-6 : Need For ConcurrentMap
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;
Concurrent Collections provide atomic versions of operations such as those encountered above:
If entry does not exist, then create and initialize
If entry exists, then update
The ConcurrentMap interface
The interface ConcurrentMap has methods to implement compound operations. Such operations include:
V putIfAbsent(K key, V value);
V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction)
package com.in28minutes.concurrency;
import java.util.Map;
import java.util.HashTable;
import java.util.concurrent.atomic.LongAdder;
Console Output
[ =2, A=3, B=3, C=3, D=2]
Snippet-8 : ConcurrentHashMap Logic - Stage 2
We can use the method computeIfAbsent() from the collection ConcurrentHashMap to reduce the code to a
single, atomic operation.
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
[ =2, A=3, B=3, C=3, D=2]
ConcurrentHashMap
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
CopyOnWriteArrayList.add() method is a synchronized method. And the copy-on-write algorithm ensures that
the copying is performed in a thread-safe manner, after which the write is done on a separate copy, while the
get() 's continue on the original array. Once the add() is done, the collection starts using the new array, and
discards the old one. This strategy continues for the lifetime of the program.
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.