Sum and reverse of a number in Java using class and object

Home » C++ Programs

Given a number, we have to reverse it using class and object approach.
Submitted by Shubh Pachori, on August 05, 2022

Example:

Input:
1234

Output:
4321

C++ Code to find the reverse of a number using class and object approach

#include 
using namespace std;

// Create a class
class Reverse {
  // Private data member
 private:
  int number;

  // Public function with an int type parameter
 public:
  void reverse[int n] {
    // Copying value of parameter in the data member
    number = n;

    // Declaring two int type variable for storing reversed
    // number and operations
    int remain, reverse = 0;

    // While loop for reversing the number
    while [number] {
      // The last digit of number is stored in remain
      // by % operator
      remain = number % 10;

      // The number which is in remain will be added in reverse
      // with a multiply of 10 at each iteration
      reverse = [reverse * 10] + remain;

      // Number is divided by 10 to discard the last digit
      number = number / 10;
    }

    // Printing the reversed number
    cout 
?
@
A
B
C
D
E
7F
G
H
I
J
K
L
M
N
O
8P
Q
R
S
T
U
V
W
X
Y
9Z
[
\
]
^
_
`
a
b
c
10d
e
f
g
h
i
j
k
l
m
11n
o
p
q
r
s
t
u
v
w
12x
y
z
{
|
}
~
 
 
 

Hex0123456789ABCDEF234567
SP ! " # $ % & ' [ ] * + , - . /
0 1 2 3 4 5 6 7 8 9 : ; < = > ?
@ 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 { | } ~  

In Java, a char can be treated as its underlying integer in the range of [0, 65535] in arithmetic operations. In other words, char and integer are interchangeable in arithmetic operations. You can treat a char as an int, you can also assign an integer value in the range of [0, 65535] to a char variable. For example,

char letter = 'a';                 
char anotherLetter = 98;           
System.out.println[letter];        
System.out.println[anotherLetter]; 
anotherLetter += 2;                
System.out.println[anotherLetter]; 

Special characters are represented by so-called escape sequence, which begins with a back-slash [\] followed by a pattern, e.g., \t for tab, \n for newline. The commonly-used escape sequences are:

Escape
SequenceDescriptionUnicode
[Decimal]Unicode
[Hex]
\t Tab 9 0009H
\n Newline [or Line-feed] 10 000AH
\r Carriage-return 13 000DH
\" Double-quote [Needed to be used inside double-quoted String] - -
\' Single-quote [Needed to be used inside single-quoted char, i.e., '\''] - -
\ Back-slash [Needed as back-slash is given a special meaning] - -
\uhhhh Unicode number hhhh [in hex], e.g., \u60a8 is 您, \u597d is 好 - hhhhH

For examples,

char tabChar = '\t';          
char anotherTabChar = 9;      
char newlineChar = '\n';      
char backSlashChar = '\\';    
char singleQuoteChar = '\'';  
char doubleQuoteChar = '"';   
System.out.println["A tab " + tabChar + " before this; end with two newlines!" + newlineChar + newlineChar];
String Literals and Escape Sequences

A String is a sequence of characters. A String literal is composed of zero of more characters surrounded by a pair of double quotes. For examples,

String directionMsg = "Turn Right";
String greetingMsg = "Hello";
String statusMsg = "";        

You need to use an escape sequence for special non-printable characters, such as newline [\n] and tab [\t]. You also need to use escape sequence for double-quote [\"] and backslash [\\] due to conflict. For examples,

String str1 = "hello\tworld\n";    
String str2 = "a double quoted \"hello\"";
String str3 = "1 back-slash \\, another 2 back-slashes \\\\";
String str1 = "A \"string\" nested \\inside\\ a string"   
String str2 = "Hello, \u60a8\u597d!"   

Single-quote ['] inside a String does not require an escape sequence because there is no ambiguity, e.g.,

String str3 = "Hi, I'm a string!"      

It is important to take note that \t or \" is ONE single character, NOT TWO!

Exercise: Write a program to print the following animal picture using System.out.println[]. Take note that you need to use escape sequences to print some characters, e.g., \" for ", \\ for \.

          '__'
          [oo]
  +========\/
 / || %%% ||
*  ||-----||
   ""     ""
End-of-Line [EOL]

Newline [0AH] and Carriage-Return [0DH], represented by the escape sequence \n, and \r respectively, are used as line delimiter [or end-of-line, or EOL] for text files. Take note that Unix and macOS use \n [0AH] as EOL, while Windows use \r\n [0D0AH].

boolean Literals

There are only two boolean literals, i.e., true and false. For example,

boolean done = true;
boolean gameOver = false;

boolean isValid;
isValid = false;
Example on Literals
public class LiteralTest {
   public static void main[String[] args] {
      String name = "Tan Ah Teck"; 
      char gender = 'm';           
      boolean isMarried = true;    
      byte numChildren = 8;        
      short yearOfBirth = 1945;    
      int salary = 88000;          
      long netAsset = 8234567890L; 
      double weight = 88.88;       
      float gpa = 3.88f;           

      
      System.out.println["Name is: " + name];
      Name is: Tan Ah Teck
      System.out.println["Gender is: " + gender];
      
      System.out.println["Is married is: " + isMarried];
      
      System.out.println["Number of children is: " + numChildren];
      
      System.out.println["Year of birth is: " + yearOfBirth];
      
      System.out.println["Salary is: " + salary];
      
      System.out.println["Net Asset is: " + netAsset];
      
      System.out.println["Weight is: " + weight];
      
      System.out.println["GPA is: " + gpa];
      
   }
}

var - Local Variable Type Inference [JDK 10]

JDK 10 introduces a new way to declare variables via a new keyword var, for examples,

var v1 = 0;        
var v2 = 0.0;      
var v3 = 1.0f;     
var v4 = '0';      
var v5 = "hello";  
var v6;          compilation error: cannot use 'var' on variable without initializer

Clearly, you need to initialize the variable, so that the compiler can infer its type.

Basic Operations

Arithmetic Operators

Java supports the following binary/unary arithmetic operations:

OperatorModeUsageDescriptionExamples
+ Binary
Unary
x + y
+x
Addition
Unary positive
1 + 2 ⇒ 3
1.1 + 2.2 ⇒ 3.3
- Binary
Unary
x - y
-x
Subtraction
Unary negate
1 - 2 ⇒ -1
1.1 - 2.2 ⇒ -1.1
* Binary x * y Multiplication 2 * 3 ⇒ 6
3.3 * 1.0 ⇒ 3.3
/ Binary x / y Division 1 / 2 ⇒ 0
1.0 / 2.0 ⇒ 0.5
% Binary x % y Modulus [Remainder] 5 % 2 ⇒ 1
-5 % 2 ⇒ -1
5.5 % 2.2 ⇒ 1.1

These operators are typically binary infix operators, i.e., they take two operands with the operator in between the operands [e.g., 11 + 12]. However, '-' and '+' can also be interpreted as unary "negate" and "positive" prefix operator, with the operator in front of the operand. For examples,

int number = -9;
number = -number;      

Arithmetic Expressions

In programming, the following arithmetic expression:

must be written as [1+2*a]/3 + [4*[b+c]*[5-d-e]]/f - 6*[7/g+h]. You cannot omit the multiplication sign [*] as in Mathematics.

Rules on Precedence

Like Mathematics:

  1. Parentheses [] have the highest precedence and can be used to change the order of evaluation.
  2. Unary '-' [negate] and '+' [positive] have next higher precedence.
  3. The multiplication [*], division [/] and modulus [%] have the same precedence. They take precedence over addition [+] and subtraction [-]. For example, 1+2*3-4/5+6%7 is interpreted as 1+[2*3]-[4/5]+[6%7].
  4. Within the same precedence level [i.e., addition/subtraction and multiplication/division/modulus], the expression is evaluated from left to right [called left-associative]. For examples, 1+2-3+4 is evaluated as [[1+2]-3]+4, and 1*2%3/4 is [[1*2]%3]/4.

Type Conversion in Arithmetic Operations

Your program typically contains data of many types, e.g., count and sum are int, average and gpa are double, and message is a String. Hence, it is important to understand how Java handles types in your programs.

The arithmetic operators [+, -, *, /, %] are only applicable to primitive number types: byte, short, int, long, float, double, and char. They are not applicable to boolean.

Same-Type Operands of int, long, float, double

If BOTH operands are int, long, float or double, the binary arithmetic operations are carried in that type, and evaluate to a value of that type, i.e.,

  • int ⊕ int ⇒ int, where denotes a binary arithmetic operators such as +, -, *, /, %.
  • long ⊕ long ⇒ long
  • float ⊕ float ⇒ float
  • double ⊕ double ⇒ double
int Division

It is important to take note int division produces an int, i.e., int / int ⇒ int, with the result truncated. For example, 1/2 ⇒ 0 [int], but 1.0/2.0 ⇒ 0.5 [double / double ⇒ double].

Same-Type Operands of byte, short, char: Convert to int

If BOTH operands are byte, short or char, the binary operations are carried out in int, and evaluate to a value of int. A char is treated as an integer of its underlying Unicode number in the range of [0, 65535]. That is,

  • byte ⊕ byte ⇒ int ⊕ int ⇒ int, where denotes a binary arithmetic operators such as +, -, *, /, %.
  • short ⊕ short ⇒ int ⊕ int ⇒ int
  • char ⊕ char ⇒ int ⊕ int ⇒ int

Take note that NO arithmetic operations are carried out in byte, short or char.

For examples,

byte b1 = 5, b2 = 9, b3;

b3 = b1 + b2;           
b3 = [byte][b1 + b2];   

However, if compound arithmetic operators [+=, -=, *=, /=, %=] [to be discussed later] are used, the result is automatically converted to the LHS. For example,

byte b1 = 5, b2 = 9;
b2 += b1;     
Mixed-Type Arithmetic Operations

If the two operands belong to different types, the value of the smaller type is promoted automatically to the larger type [known as implicit type-casting]. The operation is then carried out in the larger type, and evaluated to a value in the larger type.

  • byte, short or char is first promoted to int before comparing with the type of the other operand. [In Java, no operations are carried out in byte, short or char.]
  • The order of promotion is: int ⇒ long ⇒ float ⇒ double.

For examples,

  1. int / double ⇒ double / double ⇒ double. Hence, 1/2 ⇒ 0, 1.0/2.0 ⇒ 0.5, 1.0/2 ⇒ 0.5, 1/2.0 ⇒ 0.5
  2. 9 / 5 * 20.1 ⇒ [9 / 5] * 20.1 ⇒ 1 * 20.1 ⇒ 1.0 * 20.1 ⇒ 20.1 [You probably don't expect this answer!]
  3. char '0' + int 2 ⇒ int 48 + int 2 ⇒ int 50 [Result is an int, need to explicitly cast back to char '2' if desired.]
  4. char ⊕ float ⇒ int ⊕ float ⇒ float ⊕ float ⇒ float
  5. byte ⊕ double ⇒ int ⊕ double ⇒ double ⊕ double ⇒ double
Summary: Type-Conversion Rules for Binary Operations

The type-promotion rules for binary operations can be summarized as follows:

  1. If one of the operand is double, the other operand is promoted to double;
  2. else if one of the operand is float, the other operand is promoted to float;
  3. else if one of the operand is long, the other operand is promoted to long;
  4. else both operands are promoted to int.
Summary: Type-Conversion Rules for Unary Operations

The type-promotion rules for unary operations [e.g., negate '-'] can be summarized as follows:

  1. If the operand is double, float, long or int, there is no promotion;
  2. else the operand is byte, short, char, the operand is promoted to int.

More on Arithmetic Operators

Modulus [Remainder] Operator

To evaluate the remainder for negative and floating-point operands, perform repeated subtraction until the absolute value of the remainder is less than the absolute value of the second operand.

For example,

  • -5 % 2 ⇒ -3 % 2 ⇒ -1
  • 5.5 % 2.2 ⇒ 3.3 % 2.2 ⇒ 1.1
Exponent?

Java does not have an exponent operator. [The ^ operator denotes exclusive-or, NOT exponent]. You need to use JDK method Math.exp[x, y] to evaluate x raises to power y; or write your own code.

Overflow/Underflow

Study the output of the following program:

public class OverflowTest {
   public static void main[String[] args] {
      
      int i1 = 2147483647;        
      System.out.println[i + 1];  
      System.out.println[i + 2];  
      System.out.println[i + 3];  
      System.out.println[i * 2];  
      System.out.println[i * i];  
      
      int i2 = -2147483648;         
      System.out.println[i2 - 1];   
      System.out.println[i2 - 2];   
      System.out.println[i2 * i2];  
   }
}

In arithmetic operations, the resultant value wraps around if it exceeds its range [i.e., overflow]. Java runtime does NOT issue an error/warning message but produces an incorrect result.

On the other hand, integer division produces a truncated integer and results in so-called underflow. For example, 1/2 gives 0, instead of 0.5. Again, Java runtime does NOT issue an error/warning message, but produces an imprecise result.

It is important to take note that checking of overflow/underflow is the programmer's responsibility. i.e., your job!!!

Why computer does not flag overflow/underflow as an error? This is due to the legacy design when the processors were very slow. Checking for overflow/underflow consumes computation power. Today, processors are fast. It is better to ask the computer to check for overflow/underflow [if you design a new language], because few humans expect such results.

To check for arithmetic overflow [known as secure coding] is tedious. Google for "INT32-C. Ensure that operations on signed integers do not result in overflow" @ www.securecoding.cert.org.

More on Integer vs. Floating-Point Numbers

Integers [byte, short, int, long] are precise [exact]. But float and double are not precise but close approximation. Study the results of the following program:

public class TestPreciseness {
   public static void main[String[] args] {
      
      System.out.println[2.2 + 4.4];       6.6000000000000005
      System.out.println[6.6 - 2.2 - 4.4]; 
      
      System.out.println[[6.6] == [2.2 + 4.4]];  
 
      
      int i1 = 123456789;
      System.out.println[i1*10]; 

      float f1 = 123456789.0f;   
      System.out.println[f1];    
      System.out.println[f1*10]; 
   }
}

Always use int if you do not need the fractional part, although double can also represent most of the integers [e.g., 1.0, 2.0, 3.0]. This is because:

  • int is more efficient [faster] than double in arithmetic operations.
  • 32-bit int takes less memory space than 64-bit double.
  • int is exact [precise] in representing ALL integers within its range. double is an approximation - NOT ALL integer values can be represented by double.

Type Casting

In Java, you will get a compilation "error: incompatible types: possible lossy conversion from double|float|long to int" if you try to assign a double, float, or long value of to an int variable. This is because the fractional part would be truncated and lost. For example,

double d = 3.5;
int i = d;        Compilation error: incompatible types: possible lossy conversion from double to int

int sum = 55.66f; Compilation error: incompatible types: possible lossy conversion from float to int

long lg = 123;
int count = lg;   Compilation error: incompatible types: possible lossy conversion from long to int
Explicit Type-Casting and Type-Casting Operator

To assign the a double value to an int variable, you need toinvoke the so-called type-casting operator - in the form of [int]doubleOperand - to operate on the double operand and return a truncated value in int. In other words, you tell the compiler you consciously perform the truncation and you are fully aware of the "possible lossy conversion". You can then assign the truncated int value to the int variable. For example,

double d = 3.5;
int i;
i = [int]d;    
               

Type casting is an operation which takes one operand. It operates on its operand, and returns an equivalent value in the specified type. The syntax is:

[type]variable    
[type]literal     

There are two kinds of type-casting in Java:

  1. Explicit type-casting via a type-casting operator, as described above, and
  2. Implicit type-casting performed by the compiler automatically, if there is no loss of precision.
Implicit Type-Casting in Assignment

Explicit type-casting is not required if you assign an int value to a double variable, because there is no loss of precision. The compiler will perform the type-casting automatically [i.e., implicit type-casting]. For example,,

int i = 3;
double d;
d = i;           
                 
d = [double]i;   
double aDouble = 55;   
double nought = 0;     

The following diagram shows the order of implicit type-casting performed by compiler. The rule is to promote the smaller type to a bigger type to prevent loss of precision, known as widening conversion. Narrowing conversion requires explicit type-cast to inform the compiler that you are aware of the possible loss of precision. Take note that char is treated as an integer in the range of [0, 65535]. boolean value cannot be type-casted [i.e., converted to non-boolean].

Example: Suppose that you want to find the average [in double] of the running integers from 1 and 100. Study the following code:

public class Average1To100 {
   public static void main[String[] args] {
      int sum = 0;
      double average;
      for [int number = 1; number 
Binary
x > y
Greater than
[x > y] ⇒ false
>=
Binary
x >= y
Greater than or equal to
[x >= 5] ⇒ true
<
Binary
x < y
Less than
[y < 8] ⇒ false
= 0] && [x = 180] && [weight >= 65] && [weight = 180] || [weight >= 90]];  
   }
}

Write an expression for all unmarried male, age between 21 and 35, with height above 180, and weight between 70 and 80.

Exercise: Given the year, month [1-12], and day [1-31], write a boolean expression which returns true for dates before October 15, 1582 [Gregorian calendar cut-over date].

Ans: [year < 1582] || [year == 1582 && month < 10] || [year == 1582 && month == 10 && day < 15]

Equality Comparison ==

You can use == to compare two integers [byte, short, int, long] and char. But do NOT use == to compare two floating-point numbers [float and double] because they are NOT precise. To compare floating-point numbers, set a threshold for their difference, e.g.,

public class FloatComparisonTest {
   public static void main[String[] args] {
      
      double d1 = 2.2 + 4.4;
      double d2 = 6.6;
      System.out.println[d1 == d2];  
      System.out.println[d1];        

      
      final double EPSILON = 1e-7;
      System.out.println[Math.abs[d1 - d2] < EPSILON];  
   }
}

You also CANNOT use == to compare two Strings because Strings are objects. You need to use str1.equals[str2] instead. This will be elaborated later.

Logical Operator Precedence

The precedence from highest to lowest is: '!' [unary], '^', '&&', '||'. But when in doubt, use parentheses!

System.out.println[true || true && false];    
System.out.println[true || [true && false]];  
System.out.println[[true || true] && false];  
 
System.out.println[false && true ^ true];     
System.out.println[false && [true ^ true]];   
System.out.println[[false && true] ^ true];   
Short-Circuit Operations

The binary AND [&&] and OR [||] operators are known as short-circuit operators, meaning that the right-operand will not be evaluated if the result can be determined by the left-operand. For example, false && rightOperand gives false and true || rightOperand give true without evaluating the right-operand. This may have adverse consequences if you rely on the right-operand to perform certain operations, e.g. false && [++i < 5] but ++i will not be evaluated.

String and '+' Concatenation Operator

In Java, '+' is a special operator. It is overloaded. Overloading means that it carries out different operations depending on the types of its operands.

  • If both operands are numeric [byte, short, int, long, float, double, char], '+' performs the usual addition. For examples,
    1 + 2 ⇒ 3   
    1.2 + 2.2 ⇒ 3.4   
    1 + 2.2 ⇒ 1.0 + 2.2 ⇒ 3.2  
    '0' + 2 ⇒ 48 + 2 ⇒ 50   
  • If both operands are Strings, '+' concatenates the two Strings and returns the concatenated String. For examples,
    "Hello" + "world" ⇒ "Helloworld"
    "Hi" + ", " + "world" + "!" ⇒ "Hi, world!"
  • If one of the operand is a String and the other is numeric, the numeric operand will be converted to String and the two Strings concatenated, e.g.,
    "The number is " + 5 ⇒ "The number is " + "5" ⇒ "The number is 5"
    "The average is " + average + "!" [suppose average=5.5] ⇒ "The average is " + "5.5" + "!" ⇒ "The average is 5.5!"
    "How about " + a + b [suppose a=1, b=1] ⇒ "How about 1" + b ⇒ "How about 11" [left-associative]
    "How about " + [a + b] [suppose a=1, b=1] ⇒ "How about " + 2 ⇒ "How about 2"

We use String concatenation operator '+' frequently in the print[] and println[] to produce the desired output String. For examples,

System.out.println["The sum is: " + sum];   
System.out.println["The square of " + input + " is " + squareInput];

Flow Control

There are three basic flow control constructs - sequential, conditional [or decision], and loop [or iteration], as illustrated below.

Sequential Flow Control

A program is a sequence of instructions executing one after another in a predictable manner. Sequential flow is the most common and straight-forward, where programming statements are executed in the order that they are written - from top to bottom in a sequential manner.

Conditional Flow Control

There are a few types of conditionals, if-then, if-then-else, nested-if, switch-case-default, and conditional expression.

if-then and if-then-else SyntaxExampleFlowchart
// if-then
if [booleanTest] {
   trueBlock;
}
// next statement
int mark = 80;
if [mark >= 80] {
   System.out.println["Well Done!"];
   System.out.println["Keep it up!"];
}
System.out.println["Life goes on!];

double temperature = 80.1;
if [temperature > 80] {
   System.out.println["Too Hot!"];
}
System.out.println["yummy!"];
// if-then-else
if [booleanTest] {
   trueBlock;
} else {
   falseBlock;
}
// next statement
int mark = 50;     // Assume that mark is [0, 100]
if [mark >= 50] {  // [50, 100]
   System.out.println["Congratulation!"];
   System.out.println["Keep it up!"];
} else {           // [0, 49]
   System.out.println["Try Harder!"];
}
System.out.println["Life goes on!"];

double temperature = 80.1;
if [temperature > 80] {
   System.out.println["Too Hot!"];
} else {
   System.out.println["Too Cold!"];
}
System.out.println["yummy!"];

Braces: You could omit the braces { }, if there is only one statement inside the block. For example,

int absValue = -5;
if [absValue < 0] absValue = -absValue;   
 
int min = 0, value = -5;
if [value < min] {     
   min = value;
   System.out.println["Found new min"];
}
 

int mark = 50;
if [mark >= 50] 
   System.out.println["PASS"];   
else {                           
   System.out.println["FAIL"];
   System.out.println["Try Harder!"];
}


int number1 = 8, number2 = 9, absDiff;
if [number1 > number2] absDiff = number1 - number2;
else absDiff = number2 - number1;

However, I recommend that you keep the braces to improve the readability of your program, even if there is only one statement in the block.

Nested-if SyntaxExampleFlowchart
// nested-if
if [booleanTest1] {
   block1;
} else if [booleanTest2] {
   block2;
} else if [booleanTest3] {
   block3;
} else if [booleanTest4] {
   ......
} else {
   elseBlock;
}
// next statement
int mark = 62;  // Assume that mark is [0, 100]
if [mark >= 80] {           // [80, 100]
   System.out.println["A"];
} else if [mark >= 65] {    // [65, 79]
   System.out.println["B"];
} else if [mark >= 50] {    // [50, 64]
   System.out.println["C"];
} else {                    // [0, 49]
   System.out.println["F"];
}
System.out.println["Life goes on!"];

double temperature = 61;
if [temperature > 80] {        // > 80
   System.out.println["Too Hot!"];
} else if [temperature > 75] { // [75, 80]
   System.out.println["Just right!"];
} else {                       // = 80] {           
   System.out.println["A"];
} else if [mark >= 65] {    
   System.out.println["B"];
} else if [mark >= 50] {    
   System.out.println["C"];
} else {                    
   System.out.println["F"];
}

if [mark < 50] {            
   System.out.println["F"];
} else if [mark < 65] {     
   System.out.println["C"];
} else if [mark < 80] {     
   System.out.println["B"];
} else {                    
   System.out.println["A"];
}
Dangling-else Problem

The "dangling-else" problem can be illustrated as follows:

int i = 0, j = 0;
if [i == 0]       
   if [j == 0]    
      System.out.println["i and j are zero"];
else System.out.println["xxx"];   

The else clause in the above code is syntactically applicable to both the outer-if and the inner-if, causing the dangling-else problem.

Java compiler resolves the dangling-else problem by associating the else clause with the innermost-if [i.e., the nearest-if]. Hence, the above code shall be interpreted as:

int i = 0, j = 0;
if [i == 0]
   if [j == 0]
      System.out.println["i and j are zero"];
   else    
      System.out.println["xxx"];

Dangling-else can be prevented by applying explicit parentheses. For example, if you wish to associate the else clause with the outer-if, do this:

int i = 0, j = 0;
if [i == 0] {
   if [j == 0] 
      System.out.println["i and j are zero"];
} else {
   System.out.println["i is not zero"];   
}


int i = 0, j = 0;
if [i == 0] {
   if [j == 0] {
      System.out.println["i and j are zero"];
   } else {
      System.out.println["i is zero, j is not zero"];   
   }
}
Nested-if vs. Sequential-if

Study the following code:

int mark = 81;
if [mark > 80] {  
   grade = 'A';
}
if [mark > 65 && mark = 50 && mark  80] {   
   grade = 'A';
} else if [mark > 65 && mark = 50 && mark  80] {         
   grade = 'A';
} else if [mark > 65] {  
   grade = 'B';
} else if [mark >= 50] { 
   grade = 'C';
} else {                 
   grade = 'F';
}
switch-case-default SyntaxExampleFlowchart
// switch-case-default
switch [selector] {
   case value1:
      block1; break;
   case value2:
      block2; break;
   case value3:
      block3; break;
   ......
   case valueN:
      blockN; break;
   default: // not the above
      defaultBlock;
}
// next statement

// Selector Types:
// byte, short, int,
// char, String
// Print number in word
int number = 3;
switch [number] {   // "int" selector
   case 1:   // "int" value
      System.out.println["ONE"]; break;
   case 2:
      System.out.println["TWO"]; break;
   case 3:
      System.out.println["THREE"]; break;
   default:
      System.err.println["OTHER"];
}

// Select arithmetic operation
char operator = '*';
int num1 = 5, num2 = 8, result;
switch [operator] {   // "char" selector
   case '+':   // "char" value
      result = num1 + num2; break;
   case '-': 
      result = num1 - num2; break;
   case '*': 
      result = num1 * num2; break;
   case '/': 
      result = num1 / num2; break;
   default:
      System.out.println["Unknown operator];
}

"switch-case-default" is an alternative to the "nested-if" for fixed-value tests [but not applicable for range tests]. You can use an int, byte, short, or char variable as the case-selector, but NOT long, float, double and boolean. JDK 1.7 supports String as the case-selector.

In a switch-case statement, a break statement is needed for each of the cases. If break is missing, execution will flow through the following case, which is typically a mistake. However, we could use this property to handle multiple-value selector. For example,

char inChar = 'x';
switch [inChar] {
   case 'a': case 'b': case 'c':   
      System.out.print[2]; break;
   case 'd': case 'e': case 'f':
      System.out.print[3]; break;
   case 'g': case 'h': case 'i':
      System.out.print[4]; break;
   case 'j': case 'k': case 'l':
      System.out.print[5]; break;
   ......
   default:
      System.out.println["Invalid Input"];
}
Conditional Expression [ ... ? ... : ... ]

A conditional operator is a ternary [3-operand] operator, in the form of booleanExpr ? trueExpr : falseExpr. Depending on the booleanExpr, it evaluates and returns the value of trueExpr or falseExpr.

SyntaxExamples
// Conditional Expression
booleanExpr ? trueExpr : falseExpr
  // An expression that returns
  // the value of trueExpr
  // or falseExpr
int num1 = 9, num2 = 8, max;
max = [num1 > num2] ? num1 : num2;  // RHS returns num1 or num2
// same as
if [num1 > num2] {
   max = num1;
} else {
   max = num2;
}

int value = -9, absValue;
absValue = [value > 0] ? value : -value;  // RHS returns value or -value
// same as
if [value > 0] absValue = value;
else absValue = -value;

int mark = 48;
System.out.println[[mark >= 50] ? "PASS" : "FAIL"];  // Return "PASS" or "FAIL"
// same as
if [mark >= 50] System.out.println["PASS"];
else System.out.println["FAIL"];

Conditional expression is a short-hand for if-else. But you should use it only for one-liner, for readability.

Exercises on Getting Started and Conditional

LINK

Loop Flow Control

Again, there are a few types of loops: for, while-do, and do-while.

SyntaxExampleFlowchart
// while-do loop
while [booleanTest] {
   body;
}
// next statement
// Sum from 1 to upperbound
int sum = 0;
final int UPPERBOUND = 100;
int number = 1;   // init
while [number = '1' && hexChar = 'a' && hexChar = 'A' && hexChar  0
      if [items.length > 0] {
         System.out.print["Enter the value of all items [separated by space]: "];
         for [int i = 0; i < items.length; ++i] {
            items[i] = in.nextInt[];
         }
      }
      in.close[];

      // Print array contents, need to handle first item and subsequent items differently
      System.out.print["The values are: ["];
      for [int i = 0; i < items.length; ++i] {
         if [i == 0] {
            // Print the first item without a leading commas
            System.out.print[items[0]];
         } else {
            // Print the subsequent items with a leading commas
            System.out.print[", " + items[i]];
         }
      }
      System.out.println["]"];
   }
}
Arrays.toString[] [JDK 5]

JDK 5 provides an built-in methods called Arrays.toString[anArray], which returns a String in the form [a0, a1, ..., an]. You need to import java.util.Arrays. For examples,

import java.util.Arrays;   

public class TestArrayToString {
   public static void main[String[] args] {
      
      int[] a1 = {6 ,1, 3, 4, 5};   
      int[] a2 = {};                
      double[] a3 = new double[1];  

      System.out.println[Arrays.toString[a1]];  
      System.out.println[Arrays.toString[a2]];  
      System.out.println[Arrays.toString[a3]];  
      a3[0] = 2.2;
      System.out.println[Arrays.toString[a3]];  
   }
}

Code Example: Horizontal and Vertical Histograms

The following program prompts user for the number of students, and the grade of each student. It then print the histogram, in horizontal and vertical forms, as follows:

Enter the grade for student 1: 98
Enter the grade for student 2: 100
Enter the grade for student 3: 9
Enter the grade for student 4: 3
Enter the grade for student 5: 56
Enter the grade for student 6: 58
Enter the grade for student 7: 59
Enter the grade for student 8: 87

 0-  9: **
10- 19:
20- 29:
30- 39:
40- 49:
50- 59: ***
60- 69:
70- 79:
80- 89: *
90-100: **

                                      *
   *                                  *                           *
   *                                  *                    *      *
  0-9   10-19  20-29  30-39  40-49  50-59  60-69  70-79  80-89  90-100
import java.util.Scanner;
import java.util.Arrays;   // for Arrays.toString[]
/**
 * Print the horizontal and vertical histograms of grades.
 */
public class GradesHistograms {
   public static void main[String[] args] {
      // Declare variables
      int numStudents;
      int[] grades;  // Declare array name, to be allocated after numStudents is known
      int[] bins = new int[10];  // int array of 10 histogram bins for 0-9, 10-19, ..., 90-100

      Scanner in = new Scanner[System.in];
      // Prompt and read the number of students as "int"
      System.out.print["Enter the number of students: "];
      numStudents = in.nextInt[];

      // Allocate the array
      grades = new int[numStudents];

      // Prompt and read the grades into the int array "grades"
      for [int i = 0; i < grades.length; ++i] {
         System.out.print["Enter the grade for student " + [i + 1] + ": "];
         grades[i] = in.nextInt[];
      }
      in.close[];

      // Print array for debugging
      System.out.println[Arrays.toString[grades]];

      // Populate the histogram bins
      for [int grade : grades] {
         if [grade == 100] {   // Need to handle 90-100 separately as it has 11 items.
            ++bins[9];
         } else {
            ++bins[grade/10];
         }
      }
      // Print array for debugging
      System.out.println[Arrays.toString[bins]];

      // Print the horizontal histogram
      // Rows are the histogram bins[0] to bins[9]
      // Columns are the counts in each bins[i]
      for [int binIdx = 0; binIdx < bins.length; ++binIdx] {
         // Print label
         if [binIdx != 9] {  // Need to handle 90-100 separately as it has 11 items
            System.out.printf["%2d-%3d: ", binIdx*10, binIdx*10+9];
         } else {
            System.out.printf["%2d-%3d: ", 90, 100];
         }
         // Print columns of stars
         for [int itemNo = 0; itemNo < bins[binIdx]; ++itemNo] {  // one star per item
            System.out.print["*"];
         }
         System.out.println[];
      }

      // Find the max value among the bins
      int binMax = bins[0];
      for [int binIdx = 1; binIdx < bins.length; ++binIdx] {
         if [binMax < bins[binIdx]] binMax = bins[binIdx];
      }

      // Print the Vertical histogram
      // Columns are the histogram bins[0] to bins[9]
      // Rows are the levels from binMax down to 1
      for [int level = binMax; level > 0; --level] {
         for [int binIdx = 0; binIdx < bins.length; ++binIdx] {
            if [bins[binIdx] >= level] {
               System.out.print["   *   "];
            } else {
               System.out.print["       "];
            }
         }
         System.out.println[];
      }
      // Print label
      for [int binIdx = 0; binIdx < bins.length; ++binIdx] {
         System.out.printf["%3d-%-3d", binIdx*10, [binIdx != 9] ? binIdx * 10 + 9 : 100];
            // Use '-' flag for left-aligned
      }
      System.out.println[];
   }
}
Notes:
  1. We use two arrays in this exercise, one for storing the grades of the students [of the length numStudents] and the other to storing the histogram counts [of length 10].
  2. We use a 10-element int arrays called bins, to keep the histogram counts for grades of [0, 9], [10, 19], ..., [90, 100]. Take note that there are 101 grades between [0, 100], and the last bin has 11 grades [instead of 10 for the rest]. The bins's index is grade/10, except grade of 100.

Code Example: Hexadecimal to Binary [Hex2Bin]

The following program prompts user for a hexadecimal string and convert it to its binary equivalence. For example,

Enter a Hexadecimal string: 1bE3
The equivalent binary for "1bE3" is "0001101111100011"
import java.util.Scanner;
/**
 * Prompt user for a hexadecimal string, and print its binary equivalent.
 */
public class Hex2Bin {
   public static void main[String[] args] {
      // Define variables
      String hexStr;     // The input hexadecimal String
      int hexStrLen;     // The length of hexStr
      char hexChar;      // Each char in the hexStr
      String binStr =""; // The equivalent binary String, to accumulate from an empty String
      // Lookup table for the binary sub-string corresponding to Hex digit '0' [index 0] to 'F' [index 15]
      final String[] BIN_STRS =
         {"0000", "0001", "0010", "0011",
          "0100", "0101", "0110", "0111",
          "1000", "1001", "1010", "1011",
          "1100", "1101", "1110", "1111"};

      // Prompt and read input as "String"
      Scanner in = new Scanner[System.in];
      System.out.print["Enter a Hexadecimal string: "];
      hexStr = in.next[];
      hexStrLen = hexStr.length[];
      in.close[];

      // Process the string from the left [most-significant hex digit]
      for [int charIdx = 0; charIdx < hexStrLen; ++charIdx] {
         hexChar = hexStr.charAt[charIdx];
         if [hexChar >= '0' && hexChar = 'a' && hexChar = 'A' && hexChar  0] {
         int hexDigit = dec % radix;   // 0-15
         hexStr = HEX_CHARS[hexDigit] + hexStr;  // Append in front of the hex string corresponds to reverse order
         dec = dec / radix;
      }
      System.out.println["The equivalent hexadecimal number is " + hexStr];
   }
}
Notes
  1. We use modulus/divide algorithm to get the hex digits [0-15] in reserve order. See "Number System Conversion".
  2. We look up the hex digit '0'-'F' from an array using index 0-15.

Exercises on Arrays

LINK

Multi-Dimensional Array

In Java, you can declare an array of arrays. For examples:

int grid[][] = new int[12][8];   
grid[0][0] = 8;
grid[1][1] = 5;
System.out.println[grid.length];      
System.out.println[grid[0].length];   
System.out.println[grid[11].length];  

In the above example, grid is an array of 12 elements. Each of the elements [grid[0] to grid[11]] is an 8-element int array. In other words, grid is a "12-element array" of "8-element int arrays". Hence, grid.length gives 12 and grid[0].length gives 8.

public class Array2DTest {
   public static void main[String[] args] {
      int[][] grid = new int[12][8];   // A 12x8 grid, in [row][col] or [y][x]
      final int NUM_ROWS = grid.length;       // 12
      final int NUM_COLS = grid[0].length;    // 8
   
      // Fill in grid
      for [int row = 0; row < NUM_ROWS; ++row] {
         for [int col = 0; col < NUM_COLS; ++col] {
            grid[row][col] = row*NUM_COLS + col + 1;
         }
      }
   
      // Print grid
      for [int row = 0; row < NUM_ROWS; ++row] {
         for [int col = 0; col < NUM_COLS; ++col] {
            System.out.printf["%3d", grid[row][col]];
         }
         System.out.println[];
      }
   }
}

To be precise, Java does not support multi-dimensional array directly. That is, it does not support syntax like grid[3, 2] like some languages. Furthermore, it is possible that the arrays in an array-of-arrays have different length.

Take note that the right way to view the "array of arrays" is as shown, instead of treating it as a 2D table, even if all the arrays have the same length.

For example,

public class Array2DWithDifferentLength {
   public static void main[String[] args] {
      int[][] grid = {
         {1, 2},
         {3, 4, 5},
         {6, 7, 8, 9}
      };
 
      // Print grid
      for [int y = 0; y < grid.length; ++y] {
         for [int x = 0; x < grid[y].length; ++x] {
            System.out.printf["%2d", grid[y][x]];
         }
         System.out.println[];
      }
 
      // Another 2D array
      int[][] grid1 = new int[3][];
      grid1[0] = new int[2];
      grid1[1] = new int[3];
      grid1[2] = new int[4];
 
      // Print grid - all elements init to 0
      for [int y = 0; y < grid1.length; ++y] {
         for [int x = 0; x < grid1[y].length; ++x] {
            System.out.printf["%2d", grid1[y][x]];
         }
         System.out.println[];
      }
   }
}

Methods [Functions]

Why Methods?

At times, a certain portion of code has to be used many times. Instead of re-writing the code many times, it is better to put them into a "subroutine", and "call" this "subroutine" many time - for ease of maintenance and understanding. Subroutine is called method [in Java] or function [in C/C++].

The benefits of using methods are:

  1. Divide and conquer: Construct the program from simple, small pieces or components. Modularize the program into self-contained tasks.
  2. Avoid repeating code: It is easy to copy and paste, but hard to maintain and synchronize all the copies.
  3. Software Reuse: You can reuse the methods in other programs, by packaging them into library code [or API].

Using Methods

Two parties are involved in using a method: a caller, who calls [or invokes] the method, and the method called.

The process is:

  1. The caller invokes a method and passes arguments to the method.
  2. The method:
    1. receives the arguments passed by the caller,
    2. performs the programmed operations defined in the method's body, and
    3. returns a result back to the caller.
  3. The caller receives the result, and continue its operations.

Example: Suppose that we need to evaluate the area of a circle many times, it is better to write a method called getArea[], and re-use it when needed.

public class EgMethodGetArea {
   // The entry main method
   public static void main[String[] args] {
      double r = 1.1, area, area2;
      // Call [Invoke] method getArea[] and return
      area = getArea[r];
      System.out.println["area is " + area];
      // Call method getArea[] again and return
      area2 = getArea[2.2];
      System.out.println["area 2 is " + area2];
      // Call method getArea[] one more time and return
      System.out.println["area 3 is " + getArea[3.3]];
   }
 
   // Method getArea[] Definition.
   // Compute and return the area [in double] of circle given its radius [in double].
   public static double getArea[double radius] {
      return radius * radius * Math.PI;
   }
}

The expected outputs are:

area is 3.8013271108436504
area 2 is 15.205308443374602
area 3 is 34.21194399759284

In the above example, a reusable method called getArea[] is defined, which receives an argument in double from the caller, performs the calculation, and return a double result to the caller. In the main[], we invoke getArea[] methods thrice, each time with a different parameter.

Take note that there is a transfer of control from the caller to the method called, and from the method back to the caller, as illustrated.

Tracing Method Invocation

You can trace method operations under Eclipse/NetBeans [Refer to the the Eclipse/NetBeans How-to article]:

  • Step Over: Treat the method call as one single step.
  • Step Into: Step into the method, so that you can trace the operations of the method.
  • Step Out: Complete the current method and return to the caller.
  • Set "Breakpoints" inside the method, and "resume" running to the next breakpoint.
Method Definition Syntax

The syntax for method definition is as follows:

public static returnValueType methodName[arg-1-type arg-1, arg-2-type arg-2,... ] {
   body;
}
public static double getArea[double radius] {
   return radius * radius * Math.PI;
}


public static int max[int number1, int number2] {
   if [number1 > number2] {
      return number1;
   } else {
      return number2;
   }
}

Take note that you need to specify the type of the arguments and the return value in method definition.

Calling Methods

To call a method, simply use methodName[arguments]. For examples, to call the above methods:

double area1 = getArea[1.1];  
double r2 = 2.2;
double area2 = getArea[r2];   
double r3 = 3.3;
System.out.println["Area is: " + area[r3]];

int result1 = max[5, 8];
int i1 = 7, i2 = 9;
int result2 = max[i1, i2];
System.out.println["Max is: " + max[15, 16]];

Take note that you need to specify the type in the method definition, but not during invocation.

Method Naming Convention

A method's name shall be a verb or verb phrase [action], comprising one or more words. The first word is in lowercase, while the rest are initial-capitalized [called camel-case]. For example, getArea[], setRadius[], moveDown[], isPrime[], etc.

Another Example:

public class EgMinMaxMethod {
   
   public static void main[String[] args] {
      int a = 6, b = 9, max, min;
      max = max[a, b];  
      min = min[a, b];  
      System.out.println[max + "," + min];
   
      System.out.println[max[5, 8]]; 
      System.out.println[min[5, 8]]; 
   }

   
   public static int max[int number1, int number2] {
      if [number1 > number2] {
         return number1;
      } else {
         return number2;
      }
   }

   
   public static int min[int number1, int number2] {
      return [number1 < number2] ? number1 : number2;
   }
}

The "return" statement

Inside the method body, you could use a return statement to return a value [of the returnValueType declared in the method's signature] to return a value back to the caller. The syntax is:

return aReturnValue;   
return;                

The "void" Return-Type

Suppose that you need a method to perform certain actions [e.g., printing] without a need to return a value to the caller, you can declare its return-value type as void. In the method's body, you could use a "return;" statement without a return value to return control to the caller. In this case, the return statement is optional. If there is no return statement, the entire body will be executed, and control returns to the caller at the end of the body.

Notice that main[] is a method with a return-value type of void. main[] is called by the Java runtime, perform the actions defined in the body, and return nothing back to the Java runtime.

Actual Parameters vs. Formal Parameters

Recall that a method receives arguments from its caller, performs the actions defined in the method's body, and return a value [or nothing] to the caller.

In the above example, the variable [double radius] declared in the signature of getArea[double radius] is known as formal parameter. Its scope is within the method's body. When the method is invoked by a caller, the caller must supply so-called actual parameters or arguments, whose value is then used for the actual computation. For example, when the method is invoked via "area1=getArea[radius1]", radius1 is the actual parameter, with a value of 1.1.

Code Example: Magic Number

The following program contains a boolean method called isMagic[int number], which returns true if the given number contains the digit 8, e.g., 18, 108, and 1288. The signature of the method is:

public static boolean isMagic[int number];

It also provides the main[] method to test the isMagic[]. For example,

Enter a positive integer: 1288
1288 is a magic number

Enter a positive integer: 1234567
1234567 is not a magic number
import java.util.Scanner;
/**
 * This program contains a boolean method called isMagic[int number], which tests if the
 * given number contains the digit 8.
 */
public class MagicNumber {
   public static void main[String[] args] {
      // Declare variables
      int number;
      Scanner in = new Scanner[System.in];

      // Prompt and read input as "int"
      System.out.print["Enter a positive integer: "];
      number = in.nextInt[];

      // Call isMagic[] to test the input
      if [isMagic[number]] {
         System.out.println[number + " is a magic number"];
      } else {
         System.out.println[number + " is not a magic number"];
      }
      in.close[];
   }

   /**
    * Check if the given int contains the digit 8, e.g., 18, 82, 1688.
    * @param  number The given integer
    * @return        true if number contains the digit 8
    * @Precondition  number > 0 [i.e., a positive integer]
    */
   public static boolean isMagic[int number] {
      boolean isMagic = false;   // shall change to true if found a digit 8

      // Extract and check each digit
      while [number > 0] {
         int digit = number % 10;   // Extract the last digit
         if [digit == 8] {
            isMagic = true;
            break;  // only need to find one digit 8
         }
         number /= 10;   // Drop the last digit and repeat
      }
      return isMagic;
   }
}

Take note of the proper documentation comment for the method.

Code Example: int Array Methods

The following program contains various method for int array with signatures as follows:

public static void print[int[] array];      
public static int min[int[] array];         
public static int sum[int[] array];         
public static double average[int[] array];  

It also contains the main[] method to test all the methods. For example,

Enter the number of items: 5
Enter the value of all items [separated by space]: 8 1 3 9 4
The values are: [8, 1, 3, 9, 4]
The min is: 1
The sum is: 25
The average [rounded to 2 decimal places] is: 5.00
import java.util.Scanner;
/**
 * Test various int[] methods.
 */
public class IntArrayMethodsTest {
   public static void main[String[] args] {
      // Declare variables
      final int NUM_ITEMS;
      int[] items;  // Declare array name, to be allocated after numItems is known

      // Prompt for a non-negative integer for the number of items;
      // and read the input as "int". No input validation.
      Scanner in = new Scanner[System.in];
      System.out.print["Enter the number of items: "];
      NUM_ITEMS = in.nextInt[];

      // Allocate the array
      items = new int[NUM_ITEMS];

      // Prompt and read the items into the "int" array, if array length > 0
      if [items.length > 0] {
         System.out.print["Enter the value of all items [separated by space]: "];
         for [int i = 0; i < items.length; ++i] {
            items[i] = in.nextInt[];
         }
      }
      in.close[];

      // Test the methods
      System.out.print["The values are: "];
      print[items];
      System.out.println["The min is: " + min[items]];
      System.out.println["The sum is: " + sum[items]];
      System.out.printf["The average [rounded to 2 decimal places] is: %.2f%n", average[items]];
   }

   /**
    * Prints the given int array in the form of [x1, x2, ..., xn]
    * @param  array   The given int array
    * @Postcondition  Print output as side effect
    */
   public static void print[int[] array] {
      System.out.print["["];
      for [int i = 0; i < array.length; ++i] {
         System.out.print[[i == 0] ? array[i] : ", " + array[i]];
      }
      System.out.println["]"];
   }

   /**
    * Get the min of the given int array
    * @param  array  The given int array
    * @return        The min value of the given array
    */
   public static int min[int[] array] {
      int min = array[0];
      for [int i = 1; i < array.length; ++i] {
         if [array[i] < min] min = array[i];
      }
      return min;
   }

   /**
    * Get the sum of the given int array
    * @param  array  The given int array
    * @return        The sum of the given array
    */
   public static int sum[int[] array] {
      int sum = 0;
      for [int item: array] sum += item;
      return sum;
   }

   /**
    * Get the average of the given int array
    * @param  array  The given int array
    * @return        The average of the given array
    */
   public static double average[int[] array] {
      return [double][sum[array]] / array.length;
   }
}

Pass-by-Value for Primitive-Type Parameters

In Java, when an argument of primitive type is pass into a method, a copy is created and passed into the method. The invoked method works on the cloned copy, and cannot modify the original copy. This is known as pass-by-value.

For example,

public class PassByValueTest {
   public static void main[String[] args] {
      int number = 8, result;
      System.out.println["In caller, before calling the method, number is: " + number];  // 8
      result = increment[number]; // invoke method with primitive-type parameter
      System.out.println["In caller, after calling the method, number is: " + number];   // 8
      System.out.println["The result is " + result];  // 9
   }
 
   // Return number + 1
   public static int increment[int number] {
      System.out.println["Inside method, before operation, number is " + number]; // 8
      ++number;  // change the parameter
      System.out.println["Inside method, after operation, number is " + number];  // 9
      return number;
   }
}

Notes:

  1. Although there is a variable called number in both the main[] and increment[] method, there are two distinct copies - one available in main[] and another available in increment[] - happen to have the same name. You can change the name of either one, without affecting the program.

Pass-by-Reference for Arrays and Objects

As mentioned, for primitive-type parameters, a cloned copy is made and passed into the method. Hence, the method cannot modify the values in the caller. It is known as pass-by-value.

For arrays [and objects - to be described in the later chapter], the array reference is passed into the method and the method can modify the contents of array's elements. It is known as pass-by-reference. For example,

import java.util.Arrays;  
public class PassByReferenceTest {
   public static void main[String[] args] {
      int[] testArray = {9, 5, 6, 1, 4};
      System.out.println["In caller, before calling the method, array is: "
            + Arrays.toString[testArray]];   
      
      increment[testArray];
      System.out.println["In caller, after calling the method, array is: "
            + Arrays.toString[testArray]];   
   }

   
   public static void increment[int[] array] {
      System.out.println["Inside method, before operation, array is "
           + Arrays.toString[array]];   
      
      for [int i = 0; i < array.length; ++i] ++array[i];
      System.out.println["Inside method, after operation, array is "
           + Arrays.toString[array]];   
   }
}

Varargs - Method with Variable Number of Formal Arguments [JDK 5]

Before JDK 5, a method has to be declared with a fixed number of formal arguments. C-like printf[], which take a variable number of argument, cannot not be implemented. Although you can use an array for passing a variable number of arguments, it is not neat and requires some programming efforts.

JDK 5 introduces variable arguments [or varargs] and a new syntax "Type...". For example,

public PrintWriter printf[String format, Object... args]
public PrintWriter printf[Local l, String format, Object... args]

Varargs can be used only for the last argument. The three dots [...] indicate that the last argument may be passed as an array or as a sequence of comma-separated arguments. The compiler automatically packs the varargs into an array. You could then retrieve and process each of these arguments inside the method's body as an array. It is possible to pass varargs as an array, because Java maintains the length of the array in an associated variable length.


public class VarargsTest {
   // A method which takes a variable number of arguments [varargs]
   public static void doSomething[String... strs] {
      System.out.print["Arguments are: "];
      for [String str : strs] {
         System.out.print[str + ", "];
      }
      System.out.println[];
   }
 
   // A method which takes exactly two arguments
   public static void doSomething[String s1, String s2] {
      System.out.println["Overloaded version with 2 args: " + s1 + ", " + s2];
   }
 
   // Cannot overload with this method - crash with varargs version
   // public static void doSomething[String[] strs]
 
   // Test main[] method
   // Can also use String... instead of String[]
   public static void main[String... args] {
      doSomething["Hello", "world", "again", "and", "again"];
      doSomething["Hello", "world"];
 
      String[] strs = {"apple", "orange"};
      doSomething[strs];  // invoke varargs version
   }
}

Notes:

  • If you define a method that takes a varargs String..., you cannot define an overloaded method that takes a String[].
  • "varargs" will be matched last among the overloaded methods. The varargsMethod[String, String], which is more specific, is matched before the varargsMethod[String...].
  • From JDK 5, you can also declare your main[] method as:
    public static void main[String... args] { .... }  

Implicit Type-Casting for Method's Parameters

A method that takes a double parameter can accept any numeric primitive type, such as int or float. This is because implicit type-casting is carried out. However, a method that take a int parameter cannot accept a double value. This is because the implicit type-casting is always a widening conversion which prevents loss of precision. An explicit type-cast is required for narrowing conversion. Read "Type-Casting" on the conversion rules.

Method Overloading

In Java, a method [of a particular method name] can have more than one versions, each version operates on different set of parameters - known as method overloading. The versions shall be differentiated by the numbers, types, or orders of the parameters.

Example 1
public class AverageMethodOverloading {
   public static void main[String[] args] {
      System.out.println[average[8, 6]];     
      System.out.println[average[8, 6, 9]];  
      System.out.println[average[8.1, 6.1]]; 
      System.out.println[average[8, 6.1]];
           
      //average[1, 2, 3, 4]  
   }

   
   public static int average[int n1, int n2] {
      System.out.println["version 1"];
      return [n1 + n2]/2;  
   }

   
   public static int average[int n1, int n2, int n3] {
      System.out.println["version 2"];
      return [n1 + n2 + n3]/3;   
   }

   
   public static double average[double n1, double n2] {
      System.out.println["version 3"];
      return [n1 + n2]/2.0;  
   }
}

The expected outputs are:

version 1
7
version 2
7
version 3
7.1
version 3
7.05
Example 2: Arrays

Suppose you need a method to compute the sum of the elements for int[], short[], float[] and double[], you need to write all overloaded versions - there is no shortcut.

public class SumArrayMethodOverloading {
   public static void main[String[] args] {
      int[] a1 = {9, 1, 2, 6, 5};
      System.out.println[sum[a1]];     
      double[] a2 = {1.1, 2.2, 3.3};
      System.out.println[sum[a2]];     
      float[] a3 = {1.1f, 2.2f, 3.3f};
      //System.out.println[sum[a3]];   
   }

   
   public static int sum[int[] array] {
      System.out.println["version 1"];
      int sum = 0;
      for [int item : array] sum += item;
      return sum;  
   }

   
   public static double sum[double[] array] {
      System.out.println["version 2"];
      double sum = 0.0;
      for [double item : array] sum += item;
      return sum;  
   }
}

Notes:

  1. Unlike primitives, where int would be autocasted to double during method invocation, int[] is not casted to double[].
  2. To handle all the 7 primitive number type arrays, you need to write 7 overloaded versions to handle each array types!

"boolean" Methods

A boolean method returns a boolean value to the caller.

Suppose that we wish to write a method called isOdd[] to check if a given number is odd.

/**
 *  Testing boolean method [method that returns a boolean value]
 */
public class BooleanMethodTest {
   // This method returns a boolean value
   public static boolean isOdd[int number] {
      if [number % 2 == 1] {
         return true;
      } else {
         return false;
      }
   }
 
   public static void main[String[] args] {
      System.out.println[isOdd[5]];  // true
      System.out.println[isOdd[6]];  // false
      System.out.println[isOdd[-5]]; // false
   }
}

This seemingly correct code produces false for -5, because -5%2 is -1 instead of 1. You may rewrite the condition:

public static boolean isOdd[int number] {
   if [number % 2 == 0] {
      return false;
   } else {
      return true;
   }
}

The above produces the correct answer, but is poor. For boolean method, you can simply return the resultant boolean value of the comparison, instead of using a conditional statement, as follow:

public static boolean isEven[int number] {
   return [number % 2 == 0];
}
public static boolean isOdd[int number] {
   return ![number % 2 == 0];
}

Mathematical Methods

JDK provides many common-used Mathematical methods in a class called Math. The signatures of some of these methods are:

double Math.pow[double x, double y] 
double Math.sqrt[double x]          
double Math.random[]                
double Math.sin[]
double Math.cos[]

The Math class also provide two constants:

Math.PI   
Math.E    

To check all the available methods, open JDK API documentation ⇒ select module "java.base" ⇒ select package "java.lang" ⇒ select class "Math" ⇒ choose method [@ //docs.oracle.com/javase/10/docs/api/java/lang/Math.html for JDK 10].

For examples,

int secretNumber = [int]Math.random[]*100;  
 
double radius = 5.5;
double area = radius*radius*Math.PI;
area = Math.pow[radius, 2]*Math.PI;         
 
int x1 = 1, y1 = 1, x2 = 2, y2 = 2;
double distance = Math.sqrt[[x2-x1]*[x2-x1] + [y2-y1]*[y2-y1]];
int dx = x2 - x1;
int dy = y2 - y1;
distance = Math.sqrt[dx*dx + dy*dy];        

Exercises on Methods

LINK

Command-Line Arguments

Java's main[String[] args] method takes an argument: String[] args, i.e., a String array named args. This is known as "command-line arguments", which corresponds to the augments provided by the user when the java program is invoked. For example, a Java program called Arithmetic could be invoked with additional command-line arguments as follows [in a "cmd" shell]:

java Arithmetic 12 3456 +

Each argument, i.e., "12", "3456" and "+", is a String. Java runtime packs all the arguments into a String array and passes into the main[] method as args. For this example, args has the following properties:

args = {"12", "3456", "+"}   
args.length = 3              
args[0] = "12"   
args[1] = "3456"
args[2] = "+"
args[0].length[] = 2   
args[1].length[] = 4
args[2].length[] = 1

Code Example: Arithmetic

The program Arithmetic reads three parameters form the command-line, two integers and an arithmetic operator ['+', '-', '*', or '/'], and performs the arithmetic operation accordingly. For example,

java Arithmetic 3 2 +
3+2=5
java Arithmetic 3 2 -
3-2=1
java Arithmetic 3 2 /
3/2=1
public class Arithmetic {
   public static void main [String[] args] {
      int operand1, operand2;
      char theOperator;
      operand1 = Integer.parseInt[args[0]];  // Convert String to int
      operand2 = Integer.parseInt[args[1]];
      theOperator = args[2].charAt[0];       // Consider only 1st character
      System.out.print[args[0] + args[2] + args[1] + "="];
      switch[theOperator] {
         case ['+']:
            System.out.println[operand1 + operand2]; break;
         case ['-']:
            System.out.println[operand1 - operand2]; break;
         case ['*']:
            System.out.println[operand1 * operand2]; break;
         case ['/']:
            System.out.println[operand1 / operand2]; break;
         default:
            System.out.printf["%nError: Invalid operator!"];
      }
   }
}

Exercises on Command-Line Arguments

LINK

[Advanced] Bitwise Operations

Bitwise Logical Operations

Bitwise operators perform operations on one or two operands on a bit-by-bit basis, as follows, in descending order of precedences.

OperatorModeUsageDescriptionExample
~ Unary ~x Bitwise NOT [inversion]  
& Binary x & y Bitwise AND  
| Binary x | y Bitwise OR  
^ Binary x ^ y Bitwise XOR  
Example
public class TestBitwiseOp {
   public static void main[String[] args] {
      int x = 0xAAAA_5555;                // a negative number [sign bit [msb] = 1]
      int y = 0x5555_1111;                // a positive number [sign bit [msb] = 0]
      System.out.printf["%d%n", x];       // -1431677611
      System.out.printf["%d%n", y];       // 1431638289
      System.out.printf["%08X%n", ~x];    // 5555AAAAH
      System.out.printf["%08X%n", x & y]; // 00001111H
      System.out.printf["%08X%n", x | y]; // FFFF5555H
      System.out.printf["%08X%n", x ^ y]; // FFFF4444H
   }
}

Compound operator &=, |= and ^= are also available, e.g., x &= y is the same as x = x & y.

Take note that:

  1. '&', '|' and '^' are applicable when both operands are integers [int, byte, short, long and char] or booleans. When both operands are integers, they perform bitwise operations. When both operands are booleans, they perform logical AND, OR, XOR operations [i.e., same as logical &&, || and ^]. They are not applicable to float and double. On the other hand, logical AND [&&] and OR [||] are applicable to booleans only.
    System.out.println[true & true];   
    System.out.println[0x1 & 0xffff];  
    System.out.println[true && true];  
  2. The bitwise NOT [or bit inversion] operator is represented as '~', which is different from logical NOT [!].
  3. The bitwise XOR is represented as '^', which is the same as logical XOR [^].
  4. The operators' precedence is in this order: '~', '&', '^', '|', '&&', '||'. For example,
    System.out.println[true | true & false];  
    System.out.println[true ^ true & false];  

Bitwise operations are powerful and yet extremely efficient. [Example on advanced usage.]

Bit-Shift Operations

Bit-shift operators perform left or right shift on an operand by a specified number of bits. Right-shift can be either signed-extended [>>] [padded with signed bit] or unsigned-extended [>>>] [padded with zeros]. Left-shift is always padded with zeros [for both signed and unsigned].

OperatorModeUsageDescriptionExample
Binary x >> count Right-shift and padded with sign bit [signed-extended right-shift]  
>>> Binary x >>> count Right-shift and padded with zeros [unsigned-extended right-shift]  

Since all the Java's integers [byte, short, int and long] are signed integers, left-shift > operators perform signed-extended bit shift. Signed-extended right shift >> pads the most significant bits with the sign bit to maintain its sign [i.e., padded with zeros for positive numbers and ones for negative numbers]. Operator >>> [introduced in Java, not in C/C++] is needed to perform unsigned-extended right shift, which always pads the most significant bits with zeros. There is no difference between the signed-extended and unsigned-extended left shift, as both operations pad the least significant bits with zeros.

Example
public class BitShiftTest {
   public static void main[String[] args] {
      int x = 0xAAAA5555;               // a negative number [sign bit [msb] = 1]
      int y = 0x55551111;               // a positive number [sign bit [msb] = 0]
      System.out.printf["%d%n", x];     // -1431677611
      System.out.printf["%d%n", y];     // 1431638289
      System.out.printf["%08X%n", x1];  // D5552AAAH
      System.out.printf["%d%n", x>>1];    // negative
      System.out.printf["%08X%n", y>>1];  // 2AAA8888H
      System.out.printf["%08d%n", y>>1];  // positive
      System.out.printf["%08X%n", x>>>1]; // 55552AAAH
      System.out.printf["%d%n", x>>>1];   // positive
      System.out.printf["%08X%n", y>>>1]; // 2AAA8888
      System.out.printf["%d%n", y>>>1];   // positive
 
      // More efficient to use signed-right-right to perform division by 2, 4, 8,...
      int i1 = 12345;
      System.out.println["i1 divides by 2 is " + [i1 >> 1]];
      System.out.println["i1 divides by 4 is " + [i1 >> 2]];
      System.out.println["i1 divides by 8 is " + [i1 >> 3]];
      int i2 = -12345;
      System.out.println["i2 divides by 2 is " + [i2 >> 1]];
      System.out.println["i2 divides by 4 is " + [i2 >> 2]];
      System.out.println["i2 divides by 8 is " + [i2 >> 3]];
   }
}

As seen from the example, it is more efficient to use sign-right-shift to perform division by 2, 4, 8... [power of 2], as integers are stored in binary.

[More example on advanced usage.]

Types and Bitwise Operations

The bitwise operators are applicable to integral primitive types: byte, short, int, long and char. char is treated as unsigned 16-bit integer. There are not applicable to float and double. The '&', '|', '^', when apply to two booleans, perform logical operations. Bit-shift operators are not applicable to booleans.

Like binary arithmetic operations:

  • byte, short and char operands are first promoted to int.
  • If both the operands are of the same type [int or long], they are evaluated in that type and returns a result of that type.
  • If the operands are of different types, the smaller operand [int] is promoted to the larger one [long]. It then operates on the larger type [long] and returns a result in the larger type [long].

Algorithms

Before writing a program to solve a problem, you have to first develop the steps involved, called algorithm, and then translate the algorithm into programming statements. This is the hardest part in programming, which is also hard to teach because the it involves intuition, knowledge and experience.

An algorithm is a step-by-step instruction to accomplice a task, which may involve decision and iteration. It is often expressed in English-like pseudocode, before translating into programming statement of a particular programming language. There is no standard on how to write pseudocode - simply write something that you, as well as other people, can understand the steps involved, and able to translate into a working program.

Algorithm for Prime Testing

Ancient Greek mathematicians like Euclid and Eratosthenes [around 300-200 BC] had developed many algorithms [or step-by-step instructions] to work on prime numbers. By definition, a prime is a positive integer that is divisible by one and itself only.

To test whether a number x is a prime number, we could apply the definition by dividing x by 2, 3, 4, ..., up to x-1. If no divisor is found, then x is a prime number. Since divisors come in pair, there is no need to try all the factors until x-1, but up to √x.

int maxFactor = [int]Math.sqrt[x];   
assume x is a prime;
for [int factor = 2; factor = b, the Euclidean algorithm is based on these two properties:

1.  GCD[a, 0] = a
2.  GCD[a, b] = GCD[b, a mod b], where "a mod b" denotes the remainder of a divides by b.

For example,

GCD[15, 5] = GCD[5, 0] = 5
GCD[99,88] = GCD[88,11] = GCD[11,0] = 11
GCD[3456,1233] = GCD[1233,990] = GCD[990,243] = GCD[243,18] = GCD[18,9] = GCD[9,0] = 9

The Euclidean algorithm is as follows:

GCD[a, b]  
while [b != 0] {

   temp ← b
   b ← a mod b
   a ← temp
}

GCD is a

Before explaining the algorithm, suppose we want to exchange [or swap] the values of two variables x and y. Explain why the following code does not work.

int x = 55, y=66;

x = y;
y = x;

To swap the values of two variables, we need to define a temporary variable as follows:

int x = 55, y=66;
int temp;

temp = y;
y = x;
x = temp;

Let us look into the Euclidean algorithm, GCD[a, b] = a, if b is 0. Otherwise, we replace a by b; b by [a mod b], and compute GCD[b, a mod b]. Repeat the process until the second term is 0. Try this out on pencil-and-paper to convince yourself that it works.

TRY: Write a program called GCD, based on the above algorithm.

Exercises on Algorithm

LINK

Summary

This chapter covers the Java programming basics:

  • Comments, Statements and Blocks.
  • Variables, Literals, Expressions.
  • The concept of type and Java's eight primitive types: byte, short, int, long, float, double, char, and boolean; and String.
  • Implicit and explicit type-casting.
  • Operators: assignment [=], arithmetic operators [+, -, *, /, %], increment/decrement [++, --] relational operators [==, !=, >, >=, >].
  • Developing algorithm for solving problems.

Link to Java References and Resources

More References and Resources

  1. [MUST READ] "Code Conventions for the Java Programming Language" @ //www.oracle.com/technetwork/java/codeconvtoc-136057.html, or google the title, Sun Microsystems [now Oracle], 1999.

Chủ Đề