Getting Started

The Java programming language is a high-level language that can be characterized by all of the following buzzwords:

  • Simple
  • Object oriented
  • Distributed
  • Multithreaded
  • Dynamic
  • Architecture neutral
  • Portable
  • High performance
  • Robust
  • Secure

An overview of the software development process:

Figure showing MyProgram.java, compiler, MyProgram.class, Java VM, and My Program running on a computer.

all source code is first written in plain text files ending with the .java extension. Those source files are then compiled into .class files by the javac compiler. A .class file does not contain code that is native to your processor; it instead contains bytecodes — the machine language of the Java Virtual Machine1 (Java VM). The java launcher tool then runs your application with an instance of the Java Virtual Machine.

The Java platform has two components:

  • The Java Virtual Machine
  • The Java Application Programming Interface (API)
1
2
3
4
5
6
7
8
9
/**
 * The HelloWorldApp class implements an application that
 * simply prints "Hello World!" to standard output.
 */
class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello World!"); // Display the string.
    }
}
1
$ javac HelloWorldApp.java
1
$ java HelloWorldApp

Language Basics

Comments

1
2
/* comments */
// comments

Primitive Data Types

The Java programming language is statically-typed, which means that all variables must first be declared before they can be used. This involves stating the variable’s type and name

1
int gear = 1;    // holds numerical data, and has an initial value 

The eight primitive data types supported by the Java programming language are:

  • byte: The byte data type is an 8-bit signed two’s complement integer. The byte data type can be useful for saving memory in large arrays.
  • short: The short data type is a 16-bit signed two’s complement integer. You can use a short to save memory in large arrays, in situations where the memory savings actually matters.
  • int: By default, the int data type is a 32-bit signed two’s complement integer In Java SE 8 and later, you can use the int data type to represent an unsigned 32-bit integer. Use the Integer class to use int data type as an unsigned integer. Static methods like compareUnsigned, divideUnsigned etc have been added to the Integer class to support the arithmetic operations for unsigned integers.
  • long: The long data type is a 64-bit two’s complement integer. In Java SE 8 and later, you can use the long data type to represent an unsigned 64-bit long. Use this data type when you need a range of values wider than those provided by int. The Longclass also contains methods like compareUnsigned, divideUnsigned etc to support arithmetic operations for unsigned long.
  • float: As with the recommendations for byte and short, use a float (instead of double) if you need to save memory in large arrays of floating point numbers. This data type should never be used for precise values, such as currency. For that, you will need to use the java.math.BigDecimal class instead.
  • double: For decimal values, this data type is generally the default choice. As mentioned above, this data type should never be used for precise values, such as currency.
  • boolean: The boolean data type has only two possible values: true and false. Use this data type for simple flags that track true/false conditions.
  • char: The char data type is a single 16-bit Unicode character. It has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive).

Default Values

It’s not always necessary to assign a value when a field is declared. Fields that are declared but not initialized will be set to a reasonable default by the compiler. Generally speaking, this default will be zero or null, depending on the data type. Relying on such default values, however, is generally considered bad programming style.

Data Type Default Value (for fields)
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char ‘\u0000’
String (or any object) null
boolean false

Local variables are slightly different; the compiler never assigns a default value to an uninitialized local variable. If you cannot initialize your local variable where it is declared, make sure to assign it a value before you attempt to use it. Accessing an uninitialized local variable will result in a compile-time error.

Literals

You may have noticed that the new keyword isn’t used when initializing a variable of a primitive type.

Character and String Literals

Always use ‘single quotes’ for char literals and “double quotes” for String literals.

The Java programming language also supports a few special escape sequences for char and Stringliterals: \b (backspace), \t (tab), \n (line feed), \f (form feed), \r (carriage return), \" (double quote), \'(single quote), and \\ (backslash).

There’s also a special null literal that can be used as a value for any reference type. null may be assigned to any variable, except variables of primitive types. There’s little you can do with a null value beyond testing for its presence. Therefore, null is often used in programs as a marker to indicate that some object is unavailable.

Finally, there’s also a special kind of literal called a class literal, formed by taking a type name and appending “.class"; for example, String.class. This refers to the object (of type Class) that represents the type itself.

Variables and Constants

The Java programming language defines the following kinds of variables:

  • Instance Variables (Non-Static Fields) Technically speaking, objects store their individual states in “non-static fields”, that is, fields declared without the static keyword. Non-static fields are also known as instance variables because their values are unique to each instance of a class (to each object, in other words); the currentSpeed of one bicycle is independent from the currentSpeed of another.
  • Class Variables (Static Fields) A class variable is any field declared with the staticmodifier; this tells the compiler that there is exactly one copy of this variable in existence, regardless of how many times the class has been instantiated. A field defining the number of gears for a particular kind of bicycle could be marked as static since conceptually the same number of gears will apply to all instances. The code static int numGears = 6; would create such a static field. Additionally, the keyword final could be added to indicate that the number of gears will never change.
  • Local Variables a method will often store its temporary state in local variables. There is no special keyword designating a variable as local; that determination comes entirely from the location in which the variable is declared — which is between the opening and closing braces of a method. As such, local variables are only visible to the methods in which they are declared; they are not accessible from the rest of the class.
  • Parameters Recall that the signature for the mainmethod is public static void main(String[] args). Here, the args variable is the parameter to this method.

Naming

Every programming language has its own set of rules and conventions for the kinds of names that you’re allowed to use:

  • Variable names are case-sensitive. The convention, is to always begin your variable names with a letter, not “$” or “_”. `
  • When choosing a name for your variables, use full words instead of cryptic abbreviations. Also keep in mind that the name you choose must not be a keyword or reserved word.
  • If the name you choose consists of only one word, spell that word in all lowercase letters. If it consists of more than one word, capitalize the first letter of each subsequent word. The names gearRatio and currentGear are prime examples of this convention. If your variable stores a constant value, such as static final int NUM_GEARS = 6, the convention changes slightly, capitalizing every letter and separating subsequent words with the underscore character. By convention, the underscore character is never used elsewhere.

Constants

The keyword final indicates that you can assign to the variable once, and then its value is set once and for all. It is customary to name constants in all uppercase.

1
2
3
4
public static void main(String[] args)
{
	final double CM_PER_INCH = 2.54;
}

Set up a class constant with the keywords static final.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class Constants2
{
    public static final double CM_PER_INCH = 2.54;
    public static void main(String[] args)
    {
        double paperWidth = 8.5;
        double paperHeight = 11;
        System.out.println("Paper size in centimeters: " + paperWidth * CM_PER_INCH + " by " + paperHeight * CM_PER_INCH);
    }
}

Enumerated Types

1
enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };
1
Size s = Size.MEDIUM;

Operators

Operators Precedence
postfix expr++ expr--
unary ++expr --expr +expr -expr ~ !
multiplicative * / %
additive + -
shift << >> >>>
relational < > <= >= instanceof
equality == !=
bitwise AND &
bitwise exclusive OR ^
bitwise inclusive OR `
logical AND &&
logical OR `
ternary ? :
assignment `= += -= *= /= %= &= ^=

Strings

In addition to the eight primitive data types listed above, the Java programming language also provides special support for character strings via the java.lang.String class. Enclosing your character string within double quotes will automatically create a new String object.

1
String s = "this is a string";

String objects are immutable, which means that once created, their values cannot be changed. The String class is not technically a primitive data type, but considering the special support given to it by the language, you’ll probably tend to think of it as such.

Expressions, Statements, and Blocks

1
2
3
4
5
double aValue = 8933.234;  // declaration statement
aValue = 8933.234;   // assignment statement
aValue++;   // increment statement
System.out.println("Hello World!");   // method invocation statement
Bicycle myBike = new Bicycle();   // object creation statement
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class BlockDemo {
     public static void main(String[] args) {
          boolean condition = true;
          if (condition) { // begin block 1
               System.out.println("Condition is true.");
          } // end block one
          else { // begin block 2
               System.out.println("Condition is false.");
          } // end block 2
     }
}

Input and Output

Reading Input

1
2
import java.util.*;
Scanner in = new Scanner(System.in);
1
2
3
4
5
System.out.print("What is your name? ");

jshell> String name = in.nextLine();
Ye
name ==> "Ye"
1
String firstName = in.next();
1
2
System.out.print("How old are you? ");
int age = in.nextInt();
1
boolean hasNext()

The Scanner class is not suitable for reading a password from a console since the input is plainly visible to anyone.

1
2
3
Console cons = System.console();
String username = cons.readLine("User name: ");
char[] passwd = cons.readPassword("Password: ");

Formatting Output

1
System.out.printf("Hello, %s. Next year, you'll be %d", name, age);
Character Conversion Type Character Conversion Type
d decimal s String
x Hexadecimal integer c Character
o Octal integer b boolean
f float h
e tx Date and time
g %
a n

Date and Time Conversion Characters

Character Conversion Type Character Conversion Type
c Complete date and time D 02/09/2015
F 2015-02-09 T 18:05:19
r 06:05:19 pm
1
2
System.out.printf("%tc", new Date());  
// Mon Feb 09 18:05:19 PST 2015
1
2
System.out.printf("%1$s %2$tB %2$te, %2$tY", "Due date:", new Date());
// Due date: February 9, 2015

File Input and Output

To read from a file

1
Scanner in = new Scanner(Path.of("c:\\mydirectory\\myfile.txt"), StandardCharsets.UTF_8);

To write to a file

1
PrintWriter out = new PrintWriter("myfile.txt", StandardCharsets.UTF_8);

When you specify a relative file name, the file is located relative to the directory in which the Java virtual machine was started.

If you construct a Scanner with a file that does not exist or a PrintWriter with a file name that cannot be created, an exception occurs.

1
2
3
4
5
public static void main(String[] args) throws IOException
{
    Scanner in = new Scanner(Path.of("myfile.txt"), StandardCharsets.UTF_8);
    . . .
}

Control Flow Statements

The if-else Statements

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class IfElseDemo {
    public static void main(String[] args) {

        int testscore = 76;
        char grade;

        if (testscore >= 90) {
            grade = 'A';
        } else if (testscore >= 80) {
            grade = 'B';
        } else {
            grade = 'F';
        }
        System.out.println("Grade = " + grade);
    }
}

The switch Statement

The break statements are necessary because without them, statements in switch blocks fall through: All statements after the matching case label are executed in sequence, regardless of the expression of subsequent case labels, until a break statement is encountered.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class SwitchDemoFallThrough {

    public static void main(String[] args) {
        java.util.ArrayList<String> futureMonths =
            new java.util.ArrayList<String>();

        int month = 8;

        switch (month) {
            case 1:  futureMonths.add("January");
            case 2:  futureMonths.add("February");
            case 3:  futureMonths.add("March");
            case 4:  futureMonths.add("April");
            case 5:  futureMonths.add("May");
            case 6:  futureMonths.add("June");
            case 7:  futureMonths.add("July");
            case 8:  futureMonths.add("August");
            case 9:  futureMonths.add("September");
            case 10: futureMonths.add("October");
            case 11: futureMonths.add("November");
            case 12: futureMonths.add("December");
                     break;
            default: break;
        }

        if (futureMonths.isEmpty()) {
            System.out.println("Invalid month number");
        } else {
            for (String monthName : futureMonths) {
               System.out.println(monthName);
            }
        }
    }
}
/*
August
September
October
November
December
*/
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class StringSwitchDemo {

    public static int getMonthNumber(String month) {

        int monthNumber = 0;

        if (month == null) {
            return monthNumber;
        }

        switch (month.toLowerCase()) {
            case "january":
                monthNumber = 1;
                break;
            case "february":
                monthNumber = 2;
                break;
            // ...
            default: 
                monthNumber = 0;
                break;
        }

        return monthNumber;
    }

    public static void main(String[] args) {

        String month = "August";

        int returnedMonthNumber =
            StringSwitchDemo.getMonthNumber(month);

        if (returnedMonthNumber == 0) {
            System.out.println("Invalid month");
        } else {
            System.out.println(returnedMonthNumber);
        }
    }
}

This example checks if the expression in the switch statement is null. Ensure that the expression in any switch statement is not null to prevent a NullPointerException from being thrown.

This example Call class method getMonthNumber

The while and do-while Statements

1
2
3
while (expression) {
     statement(s)
}

The for Statement

1
2
3
for (initialization; termination; increment) {
    statement(s)
}

The three expressions of the for loop are optional; an infinite loop can be created as follows:

1
2
3
4
// infinite loop
for ( ; ; ) {
    // your code goes here
}

The for statement also has another form designed for iteration through Collections and arrays.

1
2
3
4
5
6
7
8
9
class EnhancedForDemo {
    public static void main(String[] args){
         int[] numbers = 
             {1,2,3,4,5,6,7,8,9,10};
         for (int item : numbers) {
             System.out.println("Count is: " + item);
         }
    }
}

Branching Statements

1
break;
1
continue;
1
return;

The return statement has two forms: one that returns a value, and one that doesn’t. The data type of the returned value must match the type of the method’s declared return value. When a method is declared void, use the form of return that doesn’t return a value.

Big Numbers

Use the static valueOf method to turn an ordinary number into a big number:

1
BigInteger a = BigInteger.valueOf(100);
1
BigInteger reallyBig = new BigInteger("2222322446294204455297398934619099672066669390");
1
2
BigInteger c = a.add(b); // c = a + b
BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); // d = c *
1
2
3
4
5
BigInteger add(BigInteger other)
BigInteger subtract(BigInteger other)
BigInteger multiply(BigInteger other)
BigInteger divide(BigInteger other)
BigInteger mod(BigInteger other)

Arrays

An array is a container object that holds a fixed number of values of a single type. The length of an array is established when the array is created. After creation, its length is fixed.

Declaring Arrays

1
2
// declares an array of integers
int[] anArray;
1
2
// allocates memory for 100 integers
int[] a = new int[100]; // or var a = new int[100];

Like declarations for variables of other types, an array declaration has two components: the array’s type and the array’s name. An array’s type is written as type[], where type is the data type of the contained elements; the brackets are special symbols indicating that this variable holds an array. The size of the array is not part of its type (which is why the brackets are empty). As with variables of other types, the declaration does not actually create an array; it simply tells the compiler that this variable will hold an array of the specified type.

Alternatively, you can use the shortcut syntax to create and initialize an array:

1
2
3
4
//  creating an array object and supplying initial values
int[] smallPrimes = { 2, 3, 5, 7, 11, 13 };
// reinitialize an array without creating a new variable. 
smallPrimes = new int[] { 17, 19, 23, 29, 31, 37 };

Accessing Array Elements

To find the number of elements of an array, use array.length.

1
2
String[] names = new String[10];
for (int i = 0; i < 10; i++) names[i] = "";
1
2
for (int i = 0; i < names.length; i++)
	System.out.println(names[i]);

The “for each” Loop

1
for (variable : collection) statement

The collection expression must be an array or an object of a class that implements the Iterable interface, such as ArrayList.

1
2
for (int element : a)
	System.out.println(element);
1
2
for (int i = 0; i < a.length; i++)
	System.out.println(a[i]);
1
System.out.println(Arrays.toString(a));

Array Copying

1
2
3
int[] smallPrimes = { 2, 3, 5, 7, 11, 13 };
int[] luckyNumbers = smallPrimes;
luckyNumbers[5] = 12; // now smallPrimes[5] is also 12
1
int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);

A common use of this method is to increase the size of an array.

1
luckyNumbers = Arrays.copyOf(luckyNumbers, 2 * luckyNumbers.length);

The additional elements are filled with 0 if the array contains numbers, false if the array contains boolean values.

The System class has an arraycopy method that you can use to efficiently copy data from one array into another:

1
2
public static void arraycopy(Object src, int srcPos,
                             Object dest, int destPos, int length)
1
2
3
4
char[] copyFrom = { 'd', 'e', 'c', 'a', 'f', 'f', 'e', 'i', 'n', 'a', 't', 'e', 'd' };
char[] copyTo = new char[7];
System.arraycopy(copyFrom, 2, copyTo, 0, 7);
System.out.println(new String(copyTo));   // caffein
1
2
3
char[] copyFrom = {'d', 'e', 'c', 'a', 'f', 'f', 'e', 'i', 'n', 'a', 't', 'e', 'd'};
char[] copyTo = java.util.Arrays.copyOfRange(copyFrom, 2, 9);
System.out.println(new String(copyTo));

Some other useful operations provided by methods in the java.util.Arrays class, are:

  • Searching an array for a specific value to get the index at which it is placed (the binarySearch method).
  • Comparing two arrays to determine if they are equal or not (the equals method).
  • Filling an array to place a specific value at each index (the fill method).

Command-Line Parameters

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Message
{
    public static void main(String[] args)
    {
        if (args.length == 0 || args[0].equals("-h"))
            System.out.print("Hello,");
        else if (args[0].equals("-g"))
            System.out.print("Goodbye,");
        // print the other command-line arguments
        for (int i = 1; i < args.length; i++)
            System.out.print(" " + args[i]);
        System.out.println("!");
    }
}
1
java Message -g cruel world
1
2
3
args[0]: "-g"
args[1]: "cruel"
args[2]: "world"

Array Sorting

Sorting an array into ascending order. This can be done either sequentially, using the sort method, or concurrently, using the parallelSort method introduced in Java SE 8. Parallel sorting of large arrays on multiprocessor systems is faster than sequential array sorting.

1
2
3
int[] a = new int[10000];
. . .
Arrays.sort(a)      //  QuickSort algorithm
1
2
3
4
5
6
7
8
static String toString(xxx[] a) 
static xxx[] copyOf(xxx[] a, int end) 
static xxx[] copyOfRange(xxx[] a, int start, int end) 
static void sort(xxx[] a)
static int binarySearch(xxx[] a, xxx v)
static int binarySearch(xxx[] a, int start, int end, xxx v)
static void fill(xxx[] a, xxx v)
static boolean equals(xxx[] a, xxx[] b)

Multidimensional Arrays

declare an array of arrays (also known as a multidimensional array) by using two or more sets of brackets, such as String[][] names. A consequence of this is that the rows are allowed to vary in length

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class MultiDimArrayDemo {
    public static void main(String[] args) {
        String[][] names = {
            {"Mr. ", "Mrs. ", "Ms. "},
            {"Smith", "Jones"}
        };
        // Mr. Smith
        System.out.println(names[0][0] + names[1][0]);
        // Ms. Jones
        System.out.println(names[0][2] + names[1][1]);
    }
}

Ragged Arrays

It is also easy to make “ragged” arrays—that is, arrays in which different rows have different lengths.

1
2
3
int[][] odds = new int[NMAX + 1][];
for (int n = 0; n <= NMAX; n++)
	odds[n] = new int[n + 1];
1
2
3
4
5
6
7
for (int n = 0; n < odds.length; n++)
    for (int k = 0; k < odds[n].length; k++)
    {
        // compute lotteryOdds
        . . .
        odds[n][k] = lotteryOdds;
    }

Numbers and Strings

Numbers

When working with numbers, most of the time you use the primitive types in your code. There are, however, reasons to use objects in place of primitives, and the Java platform provides wrapper classes for each of the primitive data types. These classes “wrap” the primitive in an object. The Java compiler automatically wraps (boxes) primitives for you when necessary and unboxes them, again when necessary.

1
2
3
4
int ==> Integer
float ==> Float
double ==> Double
byte ==>  Byte

The following table lists the instance methods that all the subclasses of the Number class implement.

1
2
3
4
5
6
7
// Converts the value of this Number object to the primitive data type returned.
byte byteValue()
short shortValue()
int intValue()
long longValue()
float floatValue()
double doubleValue()
1
2
3
4
5
6
7
// Compares this Number object to the argument.
int compareTo(Byte anotherByte)
int compareTo(Double anotherDouble)
int compareTo(Float anotherFloat)
int compareTo(Integer anotherInteger)
int compareTo(Long anotherLong)
int compareTo(Short anotherShort)
1
2
// Determines whether this number object is equal to the argument. 
boolean equals(Object obj)

Integer class methods for converting numbers to and from strings and for converting between number systems.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Decodes a string into an integer. 
static Integer Integer.decode(String s)

// Returns an integer (decimal only).
static int Integer.parseInt(String s)

// radix equals 10, 2, 8, or 16 respectively
static int Integer.parseInt(String s, int radix)

// Returns a String object representing the value of this Integer.
String integerInstance.toString()

// Returns a String object representing the specified integer.
static String Integer.toString(int i)

// Returns an Integer object holding the value of the specified primitive.
static Integer Integer.valueOf(int i)

// Returns an Integer object holding the value of the specified string representation.
static Integer Integer.valueOf(String s)

// Returns an Integer object holding the integer value of the specified string representation, parsed with the value of radix.
static Integer Integer.valueOf(String s, int radix)

Formatting Numeric Print Output

You can use format or printf anywhere in your code where you have previously been using print or println.

1
2
3
4
int i = 461012;
System.out.format("The value of i is: %d%n", i);
System.out.printf("The value of i is: %d%n", i);
System.out.println("");
Converter Explanation
d A decimal integer.
f A float.
n A new line character appropriate to the platform running the application. You should always use %n, rather than \n.

Beyond Basic Arithmetic

1
2
3
4
5
6
7
8
import static java.lang.Math.*;

double a = -191.635;
double b = 43.74;
int c = 16, d = 45;

double abs_a = Math.abs(a)
double ceil_b = Math.ceil(b)
1
int number = (int)(Math.random() * 10);    // 0.0 <= Math.random() < 1.0

Characters

1
2
3
4
5
char ch = 'a'; 
// Unicode for uppercase Greek omega character
char uniChar = '\u03A9';
// an array of chars
char[] charArray = { 'a', 'b', 'c', 'd', 'e' };

useful class methods in the Character class

1
2
3
4
5
6
7
8
boolean isLetter(char ch)
boolean isDigit(char ch)
boolean isWhitespace(char ch)	
boolean isUpperCase(char ch)
boolean isLowerCase(char ch)	
char toUpperCase(char ch)
char toLowerCase(char ch)	
toString(char ch)	

Escape Sequences

1
2
3
4
5
6
7
8
\t	Insert a tab 
\b	Insert a backspace 
\n	Insert a newline 
\r	Insert a carriage return 
\f	Insert a formfeed 
\'	Insert a single quote character
\"	Insert a double quote character 
\\	Insert a backslash character 

Strings

In the Java programming language, strings are objects. The String class is immutable.

1
2
3
String greeting = "Hello world!";
char[] helloArray = { 'h', 'e', 'l', 'l', 'o', '.' };
String helloString = new String(helloArray);

useful instance methods

1
2
3
4
5
length()          // returns the number of characters contained in the string object
charAt(i)         // returns the ith character in the string, counting from 0
getChars(pos, len, destChatArray, startPos) // convert a string into an array of characters
concat(string2)    // concatenating two strings
String.format()        // returns a String object rather than a PrintStream object.

The + operator is widely used in print statements.

1
2
3
4
5
6
7
8
String fs;
fs = String.format("The value of the float " +
                   "variable is %f, while " +
                   "the value of the " + 
                   "integer variable is %d, " +
                   " and the string is %s",
                   floatVar, intVar, stringVar);
System.out.println(fs);

Converting Between Numbers and Strings

The Number subclasses that wrap primitive numeric types ( Byte, Integer, Double, Float, Long, and Short) each provide a class method named valueOf that converts a string to an object of that type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class ValueOfDemo {
    public static void main(String[] args) {
        // this program requires two arguments on the command line 
        if (args.length == 2) {
            // convert strings to numbers
            float a = (Float.valueOf(args[0])).floatValue(); 
            float b = (Float.valueOf(args[1])).floatValue();
        } else {
            System.out.println("This program " +
                "requires two command-line arguments.");
        }
    }
}

Each of the Number subclasses that wrap primitive numeric types also provides a parseXXXX() method (for example, parseFloat()) that can be used to convert strings to primitive numbers. Since a primitive type is returned instead of an object, the parseFloat() method is more direct than the valueOf() method.

1
2
float a = Float.parseFloat(args[0]);
float b = Float.parseFloat(args[1]);

Converting Numbers to Strings

1
2
3
int i;
// Concatenate "i" with an empty string; conversion is handled for you.
String s1 = "" + i;
1
2
// The valueOf class method.
String s2 = String.valueOf(i);
1
2
3
4
int i;
double d;
String s3 = Integer.toString(i); 
String s4 = Double.toString(d); 

Manipulating Characters in a String

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class Filename {
    private String fullPath;
    private char pathSeparator, 
                 extensionSeparator;

    public Filename(String str, char sep, char ext) {
        fullPath = str;
        pathSeparator = sep;
        extensionSeparator = ext;
    }
    
    public String extension() {
        int dot = fullPath.lastIndexOf(extensionSeparator);
        return fullPath.substring(dot + 1);                     // substring
    }

    // gets filename without extension
    public String filename() {
        int dot = fullPath.lastIndexOf(extensionSeparator);     // indexOf, lastIndexOf
        int sep = fullPath.lastIndexOf(pathSeparator);
        return fullPath.substring(sep + 1, dot);                // substring
    }

    public String path() {
        int sep = fullPath.lastIndexOf(pathSeparator);
        return fullPath.substring(0, sep);                      // substring
    }

    public char charAt(int i) {
        String filename = this.filename();
        return filename.charAt(i);                              // chartAt
    }

    public String toUpperCase() {
        String filename = this.filename();
        filename = filename.trim();                             // trim
        return filename.toUpperCase();                          // toUpperCase, toLowerCase
    }

    public String directoryName() {
        String path = this.path();
        String[] tokens = path.split("/");                      // split
        return tokens[tokens.length - 1];
    }

    public String contains(String s) {
        Boolean ret = fullPath.contains(s);                     // contains
        return String.valueOf(ret);
    }

    public String replace(String target, String replacement) {
        return fullPath.replace(target, replacement);           // replace
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class FilenameDemo {
    public static void main(String[] args) {
        final String FPATH = "/home/user/index.html";
        Filename myHomePage = new Filename(FPATH, '/', '.');
        System.out.println("Extension = " + myHomePage.extension());
        System.out.println("Filename = " + myHomePage.filename());
        System.out.println("Path = " + myHomePage.path());
        System.out.println("Filename first letter = " + myHomePage.charAt(0));
        System.out.println("Filename uppercase = " + myHomePage.toUpperCase());
        System.out.println("Directory name = " + myHomePage.directoryName());
        System.out.println("Full path contains user = " + myHomePage.contains("user"));
        System.out.println("Test change extension = " + myHomePage.replace("html", "text"));
    }
}

Comparing Strings and Portions of Strings

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
boolean endsWith(String suffix)
boolean startsWith(String prefix)
boolean startsWith(String prefix, int offset)        
int compareTo(String anotherString)                   
int compareToIgnoreCase(String str)                    
boolean equals(Object anObject)                  
boolean equalsIgnoreCase(String anotherString)     
boolean regionMatches(int toffset, String other, int ooffset, int len)
boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
boolean matches(String regex)        
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class RegionMatchesDemo {
    public static void main(String[] args) {
        String searchMe = "Green Eggs and Ham";
        String findMe = "Eggs";
        int searchMeLength = searchMe.length();
        int findMeLength = findMe.length();
        boolean foundIt = false;
        for (int i = 0; 
             i <= (searchMeLength - findMeLength);
             i++) {
           if (searchMe.regionMatches(i, findMe, 0, findMeLength)) {
              foundIt = true;
              System.out.println(searchMe.substring(i, i + findMeLength));
              break;
           }
        }
        if (!foundIt)
            System.out.println("No match found.");
    }
}

The StringBuilder Class

1
2
3
4
5
6
StringBuilder append(String s)
StringBuilder insert(int offset, String s)
StringBuilder replace(int start, int end, String s)
void setCharAt(int index, char c)
StringBuilder reverse()
String toString()
1
2
3
4
// creates empty builder, capacity 16
StringBuilder sb = new StringBuilder();
// adds 9 character string at beginning
sb.append("Greetings");
1
2
3
4
5
6
7
8
public class StringBuilderDemo {
    public static void main(String[] args) {
        String palindrome = "Dot saw I was Tod";
        StringBuilder sb = new StringBuilder(palindrome);
        sb.reverse();                                       // reverse it
        System.out.println(sb);
    }
}

Autoboxing and Unboxing

Converting a primitive value (an int, for example) into an object of the corresponding wrapper class (Integer) is called autoboxing. The Java compiler applies autoboxing when a primitive value is:

  • Passed as a parameter to a method that expects an object of the corresponding wrapper class.
  • Assigned to a variable of the corresponding wrapper class.

Converting an object of a wrapper type (Integer) to its corresponding primitive (int) value is called unboxing. The Java compiler applies unboxing when an object of a wrapper class is:

  • Passed as a parameter to a method that expects a value of the corresponding primitive type.
  • Assigned to a variable of the corresponding primitive type.

Classes and Objects

Object-Oriented Programming Concepts

teaches you the core concepts behind object-oriented programming: objects, messages, classes, and inheritance. This lesson ends by showing you how these concepts translate into code. Feel free to skip this lesson if you are already familiar with object-oriented programming.

Real-world objects share two characteristics: They all have state and behavior. A software object have fields (state) and methods (behavior). Hiding internal state and requiring all interaction to be performed through an object’s methods is known as data encapsulation — a fundamental principle of object-oriented programming.

Classes

Declaring Classes

modifiers, constructor, fields, methods

1
2
3
4
modifiers class MyClass extends MySuperClass implements YourInterface {
    // field, constructor, and
    // method declarations
}

naming rules: the first letter of a class name should be capitalized, and the first (or only) word in a method name should be a verb.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Bicycle {
        
    // the Bicycle class has three fields
    private int cadence;
    private int gear;
    private int speed;
        
    // the Bicycle class has one constructor
    public Bicycle(int startCadence, int startSpeed, int startGear) {
        gear = startGear;
        cadence = startCadence;
        speed = startSpeed;
    }
    
    // the Bicycle class has six methods
    public int getCadence() {
        return cadence;
    }
        
    public void setCadence(int newValue) {
        cadence = newValue;
    }
        
    public int getGear() {
        return gear;
    }
    
    public void setCadence(int newValue) {
        cadence = newValue;
    }
        
    public void setGear(int newValue) {
        gear = newValue;
    }
        
    public void applyBrake(int decrement) {
        speed -= decrement;
    }
        
    public void speedUp(int increment) {
        speed += increment;
    }
        
}
Overloading Methods

The Java programming language supports overloading methods, and Java can distinguish between methods with different method signatures. This means that methods within a class can have the same name if they have different parameter lists (there are some qualifications to this that will be discussed in the lesson titled “Interfaces and Inheritance”).

You cannot declare more than one method with the same name and the same number and type of arguments, because the compiler cannot tell them apart.

Constructors

constructor use the name of the class and have no return type.

You don’t have to provide any constructors for your class, the compiler automatically provides a no-argument, default constructor for any class without constructors. This default constructor will call the no-argument constructor of the superclass. In this situation,

To create a new Bicycle object called myBike, a constructor is called by the new operator:

1
Bicycle myBike = new Bicycle(30, 0, 8);

All classes have at least one constructor. If a class does not explicitly declare any, the Java compiler automatically provides a no-argument constructor, called the default constructor. This default constructor calls the class parent’s no-argument constructor, or the Object constructor if the class has no other parent. If the parent has no constructor (Object does have one), the compiler will reject the program.

Using Objects

Code that is outside the object’s class must use an object reference or expression, followed by the dot (.) operator, followed by a simple field name, as in:

1
objectReference.fieldName

You also use an object reference to invoke an object’s method. You append the method’s simple name to the object reference, with an intervening dot operator (.).

1
objectReference.methodName(argumentList);

The Garbage Collector

Some object-oriented languages require that you keep track of all the objects you create and that you explicitly destroy them when they are no longer needed. Managing memory explicitly is tedious and error-prone. The Java platform allows you to create as many objects as you want (limited, of course, by what your system can handle), and you don’t have to worry about destroying them. The Java runtime environment deletes objects when it determines that they are no longer being used. This process is called garbage collection.

An object is eligible for garbage collection when there are no more references to that object. References that are held in a variable are usually dropped when the variable goes out of scope. Or, you can explicitly drop an object reference by setting the variable to the special value null. Remember that a program can have multiple references to the same object; all references to an object must be dropped before the object is eligible for garbage collection.

The Java runtime environment has a garbage collector that periodically frees the memory used by objects that are no longer referenced. The garbage collector does its job automatically when it determines that the time is right.

Declaring Classes

subclass: extends, super

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class MountainBike extends Bicycle {
        
    // the MountainBike subclass has one field
    public int seatHeight;

    // the MountainBike subclass has one constructor
    public MountainBike(int startHeight, int startCadence,
                        int startSpeed, int startGear) {
        super(startCadence, startSpeed, startGear);
        seatHeight = startHeight;
    }   
        
    // the MountainBike subclass has one method
    public void setHeight(int newValue) {
        seatHeight = newValue;
    }   

}
Passing Information to a Method or a Constructor

Arbitrary Number of Arguments

To use varargs, you follow the type of the last parameter by an ellipsis (three dots, …), then a space, and the parameter name.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public Polygon polygonFrom(Point... corners) {
    int numberOfSides = corners.length;
    double squareOfSide1, lengthOfSide1;
    squareOfSide1 = (corners[1].x - corners[0].x)
                     * (corners[1].x - corners[0].x) 
                     + (corners[1].y - corners[0].y)
                     * (corners[1].y - corners[0].y);
    lengthOfSide1 = Math.sqrt(squareOfSide1);

    // more method body code follows that creates and returns a 
    // polygon connecting the Points
}

Passing Primitive Data Type Arguments

This means that any changes to the values of the parameters exist only within the scope of the method. When the method returns, the parameters are gone and any changes to them are lost.

Passing Reference Data Type Arguments

Reference data type parameters, such as objects, are also passed into methods by value. This means that when the method returns, the passed-in reference still references the same object as before. However, the values of the object’s fields can be changed in the method, if they have the proper access level.

1
2
3
4
5
6
7
8
public void moveCircle(Circle circle, int deltaX, int deltaY) {
    // code to move origin of circle to x+deltaX, y+deltaY
    circle.setX(circle.getX() + deltaX);
    circle.setY(circle.getY() + deltaY);
        
    // code to assign a new reference to circle
    circle = new Circle(0, 0);
}
1
moveCircle(myCircle, 23, 56)

Inside the method, circle initially refers to myCircle. The method changes the x and y coordinates of the object that circle references (i.e., myCircle) by 23 and 56, respectively. These changes will persist when the method returns. Then circle is assigned a reference to a new Circle object with x = y = 0. This reassignment has no permanence, however, because the reference was passed in by value and cannot change. Within the method, the object pointed to by circle has changed, but, when the method returns, myCircle still references the same Circle object as before the method was called.

Object

Creating Objects

1
Point originOne = new Point(23, 94);
  1. Declaration: The code set in bold are all variable declarations that associate a variable name with an object type.
  2. Instantiation: The new keyword is a Java operator that creates the object.
  3. Initialization: The new operator is followed by a call to a constructor, which initializes the new object.

More on Classes

Returning a Value from a Method

A method can return a primitive type or a reference type.

When a method uses a class name as its return type, the class of the type of the returned object must be either a subclass of, or the exact class of, the return type.

This technique, called covariant return type, means that the return type is allowed to vary in the same direction as the subclass.

Using the this Keyword

Within an instance method or a constructor, this is a reference to the current object — the object whose method or constructor is being called. You can refer to any member of the current object from within an instance method or a constructor by using this.

Using this with a Constructor

From within a constructor, you can also use the this keyword to call another constructor in the same class. Doing so is called an explicit constructor invocation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class Rectangle {
    private int x, y;
    private int width, height;
        
    public Rectangle() {
        this(0, 0, 1, 1);
    }
    public Rectangle(int width, int height) {
        this(0, 0, width, height);
    }
    public Rectangle(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }
    ...
}

Controlling Access to Members of a Class

Access level modifiers determine whether other classes can use a particular field or invoke a particular method. There are two levels of access control:

  • At the top level—public, or package-private (no explicit modifier).
  • At the member level—public, private, protected, or package-private (no explicit modifier).

A class may be declared with the modifier public, in which case that class is visible to all classes everywhere. If a class has no modifier (the default, also known as package-private), it is visible only within its own package (packages are named groups of related classes — you will learn about them in a later lesson.)

At the member level, you can also use the public modifier or no modifier (package-private) just as with top-level classes, and with the same meaning. For members, there are two additional access modifiers: private and protected. The private modifier specifies that the member can only be accessed in its own class. The protected modifier specifies that the member can only be accessed within its own package (as with package-private) and, in addition, by a subclass of its class in another package.

Understanding Class Members

  • Class Variables

    Fields that have the static modifier in their declaration are called static fields or class variables. They are associated with the class, rather than with any object. Every instance of the class shares a class variable, which is in one fixed location in memory. Any object can change the value of a class variable, but class variables can also be manipulated without creating an instance of the class.

    Class variables are referenced by the class name itself, as in

    1
    
    Bicycle.numberOfBicycles
    

    You can use the Bicycle constructor to set the id instance variable and increment the numberOfBicycles class variable:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    public class Bicycle {
    
        private int cadence;
        private int gear;
        private int speed;
        private int id;
        // add a class variable for the number of Bicycle objects instantiated
        private static int numberOfBicycles = 0;
    
        public Bicycle(int startCadence, int startSpeed, int startGear){
            gear = startGear;
            cadence = startCadence;
            speed = startSpeed;
    
            // increment number of Bicycles and assign ID number
            id = ++numberOfBicycles;
        }
    
        // new method to return the ID instance variable
        public int getID() {
            return id;
        }
    }
    
  • Class Methods

    The Java programming language supports static methods as well as static variables. Static methods, which have the static modifier in their declarations, should be invoked with the class name, without the need for creating an instance of the class, as in

    1
    
    ClassName.methodName(args)
    

    A common use for static methods is to access static fields.

    1
    2
    3
    
    public static int getNumberOfBicycles() {
        return numberOfBicycles;
    }
    
  • Constants

    The static modifier, in combination with the final modifier, is also used to define constants. The final modifier indicates that the value of this field cannot change.

    1
    
    static final double PI = 3.141592653589793;
    

    Constants defined in this way cannot be reassigned, and it is a compile-time error if your program tries to do so. By convention, the names of constant values are spelled in uppercase letters. If the name is composed of more than one word, the words are separated by an underscore (_).

Initializing Fields

If initialization requires some logic (for example, error handling or a for loop to fill a complex array), simple assignment is inadequate. Instance variables can be initialized in constructors, where error handling or other logic can be used. To provide the same capability for class variables, the Java programming language includes static initialization blocks.

  • Static Initialization Blocks

    1
    2
    3
    4
    5
    6
    7
    
    class Whatever {
        public static varType myVar = initializeClassVariable();
    
        private static varType initializeClassVariable() {
            // initialization code goes here
        }
    }
    
  • Initializing Instance Members

    A final method cannot be overridden in a subclass.

    1
    2
    3
    4
    5
    6
    7
    
    class Whatever {
        private varType myVar = initializeInstanceVariable();
    
        protected final varType initializeInstanceVariable() {
            // initialization code goes here
        }
    }
    

Packages

Packages are a feature of the Java programming language that help you to organize and structure your classes and their relationships to one another.

A package is a namespace that organizes a set of related classes and interfaces.

The Java Platform API Specification contains the complete listing for all packages, interfaces, classes, fields, and methods supplied by the Java SE platform. Load the page in your browser and bookmark it. As a programmer, it will become your single most important piece of reference documentation.

Creating and Using Packages

Creating a Package
1
package packageName;

must be the first line in the source file.

If you put multiple types in a single source file, only one can be public, and it must have the same name as the source file. For example, you can define public class Circle in the file Circle.java, define public interface Draggable in the file Draggable.java, define public enum Day in the file Day.java, and so forth.

Naming a Package

Naming Conventions: Package names are written in all lower case to avoid conflict with the names of classes or interfaces.

perhaps by including the region or the project name after the company name (for example, com.example.region.mypackage).

Packages in the Java language itself begin with java. or javax.

Using Package Members
  • Referring to a Package Member by Its Qualified Name

    use fully qualified name when name ambiguities

    1
    
    packagename.TypeName
    
  • Importing a Package Member

    1
    
    import packagename.TypeName;
    
  • Importing an Entire Package

    1
    
    import packagename.*;
    

At first, **packages appear to be hierarchical, but they are not. **The prefix is used for a number of related packages to make the relationship evident, but not to show inclusion.

you must import both packages with all their files:

1
2
import java.awt.*;
import java.awt.color.*;

The Static Import Statement allow you access to static final fields (constants) and static methods from one or two classes.

1
2
import static java.lang.Math.PI;
import static mypackage.MyConstants.*;
Managing Source and Class Files

Documentation Comments

Comment Insertion

  • Modules
  • Packages
  • Public classes and interfaces
  • Public and protected fields
  • Public and protected constructors and methods

You can (and should) supply a comment for each of these features.

Class Comments

The class comment must be placed after any import statements, directly before the class definition.

Method Comments

Each method comment must immediately precede the method that it describes.

In addition to the general-purpose tags, you can use the following tags:

  • @param variable description
  • @return description
  • @throws class description

Field Comments

You only need to document public fields—generally that means static constants.

General Comments

  • @author name

  • @version text

  • @since text

  • @deprecated text

  • @see reference

Inheritance

In the Java language, classes can be derived from other classes, thereby inheriting fields and methods from those classes. In the absence of any other explicit superclass, every class is implicitly a subclass of Object.

Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.

A subclass inherits all of the public and protected members of its parent, no matter what package the subclass is in. If the subclass is in the same package as its parent, it also inherits the package-private members of the parent.

  • You can write a new instance method in the subclass that has the same signature as the one in the superclass, thus overriding it.
  • You can write a new static method in the subclass that has the same signature as the one in the superclass, thus hiding it.
  • You can write a subclass constructor that invokes the constructor of the superclass, either implicitly or by using the keyword super.
1
2
3
4
5
6
7
8
9
public class Bicycle {
        
    // the Bicycle class has one constructor
    public Bicycle(int startCadence, int startSpeed, int startGear) {
        gear = startGear;
        cadence = startCadence;
        speed = startSpeed;
    }  
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class MountainBike extends Bicycle {

    // the MountainBike subclass adds one field
    privite int seatHeight;
    
    // the MountainBike subclass has one constructor
    public MountainBike(int startHeight,
                        int startCadence,
                        int startSpeed,
                        int startGear) {
        super(startCadence, startSpeed, startGear);
        seatHeight = startHeight;
    }  
    
    // the MountainBike subclass adds one method
}

Casting Objects

Casting shows the use of an object of one type in place of another type, among the objects permitted by inheritance and implementations. For example, if we write

1
Object obj = new MountainBike();

then obj is both an Object and a MountainBike (until such time as obj is assigned another object that is not a MountainBike). This is called implicit casting.

1
MountainBike myBike = obj;

we would get a compile-time error because obj is not known to the compiler to be a MountainBike. However, we can tell the compiler that we promise to assign a MountainBike to obj by explicit casting:

1
MountainBike myBike = (MountainBike)obj;

This cast inserts a runtime check that obj is assigned a MountainBike so that the compiler can safely assume that obj is a MountainBike.

You can make a logical test as to the type of a particular object using the instanceof operator. This can save you from a runtime error owing to an improper cast.

1
2
3
if (obj instanceof MountainBike) {
    MountainBike myBike = (MountainBike)obj;
}

Here the instanceof operator verifies that obj refers to a MountainBike so that we can make the cast with knowledge that there will be no runtime exception thrown.

Overriding and Hiding Methods、Overloade Methods

Defining a Method with the Same Signature as a Superclass’s Method:

Superclass Instance Method Superclass Static Method
Subclass Instance Method Overrides Generates a compile-time error
Subclass Static Method Generates a compile-time error Hides
  • Instance Methods

    An overriding method can also return a subtype of the type returned by the overridden method. This subtype is called a covariant return type.

    When overriding a method, you might want to use the @Override annotation that instructs the compiler that you intend to override a method in the superclass.

  • Static Methods:

    The distinction between hiding a static method and overriding an instance method has important implications:

    • The version of the overridden instance method that gets invoked is the one in the subclass.

    • The version of the hidden static method that gets invoked depends on whether it is invoked from the superclass or the subclass.

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      
      public class Animal {
          public static void testClassMethod() {
              System.out.println("The static method in Animal");
          }
          public void testInstanceMethod() {
              System.out.println("The instance method in Animal");
          }
      }
      public class Cat extends Animal {
          public static void testClassMethod() {
              System.out.println("The static method in Cat");
          }
          public void testInstanceMethod() {
              System.out.println("The instance method in Cat");
          }
      
          public static void main(String[] args) {
              Cat myCat = new Cat();
              Animal myAnimal = myCat;
              Animal.testClassMethod();   // The static method in Animal
              myAnimal.testInstanceMethod();  // The instance method in Cat
          }
      }
      
  • Interface Methods

    Default methods and abstract methods in interfaces are inherited like instance methods. Static methods in interfaces are never inherited.

    Instance methods are preferred over interface default methods.

In a subclass, you can overload the methods inherited from the superclass. Such overloaded methods neither hide nor override the superclass instance methods—they are new methods, unique to the subclass.

Polymorphism

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class TestBikes {
  public static void main(String[] args){
    Bicycle bike01, bike02, bike03;

    bike01 = new Bicycle(20, 10, 1);
    bike02 = new MountainBike(20, 10, 5, "Dual");
    bike03 = new RoadBike(40, 20, 8, 23);

    bike01.printDescription();
    bike02.printDescription();
    bike03.printDescription();
  }
}

Using the Keyword super

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Superclass {
    public void printMethod() {
        System.out.println("Printed in Superclass.");
    }
}
public class Subclass extends Superclass {
    // overrides printMethod in Superclass
    public void printMethod() {
        super.printMethod();
        System.out.println("Printed in Subclass");
    }
    public static void main(String[] args) {
        Subclass s = new Subclass();
        s.printMethod();    
    }
}
1
2
3
4
5
6
7
public MountainBike(int startHeight, 
                    int startCadence,
                    int startSpeed,
                    int startGear) {
    super(startCadence, startSpeed, startGear);
    seatHeight = startHeight;
}   

If a subclass constructor invokes a constructor of its superclass, either explicitly or implicitly, you might think that there will be a whole chain of constructors called, all the way back to the constructor of Object, it is called constructor chaining

Object as a Superclass

  • 1
    
    protected Object clone() throws CloneNotSupportedException
    

    The simplest way to make your class cloneable is to add implements Cloneable to your class’s declaration. then your objects can invoke the clone() method.

    An object contains a reference to an external object, say ObjExternal, you may need to override clone() to get correct behavior.

  • 1
    
    public boolean equals(Object obj)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    public class Book {
        ...
        public boolean equals(Object obj) {
            if (obj instanceof Book)
                return ISBN.equals((Book)obj.getISBN()); 
            else
                return false;
        }
    }
    

    If you override equals(), you must override hashCode() as well.

  • 1
    
    protected void finalize() throws Throwable
    

    you can override finalize() to do cleanup, such as freeing resources.

  • 1
    
    public final Class getClass()
    

    You cannot override getClass.

    1
    2
    3
    4
    
    void printClassName(Object obj) {
        System.out.println("The object's" + " class is " +
            obj.getClass().getSimpleName());
    }
    
  • 1
    
    public int hashCode()
    

    The value returned by hashCode() is the object’s hash code, which is the object’s memory address in hexadecimal.

    By definition, if two objects are equal, their hash code must also be equal. If you override the equals() method, you change the way two objects are equated and Object’s implementation of hashCode() is no longer valid. Therefore, if you override the equals() method, you must also override the hashCode() method as well.

  • 1
    
    public String toString()
    

    You should always consider overriding the toString() method in your classes. The Object’s toString() method returns a String representation of the object, which is very useful for debugging.

    1
    
    System.out.println(firstBook.toString());
    

Writing Final Classes and Methods

You can declare some or all of a class’s methods final. You use the final keyword in a method declaration to indicate that the method cannot be overridden by subclasses.

1
2
3
4
5
6
7
8
class ChessAlgorithm {
    enum ChessPlayer { WHITE, BLACK }
    ...
    final ChessPlayer getFirstPlayer() {
        return ChessPlayer.WHITE;
    }
    ...
}

You might wish to make a method final if it has an implementation that should not be changed and it is critical to the consistent state of the object.

**Methods called from constructors should generally be declared final. **Note that you can also declare an entire class final. A class that is declared final cannot be subclassed.

Abstract Methods and Classes

An abstract class is a class that is declared abstract—it may or may not include abstract methods. Abstract classes cannot be instantiated, but they can be subclassed.

1
2
3
4
5
public abstract class GraphicObject {
   // declare fields
   // declare nonabstract methods
   abstract void draw();
}

When an abstract class is subclassed, the subclass usually provides implementations for all of the abstract methods in its parent class. However, if it does not, then the subclass must also be declared abstract.

Abstract Classes Compared to Interfaces:

  • similar:
    • cannot instantiate
    • may contain a mix of methods declared with or without an implementation
  • difference:
    • abstract classes, you can declare fields that are not static and final, and define public, protected, and private concrete methods.
    • interfaces, all fields are automatically public, static, and final, and all methods that you declare or define (as default methods) are public.
    • you can extend only one class, whether or not it is abstract, whereas you can implement any number of interfaces.

It is possible to define a class that does not implement all of the interface’s methods, provided that the class is declared to be abstract.

Enumeration Classes

Reflection

A program that can analyze the capabilities of classes is called reflective. The reflection mechanism is extremely powerful. As the next sections show, you can use it to

  • Analyze the capabilities of classes at runtime
  • Inspect objects at runtime—for example, to write a single toString method that works for all classes
  • Implement generic array manipulation code
  • Take advantage of Method objects that work just like function pointers in languages such as C++

The Class Class

1
2
3
Employee e;
. . .
Class cl = e.getClass();
1
System.out.println(e.getClass().getName() + " " + e.getName());

If the class is in a package, the package name is part of the class name:

1
2
3
var generator = new Random();
Class cl = generator.getClass();
String name = cl.getName(); // name is set to "java.util.Random"

You can obtain a Class object corresponding to a class name by using the static forName method.

1
2
3
4
5
6
try {
    String className = "java.util.Random";
    Class cl = Class.forName(className);
} catch(ClassNotFoundException e) {
   e.printStackTrace();
}

A third method for obtaining an object of type Class is a convenient shorthand. If T is any Java type (or the void keyword), then T.class is the matching class object.

1
2
3
Class cl1 = Random.class; // if you import java.util.*;
Class cl2 = int.class;
Class cl3 = Double[].class;

use the == operator to compare class objects.

1
if (e.getClass() == Employee.class) . . .

use the newInstance method to construct an instance

1
2
3
var className = "java.util.Random"; // or any other name of a class with a no-arg constructor
Class cl = Class.forName(className);
Object obj = cl.getConstructor().newInstance();

Resources

Classes often have associated data files, such as:

  • Image and sound files
  • Text files with message strings and button labels

In Java, such an associated file is called a resource.

The Class class provides a useful service for locating resource files.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import java.net.*;
import java.nio.charset.*;
import javax.swing.*;

public class ResourceTest
{
    public static void main(String[] args) throws IOException
    {
        Class cl = ResourceTest.class;
        URL aboutURL = cl.getResource("about.gif");
        var icon = new ImageIcon(aboutURL);
        InputStream stream = cl.getResourceAsStream("data/about.txt");
        var about = new String(stream.readAllBytes(), "UTF-8");
        InputStream stream2 = cl.getResourceAsStream("/corejava/title.txt");
        var title = new String(stream2.readAllBytes(), StandardCharsets.UTF_8
        JOptionPane.showMessageDialog(null, about, title, JOptionPane.INFORMA
    }
}

Using Reflection to Analyze the Capabilities of Classes

print out all information about a class.

1
2
3
4
5
6
7
8
9
import java.lang.reflect.*;

Class cl = Class.forName(name);
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
if (modifiers.length() > 0) 
	System.out.print(modifiers);
if (supercl != null && supercl != Object.class) 
	System.out + supercl.getName());
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Constructor[] constructors = cl.getDeclaredConstructors();
for (Constructor c : constructors) 
{
	String name = c.getName();
	String modifiers = Modifier.toString(c.getModifiers());
	Class[] paramTypes = c.getParameterTypes();
    for (int j = 0; j < paramTypes.length; j++)
    {
        if (j > 0) System.out.print(", ");
        System.out.print(paramTypes[j].getName());
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
Method[] methods = cl.getDeclaredMethods();
for (Method m : methods)
{
    Class retType = m.getReturnType();
    String name = m.getName();
    String modifiers = Modifier.toString(m.getModifiers());
    if (modifiers.length() > 0) 
    	System.out.print(modifiers + " ");
	System.out.print(retType.getName() + " " + name + "(");

    // print parameter types
    Class[] paramTypes = m.getParameterTypes();
    for (int j = 0; j < paramTypes.length; j++)
    {
        if (j > 0) 
        	System.out.print(", ");
        System.out.print(paramTypes[j].getName());
    }
    System.out.println(");");
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Field[] fields = cl.getDeclaredFields();
for (Field f : fields)
{
    Class type = f.getType();
    String name = f.getName();
    System.out.print("");
    String modifiers = Modifier.toString(f.getModifiers());
    if (modifiers.length() > 0) 
    	System.out.print(modifiers + " ");
    System.out.println(type.getName() + " " + name + ";");
}

Interfaces, Lambda Expressions, and

Inner Classes

Interface

In the Java programming language, an interface is a reference type, similar to a class, that can contain only constants, method signatures, default methods, static methods, and nested types. Method bodies exist only for default methods and static methods. Interfaces cannot be instantiated—they can only be implemented by classes or extended by other interfaces.

Defining an Interface

The public access specifier indicates that the interface can be used by any class in any package. If you do not specify that the interface is public, then your interface is accessible only to classes defined in the same package as the interface.

An interface can extend other interfaces, just as a class subclass or extend another class. However, whereas a class can extend only one other class, an interface can extend any number of interfaces. The interface declaration includes a comma-separated list of all the interfaces that it extends.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public interface Bicycle {
    // constant declarations
    
    // method signatures
    void changeCadence(int newValue);
    void changeGear(int newValue);
    void speedUp(int increment);
    void applyBrakes(int decrement);
    public int isLargerThan(Bicycle other);
}

Note that the method signatures have no braces and are terminated with a semicolon.

The interface body can contain abstract methods, default methods, and static methods. An abstract method within an interface is followed by a semicolon, but no braces (an abstract method does not contain an implementation). Default methods are defined with the default modifier, and static methods with the static keyword. All abstract, default, and static methods in an interface are implicitly public, so you can omit the public modifier.

In addition, an interface can contain constant declarations. All constant values defined in an interface are implicitly public, static, and final. Once again, you can omit these modifiers.

Implementing an Interface

To use an interface, you write a class that implements the interface. When an instantiable class implements an interface, it provides a method body for each of the methods declared in the interface.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class ACMEBicycle implements Bicycle {
    int cadence = 0;
    int speed = 0;
    int gear = 1;
    double price = 0;
   // The compiler will now require that methods changeCadence, changeGear, speedUp, and applyBrakes
   // all be implemented. Compilation will fail if those
   // methods are missing from this class.
    void changeCadence(int newValue) {
         cadence = newValue;
    }
    void changeGear(int newValue) {
         gear = newValue;
    }
    void speedUp(int increment) {
         speed = speed + increment;   
    }
    void applyBrakes(int decrement) {
         speed = speed - decrement;
    }
    void printStates() {
         System.out.println("cadence:" +
             cadence + " speed:" + 
             speed + " gear:" + gear);
    }
    void getPrice() {
         return this.price;
    }
    public int isMoreExpensive(Bicycle other) {
        // casts other to a ACMEBicycle instance. 
        ACMEBicycle otherBicycle 
            = (ACMEBicycle)other;
        if (this.getPrice() < otherBicycle.getPrice())
            return -1;
        else if (this.getPrice() > otherBicycle.getPrice())
            return 1;
        else
            return 0;               
    }
}

Because ACMEBicycle implements Bicycle, the price of any two ACMEBicycle objects can be compared.

(ACMEBicycle)other casts other to a ACMEBicycle instance. Type casting tells the compiler what the object really is. Invoking getPrice directly on the other instance (other.getPrice()) would fail to compile because the compiler does not understand that other is actually an instance of ACMEBicycle.

Using an Interface as a Type

1
2
3
4
5
6
7
8
public Object findMoreExpensive(Object object1, Object object2) {
   Bicycle obj1 = (Bicycle)object1;
   Bicycle obj2 = (Bicycle)object2;
   if ((obj1).isMoreExpensive(obj2) > 0)
      return object1;
   else 
      return object2;
}

By casting object1 to a Bicycle type, it can invoke the isMoreExpensive method.

These methods work for any “Bicycle” objects, no matter what their class inheritance is. When they implement Bicycle, they can be of both their own class (or superclass) type and a Bicycle type. This gives them some of the advantages of multiple inheritance, where they can have behavior from both a superclass and an interface.

Evolving Interfaces

1
2
3
4
public interface DoIt {
   void doSomething(int i, double x);
   int doSomethingElse(String s);
}

Suppose that, at a later time, you want to add a third method to DoIt, so that the interface now becomes:

1
2
3
4
5
6
public interface DoIt {
   void doSomething(int i, double x);
   int doSomethingElse(String s);
   boolean didItWork(int i, double x, String s);
   
}

If you make this change, then all classes that implement the old DoIt interface will break because they no longer implement the old interface. Programmers relying on this interface will protest loudly.

1
2
3
public interface DoItPlus extends DoIt {
   boolean didItWork(int i, double x, String s);
}

Now users of your code can choose to continue to use the old interface or to upgrade to the new interface.

Alternatively, you can define your new methods as default methods.

1
2
3
4
5
6
7
public interface DoIt {
   void doSomething(int i, double x);
   int doSomethingElse(String s);
   default boolean didItWork(int i, double x, String s) {
       // Method body 
   }
}

Note that you must provide an implementation for default methods. You could also define new static methods to existing interfaces. Users who have classes that implement interfaces enhanced with new default or static methods do not have to modify or recompile them to accommodate the additional methods.

Default Methods

Add new functionality to the interfaces, If they add them as static methods, then programmers would regard them as utility methods, not as essential, core methods.

Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces.

Extending Interfaces That Contain Default Methods:

  • Not mention the default method at all, which lets your extended interface inherit the default method.
  • Redeclare the default method, which makes it abstract.
  • Redefine the default method, which overrides it.

Static Methods

In addition to default methods, you can define static methods in interfaces. (A static method is a method that is associated with the class in which it is defined rather than with any object. Every instance of the class shares its static methods.) This makes it easier for you to organize helper methods in your libraries; you can keep static methods specific to an interface in the same interface rather than in a separate class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public interface TimeClient {
    // ...
    static public ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default public ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }    
}

Lambda Expressions

Nested Classes

1
2
3
4
public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY 
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class EnumTest {
    Day day;
    
    public EnumTest(Day day) {
        this.day = day;
    }
    
    public void tellItLikeItIs() {
        switch (day) {
            case MONDAY:
                System.out.println("Mondays are bad.");
                break;
            case FRIDAY:
                System.out.println("Fridays are better.");
                break;      
            case SATURDAY: case SUNDAY:
                System.out.println("Weekends are best.");
                break        
            default:
                System.out.println("Midweek days are so-so.");
                break;
        }
    }
    public static void main(String[] args) {
        EnumTest firstDay = new EnumTest(Day.MONDAY);
        firstDay.tellItLikeItIs();
    }
}
1
2
for (Day d : Day.values()) {
}

Service Loaders

Proxies

Exceptions, Assertions, and Logging

Dealing with Errors

Caching Exceptions

Using Assertions

Logging

Debugging Tips

Annotations

Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.

Annotations Basics

The at sign character (@) indicates to the compiler that what follows is an annotation.

The annotation type can be one of the types that are defined in the java.lang or java.lang.annotation packages of the Java SE API. It is also possible to define your own annotation type.

1
2
3
4
@Override
void mySuperMethod() { ... }
@SuppressWarnings(value = "unchecked")
void myMethod() { ... }

Annotations can be applied to declarations: declarations of classes, fields, methods, and other program elements. Annotations can also be applied to the use of types. This form of annotation is called a type annotation.

Declaring an Annotation Type

Many annotations replace comments in code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class Generation3List extends Generation2List {
   // Author: John Doe
   // Date: 3/17/2002
   // Current revision: 6
   // Last modified: 4/12/2004
   // By: Jane Doe
   // Reviewers: Alice, Bill, Cindy

   // class code goes here
}

To add this same metadata with an annotation, you must first define the annotation type.

The keyword interface is preceded by the at sign (@) (@ = AT, as in annotation type). Annotation types are a form of interface.

Optional: To make the information in @ClassPreamble appear in Javadoc-generated documentation, you must annotate the @ClassPreamble definition with the @Documented annotation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// import this to use @Documented
import java.lang.annotation.*;

@Documented
@interface ClassPreamble {
   String author();
   String date();
   int currentRevision() default 1;
   String lastModified() default "N/A";
   String lastModifiedBy() default "N/A";
   // Note use of array
   String[] reviewers();
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@ClassPreamble (
   author = "John Doe",
   date = "3/17/2002",
   currentRevision = 6,
   lastModified = "4/12/2004",
   lastModifiedBy = "Jane Doe",
   // Note array notation
   reviewers = {"Alice", "Bob", "Cindy"}
)
public class Generation3List extends Generation2List {
	// class code goes here
}

Predefined Annotation Types

Annotation Types Used by the Java Language:

  • @Deprecated

    1
    2
    3
    4
    5
    6
    7
    8
    
       // Javadoc comment follows
        /**
         * @deprecated
         * explanation of why it was deprecated
         */
        @Deprecated
        static void deprecatedMethod() { }
    }
    
  • @Override

    1
    2
    3
    4
    
       // mark method as a superclass method
       // that has been overridden
       @Override 
       int overriddenMethod() { }
    
  • @SuppressWarnings

    1
    2
    3
    4
    5
    6
    7
    8
    
       // use a deprecated method and tell 
       // compiler not to generate a warning
       // To suppress multiple categories of warnings, use the following syntax:
       // @SuppressWarnings({"unchecked", "deprecation"})
       @SuppressWarnings("deprecation")
        void useDeprecatedMethod() {
            objectOne.deprecatedMethod();
        }
    

Annotations That Apply to Other Annotations:

  • @Retention
  • @Documented
  • @Target
  • @Inherited
  • @Repeatable

Type Annotations and Pluggable Type Systems

Type annotations were created to support improved analysis of Java programs way of ensuring stronger type checking. For example, you want to ensure that a particular variable in your program is never assigned to null; you want to avoid triggering a NullPointerException.

1
@NonNull String str;

In many cases, you do not have to write your own type checking modules. There are third parties who have done the work for you. For example, you might want to take advantage of the Checker Framework created by the University of Washington. This framework includes a NonNull module, as well as a regular expression module, and a mutex lock module. For more information, see the Checker Framework.

Repeating Annotations

Declare a Repeatable Annotation Type:

1
2
3
4
5
6
7
8
import java.lang.annotation.Repeatable;

@Repeatable(Schedules.class)
public @interface Schedule {
  String dayOfMonth() default "first";
  String dayOfWeek() default "Mon";
  int hour() default 12;
}
1
2
3
public @interface Schedules { 
    Schedule [] value(); 
}
1
2
3
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }

Generics

are a powerful feature of the Java programming language. They improve the type safety of your code, making more of your bugs detectable at compile time.

concurrency

regular expressions

the platform environment.

aaa