Into Java - Part III

From EDM2
Jump to: navigation, search
Into Java / Part
I II III IV V VI VII VIII IX X XI XII
XIII IV XV XVI XVII XVIII XIX XX XXI XXII XXIII

By Simon Gronlund

Welcome back to the Into Java column. The past month you have acquainted yourself with the Java API documentation, as well as you have taken a glance at the theory lesson and now you know the basics about Java. You know how to write some Java code, to save the app, and to compile and run the application from your command line.

This time you can take it easy and take a closer look at the absolutely smallest parts to build with, that is data types. How do you manage data types and how do Java shows its strength at this area? At last you will examine how to extend your Java environment with Swing.

Data Types

To the contrary of REXX and other similar languages Java is a strongly typed programming language. This way javac will not let you compile the code unless you specify which data type you are using, or wish to use. Hence

x = 3.141592653589793;

is not valid, unless you have first declared x as double. As

double x;
x = 3.141592653589793;

or the shortcut

double x = 3.141592653589793;

You may ask why this strong typing is necessary, as some other languages does not require it. There are at least two benefits from it: First it will make your code less error prone as you really have to convince yourself at compile time that it should work, else the compiler will grumble at your mistakes. Second the strong typing will give you a way to differentiate methods from each other, even though using the same method name (we will explore that a few issues ahead).

Java uses eight primitive types, of which six are number types, one is char that is used for characters, and the last one is the boolean type for true/false values. These primitive types are not to be confused with any class named equally, that is boolean IS NOT an equivalent to the class Boolean of the java.lang package (but of course they have some kind of connection and you will look at that later on). The primitive types are really down as close to machine level as you ever will come when using Java, and you don't need to bother if you don't like to.

Primitive types must always be declared as shown above, but you never need to use the new operator as you have to when declaring a new object from a class.

A significant strength to other programming languages is that Java is not dependent upon the target machine. You may remember that an int in e.g. C/C++ is not of the same size on a 16-bit Intel as it is on a 32-bit SPARC, nor is it of the same size on a 32-bit processor when using Windows 3.x as it is using Windows 95/NT. That sounds quite confusing to most people, but Java is not that bothersome.

boolean

Java introduces a boolean type, that can be true or false. This data type is used in any logical test that Java supports, i.e.

boolean isSetToTrue;    // variable declaration without instantiation
isSetToTrue = getBooleanAnswerFromSomething();    // asks a method
if (isSetToTrue) {
// your code here will be executed if isSetToTrue is true
}

This way you never have to think about which return value is true or which is false, as you have to using e.g. C/C++. Further, a boolean can never be converted to anything else, it is a boolean, period.

char

The char type is designed to handle the 2-byte Unicode characters, that is a char is a 2-byte sized. Since ASCII/ANSI is a small subset of Unicode you never have to worry about this powerful feature unless you are heading for the international application market. Java for OS/2 of course supports Unicode if you like to use it, a somewhat bigger download though. However, you never have to worry about how a char looks like "under the hood", as you almost never have to know exactly what happens within the Java machinery.

To use single characters you have to use single quotes

char test = 'q';

Since char is a primitive data type you may use it with arrays right out of the box. Hence

char[] chArray = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'};

is twelve characters stored in an char array, you may access them one at a time. Compare that to the String

String hw = "Hello World!"
Java3a.gif

that is one single string containing twelve characters. The figure to your right will show the difference. The upper object is chArray containing twelve chars, and you know exactly how it looks like. The lower object is the string hw, but on the contrary you don't know how it looks like inside, except that you can get the value "Hello World!" from it if you like to.

Number Data Types

Numbers can be divided into two groups, with decimals or without decimals, that is floating-point types vs. integers. Java uses two floating-point types, and it uses four integer types. Why?

Regarding floating-point numbers you will mainly use double that gives you maximum precision, but once upon a decade you may choose float that is processed faster or maybe when you run out of disk space.

Normally you will use the int integer. But often you must choose long when calling certain methods within the Java classes, or if you like to count the distance to the sun in meters. The other two integer data types, short and byte, are used in low-level file handling, or perhaps if you are managing very large amounts of integers.

Type Size Range (approximately) Code example
Floating-point numbers
double 8 bytes 1.7977E+308, 15 significant digits double d = 3.14159;
float 4 bytes 3.4028E+38, 6 significant digits float f = 3.14159F;
Integer numbers
long 8 bytes ±9,223,372,000,000,000,000L long l = 1234567890000000000L;
int 4 bytes ±2,147,483,000 int i = 1234567890;
short 2 bytes -32,768 to 32,767 short s = 12345;
byte 1 byte -128 to 127 byte b = 123;

Note: 1/ float and long have suffixes F and L respectively and 2/ there is no unsigned numeric data types in Java

We can establish that the far most used numeric data types are double and int. Since they convey that much power you will find it unnecessary bothering with the other types except under special circumstances. Further, note the difference between a char and a string that contains none, one or more char(s).

Conversions and Casts of Number Data Types

Java is powerful at converting a number or a result to the proper data type if needed. Any mathematical operation on variables of different data types will safely convert the result to the "biggest" one used. That is the operation

short x = 33;
byte y = 11;
return (x / y);

expects to return a short, since short is the bigger one. It doesn't matter that a byte can safely convey the answer that is 3, well within the limits for a byte. Any operation will use the "bigger" data type.

Then consider this operation

short x = 33;
byte y = 10;
return (x / y); // answer is 3.3

that will result in a number with a fractional part. It is no problem if the method is told to return double values, then it will return a double. This particular case will work with float as well, since the return value is that small. But what will happen if the method is declared to return an int (or byte, short or long)?

Then Java will cast the return value to the appropriate data type. That is, Java will truncate the answer to the data type you have choosen. Hence the return value above will be 3 and the .3 factorial part is lost, truncated. That loss will always happen when you cast from a floating-point numeric to an integer.

Is that good or bad then? Intuitively you will say bad, but from time to time you are interested only in the integer part. Consider this code that is the inner working of a dice:

public int throwDice() {
  double t = 6 * Math.random() + 1;
  if (t == 7)  // just in case
  t = 6;
  return (int) t;
}

You can see how the sub result will change between 1 and 7, but the latter result will almost never occur, if ever. But surely you want to return a straight 1, 2, 3, 4, 5 or 6. Hence you can cast it down to an int at return, the last line.

If you on the other hand would like to round a floating-point number to the closest integer, then you can use the Math.round() method. But the important idea is that whenever casting decimal numbers to integers you will lose all fractional parts.

The same idea holds if you tries to cast from double to float, or from long to int , or from int to short, etc., if big enough number the cast will be implicitly done but the result will be that truncated. That is, the result is not to be trusted if the numbers casted are out of range.

The only legal direction to convert data types without losing information is the following

byte -> short -> int -> long -> float -> double

and that may be done without explicitly using cast as well.

That will be the theory lesson of today. Data types are the fundamental parts of any programming language, Java is no exception to that. Remember that data types is not to be confused with classes using the same name, but always with a leading upper case.

A Dice to Play With

This time I will only give you a dice to play with. First there will be the Dice class. It will contain only the throwDice() method that returns an int. Yes, it really is a lean and thin class, but remember that some parts are invisible to us, contained in the Java environment and you don't need to think about these parts.

public class Dice {
public int throwDice() {
double t = 6 * Math.random() + 1;
if (t == 7)  // just in case
t = 6;
return (int) t;
 }
}

Second you need a test driver. Some code in it is not discussed yet but let that be or explore the Java API by yourself to figure out how the keyboard reader really works. To get the keyboard reader you need to import the java.io.* package. You make yourself a Dice and throws it the first time. Would you like to continue? Answer with a single y and [Enter]. That's all. Let the dice roll!

import java.io.*;
public class DiceTest {
public static void main(String args[]) {
Dice diz = new Dice();
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
char answer = 'y';
try {
do {
System.out.println();
System.out.println("Dice shows: " + diz.throwDice());
System.out.println();
System.out.print("Continue? (y/n) ");
answer = in.readLine().charAt(0);
} while (answer == 'y'); // loops until not 'y'
} catch (IOException e) {} // some IO error happened and is ignored
System.out.println("Thank you. Come play again!");
 }
}

Let's Swing

Since the graphical interface of Java is that much enhanced and Swing is using the inner workings that much better I strongly recommend you to install the Swing package. This package is entirely written in Java and laid on top of the common Java system and that way you don't need to tinker with your system too much. I presume you have at least Java 1.1.7A.

There are a few steps you need to go through though:

  1. create a new folder in your Java11 folder, normally held in your root partition, name it e.g. swing.
  2. download the Swing package from Sun to that new folder.
    • chose "download" at the paragraph "JFC 1.1 with Swing 1.1.1"
    • at the bottom of that page, chose a format, that is "ZIP file" and continue
    • ACCEPT
    • pick any choice acceptable to you
  3. unzip the file using an Info-Zip aware tool
  4. make a backup copy of your CONFIG.SYS and then open the original file to edit your CLASSPATH to contain the following:
    SET CLASSPATH=.\.;... ...;X:\Java11\swing\swingall.jar;
    Maybe you need to append one line more onto the line above:
    SET CLASSPATH= ... ;X:\Java11\lib\classes.zip;
  5. Reboot your computer
  6. Open Netscape, open Preferences => OS/2 Preferences => IBM Java Properties:
    Uncheck "Ignore system Classpath" and see to it the Current Classpath shows the classpath of your CONFIG.SYS file.

[With gratitude to Aaron Williams]

Now your system and Netscape does Swing. Unfortunately some Java 2/Swing enabled web sites uses a special HTML code telling you to download a Swing Plug-in. That is not necessary, Netscape really can run that applet but the HTML-code blocks you out. Fortunately there are not too many Java 2/Swing pages around yet.

Next time you will build your first graphical application using Swing. Welcome back then.