C H A P T E R  3

Data Types

Most programming languages have data types. Java has several data types. The first characteristic of a data type is whether it's a primitive or an object. Anything that's not a primitive is an object of some sort.

Primitive Data Types

Primitive means that no class exists to define a variable of that type. Java supports a number of primitive data types. In the code samples in previous chapters, you saw several variables whose data types were primitive. Anywhere you saw int or boolean, those were primitive data types (usually called “primitives”). Primitives fall into several broad categories. From the language's point of view, the categories don't exist, but they do help people keep the primitives straight from one another.

Integer Primitives

An integer primitive is one whose value can be only an integer. It cannot be a real number (that is, one with a fractional value). The numbers 0, 1, 16, and 37 are all integers. The number 12.34 is not an integer. The distinguishing feature between the various integer primitives is how many bits make up each one. Because a type made of more bits can hold a bigger number, the practical effect of the different number of bits is to increase the maximum value of that type. The possible detrimental effect of increasing the number of bits is to consume more memory. Often, it doesn't matter, but it's good practice to use data elements no larger than you need.

Integer primitives are signed (unless you use the unsigned keyword, which we discuss more in a moment), so their values range from a negative number to a positive number.

Table 3-1 shows the details of the various integer primitives.

Images

Do you need even bigger numbers? Real-world applications sometimes need numbers even bigger than the maximum value of long. In those cases, Java provides a class called BigInteger. A number represented by a BigInteger object can be of any size, because the BigInteger class allocates enough bytes to store a representation of any number. It's a little tricky to use and operations on BigInteger objects are slow, but it's sometimes the only way. BigInteger is not a primitive.

Real Primitives

Real numbers have decimal points and values after the decimal point. Even if that value is 0, and even if you didn't type a decimal point and anything after it, the decimal point and the zeros after it are there behind the scenes. They must exist so that operations on the value can compare to the full value, so the JVM (the Java Virtual Machine, which is the program that runs your programs) fills them in even if you don't.

images Note 0 and 0.00 are different values in Java. To the average person, they both mean zero. To a scientist or mathematician, the one indicates greater precision than the other, but they still both mean zero. But to a compiler, they are different data types and have nothing to do with one another.

Java supports two real primitives, float and double. float gets its name from the idea of a floating point number. The decimal point can move, so it's said to “float.” double gets its name because it takes twice the storage space of a float. Table 3-2 shows the details of float and double.

Images

Again, sometimes even that isn't enough. For those cases, Java provides a class called BigDecimal. As with BigInteger, it can be tricky to use, and operations on it are slow. But when you absolutely have to have a number bigger than a double, use BigDecimal. As with BigInteger, BigDecimal is not a primitive.

boolean

The boolean data type indicates whether something is true or false. In fact, those two words (true and false) are the only two values boolean data types can have.

images Note true and false are reserved words in Java. You can't use them for anything other than the value of a boolean variable. For example, trying to create an int called true throws an error.

char

The char data type holds two 8-bit bytes and is meant to represent characters. It's stored as an unsigned 16-bit integer with a minimum value of 0 and a maximum value of 65,535. However, you should never use a char to store a number, because that can lead to confusion. Use char variables to hold individual characters, and you'll avoid trouble.

So why is the maximum so big when relatively few characters exist? Well, when you look at all the character sets in use all around the world, 65,535 isn't so unreasonable. In fact, it's not enough when dealing with traditional Chinese.

images Tip If you're curious about how to handle characters from other languages, look up Unicode, which is a standard that defines all the world's characters. For more on handling multi-byte characters, look up variable-width encoding.

The Special Type: String

String is a type that has some of the characteristics of both a primitive and an object. Strictly speaking, it is an object; that is, a String class defines it. A String object is a sequence of characters (and Java provides utilities for turning a String object into a collection of char primitives and for making a String object from such a collection). It's often handy to work on the collection (the string) rather than on each character, so Java (and nearly all other programming languages) provides a String object.

Java offers special support for the String class that lets String objects act a little like primitives. In particular, you can create a String object by using the equals sign (=), and you can concatenate String objects with the plus sign (+), as shown in Listing 3-1. Concatenation applies only to strings, by the way. If you use a plus symbol with the other data types, you either get an error (try adding two boolean values and you'll see it) or you get the mathematical plus operation that you usually associate with the plus symbol.

Listing 3-1. String examples

String myString = "my string";
String yourString = "your string";
String ourString = myString + " " + yourString;
System.out.println(myString);
System.out.println(yourString);
System.out.println(ourString);

The output is in Listing 3-2.

Listing 3-2. String example output

my string
your string
my string your string

Notice that the value of ourString consists of the concatenation of three values: myString, " ", and yourString. " " is a String literal. That's another way String differs from other objects; no other object (remember, primitives aren't objects) can have a literal.

Literals

All the primitives and the String class can have literal values. A literal is a constant value that corresponds to a particular data type. Table 3-2 provides examples for each of the primitive data types and the String class.

Images

images Note Declaring a float literal requires appending f to the end of the literal. Otherwise, the compiler tries to make it into a double value and then complains that it can't cast from double to float. Similarly, you can put d at the end of a double literal's declaration. However, that is redundant because double is the default floating-point type.

Literals pop up all over the place, often without anyone stopping to think about it (after all, we're usually trying to get the computer to do something). Every time we write a loop that starts at 0 and counts to some value, we use a literal (in that case, an integer literal). A number of other literals are common in nearly all programming languages and tasks. In set theory (which informs a lot of programming and especially database programming), the only three values that matter are 0, 1, and many. Consequently, 0 and 1 appear over and over again throughout all kinds of software. The empty string (""—sometimes handy for comparing String objects) and the single space (" "—handy for linking String objects without inventing a new and large word in the process) also appear often.

Escaping Characters

Variables of type char can have several special values. First, singe quotation marks ('), double quotation marks ("), and backslashes () all have to be marked as special (that's called escaping a character), so that the JVM knows you want one of those characters. Otherwise, it would process all your single quotations as the beginning or end of a char and all your double quotations as the beginning or end of a String object. The backslash character has to be escaped because it is the escape character. If it couldn't itself be escaped, then every backslash would indicate an escaped character, which would be a real problem. Escaped characters (often called escape sequences because they consist of at least two characters—the escape character and at least one other character) are also used for non-graphical characters (often called control characters). There's even a character called Bell. It never appears, but it can (if your computer enables it) make your computer beep. The Bell (or Alert) character's escape sequence is a. All of that might not make sense, so let's consider some examples in Listing 3-3 to make things clearer.

Listing 3-3. Examples of escaping characters

// Let's start with double quotation marks
// The following line throws an error because meow isn't defined
System.out.println("My cat says, "meow."");
// so the line has to be
System.out.println("My cat says, "meow."");
// which will produce this output: My cat says, "meow."

// And now single quotation marks
// The following line throws an error because ' never has a matching '
System.out.println("' is my favorite character");
// so the line has to be
System.out.println("' is my favorite character");
// which will produce this: ' is my favorite character
// (my actual favorite character is ;)

// And now the backslash itself
// The following line throws an error because it's missing a closing "
// (The compiler takes " as a literal " and then can't find an end to the string.)
System.out.println("I want a ");
// so the line has to be
System.out.println("I want a \");
// which will produce this: I want a

Java also supports several special escape sequences (all beginning with a backslash). Table 3-3 shows each escape sequence and describes its effect. It begins with the three we already covered.

Images

The newline character ( ) sees a lot of use for things like creating readable error messages, separating lines in files, and other output. Tabs are less common but still sometimes used (to line up pieces of output to make things easier to read). The others are much more rarely used, because software developers hardly ever create unstructured output these days. When programmers used punch cards for input and line printers for output, programmers made extensive use of the form feed and start-of-line characters. We still produce plenty of output, of course, but it's usually HTML, XML, or some other structured format.

In addition to being possible values for variables of type char, String objects can also contain escape sequences. Many String objects contain a newline sequence or two. Listing 3-4 shows a short example.

Listing 3-4. String with escape sequence

String errorMessage = "The whatsit didn't work! Check the doohickey.";
System.out.println(errorMessage);

That code snippet produces the output in Listing 3-5.

Listing 3-5. Newline output

The whatsit did't work!
Check the doohickey.

Wrapper Classes

Each of the primitives (remember, String is an object rather than a primitive) has a corresponding class that provides several useful abilities that are otherwise unavailable to a primitive. You can think of the wrapper classes as each being a package of useful abilities wrapped around a primitive of the corresponding type. Table 3-4 shows the corresponding class for each primitive:

Images

The most commonly used methods of those classes are the various parse, Value, and valueOf methods, because those methods let you turn a String into a primitive. Each class also supports toString methods to turn primitives into String objects. Listing 3-6 shows what you can do with an int primitive.

Listing 3-6. Integer example

// declare an int
int myInt = 1;

// and a String that contains a number
String myString = "1";

// turn myInt into a String
String myIntString = Integer.toString(myInt);

// turn myString into an int
int myStringInt = Integer.parseInt(myString);

// turn myString into an Integer
// be prepared for an exception if myString does not hold a number
Integer myStringInteger = new Integer(myString);

// and then turn myStringInteger into an int
int myOtherStringInt = myStringInteger.intValue();

// Now for more unusual things
// convert an int to a float (perhaps for further floating-point work)
float myFloat = new Integer(myInt).floatValue();

// convert an int to a byte
// be prepared for an exception if the value is out of byte's range
byte myByte = new Integer(myInt).byteValue();

// convert an int to a long
// no need to worry about an exception this time
long myLong = new Integer(myInt).longValue();

// just for fun, get the binary string representation of myInt
// creates a String object that holds "1"
String myIntBinary = Integer.toBinaryString(myInt);

The other numeric primitives (byte, short, long, float, and double) all work in a similar way. You can convert any of them to any other, though you might have to handle an exception if the conversion can't be done with the value you provide. For example, if you have an int variable that holds a value of 300, converting it to a byte gives an error, because a byte can't hold that value. We cover this concept in greater detail in the next chapter, when discussing casting values.

The Boolean and Character classes work a little differently. You can still convert strings to boolean or char values and vice-versa, but you can't convert boolean and char primitives into other primitives. Also, the Boolean class includes an equals method, and the Character class includes many methods for dealing with issues such as characters that are meant to be read from right to left (for example, characters from Arabic and Hebrew) and other special issues that relate only to characters. All of those operations are fairly unusual, though, so we don't cover them. If you want to learn more about them (and good for you if you do), look at the Javadoc for the Boolean and Character classes. As you might recall from Chapter 1, JavaDoc is a special kind of documentation that is built into the code itself. Oracle (the company that makes Java) provides extensive JavaDoc for all the standard Java libraries. You can find the JavaDoc for the Boolean class at http://download.oracle.com/javase/7/docs/api/java/lang/Boolean.html and the JavaDoc for the Character class at http://download.oracle.com/javase/7/docs/api/java/lang/Character.html. You can find all the JavaDoc for Java 7 at http://download.oracle.com/javase/7/docs/api/ (as you can see, the other topics are branches of the overall API documentation).

Arrays

An array is a data structure that holds a group of variables under a single identifier. Java supports arrays for both primitives and objects. Square brackets after a variable's name indicate that it is an array. Listing 3-7 shows several arrays and how to manipulate them.

Listing 3-7. Arrays of primitives

int[] a; // array declaration without assignment
a = new int[2]; // specify the length of an existing array
int[] b = {1, 2, 3, 4}; // array declaration with assignment
int bLength = b.length; // how to get the length of an array

// arrays start at 0, not 1
b[0] = 2; // have to reassign each value in the array individually

Let's look at the code one line at a time. The first line creates an array of int primitives but doesn't assign anything to it (a is null at that point). The second line shows how to set an array to be a particular length. If a had values, its values would be replaced by the default values of the new type (0 in this case). The third line shows how to create an array with a set of starting values. You can use that block assignment syntax only when creating an array, not when assigning new values to an existing array. The fourth line shows how to get the length of an array. The last line shows how to reassign one of the values in an array and shows that array addresses start at 0. (Most programming languages start counting at 0.)

Now let's consider an array of objects in Listing 3-8, which reveals some interesting things.

Listing 3-8. Arrays of objects

Integer[] myIntegers = new Integer[4];
for (int i = 0; i < myIntegers.length; i++) {
        myIntegers[i] = new Integer(i);
}

Again, let's go line by line. As you can see, the syntax for creating an array of objects differs a bit from that for primitives. The part to the left of the equal sign looks the same (the kind of object, the array indicator, and the name of the variable), but the part to the right of the equal sign differs by having the new keyword before the type of the item going into the array. To create a new instance of an object, we usually use the new keyword (though other ways exist), which calls a constructor for the object. However, all of the Integer class's constructors require an object (either an int or a String object that holds an integer value), so this array ends up holding four null references. (We dive into what null means in a moment.)

This listing also shows how to loop through an array and, in this case, create an object for each of the null objects. Notice that we have to use the new keyword again, even though we used it when we created the array. Because we got four null references rather than actual objects from the original assignment, we have to create new ones here. In this case, we end up with four Integer objects having values of 0, 1, 2, and 3.

Java provides a convenience class for arrays. The Arrays class consists of many static methods to do handy things such as copy and sort arrays. For example, Listing 3-9 shows one way to sort an array of ints.

Listing 3-9. Using the Arrays convenience class

int[] a = {5, 4, 3, 2};
// at the top of the program, we had to import
// java.util.Arrays for this to work correctly
Arrays.sort(a);
for (int i = 0; i < a.length; i++) {
  System.out.println(a[i]);
}

The result is 2, 3, 4, 5 (each on its own line) in the console.

The Non-Existent Type: null

Java includes a value that isn't anything: null. It refers to a memory address that has not been assigned. In Java terms, that means it refers to an object or primitive that has not been created. As I mentioned in the “Arrays” section, when you create an array without specifying its values, you are creating a collection of null values. They have no memory address, no corresponding primitive or object exists for them, and so they are null. That might sound like a problem, and the whole concept of null often causes novice programmers some trouble. You can keep it straight by remembering that a null is a non-existent reference.

The oft-maligned null has its uses (otherwise, it wouldn't exist—programmers are pragmatic people, most of the time). For example, we often compare an object to null to be sure that something exists before we try to use it. If the graphics library is supposed to give us an object of type Color and we get null instead, we have a problem. So we might compare that Color value to null to ensure that we are getting a Color object and do something useful (such as trying another way or at least logging an error) if not.

Also, it's common practice to create a variable in one place and assign it in another place. In between the creation and assignment, the value of that variable might be null (it also might not be null because some primitives, such as int, have default values). This technique is handy because we might want to assign different values to the variable based on some logic. For example, a series of if-else statements or a switch statement might contain code to assign the value of a variable. Let's consider a small example (from a minesweeper game), shown in Listing 3-10.

Listing 3-10. Using a null value

public getMineIcon(int x, int y) {

// x and y are the position within the game grid
  int numberOfAdjacentMines = getNumberOfAdjacentMines(x, y);

  MineIcon mineIcon = null; // here's our null
  if (numberOfAdjacentMines == 1) {
    mineIcon = new MineIcon(1);
  } else if (numberOfAdjacentMines == 2) {
    mineIcon = new MineIcon(2);
  }
  // and so on up to 8

  return mineIcon;
}

In this case, we can assume a MineIcon class exists and that its constructor will take two integer values and return an icon (a type of image) that shows the value of that integer argument. Because a square in a minesweeper game can have no more than eight neighboring mines, we stop at eight. We also let the method return null, to represent the case where a square has no neighboring mines. Consequently, the code that calls this method then has to know what to do with a null value. As it happens, the setIcon method that we need to use accepts a null argument as a way of saying that no icon needs to be set.

We create a full-blown minesweeper program in Chapter 7, “Creating a User Interface.” When we do, you see a different way to use set the mine icon, but it still uses a null reference. For now, just remember that null is just another value (though a special one that indicates a non-existent reference) and that it can be used for purposes other than just checking for missing objects.

Enumerations

An enumeration (often written as “enum”) is a data type that consists of a fixed number of constants. For example, if you are writing a game that involves navigation, you might have an enumeration to define the four cardinal directions, similar to the one shown  in Listing 3-11.

Listing 3-11. Enum for directions

public enum Direction {
    NORTH, EAST, SOUTH, WEST;
}

As Listing 3-11 shows, the declaration of an enumeration consists of the enum keyword, a name for the enumeration, and the values that comprise the enumeration. The values are just names and have no type of their own. That works because we need unique identifiers but don't need a type for each one.

The value of enumerations is that they are type-safe (meaning that it can't be confused with another type—enums used to be created with integers, so confusing an enum with an integer was a real problem). Without enumerations, we'd have to set up constants in a different way—usually with integers, as shown in Listing 3-12.

Listing 3-12. Constants to define directions

public static final int NORTH = 0;
public static final int EAST = 1;
public static final int SOUTH = 2;
public static final int WEST = 3;

images Caution Do not write collections of constants this way. They can be compared to int values, and that's almost certain to be the wrong thing to do. That's why Java has enumerations.The enumeration value, Direction.NORTH, can't be treated as an integer (not even by accident), whereas the constant, NORTH, can be. Also, constants are static and final (and often public). Enumerations remove the need for those modifiers, which gives us greater flexibility when using enumerations.

Each enum is actually a class. (You might have noticed that its declaration syntax is similar to a class.) All enum objects implicitly extend java.lang.Enum. That lets us make more meaningful enums than just lists of constants. To continue with our example, we can set the degrees for each direction, as shown in Listing 3-13.

Listing 3-13. Enum with more information

public enum Direction {
  NORTH (0),
  EAST (90),
  SOUTH (180),
  WEST (270);

  private final int degrees;
  Direction(int degrees) {
    this.degrees = degrees;
  }

  public int getDegrees() {
    return degrees;
  }
}

From there, we can write code to get the additional information associated with each value in the enumeration, as shown in Listing 3-14.

Listing 3-14. Getting details from an enum

for (Direction d : Direction.values()) {
  System.out.println(d + " is " + d.degrees + "degrees.");
}

We can also write methods that work with the enumeration's values. For example, we can write a method that, given a direction in degrees, tells you which cardinal direction is closest. Listing 3-15 shows one way to write such a method.

Listing 3-15. findCardinalDirection method

public static Direction findCardinalDirection (int degrees) {
  if (degrees < 45) {
    return NORTH;
  } else if (degrees < 135) {
    return EAST;
  } else if (degrees < 225) {
    return SOUTH;
  } else if (degrees < 315) {
    return WEST;
  } else {
    return NORTH;
  }
}

You might also notice the different syntax for iterating through a for loop. That syntax is not unique to enumerations, but it is especially handy for them. We cover these alternate ways of using a for loop when we get to looping in a later chapter.

As you can see, an enumeration is far more powerful than a simple list of constants. It's also safer, because no one can confuse Direction.North with 0.

Listing 3-16 shows the full code for the Direction enumeration.

Listing 3-16. Complete enumeration example

package com.bryantcs.examples.enumExample;

public enum Direction {
  NORTH (0),
  EAST (90),
  SOUTH (180),
  WEST (270);

  private final int degrees;
  Direction(int degrees) {
    this.degrees = degrees;
  }

  public int getDegrees() {
    return degrees;
  }

  // static because it doesn't rely on a particular direction
  public static Direction findCardinalDirection (int degrees) {
    if (degrees < 45) {
      return NORTH;
    } else if (degrees < 135) {
      return EAST;
    } else if (degrees < 225) {
      return SOUTH;
    } else if (degrees < 315) {
      return WEST;
    } else {
      return NORTH;
    }
  }
}

Now that we have our completed enumeration, we need a test program to see how it works. Listing 3-17 shows a program class that does the job.

Listing 3-17. A test class for our enumeration

package com.bryantcs.examples.enumExample;

public class EnumExample {

  public static void main(String[] args) {
    int[] compassPoints = {22, 77, 144, 288};
    for (int i = 0; i < compassPoints.length; i++) {
      System.out.println(compassPoints[i] + " degrees is (very roughly) "
        + Direction.findCardinalDirection(compassPoints[i]));
    }
    for (Direction d : Direction.values()) {
      System.out.println(d + " is " + d.getDegrees() + " degrees.");
    }
  }
}

EnumExample produces the output in the console shown in Listing 3-18.

Listing 3-18. EnumExample output

22 degrees is (very roughly) NORTH
77 degrees is (very roughly) EAST
144 degrees is (very roughly) SOUTH
288 degrees is (very roughly) WEST
NORTH is 0 degrees.
EAST is 90 degrees.
SOUTH is 180 degrees.
WEST is 270 degrees.

Summary

In this chapter, we learned the basics about the various data types that are available in Java. We found out:

  • The size restrictions of the various numeric data types
  • How the boolean data type works
  • How char variables and String objects work and a bit about how they can interact
  • How arrays work
  • What a null is and how to use one to good effect
  • How to create and use enumerations

We make repeated use of all these concepts and techniques throughout the rest of the book. As we work more with Java, you'll do a lot with primitives, the String object, primitive and String literals, and arrays. Enumerations appear less often in most code, but they offer important advantages (most notably type safety) when we need them.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.187.108