Notes on Java
Back
To Mike's WebToys Page!
This page was created as a summary of what I learned in order to use
Java effectively. Keep in mind that I've several years of C,C++ experience,
and so this material tends to focus on differences and new stuff, rather
than basic syntax.
Differences between C/C++ and Java
-
No preprocessor. No hash-define, no hash-ifdef's, nothing.
Note that most Java compilers should eliminate dead code, thus something
like
if( false) { . . . }
should be the equivalent of conditional compilation.
-
No global variables, functions, or classes. Everything is part
of a package, and has a name the reflects that. You can create static
(class) methods to simulate this, but it will still prevent stuff like
multiple sleep functions from overlapping/colliding.
-
No environment variables. There are system properties that can be
accessed through System.getProperty, instead. These properties can
be pass to the Java interpreter through a command-line argument
-
One public class per .java file, and that class must have the same name
as the file. .java files get compiled into .class files
-
.class files must reside within the file system at the same point as they
do in their package hierarchy. Thus, java.lang.System's class file
must be located in java/lang. The 'core' classes are placed in a
special directory, everything else uses the CLASSPATH variable (see packages)
-
Loading classes: core classes are in a trusted location. If the .class
file isn't found there, then the Java interp. looks in (in this order:)
In the current directory, then by appending the package name to the
current directory
In each of the directories/ZIP files listed in the CLASSPATH
variable, then by appending the package name to the directories.
-
No constants. Any variable declared final isn't allowed to change
value. This retains type-checking, which is nice.
-
Java uses Unicode character, not ANSI/multibyte characters
-
All variables have default values. Numbers default to zero, booleans
to false, etc.
-
Objects are manipulated with references to them (NOT POINTERS!)(though
they sure look an awful lot like them). Note that the objects references
are passed by value (to method calls)
-
no sizeof operator
-
like C++, local variables can be declared anywhere, and can be initialized
at the time of declaration.
-
forward references are ok, so we don't have to define things to be able
to use them (prior to their declaration). The only exception is that
variable initializers may not be forward-declared.
-
void can be used as a return type (ie, no return value), but can't be used
in casts, and empty argument lists are written (), not (void)
-
no structs, unions, enums, function pointers
(use functors, instead), bit fields, typedefs, or variable-length
argument lists
-
no mulitple inheritance (yeah!)
-
no operator overloading (yeah!!)
-
no templates (but every object is a subclass of Object, so this
isn't bad)
Objects
The keyword null indicates that the reference is invalid.
To copy an object, the object must support the Clonable interface.
To create an object:
-
use the new operator
-
Strings can be created using literals - "This is a string" gets converted
by the compiler into a string object
-
use the class object's newInstance() method
-
Java1.1: unserialize a serialized object
Variables: Within a method, the variable this refers to
the instance itself. You can declare a field or method to be a 'class
variable/method' using the static keyword. Within the class,
you can refer to static variables/methods as if they were regular instance
variables, but outside the class, you must prefix the variable/method with
the class name, like so:
Thread.sleep( 1000 );
Note that class methods don't have a this variable.
Constructors: Constructors (C'tors) exist, and can be overloaded.
Within a C'tor, you can invoke another constructor via a call to this(
<args, if any> ).
Static Initializers: Just as constructors initialize a
given instance, static initializers initialize a class when it's loaded.
Note that you can have multiple static initialzer methods in a class, and
that at compile-time, all the static variable initializers, and static
initializer methods get concatenated together to get one huge static initializer.
Note also that since it gets executed when the class is loaded, it takes
no arguments, and returns no values. It's declare like so:
static { ... }
Destructors: The finalize() method is used as a destructor,
kinda. It's an instance method, takes no arguments, and returns no
values. This gets invoked when the object is garbage collected, which may
not happen before the JVM exits. Note that finalize() is
invoked once and once only, thereafter the object is rechecked to see if
there's a reference to it elsewhere. Otherwise, it could hand off
it's this reference to another object in the finalize method,
and then get garbage collected, thus allowing a reference to an invalid
object.
Inheritance:
Use the extends keyword to indicate that a class is
a subclass of another class. The parent and child classes can be
in different packages. Classes declared final can't be subclassed.
Constructors: to call a superclass's constructor, use super(
<args, if any> ). This can only be called within a constructor,
and must be the very first call in the constructor. If it's not,
Java automatically puts a default call ("super()") there for you,
ensuring that the highest superclass's constructor is called first, then
the next,etc).
Destructors: finalize() methods aren't chained like
constructors are, so you have to do this yourself. It's recommended
that you call the super.finalize() method at the end of your
finalize().
Shadowed variables: Subclasses can declare variables with the
same name as variables in a superclass, which are accessed when no qualification
is used, thus 'shadowing' the super variable. There are three things
to note: this.variableName to explicitly refer to the subclass's
variable (when variableName is used alone, it does the same thing),
super.variableName to access the immediate superclass's variable,
or ((SuperClassName)this).variableName to get to any super class's
variable. Note that you cannot refer to overridden methods this way.
Overridden methods: outside the class, you can't invoke
a parent's method, no matter what. Using a dynamic invocation mechanism,
the 'most specific' method is used. final methods (which can't
be overridden) use static invocation. Within a class, you can use
super.method() to invoke superclass methods, but only on the immediate
parent.
Visibility of fields and methods:
Type |
Accessible To |
public |
everyone |
protected |
the class, subclasses, but no others |
private |
the class, only |
package (this is the default, if you don't specify one of the others) |
the class, any classes in the same package |
Abstract class/methods: Any method declared abstract doesn't
have an implementation, and automatically makes the class an abstract
class (and thus can't be instantiated). Classes can likewise be declared
abstract. A subclass of an abstact class that doesn't implement
all of the abstract methods is also, automatically, made abstract.
Arrays
The indeces of arrays are of type int. Arrays can be allocated like
so:
byte arr1[];
Object arr2[1024]; //allocates space for
the Objects, not the Objects themselves
can initialize objects using something like:
byte arr3[] = { 1, 2, 3 };
in Java1.1, you can also anonymously initialize arrays, doing something
like
O.Method( new String[] = { "yup", "second" } );
You can declare multidimensional arrays, which are actually arrays-of-arrays
(or rather, arrays of reference to arrays, since arrays are kind of like
objects), like so:
String[10][20][][]; //note that all arrays
of specified dimension must come first - String[][10] is erroneous
Nested initializers are fair game, and the multiD arrays don't have
to be rectangular
Interfaces
One of the really cool things about Java is the language-level support
for Interfaces. And this maps really well into ActiveX, which is cool.
Declared using the interface keyword, in the same place that a
class keyword would go. Any methods in the interface are abstract,
and any variables must be declared to be static final (ie, constants),
which can be accessed outside the class via the Interface.variableName
syntax like for a class variable. Classes that implement an interface
can be treated as instances of that interface, just as if the interface
was a type or class. They can also access the interface constants
without having to prefix the constant with the interface name. Interfaces
can inherit from on and other, and can inherit from multiple interfaces,
if they choose. They inherit using the extends keyword,
just like classes. Interfaces with no methods or variables can also
be used as marker interfaces, in that other classes can
see if the class implements an interface, and if so, take an action based
on it. Like the Cloneable and Serializable interfaces.
Exceptions
Exceptions themselves are objects, meaning that they have fields, methods,
and can be / are dynamically created. Exceptions are subclasses of
the Throwable class, and fall into three categories. Errors
are things like linkage (class couldn't be loaded) and JVM errors (out
of memory), and the program can't be expected to actually handle.
Normal Exceptions are stuff the the program should handle, like
FileNotFound errors. RuntimeExceptions (like NullPointerExceptions)
could conceivably be handled by the program, but are prevalently possible
that we don't have to specify that our methods throw these exceptions.
If a method throws an exception, it must say that using the throws
clause of the method name, like so
public void method() throws IOException()
methods throw exceptions using the throw keyword, and giving it an
Exception object, like so:
throw new Exception( "Exception Message Here" );
If a method A invokes another method B, and B throws some exception,
method A must either handle the exception, or list the exception in it's
throws clause. To handle the exception use the try..catch...finally construct
try { ... }
executes the code, and eventaully returns, either becuase the end of
the block was reached, or because of an abnormal exit, which include exceptions
being tossed, and break, continue, and return
statments.
catch( ExceptionType E ) { ... }
Every try must have 0 or more catch clauses. if there are 0 catch
clauses, then there must be a finally clause. If there are
multiple catch clauses, the first (ie, top-most) clause that matches the
thrown exception will be invoked, and none of the others will (but the
finally clause,if it exists, will be). Exception handling code goes
in here.
finally { ... }
The finally clause is always executed, regardless of how much
of the try statement, or if any of the catch statements are executed. This
includes returning from the try clause because of a break, continue,
or return. Useful for clean-up
Packages
A package is a collection of classes, for example java.lang. Creating
a package yourself is easy, though there are some tricky issues with placing
these files in directories that you have to watch out for.
Packages are declared by simply putting the text package pckName
at the top of the .java file. Any classes defined in the file are now part
of package pckName. You can then compile this into a .class file. Note
that subpackages are delimited by periods (e.g., edu.cornell.mwp3.Whatever.*).
The tricky part is figuring out how to setup the directories in relation
to your CLASSPATH variable. If you have a package named pckName that contains
class Foo.class, Java expects the for some directory in your CLASSPATH
variable, there will exist a subdirectory named pckName. Within that directory,
there will exist a file named Foo.class. The way you then run that class
(assuming it has a main() method, etc) is to invoke the following:
java pckName.Foo
The package stuff can be frustrating since you can cd to the directory
pckName, and type java Foo, only to be told that Java "Can't find
class Foo", even though it's in your current working directory!
Note that you can abbreviate class names by use of the import keyword.
By saying import java.net.URL, you're telling the compiler that any time
you refer to URL, you mean java.net.URL. Pretty handy. Also,
you can do import java.net.*, meaning grab everything in the java.net package,
and allow the abbreviations to be used. Note that it's a compile-time
error for a naming collision to occur because of this (ie, you can't import
java.net.URL, mike.net.URL, and then try and instantiate a "URL" object)
Inner Classes
Nested classes for Java. Useful for the new Java1.1 AWT event model, because
it allows one to create lots of small objects to use as functors without
cluttering up a package or namespace. At this point, I'm not interested.
Keyword summary
abstract: class is abstract (can't be instantiated), or method doesn't
have an implementation. Classes that contain abstrat methods are
abstract.
extends: to indicate that a class is a subclass of another class.
catch: catch an exception
final: Once initialized, a variable tagged with "final" isn't
allowed to change value. final methods can't be overridden, and final classes
can't be subclassed.
import
native: method isn't implemented in Java - use ";" in place
of a method body
null
package
synchronized: method will lock class or instance (for class
or instance method, respectively) before executing
throw: throw an exception
throws: placed after the method name/args, indicates that the
method may throw an exception
transient: in Java1.1, marks a field as being something that
won't get serialized
try: exceute the code in the block, which may thrown an exception.
volatile: marks a field as being used by multiple threads, so
access the real variable each and every time you use it
Class Hierarchy
Using Files
There are (at least) three ways to manipulate files. The java.io.File
class is for manipulating files in a manner similar to command-line ls
/ dir stuff. It doesn't allow you to read or write the files, but
you can list directories, etc. If you want to read/write files, you
should be looking in the InputStream/OutputStream subclasses (for byte
streams), and (in Java 1.1) the Reader/Writer classes (for character streams,
which include handy things like readLine/writeLine). Note that InputStreamReader
forms a nice bridge between the two, allowing you to take an InputStream,
hand it to the constructor of InputStreamReader, and get something that's
a subclass of Reader. One of the tricks to using streams is figuring out
when they've finished. For Reader streams, if the method returns
-1, the stream is finished. If the BufferedReader.readLine() returns
null (instead of a String object), then the stream is finished. Otherwise,
there's something left. This is especially important for streams like the
one you get back from a URLConnection.
Accessing URLs
java.net.URL, which can either generate the InputStream directly (getInputStream(),
which can be wrapped into an InputStreamReader, then into a BufferedReader,
to do a readLine-by-readLine access), or through the getConnection() method,
which gives you a URLConnection, which can be twiddled for various purposes,
and from which you can then get the InputStream.
Object Serialization (Java1.1)
ObjectOutputStream.writeObject( Object o) serializes an object into a stream,
ObjectInputStream.readObject() deserializes it. Pretty cool, sounds
like. The object must
-
Implement the Serializable or Externalizable interface
-
fields marked transient aren't serialized
-
custom (de-)serialization may be done, if you want -- implement a different
writeObject()/readObject()
Reflection (Java1.1)
Dynamic invocation, made accessible to the outside world. java.lang.Class
has been extended to return more info - can now get Field, Method, and
Constructor objects (and interrogate them)
Java Beans (Java1.1)
Uses the reflection stuff & a naming scheme to expose events, data,
etc. Split into GUI Builder stuff and Bean Dev stuff
GUI Builder: Introspector returns BeanInfo, which enumerates
FeatureDescriptors. Lots more
Bean Dev: auxilliary classes to facillitate interaction with
the GUI Builder stuff
AWT: Layout managers
It would be nice if we could have a layout manager that simply allows to
"put button at x,y", but it's not here yet. I've not clue how one
implements a LayoutManager, either. LayoutManagers include:
- FlowLayout
- The default layout manager for a container, it put things into the
container as best they'll fit, left-to-right in first-to-last order
- CardLayout
- aka property sheet
- BorderLayout
- Allows for N,E,W,S,Center placement
- GridBagLayout
- GridLayout