Basic Java Refresher
Basic Java Refresher
Object Pointers
• The declaration "Student x;" declares a pointer "x" to a Student object, but does not
allocate the object yet.
• Java has a very simple and uniform memory system. Objects and arrays are allocated in
the heap and accessed through pointers.
2
• There is no "&" operator to make a pointer to something in the stack and there is no
pointer arithmetic. The only pointers that exist in java point to objects and arrays in
the heap -- simple.
- Objects and arrays are allocated with the "new" operator (below).
- Using = on an object pointer just copies the pointer, so there are multiple pointers to
the one object (aka "shallow" or "sharing").
- Likewise, using == on object pointers just compares the pointers (for some classes,
the equals() method will do a "deep" comparison of two objects).
Message send
• Send a message to an object.
- a.getUnits();
- b.getStress();
• Finds the matching method in the class of the receiver, executes that method against the
receiver and returns.
• The Java compiler will only allow message sends that the receiver actually responds to.
• Instead of "sending a message to the object", we could say we "called that method on
the object". The distinction between a message and a method is not important, until
we get to inheritance later on.
Object Lifecycle
• The client allocates objects and they are initialized by the class ctor code
• The client then sends messages which run class method code on the objects.
3
• The client essentially makes requests -- all the code that actually operates on the objects
is defined by the class, not the client.
• As a result, if the class is written correctly, the client should not be able to introduce
new bugs in the class and visa-versa.
• This is the benefit of using public/private to keep the client and the implementation
separate.
/*
OUTPUT...
a units:12 stress:120
b units:15 stress:150
a units:9 stress:90
a units:10 stress:100
*/
4
Class Definition
• A class defines the instance variables and methods used by its objects.
• Each variable and method may be declared as "public" if it may be used by clients, or
"private" or "protected" if it is part of the implementation and not for use by clients.
• The compiler and JVM enforce the public/private scheme.
Public Interface
• The most common public/private scheme is...
• All ivars are declared private
• Methods to be used by clients are declared public -- these make up the interface that the
class exposes to clients.
• Utility methods for the internal use of the class are declared private.
Java Class
• The convention is that java classes have upper case names like "Student" and the code
is in a file named "Student.java".
• By default, java classes have the special class "Object" as a superclass. We'll look at
what that means later when we study superclasses.
• Inside the Student.java file, the class definition looks like...
public class Student extends Object {
... <definition of the Student ivars and methods> ....
}
• The "extends Object" part can be omitted, since java classes extend Object by default if
there is no "extends" clause.
• There are not separate .h and .c files to keep in synch -- the class is defined in one
place.
- This is a nice example of the "never have two copies of anything" rule. Keeping
duplicate info in the .h and .c files in synch was a bore -- better to just have one
copy.
5
Instance Variables
• Instance variables (ivars) are declared like ordinary variables -- a type followed by a
name.
protected int units;
• An ivar defines a variable that each object of this class will have -- allocates a slot
inside each object. In this case, every Student object has an int ivar called "units"
within it.
• The object itself is allocated in the heap, and its ivars are stored inside it. The ivars may
be primitive types, such as int, or they may be pointers to other objects or arrays.
public/private/protected
• An ivar or other element declared private is not accessible to client code. The element is
only accessible to the implementation inside the class.
• Suppose on the client side we have a pointer "s" to a Student object. The statement
"s.units = 13;" in client code will not compile if "units" is private or protected.
• "protected" is similar to private, but allows access by subclasses or other classes in the
same package (we will not worry about those cases)
• "public" makes something accessible everywhere
• There is also a "default" protection level that you get when no public, private, or
protected keyword is specified. In that case, the element is accessible to all other
classes in the same package as the compiled class. This is an odd case, and I
recommend against using it.
Constructor (ctor)
• A constructor has the same name as the class.
• It runs when new objects of the class are created to set up their ivars.
public Student(int initUnits) {
units = initUnits;
}
• Bug control
- Ctors make it easier for the client to do the right thing since objects are
automatically put into an initialized state when they are created.
• Every ivar goes in Ctor
- Every time you add an instance variable to a class, go add the line to the ctor that
inits that variable.
- Or you can give an initial value to the ivar right where it is declared, like this...
"private int units = 0;" -- there is not agreement about which ivar init style is
better.
Default Ctor
• A constructor with no arguments is known as the "default ctor".
public Student() {
units = 15;
}
• If a class has a default ctor, and a client creates an instance of that class, but without
specifying a ctor, the default ctor is automatically invoked.
• e.g. new Student() -- invokes the default ctor, if there is one.
Method
• A method corresponds to a message that the object responds to
public int getStress() {
return(units * 10);
}
• When a message is sent to an object, the corresponding method runs against that
receiver.
• Methods may have a return type, int in the above example, or may return void.
• Message-Method Lookup sequence
- Message sent to a receiver
- Receiver knows its class and looks for a matching method
- The matching method executes against the receiver
• e.g. the "units" ivar in the Student methods is automatically that of the receiver
• Likewise, sending a message to the same receiver from inside a method requires no
extra syntax.
• e.g.. inside the Student dropClass() method, the code sends the setUnits() message to
change the number of units with the simple syntax:
setUnits(units - drop);
"this" -- receiver
• "this" in a method
- "this" is a pointer to the receiver
- Don't write "this.units", write: "units"
- Don't write "this.setUnits(5)", write "setUnits(5);"
• Some programmers, like sprinkling "this" around to remind themselves of the OOP
structure involved, but I find it distracting. The nice thing about OOP is the
effortlessness of the receiver-relative style.
• A common use of "this" is when one object is trying to register itself with another
object.
/* NOTE
"public static final" declares a public readable constant that
is associated with the class -- it's full name is Student.MAX_UNITS.
It's a convention to put constants like that in upper case.
*/
public static final int MAX_UNITS = 20;
public static final int DEFAULT_UNITS = 15;
/*
Stress is units *10.
/*
Tries to drop the given number of units.
Does not drop if would go below 9 units.
9
/*
Here's a static test function with some simple
client-of-Student code.
NOTE Invoking "java Student" from the command line runs this.
It's handy to put test/demo/sample client code in the main() of a class.
*/
public static void main(String[] args) {
// Make two students
Student a = new Student(12); // new 12 unit student
Student b = new Student(); // new 15 unit student
/*
OUTPUT...
a units:12 stress:120
b units:15 stress:150
a units:9 stress:90
a units:10 stress:100
*/
}
10
Java Features
Inheritance -- ignore for now
• OOP languages have an important feature called "inheritance" where a class can be
declared as a "subclass" of another class, known as the superclass.
• In that case, the subclass inherits the features of the superclass. This is a tidy way to
give the subclass features from its superclass -- a form of code sharing.
• This is an important feature in some cases, but we will cover it a little later.
• By default in Java, classes have the superclass "Object" -- this means that all classes
inherit the methods defined in the Object class.
Java Primitives
• Java has "primitive" types, much like C. Unlike C, the sizes of the primitives are fixed
and do not vary from one platform to another, and there are no unsigned variants.
- boolean -- true of false
- byte -- 1 byte
- char -- 2 bytes (unicode)
- int -- 4 bytes
- long -- 8 bytes
- float -- 4 bytes
- double - 8 bytes
• Primitives can be used for local variables, parameters, and ivars.
• Local variables are allocated on the runtime stack when the code runs, just as in C.. At
runtime, primitives are simple and work fast.
• Primitives may be allocated inside objects or arrays, however, it is not possible to get a
pointer to a primitive itself (there is no & operator). Pointers only work for objects
and arrays in the heap -- this makes pointers much simpler in Java than in C or C++.
• Java is divided into two worlds: primitives work in simple ways and there are no
pointers, while objects and arrays only work through pointers. The two worlds are
separate, and the boundary between the two can be a little awkward.
• There are "wrapper" classes Integer, Boolean, Float, Double.... that can hold a single
primitive value. These classes are "immutable", they cannot be changed once
constructed. They can finesse, to some extent, the situation where you have a
primitive value, but need a pointer to it. Use intValue() to get the int value out of an
Integer object.
• Use the static method Integer.parseInt(String) -> int to parse a String to an int
• Use the static method Integer.toString(int) -> String to make a String out of an int
11
Arrays
• Java has a nice array functionality built in to the language.
• An array is declared according to the type of element -- an int[] array holds ints, and a
Student[] array holds Student objects.
• Arrays are always allocated in the heap with the "new" operator and accessed through
pointers (like objects)
• An array may be allocated to be any size, although it may not change size after it is
allocated (i.e. there is no equivalent to the C realloc() call).
• Array Declaration
- int[] a; -- a can point to an array of ints (the array itself is not yet allocated)
- int a[]; -- alternate syntax for C refugees -- do not use!
- Student[] b; -- b can point to an array of Student objects. Actually, the array will
hold pointers to Student objects.
• a = new int[100];
- Allocate the array in the heap with the given size
- Like allocating a new object
- The array elements are all zeroed out when allocated.
- The requested array length does not need to be a constant -- it could be an
expression like new int[2*i +100];
• Array element access
- Elements are accessed 0..len-1, just like C and C++
- Java detects array-out--of-bounds access at runtime
- a[0] = 1; -- first element
- a[99] = 2; -- last element
- a[-1] = 3; -- runtime array bounds exception
• a.length -- returns the length of the array (read-only)
- Arrays know their length -- cool!
- NOT a.length()
• Arrays have compile-time types
- a[0] = "a string"; // NO -- int and String don't match
- At compile time, arrays know their element type and detect type mismatches such
as above
- The other Java collections, such as ArrayList, do not have this compile time type
system error catching, although compile time types are being added for Java 1.5
• Student[] b = new Student[100];
- Allocates an array of 100 Student pointers (initially all null)
- Does not allocate any Student objects -- that's a separate pass
• (also, notice that the "int i" can be declared right in the for loop -- cute.)
{
int[] squares;
squares = new int[100]; // allocate the array in the heap
Array Literal
• There's a syntax to specify an array and its contents as part of an array variable
declaration.
• This is called an "array constant" or an "array literal".
- String[] words = { "hello", "foo", "bar" };
- int[] squares = { 1, 4, 9, 16 };
-
- // in this case, we call new to create objects in the array
- Student[] students = { new Student(12), new Student(15) };
Anonymous array
• Alternately, you can create outside of a variable declaration like this... .
- ... new String[] { "foo", "bar", "baz"} ...
Array Utilities
• Java has a few utility functions to help with arrays...
• There is a method in the System class, System.arraycopy(), that will copy a section of
elements form one array to another. This is likely faster than writing the equivalent
for-loop yourself.
- System.arraycopy(source array, source index, dest array, dest index, length);
• Arrays Class
13
- The Arrays class contains many convenience methods that work on arrays -- filling,
searching, sorting, etc.
Multidimensional Arrays
• An array with more dimensions is allocated like this...
- int[][] big = new int[100][100]; // allocate a 100x100 array
- big[0][1] = 10;// refer to (0,1) element
• Unlike C, a 2-d java array is not allocated as a single block of memory. Instead, it is
implemented as a 1-d array of pointers to 1-d arrays.
String
• Java has a great built-in String class. See the String class docs to see the many
operations it supports.
• Strings (and char) use 2-byte unicode characters -- work with Kanji, Russian, etc.
• String objects use the "immutable" design style
- Never change once created
- i.e. there is no append() or reverse() method that changes the string state
- To represent a different string state, create a new string with the different state
- The immutable style, has an appealing simplicity to it -- easy for clients to
understand.
- The immutable style happens to avoid many complexities when dealing with (a)
multiple pointers sharing one object, and (b) multiple threads sharing one object.
- On the other hand, the immutable style can cause the program to work through a lot
of memory over time which can be expensive.
• String constants
- Double quotes (") build String objects
- "Hello World!\n" -- builds a String object with the given chars and returns a pointer
to it
- The expression new String("hello") is a little silly, can just say "hello".
- Use single quotes for a char 'a', 'B', '\n'
• System.out.print("print out a string"); // or use println() to include the endline
• String + String
- + concats strings together -- creates a new String based on the other two
String a = "foo";
String b = a + "bar"; // b is now "foobar"
• String + int
- + between a String and an int, converts the int to a String and concats it all together:
int i = 6;
String s = "hello " + i + "there " + (i*10);
// s is now "hello 6there 60"
14
• toString()
- Many objects support a toString() method that creates some sort of String version of
the object -- handy for debugging. print(), printLn(), and + will use the toString()
of any object passed in. The toString() method is defined up in the Object class,
so that's why all classes respond to it. (More on this when we talk about
inheritance.)
String Methods
• Here are some of the representative methods implemented in the String class
• Look in the String class docs for the many messages it responds to
- int length() -- number of chars
- char charAt(int index) -- char at given 0-based index
- int indexOf(char c) -- first occurrence of char in the string, or -1
- int indexOf(String s)
- boolean equals(Object) -- test if two strings have the same characters
- boolean equalsIgnoreCase(Object) -- as above, but ignoring case
- String toLowerCase() -- return a new String, lowercase
- String substring(int begin, int end) -- return a new String made of the
begin..end-1 substring from the original
StringBuffer
• StringBuffer is similar to String, but can change the chars over time. More efficient to
change one StringBuffer over time, than to create 20 slightly different String objects
over time.
{
StringBuffer buff = new StringBuffer();
for (int i=0; i<100; i++) {
buff.append(<some thing>); // efficient append
}
String result = buff.toString(); // make a String once done with appending
15
System.out
• System.out is a static object in the System class that represents standard output. It
responds to the messages...
- println(String) -- print the given string on a line (using the end-line character of the
local operating system),
- print(String) -- as above, but without and end-line
• Example
- System.out.println("hello"); -- prints to standard out
== vs equals()
• == -- compare primitives or pointers
- Use == to compare primitives, such as int or car or boolean
- Use == to compare a pointer to null
- With two pointers, == will test if the two pointers point to the same object, which is
not the same as testing if they are byte-for-byte the same.
• boolean equals(Object other)
- There is a default definition in the Object superclass that just does an == compare of
(this == other), so it's just like using == directly.
- However, many of the library classes such as String, Integer, Color, etc. override
equals() to provide "deep" byte-by-byte compare version. See the docs for a
particular class to see if it overrides equals().
• String Example
- String a = new String("hello"); // in reality, just write this as "hello"
- String a2 = new String("hello");
- a == a2 // false
- a.equals(a2) // true
-
• Foo Example
- Foo a = new Foo("a");
- Foo a2 = new Foo("a");
- a == a2 // false
- a.equals(a2) // ??? -- depends on Foo overriding equals()
Garbage Collector GC
• String a = new String("a");
• String b = new String("b");
• a = a + b; // a now points to "ab"
• Where did the original a go?
16
- It's still sitting in the heap, but it is "unreferenced" or "garbage" since there are no
pointers to it. The GC thread comes through at some time and reclaims garbage
memory.
- GC slows Java code down a little, but eliminates all those &/malloc()/free() bugs.
The GC algorithm is very sophisticated.
• Stack vs. Heap
- Remember, stack memory (where locals are allocated for a method call), is much
faster than heap memory for allocation and deallocation.
• Destructor
- In C++, the "destructor" is an explicit notification that the object is about to be
destroyed.
- In Java, the "finalizer" is like a destructor -- it runs when an object is about to be
GC'd. However, when or even if the finalizer runs is very random because of the
odd scheduling of the GC. Because the timing of the finalizer is imprecise,
depending on them can make the whole program behavior a little unpredictable.
Therefore, I recommend not using finalizers if at all possible.
Static
• Ivars or methods in a class may be declared "static".
• Regular ivars and methods are associated with objects of the class.
• Static variables and methods are not associated with an object of the class. Instead, they
are associated with the class itself.
Static variable
• A static variable is like a global variable, except it exists inside of a class.
17
• There is a single copy of the static variable inside the class. In contrast, regular ivars
such as "units" exist many times -- once for each object of the class.
• Static variables are rare compared to ordinary ivars.
• The name of a static variable starts with the name of its class -- so a static variable
named "count" in the Student class would be referred to as "Student.count".
• Output Example
- "System.out" is a static variable in the System class that represents standard output.
• Monster Example
- Suppose you are implementing the game Doom. You have a Monster class that
represents the monsters that run around in the game. Each monster object needs
access to a Sound object that holds the sound "roar.mp3". so the monster can play
that sound at the right moment. With a regular ivar, each monster would have
their own copy of the variable. Instead, the Monster class contains a static Sound
variable, and all the monster objects share that one variable.
Static method
• A static method is like a function that is defined inside a class.
• A static method does not execute against a receiver. Instead, it is like a plain C function
-- takes arguments, but there is no receiver.
• The full name of a static method begins with the name of its class, so a static foo()
method in the Student class is called Student.foo().
• The Math class contains the common math functions, such as sin(), cos(), etc.. These
are defined as static methods in the Math class. Their full names are Math.sin(),
Math.cos(), ...
• The System.arraycopy() method is another example of a static method. The static
method does not have a receiver that it executes against. Instead, we call it like a
regular function, and pass it the arguments to work on.
• A "static int getCount()" method in the Student class is invoked as Student.getCount();
• In contrast, a regular method would be invoked with a message send to a receiver like
s.getStress(); where s points to a Student object.
• The method "static void main(String[] args)" is special. To run a java program, you
specify the name of a class. The system then starts the program by running the static
main() function in that class, and the String[] array represents the command-line
arguments.
• Call a static method like this: Student.foo(), NOT s.foo(); where s points to a Student.
- s.foo() actually compiles, but it discards s as a receiver and translates to the same
thing as Student.foo() using the compile-time type of the receiver variable. The
s.foo() syntax is misleading, since it makes it look like a regular message send.
18