| Declare Array Variables | |
|---|---|
| Array variables are objects in their own right, and created in heap memory. | |
| int[] key; | Declare a variable that will contain a one dimension array of int |
| int key[]; | Declare a variable that will contain a one dimension array of int - same as above.
The braces can be either on the type or on the variable name. |
| int[][] key; | Declare a variable that will contain a two dimension array of int |
| int[][][] key;
int[][] key[]; int[] key[][]; int key[][][]; | These declarations all do the same thing - declare a variable that can contain a three dimension array of int. The braces can be broken up and distributed between the variable type and the variable name. |
| Declare and Construct Arrays | |
|---|---|
| All arrays created in this manner will have their entries initialized to their default values.
For primitive types, the value is 0; for objects, the value is null. When constructing arrays, you must always specify the size of the array at creation time, on the right hand side of the equals sign. It is never legal to specify the size of the array on the left hand side of the equals sign! That results in a compilation error. | |
| int[] key;
key = new int[4]; | Declare an array of ints, then construct an array and assign it to the array variable.
All entries in the array will be initialized to the default value of 0. |
| int[] key = new int[4]; | Declare and construct an array of ints. Same as above, just in a single line. |
| Cat[] cats = new Cat[5]; | Declare and construct an array of cats.
All entries in the array will be initialized to the default value of null. |
| Declare, Construct, and Initialize Arrays | |
|---|---|
|
All arrays constructed in this manner are initialized with the values between the curly braces, not the default value.
The array size is determined at creation time by the count of the items in the curly braces. | |
| int[] key = {1, 2, 3}; | Declare, construct, and initialize a one dimension array of int. |
| int[][] key = {{1, 2}, {2, 3, 4}}; | Declare, construct, and initialize a two dimension array of int.
The number of items in each row can vary, thus creating a "jagged" array. |
| int[] key[] = {{1, 2}, {2, 3, 4}}; | Declare, construct, and initialize a two dimension array of int - same as above. |
| Cat[] key = {new Cat(), new Cat()}; | Declare, construct, and initialize a one dimension array of cat. |
| Constructing and Initializing an Anonymous Array | |
|---|---|
| This technique can be used for creating anonymous arrays, which can then be assigned to variables or passed to methods.
When you initialize an array with curly braces, you can't specify a size as well - it must be one or the other, not both. | |
| int[] key = new int[]{1, 2, 3}; | Construct and initialize a one dimension array of int, and assign to an array variable. |
| PrintArray(new int[]{1, 3, 2}); | Construct and initialize a one dimension array of int, and pass it to a method without assigning it to an array variable first. |
| Size Doesn't Matter | |
|---|---|
An array variable is an object that can refer to any array of the same type (or subtype) and dimensions, regardless of size. For instance:
int[] arr1 = {1, 2, 3, 4};
int[] arr2 = {1};
System.out.println(Arrays.deepToString(arr2)); // [1]
arr2 = arr1;
System.out.println(Arrays.deepToString(arr2)); // [1, 2, 3, 4]
Object[][] arr3 = {{1, 2, 3}, {4, 5, 6}};
String[][] arr4 = {{"a"}, {"b"}};
System.out.println(Arrays.deepToString(arr3)); // [[1, 2, 3], [4, 5, 6]]
arr3 = arr4;
System.out.println(Arrays.deepToString(arr3)); // [[a], [b]]
When determining what types an array variable can point at, use the 'instanceof' test. If the array were a single variable of type 'Car', it could contain an instance of type 'DragRacer', which is a type of car. Therefore, an array of Car can point to an array of DragRacer, since DragRacer is an instanceof car. However, the reverse is not true! Car fails the instanceof test, since it's not a type of DragRacer, therefore an array variable of DragRacer could not point to an array of Car.
class Car {}
class DragRacer extends Car {
public static void main(String args[]) {
Car[] cars = new Car[5];
DragRacer[] racers = new DragRacer[5];
racers = cars; // Type mismatch: cannot convert from Car[] to DragRacer[]
cars = racers; // This one works.
}
}
}
|
| Multi Dimensional Array Example |
|---|
| When constructing an array, it is necessary to specify the size.
For a multidimensional array, the size is the first dimension, therefore, only the first dimension is required to construct the array.
Here's an example of three different ways of declaring a three dimensional array. All are valid, and all result in the same output.
/*
Output for ArrayTest1, ArrayTest2, and ArrayTest3 is:
[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]],
[[12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23]]]
The number of rows is 2, the first dimension.
The number of groups in each row is 3, the second dimension.
The number of entries in each group is 4, the third dimension.
*/
public class ArrayTest {
public static void main(String args[]) {
ArrayTest1();
ArrayTest2();
ArrayTest3();
}
public static void ArrayTest1() {
int dim1 = 2;
int dim2 = 3;
int dim3 = 4;
int ctr = 0;
int[][][] arr = new int[dim1][][];
for (int i = 0; i < dim1; i++) {
arr[i] = new int[dim2][];
for (int j = 0; j < dim2; j++) {
arr[i][j] = new int[dim3];
for (int k = 0; k < dim3; k++)
arr[i][j][k] = (int) (ctr++);
}
}
System.out.println(java.util.Arrays.deepToString(arr));
}
public static void ArrayTest2() {
int dim1 = 2;
int dim2 = 3;
int dim3 = 4;
int ctr = 0;
int[][][] arr = new int[dim1][dim2][];
for (int i = 0; i < dim1; i++) {
// The following line is no longer necessary because dim2
// was specified when declaring the array variable.
// arr[i] = new int[dim2][];
for (int j = 0; j < dim2; j++) {
arr[i][j] = new int[dim3];
for (int k = 0; k < dim3; k++)
arr[i][j][k] = (int) (ctr++);
}
}
System.out.println(java.util.Arrays.deepToString(arr));
}
public static void ArrayTest3() {
int dim1 = 2;
int dim2 = 3;
int dim3 = 4;
int ctr = 0;
int[][][] arr = new int[dim1][dim2][dim3];
for (int i = 0; i < dim1; i++) {
// The following line is no longer necessary because dim2
// was specified when declaring the array variable.
// arr[i] = new int[dim2][];
for (int j = 0; j < dim2; j++) {
// The following line is no longer necessary because dim3
// was specified when declaring the array variable.
// arr[i][j] = new int[dim3];
for (int k = 0; k < dim3; k++)
arr[i][j][k] = (int) (ctr++);
}
}
System.out.println(java.util.Arrays.deepToString(arr));
}
}
|
| Static and Instance Initializers |
|---|
|
Static initializers run once, the first time the class is loaded. Instance initializers run before the constructor runs, every time an object is created.
Static and instance initializers can appear between methods of a class, or at the end - they don't have to come at the top, though that is the usual place for them. There can be more than one static and instance initializer, and if there are multiple initializers, they run in the order they appear in the source file. Take a look at the following:
class A {
static { System.out.println("A static initializer 1"); }
{ System.out.println(" A instance initializer 1"); }
static { System.out.println("A static initializer 2"); }
{ System.out.println(" A instance initializer 2"); }
public A() { System.out.println(" A constructor."); }
}
class B extends A {
static { System.out.println("B static initializer"); }
{ System.out.println(" B instance initializer"); }
public B() { System.out.println(" B constructor."); }
}
class C extends B {
static { System.out.println("C static initializer"); }
{ System.out.println(" C instance initializer"); }
public C() { System.out.println(" C constructor."); }
}
class D {
static { System.out.println("D static initializer"); }
{ System.out.println(" D instance initializer"); }
public D() { System.out.println(" D constructor."); }
}
public class InitializerTesting {
public static void main(String[] args) {
System.out.println("\nCreating C\n");
C c = new C();
System.out.println("\nCreating A\n");
A a = new A();
System.out.println("\nCreating B\n");
B b = new B();
System.out.println("\nCreating D\n");
D d = new D();
}
}
/*
The output is:
Creating C
A static initializer 1
A static initializer 2
B static initializer
C static initializer
A instance initializer 1
A instance initializer 2
A constructor.
B instance initializer
B constructor.
C instance initializer
C constructor.
Creating A
A instance initializer 1
A instance initializer 2
A constructor.
Creating B
A instance initializer 1
A instance initializer 2
A constructor.
B instance initializer
B constructor.
Creating D
D static initializer
D instance initializer
D constructor.
*/
In the above, class B inherits from A, and class C inherits from B.
When the first C object is created, it load class C. C inherits from B, so it loads class B. B inherits from A, so it loads class A.
Now that we're at the top of the hierarchy, all the static initializers run - first, for class A, then for class B, then for class C.
Again from the top of the hierarchy, the constructors run - but before the constructors, the instance initializers run.
In this case, that means Class A's instance initializer(s), class A's constructors, then class B's instance initializer, then class B's contructor, finally class C's instance initializer, then class C's constructor.
When creating object A, all the static initializers for this hierarchy have run already, and they only run once, so those are skipped. So, it runs class A's instance initializer(s), then class A's constructor. When creating object B, we skip the static initializers because they already ran, and run the instance initializers + constructors for every class down the chain. Thus, class A's instance initializers, class A's constructor, class B's instance initializer's, then class B's constructor. Finally, we create the last object, D. D is a class that stands alone - it doesn't extend anything but Object. Since we haven't created any instances of D yet, its static initializer hasn't run. Therefore, when we create D, first the static initializer runs, then the instance initializer, then the constructor. |
| Wrapper Classes | ||
|---|---|---|
|
Wrapper classes are immutable - once created, their value can't be changed. That means if you have the following: Integer i = 2; the 2 is autoboxed into an Integer, and assigned to the variable i. If later you say: i = 3; the 3 is autoboxed into an Integer, and assigned to the variable i. The wrapper with a value of 2 that the variable used to point to, is now discarded, and is eligible for garbage collection. As you can imagine, using wrappers can cause programs to suffer a performance hit. Incrementing an Integer from 1 to 1,000 causes 1000 Integer wrappers to be created, then discarded! One final thought - it can be difficult to remember which of the static wrapper methods below return a wrapper, versus return a primitive or String. Remember this handy tip: Out of all the static methods you see down there, only the 'valueOf' methods return a wrapper object - all the rest return primitives or strings. See the big 'O' in valueOf? That's a handy mnemonic to remember it returns an object.
|
| Primitive | Wrapper Class | Constructor Arguments |
|---|---|---|
| boolean | Boolean | boolean or String |
| byte | Byte | byte or String |
| short | Short | short or String |
| char | Character | char |
| int | Integer | int or String |
| short | Short | short or String |
| long | Long | long or String |
| float | Float | float, double, or String |
| double | Double | double or String |
| Wrapper Constructor Examples | ||
|---|---|---|
| new Integer(5) | Creates a new wrapper object, with the value 5. | |
| new Long("5") | Parses the supplied string into a numeric value, and creates a new wrapper object, with that value. If the string is invalid and can't be converted to the desired format, such as new Integer("a"), a java.lang.NumberFormatException error will be thrown at runtime. | |
| XXX.valueOf Static Wrapper Method Return a Wrapper Object | ||
|---|---|---|
| For all the static methods that take a String as an argument, the method parses the string into a numeric value, and if the string can't be parsed, a java.lang.NumberFormatException error will be thrown at runtime.
For all the static methods that take both a String and a radix, the method parses the string into a value using the supplied base, and if the string can't be parsed, throws a java.lang.NumberFormatException error at runtime. | ||
|
Boolean.valueOf(boolean value)
Boolean.valueOf(String value) | Creates a new Boolean wrapper object with the supplied value. | |
|
Byte.valueOf(byte value)
Byte.valueOf(String value) Byte.valueOf(String value, int radix) | Creates a new Byte wrapper object with the supplied value. | |
|
Short.valueOf(short value)
Short.valueOf(String value) Short.valueOf(String value, int radix) | Creates a new Short wrapper object with the supplied value. | |
| Character.valueOf(char value) | Creates a new Character wrapper object with the supplied value. | |
|
Integer.valueOf(int value)
Integer.valueOf(String value) Integer.valueOf(String value, int radix) | Creates a new Integer wrapper object with the supplied value. | |
|
Long.valueOf(long value)
Long.valueOf(String value) Long.valueOf(String value, int radix) | Creates a new Long wrapper object with the supplied value. | |
|
Float.valueOf(float value)
Float.valueOf(String value) | Creates a new Float wrapper object with the supplied value. | |
|
Double.valueOf(double value)
Double.valueOf(String value) | Creates a new Double wrapper object with the supplied value. | |
| ParseXXX Static Wrapper Methods Return a Primitive | ||
|---|---|---|
|
For all the static methods that take a String as an argument, the method parses the string into a numeric value, and if the string can't be parsed, a java.lang.NumberFormatException error will be thrown at runtime.
For all the static methods that take both a String and a radix, the method parses the string into a value using the supplied base, and if the string can't be parsed, throws a java.lang.NumberFormatException error at runtime. | ||
| Boolean.parseBoolean(String value) | Parses the supplied value, and returns a boolean. | |
|
Byte.parseByte(String value)
Byte.parseByte(String value, int radix) | Parses the supplied value, and returns a byte. | |
|
Short.parseShort(String value)
Short.parseShort(String value, int radix) | Parses the supplied value, and returns a short. | |
|
Integer.parseInteger(String value)
Integer.parseInteger(String value, int radix) | Parses the supplied value, and returns an int. | |
|
Long.parseLong(String value)
Long.parseLong(String value, int radix) | Parses the supplied value, and returns a long. | |
| Float.parseFloat(String value) | Parses the supplied value, and returns a float. | |
| Double.parseDouble(String value) | Parses the supplied value, and returns a double. | |
| XXX.toString() Static Wrapper Methods Return a String | |
|---|---|
| Boolean.toString(true) | returns "true" |
| Byte.toString(1) | returns "1" |
| Character.toString('c') | returns "c" |
| Short.toString(2) | returns "2" |
| Integer.toString(3)
Integer.toString(3, radix) | returns "3", optionally converted with the specified radix. |
| Long.toString(4)
Long.toString(4, radix) | returns "4", optionally converted with the specified radix. |
| Float.toString(4.1) | returns "4.1" |
| Double.toString(3.14); | returns "3.14" |
| XXX.toBinaryString(), XXX.toHexString(), XXX.toOctalString() Static Wrapper Methods Return a String | |
|---|---|
| Integer.toBinaryString(10) Long.toBinaryString(10) | returns "1010" |
| Integer.toHexString(10) Long.toHexString(10) | returns "a" |
| Integer.toOctalString(10) Long.toOctalString(10) | returns "12" |
| xxxValue() Instance Methods Return a Primitive | ||
|---|---|---|
| booleanValue() | Returns the wrapper's value converted to a boolean.
Available on Boolean wrappers only. | |
| byteValue() | Returns the wrapper's value converted to a byte. | |
| shortValue() | Returns the wrapper's value converted to a short. | |
| intValue() | Returns the wrapper's value converted to an int. | |
| longValue() | Returns the wrapper's value converted to a long. | |
| floatValue() | Returns the wrapper's value converted to a float. | |
| doubleValue() | Returns the wrapper's value converted to a double. | |
| Autoboxing | |
|---|---|
When an integer is passed to a method that requires an Integer, the int will be autoboxed into an Integer.
When an Integer is compared to a primitive int, the Integer is unboxed to a primitive int, then the comparison occurs.
The choice of which overloaded method to call based on autoboxing and unboxing rules is tricky. Here's the precedence:
You can't expect it to first widen then autobox a variable to call a method - that will result in a compile time error. As an example, calling a function that requires a Long, with a byte argument, will fail. You CAN autobox then widen, however. Here's an example: If a method requires a Number, you can pass it a byte. The byte will be autoboxed to a Byte, which is an instanceof Number, so the call will succeed. You can also widen, then call a var-args function - ie, passing several integer, short, and byte values into a function that requires a var-args long (long... args) |