These notes present the basics of Java in outline form. They only discuss the ways in which Java differs from C++. If something is not discussed, such as for loops, if-then-else statements, or constructors, it is because they are identical in both C++ and Java.
I. Java's built-in types
1. boolean -- true/false
2. byte -- 8 bit integer
3. char -- character
4. double -- double precision floating point number
5. float -- single precision floating point number
6. int -- integer
7. long -- long integer
8. short -- short integer
II. The String class--String is a built-in class that replaces C++'s
char * strings
A. Strings are immutable--once they are created, their contents cannot
be changed
Example: String a = "Hello";
a = a.concat(" World");
In this example, the concatenation operation creates a
completely new string to hold "Hello World" and then makes
a point to the new string. The old string object
remains unaltered with its contents still being "Hello".
Since no other variables point to it, it is eventually
garbage collected.
B. Strings should be compared using a string's equals method.
Example: String token;
String day = "Wednesday";
...
if (token.equals("+")) ...
else if (token.equals(day)) ...
C. Use a StringBuilder or StringBuffer object if you
want to modify/edit a string
1) StringBuilder is designed to be used with single threaded
applications and is faster than StringBuffer
2) StringBuffer works with multiple threaded applications.
3) The two classes are interchangeable, in the sense that they
support the same interface
III. Enumerated Types: Java 1.5 introduced a sophisticated mechanism for
enumerated types. Follow the link to read Sun's documentation. Some
salient features of Java's enumerated types are that:
1. An enumerated type is actually a class and each enumerated constant
is an object.
a. Because enumerated types are classes, they can be declared
independently of any class, and placed in their own .java file
2. An enumerated type may associated values with each enumerated
constant using a constructor and then declaring accessor methods
that allow a user to query for these values.
a. See the planets example in the Java documentation for a detailed
example.
b. As another example of associating values with enumerated constants,
you might want to associated point sizes with enumerated constants
for font sizes. For example, small might be a 9 point font, medium
a 12 point font, large a 24 point font, and so on.
IV. Scope rules
1. A variable may be declared within
a. a class
b. a method
c. a block
d. a statement
2. A variable may not be declared anywhere outside a class (i.e., there
are no global variables in Java)
3. A variable that is declared within a block cannot have the same
name as a variable in an enclosing block or method. This rule is
different from C/C++ where a variable may have any name when it is
declared within a block.
Example: The following code will cause the Java compiler to
generate an error message because j is declared
both in the body of the method and in the body of the
for loop:
int countPositive(int [] a) {
int count = 0;
int i, j;
for (i = 0; i < a.length; i++) {
int j = a[i];
if (j > 0) count++;
}
j = a.length - count;
System.out.println("number of negatives = " + j);
return count;
}
a. It is okay to have two identical variable declarations in the
same block, as long as they are not nested. For example, the
following code is valid:
int countPositive(...) {
...
for (int i = 0; i < 10; i++) {
...
}
for (int i = 20; i > 10; i--) {
...
}
}
4. Once a variable goes out of scope it is undefined. Hence if I had
only declared j in the block which is inside the for loop in the
above code (and also not declared it outside the block), j would
not be accessible outside the block.
V. Arrays: They are objects, not primitive types
1. Must be explicitly allocated using the new operator
2. The declaration uses empty brackets since the size of the array
is not known until it is allocated.
a. The brackets can be either after the type name or after the
variable name
e.g., int a [];
int [] b;
int c [] = new int[6];
int [] d = new int[10];
b. You can also provide a set of items to initialize an array, in
which case new is not required:
String weekdays [] = {"Monday", "Tuesday", "Wednesday",
"Thursday", "Friday"};
3. The length field returns the number of elements
e.g., System.out.println(c.length) will print 6
4. Multi-dimensional arrays are arrays of arrays
e.g., int a [][] = new int[4][5];
int b [][] = new int[4][];
b[0] = new int[5];
b[1] = new int[4];
...
5. For Each: Java 1.5 introduces a nifty construct for iterating through
a collection of objects, such as an array:
e.g., int[] arrayOfInts = { 32, 87, 3, 589, 12, 1076, 2000, 8, 622 };
int sum = 0;
for (int element : arrayOfInts) {
sum += element;
}
VI. Type conversions and type comparisons
1. Java automatically performs a type conversion if the conversion does
not entail a loss of precision. For example, Java will convert an
int to a double:
int a;
double b;
b = a; // okay because there is no loss of precision
2. Java will generate an error message if the type conversion would
entail a loss of precision:
a = b; // not okay because the fractional portion will be lost when
// b's value is assigned to a
a. In C++ such a conversion will only generate a warning message
3. The programmer may explicitly downcast (also called narrowing)
one type to a less precise type, in which case Java will perform
a pre-defined narrowing operation:
a = (int) b; // okay. Java will truncate the fractional part of b's
// value and assign it to a
4. The instanceof operator allows you to test whether an object
is of a specified class or array type. The operator returns true
if the comparison type is either the object's type or a superclass
of the object's type. For example, if Stack is a subclass of
LinkedList, then if b is a Stack, then:
b instanceof Stack
b instanceof LinkedList
b instanceof Object
are all true. A way of seeing why this makes sense, is that you
typically ask if an object is an instance of something because
you want to either cast to that type, or because you want to
use a certain method. It is always safe to lose "precision" by
casting to a superclass object, which is a less precise object.
VII. Classes
A. Variables whose types are classes cannot be allocated on the stack.
In other words, Java does not allow value objects. All objects are
allocated from the heap by calling new.
1. Java does not have a concept of pointers. When you create an
object and assign the result to a variable, Java does indeed
assign a pointer to the object to the variable, but you do
not have access to the pointer like you do in C++ (e.g., in C++
you can increment or decrement a pointer).
2. All member accesses are performed via the dot (.) operator
3. When an assignment statement such as a = b is executed,
a is made to point to the same object to which b
points. If a modifies a member of this object, then
b will also appear to change. For example:
Stack a;
Stack b = new Stack();
b.push(3);
a = b;
a.push(5); // b's stack now contains both 3 and 5
a. this treatment is different than in C++ where a would
receive a copy of b's object because a and b
would both be value objects. In C++, a's stack would
contain 3 and 5 after the push operation but b's stack
would only contain 3.
4. The == operator compares pointers, not the contents of two objects.
We say that the == operator performs a shallow comparison,
rather than a deep comparison. This comparision of pointers
is why you need to use a string's equals method to compare the
values of two strings.
B. Declaration of instance variables in a class: Unlike C++, you may
assign a default value to a variable when it is declared. Java
will assign this value to the variable before the object's constructor
is called.
Example: class Stack {
int size = 10;
int top = 0;
...
}
1. To accomplish the same type of initialization in C++ you
would have to ensure that every constructor explicitly
performed this initialization or called a method that
performed this initialization.
2. Since the initializer code is run each time an instance is created,
the initialization code may involve function calls, but it
may not depend on values that are passed into the constructor,
since the initialization statements are executed before the
constructor is executed.
C. Initialization blocks: You can use an unnamed block, called an
initialization block, to perform initialization actions on a
class's members, before the constructor is called.
1. If you create an initialization block, then it will also be called
when an object is first created
2. The code in the initialization block is executed after the default
values are assigned to variables but before the constructor is
called. It is helpful when something more complicated than a
single line initialization has to be performed, such as the
assigning of computed values to array elements.
3. Example: class RandomValues {
int randomNums[] = new int[10];
// Initialization block
{
for(int i=0; i<randomNums.length; i++)
{
randomNums[i] = (int)(100.0*Math.random());
}
}
}
4. Initialization blocks are in effect copied into each constructor for
that class. Hence it provides a way for constructors to share a
generic piece of code. In C++ you would accomplish the same sharing
by placing the generic code in the 0-argument constructor and then
making each constructor explicitly call the 0-argument constructor.
This approach has two drawbacks. First, you might not want to
provide a public 0-argument constructor (so then you have to go to
the trouble of protecting the 0-argument constructor). Second, you
have to remember to call the 0-argument constructor in each
constructor. That may not be hard to remember to do when the class
is first created, but a maintainer of the class might not know that
fact and inadvertently add a new constructor that does not call the
0-argument constructor and hence does not perform some important
initialization.
D. Static keyword: declares a variable to be a class member.
1. A static variable is essentially a global variable for that class
and all instances of that class share its value.
2. There is only one copy of the variable and it is stored with the
class rather than with an instance
Example: static String color = "red";
E. static blocks: static blocks are similar to initialization blocks
but are proceeded by the keyword static
1. they are called when the class is first loaded and can
be used to initialize static members
2. this treatment differs from C++ in which a static member must be
declared and initialized in a .cpp file as well as being declared
in a .h file.
F. Finalize method: replaces C++'s destructor and is only needed to
release non-Java resources, such as a file handle
1. signature: void finalize() {...}
2. is called by the garbage collector when the object is finally
destroyed
3. cannot count on when it will be called because you cannot count
on when the object will be garbage collected
a. if you need to ensure that a final set of actions is performed
on an object, create your own method and then call it when
you are ready to release the object
b. do not call finalize yourself since it will also get
called by the garbage collector. Doing so may cause your
program to try to release the same set of resources twice, and
the second time you won't own them anymore. Trying to release
resources you don't own could create unpredictable results.
G. Parameter passing
1. Java uses pass-by-value, not pass-by-reference
2. Can return objects since they're allocated off the heap
3. Passing a reference to an object allows the method to modify
the contents of the object. This may seem like a contradiction
to the pass-by-value dictum but if you think about it, it
makes sense. The reference value (i.e., pointer value) gets
copied, and hence the parameter has a pointer to the same object
as the argument
4. C++ uses both pass-by-value and pass-by-reference
a. Place an ampersand (&) after the type in a parameter declaration
to make the parameter a reference parameter:
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
actual call: swap(x, y);
b. When C++ sees a reference parameter, it passes a pointer to
the argument rather than a copy of the argument to the
method.
i. When you assign a value to a reference parameter, the value
is assigned to the argument's memory location, thus
modifying the original argument
ii. When you access a reference parameter on the right side
of an assignment statement, C++ return's the current
value at the argument's memory location
c. Reference parameters were introduced into C++ to reduce
the need to explicitly pass addresses of arguments to
methods and to explicitly dereference parameters in methods.
Compare how swap must be implement and called using the
pass-by-value method as compared to the pass-by-reference
method:
void swap(*int a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
actual call: swap(&x, &y)
As you can see, there are many more opportunities to introduce
an error when you have to explicitly take the address of
arguments and dereference parameters.
d. Unfortunately, in practice reference parameters are confusing
to use and the designers of Java decided to do away with
them. Note however that because of the way Java copies
reference values to parameters, Java really is doing
a form of pass-by-reference for objects.
H. Nested classes: Classes can be nested inside a Java class just as
in C++
1. A nested class has access to all the members and methods of its
enclosing class
2. An instance of a nested class is associated with the instance of
the class which creates it. A nested class cannot be created
independently of its outer class
e.g., class foo {
...
public class goo { ... }
}
foo.goo a = new foo.goo() will fail because a goo can only
be created by an instance of a foo
a. The java compiler creates a foo$x.class for goo
where x is a number
3. The compiled class name for the inner class will be
OuterClassName$InnerClassName.class. For example, goo
will be compiled to foo$goo.class.
I. Final keyword: Java's way of creating a constant
1. Memory for the final keyword is shared by all instances so you
do not have to declare them static.
2. If you want the constant to be accessible outside the class, you
must declare it both public and static.
VIII. Writing a C++-like Program in Java: If you want to write a program
in Java that is essentially a main program with a number of
function calls (i.e., you don't want to use Java's object mechanism),
then the way to do it is to place the code that would normally
go in main into a constructor. For example, here is a simple C++
program:
int sum(int x, int y) { return x + y; }
int multiply(int x, int y) { return x * y; }
int main(int argc, char *argv[]) {
sum(3,6);
multiply(9,5);
}
In Java, a comparable program should be written as:
class Arithmetic {
public Arithmetic() {
sum(3,6);
multiply(9,5);
}
int sum(int x, int y) { return x + y; }
int multiply(int x, int y) { return x * y; }
public static void main(String args[]) {
new Arithmetic();
}
}
A lot of Java newbie's might think this code is too complicated
and try to write it more simply as:
class Arithmetic {
int sum(...)
int multiply(...)
public static void main(String args[]) {
sum(3,6);
multiply(9,5);
}
}
The java compiler will complain about this code though because
sum and multiply have not been declared static as well. If you
call a function from a static function, then the called function
must also be declared static. The reason is that non-static
functions expect a pointer to an object to be passed to them via
the this variable. However, static functions don't have
objects associated with them and hence cannot pass a reference to
an object to a non-static function. Trying to keep track of all
the function calls made from a static function becomes tiresome,
and hence it is best to simply move the code that typically
appears in main to a constructor instead, and then invoke that
code by having main create a single instance of the class.