Grinding Java - Introduction

From EDM2
Jump to: navigation, search

Written by Shai Almog

Introduction

I remember the first article I read about Java in Dr. Dobb's Journal: I reached the word interpreted and skipped over to the next article. Many programmers treated Java the same way at first. Hype does not work on most of us and I felt at first that Java was mostly hype. When I was looking for a new job I figured Java appearing on my resume would look real good so I downloaded one of the early betas of the JDK [Java Developers Kit - Ed] and started learning. I was not much impressed at first. As a person who grew on the knees of IDEs [Integrated Development Environments. Ed.] I was annoyed by this command line debugger most of all. But soon enough I found out that I needed a debugger much less than I ever needed one when writing C++ code. I actually found myself having fun learning a very simple and intuitive new language, with possibilities that almost reach the level of the hype surrounding it.

Warning
don't read on if you know all of Java's basics, it will be a waste of your time. If you are well into C++ [or even C - EDM] then browse through the article and look at the comments I made about the code. There are some interesting things for you. Next month I will introduce AWT and after that things will start getting more interesting.

I intend to make the first few articles an introduction to Java. I chose the JDK 1.1 because it will become a standard by the time you will finish reading the introduction (knock wood). It's far better than JDK 1.02 (and by any other vendor it would have been a major version upgrade). I will teach Java applications rather than applets. [Java applets are small Java routines that only work within an applet browser, or web browser, and are usually subject to quite tight security. Java applications run by themselves, and usually have more freedom, so you can accomplish more with them. Ed.] Most tutorials focus on applets and I feel applications will be more interesting to the OS/2 audience and that I will have less chance repeating myself. Debugging applications is easier and it's relatively easy to move application code to applet code (with considerations of applets limitations). I will assume that you know the hype but don't have Java background, and I will also assume that you have some minimal programming knowledge. If any of my terms seem confusing check out the Free On-line Dictionary of Computing or some of these Java related links I found useful:

And Yahoo of course has a huge Java section.

Java hype and misconception versus reality

  • Java doesn't have pointers - Slight hype and not a positive one at that. Java has nothing but pointers and native types. You need to allocate memory using the "new" operator but you don't have to free it (automatic garbage collection will do that) and range checking is applied to arrays. You cannot refer to a variable's memory region (no pointer arithmetic) and you cannot have two types on the same memory region (union). For example:
MyClass A,B;       // I define my own data type and 2 instances of that type.
A = new MyClass(); // I must allocate space for A before using it.
B = A;             // B and A are now pointing at the same thing any change you
                   // will make to A will apply to B and vice versa.
int C,D;           // Two numeric variables
C = 1;             // C equals 1.
D = C;             // D equals 1;
C = 2;             // C equals 2. D still equals 1. This is because int is a
                   // native type to Java which means the default action is
                   // to copy it and not to create a new reference to it.

// if you wish to copy one of your types in a similar manner you can use:
B = A.clone();     // This will create 2 separate instances of the same type.
  • Java is easy for C++ programmers - Yes. Java is very easy for C++ programmers, but you need to get used to the differences that look identical in Java and C++ but have different meaning. The example above is the most obvious case, but there's more. I highly recommend the Taligent page for converting C++ to Java] for all of you C++ programmers out there.
  • Java is object-oriented (object based) - Java is very OO I feel it's more so than C++ ever will be (but that is debatable). Java does not support multiple inheritance which I believe to be a plus since most C++ programmers misuse that powerful feature of C++. Java supports interfaces which are a very pure OO tool. Java evangelists say Java is above all regular OO languages and is in fact based on objects. Well, inventing new terms is a real cool way of adding features, and Java does add many paradigms I have yet to see in other languages, but it is debatable whether they are a part of the object paradigm.
  • Java is small - if the mean a small class file size, they are correct they are very small and with JDK 1.1 Jar file format (a compression for multiple class files) it will be even smaller. Java was optimized for size since it's supposed to run with low resources and low bandwidth. Java's APIs are definitely not small, and they are growing at a most unbelievable rate.
  • Java is secure - The bugs that were found in the early JDK were things that would have taken forever to be found if Java were not openly developed. Java deals with most security issues with the virtual machine concept, Java is secure but the issues involved are very complex I might deal with them in a future article. Check the item below for more information.
  • Java programs can't access the disk - Java applets in today's browsers (such as NS 3) cannot access the local disk in any way (read or write). Navigator 4 will allow signed applets to be given permission by the user to access the disk. In future versions you will be able to throw restrictions at applets so as they will only see certain parts of your disk. These restrictions do not apply to applications which are 100% free to do as they will, since it's assumed the user is responsible for the application they are running.
  • Java does GUIs - True Java is very good at GUI development, but it's not all that it does. Java has excellent command line interfaces, and developing command line Java utilities has a (possibly) bright future. I am told that javac (the java command line compiler) was written in Java.

There is much more to say (e.g. multithreading, persistence, networking) but there are many other articles dealing with the those issues.

Hello world

We will start learning Java using the traditional hello world program: HelloWorld.java

public class HelloWorld
{
  public static void main(String argv[]);
  {
    // Prints Hello world.
    System.out.println("Hello world");
  }
}

Things to notice:

  • The file name and the class name have the same name Java is case-sensitive which means there is a difference between the lower and upper case letters, i.e. X does not equal x. (Be warned: the file name is case-sensitive too in OS/2).
  • Keyword class - the class keyword is familiar to all of you C++ people but you might not know what it's doing here. In Java everything must be grouped in a class which is the type which you use to construct an object. Think of a class as the mould which you use to bake the objects, all of them are made from the same mould. A class has methods and properties, the methods are functions which usually relate to the class and the properties are the information contained in the class.
  • The class is declared public. The public keyword means every object can have access to this class.
  • "main" is a method. This method is actually the method that gets called when you run a Java application, very much like C's main();
  • "String argv[]" is the parameter that is passed to the method, if the application is run from command line then the string array (I will explain arrays soon) will contain the list of parameters.
  • "main" is declared as "public static void". public: So we can all see it. static: the static key word has a meaning that the subject that is being declared is related to the class but not to the object (instance). In other words, a static method cannot change any property (variable) of the class (since it's related to the specific class) and a static variable will always be the same in all the instances of the class, so if one object changes it, another one will see the change immediately. void: void is a null type. When you wish to define that a method returns no value you specify it to return a void (as is in C).
  • System - is a Java standard class (java.lang.System) which is used to encapsulate the basic functionality needed by a programming language (such as system I/O and more). To gain better understanding of this class try running the command:javap java.lang.System
    This should show you the public methods that the class System has. You will find javap to be one of the most useful tools available in the JDK.
  • System.out - The out property of the class system is actually an instance of java.io.PrintStream which you can see by typing the command: "javap java.io.PrintStream". It has a method called println(java.lang.String); that method prints out the string and then moves to a new line (like writeln() in Pascal).
  • "Hello world" - This is for you C++ people. A C++ programmer will see a string, but a Java programmer should see an object of type string. This is very important to the understanding of Java. Everything is an object.
  • A comment starts with //. Everything after these two characters until a new line is ignored. Comments are a necessity to document code and Java supports both of C++'s comment styles, i.e. from the "//" to the end of line, and comments starting with "/*" and ending with "*/" [like C - Ed]. Java has a tool called Javadoc which turns comments into documentation, but I will not cover that here.
  • Naming conventions - by convention a method or property name will start with a lower case first word and then the second word and on will become upper-cased i.e. getData(), myVariable. A class name starts with upper case and continues with upper case of every word as is in methods: MyClassName, ControlPanel

Extending HelloWorld (variables, arrays, input and flow control)

Hello world was very nice (not) but we need something more before we get to the heavy stuff. First we will have to make a small modification to HelloWorld.

public class HelloWorld
{
  public static void main(String argv[]);
  {
    new HelloWorld();
  }
  public HelloWorld()
  // The class constructor.
  {
    System.out.println("Hello world"); // Prints Hello world.
  }
}

Some of you C++ programmers might have confused the main method with the constructor and the new HelloWorld() statement might not be clear. A constructor is a method that is called every time a new instance of the class is created (by using the "new" operator). Our program starts execution in the main method (as usual) where it allocates memory for the class HelloWorld and calls its constructor, notice the main method does not need the class to be allocated because it's a static method and does not use any of the variables (properties) in the instance. The constructor goes on as before. Why was this change needed? A static function cannot reach the properties: imagine a program with no variables.

Variables

Java supports the following native types:

double  - used for floating point numbers.
float   - used for floating point numbers.
int     - 32 bit round number.
char    - An Unicode (16 bit) character.
short   - 16 bit round number.
long    - 64 bit round number.
byte    - 8 bit round number.
boolean - Contains true or false.

Another example:

public class HelloWorld
{
  public static void main(String argv[]);
  {
    new HelloWorld();
  }
  public HelloWorld()
  // The class constructor.
  {
    System.out.println("The number is:" + Number);
    // Notice how java converts the type automatically
  }
  private int Number = 3;
}

Notice the keyword private. It means that no other class may use that property (in this case Number) and thus it protects us from depending on implementation specific concepts. This concept is called encapsulation and is one of the basic concepts of OO programming. Encapsulation is the idea of hiding the implementation, the detail of how the task is done, from the way you use the object, the interface and thus grouping functionality into manageable classes rather than huge APIs. The best example of encapsulation is in GUI programming. Writing API specific code is a Herculean task with hundreds of APIs for each platform. Class libraries make this task easier by providing classes that wrap the API with simpler related methods. Then if you want to port your program all you have to do is change the hidden layer which is platform specific. That is why hiding the implementation is very important, so you or any other programmer won't rely on specific code. Wrapping things up and only using the higher level wrappers is referred to as abstraction.

if statement

The if statement is one of the most basic things of every programming language I know. An if statement is where your code enters a crossroad by testing a condition and choosing the correct path. An if statement accepts a conditional statement:

if ([Conditional statement])
  // statement to execute if the conditional statement is true.
else
  // statement to execute if the conditional statement is false.

If you wish to perform multiple commands as a result of an if statement you can use the following syntax:

if ([Conditional statement])
{
  ... // as many statements as you want.
      // if the conditional statement is true.
}
else
{
  ... // as many statements as you want.
      // if the conditional statement is false.
}

A conditional statement returns true or false, and they can be nested using conditional operators. This is quite a basic notion of programming and I assumed you have some knowledge of programming so I will not run too many examples by you.

A simple example will speak in this case much better than any definition:

int Number = 3;
if (Number == 3)
  System.out.println("It is a 3");
else
  System.out.println("It is not 3");

This program will obviously print out: It is a 3. Notice that the operator == was used and not =.

The following operators are allowed in conditional statements:

  • ==
    Equal. In C++ == determines if the two variables contain the same value this is not true in Java (unless you are working with native data types). In Java this operator checks if these 2 classes are of the same instance and not if they contain the same data, every Java object has a method called i.e.
Integer I(3);
Integer B(3);
if (I == B)      // will return false
if (I.equals(B)) // will return true.
I = B;
if (I == B)      // will return true
  • <
    Bigger than operator is quite similar to the == operator and useful for Native types.
  • >
    Smaller than operator is quite similar to the == operator and useful for Native types.
  • !
    Operator not. It turns true into false and false into true. != Operator not equal. it returns true when a variable is not equal to another variable.
  • &&
    Operator and takes two boolean values and if both of them are true it returns true, otherwise it will return false.
  • ||
    Operator or takes two boolean values and if one of them is true it returns true, otherwise it will return false.

I'll give some examples and results:

((1==5) && (1==1))
  // in english (1 equals 5) AND (1 equals 1) == false
((1==5) || (1==1))
  // in english (1 equals 5) OR (1 equals 1) == true
((1!=5) && (1==1))
  // in english (1 is not equal of 5) and (1 equals 1) == true

Loops

Java supports loops almost (if not totally) identically to C.

For loop - A for loop is designed to iterate to a known or measurable number while counting iterations.

Syntax:

for ([early variable initializations];
     [Condition that will stop the loop];
     [operation to perform with each iteration])

Example:

for (int I = 0;I < 100; I++)
  // the ++ operator increments a number by 1. The
  // -- operator is the decrements by 1. It's like I = I + 1;
{
  ... // The body of code to be repeatedly performed
}

While loop - checks for a condition and then performs a body of code as long as the condition is valid.

Syntax:

while ([condition])
{
  ... // The body of code to be repeatedly performed
}

Example:

while (I < 100)
{
  ... // The body of code to be repeatedly performed
}

Do - Identical to the while, except that the first iteration of the loop will always be performed and then the condition will start getting checked after that.

do
{
  ... // The body of code to be repeatedly performed
} while ([condition])

Example:

do
{
  ... // The body of code to be repeatedly performed
} while (i < 100)

The following program will accept command line arguments and print them.

public class PrintArguments
{
  public static void main(String argv[])
  {
    for (int i=0; i < argv.length; i++)
      new PrintArguments(argv[i]);
  }
  public PrintArguments(String Token) // The class constructor.
  {
    System.out.println(Token);
  }
}

Things to notice:

  • We create an instance of PrintArguments for each argument, yet we don't delete any of them. This is automatically done by the garbage collector.
  • argv.length returns the number of arguments. This feature is common to all of Java's arrays: they all have a length property.

Exception handling

One of Java's nice features are thread safe and predefined exceptions. Exception handling exists in an almost identical way in C++.

What are exceptions?

An exception is a point in the code where something out of the ordinary has happened and the regular flow of the program needs to be interrupted; an exception is not necessarily an error. A method which has run into such a case will throw an exception using the throw(ExceptionClass) method. When an exception is thrown it must be caught by a catch statement that should sit right after a try statement. That is a bit complicated, but to make things simpler I will explain why this complexity is really needed. To start a section of code which might fail or not follow through you start a try clause:

try
{
  // Section of code which might fail
}

After the try section there must exist one or more catch statements to catch some or all of the exceptions that can happen within the try block. [Exceptions that are not caught will be passed up to the next level, such as the function that called the one which threw the exception, and so on. - Ed.]

try
{
  // Section of code which might fail
}
catch (Exception1ThatCanHappen E)
{
  // things to do if this exception was thrown..
}
catch (Exception2ThatCanHappen E)
{
  // things to do if this exception was thrown..
}

This has the following advantages over handling your errors internally:

  • You can react to an error in custom defined way. A read error does not mean that the program should crash.
  • You can write code with no worry about failure which will be handled by the users of your class.
  • You can group your error handling code much better.
  • You can make your application more transactional focused with nested try catch blocks:
public class AnnoyingPrintInputClass
{
  public static void main(String argv[])
  {
    new AnnoyingPrintInputClass();
  }
  public AnnoyingPrintInputClass()
  {
    try
    {
      do
      {
        lastCharacterTyped = (char)System.in.read();
        // Type cast is needed to
        // convert int to a char convert an
      }
      while (lastCharacterTyped != '\n'); // The new line
    }
    catch (java.io.IOException E)
    {
      System.out.print("An I/O error has occurred!");
    }
  }
  char lastCharacterTyped;
}

Things to notice:

  • The try statement is needed in case of an exception. If the read fails in some way it will throw an exception of type java.io.IOException. That exception must be caught in this method, or the method can declare that it will continue throwing that message. Exception handling is a very powerful tool, you can use it to catch and throw exceptions and thus group all your error handling code very well and make it much more readable in larger more complex applications.
  • We perform a type cast. Java is strongly typed which means the compiler won't guess what type you mean and will not let you do every thing. E.g. if you will try to put an int (32 bit) into a short (16 bit) you may lose some digits. Some languages will only warn you of this (or worse). Java requires you to explicitly state that this is the action you want (that you are willing to risk the digit transaction). For example:
int a = 100;
short b = (short) a; // Correct
short b = a; // Incorrect.
  • '\n' is the new line character (as in C and C++). Thus the loop will stop when the user presses enter.

Arrays

public class ArrayExample
{
  public static void main(String argv[])
  {
    // Default value of 30.
    int arraySize = 30;

    // Notice that in the if statement == is used to test quality.
    if (argv.length == 1)
    {
      Integer I = new Integer(argv[0].trim());
      arraySize = I.intValue();
    }
    new ArrayExample(arraySize);
  }
  public ArrayExample(int sizeOfArray)
  {
    charactersTyped = new byte[sizeOfArray];
    try
    {
      System.in.read(charactersTyped);
    }
    catch (java.io.IOException E)
    {
      System.out.print("An I/O error has occurred!");
      System.exit(1);
    }
    String outString = new String(charactersTyped);
    System.out.println(outString);
    System.out.print("The third character is :"
                     + (char)charactersTyped[2]);
  }
  byte charactersTyped[];
}

Things to notice:

  • We read a block in one go. The read method automatically uses the length property of the array.
  • The Integer class is used to encapsulate methods on Integers.

Inheritance, interfaces and polymorphism

To those of you who are used to C++ this section is really unneeded. I hope you know the basic OO concepts because I'm not going to explain them in depth here.

Say you built a class called MyButton but MyButton only supports text labels and no image buttons. Let's say you (or someone else) wants to build an Image button. You can sit on your old button code and modify it and hack it into becoming an image button but then you will have a big and bloated class that does 2 distinct things. You could write a whole new class based on the old MyClass source, but then your old code that uses button might not be easy to convert back and forth with image button and you will have code duality between Image button and button, which means that every change you wish to add to both you will have to do in both of them.

OO programming comes with the solution. You can inherit the original class and have all its functionality, and you can override some of the old functionality.

A hello inheritance program will be the best example:

class BaseClass // Base class not public so I
                // don't have to define it in a new file.
{
  BaseClass()
  {
    System.out.println("BaseClass is constructed");
  }
  void sayHello()
  {
    System.out.println("Ahalan from the base class");
  }
}

public class HelloOO extends BaseClass
{
  public static void main(String argv[])
  {
    new HelloOO();
  }
  HelloOO()
  {
    System.out.println("HelloOO is constructed");
    sayHello();
  }
  void sayHello()
  {
    System.out.println("Hello OO world");
  }
}

The output of this program is:

BaseClass is constructed
HelloOO is constructed
Hello OO world

But say, I'd like to modify the functionality of the previous method, not replace it, how do I do that? The keyword "super" does it for me. The Java tutorial defines super as: super is a Java language keyword that allows a method to refer to hidden variables and overridden methods of the super class (parent class).

To make this more visual: The inheritance mechanism actually creates a full instance of the class you are inheriting and adds on the properties of the subclass afterwards. A super is a pointer to the part which is the parent class that doesn't know of the sub classing [inheritance - Ed].

void sayHello()
{
  super.sayHello();
  System.out.println("Hello OO world");
}

Here super is a reference to the instance of the class you've inherited so in other words super.sayHello() will invoke BaseClass.sayHello() and not HelloOO.sayHello()

The output of this modified program is:

BaseClass is constructed
HelloOO is constructed
Ahalan from the base class
Hello OO world

This is all very nice, but wait there is more. Homomorphic class design is the paradigm based on the idea that every code that works with a base class should work with all the derived classes of that base class. This is the theory of polymorphism which means different instances of the same object can have different kinds of behaviour. An example to is the best way to explain this so let's go back to the button example:

class Button
{
  public Button()
  {
    ... // code
  }
  public void displayButton()
  {
    ... // code
  }
  public void setLabel(String S)
  {
    ... // code
  }
  ... // more methods
}

class ImageButton extends Button
{
  public void displayButton()
  {
    ... // code
  }
  public void setImage(String S)
  {
    ... // code
  }
}

class Toolbar
{
  Button arrayOfButtons[]
  Toolbar()
  {
    ... // code
  }
  void addButtonToToolbar(Button B)
  {
    ... // code
  }
  ... // more methods
}

The following piece of code will work correctly:

Toolbar T = new Toolbar();
Button TextButton = new Button();
T.addButtonToToolbar(TextButton);
Button ImageButton = new ImageButton();
T.addButtonToToolbar(ImageButton);

An image button behaves just like a text button, so it can pretend to be one even though it supports extra functionality. The other way around is not possible (in this example).

I will not give an example but there exists a definition in Java called abstract. When a method is declared abstract it has to be overridden or else the class cannot be built. This is good for classes that are supposed to be general purpose classes so the user can derive from them to their classes and supply the functionality the class needs. There is also a Java keyword called final which means when applied to a method that that method cannot be overridden and when applied to a class that that class cannot be inherited.

Interfaces are a very powerful concept which Java implements. Some people claim that interfaces are Java's replacement to multiple inheritance but they are not really.

An interface is like a class which cannot inherit from a class but can inherit from another interface they cannot have method bodies, only abstract methods. However you don't have to specify that they are abstract. All the members of an interface are public. An interface can only include final static public variables.

What good is an interface?

Say you want code to interact, yet you don't want the same base classes and code to be used for basically different functions. For example:

interface Picture
{
  void showPicture();
  public static final int NOT_JPEG = 2;
  public static final int NOT_GIF  = 1;
}

class Gif implements picture
{
  void showPicture()
  {
    ... // code
  }
}

class Jpeg implements picture
{
  void showPicture()
  {
    ... // code
  }
}

This will work too:

Picture P = new Gif();
if (P.err == P.NOT_GIF)
  P = new Jpeg();
P.showPicture();

the Vector class

I will talk about the Vector class here because it's very simple and it'll help you understand the Java OO paradigm.

A Vector is actually an array that can contain all types of objects and can grow and shrink. Vector is a member of the java.util package so in order to use a vector you will have to either:

import java.util.Vector;

Or

import java.util.*;

I very much recommend running javap java.io.Vector to see the list of methods that a vector supports. You can create a vector by simply building a new instance of class vector:

Vector v = new Vector();

You can have a vector with a predetermined size by building

Vector v = new Vector(Size);

You can resize a vector to a new size using the setSize method.

v.setSize(Size);

The capacity and size methods can give you valuable information of your space usage and the length of the vector.

setElementAt(java.lang.Object,int) will make an element at a specified offset into the element given as a parameter:

MyClass S = new MyClass();
v.setElementAt((Object)S,1);

Notice the type casting to Object. Object is the most basic class in Java; all of Java's class inherit from it. I will explain in detail the structure of Object in a future article but for now try javap java.lang.Object. It should be very interesting.

removeElementAt(int X) // will remove the element at offset X.
v.removeElementAt(1);

insertElementAt(java.lang.Object,int) will push an element into the array (without overriding any existing elements.

MyClass S = new MyClass();
insertElementAt((Object)S,1);

addElement(java.lang.Object) will add an element to the end of the vector and will enlarge it appropriately.

MyClass S = new MyClass();
addElement((Object)S);

boolean removeElement(java.lang.Object); Will return true or false upon success in removing the given element from the Vector.

removeAllElements(); I think this method is quite clear.

import java.util.Vector;

public class VectorExample
{
  public static void main(String argv[])
  {
    new VectorExample(argv);
  }
  VectorExample(String argv[])
  {
    int i;
    Vector v = new Vector();
    for (i = 0; i < argv.length; i++)
    {
      v.addElement((Object)argv[i]);
    }
    if (v.size() >= 2)
    // If the number of elements in V is equal to or larger than 2.
      v.insertElementAt((Object)"Inserted Element",1);
      //Push an element into v.
    for (i = 0; i < v.size(); i++)
      System.out.println((String)v.elementAt(i));
  }
}

Notice the type casting in the example, all objects must be converted to and from class Object. Notice the check of the size of the vector before insertion, the insert method will fail if the array is too small.

In Conclusion

We learned about the most basic syntax and concepts of Java (there is much more in depth basic stuff). I did not mention threads but I'd like to delay them until we have covered a little more. I rushed us through this whole thing so we can get to the neat GUI AWT stuff next month, since I believe that by trying out practical examples you can learn much more than by stupid hello world programs (although there are some more coming next month).