Spring 2004
Monday 3:00 - 5:00 Rm 521, Thursday 4:00 - 5:00 Rm 621
In our week and a half of Java, we have covered a good deal of the language. We were able to do this because we have seen most of the concepts before when covering C++. One of the biggest differences between Java and C++ (and really any other language) is that Java is not exactly a compiled language but it is also not exactly an interpreted language. The designers of Java have designed what is known as Java bytecode, which is an intermediate representation in between source code and machine code. Then interpreters on different machines, known as Java Virtual Machines, or Java VMs, interpret the bytecode. Java VMs are included as part of most modern web browsers.
Another major difference between Java and C++ is that in Java, everything is part of a class. Each class is coded in a separate source code file, and the files end with the extension ".java". A Java compiler (javac on Unix systems) converts a .java file to a .class file, which contains bytecode. If a .class file is created for a class that has an accessible "main" method (in C++, methods were more commonly referred to as member functions), then the bytecode of the class can be run as a standalone application using a Java VM. On Unix systems, that Java VM will be called "java". The "main" method must be declared to be public (same meaning as in C++) and static (meaning it can be called through the class instead of an object, also like C++). It should return nothing and take an array of strings as command-line arguments (although we have not used these in our lectures).
Instead of using Java to create a standalone application, it can be used to create an applet. To create a Java applet, a class must contain one of a few methods, and we looked at an example using the "paint" method to run the applet. To run the applet, in addition to the bytecode, you also have to create an html file that refers to the bytecode file, as we have seen in one of our lectures. Once the bytecode for an applet and a corresponding .html file are created, the applet can be run using an applet viewer (simply "appletviewer" on a Unix system), but that will only work if you are using an environment that allows you to open new windows. The other way to run an applet is to open the bytecode file in a web browser. The source code file for an applet class must import two packages, namely java.applet.* and java.awt.*; a package is a collection of related classes that can access each other. (It is also possible to import a single class instead of an entire package.)
Java provides the static object "out" as part of the provided "System" class; this is the standard output stream. The System class is part of the java.lang.* package, and this is imported automatically. The out object includes a "println" method which accepts a single string (that can be the concatenation of other strings) to display to standard output followed by a newline character. There is also a "print" method which does not display a newline character at the end of the string.
Much of the rest of the basic functionality of Java is the same as C++, including comments (there is also a third type of comment in Java, but we didn't really talk about it), loops, if statements, arithmetic operators, expressions, variable declarations, etc. Java does not include a "goto" statement, but it does have a labeled break statement. Java includes boolean as one of its primitive types (as opposed to classes) which can take the values "true" or "false". Java does not allow any global variables, so a variable is either a local to a method, a parameter to a method, a member variable of a class, or an exception handler parameter. A "final" variable is like a C or C++ constant, except that you can put off the initialization, but once the variable is initialized, its value can not be changed. It is common convention to start variable names with a lower case letter and start class names with a capital letter.
Java does not have pointers, at least not explicitly! Or perhaps another way to look at it is that every Java object (an instance of a class) is a pointer. Maybe considering them references would be better (they act more like a C++ reference, except that two objects that refer to the same memory may not refer to the same memory later). When an object is just declared, only space for the memory address is allocated. When an object is initialized, which can occur at the same time as the declaration or later, the "new" operator must be used to allocate memory for the object. This is not the case for primitives (int, char, etc.); for primitive types, enough memory to hold a value of the appropriate type is allocated, and the variable name corresponds specifically to that memory.
Java performs garbage collection. This basically means that you don't need to worry about losing memory, it will be automatically freed by the run-time environment. Some people do not like this, because it adds an unpredictable element to performance, but on the positive side you don't have to keep track of as much yourself. Garbage collection will happen periodically automatically, but you can also force it by calling the static ".gc()" method of the System class.
Java provides two classes for strings, namely String and StringBuffer. The first is more efficient, but strings of this class can not be modified. Therefore, if two String objects refer to the memory and you try to modify one of them, that object will refer to new memory and the two objects will no longer refer to the same memory, which may or may not be what you want. We have also covered Java arrays; the memory for the array is not allocated at the declaration (except space for a single memory address to refer to the start of the array), but only when "new" is used to allocate the array. Java also allows arrays of arrays, which, as we discussed in a lecture, is not exactly the same as a two-dimensional array in C++; e.g. the individual subarrays (you may tend to think of them as the rows, but this does not make as much sense in Java as it does in C++) do not all have to be the same size! Arrays can be initialized with specified values when they are declared.
Reading from standard input is more complicated in Java than in most other languages. Java provides a System.in object to refer to the standard input stream, but the class of this object is InputStream. From this type, you can only read bytes. You need to convert this to an object of type InputStreamReader (using the constructor of that class), from which you can read characters. (First you need to import the java.io.* package.) However, we can still only read one character at a time. You can convert the InputStreamReader to an BufferedReader (using the constructor of that class), and using an object of this type, you can read an entire line at a time using the .readLine() method. The readLine method may cause an exception of type IOException, and you are required to put all calls to this method in a try block followed by a catch block that watches for this type of exception. Finally, if you want to read an int or a double from the standard input stream, you read an entire line and then convert it using the ParseInt or parseDouble methods of the Integer or Double class. If the input is not of the correct form, these can throw a NumberFormatException which you can catch.
In Java, all classes, by default, extend (i.e. are derived from) the Object class. This allows Java to create some classes in interesting ways. For example, we have discussed the Java Vector and Stack classes, which implement vectors and stacks of general objects. That is, any object of any class can be placed into a Vector object or a Stack object. In order to place a primitive, e.g. an int, you must wrap it to a class, e.g. the Integer class. Java implement polymorphism automatically, so using methods such as getClass, getName, and compareTo you can test, at run-time, the class of an object. For example, if "o" is of type Object, you can test: o.getClass().getName.compareTo("java.lang.Integer"), which, if true, means the object of really of type Integer, and you can the intValue method of the Integer class to get a regular int primitive.
In class, we went over the various modifiers that can be found in the header of a class definition, the declarations of member variables, and the declarations of methods. Many are similar to C++. In addition to private, public, and protected, C++ also has "package" protection, which means that a class or member can be seen by other classes of the same package. An "abstract" class is one that can not be instantiated (this happens automatically in C++ when there are one or more pure virtual functions in the class). The "extends" keyword is used to make one class extend another (in C++, we would say that this class derives from another); as mentioned earlier, if nothing is specified, by default the class will extend Object. (Since all classes extend Object, either directly or indirectly, there are certain methods that are derived from this class shared by all other objects; some of them, such as clone, can be overridden, while others, such as getClass, can not.) We have also discussed in lectures that a class can "implement" an interface, and we have went over why this can be useful; there is no real analogy to this in C++.
Like C++, Java has the notion of "this", which represents the current object through which a method was called. (In C++, it is a pointer to the current object, but Java does not have explicit pointers.) Java also provides an automatically created object called "super", which basically represents the current object cast to its own superclass. This allows you to access member variables of the superclass that have been "hidden", e.g. due to a parameter of the same name, or to access methods of the superclass that have been overloaded. (You can do this in C++ using the name of the superclass and the scope resolution operator.)