Thursday, July 29, 2021

Java Tutorial: Formatting Date&Time, Number and String

Chapters

Format Specifiers

If we want more control of how our string is formatted, we can use format specifiers to do just that. Some methods support the use of format specifiers like the printf() and format() methods. Format specifiers start with "%" symbol, followed by characters(converters) that do the formatting and optionally, other formatting elements like flags. These converters indicate the type to be formatted.
Format specifiers for general, character, and numeric types
%[argument_index$][flags][width][.precision]conversion

e.g. 
String str = String.format("PI: %f, %1$+010.5f",Math.PI);

Result
PI: 3.141593, +003.14159
The elements enclosed with brackets[] are optional elements. "%" and conversion elements are the essential parts of the formatter. Elements must be placed according to the order of the format specifiers above.

argument_index is a decimal integer indicating the position of the argument in the argument list. The first argument is referenced by "1$", the second by "2$", etc. Use the < flag to refer to previous argument that is used.

flags is a set of characters that modify the output format. The set of valid flags depends on the conversion. In other words, some flag may not work with some converters.
e.g. #,+,-,0

width is a positive decimal integer indicating the minimum number of characters to be written to the output.

precision is a non-negative decimal integer preceded by dot(.) and it's usually used to restrict the number of characters. The specific behavior depends on the conversion.

conversion or converter indicates the type to be formatted.
e.g. d,f,h

Conversion elements are divided into different categories as follows:

General: may be applied to any argument type.

Character: may be applied to basic types which represent Unicode characters like char, byte, Character,etc. This conversion may also be applied to the types int and Integer when Character.isValidCodePoint(int) returns true.

Numeric: may be applied to integral and floating-point types like int, float, Double, etc.

Date/Time: may be applied to Java types which are capable of encoding a date or time like long, Calendar, Date, etc.

Percent: produces a literal '%' ('\u0025')

Line Separator: produces the platform-specific line separator.

Conversion elements, except for percent(%%) and line separator(%n), expect arguments. This tutorial is just a summary of the formatting tutorial in java. Visit the Formatter documentation to read the full, informative and insightful documentation about formatting in java.
Formatting Strings and Numbers

We can format strings and numbers by using the printf() method or the format() method. These two methods are present in some streams like PrintStream. format() method can also be seen in String class.

Let's try formatting strings and display them on the console.
public class SampleClass{

  public static void main(String[]args){
    String str1 = "Systemic";
    String str2 = "Caution";
    
    //we can use printf() and format()
    //to format an output of a
    //stream
    //printf(String format, Object... args)
    System.out.printf("Sample#1: %n%s%n%S",
                      str1,str2);
    
    System.out.println();
    
    //Formatting doesn't change 
    //the argument's original value
    System.out.println(str2);
  }
}

Result
Sample#1:
Systemic
CAUTION
Caution
First off, let's examine Sample#1. So, the first argument of prinf() and format() is the format string. This string will be displayed on the console. The second arugment is an array of objects. These objects are called arguments of the format string. When evaluating the format string, we start from left to right position.

So, this is the format string: "Sample#1: %n%s%n%S%n"
Let's evaluate this string starting from the left side. There's no specifier in this sebsequence: Sample#1:
So, java writes this subsequence as is. Next, we found a format specifier. Remember, format specifier starts with "%" and ends with a convertor.

the format specifier is "%n". This specifier is a line-separator specifier. This specifier is platform-specific and we should use this instead of "\n". There are java platforms that don't recognize "\n" as newline. Some platforms prefer "\r\n" as newline. This specifier doesn't expect an argument.

Next, that "%n" is followed by "%s" which is a string specifier. This specifier formats an argument as string. This specifier expects an argument and will throw MissingFormatArgumentException if there's no argument found.

This specifier is our first specifier that requires an argument so, this specifier formats the first argument which is the string "Systemic" in the example above. Next, "%s" is followed by "%n", followed by "%S". "%S" is the same as "%s" the only difference is that "%S" formats argument's letters into uppercase letters whereas "%s" doesn't format the letter case of an argument. This also applies to other convertors except for "%n" and "%%".

"%S" is the second specifier that requires an argument so, it formats the second argument which is the string "Caution" in the example above.

Let's try another example.
import java.util.UnknownFormatConversionException;
import java.util.IllegalFormatConversionException;
public class SampleClass{

  public static void main(String[]args){
    try{
      //format() is equivalent to printf().
      //according to java documentation, printf()
      //is more of a convenience method
      System.out.format("Sample#2:%nchar: %c%n"+
                        "dec: %d%noct: %2$o%n"+
                        "hex: %<X%npercent: %<d%%",
                        '\u0042',1325);
      }
      catch(UnknownFormatConversionException e){
        System.err.printf("%nUnknown Format!%nMessage: %s%n"+
                          "Conversion char: %s",
                          e.getMessage(),e.getConversion());
      }
      catch(IllegalFormatConversionException e){
        System.err.printf("%nIllegal Format!%nConverstion char:"+
                          " %c",
                          e.getConversion());
      }
  }
}

Result
Sample#2:
char: B
dec: 1325
oct: 2455
hex: 52D
percent: 1325%
So, this is the format String
"Sample#2:%nchar: %c%ndec: %d%noct: %2$o%nhex: %<X%npercent: %<d%%"
Let's examine this format and start off with "%c". "%c" specifier formats an argument as a character. We use this convertor if we have character argument like unicode sequence and other character sequences.

Next, "%d" formats an argument to a decimal integer. Don't be confuse between decimal integer and decimal number. Decimal integers refers to base-10 integer whereas decimal number refers to a number with fractional part or refers to the fractional part of a number. In US and some other countries, fractional part is separated by dot(.).

Next is this format specifier "%2$o". This specifier contains argument index and an "o" converter. "2$" refers to the second argument which is an integer. "o" formats the second argument to an octal(base-8) integer. In the example above, there are only two arguments. If we remove the argument index, we will encounter an error 'cause "o" expects a third argument.

By using argument index, the second argument is reused and "o" formats the argument to an octal(base-8) integer.

Next, Let's examine this specifier "%<X". "<" refers to the previous argument that has been used. "%2$o" precedes "%<X" and "%2$o" refers to the second argument so, "%<" also refers to the second argument. "X" formats argument to a hexadecimeal(base-16) integer.

"%%" puts a literal percent(%) in the format string. "%" alone indicates a format specifier in format string whereas "%%" indicates a percent(%) literal in format string.

In the example above, UnknownFormatConversionException and IllegalFormatConversionException can be caught. try changing this specifier "%2$o%n" into "%1$o%n" and you will encounter an IllegalFormatConversionException. It throws an exception 'cause "1$" refers to the first argument which is a character and the character can't be formatted to octal integer.

Try changing this specifier "%<X" into "%&X" and you will encounter an UnknownFormatConversionException. It throws an exception 'cause "&" is not a valid formatting element.

Those exceptions are usually encountered when formatting string. There more exceptions that can be encountered. Also, notice that we use System.err instead of System.out to display the error messages on the console. err and out are the same though by convention, use err when displaying error messages in console-based applications.

Let's try one more example.
import java.util.Locale;
public class SampleClass{

  public static void main(String[]args){
    //there's a format() method in String
    //class that we can use. This method
    //is helpful if we want to format 
    //a string and put it somewhere
    //else aside from streams
    //like in a textbox for example
    String str = String.format(Locale.US,
                               "%,.1f",
                               1234567.55);
    System.out.println(str);
    str = String.format("%.4s","Hello Everyone!");
    System.out.println(str);
    str = String.format("%010d",150);
    System.out.println(str);
    str = String.format("[%-10d]",150);
    System.out.println(str);
  }
}

Result
1,234,567.6
Hell
0000000150
[150       ]
First off, let's examine this specifier "%,.1f". "," groups numbers using a locale-specific grouping separators. In US, comma(,) is used to group numbers to represent thousands and beyond. Try changing the locale to Locale.FRENCH and you will get a different grouping separators.

".1" is a precision element. When applied in floating-point number, precision restricts the floating-point's decimal places and round off the last decimal place. "f" is a converter that formats floating-point argument to a floating-point number.

Next, let's examine this specifier "%.4s". ".4" is a precision element. When applied in a string, precision restricts the maximum character that is gonna be included in the formatted result. "s" is convertor that formats argument to a string.

Next, this specifer "%010d". "0" is a flag that pads 0 from left to right. This flag requires width otherwise, an exception will be thrown. Also, there are flags are not allowed to be concatenated with "0" flag like the "-" flag. "10" is the width. It specifiy the maximum character length of the formatted result. If the argument length is less than the width, and "0" flag is set then, excess length will be padded with 0. If "0" flag is not set, whitespace will be used for padding. "d" is a converter that formats an integer argument to a decimal integer.

Last, this specifier "[%-10d]". "[]" are literals. "-" is a flag that formats the alignment of an argument to left justified. "10" is the width and "d" is the decimal integer converter.

NumberFormat Class

We use NumberFormat class to format or parse numbers. NumberFormat is an abstract class so we can't instantiate it directly. Though, there are static NumberFormat's methods that return a NumberFormat instance like getInstance() and other getXXXInstance() methods.
import java.text.NumberFormat;
import java.util.Locale;
public class SampleClass{

  public static void main(String[]args){
    //Use getInstance() or getNumberInstance()
    //to get the normal number format.
    NumberFormat nf = NumberFormat.getInstance();
    
    float f = 12345.44f;
    System.out.println("Default: " + nf.format(f));
    
    nf = NumberFormat.getInstance(Locale.US);
    String str = nf.format(f);
    System.out.println("US: " + str);
    
    nf = NumberFormat.getInstance(Locale.ITALY);
    System.out.println("ITALY: " + nf.format(f));
    
    nf = NumberFormat.getInstance(Locale.US);
    nf.setGroupingUsed(false);
    System.out.println("US: " + nf.format(f));
  }
}

Result
Default: 12,345.44
US: 12,345.44
ITALY: 12.345,44
US: 12345.44
In the example above, getInstance() without argument returns a NumberFormat type with default locale. Default locale is platform-specific. As the time of this writing, my default locale is Locale.US. That's why the first and second result are equal. Yours might not be. Italy groups numbers differently, That's why it's not equal to the previous results.

Use setGroupingUsed() to disable number grouping. By default, setGroupingUsed() is set to true.

Limiting Fractional Digits

We can limit the number of decimal places in the formatted result using the setMinimumFractionDigits() and setMaximumFractionDigits(). Zeroes will fill up unused space if the argument digits count is less than the minimum fraction digits. If the digits are greater than the maximum fraction digits then, the argument will be rounded.
import java.text.NumberFormat;
import java.util.Locale;
public class SampleClass{

  public static void main(String[]args){
  
    NumberFormat nf = NumberFormat.getInstance(Locale.US);
    float f1 = 3.3f;
    float f2 = 3.43588f;
    
    nf.setMinimumFractionDigits(3);
    nf.setMinimumFractionDigits(4);
    System.out.println(nf.format(f1));
    System.out.println(nf.format(f2));
  }
}

Result
3.3000
3.43588
Formatting of Percentage, Currency and Rounding Numbers

We can format percentage and currency. Also, we can assign different types of rounding when formatting decimal numbers.
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Currency;
import java.math.RoundingMode;
public class SampleClass{

  public static void main(String[]args){
    float f1 = 0.5f;
    float f2 = 12300.55f;
    
    //use getPercentInstance() get a
    //percentage format
    NumberFormat nf = NumberFormat.
                      getPercentInstance(Locale.US);
    System.out.println(nf.format(f1));
    
    //use getCurrencyInstance() get a
    //currency format
    nf = NumberFormat.
         getCurrencyInstance(Locale.JAPAN);
    System.out.println(nf.format(f2));
    
    //To set new Currency, use setCurrency() method
    //or invoke getCurrencyInstance()
    nf.setCurrency(Currency.getInstance(Locale.US));
    System.out.println(nf.format(f2));
    
    nf = NumberFormat.getNumberInstance(Locale.US);
    nf.setMaximumFractionDigits(1);
    
    //use getRoundingMode() to get the current
    //RoundingMode. Use setRoundingMode to assign
    //new RoundingMode to the NumberFormat.
    System.out.println("Default Rounding Mode: " + 
                       nf.getRoundingMode());
    System.out.println(nf.format(f2));
    
    nf.setRoundingMode(RoundingMode.DOWN);
    System.out.println("New Rounding Mode: " + 
                       nf.getRoundingMode());
    System.out.println(nf.format(f2));
    
    nf.setRoundingMode(RoundingMode.UP);
    System.out.println("New Rounding Mode: " + 
                       nf.getRoundingMode());
    System.out.println(nf.format(f2));
  }
}

Result
50%
?12,301
$12,301
Default Rounding Mode: HALF_EVEN
12,301.5
New Rounding Mode: DOWN
12,301.5
New Rounding Mode: UP
12,301.6
The first result is "50%" 'cause 0.5 multiplied by 100 is equal to 50 or 50%. The second result is "?12,301" 'cause my default charset of my java platform doesn't support japanese characters. If my default charset supports japanese characters then, the result could be this: "¥12,301". Also, notice that the result is rounded off, discarding decimals in the process. The third result is "$12,301" 'cause I used US locale in this formatted result and my default charset supports "$" character.
Note: It's recommended to use BigDecimal When handling currencies.

Read the Currency class documentation to know more about currencies in java. Read the RoundingMode class documentation to know more about rounding modes in java.

Parsing

If we want a string to be converted to a number then, we can use the parse() method.
import java.text.NumberFormat;
import java.util.Locale;
import java.text.ParseException;
public class SampleClass{

  public static void main(String[]args) 
  throws ParseException{
    NumberFormat nf = NumberFormat.
                      getInstance(Locale.ITALY);
    Number num = nf.parse("1.234.567");
    
    System.out.println(num.intValue());
  }
}

Result
1234567
In the example above, we successfully parse all the characters in "1.234.567" string as a number 'cause we use the Locale.ITALY as locale in the formatted result and dot(.) is equivalent to comma(,) in US locale when grouping digits. Try changing the locale to Locale.US and the result will be different.

Read the parse() method description in the NumberFormat class to know more about the parse() method.

DecimalFormat Class

We can use DecimalFormat to format decimal integers and numbers with more control and flexibility. DecimalFormat uses patterns to format decimal numbers. Patterns contain characters, some of them are normal literals, some have special meaning. Here's the pattern syntax of DecimalFormat.
Prefix(opt) Number Suffix(opt)
Prefix and suffix are any Unicode characters except for \uFFFE, \uFFFF, and some special characters.
Number is a number of set of numbers that we wanna format.

Here are some characters that we can use in a pattern for DecimalFormat.

0: represents a digit and sets the minimum integer or decimal digits of a pattern. 0 digit substitutes for absence of digits. Applicable in the "Number" part of the pattern.

#: represents a digit. absence of digits is ignored. Should be placed in the "Number" part of the pattern.

dot(.): Decimal separator or monetary decimal separator. Should be placed in the "Number" part of the pattern.

comma(,): Grouping separator or monetary grouping separator. Should be placed in the "Number" part of the pattern.

semicolon(;): Separates positive and negative patterns

%: Multiply by 100 and show as percentage. Should be placed in "prefix" and "suffix" part of the pattern.

': Used to quote special characters in a prefix or suffix. Should be placed in "prefix" and "suffix" part of the pattern.

More information can be found in the DecimalFormat class documentation.

"#" and "0" characters

Let's create an example to demonstrate DecimalFormat.
import java.text.DecimalFormat;
public class SampleClass{

  public static void main(String[]args){
    String pattern1 = "##,###.##";
    String pattern2 = "00,000.00";
    double decimal = 1234567.567;
    
    DecimalFormat df = new DecimalFormat(pattern1);
    
    System.out.println("Pattern1: " + 
                       df.format(decimal));
                       
    df = new DecimalFormat(pattern2);
    
    System.out.println("Pattern2: " + 
                       df.format(decimal));
  }
}

Result
Pattern1: 1,234,567.57
Pattern2: 1,234,567.57
First off, we use DecimalFormat(String pattern) constructor. This constructor creates a DecimalFormat using the given pattern and the symbols for the default FORMAT locale. This is a convenient way to obtain a DecimalFormat when internationalization is not the main concern. "##,###.##" and "00,000.00" expect 5 decimal integers and 2 decimal numbers.

if the given decimal integer is less than the pattern's expected digits, absence of digits is ignored(if # is used) or replaced by "0" digit(if 0 is used) and numbers are grouped based on the last group in the pattern. Otherwise, numbers are just grouped based on the last group in the pattern.
Example

Pattern: ##,####

Given: 123456789
Result: 1,2345,6789

Given: 12345678
Result: 1234,5678

Given: 1234567
Result: 123,4567

Given: 123456
Result: 12,3456

Pattern: #,###

Given: 1234
Result: 1,234

Given: 12345
Result: 12,345

Given: 123456
Result: 123,456

Given: 1234567
Result: 1,234,567

As we can see from the results above, numbers are grouped based on the last group in the pattern. For example, in "#,###" pattern, the last group is "###" so, if possible, the given digits are grouped into three digits. The given digits above are greater than the Patterns' expected digits. Let's try given digits that are less than the expected digits of patterns.
Example

Pattern: ##,###

Given: 1234
Result: 1,234

Given: 123
Result: 123

Given: 12
Result: 12

Pattern: 00,000

Given: 1234
Result: 01,234

Given: 123
Result: 00,123

Given: 12
Result: 00,012

Pattern: ##,####,###

Given: 123456789
Result: 123,456,789

Given: 12345678
Result: 12,345,678

Given: 1234567
Result: 1,234,567

Given: 123456
Result: 123,456

Pattern: 00,0000,000

Given: 123456789
Result: 123,456,789

Given: 12345678
Result: 012,345,678

Given: 1234567
Result: 001,234,567

Given: 123456
Result: 000,123,456

For the decimal part, If the given decimal number is greater than the pattern's expected digits then, the given decimal numbers are rounded off, removing the unnecessary decimal numbers in the process. Otherwise, absence of digits is ignored(if # is used) or replaced by "0" digit(if 0 is used) and no rounding off will happen. If the pattern doesn't expect decimal numbers but the given digit has decimal numbers, decimal integers will be rounded off, removing the decimal numbers in the process.
Example

Pattern: #,###.##

Given: 1234.556
Result: 1234.56

Given: 1234.55
Result: 1234.55

Given: 1234.5
Result: 1234.5

Given: 1234
Result: 1234

Pattern: 0,000.00

Given: 1234.556
Result: 1234.56

Given: 1234.55
Result: 1234.55

Given: 1234.5
Result: 1234.50

Given: 1234
Result: 1234.00

Pattern: #,###

Given: 1234.56
Result: 1235
Mixing "#" and "0" in a Pattern

Next, we can mix "#" and "0" in a single pattern. Let's create an example.
import java.text.DecimalFormat;
public class SampleClass{

  public static void main(String[]args){
    //Note, don't put "0" before or in-between "#"
    //You will encounter a runtime exception
    //e.g. 0,###,###
    //
    //put "0" after "#". This only applies in the
    //decimal integer part of the pattern. For
    //the decimal number part, don't put "0"
    //after or in-between "#" or you will
    //encounter a runtime exception
    //e.g.
    //#,#00.00# //legal
    //0,#0#.#0 //Illegal
    
    String pattern = "##,000.00#";
    DecimalFormat df = new DecimalFormat(pattern);
    System.out.println(df.format(12345.246));
    System.out.println(df.format(1234.24));
    System.out.println(df.format(123.2));
    System.out.println(df.format(12));
  }
}

Result
12,345.246
1,234.24
123.20
012.00
Subpattern

Next, let's try a pattern with a subpattern.
import java.text.DecimalFormat;
public class SampleClass{

  public static void main(String[]args){
    String pattern1 = "#,000.00;(-0,000.00)";
    String pattern2 = "#,000.00";
    double digits = 12345.56;
    
    DecimalFormat df = new DecimalFormat(pattern1);
    
    System.out.println("p1: " + df.format(digits));
    System.out.println("p1: " + df.format(-digits));
    
    df = new DecimalFormat(pattern2);
    
    System.out.println("p2: " + df.format(digits));
    System.out.println("p2: " + df.format(-digits));
  }
}

Result
p1: 12,345.56
p1: (-12,345.56)
p2: 12,345.56
p2: -12,345.56
A pattern, can have a positive and negative parts. By default, a pattern without a subpattern is a positive pattern. If a pattern without subpattern formats a negative number, a minus sign(-) will be added as a prefix in the formatted result. Otherwise, a subpattern will be used as a format for negative numbers. The "()" in the subpattern above are normal literals.
Note: Explicit subpattern still follows the number of digits, minimal digits, and other characteristics of a positive pattern. The purpose of subpattern is to apply prefix and suffix to negative pattern that are distinct from the positive pattern.
So, "0,000.0;(0)" == "0,000.0;(0,000.0)"

Letters and Other Characters as Prefix/Suffix

We already have seen in the example above that we can add literals as part of DecimalFormat's pattern. We can put letters and other acceptable characters in the pattern as prefixes/suffixes.
import java.text.DecimalFormat;
public class SampleClass{

  public static void main(String[]args){
    String pattern1 = "Postive: |#,000.00|;"+
                      "Negative: (-0,000.00)";
    double digits = 12345.56;
    
    DecimalFormat df = new DecimalFormat(pattern1);
    
    System.out.println(df.format(digits));
    System.out.println(df.format(-digits));
    
  }
}

Result:
Positive: |12,345.56|
Negative: (-12,345.56)
If we misplace prefix/suffix characters and put them in the "Number" part of a pattern, java may make those misplaced characters as suffixes or an exception may be thrown. For example, "#,(##)0" is equivalent to "#,##0()".

Formatting With Percent(%), Single Quote(') and Scientific Notation

Let's try other characters that have special meaning in DecimalFormat.
import java.text.DecimalFormat;
public class SampleClass{

  public static void main(String[]args){
    String pattern1 = "0.00#%";
    String pattern2 = "'#': #,###";
    String pattern3 = "##0.##E0";
    String pattern4 = "000.00E0";
    
    DecimalFormat df = new DecimalFormat(pattern1);
    System.out.println("p1 " + df.format(.009));
    System.out.println();
    
    df = new DecimalFormat(pattern2);
    System.out.println("p2 " + df.format(1955));
    System.out.println();
    
    df = new DecimalFormat(pattern3);
    System.out.println("p3 " + df.format(1234));
    System.out.println("p3 " + df.format(123456));
    System.out.println("p3 " + df.format(0.00123));
    System.out.println("p3 " + df.format(0.001));
    System.out.println();
    
    df = new DecimalFormat(pattern4);
    System.out.println("p4 " + df.format(1234));
    System.out.println("p4 " + df.format(123456));
    System.out.println("p4 " + df.format(0.00123));
    System.out.println("p4 " + df.format(0.001));
    System.out.println();
  }
}

Result
p1 0.90%

p2 #: 1,955

p3 1.234E3
p3 123.46E3
p3 1.23E-3
p3 1E-3

p4 123.40E1
p4 123.46E3
p4 123.00E-5
p4 100.00E-5
So, pattern1 formats 0.009 to percent by multiplying by 100 and the result is 0.90%. Pattern2 uses the single quote('). The purpose of this character is to format special characters of DecimalFormat to literally literals; disregarding their special meanings.

Pattern3 and pattern4 formats numbers to scientific notation. Pattern3 "##0.##E0" formats 1234 to 1.234E3. Let's discuss how java yielded the result. First off, let's examine the pattern "##0.##E0". Let's focus on the decimal integer part "##0".

According to DecimalFormat's documentation: "If the maximum number of integer digits is greater than their minimum number and greater than 1, it forces the exponent to be a multiple of the maximum number of integer digits, and the minimum number of integer digits to be interpreted as 1"

In my intuition, "0" denotes the minimum integer or decimal digits. The number of "#" and "0" in the pattern denotes that maximum integer or decimal digits. So, the pattern "##0.##E0" has a minimum of one and a maximum of 3 decimal integers. For the decimal part, the pattern doesn't have minimum and a has maximum of 2 decimal numbers.

In pattern3, maximum integer digits are graater than minimum integer digits. So, the given digits 1234 is formatted 1.234E3. As you can see, the exponent is multiple of 3 which is the maximum integer digits. We can also see the other results that are formatted by pattern3 have exponents that are multiple of 3.

In patern4, the exponents of the results that are formatted by pattern4 are not the multiple of maximum integer digits because the maximum integer digits are not greater than the minimum. Instead, they are equal. So, the quoted statement above is not applicable to pattern4.

In pattern4, "0" is replaced with with non-zero digits that are supplied by the given digits. If the given digits are not enough to supply the minimum integer or decimal digits, zeroes will be substituted to meet the minimum digit requirement.

Changing Grouping Separator Symbols of Numbers

We can change the decimal and grouping sepators of a DecimaFormat instance by using the DecimalFormatSymbols class.
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
public class SampleClass{

  public static void main(String[]args){
    DecimalFormatSymbols dfs = new DecimalFormatSymbols();
    
    //This method sets new character as a decimal point
    dfs.setDecimalSeparator('|');
    
    //This method sets new character as a grouping
    //separator. In US and some other countries,
    //comma(,) is used as grouping separator.
    dfs.setGroupingSeparator('-');
    
    String pattern = "#,##0.00";
    
    //We use the third form of DecimalFormat's constructor
    DecimalFormat df = new DecimalFormat(pattern, dfs);
    
    String result = df.format(1234567);
    System.out.println(result);
  }
}

Result
1-234-567|00
Check the DecimalFormatSymbols documentation for more information.

Changing DecimalFormat's Locale

We can use NumberFormat's getInstance() or getNumberInstance() method to get a NumberFormat type with a specified locale that we want.
import java.text.NumberFormat;
import java.text.DecimalFormat;
import java.util.Locale;
public class SampleClass{

  public static void main(String[] args){
    NumberFormat nf = NumberFormat.
                      getInstance(Locale.ITALY);
    
    if(nf instanceof DecimalFormat){
      DecimalFormat df = (DecimalFormat)nf;
      
      //This method uses the notation of the
      //default locale of our platform.
      //Though, Symbols like decimal and 
      //grouping separators in the 
      //formatted result are going to be
      //based on a specified locale, if
      //there's one.
      df.applyPattern("#,###,##0.00");
      
      //This method uses the localized notation.
      //In other words, the locale that we 
      //specified when we invoked getInstace()
      //If there's no specified locale, the
      //default locale is used
      //df.applyLocalizedPattern("#.###.##0,00");
      System.out.println(df.format(1234567));
    }
  }
}

Result
1.234.567,00
Note: DecimalFormat inherits the parse() method in NumberFormat so, we can still do parsing in DecimalFormat.

MessageFormat Class

MessageFormat provides a means to produce concatenated messages in a language-neutral way. Unlike other Format classes such as NumberFormat and DecimalFormat, this class doesn't have getInstance factory methods.

A format pattern of MessageFormat consists of String and Format Element. A Format element can be one of these forms:
{ ArgumentIndex }
{ ArgumentIndex , FormatType }
{ ArgumentIndex , FormatType , FormatStyle }
ArgumentIndex is an index that is used for mapping the String to be formatted. FormatType defines the type of the format. FormatStyle defines the styles of the format. Values of FormatType and FormatStyle can be found in the documentation.

Note: MessageFormat differs from the other Format classes in that you create a MessageFormat object with one of its constructors (not with a getInstance style factory method). The factory methods aren't necessary because MessageFormat itself doesn't implement locale specific behavior. Any locale specific behavior is defined by the pattern that you provide as well as the subformats used for inserted arguments.

This means that MessageFormat doesn't have any implementations that handles locale specific behavior. Instead, it delagates the formatting to implementations with locale specific behavior from Format classes. For example, you instantiate a MessageFormat with Locale.US and then format a string using this pattern "{0, number, percent}".

If that's the case then MessageFormat will invoke NumberFormat.getPercentInstance(getLocale()) and delegate the formatting to it.

This example demonstrates MessageFormat.
import java.text.MessageFormat;
import java.util.Date;
import java.util.Locale;

public class SampleClass{

  public static void main(String[] args){
    String message = 
    "My {0} increased in {1, date} {1, time} by "+
    "{2, number, percent}.";
    
    MessageFormat mf = 
    new MessageFormat(message, Locale.US);
    
    System.out.println(mf.format(
    new Object[]{"salary",new Date(), .15}));
  }
}

Result
My salary increased in May 20, 2022 5:12:09 PM by 15%.
In the example above, objects are converted to String objects. MessageFormat(String pattern, Locale locale) constructs a MessageFormat instance with the specified pattern and locale.

format(Object obj) is a method from Format class that formats an object to produce a string with the specified object. This method prefers an Object array as its argument.

If you want to quickly format a pattern without locale, you can use format(String pattern, Object... arguments) static method. For example:
...
String message = 
"My {0} increased in {1, date} {1, time} by "+
"{2, number, percent}.";

System.out.println(MessageFormat.format
(message, "salary", new Date(), .15));
...
We can also use MessageFormat to convert String objects to objects. Take a look at this example.
import java.text.MessageFormat;
import java.text.ParsePosition;
import java.text.ParseException;

public class SampleClass{

  public static void main(String[] args)
                     throws ParseException{
    String pattern = "{0} happened in {1, date} in my {2}.";
    String message = "Olympics happened in May 20, 2012 in my country.";
    
    MessageFormat mf = new MessageFormat(pattern);
    
    Object obj[] = mf.parse(message, new ParsePosition(0));
    
    for(Object o : obj)
      System.out.println(o);
  }
}

Result
Olympics
Sun May 20 00:00:00 CST 2012
country
parse(String source, ParsePosition pos) parses the source starting from the specified ParsePosition. The index that is used by ParsePosition is based on the ArgumentIndex in pair of brackets.

Take note that the message that is going to be parsed must have the characters of the pattern that are not in pair of brackets({}). Otherwise, parse method will return null. More information about this method can be found in this documentation.

Brackets have special meaning in MessageFormat. If we want to denote a bracket as a literal in a pattern, we need to enclose it within single quotes. Take a look at this example.
import java.text.MessageFormat;
import java.util.Locale;

public class SampleClass{

  public static void main(String[] args){
    String message = 
    "''Objects'': '{'{0}, {1}, {2}'}'";
    
    MessageFormat mf = 
    new MessageFormat(message, Locale.US);
    
    System.out.println(mf.format(
    new Object[]
    {"Object1","Object2","Object3"}));
  }
}

Result
'Objects': {Object1, Object2, Object3}
To denote a single quote as a literal in a pattern, use double single quotes just like in the example above.

In the FormatStyle of FormatElement, we can use a pattern that is used by other Format classes. This pattern in the FormatElement is called SubformatPattern. Take a look at this example.
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Date;

public class SampleClass{

  public static void main(String[] args){
    String message = 
    "Date: {0, date, dd-MM-yyyy}\n" +
    "Number: {1, number, #,###,###}";
    
    MessageFormat mf = 
    new MessageFormat(message);
    
    System.out.println(
    mf.format(new Object[]{new Date(), 1234567}));
  }
}

Result
Date: 20-05-2022
Number: 1,234,567
"dd-MM-yyyy" pattern comes from SimpleDateFormat. "#,###,###" pattern comes from DecimalFormat


ChoiceFormat Class

A ChoiceFormat allows you to attach a format to a range of numbers. It is generally used in a MessageFormat for handling plurals. The choice is specified with an ascending list of doubles, where each item specifies a half-open interval up to the next item:
X matches j if and only if limit[j] ≤ X < limit[j+1]
If there is no match, then either the first or last index is used, depending on whether the number (X) is too low or too high. If the limit array is not in ascending order, the results of formatting will be incorrect. ChoiceFormat also accepts \u221E as equivalent to infinity(INF).

This example demonstrates simple usage of ChoiceFormat.
import java.text.ChoiceFormat;

public class SampleClass{

  public static void main(String[] args){
  
    double[] limits = {1, 10, 100, 1000};
    String[] formats = 
    {"Ones", "Tens", 
     "Hundredths", "Thousandths"};
    
    ChoiceFormat cf = 
    new ChoiceFormat(limits, formats);
    
    System.out.println(cf.format
    (Double.NEGATIVE_INFINITY));
    System.out.println(cf.format(1));
    System.out.println(cf.format(10));
    System.out.println(cf.format(100));
    System.out.println(cf.format(150));
    System.out.println(cf.format(160));
    System.out.println(cf.format(1000));
    System.out.println(cf.format
    (Double.POSITIVE_INFINITY));
  }
}

Result
Ones
Ones
Hundredths
Hundredths
Hundredths
Thousandths
Thousandths
ChoiceFormat(double[] limits, String[] formats) constructs with the limits and the corresponding formats. Remember that the length of limits must be the same as the length of formats. Take note that Double.NEGATIVE_INFINITY and Double.POSITIVE_INFINITY are not real numbers.

ChoiceFormat is generally used in conjunction with MessageFormat. Take a look at this example.
import java.text.ChoiceFormat;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.text.Format;
import java.util.Locale;

public class SampleClass{

  public static void main(String[] args){
    double[] limits = {.25, .5, .9};
    String[] formats = 
    {"Low", "Average", "Above Average"};
    
    ChoiceFormat cf = 
    new ChoiceFormat(limits, formats);
    
    MessageFormat mf = 
    new MessageFormat
    ("{0} is {0}.", Locale.US);
    
    Format[] mfFormats = {
    NumberFormat.getPercentInstance(Locale.US), cf};
    mf.setFormats(mfFormats);
    
    System.out.println(mf.format(new Object[]{.25}));
    System.out.println(mf.format(new Object[]{.2}));
    System.out.println(mf.format(new Object[]{.5}));
    System.out.println(mf.format(new Object[]{.9}));
    System.out.println(mf.format(new Object[]{.99}));
  }
}

Result
25% is Low.
20% is Low.
50% is Average.
90% is Above Average.
99% is Above Average.
setFormats(Format[] newFormats) sets the formats to use for the format elements in the pattern of MessageFormat instance. The order of formats in newFormats corresponds to the order of format elements in the pattern string. In the example above NumberFormat.getPercentInstance formats the first "{0}" argument index in the pattern.

Then, cf formats the second "{0}" argument index. More information can be found in the documentation.

We can create a pattern for ChoiceFormat. These are the available symbols that we can use in the pattern.
import java.text.ChoiceFormat;

public class SampleClass{

  public static void main(String[] args){
    ChoiceFormat cf = new ChoiceFormat(
    "-1#is negative| 0#is zero or fraction "+
    "| 1#is one |1.0<is 1+ |2#is two "+
    "|2<is more than 2.");
    
    System.out.println(cf.format(-1));
    System.out.println(cf.format(1.5));
    System.out.println(cf.format(0.9));
    System.out.println(cf.format(1));
    
  }
}

Result
is negative
is 1+
is zero or fraction
is one
Let's examine the pattern and I'm gonna explain the pattern symbols. "|" is an OR symbol. We put this symbol after we're done writing a limit and format. OR symbol functions as an OR operator. "#" and "<" symbols are limiter symbols. "#" is typically used to create a limit. "<" is typically used to create a limit in-between limits created by "#". For example:
import java.text.ChoiceFormat;

public class SampleClass{

  public static void main(String[] args){
    ChoiceFormat cf = new ChoiceFormat(
    "| 1#is one |1.5<is 1+ |2#is two "+
    "|2<is more than 2.");
    
    System.out.println(cf.format(1));
    System.out.println(cf.format(1.4));
    System.out.println(cf.format(1.5));
    System.out.println(cf.format(1.9));
    System.out.println(cf.format(2));
    
  }
}

Result
is one
is one
is one
is 1+
is two
"\u2264" or "≤" symbol is similar to "<" symbol. However, "≤" symbol is inclusive. It means that the specified number after the symbol is included in the limit. For example:
import java.text.ChoiceFormat;

public class SampleClass{

  public static void main(String[] args){
    ChoiceFormat cf = new ChoiceFormat(
    "| 1#is one |1.5\u2264is 1+ |2#is two "+
    "|2<is more than 2.");
    
    System.out.println(cf.format(1));
    System.out.println(cf.format(1.4));
    System.out.println(cf.format(1.5));
    System.out.println(cf.format(1.9));
    System.out.println(cf.format(2));
    
  }
}

Result
is one
is one
is 1+
is 1+
is two
We can also parse a limit number from an input text using parse method. Take a look at this example.
import java.text.ChoiceFormat;
import java.text.ParsePosition;

public class SampleClass{

  public static void main(String[] args){
    ChoiceFormat cf = new ChoiceFormat(
    "| 1#is one |1.5<is 1+ |2#is two "+
    "|2<is more than 2.");
    
    System.out.println(cf.parse(
    "is one ", new ParsePosition(0)));
    
  }
}

Result
1.0
Make sure that the string in the parse method matches the string, excluding the limit number and symbol, in one of the choices in the ChoiceFormat.

Formatting Date&Time

We can format date/time by using DateFormat or SimpleDateFormat class. These two classes have distinct features that we can use in certain situations.

DateFormat Class

DateFormat class is an abstract class that provides methods and fields which help us to format date/time. DateFormat has fields that control the format of the formatted result. These fields are called "Style Patterns".
  • SHORT is completely numeric, such as 12.13.52 or 3:30pm
  • MEDIUM is longer, such as Jan 12, 1952
  • LONG is longer, such as January 12, 1952 or 3:30:32pm
  • FULL is pretty completely specified, such as Tuesday, April 12, 1952 AD or 3:30:42pm PST.
import java.util.Date;
import java.util.Locale;
import java.text.DateFormat;
import java.text.ParseException;
public class SampleClass{

  public static void main(String[]args){
    //This Date constroctor allocates
    //a Date object and initializes it
    //so that it represents the time at
    //which it was allocated, measured
    //to the nearest millisecond
    Date date = new Date();
    
    System.out.println("Date/Time Default Format: "+
                       date.toString());
    
    //getInstance() gets a default date/time
    //formatter that uses the SHORT style for
    //both the date and the time.
    DateFormat df = DateFormat.getInstance();
    System.out.println("getInstance(): " + df.format(date));
    
    //getDateInstance() gets the date formatter
    //with the default formatting style for the
    //default FORMAT locale. 
    df = DateFormat.getDateInstance();
    System.out.println("getDateInstance(): "+
                       df.format(date));
    
    //This form of getDateInstance() gets the time
    //formatter with the given formatting style
    //for the given locale.
    df = DateFormat.getDateInstance(DateFormat.LONG,
                                    Locale.US);
    System.out.println("getDateInstance(): "+
                       df.format(date));
    
    //getTimeInstance() gets the time formatter
    //with the default formatting style for the
    //default FORMAT locale. 
    df = DateFormat.getTimeInstance();
    System.out.println("getTimeInstance(): "+
                       df.format(date));
    
    //getDateTimeInstance() gets the date/time
    //formatter with the default formatting style
    //for the default FORMAT locale.
    df = DateFormat.getDateTimeInstance();
    System.out.println("getDateTimeInstance(): "+
                       df.format(date));
                       
    try{
      df = DateFormat.getDateInstance(DateFormat.LONG,
                                      Locale.US);
      
      //parse() Parses text from the beginning of
      //the given string to produce a date.
      //The method may not use the entire text
      //of the given string due to some reasons
      //like for example, some characters in the
      //given string can't be parsed
      Date pDate = df.parse("September 1, 1994");
      System.out.println(pDate.toString());
    }catch(ParseException e){
      e.printStackTrace();
    }
  }
}

Note
Some parts of the results below are based on the
system's current date/time where
the example is executed
Result
Date/Time Default Format: Thu Jul 29 07:02:31 CST 2021
getInstance(): 7/29/21, 7:02 AM
getDateInstance(): Jul 29, 2021
getDateInstance(): July 29, 2021
getTimeInstance(): 7:02:31 AM
getDateTimeInstance(): Jul 29, 2021, 7:02:31 AM
Thu Sep 01 00:00:00 CST 1994
SimpleDateFormat Class

SimpleDateFormat class is a concrete class that can format date/time by using a pattern. Though, methods like getTimeInstance(), getDateInstance(), or getDateTimeInstance() and parse() can still be used here.
import java.text.SimpleDateFormat;
import java.util.Date;
public class SampleClass{

  public static void main(String[]args){
    String pattern1 = "MM/dd/yyyy hh:mm:ss a";
    String pattern2 = "hh:mm:ss a | MM-dd-yy";
    String pattern3 = "'hh': hh";
    Date date = new Date();
    SimpleDateFormat sdf =
    new SimpleDateFormat(pattern1);
    
    String result = sdf.format(date);
    System.out.println("Pattern1: " + result);
    
    sdf = new SimpleDateFormat(pattern2);
    result = sdf.format(date);
    System.out.println("Pattern2: " + result);
    
    sdf = new SimpleDateFormat(pattern3);
    result = sdf.format(date);
    System.out.println("Pattern3: " + result);
  }
}

Note
the results below are based on the
system's current date/time where
the example is executed
Result
Pattern1: 07/29/2021 11:34:51 AM
Pattern2: 11:34:51 AM | 07-29-21
Pattern3: hh: 11
First off, let's examine pattern1. Some letters have special meanings in SimpleDateFormat's pattern. For example, "M" represents month in year(context sensitive), "m" represents minute in hour, "s" represents second in minute, etc.

We can see in the patterns above that some letters are repeated. We repeat letters in order to set the minimum number that those letters represent. For example, "mm" denotes seconds with minimum of two digits. If the given numberis less than the minimum number, additional zero will be added to meet the minimum number. For example, if the given second is "1" it will be formatted to "01".

Some characters have special formats when repeated. For example, "y", which represents year, has different formats when repeated two or four times. For example, the given year is "2021". "yy" formats "2021" to "21", "yyyy" formats "2021" to "2021".

Another examples are the "z","Z" and "X". "z","zz" and "zzz" formats general timezone to an abbreviation. For example, "z" formats "Central Standard Time" to "CST". "zzzz" formats general time zone to its full name. Any repetitions of "z"'s that are more than four have the same result as "zzzz". "Z" formats timezone using "RFC 822 4-digit time zone format". Any repetitions of "Z"'s that are more than two have the same result as "Z".

"X" formats timezone using "ISO 8601 Time zone format". "X" can be repeated up to 3 times. each repitition increases the characters of the formatted timezone. An exception will be thrown if "X" is repeated more than 4 times.

Letters from A-Z(case insensitive) are reserved for SimpleDateFormat. It means, we can't directly use them as literals in SimpleDateFormat's pattern. Though, there's a way to use letters as literals in the pattern. Use the single quote(') and enclose the string that you wanna be used as literals. Pattern3 demonstrated this concept.

Check the documentation of SimpleDateFormat for more information.

DateTimeFormatter Class

DateTimeFormatter is a class used for formatting date and time in some date-time-related classes in java.time like LocalDate, LocalDateTime, etc. This formatter has predefined constants that contain predefined formats. Additionally, this formatter allows patterns and localized styles.

This topic assumes that readers are knowledgeable about java.time package. If you're not familiar with java.time package, you may wanna read this blogpost I created before reading this topic. This example demonstrates DateTimeFormatter class.
import java.time.*;
import java.time.format.*;
import java.util.Locale;

public class SampleClass{

  public static void main(String[] args){
    LocalDateTime date = 
    LocalDateTime.of(2021, 9, 20, 16, 35, 25);
    
    System.out.println("LocalDate Default Format");
    System.out.println(date);
    System.out.println("With ISO_WEEK_DATE Format");
    System.out.println(
    date.format(DateTimeFormatter.ISO_WEEK_DATE));
    System.out.println();
    
    System.out.println("Using Pattern");
    System.out.println(date.format(
    DateTimeFormatter.
    ofPattern("MM/dd/uu hh:mm:ss", Locale.US)));
    System.out.println();
    
    System.out.println("Using Localized Style");
    DateTimeFormatter dtf = 
    DateTimeFormatter.ofLocalizedDateTime(
    FormatStyle.MEDIUM).withLocale(Locale.US);
    System.out.println("Style #1: " + date.format(dtf));
    
    dtf = DateTimeFormatter.ofLocalizedDateTime(
    FormatStyle.LONG, FormatStyle.MEDIUM).
    withLocale(Locale.US);
    System.out.println("Style #2: " + 
    date.format(dtf));
    
  }
}

Result
LocalDate Default Format
2020-09-20T16:35:25
With ISO_WEEK_DATE Format
2020-W38-1

Using Pattern
09/20/21 04:35:25

Using Localized Style
Style #1: Sep 20, 2021, 4:35:25 PM
Style #2: September 20, 2021, 4:35:25 PM
First off, format method can be found in some date-time-related classes in java.time like LocalDate, LocalDateTime, etc. format method returns a formatted text of date and time information of a date-time-related class.

DateTimeFormatter has predefined constants that we can use. Most of these constants are used by some date-time-related classes. For example, LocalDateTime.toString uses DateTimeFormatter.ISO_LOCAL_DATE_TIME as its default formatter. More information about constant formatters can be found in the documentation.

DateTimeFormatter can use patterns as formatters. There are characters that have special meaning in a pattern like 'm', 'h', 'M', etc. DateTimeFormater.ofPattern is used to create a formatter via pattern. Some characters have the same meaning but different presentation.

For example, "M" and "L" characters are the symbols for month-of-year. However, "M" presents month-of-year as a number e.g. 7 or 07. "L" presents month-of-year as a text e.g. Jul or July. More information about pattern and characters with special meaning can be found in the documentation.

Also, don't be confused with year and year-of-era. year-of-era represents the concept of the year within the era. There are two eras: BC(Before Christ) or BCE(Before Common Era) and AD(Anno Domini) or CE(Common Era).

CE starts at 1 and moves forward as moving forward to the present time. BCE starts at 1 and moves backward as moving forward to the past time. year, starting at 1, is equivalent to years in CE. Year 0 is equal to 1 BCE. Proleptic year is commonly used when handling standard dates. year-of-era is commonly used in historical timeline.

methods with 'ofLocalized' prefix returns a locale specific date format for the ISO chronology. In the example above I used two overloaded forms of ofLocalizedDateTime:

ofLocalizedDate(FormatStyle dateStyle)
ofLocalizedDateTime(FormatStyle dateStyle, FormatStyle timeStyle)

The first form uses dateStyle to format both date and time. The second form uses dateStyle to format date and timeStyle to format time. FormatStyle is an enumeration of the style of a localized date, time or date-time formatter.

withLocale returns a DateTimeFormatter instance with the given locale. Note that the localized pattern is looked up lazily. In the example above, DateTimeFormatter holds FormatStyle given by method with 'ofLocalized' prefix and the locale given by withLocale.

Actual formatting happens if the formatted text is requested. If withLocale is not invoked, your system's default locale is used. If the locale you're using has Unicode Extensions, you may wanna use localizedBy().

Take note that format method may throw an exception if the given FormatStyle can't find necessary information it needs. For example, FormatStyle.FULL may throw an exception when used as formatter in LocalDateTime because FormatStyle.FULL may look for offset and zone id which LocalDateTime don't have.

Parsing

Next, we can use DateTimeFormatter to parse text based on its formatter. This example demonstrates DateTimeFormatter.parse and its overloaded forms.
import java.text.ParsePosition;
import java.time.*;
import java.time.format.*;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalQueries;
import java.util.Locale;

public class SampleClass{

  public static void main(String[] args){
    
    TemporalAccessor ta = DateTimeFormatter.
    ofPattern("MM/dd/uu hh:mm:ss", Locale.US).
    parse("09/20/21 04:35:25");
    
    if(ta.isSupported(ChronoField.DAY_OF_MONTH))
      System.out.println("Day of month: "+
      ta.get(ChronoField.DAY_OF_MONTH));
    
    ta = DateTimeFormatter.
    ofPattern("hh:mm:ss", Locale.US).
    parse("09/20/21 04:35:25", new ParsePosition(9));
    
    System.out.println("Hour of AM/PM: " + 
    ta.get(ChronoField.HOUR_OF_AMPM));
    System.out.println("Minutes of hour: " + 
    ta.get(ChronoField.MINUTE_OF_HOUR));
    System.out.println("Seconds of minute: " + 
    ta.get(ChronoField.SECOND_OF_MINUTE));
    
    LocalDate ld = DateTimeFormatter.
    ofPattern("MM/dd/uu hh:mm:ss", Locale.US).
    parse("09/20/21 04:35:25", 
          TemporalQueries.localDate());
    System.out.println("LocalDate: " + ld);
  }
}

Result
Day of month: 20
Hour of AM/PM: 4
Minutes of hour: 35
Seconds of minute: 25
LocalDate: 2021-09-20
parse has three overloaded forms:
parse(CharSequence text)
parse(CharSequence text, ParsePosition position)
parse(CharSequence text, TemporalQuery<T> query)

The first form has a text parameter that contains the text to be parsed. This form returns a TemporalAccessor instance.

isSupported returns true if a TemporalField instance is supported by the instance that calls this method. Otherwise, returns false. ChronoField implements TemporalField thus its constant fields are compatible with TemporalField.

The second form has a text parameter that contains the text to be parsed and position parameter that contains the position or index of where the parsing starts. ParsePosition is a simple class used by Format and its subclasses to keep track of the current position during parsing.

The third form has a text parameter that contains the text to be parsed and a query that contains a query command. TemporalQuery is a functional interface used for querying a temporal object. Queries are a key tool for extracting information from temporal objects. TemporalQueries provides common implementations of TemporalQuery.

Resolving

Parsing is implemented as a two-phase operation. First, the text is parsed using the layout defined by the formatter, producing a Map of field to value, a ZoneId and a Chronology. Second, the parsed data is resolved, by validating, combining and simplifying the various fields into more useful ones. More information can be found in the documentation.

In this topic, we're gonna be discussing withResolverFields and withResolverStyle methods. This example demonstrates the two methods.
import java.time.*;
import java.time.format.*;
import java.time.temporal.*;

public class SampleClass{

  public static void main(String[] args){
    TemporalAccessor ta = DateTimeFormatter.
    ofPattern("MM/dd/uu DDD", Locale.US).
    withResolverFields(ChronoField.YEAR, 
                       ChronoField.DAY_OF_YEAR).
    withResolverStyle(ResolverStyle.LENIENT).
    parse("09/20/21 489");
    
    System.out.println(ta);
  }
}

Result
{}, ISO resolved to 2022-05-04
withResolverFields acts as a filter between phase 1 and phase 2 of parsing. The fields in withResolverFields are the fields that are going to phase 2. If the fields are possible to be resolved to a date, the new date will form. In the example above, the year(2021) and day-of-year(489) are extracted, resolved and form the new date(2022-05-04).

This is valid because in java, a combination of YEAR and DAY_OF_YEAR can be resolved and consequently produces a date. More information on how java resolves fields can be found in this documentation.

withResolverStyle sets a new resolve style. There are three resolver styles:

LENIENT
SMART
STRICT

These constants can be found in the ResolverStyle enum. Lenient mode is the least strict mode of the resolver styles. It accepts out-of-range values. For example, lenient mode allows the month in the ISO calendar system to be outside the range 1 to 12. For example, month 15 is treated as being 3 months after month 12.

Strict mode is the most strict mode of the resolver styles. all field values must not be out-of-range, otherwise, it will be treated as invalid value. For example, month 15 is invalid because it's outside of the range 1 to 12. In the example above, day-of-year is outside the range 1 to 365 in standard years and 1 to 366 in leap years. However, the resolver mode is lenient that's why 489 is still a valid value. Try changing the resolver mode to strict mode and you will encounter an exception.

Smart mode is a mix between lenient and strict mode with some modifications. Some fields may accept out-of-range values, some may not and the resolving process of this mode may be different from lenient and strict modes. For example, let's parse 2019-03-32 using the resolver modes. In lenient mode, the result is 2019-04-01. As you can see the month moved to 4 and the day is set to 1.

In strict mode, an exception is thrown because 32 is outside the range 1 to 31 of month of march of ISO calendar. In smart mode, the result is 2019-03-01. As you can see, the month didn't move and the day is reset to 1. Take note the the smart mode is the default mode of DateTimeFormatter class.

DateTimeFormatterBuilder

DateTimeFormatterBuilder allows a DateTimeFormatter to be created. All DateTimeFormatter are created ultimately using this builder. This example demonstrates some methods of DateTimeFormatterBuilder.
import java.time.*;
import java.time.format.*;
import java.time.temporal.ChronoField;

public class SampleClass{

  public static void main(String[] args){
    DateTimeFormatterBuilder builder =
    new DateTimeFormatterBuilder();
    
    DateTimeFormatter formatter = 
    builder.
    optionalStart().
    appendLiteral("Date: ").
    optionalEnd().
    appendValue(ChronoField.YEAR).
    appendLiteral("/").
    appendValue(ChronoField.MONTH_OF_YEAR).
    appendLiteral("/").
    appendValue(ChronoField.DAY_OF_MONTH, 2).
    parseStrict().
    toFormatter(java.util.Locale.US);
    
    LocalDate ld = LocalDate.of(2020, 4, 5);
    
    System.out.println("Formatting");
    System.out.println(ld.format(formatter));
    System.out.println();
    
    System.out.println("Parsing");
    ld = LocalDate.parse("2015/10/02", 
                         formatter);
    System.out.println("Date: " + ld);
  }
}

Result
Formatting
Date: 2020/4/05

Parsing
Date: 2015-10-02
appendLiteral(String literal) appends plain text to the formatter. appendValue(TemporalField field) appends the value of a date-time field to the formatter using a normal output style.

appendValue(TemporalField field, int width) appends the value of a date-time field to the formatter with fixed width. If the value of the date-time field is less than the fixed width, zeroes will be added to compensate.

parseStrict changes the parse style to strict mode for the remainder of the formatter. optionalStart marks the start of an optional section and optionalEnd ends the optional section. All elements in the optional section are treated as optional that may affect formatting or parsing.

In the example above, the literal "Date: " without quotes will be output during formatting. During parsing, the literal "Date: " without quotes doesn't need to be an input. Another example, consider this builder setup:

optionalStart().appendValue(MINUTE_OF_HOUR,2).optionalEnd()

During formatting, the minute will only be output if its value can be obtained from the date-time. During parsing, the input will be successfully parsed whether the minute is present or not.

toFormatter(Locale locale) completes this builder by creating the DateTimeFormatter using the specified locale. Let's try another example.
import java.time.*;
import java.time.format.*;
import java.time.temporal.ChronoField;

public class SampleClass{

  public static void main(String[] args){
    DateTimeFormatterBuilder builder =
    new DateTimeFormatterBuilder();
    
    DateTimeFormatter formatter =
    builder.
    appendPattern("[yyyy][yyyyMMdd]").
    optionalStart().
    parseDefaulting(
      ChronoField.MONTH_OF_YEAR, 1).
    parseDefaulting(
      ChronoField.DAY_OF_MONTH, 1).
    optionalEnd().
    toFormatter(java.util.Locale.US);
    
    System.out.println("Parsing");
    LocalDate ld = LocalDate.parse(
                   "2015", 
                    formatter);
    System.out.println("Date: " + ld);
    
  }
}

Parsing
Date: 2015-01-01
parseDefaulting appends a default value for a field to the formatter for use in parsing. As the name implies, this method affects the parsing process of the formatter. In the example above, we only put year in the parse method. Because there are no day and month in the parse method, the formatter uses the values assigned in the parseDefaulting method.

appendPattern method appends the elements defined by the specified pattern to the builder. The "[" and "]" characters are similar to optionalStart and optionalEnd methods.

This pattern "[yyyy][yyyyMMdd]" accepts four-digit year or four-digit year, two-digit month and two-digit day. More details about reserved characters for date-time patterns can be found in this documentation.

Let's do one more example.
import java.time.*;
import java.time.format.*;
import java.time.temporal.ChronoField;

public class SampleClass{

  public static void main(String[] args){
    DateTimeFormatterBuilder builder =
    new DateTimeFormatterBuilder();
    
    DateTimeFormatter formatter =
    builder.
    appendText(
      ChronoField.MONTH_OF_YEAR,
      TextStyle.SHORT_STANDALONE).
    appendLiteral(" ").
    appendValue(ChronoField.DAY_OF_MONTH).
    appendLiteral(", ").
    appendValueReduced(ChronoField.YEAR, 
                       2, 2, 
                       LocalDate.of(1940,1,1)).
    toFormatter(java.util.Locale.US);
    
    LocalDate ld = LocalDate.of(1990, 5, 10);
    System.out.println("Formatting");
    System.out.println(ld.format(formatter));
    System.out.println();
    
    System.out.println("Parsing");
    ld = LocalDate.parse("Feb 20, 90", 
                         formatter);
    System.out.println(ld);
  }
}

Result

Formatting
May 10, 90

Parsing
1990-02-20
appendText(TemporalField field, TextStyle textStyle) appends the text of a date-time field to the formatter. TextStyle is an enumeration of the style of text formatting and parsing.

appendValueReduced appends the reduced value of a date-time field to the formatter. This method is typically used for formatting and parsing a two-digit year. When formatting or parsing a year such as YEAR or YEAR_OR_ERA, It's recommended to use this form:
appendValueReduced(TemporalField field, int width, int maxWidth, ChronoLocalDate baseDate)
For other fields, use this form:
appendValueReduced(TemporalField field, int width, int maxWidth, int baseValue)

field is a date-time field that we wanna format or parse. width and maxWidth determines the number of characters to be parsed or formatted. baseDate or baseValue determines the range of where to get the full value of reduced value.

In my opinion, to get the number of digits of full value of reduced value with two or more digits, full value digits should be two digits higher than the reduced value digits. For example, the number of digits of full value of reduced value with two digits is 4 digits. To get the full value range, add 9*10 to the given full value if the reduced value has two digits, 9*100 is the reduced value has three digits and so on.

For example, the given full value is 1940 and the reduced value is two digits such as 40. Add 9*10 to 1940 and we get 2030. Thus, full value range starts from 1940 to 2030. In the example above, appendValueReduced only use the YEAR field. Fields like MONT_OF_YEAR and DAY_OF_MONTH in LocalDate are not used.

During formatting, width and maxWidth determines the number of characters to format. If they are equal then the format is fixed width. If the value of the given field is within the full value range then the reduced value is formatted. If the value of the given field is out-of-range and number of digits is greater than maxWidth, the value of the given field is truncated to fit maxWidth. The rightmost characters are output to match the value of the ginve field, left padding with zero.

Examples:
#1: Given field value is out-of-range and number of digits is greater than maxWidth.
...
appendValueReduced(ChronoField.YEAR, 
                   2, 2, 
                   LocalDate.of(1940,1,1))
...
LocalDate ld = LocalDate.of(2040, 5, 10);
System.out.println(ld.format(formatter));

Result: May 10, 40

...
appendValueReduced(ChronoField.YEAR, 
                   2, 2, 
                   LocalDate.of(1940,1,1))
...
LocalDate ld = LocalDate.of(1910, 5, 10);
System.out.println(ld.format(formatter));

Result: May 10, 10
#2: Given field value is within full value range.
...
appendValueReduced(ChronoField.YEAR, 
                   2, 2, 
                   LocalDate.of(1940,1,1))
...
LocalDate ld = LocalDate.of(2020, 5, 10);
System.out.println(ld.format(formatter));

Result: May 10, 20
#3: The rightmost characters are output to match the value of the ginve field, left padding with zero.
...
appendValueReduced(ChronoField.YEAR, 
                   2, 2, 
                   LocalDate.of(1940,1,1))
...
LocalDate ld = LocalDate.of(9, 5, 10);
System.out.println(ld.format(formatter));

Result: May 10, 09
#4: Given field value is out-of-range and number of digits is equal or less than maxWidth.
...
appendValueReduced(ChronoField.YEAR, 
                   2, 4, 
                   LocalDate.of(1940,1,1))
...
LocalDate ld = LocalDate.of(1900, 5, 10);
System.out.println(ld.format(formatter));

Result: May 10, 1900
During parsing, the created DateTimeFormatter gets the value; check if the value is within width and maxWidth; and check if the value has a full value match in the full value range.

Examples:
#1: parse reduced year value to full year value.
...
appendValueReduced(ChronoField.YEAR, 
                   2, 2, 
                   LocalDate.of(1940,1,1))
...
LocalDate ld = LocalDate.
               parse("Feb 20, 90", 
                     formatter);
System.out.println(ld);

Result: 1990-02-20
#2: given value can't be matched with full value range.
...
appendValueReduced(ChronoField.YEAR, 
                   2, 4, 
                   LocalDate.of(1940,1,1))
...
LocalDate ld = LocalDate.
               parse("Feb 20, 1900", 
                     formatter);
System.out.println(ld);

Result: 1900-02-20

Tuesday, July 20, 2021

Java Tutorial: Scanner Class

Chapters

Scanner Class

Scanner class is a simple text scanner that can parse primitive types and string. In other words, scanner can break down primitive types and string into multiple parts called tokens. By default, scanner uses whitespace characters like "\n", "\t" and the good ol' whitespace(" ") as delimiter to separate primitive types and string.

Tokens in this topic means, parts of the input that are separated by delimiter.

Delimiter is a sequence of one or more characters for specifying the boundary between separate, independent regions in plain text, mathematical expressions or other data streams.

Check out Character.isWhiteSpace() to know the different kinds of whitespaces. Scanner can scan inputstreams, String or a text file. Let's use Scanner with Strings.

Scan String Input Using Scanner Class
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      scanner = new Scanner("A Big Brown Fox");
    
      //hasNext() returns true if the
      //scanner has another token in
      //its input
      while(scanner.hasNext()){
      
        //next() returns the next token
        //in this scanner as a string type.
        //To understand the function of this
        //method, imagine the tokens are
        //stored in an array, and next()
        //iterates through that array
        //until it reaches the last index
        //of the array.
        System.out.println(scanner.next());
      }
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Result
A
Big
Brown
Fox
Methods Similar To next()

If we have numbers as input, we can use the nextInt() if the numbers are integer, nextFloat() if the numbers are floating-point, etc.
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
    
      scanner = new Scanner("10 100 1000 2000 50000");
     
      //hasNextInt() checks if the current token
      //matches an integer type
      //
      //nextInt() returns the current token as
      //integer type
      while(scanner.hasNextInt())
        System.out.println(scanner.nextInt());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Result
10
100
1000
2000
50000
Note: If we use nextXXX(); methods like nextInt() and a token doesn't match the type that the method is expecting, java will throw InputMismatchException.
import java.util.Scanner;
import java.util.InputMismatchException;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      scanner = new Scanner("10.50");
      while(scanner.hasNext())
        System.out.println(scanner.nextInt());
    }
    catch(InputMismatchException e){
      System.out.println("Token is not an"+
                         " integer type!");
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Result
Token is not an integer type!
Scanner can also read other number formats that are locale specific like "10,000". "Numbers with commas" format is supported by US locale. So, we need to set the locale of our scanner to US locale.
import java.util.Scanner;
import java.util.Locale;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      scanner = new Scanner("1,000,000");
      scanner.useLocale(Locale.US);
      
      while(scanner.hasNextInt())
        System.out.println(scanner.nextInt());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }

  }
}

Result
1000000
If we don't specify a locale then, the scanner uses the default locale of JVM. To get the default locale of our JVM, use the getDefault() static method of Locale class. Try changing the locale in the example to Locale.FRENCH and the scanner won't recognize "1,000,000" as an integer type.

Check out the Scanner class if you want to know more variations of the next() method in the Scanner class.

useDelimiter() Method

We can use useDelimiter() method to change the scanner's delimiter.
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      scanner = new Scanner("Tomato, Potato, Falsetto");
      
      //useDelimiter changes scanner's delimiter
      //to the specified string
      scanner.useDelimiter(", ");
      
      while(scanner.hasNext())
        System.out.println(scanner.next());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Result

Tomato
Potato
Falsetto
If we are knowledgeable about Regular Expressions then, we can use a pattern as a delimiter in our scanner.
import java.util.Scanner;
import java.util.regex.Pattern;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      scanner = new Scanner("-Tomato-0-Potato-1-Falsetto-");
      
      //useDelimiter changes scanner's delimiter
      //to the specified pattern
      scanner.useDelimiter(Pattern.compile("\\d"));
      
      while(scanner.hasNext())
        System.out.println(scanner.next());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Result
-Tomato-
-Potato-
-Falsetto-
Scan File Using Scanner Class

We can use the Scanner class to read files like text files.
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
public class SampleClass{

  public static void main(String[]args) throws IOException{
    Scanner scanner = null;
    
    try{
      //test.txt must exist in the folder where
      //this file is running
      scanner = new Scanner(new File("test.txt"));
      
      while(scanner.hasNext())
        System.out.println(scanner.next());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Assume the words are written in test.txt
like this: Big Brown Fox

Result
Big
Brown
Fox
Scan Console Input Using Scanner Class

We can put System.in of System class as argument in a scanner. Though, there's a problem. Take a look at this example.
Note: This example will ask for input forever. This program needs to be terminated forcefully. If you're using CMD on windows, just close the console or press CTRL+C to terminate the program.
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      System.out.println("Enter Input: ");
      scanner = new Scanner(System.in);
      
      while(scanner.hasNext())
        System.out.println(scanner.next());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
     //closing the scanner also closes the stream
     //that is connected to the scanner. Always
     //close streams if they're not gonna be used
     //anymore to free up resources
     scanner.close();
    }
    
  }
}
First off, System.in is the "standard" input stream. This stream is already open and ready to supply input data. Typically this stream corresponds to keyboard input or another input source specified by the host environment or user.

The example above asks for input forever no matter how many inputs you type. The reason why the example is not stopping to ask for input is because System.in is a continuous stream. Thus, it doesn't give an EOL or End Of Line. So, the next() method can't verify if the stream ends. For example, we type "A B", after the program displays "B", next() still waits for an input 'cause it can't find an EOL.

There's a solution to bypass this problem. Since we're only inputting one line on the console, we can use the nextLine() method in the Scanner class.
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      System.out.println("Enter Input: ");
      scanner = new Scanner(System.in);
      
      if(scanner.hasNextLine())
        System.out.println(scanner.nextLine());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Input: Big Brown Fox
Result
Big Brown Fox
Notice that we use if statement when checking if the scanner has a line of strings. We use if statement and not loop statement to avoid the never ending input loop. Since we only use one line when typing an input on the console, it's alright if we just get a single line.

The problem here is that "Big Brown Fox" is not broken down into three parts 'cause we use nextLine() which skips an entire line. Well, we can use the split() method in the String class to break down the string into multiple parts.
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    String str = "";
    
    try{
      System.out.println("Enter Input: ");
      scanner = new Scanner(System.in);
      
      if(scanner.hasNextLine())
        str = scanner.nextLine();
        
      String[] tokens = str.split(" ");
      
      for(String s : tokens)
        System.out.println(s);
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Input: Big Brown Fox
Result
Big
Brown
Fox
split(String regex) method in string splits strings based on a pattern which is a regular expression. That's right, the string that we put in this method as an argument is a regular expression.

There are characters that have special meanings in regular expression so, some characters that we put in split() might not behave as normal literal like the dot(.) character. If you wanna learn regular expressions then, visit my "Regular Expressions" blogpost.

Monday, July 19, 2021

Java Tutorial: Exploring The String Class

Chapters

Java String

String in java represents a set of characters that acts as a form of text. All string literals that are defined in a program are instances of the String class. So, new String("abc") is a string object and "abc" is also a string object.

String Immutability

Strings are immutable. Immutability means fixed or unmodifiable. Everytime we create a string object, its value can't be modified. So, s1 = "abc" and s1 = "bcd" are different strings. Once we assign a different string literal to s1, the current string literal("abc") that is assigned to s1 will be unreferenced and the new string literal("bcd") will be referenced to s1. In other words, we didn't modify "abc", we just replaced it with "bcd".

There are classes that support mutable string. StringBuffer and StringBuilder support mutable string.

String Constant Pool

String Constant Pool(SCP) is a container in java heap memory where some string objects are placed in order to optimize string usage in a program. Take a look at this example.
References:
15.28. Constant Expressions
When will a string be garbage collected in java

public class SampleClass{

  public static void main(String[]args){
    String s1 = new String("Hello");
    String s2 = "Hello";
    String s3 = "Hello";
    
    //Remember, == is different from equals()
    //
    //==, when is used in string objects,
    //compares strings' memory 
    //addresses
    //
    //whereas equals() compares
    //objects' string representation
    System.out.println(s1 == s2);
    System.out.println(s2 == s3);
    
    //concatenation that is evaluated
    //as compile time constant can be
    //automatically interned
    //
    //"Hello" and 123 are both literals
    //and they're going to be assigned
    //in a string variable. So,
    //"Hello"+123 is evaluated as
    //compile time constant
    String s4 = "Hello"+123;
    System.out.println(s4 == "Hello123");
    
    int i = 35;
    //In this code, we include a non-final
    //variable which is not a constant
    //variable.
    //Thus, the expression below is not
    //evaluated as compile time constant
    //
    //When we include variables in an
    //expression and we want that 
    //expression to be evaluated as
    //a string contant then, the
    //variables must be constant also.
    //
    //make "i" final and the println()
    //below is going to display true
    //on the console
    String s5 = "Hello" + i + "Hi";
    System.out.println(s5 == "Hello35Hi");
  }
}

Result
false
true
true
false
The first comparison is false because "Hello" in s1 is the string reference in the heap whereas "Hello" in s2 is the string reference in the String Constant Pool. The second comparison is true because the string reference in s3 is copied from SCP. The thrid comparison is true 'cause the expression "Hello"+123 is a compile time constant. The fourth comparison is false 'cause the expression "Hello" + i + "Hi" is not a compile time constant.

String literals, as long as they're not yet registered in the SCP and they're considered as compile time constants, are placed in the SCP. Also, when we use this overloaded constructor of String: public String(String original)
It creates a string in the SCP and then creates an explicit copy into the heap, as long as the "original" is not yet registered in the SCP and considered as compile time constant. Otherwise, the constructor will only create the string object in the heap. The constructor returns the reference of the string object in the heap.

Note: Garbage Collector may collect unreferenced string instance in SCP.
Note: Prior to Java 6, String constant pool was located in Permgen Area(renamed to metaspace in Java 8) and Since Java 7, String constant pool is relocated to heap.

String Interning

There are times that we may want to get a string reference in the SCP or put a string instance in the SCP that is not yet there. Well, we can do that by using the intern() method.
public class SampleClass{

  public static void main(String[]args){
    String s1 = new String("Hello");
    String s2 = "Hello";
    String s3 = s1;
    String s3a = s1.intern();
    
    System.out.println(s1 == s2);
    System.out.println(s2 == s3);
    System.out.println(s2 == s3a);
    
    char[] c = new char[]{'H','i'};
    String s4 = new String(c);
    String s5 = s4.intern();
    
    System.out.println(s4 == s5);
    String s6 = "Hi";
    System.out.println(s5 == s6);
  }
}

Result
false
false
true
false
true
The first comparison is false 'cause "Hello" in s1 is in the heap whereas "Hello" in s2 is in the SCP. The second comparison is still false 'cause "Hello" in s3 is the reference of "Hello" in s1. The third comparison is true 'cause "Hello" in s3a is the reference of "Hello" in SCP which is also the reference of "Hello" in s2.

As we can see in the example above, if we intern a string and that string is equal to one of the strings in the SCP, intern() returns the reference of that one string instead of adding a copy of the interned string in the SCP. That's why the thrid comparison is true.

Now, let's evaluate the 4th and 5th comparison. The fourth comparison is false 'cause "Hi" in s4 is in the heap whereas "Hi" in s5 is in the SCP. Fifth comparison is true 'cause "Hi" in s6 and s5 are in the SCP.

As we can see in the example above, if the interned string doesn't have a copy in the SCP, intern() creates a copy of the interned string to SCP and returns the reference of that copy. That's why the fourth comparison is false.

Demonstrating Some String Constructors

String class has many overloaded constructors that we can use. I will demonstrate some of them here. To check out all overloaded constructors of String class, go visit the String Class Documentation.
public class SampleClass{

  public static void main(String[]args){
    
    //String(String original)
    String s1 = new String("String1");
    
    char c[] = new char[]{'H','i'};
    //String(char[] value)
    String s2 = new String(c);
    
    //String(byte[] bytes)
    //constructs a new String by decoding the
    //specified array of bytes using the platform's
    //default charset.
    String s3 = new String(new byte[]{50,80,90,75});
                           
    System.out.println(s1);
    System.out.println(s2);
    System.out.println(s3);
  }
}

Result
String1
Hi
2PZK
Demonstrating Some String Methods

String class has many methods that we can use. I will demonstrate some of them here. To check out all methods of String class, go visit the String Class Documentation.
public class SampleClass{

  public static void main(String[]args){
    String s1 = "Hello";
    
    //charAt(int index)
    //Returns the char value at the specified index.
    System.out.println(s1.charAt(1));
    
    //contains(CharSequence s)
    //Returns true if and only if this string
    //contains the specified sequence of char
    //values.
    System.out.println(s1.contains("Hi"));
    
    //equals(Object anObject)
    //compares this string to the specified
    //object.
    System.out.println(s1.equals("Hello"));
    
    //equalsIgnoreCase(String anotherString)
    //compares this String to another String,
    //ignoring case considerations.
    System.out.println(s1.equalsIgnoreCase("hello"));
    
    //lastIndexOf(int ch)
    //returns the index within this string of the
    //last occurrence of the specified character.
    //The result is 3 'cause the last occurence
    //of 'l' is in index 3
    System.out.println(s1.lastIndexOf('l'));
    
    //indexOf(int ch)
    //returns the index within this string
    //of the first occurrence of the specified
    //character.
    //The result is 2 'cause the first 
    //occurence of 'l' is in index 2
    System.out.println(s1.indexOf('l'));
    
    String s2 = "Hello, Hello";
    
    //lastIndexOf(String str)
    //returns the index within this string of
    //the last occurrence of the specified
    //substring.
    //
    //The result is 7 'cause the last occurence
    //of "Hell" starts at index 7
    System.out.println(s2.lastIndexOf("Hell"));
    
    //length()
    //returns string length
    System.out.println(s2.length());
    
    //toLowerCase()
    //Converts all of the characters in this
    //String to lower case using the rules of
    //the default locale.
    System.out.println(s2.toLowerCase());
    
    //toUpperCase()
    //Converts all of the characters in this
    //String to upper case using the rules of
    //the default locale.
    System.out.println(s2.toUpperCase());
    
    //startsWith(String prefix)
    //Tests if this string starts with the
    //specified prefix.
    System.out.println("startsWith: " + s2.startsWith("Hell"));
    
    //endsWith(String suffix)
    //Tests if this string ends with the
    //specified suffix.
    System.out.println("endsWith: " + s2.endsWith("Hello"));
    
    String s3 = "Hello Everyone!";
    
    //substring(int beginIndex)
    //Returns a string that is a substring
    //of this string.
    //
    //Parameters:
    //beginIndex - the beginning index, inclusive.
    System.out.println(s3.substring(6));
    
    //substring(int beginIndex, int endIndex)
    //Returns a string that is a substring
    //of this string.
    //
    //Parameters:
    //beginIndex - the beginning index, inclusive.
    //endIndex - the ending index, exclusive.
    //
    //inclusive means included in the result.
    //Therefore, the character in the beginIndex
    //is included in the result
    //
    //exclusive means excluded in the result
    //Therefore, the character in the endIndex
    //is excluded in the result
    System.out.println(s3.substring(6,11));
    
    //isEmpty()
    //Returns true if, and only if, length() is 0.
    String s5 = "\n";
    System.out.println("isEmpty? " + s5.isEmpty());
    
    //static String valueOf(int i)
    //Returns the string representation
    //of the int argument.
    //Check the documentation for more
    //valueOf() forms
    String s6 = String.valueOf(100);
    System.out.println(s6);
  }
}

Result
e
false
true
true
3
2
7
12
hello, hello
HELLO, HELLO
startsWith: true
endsWith: true
Everyone!
Every
isEmpty? false
100

Saturday, July 17, 2021

Java Tutorial: Multithreading

Chapters

Java Multithreading

Multithreading is the process of executing multiple threads. A thread is a small, lightweight unit of a process that allows to achieve multitasking. Each thread has a separate path of execution, so, each thread is independent to one other. Thus, unless threads don't share resources, execution of one thread doesn't affect the execution of another. So, for example, if one thread encounters an exception, other threads won't be affected by that exception, only the thread that encountered that exception would be affected.

What is a process? A process is often referred to as programs in execution(or sometimes a task) and each process can have multiple threads. The text editor that we are using when writing java codes is an example of process. Process is processed by a processor(CPU). Processor is a broad term that is used as a term for CPU and core.

CPU or central processing unit is a computer processor that is attached to a motherboard. A core is a processor in a CPU that processes task(thread). Task is a unit of work that can be used as a term for a process or a thread.

So, What's the difference between thread and process? Processes are heavyweight whereas threads are lightweight. A process is comprised of multiple threads whereas a thread is a subset of process. processes have its own address space in memory whereas threads use their process' address space and share it with one another.

Don't be confuse between concurrency and multithreading. Concurrency involves parallelism whereas thread is the use of multiple threads. Operating systems schedule threads in every processor. This scheduling is called context switch.

Parallelism consists of two threads that run in parallel(at the same time). To achieve parallelism, we very likely need to have two or more processors. A single processor can handle multiple threads. In a single processor that handles multiple threads, context switching is implemented.

Context switch gives every thread a slice of time to run until all of their tasks are complete or they're all dead. Context switch gives an illusion of parallelism. That's why a computer that only has a single processor can still do multiple tasks.

Creating Thread

There are two ways to create a thread: by extending the Thread class or implementing the Runnable interface. Each method has its own merits. Once we create a thread, we can start it by invoking the start() method of the Thread class.

Implementing Runnable Interface

If we want to create a simple thread then, we just need to create a class that implements a Runnable interface and overrides run() method.
public class SampleClass{

  public static void main(String[]args){
    System.out.println("Main Thread Started!");
    Thread t = new Thread(new MyRunnable());
    t.start();
    System.out.println("Ending Main Thread...");
  }
}

class MyRunnable implements Runnable{

  @Override
  public void run(){
    System.out.println("Thread Started!");
    for(int i = 0; i < 5;i++)
      System.out.println(i);
  }
}

Note: Your result might be different from mine
'cause thread process is related to CPU
performance. Especially, if two or more threads
are running simultaneously

Result
Main Thread Started?
Ending Main Thread...
Thread Started!
0
1
2
3
4
First off, We created a class called MyRunnable that implements Runnable interface. Then, we overrode run() method in Runnable interface. Threads execute codes in the run() method once they start. Then, we instantiated a Thread class and put MyRunnable in the constructor as an argument. Then, we started that thread and it executed the codes that we put in the run() method.

Next, notice the result. So, we have two threads running in the example: Main thread and thread t. Main thread is the thread that automatically starts everytime JVM calls the main() method. We can use the main thread to start other threads.

Also, we can see in the result that, the main thread ended first before thread t started. Threads won't be terminated if one of the active threads is terminated.

We can also use anonymous class as a Runnable. If you're using java8 and above then, we can use lambda expression as an alternative to anonymous class since runnable has only one abstract method.
public class SampleClass{

  public static void main(String[]args){
    System.out.println("Main Thread Started!");
    
    //Creating a runnable using lambda
    Thread t = new Thread(() ->{
      System.out.println("Thread Started!");
      for(int i = 0; i < 5;i++)
      System.out.println(i);
    });
    t.start();
    
    System.out.println("Ending Main Thread...");
  }
}

Note: Result may vary
Result
Main Thread Started?
Ending Main Thread...
Thread Started!
0
1
2
3
4
Extending Thread Class

We can create a thread by extending the Thread class. If we want to take advantage of some members of the Thread class then extending the Thread class is a good choice.
public class SampleClass{

  public static void main(String[]args){
    
    Counter myCounter = new Counter(
                        new int[]{1,2,3,4,5});
    
    myCounter.start();
  }
}

class Counter extends Thread{
  int[] numbers;
  
  Counter(int[] numbers){
    this.numbers = numbers;
  }
  
  @Override
  public void run(){
    for(int i = 0; i < numbers.length;i++)
      System.out.print(numbers[i] + " ");
  }
  
  @Override
  public void start(){
    //don't forget to call the start()
    //of thread class if you override
    //the start() method. Otherwise,
    //your thread won't start.
    if(numbers != null)
      super.start();
    else
      System.out.println("Can't start thread "+
                        "'cause the array is null!");
  }
}

Result
1 2 3 4 5
sleep() Method

sleep() is a static method that causes a thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. sleep() throws InterruptedException which is a checked exception. Thus, we need to handle it.
public class SampleClass{

  public static void main(String[]args){
    Thread t = new Thread(() ->{
      
       for(int i = 0; i < 5;i++){
         System.out.print(i + " ");
         try{
           Thread.sleep(100);
         }
         catch(InterruptedException e){
           System.out.println("Thread has been"
                             +" interupted!");
           e.printStackTrace();
         }
         
       }
       
    });
  }
}
The example above displays numbers per 100 milliseonds interval. sleep() has another overloaded form: sleep(long millis,int nanos)
We can use that form if we want a time with nano-precision.

currentThread() Method

This static method returns a reference to the currently executing thread object.
public class SampleClass{

  public static void main(String[]args){
    Thread mainThreadName = Thread.currentThread();
    System.out.println("Executing thread name: " + 
                       mainThreadName.getName());
                       
    Thread t = new Thread(() ->{
      System.out.println("Executing thread name: " + 
                       Thread.currentThread().getName());
    },"MyThread");
    t.start();
    
  }
}

Result

Executing thread name: main
Executing thread name: MyThread
Stopping Thread By Using Boolean Flag

We can use a boolean flag to kill/stop a thread. We can't use the stop() method in Thread class because that's obsolete.
public class SampleClass{

  public static void main(String[]args){
  
    Counter myCounter = new Counter();
    try{
      myCounter.start();
      Thread.sleep(500);
      myCounter.stopThread();
    }catch(InterruptedException e){
      e.printStackTrace();
    }
    
  }
}

class Counter extends Thread{
  private boolean stopFlag = false;
  private int count = 0;
  
  @Override
  public void run(){
    while(!stopFlag){
      try{
        System.out.println("count: " + count);
        count += 1;
        sleep(100);
      }
      catch(InterruptedException e){
        e.printStackTrace();
      }
    }
    System.out.println("Thread ended!");
  }
  
  public void stopThread(){
    stopFlag = true;
  }
  
}

Result
count: 0
count: 1
count: 2
count: 3
count: 4
Thread Ended!
Stopping Thread By Interruption

We can interrupt a thread in order to stop its execution. Unlike in boolean flag method, we need a java element that throws InterruptedException like the sleep() method.
public class SampleClass{

  public static void main(String[]args){
  
    Counter myCounter = new Counter();
    try{
      myCounter.start();
      Thread.sleep(500);
      
      //interrupt() method
      //interrupts the thread
      //that invokes interrupt()
      myCounter.interrupt();
    }catch(InterruptedException e){
      e.printStackTrace();
    }
    
  }
}

class Counter extends Thread{
  private int count = 0;
  
  @Override
  public void run(){
    try{
      while(true){
        System.out.println("count: " + count);
        count += 1;
        sleep(100);
      }
    }
    catch(InterruptedException e){
      System.out.println("Thread has been"+
                         " interrupted!");
    }
  }
  
}

Result
count: 0
count: 1
count: 2
count: 3
count: 4
Thread has been interrupted!
We can use the interrupted() or isInterrupted() method if we want to check if a thread is interrupted or not. The differences between interrupted() and isInterrupted() are: interrupted() is static whereas isInterrupted() is non-static; interrupted() clears the interrupt status of a thread once it's invoked whereas isInterrupted() doesn't clear the interrupt status of a thread once it's invoked.

Interrupt status is a flag in every thread that indicates if a thread is interrupted or not. interrupt() method sets the interrupt status of threads.
public class SampleClass{

  public static void main(String[]args){
  
    Counter myCounter = new Counter();
    try{
      myCounter.start();
      Thread.sleep(200);
      myCounter.interrupt();
    }catch(InterruptedException e){
      e.printStackTrace();
    }
    
  }
}

class Counter extends Thread{
  private int count = 0;
  
  @Override
  public void run(){
    try{
      while(!isInterrupted()){
        System.out.println("count: " + count);
        count += 1;
        sleep(5000);
      }
      throw new InterruptedException();
    }
    catch(InterruptedException e){
      System.out.println("Thread has"
                +" been interrupted!");
    }
  }
}

Result
count: 0
Thread has been interrupted!
One of the advantages of interruption is that, interruption can stop the thread even it's blocked. Unlike using a boolean flag. Take a look at this example.
public class SampleClass{

  public static void main(String[]args){
  
    Counter myCounter = new Counter();
    try{
      myCounter.start();
      Thread.sleep(200);
      myCounter.stopThread();
    }catch(InterruptedException e){
      e.printStackTrace();
    }
    
  }
}

class Counter extends Thread{
  private boolean stopFlag = false;
  private int count = 0;
  
  @Override
  public void run(){
    while(!stopFlag){
      try{
        System.out.println("count: " + count);
        count += 1;
        sleep(5000);
      }
      catch(InterruptedException e){
        e.printStackTrace();
      }
    }
    System.out.println("Thread ended!");
  }
  
  public void stopThread(){
    stopFlag = true;
  }
  
}

Result
count: 0
Thread ended!
This example couldn't stop the thread when it was blocked. We needed to wait for the thread to be unblocked in order to check the boolean flag.

Though, in my personnal preference, I still prefer using boolean flag when I wanna stop threads. For me, using boolean flag is much simplier than interruption. Though, there are circumstances where interruption is more preferrable than boolean flag.

join() Method

join() waits the thread that invokes this method to die. join() ensures that any instructions next to this method won't be executed unless the thread that calls this method dies or the time specified in this method is due. join() throws InterruptedException which is a checked exception. Thus, we need to handle it.
public class SampleClass{

  public static void main(String[]args){
    System.out.println("Main Thread Started!");
    Counter c1 = new Counter("Counter1");
    Counter c2 = new Counter("Counter2");
    Counter c3 = new Counter("Counter3");
    try{
      c1.start();
      
      //this join() is equivalent to join(0)
      c1.join();
      //code below won't be executed unless
      //c1 dies
      
      System.out.println();
      c2.start();
      c2.join(500);
      //code below won't be executed unless
      //the time in millis that is specified
      //is due
      
      System.out.println();
      c3.start();
      c3.join(500,500_000);
      //code below won't be executed unless
      //the time in millis + nanos that
      //is specified is due
      
      System.out.println("Main Thread Ended!");
    }catch(InterruptedException e){
      e.printStackTrace();
    }
    
  }
}

class Counter extends Thread{
  private String name;
  
  Counter(String name){
    this.name = name;
  }
  
  @Override
  public void run(){
  
    try{
      for(int i = 0; i < 5;i++){
        System.out.println(name + " " + i);
        sleep(100);
      }
    }
    catch(InterruptedException e){
      e.printStackTrace();
    }
    
  }
}

Result

Main Thread Started!
Counter1 0
Counter1 1
Counter1 2
Counter1 3
Counter1 4

Counter2 0
Counter2 1
Counter2 2
Counter2 3
Counter2 4

Counter3 0
Counter3 1
Counter3 2
Counter3 3
Counter3 4
Main Thread Ended!
In the example above, we have four threads, the main thread an three user threads. Once main thread calls c1.join(), main thread will temporarily stop its execution while waiting for c1 to die. Then, After c1 dies, main thread continues and calls c2.join().

c2.join() has specified time so, main thread will stop executing for the specified time. Once the specified time is due, main continues its execution and calls c3.join(). c3.join() also has specified time so, main thread will stop executing for the specified time. Once the specified time is due, main continues its execution and eventually dies.

join() dependent on OS for timing. So, don't assume that join() will exactly stop for a specified time that we put in it.
Lifecycle Of a Thread

Threads go through different states during their lifetime. Basically, threads have four states: New, Runnable, Blocked or Non-Runnable and Terminated.

Thread Lifecycle
New: This is the state of every thread that is newly created but they aren't started yet. Their states change if they start to run.
Runnable: This state indicates that a thread is running or is being prepped to run.
Blocked: This state indicates that a thread is not running temporarily or indefinitely.
Terminated: This state indicates that a thread is terminated or in other words, the thread is dead.

Thread States

In java, thread states are wrapped into an enum class. To get the state of a thread, we will use the getState() method in Thread class. These are the thread states accoding to Enum Thread.State.

NEW: A thread that has not yet started is in this state.

RUNNABLE: A thread executing in the Java virtual machine is in this state.

BLOCKED: A thread that is blocked waiting for a monitor lock is in this state.

WAITING: A thread that is waiting indefinitely for another thread to perform a particular action is in this state.

TIMED_WAITING: A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.

TERMINATED: A thread that has exited is in this state.
public class SampleClass{

  public static void main(String[]args){
    System.out.println("Main Thread Started!");
    
    //we use getState() method to get the state
    //of a thread
    System.out.println("Main Thread state: " +
                       Thread.currentThread().
                       getState());
                       
    Counter c1 = new Counter("Counter1",
                             Thread.currentThread());
    System.out.println("c1 has been created!");
    System.out.println("c1 Thread State: "+
                         c1.getState());
    try{
      c1.start();
      c1.join();
      
      System.out.println("c1 Thread State: "+
                          c1.getState());
                         
      System.out.println("Main Thread Ended!");
    }catch(InterruptedException e){
      e.printStackTrace();
    }
    
  }
}

class Counter extends Thread{
  private String name;
  Thread mainThread;
  
  Counter(String name,Thread mainThread){
    this.name = name;
    this.mainThread = mainThread;
  }
  
  @Override
  public void run(){
    System.out.println("c1 has been started!");
    System.out.println("c1 Thread State: "+
                       getState());
    
    try{
      for(int i = 0; i < 5;i++){
        System.out.println(name + " " + i);
        sleep(100);
      }
      System.out.println("Main Thread State: "+
                         mainThread.getState());
    }
    catch(InterruptedException e){
      e.printStackTrace();
    }
    
  }
}

Result

Main Thread Started!
Main Thread State: RUNNABLE
c1 has been created!
c1 Thread State: NEW
c1 has been started!
c1 Thread State: RUNNABLE
Counter1 0
Counter1 1
Counter1 2
Counter1 3
Counter1 4
Main Thread State: WAITING
c1 Thread State: TERMINATED
Main Thread Ended!

Thread Priorities

Thread priorities are ranked from 1 to 10. 10 is the highest priority whereas 1 is the lowest priority. Threads with higher priorities preempt threads with lower priorities. In case two threads that have the same priority, a FCFS(First Come First Serve) ordering is followed. Also, high priority threads get more CPU time. In java, there are three static constants that represent thread priorirites: MAX_PRIORITY(10), NORM_PRIORITY(5) and MIN_PRIORITY(1).

If we don't specify the priority of a thread then, JVM will assign the default priority value which is NORM_PRIORITY. Thread scheduler handles the schedules of threads.

Note: High priority threads don't guarantee to be executed first over lower priority threads even we assume that they're scheduled first in the scheduler. The OS scheduler is still the one that will decide when and where a thread should run. The OS may follow the JVM's scheduler or not.
public class SampleClass{

  public static void main(String[]args){
    Thread t1 = new Thread();
    Thread t2 = new Thread();
    Thread t3 = new Thread();
    
    //use setPriority to set thread priority
    //we can use int literal or the three
    //constants
    t1.setPriority(Thread.MAX_PRIORITY);
    t2.setPriority(6);
    t3.setPriority(Thread.MIN_PRIORITY);
    
    //use getPriority() to get thread priority
    System.out.println("t1 priority: " + t1.getPriority());
    System.out.println("t2 priority: " + t2.getPriority());
    System.out.println("t3 priority: " + t3.getPriority());
  }
}

Thread Scheduler

JVM has thread scheduler that handles the schedules of java application threads. Scheduling schedules threads in some order. There are scheduling algorithms that are implemented in JVMs and we need to have a basic understanding of those algorithm in order to have an idea of how JVMs schedule threads.

Preemptive scheduling schedules threads based on their priorities. Threads with higher priorities can preempt threads with lower priorities. For example, a lower priority thread may pause if the allocated time or time slice to that thread is due or a higher priority thread comes back online and preempts the lower priority thread. Depending on JVM implementation, JVM usually uses Round Robin scheduling which is a type of preemptive scheduling.

Note: High priority threads don't guarantee to be executed first over lower priority threads even we assume that they're scheduled first in the scheduler. The OS scheduler is still the one that will decide when and where a thread should run. The OS may follow the JVM's scheduler or not.

First Come First Serve Scheduling schedules based on their order. The first thread that requests for CPU time will be the first one to get CPU time. This algorithm is managed by FIFO(First In First Out) queue. FCFS is a non-preemptive scheduling algorithm.

Time Slicing Scheduling heavily relies on time slice or time quantum. This preemptive algorithm divides CPU time to multiple time slices or quanta and then allocate those quanta into threads that are in ready state. This algorithm executes threads in a cyclical manner. Time Slicing Scheduling is also called Round Robin Scheduling.

Java Synchronization

Synchronization is a process of controlling threads that access any shared resource. If we want one thread to access any shared resource at a time then, we can use synchronization. In java, we can use synchronized methods or statements/blocks to achieve synchronization. Synchronization using synchronized methods or blocks is called mutual exclusion.

Mutual exclusion ensures that only one thread can access a critical section at a time. Critical section is a part of a code that alters memory value that can cause conflicts between threads. For example, threadA assigns value to variableA while threadB reads the data of variableA.

This situation is called a critical section 'cause threadA is altering the value of variableA while threadB is accessing the value of variableA. ThreadB might not be aware of the changes of variableA in threadA. Thus, there might be a conflict between threadA and threadB.

Any synchronized elements like volatile fields and codes in synchronized methods/blocks are considered as thread safe. Thread safe is a term that is used to indicate that code elements like fields, statements, etc. are safe from inconsistent data due to multiple threads updating those elements.

Thread Interference

Thread interference or interleaving happens when two operations run in different threads and accessing the same resource. These operations are comprised of multiple steps and these steps may overlap one another.

For example, we have a class and that class is accessed by two or more threads.
class Counter{
  private int count;
  
  public void increase(){
    count++;
  }
  
  public void decrease(){
    count--;
  }
}
By looking at the class above, it seems like thread interference is not going to be a problem 'cause we're only using the posfixes "++" and "--". However, there are single operations that can be broken down into multiple steps and increment(++) and decrement(--) operators are some of those.

For example, increment(++) can be decomposed into three steps:
1.) Gets the current value of count
2.) Increases the value by 1
3.) Assigns the modified value to count

The steps are the same as the steps of decrement(--) except for the second step. Change the second step from "increase" to "decrease".

Suppose thread A calls increase() and at the same time thread B calls decrease() and we expect count to become zero. This situation may lead to thread interferece. Suppose a thread interference happens between thread A and thread B. If so, the overlaps might be like this:
Thread A gets the current value of count
Thread B gets the current value of count
Thread B decreases the value of count by 1. The value is now -1
Thread B increases the value of count to 1. The value is now 0
Thread A assigns 0 to count. Now count is 0.
Thread B assigns -1 to count. Now count is -1.

Due to thread interference, count value became -1 instead of 0, which is not the result that we expected. Thread interference can be very problematic especially in a complex concurrency structure. Synchronization can help us to avoid thread interference.

Memory Consistency Errors

Memory Consistency Errors happen if two or more threads access the same resource but the value of the resource is inconsistent with each thread. The causes of these errors are complex and not going to be covered in this tutorial.

Knowing the causes of consistency errors is not required when creating a strategy to avoid these errors. However, we need to understand an important concept in multithreading, which is the happens-before relationship.

Happens-before relationship guarantees that the changes to shared resource in one thread is visibile to another thread that is using the resource.

For example, we have variable named as "counter" with zero initial value and then, threadA increases the value of counter by 1. Then threadB calls println() to print the value of counter on the console. If this scenario happens in a single thread then, we can assume that the value that is going to be displayed is 1.

If the increment code is executed by a thread and at the same time, the println() is called by another thread then, consistency errors may happen. Since we're dealing with two executions, we don't know if threadB is aware of the changes that happens in threadA.

The resolve this problem, we need to establish a happens-before relationship between threadA and threadB. Synchronization using synchronized blocks or methods is one of the features of java that can establish happens-before relationship between threads. We will learn about synchronized methods and blocks later.

We already witness some happens-before scenarios in previous topics. These scenarios create happens-before relationship.
  • If one thread executes statements before it creates a new thread then, every changes those statements made are visible to the newly created thread. For example, We have a variable named as "counter" with 0 initial value and then, threadA increments its value by 1 and after that, we create and start threadB. Now, threadB displays the value of counter.

    In this scenario, it's safe to assume that the value that is going to be displayed is "1", since the increment only happened in the individual thread and then that thread subsequently created threadB after the increment.

  • If a thread is terminated due to the invocation of join() method then, the changes that happened in the terminated thread is visible to the thread that invoked the join() method.
Intrinsic Locks

Intrinsic locks or monitor locks(Java API calls these lock as "monitors") enforce exclusive access to an object's state and building happens-before relationship between threads. Every object has intrinsic lock and a thread needs to acquire that lock first in order to have an exclusive access to that object. Once the thread is done accessing the object, it can release the lock so other threads have a chance to grab the lock.

As long as a thread holds an object's lock, other threads can't acquire that lock unless the thread releases the lock. In java synchronization, threads can't acquire the same lock that is owned by another thread.

When a lock is released, a happens-before relationship is created between the changes that happened in the former thread that owned the lock and the other threads that might acquire the lock.

These mechanics are part of java synchronization and we can implement these mechanics by using synchronized methods or statements/blocks.

Synchronized Method

A thread acquires an object's lock if the thread invokes one of the object's synchronized methods. The thread releases the lock once the method returns the execution to the caller which is the thread itself. The lock is released even if the return statement throws an exception.
public class SampleClass{

  public static void main(String[]args){
    Counter c1 = new Counter();
    
    Thread t1 = new Thread(new MyRunnable(c1,"t1","increase"),
                           "t1");
    Thread t2 = new Thread(new MyRunnable(c1,"t2","increase"),
                           "t2");
    Thread t3 = new Thread(new MyRunnable(c1,"t3","decrease"),
                           "t3");
    Thread t4 = new Thread(new MyRunnable(c1,"t4","increase"),
                           "t4");
    
    t1.start();
    t2.start();
    t3.start();
    t4.start();
  }
}

class MyRunnable implements Runnable{
  private Counter counter;
  private String name;
  private String operation;
  
  MyRunnable(Counter counter, String name,
             String operation){
    this.counter = counter;
    this.name = name;
    this.operation = operation;
  }
  
  @Override
  public void run(){
    counter.count(name,operation);
  }
  
}

class Counter{
  private int num = 0;
  
  //To create a synchronized method
  //use the synchronized keyword
  synchronized void count(String threadName,
                          String operation){
    System.out.print("Thread: " + threadName);
    if(operation.equals("increase"))
      System.out.print(" num: " + ++num);
    else if(operation.equals("decrease"))
      System.out.print(" num: " + --num);
    System.out.println();
  }
  
}

Note: Result may vary
Result
Thread: t1 num: 1
Thread: t3 num: 0
Thread: t2 num: 1
Thread: t4 num: 2
By using synchronized method, we can avoid thread interference and memory consistency errors. Remember, a happens-before relationship is established when a thread releases a lock. We can create a static synchronized method. The lock of synchronized method resides at the class itself not in the instances of the class. Note, Synchronized constructor is not allowed.

Synchronized Block/Statement

Another way of synchronizing java codes is to use synchronized block. This block synchronizes a collection of statements that may be related to the specified object. The specified object is in the parentheses of the block. Take a look at this code excerpt.
public class Counter{
  private int num1 = 0;
  private final int num2;
  ArrayList<Integer> numList;
  
  Counter(int num2,ArrayList<Integer> numList){
    this.num2 = num2;
    this.numList = numList;
  }
  
  void count(){
    //final primitive variables can be 
    //safely read in non-synchronized 
    //methods/blocks 'cause these
    //variables are constants. Thus,
    //their values won't change
    System.out.println("num2: " + num2);
    
    //This is how we create a synchronized
    //block. The "this" in the parentheses
    //is the object resource where its lock
    //is gonna be acquired by threads that
    //are gonna access this block
    synchronized(this){
      num1 += num2;
      System.out.println("num1: " + num1);
      
      //If possible, avoid invoking other
      //objects' methods from synchronized
      //methods/blocks doing so may lead to
      //nasty problems like deadlock, 
      //starvation, etc.
      //numList.add(num1);
    }
    
    //It's preferrable to put other objects'
    //methods out of synchronized block/method
    numList.add(num1);
  }
}
Sometimes, we want some variables that have nothing to do with one another to be synchronized separately. We can use a dummy objects,if you will, to synchronize those variables. Take a look at this code excerpt.
public class Counter{
  private int num1;
  private int num2;
  //Dummy objects. The sole purpose
  //of dummy objects is to act as a lock.
  //These objects are unrelated to
  //num1 and num2
  Object o1 = new Object();
  Object o2 = new Object();
  
  void num1Inc(){
    synchronized(o1){
      num1++;
    }
  }
  
  void num2Inc(){
    synchronized(o2){
      num2++;
    }
  }
  
}
Now, any threads that want to increment num1 can access num1Inc() method. To increment num2, threads have to access num2Inc() method. In this way, any unnecessary blocks between num1 and num2 are removed.

Imagine putting num1 and num2 in one synchronized method, more threads have to access that method synchronously. Therefore, more threads have to be in waiting state.

Tip: String objects are not good candidate for synchronized blocks because some of them may reside in a special memory pool called String Constant Pool. Locking an object inside SCP may degrade our app performance because String object is one of the most used objects in Java. Therefore, SCP is one of the most accessed memory area in a java application. Instead, we can use Object instance as an alternative.

Multiple Locks Acquisition

A thread can acquire multiple locks as long as those locks are not owned by other threads. Though, Acquiring multiple locks may cause nasty problems like deadlock.
class ClassA{

  synchronized void meth1(){
  }
}

class ClassB{
  ClassA a1= new ClassA();
  
  synchronized void meth2(){
    a1.meth1();
  }
}
So, for example, ThreadA access ClassB object and invoke meth2(). When ThreadA invokes meth2(), that thread acquires the lock of ClassB's object. Then, meth1() is invoked by ThreadA so, the thread acquires the lock of ClassA's object. Now, ThreadA has two locks.

Reentrant Synchronization

A thread can acquire a lock that it already owns more than once. This process is called reentrant synchronization. Take a look at this code.
public class ClassA{

  synchronized void meth1(){
  }
  
  void meth2(){
    synchronized(this){
      meth1();
    }
  }
}
So, let's say threadA invokes meth2() and then it enters in the the synchronized block. Thus, threadA acquires the lock of ClassA's object. Then, threadA invokes meth1 and subsequently acquire the lock of ClassA's object again. So, threadA has two locks of ClassA's object.

Now that we have two locks, threadA needs to release two locks in order to fully release the lock of ClassA's object. Otherwise, other threads can't acquire the lock of ClassA's object.

Imagine if there's no reentrant synchronization. ThreadA can't invoke meth1() 'cause the lock of ClassA's object is already owned by threadA itself. So, to invoke meth1(), threadA needs to release the lock it's owning. To release the lock, threadA needs to exit the synchronized block first.

ThreadA can't leave the synchronized block 'cause it needs to invoke meth1() and meth1() requires threadA to release its lock. Therefore, threadA is blocking itself.

Inter-Thread Communication

The process of communication between threads in synchronized environment is called Inter-thread Communication or Co-operation. We already know that threads communicate with other threads through acquiring object locks in synchronized environment. We can improve the communication between threads by using the three methods in the Object class: wait(), notify() and notifyAll().

wait(), notify() and notifyAll() Methods

wait() method temporarily stops the execution of a thread that invoked the method and releases the lock that the thread holds. The thread state is changed to a waiting state and will wait for an opportunity to acquire the object's lock again once notified.
notify() method awakens one thread that is waiting on a particular object's lock. If two or more threads are waiting for the object's lock, one of them will be awakened arbitrarily. The arbitrary choice depends on the implementation.
notifyAll() method awakens all threads that are waiting on a particular object's lock.

Note: notify() and notifyAll() don't give up locks to awakened threads. Their job is only to awaken threads.
Note: These methods can only be used in synchronized environment like synchronized methods and blocks.

Let's take a look at this simple cosumer-producer example.
public class SampleClass{

  public static void main(String[]args){
    Counter c1 = new Counter();
    
    //ThreadA
    new Thread(() ->{
      c1.consume();
    }).start();
    
    //ThreadB
    new Thread(() ->{
      c1.produce();
    }).start();
    
  }
}

class Counter{
  private int num = 1;
  private boolean isProduced = false;
  
  void consume(){
    synchronized(this){
      try{
        if(!isProduced)
          wait();
      }
      catch(InterruptedException e){
        e.printStackTrace();
      }
      System.out.println("Consumed number: " + num);
      notify();
    }
  }
  
  void produce(){
    synchronized(this){
      try{
        if(isProduced)
          wait();
      }
      catch(InterruptedException e){
        e.printStackTrace();
      }
    
      num += num;
      System.out.println("Produced number: " + num);
      notify();
    }
  }
}

Result
Produced number: 2
Consumed number: 2
So, threadA starts first and invokes consume(). Though, it invokes wait() and invoking wait() changes threadA state from running to waiting state. Then, threadB starts and invokes produce(). Then, the produced number is displayed on the console and threadB invokes notify() to notify waiting threads and invokes wait() to pause itself. notify() notifies one of the threads that is waiting for the lock of Counter object.

In this example, threadA is the only waiting thread before threadB invoked notify(). So, threadA is notified and continues its execution from where it left. Then, the consumed number is displayed on the console and threadA invokes notify() to notify threadB.

Deadlock

Deadlock is a situation where a thread is forever waiting for another thread to release a lock or two threads are waiting for the locks of each other. For example, threadA is waiting for objectB lock that is owned by threadB. ThreadB is waiting for objectA lock that is owned by threadA. Let's take a look at this example.

Note: This example will be non-responsive. So, we need to terminate this program by force. If you're on windows and using cmd, just press CTRL+C to terminate the program.
public class SampleClass{

  public static void main(String[]args){
    Object o1 = new Object();
    Object o2 = new Object();
    
    new Thread(()->{
      
      synchronized(o1){
        System.out.println("ThreadA acquires o1 lock!");
        try{Thread.sleep(150);}
        catch(InterruptedException e){
          e.printStackTrace();
        }
        synchronized(o2){
          System.out.print("ThreadA acquires o2 lock!");
          System.out.println();
        }
      }
    }).start();
    
    new Thread(() ->{
        synchronized(o2){
        System.out.println("ThreadB acquires o2 lock!");
        try{Thread.sleep(150);}
        catch(InterruptedException e){
          e.printStackTrace();
        }
        synchronized(o1){
          System.out.print("ThreadB acquires o1 lock!");
          System.out.println();
        }
      }
    }).start();
    
  }
}

Result
ThreadA acquires o1 lock!
ThreadB acquires o2 lock!
First off, threadA has been started and acquired o1 lock. Then, threadB has been started and acquired o2 lock. After that, threadA couldn't acquire o2 lock because it was owned by threadB. ThreadB couldn't acquire o1 lock because it was owned by threadA. Therefore, the two threads are in a deadlock situation.

Deadlock is unfixable once it occurs. The only way to fix deadlock is to redesign our thread structure.

Contention, Starvation and Livelock

Contention, Starvation and livelock are less common problems than deadlock. Though, It's better to learn them 'cause, once we design a concurrent software, we may encounter them in the future and it's our responsibility to fix them.

Contention is a situation where a greedy thread or high priority thread, holds a shared resource for too long indefinitely blocking other threads in the process. Contention creates an imbalance between threads and that imbalance may hurt our software performance.

Starvation is a situation where a thread can't access a shared resource because other threads access it first. This situation leaves the thread "starved".

Livelock is a situation where two threads are busy responding to each other's actions. Thus, stalling the progress of both threads. Unlike deadlock, threads in livelock are not blocked, they're just too busy responding to each other.

Livelock is comparable to a scenario where two people want the other to eat first. For example, personA wants personB to eat first. At the same time, personB wants personA to eat first. These people are trying their best to respond to each other's request and both of them are not backing off. Thus, both of them are not eating and they're convincing each other to eat first forever.

volatile Keyword

volatile keyword can be used in global fields to ensure memory visibility just like what other synchronization methods do. Just like other synchonization methods, this keyword solves some problems regarding Cache Coherence.

We can use this keyword to implement non-blocking synchronization. However, volatile keyword doesn't support mutual exclusion. Because of this, thread interference(race condition) may occur. This is how we apply volatile keyword to fields.
public class SampleClass{
  
  private static volatile int num = 0;
  //or
  //static private volatile int num = 0;
  
  public static void main(String[]args){
    
    new Thread(()->{
    System.out.println(Thread.currentThread().getName()+
	": " + (++num));
    }).start();
    
  }
}

Result(may vary)
Thread-0: 1
Thread-1: 2
Note that thread interference may occur in the example above. These are the benefits that we're going to get if we use the volatile keyword:
  • Memory Visibility
  • Happens-Before Relationship
One of the advantages of this keyword is that we can update variables via atomic operations with the effect of Happens-Before Relationship. I explained atomic operations in this article.

Atomic operations with Happens-Before Relationship avoid data race. In terms of performance, non-blocking synchronization like volatile keyword is less expensive than blocking synchronization like synchronized keyword.

Reordering

Reordering is a way of improving program's performance by allowing Java or CPU to rearrange instructions. For example, threadA has these statements:
Statements #1
int i1 = 9;
int i2 = i1;
i1++;

Java or the CPU may rearrange the statements above like this:
Statements #2
int i1 = 9;
i1++;
int i2 = i1;

Let's assume that the statements above are not synchronized. As we can see, i2 in "statements #1" example is 9 and i2 in "statements #2" is 10. This reordering mechanism may also happen in synchronized environment. Although, reordering volatile fields and synchronized statements is sequentially consistent and respects the happens-before relationship.

According to jls-17.4.5: "It should be noted that the presence of a happens-before relationship between two actions does not necessarily imply that they have to take place in that order in an implementation. If the reordering produces results consistent with a legal execution, it is not illegal."

Roach Motel Principle

Reference: JMM Interpretation: Roach Motel
Java or CPU may move codes into a synchronized block. Once the codes move into the synchronized block, they can't be move out of the block. This process is called the Roach Motel Principle. Take a look at this example.
Original Order
a++;
synchronized(object){
  int b = 3;
}
c = 2;

Possible Reorder #1

synchronized(object){
  a++;
  int b = 3;
}
c = 2;

Possible Reorder #2

a++;
synchronized(object){
  int b = 3;
  c = 2;
}
As we can see, our codes may be reordered in diferent ways. This type of reordering is allowable as long as it doesn't violate the Java Memory Model.

Daemon Thread

Daemon thread is a thread that is suitable for background tasks. One example of daemon thread is the garbage collector. Garbage collector is a daemon thread that manages memory allocation of java programs.

There are two types of threads in java: User thread or non-daemon thread; and daemon thread. JVM prioritizes users threads more than daemon threads. When all user threads die, JVM terminates the program and kill all deamon threads. Unlike user threads, JVM won't kill any user thread or terminate itself if all deamon threads die.

Daemon thread is suitable for background tasks and it's recommended to be easily terminated. Blocking mechanism like join() in daemon thread may block the termination of a program even all user thraeds are dead.

To create a daemon thread, we need to create a user thread first and set the thread to a daemon thread.
public class SampleClass{

  public static void main(String[]args){
    Thread t1 = new Thread(() -> {
       while(true){}
    });
    
    //use isDaemon() method to check if a
    //thread is a deamon thread
    System.out.println(t1.isDaemon());
    
    //use setDaemon() method to set a thread
    //to a daemon thread
    t1.setDaemon(true);
    System.out.println(t1.isDaemon());
    t1.start();
    System.out.println("Main Thread End!");
  }
}
In the example above, t1 is terminated even it runs an infinite loop. Once the main thread is terminated, JVM terminates all daemon threads and subsequently end the program.

ThreadGroup Class

We can use the ThreadGroup class to group threads and thread groups so, we can easily manage many threads of our program. Thread groups form a tree where every thread group, except for the initial thread group, has a parent.
public class SampleClass{

  public static void main(String[]args){
    //ThreadGroup constructor with ThreadGroup name
    //as argument
    ThreadGroup tg1 = new ThreadGroup("Group1");
    //ThreadGroup constructor with parent thread group
    //and thread group name as arguments
    ThreadGroup tg2 = new ThreadGroup(tg1,"Group2");
    
    //create threads and assign groups
    new Thread(tg1,"t1");
    new Thread(tg1,"t2");
    new Thread(tg2,"t3");
    new Thread(tg2,"t4");
    new Thread(tg2,"t5");
    
    //activeCount() returns an estimate of the number
    //of active threads in this thread group and its
    //subgroups.
    System.out.println(tg1.getName() + " active threads: "
                       + tg1.activeCount());
    
    //activeGroupCount() returns an estimate of the
    //number of active groups in this thread group and
    //its subgroups.
    //
    System.out.println(tg1.getName() + " active groups: "
                       + tg1.activeGroupCount());
    
    //getParent() returns the parent of this thread
    //group.
    //Group1 parent is the main thread group which is the
    //initial thread group
    System.out.println(tg1.getName() + " parent: "
                       + tg1.getParent());
    System.out.println(tg2.getName() + " parent: "
                       + tg2.getParent());
    
  }
}

Result
Group1 active threads: 0
Group1 active groups: 1
Group1 parent: java.lang.ThreadGroup[name=main,maxpri=10]
Group2 parent: java.lang.ThreadGroup[name=Group1,maxpri=10]
There are some interesting methods that I didn't show in the example above. To interrupt all threads in a thread group, use the interrupt() methd in ThreadGroup class. If we want to put active threads in a thread group into an array, use the enumerate() method. If we want to set and get thread group priority, use setMaxPriority() and getMaxPriority() methods. For more information, check out the ThreadGroup class.

yield() Method

yield() method gives a hint to the JVM scheduler that the thread that invoked yield() is willing to give up its CPU time which can be used by other threads. The thread scheduler may ignore the hint or not. Using this method effectively is not an easy task. You may need to use detailed profiling and benchmarking in order to achieve the desired effect that you wanna achieve. Let's create an example to introduce the yield() method.
public class SampleClass{

  public static void main(String[]args){
    Thread t1 = new Thread(new Counter("t1"));
    Thread t2 = new Thread(new Counter("t2"));
    
    t1.setPriority(Thread.MAX_PRIORITY);
    t2.setPriority(Thread.MIN_PRIORITY);
    
    t1.start();
    t2.start();
  }
}

class Counter implements Runnable{
  private String name;
  
  Counter(String name){
    this.name = name;
  }
  
  @Override
  public void run(){
  
    for(int i = 0; i < 2; i++){
      System.out.println(name +": " + i);
      Thread.yield();
    }
  }
}

Note: Result may vary
Result
t1: 0
t2: 0
t1: 1
t2: 1