Java 1.1 Unleashed
- 3 -
|
Valid | Invalid |
HelloWorld | Hello World (uses a space) |
Hi_Mom | Hi Mom! (uses a space and punctuation mark) |
heyDude3 | 3heyDude (begins with a numeral) |
tall | short (this is a Java keyword) |
poundage | #age (does not begin with a letter) |
Another more critical naming issue regards the use of underscore and dollar-sign characters at the beginning of identifier names. Using either of these characters at the beginning of identifier names is a little risky because many C libraries use the same naming convention for libraries that can be imported into your Java code. To eliminate the potential problem of name-clashing in these instances, it's better to stay away from the underscore and dollar-sign characters at the beginning of your identifier names. A good use of the underscore character is to use it to separate words where you normally would use a space (Hi_Mom).
NOTE: With Java 1.1, the style conventions in naming identifiers has become significantly more important. JavaBeans, which is the new component technology in Java 1.1, depends in part on the standard identifier-naming conventions if it is to function properly. You learn more about JavaBeans and the naming conventions it relies on in Part VIII, "Java Archives and JavaBeans."
Keywords are predefined identifiers reserved by Java for a specific purpose and are used only in a limited, specified manner. Java has a richer set of keywords than C or C++, so if you are learning Java with a C/C++ background, be sure to pay attention to the Java keywords. The following keywords are reserved for Java:
abstract | double | int | super |
boolean | else | interface | switch |
break | extends | long | synchronized |
byte | false | native | this |
byvalue | final | new | threadsafe |
case | finally | null | throw |
catch | float | package | transient |
char | for | private | true |
class | goto | protected | try |
const | if | public | void |
continue | implements | return | while |
default | import | short | |
do | instanceof | static |
Program elements used in an invariant manner are called literals, or constants. Literals can be numbers, characters, or strings. Numeric literals include integers, floating-point numbers, and booleans. Booleans are considered numeric because of the C influence on Java. In C, the boolean values for true and false are represented by 1 and 0. Character literals always refer to a single Unicode character. Strings, which contain multiple characters, are still considered literals even though they are implemented in Java as objects.
NOTE: If you aren't familiar with the Unicode character set, you should know that it is a 16-bit character set that replaces the ASCII character set. Because it is a 16-bit character set, there are enough entries to represent many symbols and characters from other languages. Unicode is quickly becoming the standard for modern operating systems.
Integer literals are the primary literals used in Java programming. They come in a few different formats: decimal, hexadecimal, and octal. These formats correspond to the base of the number system used by the literal. Decimal (base 10) literals appear as ordinary numbers with no special notation. Hexadecimal numbers (base 16) appear with a leading 0x or 0X, similar to the way they do in C/C++. Octal (base 8) numbers appear with a leading 0 in front of the digits. For example, an integer literal for the decimal number 12 is represented in Java as 12 in decimal, 0xC in hexadecimal, and 014 in octal.
Integer literals default to being stored in the int type, which is a signed 32-bit value. If you are working with very large numbers, you can force an integer literal to be stored in the long type by appending an l or L to the end of the number, as in 79L. The long type is a signed 64-bit value.
Floating-point literals represent decimal numbers with fractional parts, such as 3.142. They can be expressed in either standard or scientific notation, meaning that the number 563.84 also can be expressed as 5.6384e2.
Unlike integer literals, floating-point literals default to the double type, which is a 64-bit value. You have the option of using the smaller 32-bit float type if you know the full 64 bits are not required. You do this by appending an f or F to the end of the number, as in 5.6384e2f. If you are a stickler for details, you also can explicitly state that you want a double type as the storage unit for your literal, as in 3.142d. But because the default storage for floating-point numbers is double already, this addition isn't necessary.
Boolean literals are certainly welcome if you are coming from the world of C/C++. In C, there is no boolean type, and therefore no boolean literals. The boolean values true and false are represented by the integer values 1 and 0. Java fixes this problem by providing a boolean type with two possible states: true and false. Not surprisingly, these states are represented in the Java language by the keywords true and false.
Boolean literals are used in Java programming about as often as integer literals because they are present in almost every type of control structure. Any time you have to represent a condition or state with two possible values, a boolean is what you want. You learn a little more about the boolean type later in this chapter. For now, just remember the two boolean literal values: true and false.
Character literals represent a single Unicode character and appear within a pair of single quotation marks. Similar to C/C++, special characters (control characters and characters that cannot be printed) are represented by a backslash (\) followed by the character code. A good example of a special character is \n, which forces the output to a new line when printed. Table 3.2 shows the special characters supported by Java.
Description | Representation |
Backslash | \\ |
Continuation | \ |
Backspace | \b |
Carriage return | \r |
Form feed | \f |
Horizontal tab | \t |
Newline | \n |
Single quote | \' |
Double quote | \" |
Unicode character | \udddd |
Octal character | \ddd |
NOTE: To find out more information about the Unicode character set, check out the Unicode home page at this URL:
http://www.unicode.org
String literals represent multiple characters and appear within a pair of double quotation marks. Unlike all the other literals discussed in this chapter, string literals are implemented in Java by the String class. This arrangement is very different from the C/C++ representation of strings as an array of characters.
When Java encounters a string literal, it creates an instance of the String class and sets its state to the characters appearing within the double quotation marks. From a usage perspective, the fact that Java implements strings as objects is relatively unimportant. However, it is worth mentioning at this point because it is a reminder that Java is very object oriented in nature--much more so than C++, which is widely considered the current object-oriented programming standard.
Operators, also known as operands, specify an evaluation or computation to be performed on a data object or objects. These operands can be literals, variables, or function return types. The operators supported by Java follow:
+ | - | * | / | % | & | | |
^ | ~ | && | || | ! | < | > |
<= | >= | << | >> | >>> | = | ? |
++ | -- | == | += | -= | *= | /= |
%= | &= | |= | ^= | != | <<= | >>= |
>>>= | . | [ | ] | ( | ) |
Separators are used to inform the Java compiler of how things are grouped in the code. For example, items in a list are separated by commas much like lists of items in a sentence. Java separators go far beyond commas, however, as you discover in the next chapter. The separators supported by Java follow:
{ | } | ; | , | : |
Earlier in this chapter, you learned that comments and whitespaces are removed by the Java compiler during the tokenization of the source code. You may be wondering, "What qualifies as whitespace and how are comments supported?" First, whitespace consists of spaces, tabs, and linefeeds. All occurrences of spaces, tabs, and linefeeds are removed by the Java compiler, as are comments. Comments can be defined in three different ways, as shown in Table 3.3.
Type | Usage |
/* comment */ | All characters between /* and */ are ignored. |
// comment | All characters after the // up to the end of the line are ignored. |
/** comment */ | Same as /* */, except that the comment can be used with the javadoc tool to create automatic documentation. |
/* This is a C style comment. */ // This is a C++ style comment. /** This is a javadoc style comment. */
One of the fundamental concepts of any programming language is data types. Data types define the storage methods available for representing information, along with how the information is interpreted. Data types are linked tightly to the storage of variables in memory because the data type of a variable determines how the compiler interprets the contents of the memory. You already have received a little taste of data types in the discussion of literal types.
To create a variable in memory, you must declare it by providing the type of the variable as well as an identifier that uniquely identifies the variable. The syntax of the Java declaration statement for variables follows:
Type Identifier [, Identifier];
The declaration statement tells the compiler to set aside memory for a variable of type Type with the name Identifier. The optional bracketed Identifier indicates that you can make multiple declarations of the same type by separating them with commas. Finally, as in all Java statements, the declaration statement ends with a semicolon.
Java data types can be divided into two categories: simple and composite. Simple data types are core types not derived from any other types. Integer, floating-point, boolean, and character types are all simple types. Composite types, on the other hand, are based on simple types and include strings, arrays, and both classes and interfaces in general. You learn about arrays later in this chapter. Classes and interfaces are covered in Chapter 5, "Classes, Packages, and Interfaces."
Integer data types are used to represent signed integer numbers. There are four integer types: byte, short, int, and long. Each of these types takes up a different amount of space in memory, as shown in Table 3.4.
Type | Size |
byte | 8 bits |
short | 16 bits |
int | 32 bits |
long | 64 bits |
int i; short rocketFuel; long angle, magnitude; byte red, green, blue;
Floating-point data types are used to represent numbers with fractional parts. There are two floating point types: float and double. The float type reserves storage for a 32-bit single-precision number; the double type reserves storage for a 64-bit double-precision number.
Declaring floating-point variables is very similar to declaring integer variables. Following are some examples of floating-point variable declarations:
float temperature; double windSpeed, barometricPressure;
The boolean data type (boolean) is used to store values with one of two states: true or false. You can think of the boolean type as a 1-bit integer value (because 1 bit can have only two possible values: 1 or 0). However, instead of using 1 and 0, you use the Java keywords true and false. true and false aren't just conveniences in Java; they are actually the only legal boolean values. This means that you can't interchangeably use booleans and integers as you can in C/C++. To declare a boolean value, just use the boolean type declaration:
boolean gameOver;
The character data type (char) is used to store single Unicode characters. Because the Unicode character set is composed of 16-bit values, the char data type is stored as a 16-bit unsigned integer. You create variables of type char as follows:
char firstInitial, lastInitial;
Remember that the char type is useful only for storing single characters. If you come from a C/C++ background, you may be tempted to fashion a string by creating an array of chars. In Java, this isn't necessary because the String class takes care of handling strings. This doesn't mean that you should never create arrays of characters, it just means that you shouldn't use a character array when you really want a string. C and C++ do not distinguish between character arrays and strings, but Java does.
Inevitably, there will be times when you have to convert from one data type to another. The process of converting one data type to another is called casting. Casting is often necessary when a function returns a type different than the type you need to perform an operation. For example, the read() member function of the standard input stream (System.in) returns an int. You must cast the returned int type to a char type before storing it, as in the following:
char c = (char)System.in.read();
The cast is performed by placing the desired type in parentheses to the left of the value to be converted. The System.in.read() function call returns an int value, which then is cast to a char value because of the (char) cast. The resulting char value is then stored in the char variable c.
When casting, the destination type should always be equal to or larger in size than the source type. Furthermore, you should pay close attention to casting across fundamental types, such as from floating-point to integer types. Table 3.5 lists the casts that are guaranteed to result in no loss of information.
From Type | To Type |
byte | short, char, int, long, float, double |
short | int, long, float, double |
char | int, long, float, double |
int | long, float, double |
long | float, double |
float | double |
In Java, source code is divided into parts separated by opening and closing curly braces: { and }. Everything between curly braces is considered a block and exists more or less independently of everything outside the braces. Blocks aren't important just from a logical sense--they are required as part of the syntax of the Java language. If you don't use braces, the compiler has trouble determining where one section of code ends and the next section begins. And from a purely aesthetic viewpoint, it is very difficult for someone else reading your code to understand what is going on if you don't use the braces. For that matter, it isn't very easy for you to understand your own code without the braces!
Braces are used to group related statements together. You can think of everything between matching braces as being executed as one statement. In fact, from an outer block, that's exactly what an inner block appears like: a single statement. But what's a block? A block is simply a section of code. Blocks are organized in a hierarchical fashion, meaning that code can be divided into individual blocks nested under other blocks. One block can contain one or more nested subblocks.
It is standard Java programming style to identify different blocks with indentation. Every time you enter a new block, you should indent your source code by a number of spaces--preferably two. When you leave a block, you should move back, or deindent, two spaces. This is a fairly established convention in many programming languages. However, indentation is just a style issue and is not technically part of the language. The compiler produces identical output even if you don't indent anything. Indentation is used for the programmer, not the compiler; it simply makes the code easier to follow and understand. Following is an example of the proper indentation of blocks in Java:
for (int i = 0; i < 5; i++) { if (i < 3) { System.out.println(i); } }
Following is the same code without any block indentations:
for (int i = 0; i < 5; i++) { if (i < 3) { System.out.println(i); } }
The first bit of code clearly shows the breakdown of program flow through the use of indentation; it is obvious that the if statement is nested within the for loop. The second bit of code, on the other hand, provides no visual clues about the relationship between the blocks of code. Don't worry if you don't know anything about if statements and for loops; you'll learn plenty about them in the next chapter, "Expressions, Operators, and Control Structures."
The concept of scope is tightly linked to blocks and is very important when you are working with variables in Java. Scope refers to how sections of a program (blocks) affect the lifetime of variables. Every variable declared in a program has an associated scope, meaning that the variable is used only in that particular part of the program.
Scope is determined by blocks. To better understand blocks, take a look again at the HelloWorld class in Listing 3.1, earlier in this chapter. The HelloWorld class is composed of two blocks. The outer block of the program is the block defining the HelloWorld class:
class HelloWorld { ... }
Class blocks are very important in Java. Almost everything of interest is either a class itself or belongs to a class. For example, methods are defined inside the classes to which they belong. Both syntactically and logically, everything in Java takes place inside a class. Getting back to HelloWorld, the inner block defines the code within the main() method, as follows:
public static void main (String args[]) { ... }
The inner block is considered to be nested within the outer block of the program. Any variables defined in the inner block are local to that block and are not visible to the outer block; the scope of the variables is defined as the inner block.
To get an even better idea of what's behind the usage of scope and blocks, take a look at the HowdyWorld class in Listing 3.2.
class HowdyWorld { public static void main (String args[]) { int i; printMessage(); } public static void printMessage () { int j; System.out.println("Howdy, World!"); } }
The HowdyWorld class contains two methods: main() and printMessage(). main() should be familiar to you from the HelloWorld class, except that in this case, it declares an integer variable i and calls the printMessage() method. printMessage() is a new method that declares an integer variable j and prints the message Howdy, World! to the standard output stream, much like the main() method does in HelloWorld.
You've probably figured out already that HowdyWorld results in basically the same output as HelloWorld because the call to printMessage() results in a single text message being displayed. What you may not see right off is the scope of the integers defined in each method. The integer i defined in main() has a scope limited to the body of the main() method. The body of main() is defined by the curly braces around the method (the method block). Similarly, the integer j has a scope limited to the body of the printMessage() method. The importance of the scope of these two variables is that the variables aren't visible beyond their respective scopes; the HowdyWorld class block knows nothing about the two integers. Furthermore, main() doesn't know anything about j, and printMessage() knows nothing about i.
Scope becomes more important when you start nesting blocks of code within other blocks. The GoodbyeWorld class shown in Listing 3.3 is a good example of variables nested within different scopes.
class GoodbyeWorld { public static void main (String args[]) { int i, j; System.out.println("Goodbye, World!"); for (i = 0; i < 5; i++) { int k; System.out.println("Bye!"); } } }
The integers i and j have scopes within the main() method body. The integer k, however, has a scope limited to the for loop block. Because k's scope is limited to the for loop block, it cannot be seen outside that block. On the other hand, i and j still can be seen within the for loop block. What this means is that scoping has a top-down hierarchical effect--variables defined in outer scopes can still be seen and used within nested scopes; however, variables defined in nested scopes are limited to those scopes. Incidentally, don't worry if you aren't familiar with for loops--you learn all about them in the next chapter.
For more reasons than visibility, it is important to pay attention to the scope of variables when you declare them. Along with determining the visibility of variables, the scope also determines the lifetime of variables. This means that variables are actually destroyed when program execution leaves their scope. Look at the GoodbyeWorld example again: Storage for the integers i and j is allocated when program execution enters the main() method. When the for loop block is entered, storage for the integer k is allocated. When program execution leaves the for loop block, the memory for k is freed and the variable is destroyed. Similarly, when program execution leaves main(), all the variables in its scope (i and j) are freed and destroyed. The concepts of variable lifetime and scope become even more important when you start dealing with classes. You'll get a good dose of this in Chapter 5, "Classes, Packages, and Interfaces."
An array is a construct that provides for the storage of a list of items of the same type. Array items can have either a simple or composite data type. Arrays also can be multidimensional. Java arrays are declared with square brackets: []. Following are a few examples of array declarations in Java:
int numbers[]; char[] letters; long grid[][];
If you are familiar with arrays in another language, you may be puzzled by the absence of a number between the square brackets specifying the number of items in the array. Java doesn't allow you to specify the size of an empty array when declaring the array. You must always explicitly set the size of the array with the new operator or by assigning a list of items to the array at the time of creation. The new operator is covered in the next chapter, "Expressions, Operators, and Control Structures."
Another strange thing you may notice about Java arrays is the optional placement of the square brackets in the array declaration. You can place the square brackets either after the variable type or after the identifier.
Following are a couple examples of arrays that have been declared and set to a specific size by using the new operator and by assigning a list of items in the array declaration:
char alphabet[] = new char[26]; int primes = {7, 11, 13};
More complex structures for storing lists of items, such as stacks and hash tables, are also supported by Java. Unlike arrays, these structures are implemented in Java as classes. You'll get a crash course in some of these other storage mechanisms in Chapter 11, "The Utilities Package."
In Java, strings are handled by a special class called String. Even literal strings are managed internally by an instantiation of a String class. An instantiation of a class is simply an object that has been created based on the class description. This method of handling strings is very different from languages like C and C++, where strings are represented simply as an array of characters. Following are a few strings declared using the Java String class:
String message;
String name = "Mr. Blonde";
At this point, it's not that important to know the String class inside and out. You'll learn all the gory details of the String class in Chapter 10, "The Language Package."
This chapter took a look at the core components of the Java language. Hopefully, you now have more insight about why Java has become popular in such a relatively short time. With vast improvements over the weaknesses of the C and C++ languages--arguably the industry's language standards--Java is making huge waves and may conceivably replace C and C++ at some point in the near future. The language elements covered in this chapter are just the tip of the iceberg when it comes to the benefits of programming in Java.
Now that you are armed with the fundamentals of the Java language, you are no doubt ready to press onward and learn more about the Java language. The next chapter, "Expressions, Operators, and Control Structures," covers exactly what its title suggests. In it, you learn how to work with and manipulate much of the information you learned about in this chapter. In doing so, you will be able to start writing programs that do a little more than display cute messages on the screen.
©Copyright, Macmillan Computer Publishing. All rights reserved.