Java Fundamentals Tutorial: Additional Java Features

15. Additional Java Features

15.1. The Date Class

  • The java.util.Date class represents a specific instant in time.

    • The time is measured as a signed long representing the number of milliseconds since the epoch.
    • In Java, the epoch is defined as January 1, 1970, 00:00:00 UTC (GMT).
  • Invoking the Date constructor with:

    • A long value initializes the object to represent that time
    • No arguments initializes the object to represent the object’s creation time
  • The Date class includes methods for:

    • Retrieving the time: getTime()
    • Changing the object’s time: setTime(long)
    • Comparing Date objects: after(Date), before(Date), compareTo(Date), equals(Object)
    • Generating a String representation in a fixed format: toString()
[Caution]Caution

Most other Date methods are deprecated. They supported time and date manipulation, parsing, and formatting, but they have been superseded by newer classes.

The System.currentTimeMillis() returns the current epoch-based time as a long.

15.2. The Calendar Class

  • The java.util.Calendar class is used to represent a specific instance in time. It supports methods for:

    • Setting individual time and date fields of the object (month, day, hour, minute, etc.)
    • Performing time and date arithmetic
    • Performing comparisons with other Calendar objects
    • Retrieving the represented time as a Date object or an epoch-based long
  • The Calendar class is an abstract class, to support concrete implementations for different calendar styles and languages.

    • The java.util.GregorianCalendar class is a concrete implementation of the Gregorian calendar used by most of the world.
    • Invoking the static Calendar.getInstance() method returns a localized Calendar object whose calendar fields have been initialized with the current date and time.

15.3. The TimeZone Class

  • The java.util.TimeZone class represents a time zone offset, and also figures out daylight savings time.
  • Typical use is to invoke the static TimeZone.getTimeZone() method to return a TimeZone object based on the system’s time zone setting.

    • You can also invoke TimeZone.getTimeZone() with a String time zone ID. For example:

      TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");

package util;

import java.util.Date;
import java.util.Calendar;
import java.util.TimeZone;

public class Now {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date);
        System.out.println(date.getTime() + " ms since the epoch");

        Calendar c = Calendar.getInstance();
        System.out.println("Year:   " + c.get(Calendar.YEAR));
        System.out.println("Month:  " + c.get(Calendar.MONTH));
        System.out.println("Day:    " + c.get(Calendar.DAY_OF_MONTH));
        System.out.println("WeekDay:" + c.get(Calendar.DAY_OF_WEEK));
        System.out.println("Hour:   " + c.get(Calendar.HOUR_OF_DAY));
        System.out.println("Minute: " + c.get(Calendar.MINUTE));
        System.out.println("Second: " + c.get(Calendar.SECOND));
        System.out.println("Millis: " + c.get(Calendar.MILLISECOND));

        TimeZone timeZone = c.getTimeZone();
        System.out.println("TZ ID:  " + timeZone.getID());
        System.out.println("TZ Name:" + timeZone.getDisplayName());
        System.out.println("TZ Off: " + timeZone.getRawOffset());
    }
}

15.4. Formatting and Parsing Dates

  • java.text.DateFormat is an abstract class for date/time formatting subclasses which formats and parses dates or time in a language-independent manner.

    • The date/time formatting subclass handles formatting and parsing dates and times.
  • DateFormat includes several static methods for obtaining default date/time formatters based on the default or a specified locale.

    • For example, to get a date/time formatter with the default formatting style for the default locale:

      DateFormat df = DateFormat.getDateTimeInstance();
    • To get a formatter for the long-style date in the French locale:

      DateFormat df = DateFormat.getDateInstance(
              DateFormat.LONG, Locale.FRANCE);
  • For a given instance of a DateFormat object:

    • The format(Date) method generates a String representation of a Date object
    • The parse(String) method generates a Date object by parsing a date/time string
  • java.text.SimpleDateFormat is a concrete subclass of DateFormat.

    • It allows you to define you own patterns for date/time formatting and parsing.

package com.marakana.demo;

import java.util.Date;
import java.text.*;

public class DateExample {

    public static void main(String[] args) {
        Date now = new Date();
        DateFormat df;

        // Get the default date and time format
        df = DateFormat.getDateTimeInstance();
        System.out.println(df.format(now));

        // Get the default "short" date and time format
        df = DateFormat.getInstance();
        System.out.println(df.format(now));

        // Get the default "medium" date format
        df = DateFormat.getDateInstance(DateFormat.MEDIUM);
        System.out.println(df.format(now));

        // Get the default "long" time format
        df = DateFormat.getTimeInstance(DateFormat.LONG);
        System.out.println(df.format(now));

        // An example of parsing a time and date string into a Date
        try {
            df = new SimpleDateFormat("MMM d, y");
            Date birthday = df.parse("August 29, 1965");
            df = DateFormat.getDateInstance(DateFormat.MEDIUM);
            System.out.println(df.format(birthday));
        } catch (ParseException e) {
            // Couldn't parse the date string
            e.printStackTrace();
        }


    }

}

15.5. Formatting and Parsing Dates

  • java.text.NumberFormat is an abstract class for number formatting subclasses which formats and parses numbers in a language-independent manner.

    • The number formatting subclass handles formatting and parsing numbers.
    • Your code can be completely independent of the locale conventions for decimal points, thousands-separators, currency symbols, or even the particular decimal digits used.
  • NumberFormat includes several static methods for obtaining default numbers formatters based on the default or a specified locale.

    • For example, to get a number formatter with the default formatting style for the default locale:

      NumberFormat nf = NumberFormat.getInstance();
    • To get a currency formatter for the Japanese locale:

      NumberFormat nf
          = NumberFormat.getCurrencyInstance(Locale.JAPAN);
  • For a given instance of a NumberFormat object:

    • The format() method generates a String representation of a number
    • The parse() method generates a Number object by parsing a numeric-formatted string

package com.marakana.demo;

import java.text.*;
import java.util.Locale;

public class FormatNumbers {

    public static void main(String[] args) {
        // Print out a number using the localized number, integer, currency,
         // and percent format for each locale
         Locale[] locales = NumberFormat.getAvailableLocales();
         double myNumber = -123456.78;
         NumberFormat form;
         for (int j=0; j<4; ++j) {
             System.out.println("FORMAT");
             for (Locale l: locales) {
                 if (l.getCountry().length() == 0) {
                    continue; // Skip language-only locales
                 }
                 System.out.print(l.getDisplayName());
                 switch (j) {
                 case 0:
                     form = NumberFormat.getInstance(l); break;
                 case 1:
                     form = NumberFormat.getIntegerInstance(l); break;
                 case 2:
                     form = NumberFormat.getCurrencyInstance(l); break;
                 default:
                     form = NumberFormat.getPercentInstance(l); break;
                 }
                 if (form instanceof DecimalFormat) {
                     System.out.print(":\t" + ((DecimalFormat) form).toPattern());
                 }
                 System.out.print(" ->\t" + form.format(myNumber));
                 try {
                     System.out.println(" ->\t" + form.parse(form.format(myNumber)));
                 } catch (ParseException e) {}
             }
         }
    }

}

15.6. Typesafe Enums

  • Prior to Java 5, it was common to use public final static ints for enumerated values (enums). But there are drawbacks:

    • Not typesafe — It’s just an int, and nothing prevents you from passing in another int where an enum was expected.
    • No namespace — You need to invent prefixes to avoid name collisions with other int enum types.
    • Brittle — The int constants are compiled into client code, so the behavior is undefined if the enum order changes or new constants are added.
    • Uninformative printed values — Because they are just ints, if you print one out all you get is a number.
    • No easy way to guarantee iteration over all enum values.

public class ChessPiece {
    public static final int COLOR_WHITE = 0;
    public static final int COLOR_BLACK = 1;
    public static final int FIGURE_KING = 0;
    public static final int FIGURE_QUEEN = 1;
    public static final int FIGURE_ROOK = 2;
    public static final int FIGURE_BISHOP = 3;
    public static final int FIGURE_KNIGHT = 4;
    public static final int FIGURE_PAWN = 5;

    private final int color;
    private final int figure;

    public ChessPiece(int color, int figure) {
        this.color = color;
        this.figure = figure;
    }

    public int getColor() { return this.color; }
    public int getFigure() { return this.figure; }

    public String toString() {
        /* switch statements to convert int color and figure to string */
    }
}

But the following invalid initialization still compiles!

new ChessPiece(ChessPiece.FIGURE_QUEEN, ChessPiece.FIGURE_KING);

You can even combine int enums in unnatural ways:

int combined = ChessPiece.COLOR_BLACK + ChessPiece.FIGURE_PAWN;

15.7. Typesafe Enums (cont.)

  • Java 5 introduced the enum keyword for defining typesafe enumerated values. For example:

    enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }
  • Java 5 enums are full-fledged classes, providing many benefits:

    • They are typesafe, with compile-time error checking.
    • The declared enum provides a natural namespace.
    • Enum definitions are loaded at runtime, so enums can evolve and change without breaking existing code.
    • You can switch on enum values.
    • They can have fields and methods to add state and behavior.
    • They have a default toString() that returns the enum value as a String — but you can override it if you want!
    • The enum type has a default values() method that returns an array of all the enum values. For example:

      for (Day d: Day.values()) System.out.println(d);
    • They can implement interfaces
    • They automatically implement the Comparable and Serializable interfaces.
    • They can be used in collections as objects more efficiently than int values — no boxing/unboxing required!
[Tip]Tip

Use enums any time you need a fixed set of constants. For example: choices on a menu, rounding modes, command line flags, etc.

public class ChessPiece {
  public static enum Figure {KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN};
  public static enum Color { WHITE, BLACK };

  private final Color color;
  private final Figure figure;

  public ChessPiece(Color color, Figure figure) {
    this.color = color;
    this.figure = figure;
  }

  public Figure getFigure() {
    return this.figure;
  }

  public Color getColor() {
    return this.color;
  }

  public String toString() {
    return this.color + " " + this.figure;
  }
}

15.8. EnumSet and EnumMap

  • The java.util package contains special-purpose Set and Map implementations called EnumSet and EnumMap.
  • EnumSet is a high-performance Set implementation for enums.

    • All of the members of an EnumSet must be of the same enum type.
    • EnumSets support iteration over ranges of enum types. For example:

      for (Day d : EnumSet.range(Day.MONDAY, Day.FRIDAY))
          System.out.println(d);
    • EnumSets can replace traditional bit flags:

      EnumSet.of(Style.BOLD, Style.ITALIC)
  • EnumMap is a high-performance Map implementation for use with enum keys.

    • Enum maps combine the richness and safety of the Map interface with speed approaching that of an array.
    • If you want to map an enum to a value, you should always use an EnumMap in preference to an array.

15.9. Annotations

  • Java 5 introduced annotations to the language, which provide metadata about a program that is not part of the program itself.

    • They have no direct effect on the operation of the code they annotate.
  • Annotations have a number of uses, including:

    Information for the compiler
    Annotations can be used by the compiler to detect errors or suppress warnings.
    Compile-time and deployment-time processing
    Software tools can process annotation information to generate code, XML files, etc.
    Runtime processing
    Libraries and frameworks can use annotations conditionally process classes and methods, and to inject objects into annotated classes and methods.

The Java annotation facility consists of:

  • Syntax for declaring annotation types
  • Syntax for annotating declarations
  • APIs for reading annotations reflectively
  • Class file representation for annotations
  • Annotation Processing Tool (apt)

15.10. Using Annotations

  • Annotations can be applied to a program’s declarations of classes, fields, methods, and other program elements.
  • The annotation appears first, usually on its own line, and may include elements with named or unnamed values:

    @Unfinished(
       owner = "Zaphod Beeblebrox",
       date = "5/11/2001"
    )
    class HeartOfGold { }
    
    @SuppressWarnings(value = "unchecked")
    void myMethod() { }
  • If there is just one element named "value", then the name may be omitted:

    @SuppressWarnings("unchecked")
    void myMethod() { }
  • If an annotation has no elements, the parentheses may be omitted:

    @Override
    void myMethod() { }

Annotation elements must be compile-time constant values.

Annotation elements may be primitive types, Strings, Classes, enums, annotations, or an array of a permitted type.

An array of values is supplied within braces, as in:

@SuppressWarnings({"unchecked","deprecation"})

15.11. Standard Java Annotations

The java.lang package defines three standard annotations:

@Override
Indicates that a method declaration is intended to override a method declaration in a base class. Compilers generate an error message if the method does not actually override a base class method (e.g., misspelled, wrong signature, etc.).
@Deprecated
Marks a feature that programmers should not use (e.g., replaced by a newer method, dangerous, etc.). Compilers generate a warning when code uses a deprecated feature.
@SuppressWarnings

Suppresses a compiler warning on the annotated element (and those contained within the element). You must provide a value element with a single String or an array of String values indicating the warnings to suppress. Supported values include:

  • all — all warnings
  • deprecation — warnings relative to deprecation
  • fallthrough — warnings relative to missing breaks in switch statements
  • hiding — warnings relative to locals that hide variable
  • serial — warnings relative to missing serialVersionUID field for a serializable class
  • unchecked — warnings relative to unchecked operations
  • unused — warnings relative to unused code