This page is obviously not a full introduction to the Java programming language, a topic that could (and does) fill many books. It is instead a quick guide to some concepts that will help you navigate through writing a Java program.
Everything in Java is an object. An object is a collection of data and actions that make up a programming entity. A 'car' object, for example, might have some data (i.e. 'speed,' 'direction,' 'lights on,' 'current fuel,' etc.) and some actions it can take ('turn right,' 'turn lights on,' 'accelerate,' etc.). Objects provide a number of advantages when programming; the include the ability to hide what's going on 'inside' the object from other programmers, which is useful for writing code that others can work with but not easily mess up. However, the biggest advantage to programming with objects is that objects simply 'make sense'; they form the nouns and verbs that you use to write the 'story' of your program.
Our 'car' example would be (pseudo-) coded as follows:
public class Car {
double speed;
double direction;
boolean lightsOn;
double currentFuel;
public void turnRight(){
- code here to turn right -
}
public void turnLightsOn(){
- code here to turn lights on -
}
public void accelerate(double rate){
- some code here to accelerate -
}
}
Some comments to make this clearer; note that this skips a lot of subtle points that we can ignore for now.
Note also the Java convention of naming classes with proper case ("Car") while variables and methods are named with lower case first letters and upper case letters marking the beginning of words inside the name ("currentFuel").
See the semicolons after the variable names? Semicolons end pretty much every line of code in Java.
A constructor is a piece of code that runs when an instance of an object is created. A constructor is indicated by a method that:
Thus, a constructor for the 'car' object might look like:
public class Car {
double speed;
double direction;
boolean lightsOn;
double currentFuel;
public car(){
// Code here runs when a new 'car' object is created
lightsOn = true;
}
}
You can make more than one constructor, using different numbers of arguments for each one:
public class Car {
double speed;
double direction;
boolean lightsOn;
double currentFuel;
public car(){
// Code here runs when a new 'car' object is created
lightsOn = true;
}
public car(boolean lights){
// Code here runs when a new 'car' object is created
lightsOn = lights;
}
}
In this example, the statement:
car MyCar = new car();
will create a new car object with the lights on, because it will use the constructor with no arguments, while:
car MyCar = new car(false);
will create a car object with the lights off, because it runs the constructor with one argument, which gets passed the new value for the lightsOn variable.
It is possible to define an object that uses all of the pre-existing functions of another object you or someone else programmed earlier; this is called inheritance. For example, we can define another object that has all the features of a car, but to which we add more:
public class fancyCar extends Car{
double radioStation;
public void tuneRadio(double newStation){
- code here to tune the radio -
}
}
This code creates a new kind of object, called a 'fancy car,' that 'extends' the original Car object. It will have all of the functionality of the Car object, but will add a new method, 'tuneRadio.'
The keyword 'static' means that only one instance of a given variable exists for a class. This is often used to define constants because their values can be retrieved by invoking the class without creating an instance of it. So if you see "Math.PI", you should recognize that there is a 'Math' object with a variable 'PI', and that there is only one variable 'PI', and that you can retrieve the value of PI by invoking the Math object without creating an instance of it.
Static variables are scoped differently than instance variables, of course, and it is important to keep in mind that the 'main' function is also static and cannot access the instance variables of any given instance of the class directly. One effect of static variables is that every instance has access to them; this is often used to keep a counter of the number of instances that exist.
An important variation on Inheritance is called an Interface. An ‘Interface’ is essentially a contract: an object of Class B that implements Interface Y says, in effect, that type B will live up to certain obligations established by that Interface.
To see why this might be useful, consider the following scenario: You are creating a program about some kind of sport ball. You create, as your most basic class, ‘Ball.’ A ‘Ball’ has certain attributes- diameter, let’s say. All balls will have this, so it’s built into the class. You can then create different kinds of ‘child’ objects, all of which are balls- tennis balls, basketballs, etc.
Let’s say that you know you may also create a collection of other ‘toy’ objects- including water wings, hippity-hops, and bobo dolls. You notice that all of those share a property- they’re inflatable- and, moreover, that some balls are inflatable, too.
Inflatable things have certain properties: they can have a correct pressure, they can have a current pressure, they can be inflated or deflated. You may be writing a program that has to check a collection of these things- balls and/or toys- to see if they’re correctly inflated.
The solution to this is to create an Interface ‘Inflatable’ that says, in effect, the following:
Hear, Ye! Any object that implements this “Inflatable” Interface will have these methods: getCurrentAirPressure, getCorrectAirPressure, deflate, inflateToCorrectPressure, isInflatable.
The correct way, in our scenario, to use this would be to create some children of the ‘Ball’ class using this interface; so:
class ShotPut extends Ball {} // Does not use the inflatable interface
class Basketball extends Ball implements Inflatable {} // Uses the inflatable interface
In the case of Basketball, if you say it implements the Inflatable interface, then you must provide the methods in the contract. Thus:
class Basketball extends Ball implements Inflatable {
double currentAirPressure;
double properAirPressure;
public double getCurrentAirPressure(){
return currentAirPressure;
}
public double getCorrectAirPressure(){
return properAirPressure;
}
public void deflate(){
currentAirPressure = 0;
}
public void inflateToCorrectPressure() {
currentAirPressure = properAirPressure;
}
public boolean isInflatable() {
return true;
}
}
The advantage of something like this is that it allows the programmer to write routines that deal with lots of different kinds of objects, but without worrying whether the objects have certain attributes or not. Anything that implements ‘Inflatable’ will have a ‘deflate’ method, and the programmer using those objects doesn’t need to worry about it not having one or, indeed, to really care about how the programmer who created the object set up the internal workings of the class.
Like many programming languages, Java programs are built in pieces that are generally stored in separate files. A given file, if it refers to pieces that are stored in other files, must be told where to find those other pieces. For example, Java provides a programming object called a Vector, which is used to store data in a structured way. A piece of programming code that makes use of the Vector object must contain a line like:
import java.util.Vector;
This line is placed before the class definition begins.
Java objects are used in one of two ways. The most common way is as a programming component that is instantiated by another program. However, some objects are endowed with the ability to be run as programs (it has to start running somewhere!). This is achieved by putting a 'main' method in the object. Thus:
// Hello World
class HelloWorld {
private static void main (String[] args){
System.out.println("Hello, world!");
}
}
An inner class is a class that is declared within another class. Scoping in this case is similar to variables declared within a class.
Java organizes code pieces into packages. Packages are primarily an organizational tool, but certain scoping constraints are impacted by them (that is, things in one package may be usable by other things in that package but not outside it). In the 'import' statement above, java.util is a package, which contains the object 'Vector'.
In your own work, a package is generally formed by having all the source files for all the objects in the same directory, and having a statement at the beginning of the source file like:
package myPackage;
When the Java compiler compiles the program from the source code as you've typed it into code that can be run using a Java capable machine it needs to know where all of the requisite pieces are. This is typically done in one of several ways. One way is that the necessary files can all be placed in the same directory as the files you're writing, but this is a difficult way to manage projects. As a second method, the compiler, when run from a command line, allows a command line option to be entered that defines the "classpath", which lists all directories that should be 'checked' for pieces needed by your code. A third method allows a system variable to be defined that stores the 'classpath' permanently. A fourth method is perhaps the simplest: the development environment can be configured to examine a given build path. This is of special importance because libraries of pre-made Java objects are often distributed and can be stored once and used in many different Java projects simply by setting the build path.
RePast is distributed in a jar file and is used by making this jar file available on the build path. RePast also makes use of some additional libraries, like colt.jar (see using the 'shuffle' routine), that must be made available to the compiler using one of these four methods.
Java ARchive files are like zipped files in that they are single files that contain compressed data. The data are typically archives of Java files, either source code or compiled. In this way a package that includes hundreds of source files can be distributed as a single file. This also makes setting the build path easier.
Java classes are documented using an API specification; API stands for Application Programming Interface, and it provides the documentation necessary to make use of the pieces that a collection of Java code provides. Of greatest importance, the Java language itself, aside from core elements like syntax and data types, is described by the API that documents all of the Java objects (like Vector, mentioned above) that are available in the core Java environment. This API is available from SUN; the latest version is at http://java.sun.com/j2se/1.4.2/docs/api/index.html.
As in all computer code, comments should be used to make the operation and structure of the code clear to other programmers and to yourself after enough time passes that you've forgotten the details. Java permits code comments to be inserted in several formats:
// This type of comment is a single-line.
int x; // it can be inserted at the end of lines
/* This kind of comment uses a slash and asterisk to open
the comment, and can
continue across lines until an asterisk-slash
closes it */
JavaDoc is an extraordinarily useful tool. All code that is written should be thoroughly documented, both inside the code and in some format that allows others working with the programming pieces but without direct access to the source code. JavaDoc takes comments that are placed in the source code itself and translates them into automatically-generated html files. The Java API is a perfect example. I strongly recommend you cultivate the construction of JavaDoc compatible comments as a matter of habit in your Java programming. See http://java.sun.com/j2se/javadoc/ for the formats required for this.