CMSC 330, Fall 2005

Organization of Programming Languages

Less than meets the eye: Fast Java for C++ people

By Asad B. Sayeed

Some of you in this semester's (Fall 2005) version of CMSC330 come from a previous stream in which C++ was taught and not Java. In previous semesters, you would have had lectures and discussion sections to introduce you to Java, but now that we're on a new course sequence, well, unfortunately you're expected to know Java. But we aren't totally unmindful of the fact that some of you haven't seen Java: we're giving you this little tutorial to set you in the right direction.

And seriously, it's not that hard. In reality, C++ is a superset of Java. What you have to learn, mostly, is to do without some of the advantages and flexibility C++ gives you—and without some of the grief, too.

Most importantly, what you don't have to know

So like I said, there are many things in C++ you can now forget about. For instance,

  • Preprocessor directives.
  • Pointers or pointer arithmetic. That is to say, you don't manage memory directly except to create objects and local variables.
  • No destructors or delete (ditto).
  • No templates (at least, not until recently—we'll ignore this for the time being).
  • No operator overloading.
  • No need for forward declarations.
  • No mixed object-oriented/non-object-oriented code. In C++ you were probably often used to writing functions outside of classes. You can't do this in Java. Everything, including main(), gets to be a member of a class. Like it or not.
  • No arbitrary booleans. You can't just use any value as a boolean value in a conditional expression. 0/NULL does not mean false. Everything else does not mean true.
  • No multiple inheritance.
The best part of this, really, is the lack of memory management. Java has a garbage collector that knows how to reclaim objects when you don't want them. So some of the more pernicious types of memory leaks shouldn't ever happen.

What you have to learn

So Java is actually somewhere between C++ and Ruby in behaviours. It's got a C++-style syntax, but behind it are some Ruby-like concepts, at least in terms of object-orientedness. But it also has some of its own unique ideas. Here are some of the things you'll have to learn, as opposed to unlearn like above:

  • Everything is an object except a few primitive types---which have to be converted to objects for certain uses.
  • All classes are descendents of Object.
  • Inner classes: you can define classes within classes
  • Interfaces provide a much weaker form of multiple inheritance.
  • A large API (similar to STL) of standard data structures and algorithms.
  • Object references (rather than pointers).

And, of course, there are many other little details in which the languages from one another.

Some resources

We've linked to some Java resources on the main page, particularly the Java 1.5.0 core API. Once you become familiar with Java, this will become the most important piece of documentation you will result, as it catalogues the details of the builtin standard Java library. The other biggest resource is Google (or the search engine of your choice). It will find literally thousands of Java tutorials, articles, and so on, though very few oriented towards making the transition from C++ to Java like this one is.

The example below, incidentally, are inspired by the children's science fantasy cartoon series, The Transformers. For a TV series, it lasted a very long time in several (increasingly computer-animated) incarnations. In my very arrogant and unquestionable opinion, the 80s version was the best, and it's the one I'm using below. And now you know a significant influence on my warped psyche.

Your first Java program

So let's get down to business. Here's a sample trivial Java program:

public class TransformerWorld {
   public static void main(String[] args) {
      System.out.println("The Transformers: More than meets the eye.\n");
   }
}
So what does this do? Well, you have to first paste it into a file called TransformerWorld.java. (In fact, every class should have its own file.) Then you have to run
javac TransformerWorld.java
to compile it. This creates a file in the same directory called TransformerWorld.class. Finally, you have to run
java TransformerWorld
in order to get it to print out "The Transformers: More than meets the eye." and an extra newline character. All of this works on the Grace cluster.

But what does the code itself mean? Well, first of all, it defines a class called TransformerWorld. This class is public: we won't worry about how a class can be public until/unless we start talking about Java packages. This class is also implicitly a descendent of Object.

Next, within the class it defines a main function. This is just like a C++ or C main function, except that in the case of Java, you cannot write methods outside a class. The main function is public, which means what you would expect it to mean if it were C++: it is accessible by any other class. The main function is static, which also means what it does in C++: it is accessible directly on the class, rather than on instances of the class (created by the new operator). It is void; again, just like C—it returns nothing.

None of that was really very interesting or difficult. What's more interesting is the String[] args parameter. Yes, it is an array of String objects, just like you thought. It's an array of command line parmeters, no less. Similar to Ruby, Java implicitly considers all strings to be instances of the String class. Now, you may wonder why there isn't an int argv parameter: after all, how do you know when you aren't seeking beyond the limit of the array. The answer is, you don't have to know this! Unlike C, arrays in Java are objects of type Array. args.length works just fine to find the upper bound on the array. args is just a reference (pointer) to an Array object. Java allows no pointer arithmetic, so there's no way to violate the allocated size of the Array object passed in as args.

Now for the action. System is a special class you never instantiate with new. It simply serves as a placeholder for a collection of static variables containing important objects needed to interact with the operating system. One of these is System.out, an object representing the standard output. println is a method on System.out: it prints things to the console with a newline.

Note, however, that System.out is a reference. It's actually stored behind the scenes as a number that points to the location of a stream object. If this were C++, you'd be accessing its constituent methods and data members with -> rather than a period character. But this is Java, and there's no other way to access members of a class except as a pointer (once again, without actual pointer arithmetic). So we just use a period in all cases. Also note that all parameters are passed by reference/pointer, never by value, unless it's a primitive type (int, char, and so on).

So this is your first Java program. It prints out one line and two newlines (since the escape characters within strings are almost the same as in C++). The code is entirely written in the body of the class definition; this is the rule in Java, in contrast with C++, where you can (and usually should) write the method definitions separately from the overall class definition.

Next, we will talk about inheritance!

Simple inheritance and polymorphism


Energon cubes: necessary for Transformer life

Unlike C++, all methods on Java classes are inherently virtual. So there's no virtual keyword. So with that, we'll write a few classes. First: Tranformer.java

public class Transformer {
   // protected means what it does in C++, give or take a giant
   // caveat through which you could drive a couple of trucks.
   // But we won't get to that this time.
   protected int energonCubes;
   protected String name;

   // Constants
   public static int GOODGUY = 0x0;
   public static int BADGUY = 0x1;

   // Constructor
   public Transformer(int energon, String name) {
      energonCubes = energon;
      this.name = name;
   }

   // Get and set methods.
   public int getEnergonCubes() {
      return energonCubes;
   }

   public void setEnergonCubes(int energon) {
      energonCubes = energon;
   }

   public String getName() {
      // Do note that objects are returned by reference, not copied.
      // It's always safe to return an object!
      return name;
   }


   public void setString(String name) {
      this.name = name;
   }

   // This function performs an action. 
   public void showStatus() {
      System.out.print("I am a Transformer from planet Cybertron. "
                       + "My name is " + name + ".  I have "
		       + energonCubes + " energon cubes left.\n");

      
      if (energonCubes == 0)
         System.out.println("Ooops, no more energon!  That's it for me!");
      else energonCubes--;      
   }
}


Optimus Prime, the Autobot leader.
Under stressful conditions he turns into a freight truck.

Now we'll write two child classes of this class. First, Autobot.java:

public class Autobot extends Transformer {
   int alignment;

   public Autobot(String name) {
      super(5, name);

      alignment = Transformer.GOODGUY;
   }

   public int getAlignment() {
      return alignment;
   }

   public void setAlignment(int alignment) {
      // Look: we have the "this" pointer here.  Only now
      // it's a Java reference to the current object.
      this.alignment = alignment;
   }

   public void showStatus() {
      super.showStatus();
      System.out.println("In addition, I am an Autobot and a "
                         + (alignment == GOODGUY ? "good guy." 
                                                 : "bad guy."));
   }
}

And then in Decepticon.java:

public class Decepticon extends Transformer {
   public Decepticon() {
      super(100, "Starscream");
   }

   public void showStatus() {
      System.out.println("I am a Decepticon and I'm going to tell you "
                         + "NOTHING!");
   }
}

So we have used the extends keyword to make two classes from the Transformer class. Each constructor calls the parent constructor. And each of the two derived classes redefines showStatus. Now let's make a TransformerWorld2.java that uses it.

public class TransformerWorld2 {
   public static void main(String[] args) {
       System.out.println("The Transformers: More than meets the "
                          + "eye.\n");
       
       System.out.println("Here are some robots in disguise.");

       Transformer t = new Transformer(5, "Oracle");
       t.showStatus();
       t.showStatus();

       t = new Autobot("Optimus Prime");
       t.showStatus();
       // The following involves a downcast.
       ((Autobot) t).setAlignment(Transformer.BADGUY);
       t.showStatus();

       t = new Decepticon();
       t.showStatus();        
   }
}

All these classes should be in the same directory. And now you can compile all of this with:

javac *.java

This actually figures out all the dependencies for you along a system similar to make. You can run it by typing:

java TransformerWorld2
You should get the following output:
The Transformers: More than meets the eye.
 
Here are some robots in disguise.
I am a Transformer from planet Cybertron. My name is Oracle.  I have 5 energon cubes left.
I am a Transformer from planet Cybertron. My name is Oracle.  I have 4 energon cubes left.
I am a Transformer from planet Cybertron. My name is Optimus Prime.  I have 5 energon cubes left.
In addition, I am an Autobot and a good guy.
I am a Transformer from planet Cybertron. My name is Optimus Prime.  I have 4 energon cubes left.
In addition, I am an Autobot and a bad guy.
I am a Decepticon and I'm going to tell you NOTHING!

Now take a look at the code for TransformerWorld2. We created one variable of type Transformer and instantiated a Transformer in that variable. We called showStatus twice. Then, using the same variable, we instantiated an object of type Autobot. And then we called showStatus once. Even though t is a Transformer, it still called showStatus. Like I said, all methods are virtual. In order to call setAlignment, however, we had to cast t down to an Autobot, since setAlignment doesn't exist on the parent Transformer class. Note that Autobot's showStatus also calls Transformer's showStatus via the super keyword (similar to Ruby). Both constructors for Autobot and Decepticon use super to call the parent constructor.

Do note that we never deleted the objects we created. In fact, when we reassigned t, we let the previous object in t float off into the ether. That's exactly what we wanted to do. In C++, that would be a classic memory leak. But in Java, we have a friend helping us: the garbage collector. It rummages through apparently "lost" objects, and it destroys them. Now there are things you may need to clean up: there's a way to make the garbage collector do this, but we might save this for later. Nevertheless, Java doesn't really contain an explicit concept of a destructor.

Rudimentary data structure fun

Let's make things a little more interesting. We'll define a TransformerWorld3 class as below (in TransformerWorld3.java):

import java.util.*;
                                                                               
public class TransformerWorld2 {
    public static void main(String[] args) {
        System.out.println("The Transformers: More than meets the "
                           + "eye.\n");
        System.out.println("Here are some robots in disguise.");

        Vector ts = new Vector();
        ts.addElement(new Transformer(5, "Oracle"));
        ts.addElement(new Autobot("Optimus Prime"));
        ts.addElement(new Decepticon());

        Enumeration e = ts.elements();
        while (e.hasMoreElements()) {
            ((Transformer) e.nextElement()).showStatus();
        }
    }
}

We touched on Arrays earlier. And while there are some differences between C++ arrays and Java arrays, the basic semantics are quite similar. They're both fixed-length containers, for one thing. Java offers a simple varible-length container called Vector. You instantiate it as you would any other object, and you fill it with any arbitrary descendent of Object, such as Transformer.

(And what, you ask, is that "import" line on top? Well, C++ has #include directives for header files. Java doesn't use header files, but it organizes libraries into "packages". The import line asks Java to include all the classes in the java.util package, which includes Vector—and Enumeration. You could also omit that line, but then you'd have to write java.util.Vector every time you want to refer to the class. In situations where classes have the same name but different packages, you have to do just that.)


This is Starscream, Megatron's annoying henchbot.
He causes a lot of exceptions.
We'll learn about Java exceptions in a later episode.

Now the most interesting part of this code is the Enumeration bit. Enumerations are ways of allowing us to access members of data structures without having to worry about the details of the structure. It doesn't really matter too much with Vector, but it does give us a handy way of simulating Ruby iterators like "each". So we take the Enumeration of the Vector and step through it in the while loop. Enumerations give us only Objects, so we have to downcast them to Transformers in order to call showStatus.

So let's compile this:

javac *.java

But on Grace, it gives us an error message.

Note: TransformerWorld3.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

This is because Grace is using one of the latest and greatest versions of Java, 1.5.0. This version of Java allows something similar C++ templates, but it uses them in such a way as to help programmers enforce type consistency and avoid downcasting, which is unsafe. (What if the Enumeration also contained a String? Java would throw an exception safely, but you still want to avoid this.) Nevertheless, it will compile code that hasn't been "fixed" this way, and so it's generated a .class file for us. We can run it as usual.

java TransformerWorld3

And we get:

The Transformers: More than meets the eye.
 
Here are some robots in disguise.
I am a Transformer from planet Cybertron. My name is Oracle.  I have 5 energon cubes left.
I am a Transformer from planet Cybertron. My name is Optimus Prime.  I have 5 energon cubes left.
In addition, I am an Autobot and a good guy.
I am a Decepticon and I'm going to tell you NOTHING!

Try stuff on your own

I think you should now have a reasonable idea of what Java entails: syntactically, it's just a fussier and less flexible C++, but with it's good points: less ugly memory management to worry about, for one thing. Of course, you should now take these files and spend some time modifying them to do stuff. Some suggestions:
  1. Allow Transformers to give each other energon cubes, but have Decepticons and Autobots react differently to the transfer. Write this in terms of functions on the Transformer class and children. Remember that objects are always passed by reference!
  2. Learn how to delete objects from the vector. When a Transformer's energon cube level is low, remove that Transformer.
  3. Make use of other data structures from the Java API, such as its plethora of hashing structures.

Until next time.

Valid HTML 4.01!

Web Accessibility