This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
A Programmer’s Guide to Java™ SCJP Certification Third Edition
This page intentionally left blank
A Programmer’s Guide to Java™ SCJP Certification A Comprehensive Primer Third Edition
Khalid A. Mughal Rolf W. Rasmussen
Upper Saddle River, New Jersey • Boston • Indianapolis • San Francisco New York • Toronto • Montreal • London • Munich • Paris • Madrid Capetown • Sidney • Tokyo • Singapore • Mexico City
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and the publisher was aware of a trademark claim, the designations have been printed with initial capital letters or in all capitals. The authors and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein. The publisher offers excellent discounts on this book when ordered in quantity for bulk purchases or special sales, which may include electronic versions and/or custom covers and content particular to your business, training goals, marketing focus, and branding interests. For more information, please contact: U.S. Corporate and Government Sales (800) 382-3419 [email protected] For sales outside the United States please contact: International Sales [email protected] Visit us on the Web: informit.com/aw Library of Congress Cataloging-in-Publication Data Mughal, Khalid Azim. A programmer's guide to Java SCJP certification : a comprehensive primer / Khalid A. Mughal, Rolf W. Rasmussen.—3rd ed. p. cm. Previously published under title: A programmer’s guide to Java certification. Includes bibliographical references and index. ISBN 0-321-55605-4 (pbk. : alk. paper) 1. Electronic data processing personnel--Certification. 2. Operating systems (Computers)—Examinations--Study guides. 3. Java (Computer program language)--Examinations-Study guides. I. Rasmussen, Rolf (Rolf W.) II. Mughal, Khalid Azim. Programmer’s guide to Java certification. III. Title. QA76.3.M846 2008 005.2'762--dc22
To the loving memory of my mother, Zubaida Begum, and my father, Mohammed Azim. —K.A.M.
For Olivia E. Rasmussen and Louise J. Dahlmo. —R.W.R.
This page intentionally left blank
Contents Overview
Foreword Preface 1 Basics of Java Programming
xxxv xxxvii 1
2 Language Fundamentals
19
3 Declarations
39
4 Access Control
103
5 Operators and Expressions
159
6 Control Flow
203
7 Object-Oriented Programming
283
8 Nested Type Declarations
351
9 Object Lifetime
389
10 Fundamental Classes
423
11 Files and Streams
467
12 Localization, Pattern Matching and Formatting
531
13 Threads
613
14 Generics
661 vii
viii
CONTENTS
15 Collections and Maps
747
A Taking the SCJP 1.6 Exam
851
B Objectives for the SCJP 1.6 Exam
857
C Objectives for the SCJP 1.6 Upgrade Exam
863
D Annotated Answers to Review Questions
869
E Solutions to Programming Exercises
935
F Mock Exam
959
G Number Systems and Number Representation Index
1005 1013
Contents
List of Figures
xxiii
List of Tables
xxvii
List of Examples
xxix
Foreword
xxxv
Preface 1 Basics of Java Programming 1.1 1.2 1.3
1.4 1.5 1.6 1.7 1.8 1.9 1.10
Introduction Classes Declaring Members: Fields and Methods Objects Class Instantiation, Reference Values, and References Object Aliases Instance Members Invoking Methods Static Members Inheritance Aggregation Tenets of Java Review Questions Java Programs Sample Java Application Essential Elements of a Java Application Compiling and Running an Application Review Questions Chapter Summary Programming Exercise
Basic Language Elements Lexical Tokens Identifiers Keywords Literals Integer Literals Floating-Point Literals Boolean Literals Character Literals String Literals White Spaces Comments Review Questions Primitive Data Types Integer Types The char Type The Floating-Point Types The boolean Type Review Questions Variable Declarations Declaring and Initializing Variables Reference Variables Initial Values for Variables Default Values for Fields Initializing Local Variables of Primitive Data Types Initializing Local Reference Variables Lifetime of Variables Review Questions Chapter Summary Programming Exercise
Declarations 3.1 3.2
3.3
3.4
Class Declarations JavaBeans Standard Naming Patterns for Properties Naming Patterns for the Event Model Method Declarations Statements Instance Methods and the Object Reference this Method Overloading Constructors The Default Constructor Overloaded Constructors Review Questions
Enumerated Types Declaring Typesafe Enums Using Typesafe Enums Declaring Enum Constructors and Members Implicit Static Methods for Enum Types Inherited Methods from the Enum Class Extending Enum Types: Constant-Specific Class Bodies Declaring Typesafe Enums Revisited Review Questions Arrays Declaring Array Variables Constructing an Array Initializing an Array Using an Array Anonymous Arrays Multidimensional Arrays Review Questions Parameter Passing Passing Primitive Data Values Passing Reference Values Passing Arrays Array Elements as Actual Parameters final Parameters Variable Arity Methods Calling a Varargs Method Varargs and Non-Varargs Method Calls The main() Method Program Arguments Review Questions Chapter Summary Programming Exercises
4 Access Control 4.1 4.2
4.3 4.4 4.5 4.6
Java Source File Structure Packages Defining Packages Using Packages Compiling Code into Packages Running Code from Packages Searching for Classes The JAR Utility System Properties Review Questions Scope Rules Class Scope for Members
Block Scope for Local Variables Accessibility Modifiers for Top-Level Type Declarations Other Modifiers for Classes abstract Classes final Classes Review Questions Member Accessibility Modifiers public Members protected Members Default Accessibility for Members private Members Review Questions Other Modifiers for Members static Members final Members abstract Methods synchronized Methods native Methods transient Fields volatile Fields Review Questions Chapter Summary Programming Exercise
Operators and Expressions 5.1
5.2
5.3 5.4
5.5
Conversions Widening and Narrowing Primitive Conversions Widening and Narrowing Reference Conversions Boxing and Unboxing Conversions Other Conversions Type Conversion Contexts Assignment Context Method Invocation Context Casting Context of the Unary Type Cast Operator: (type) Numeric Promotion Context Precedence and Associativity Rules for Operators Evaluation Order of Operands Left-Hand Operand Evaluation First Operand Evaluation before Operation Execution Left to Right Evaluation of Argument Lists The Simple Assignment Operator = Assigning Primitive Values Assigning References Multiple Assignments Type Conversions in Assignment Context
Review Questions Arithmetic Operators: *, /, %, +, Arithmetic Operator Precedence and Associativity Evaluation Order in Arithmetic Expressions Range of Numeric Values Unary Arithmetic Operators: -, + Multiplicative Binary Operators: *, /, % Additive Binary Operators: +, Numeric Promotions in Arithmetic Expressions Arithmetic Compound Assignment Operators: *=, /=, %=, +=, -= Review Questions The Binary String Concatenation Operator + Variable Increment and Decrement Operators: ++, -The Increment Operator ++ The Decrement Operator -Review Questions Boolean Expressions Relational Operators: <, <=, >, >= Equality Primitive Data Value Equality: ==, != Object Reference Equality: ==, != Object Value Equality Boolean Logical Operators: !, ^, &, | Operand Evaluation for Boolean Logical Operators Boolean Logical Compound Assignment Operators: &=, ^=, |= Conditional Operators: &&, || Short-Circuit Evaluation Review Questions The Conditional Operator: ?: Other Operators: new, [], instanceof Chapter Summary Programming Exercise
6 Control Flow 6.1 6.2
6.3
Overview of Control Flow Statements Selection Statements The Simple if Statement The if-else Statement The switch Statement Review Questions Iteration Statements The while Statement The do-while Statement The for(;;) Statement The for(:) Statement
Transfer Statements Labeled Statements The break Statement The continue Statement The return Statement Review Questions Stack-Based Execution and Exception Propagation Exception Types The Exception Class The RuntimeException Class The Error Class Checked and Unchecked Exceptions Defining New Exceptions Exception Handling: try, catch, and finally The try Block The catch Block The finally Block The throw Statement The throws Clause Review Questions Assertions The assert Statement and the AssertionError Class Compiling Assertions Runtime Enabling and Disabling of Assertions Using Assertions Review Questions Chapter Summary Programming Exercises
Object-Oriented Programming 7.1
7.2
7.3
7.4 7.5
Single Implementation Inheritance Inheritance Hierarchy Relationships: is-a and has-a The Supertype-Subtype Relationship Overriding Methods Instance Method Overriding Covariant return in Overriding Methods Overriding vs. Overloading Hiding Members Field Hiding Static Method Hiding The Object Reference super Review Questions Chaining Constructors Using this() and super() The this() Constructor Call
The super() Constructor Call Review Questions Interfaces Defining Interfaces Abstract Method Declarations Implementing Interfaces Extending Interfaces Interface References Constants in Interfaces Review Questions Arrays and Subtyping Arrays and Subtype Covariance Array Store Check Reference Values and Conversions Reference Value Assignment Conversions Method Invocation Conversions Involving References Overloaded Method Resolution Reference Casting and the instanceof Operator The Cast Operator The instanceof Operator Review Questions Polymorphism and Dynamic Method Lookup Inheritance Versus Aggregation Basic Concepts in Object-Oriented Design Encapsulation Cohesion Coupling Review Questions Chapter Summary Programming Exercises
8 Nested Type Declarations 8.1 8.2
8.3
8.4
8.5
Overview of Nested Type Declarations Static Member Types Declaring and Using Static Member Types Accessing Members in Enclosing Context Non-Static Member Classes Instantiating Non-Static Member Classes Accessing Members in Enclosing Context Review Questions Local Classes Accessing Declarations in Enclosing Context Instantiating Local Classes Anonymous Classes Extending an Existing Class
Overview of the java.lang Package The Object Class Review Questions The Wrapper Classes Common Wrapper Class Constructors Common Wrapper Class Utility Methods Numeric Wrapper Classes The Character Class The Boolean Class Review Questions The String Class Immutability Creating and Initializing Strings The CharSequence Interface Reading Characters from a String Comparing Strings Character Case in a String Concatenation of Strings Searching for Characters and Substrings Extracting Substrings Converting Primitive Values and Objects to Strings
Formatting Values Pattern Matching Review Questions The StringBuilder and the StringBuffer Classes Thread-Safety Mutability Constructing String Builders Reading and Changing Characters in String Builders Constructing Strings from String Builders Appending, Inserting, and Deleting Characters in String Builders Controlling String Builder Capacity Review Questions Chapter Summary Programming Exercises
11 Files and Streams 11.1 11.2
11.3
11.4
11.5 11.6
Input and Output The File Class Querying the File System File or Directory Existence File and Directory Permissions Listing Directory Entries Creating New Files and Directories Renaming Files and Directories Deleting Files and Directories Byte Streams: Input Streams and Output Streams File Streams Filter Streams Reading and Writing Binary Values Review Questions Character Streams: Readers and Writers Print Writers Writing Text Files Reading Text Files Using Buffered Writers Using Buffered Readers The Standard Input, Output, and Error Streams Comparison of Byte Streams and Character Streams The Console class Review Questions Object Serialization The ObjectOutputStream Class The ObjectInputStream Class Customizing Object Serialization Serialization and Inheritance
12 Localization, Pattern Matching, and Formatting 12.1 12.2 12.3
12.4
12.5
12.6
12.7
The java.util.Locale Class The java.util.Date Class The java.util.Calendar Class Static Factory Methods to Create a Calendar Interoperability with the Date Class Selected get and set Methods Manipulating a Calendar Comparing Calendars The java.text.DateFormat Class Static Factory Methods to Create a Date/Time Formatter Formatting Dates Parsing Strings to Date/Time Managing the Calendar and the Number Formatter The java.text.NumberFormat Class Static Factory Methods to Create a Number Formatter Formatting Numbers and Currency Parsing Strings to Numbers Specifying the Number of Digits Review Questions String Pattern Matching Using Regular Expressions Regular Expression Fundamentals Escaping Metacharacters The java.util.regex.Pattern Class The java.util.regex.Matcher Class The java.util.Scanner Class Review Questions Formatting Values Overview Defining Format Specifiers Conversion Categories and Formatting Conversions Selected Format Exceptions Using the format() Method Review Questions Chapter Summary Programming Exercises
Thread Creation Implementing the Runnable Interface Extending the Thread Class Review Questions Synchronization Locks Synchronized Methods Synchronized Blocks Review Questions Thread Transitions Thread States Thread Priorities Thread Scheduler Running and Yielding Sleeping and Waking Up Waiting and Notifying Joining Blocking for I/O Thread Termination Deadlocks Review Questions Chapter Summary Programming Exercises
14 Generics 14.1 14.2
14.3 14.4
14.5
14.6
Introducing Generics Generic Types and Parameterized Types Generic Types Parameterized Types Generic Interfaces Extending Generic Types Raw Types and Unchecked Warnings Collections and Generics Wildcards The Subtype Covariance Problem with Parameterized Types Wildcard Types Subtype Covariance: ? extends Type Subtype Contravariance: ? super Type Subtype Bivariance: ? Subtype Invariance: Type Some Restrictions on Wildcard Types Using References of Wildcard Parameterized Types Generic Reference Assignment Using Parameterized References to Call Set and Get Methods Bounded Type Parameters Multiple Bounds
Review Questions Implementing a Simplified Generic Stack Generic Methods and Constructors Generic Method Declaration Calling Generic Methods Wildcard Capture Capture Conversion Flexibility with Wildcard Parameterized Types Nested Wildcards Wildcard Parameterized Types as Formal Parameters Flexible Comparisons with Wildcards Recursive Bounds Type Erasure Bridge Methods Implications for Overloading and Overriding Method Signature Implications for Overloading Implications for Overriding Limitations and Restrictions on Generic Types Reifiable Types Implications for instanceof operator Implications for Casting Implications for Arrays Implications for Varargs Implications for Exception Handling Implications for Nested Classes Other Implications Review Questions Chapter Summary Programming Exercises
15 Collections and Maps 15.1
15.2
15.3
Comparing Objects The equals() Method The hashCode() Method The Comparable<E> Interface The Comparator<E> Interface Review Questions The Java Collections Framework Core Interfaces Implementations Collections Basic Operations Bulk Operations Iterators
Array Operations Review Questions 15.4 Sets The HashSet<E> and LinkedHashSet<E> Classes 15.5 The SortedSet<E> and NavigableSet<E> Interfaces The SortedSet<E> Interface The NavigableSet<E> Interface The TreeSet<E> Class 15.6 Lists The ArrayList<E>, LinkedList<E>, and Vector<E> Classes 15.7 Queues The Queue<E> Interface The PriorityQueue<E> and LinkedList<E> Classes The Deque<E> Interface The ArrayDeque<E> and LinkedList<E> Class Review Questions 15.8 Maps Basic Operations Bulk Operations Collection Views 15.9 Map Implementations The HashMap, LinkedHashMap, and Hashtable Classes 15.10 The SortedMap and NavigableMap Interfaces The SortedMap Interface The NavigableMap Interface The TreeMap Class Review Questions 15.11 Working with Collections Ordering Elements in Lists Searching in Collections Changing Elements in Collections Sorting Arrays Searching in Arrays Creating List Views of Arrays Miscellaneous Utility Methods in the Arrays Class Review Questions Chapter Summary Programming Exercises
A
Taking the SCJP 1.6 Exam A.1 A.2
Preparing for the Programmer Exam Registering for the Exam Obtaining an Exam Voucher Signing Up for the Test Contact Information
After Taking the Exam How the Examination Is Conducted The Testing Locations Utilizing the Allotted Time The Exam Program The Questions Types of Questions Asked Types of Answers Expected Topics Covered by the Questions Moving on to Other Java Technology Exams
Objectives for the SCJP 1.6 Exam
853 853 853 853 854 854 854 855 855 856
857
C Objectives for the SCJP 1.6 Upgrade Exam
863
D Annotated Answers to Review Questions
869
E Solutions to Programming Exercises
935
F Mock Exam
959
G
Number Systems and Number Representation G.1
G.2 G.3
G.4
Number Systems Binary, Octal, and Hexadecimal Number System Converting Binary Numbers to Decimals Converting Octal and Hexadecimal Numbers to Decimals Relationship between Binary, Octal, and Hexadecimal Numbers Converting Decimals Converting Decimals to Binary Numbers Converting Decimals to Octal and Hexadecimal Numbers Representing Integers Calculating 2’s Complement
1.1 UML Notation for Classes Chapter 11 1.2 UML Notation for Objects 1.3 Aliases 1.4 Class Diagram Showing Static Members of a Class 1.5 Members of a Class 1.6 Class Diagram Depicting Inheritance Relationship 1.7 Class Diagram Depicting Aggregation 2.1 Primitive Chapter 2 19Data Types in Java 3.1 The Event Chapter 3 39Model 3.2 Array of Arrays 3.3 Parameter Passing: Primitive Data Values 3.4 Parameter Passing: Reference Values 3.5 Parameter Passing: Arrays 4.1 Java Source Chapter 4 103File Structure 4.2 Package Hierarchy 4.3 File Hierarchy 4.4 Searching for Classes 4.5 Searching in JAR files 4.6 Block Scope 4.7 Public Accessibility 4.8 Protected Accessibility 4.9 Default Accessibility 4.10 Private Accessibility 5.1 Widening Primitive Conversions Chapter 5 159 5.2 Overflow and Underflow in Floating-point Arithmetic 5.3 Numeric Promotion in Arithmetic Expressions 6.1 Activity Diagram for if Statements Chapter 6 203 6.2 Activity Diagram for a switch Statement 6.3 Activity Diagram for the while Statement 6.4 Activity Diagram for the do-while Statement 6.5 Activity Diagram for the for Statement 6.6 Enhanced for Statement 6.7 Method Execution
6.8 Exception Propagation 6.9 Partial Exception Inheritance Hierarchy 6.10 The try-catch-finally Construct 6.11 Exception Handling (Scenario 1) 6.12 Exception Handling (Scenario 2) 6.13 Exception Handling (Scenario 3) 6.14 Execution of the Simple assert Statement (with Assertions Enabled) 6.15 Package Hierarchy 7.1 Inheritance Chapter 7 283Hierarchy 7.2 Inheritance Relations 7.3 Reference Type Hierarchy: Arrays and Subtype Covariance 7.4 Type Hierarchy to Illustrate Polymorphism 7.5 Implementing Data Structures by Inheritance and Aggregation 8.1 Static Member Chapter 8 351 Classes and Interfaces 8.2 Outer Object with Associated Inner Objects 8.3 Nested Classes and Inheritance 8.4 Local Classes and Inheritance Hierarchy 9.1 Memory Organization at Runtime Chapter 9 389 10.1 Partial10 Inheritance Hierarchy in the java.lang Package Chapter 423 10.2 Converting Values Between Primitive, Wrapper, and String Types 11.1 Partial11 Byte Stream Inheritance Hierarchies Chapter 467 11.2 Stream Chaining for Reading and Writing Binary Values to a File 11.3 Partial Character Stream Inheritance Hierarchies 11.4 Setting up a PrintWriter to Write to a File 11.5 Setting up Readers to read Characters 11.6 Buffered Writers 11.7 Buffered Readers 11.8 Keyboard and Display as Console 11.9 Object Stream Chaining 13.1 Spawning Threads Using a Runnable Object Chapter 12 613 13 531 13.2 Spawning Threads—Extending the Thread Class 13.3 Thread States 13.4 Running and Yielding 13.5 Sleeping and Waking up 13.6 Waiting and Notifying 13.7 Thread Communication 13.8 Stack Users 13.9 Joining of Threads 13.10 Deadlock 14.1 Extending Generic Types Chapter 14 661 14.2 No Subtype Covariance for Parameterized Types 14.4 Partial Type Hierarchy for Node super Integer> 14.3 Partial Type Hierarchy for Node extends Number> 14.5 Partial Type Hierarchy for Selected Parameterized Types of Node<E> 14.6 Flexible Comparisons with Wildcards 15.1 The Core Interfaces Chapter 15 747
The Core Collection Interfaces and Their Implementations The Core Map Interfaces and Their Implementations Bulk Operations on Collections Converting between Binary, Octal, and Hexadecimal
Terminology for Class Members Keywords in Java Reserved Literals in Java Reserved Keywords not Currently in Use Examples of Literals Examples of Decimal, Octal, and Hexadecimal Literals Examples of Character Literals Escape Sequences Examples of Escape Sequence \ddd Range of Integer Values Range of Character Values Range of Floating-Point Values Boolean Values Summary of Primitive Data Types Default Values Parameter Passing By Value Accessing Members within a Class Summary of Accessibility Modifiers for Top-Level Types Summary of Other Modifiers for Types Summary of Accessibility Modifiers for Members Summary of Other Modifiers for Members Selected Conversion Contexts and Conversion Categories Operator Summary Examples of Truncated Values Arithmetic Operators Examples of Arithmetic Expression Evaluation Arithmetic Compound Assignment Operators Relational Operators Reference Equality Operators Primitive Data Value Equality Operators Truth-Values for Boolean Logical Operators Boolean Logical Compound Assignment Operators Conditional Operators
Truth-values for Conditional Operators 197 The return Statement 228 Granularities for Enabling and Disabling Assertions at Runtime 269 Enabling and Disabling Assertions in All System Classes at Runtime 272 Overriding vs. Overloading 293 Types and Values 317 Overview of Type Declarations 354 Selected Input Streams 477 Selected Output Streams 477 480 The DataInput and DataOutput Interfaces Selected Readers 488 Selected Writers 490 Print Methods of the PrintWriter Class 491 Correspondence Between Selected Byte and Character Streams 500 Selected Language Codes 532 Selected Country Codes 532 Selected Predefined Locales for Languages 533 Selected Predefined Locales for Countries 533 Selected Field Numbers to Indicate Information in a Calendar 537 Selected Constants that Represent Values in a Calendar 538 Formatting Styles for Date and Time 542 Selected Characters 555 Selected Character Classes 556 Selected Predefined Character Classes 557 Boundary Matchers 557 Selected Logical Operators 558 Quantifier Classification 561 Implications of the Limit Value in the split() Method 564 Formatting Conversions 596 Flags 597 Selected Format Exceptions 601 Selected Time/Date Composition Conversions 601 Thread States 636 Summary of Subtyping Relationships for Generic Types 675 Get and Set Operations Using Parameterized References 682 Summary of Get and Set Operations using Parameterized References 684 Examples of Type Erasure 714 Examples of Reifiable Types 723 Examples of Non-Reifiable Types 723 Core Interfaces in the Collections Framework 779 Summary of Collection and Map Implementations 782 Bulk Operations and Set Logic 796 Number Systems 1005 Representing Signed byte Values Using 2’s Complement 1010
List of Examples
1.1 Basic Elements of a Class Declaration Chapter 11 1.2 Static Members in Class Declaration 1.3 Defining a Subclass 1.4 An Application 2.1 Default for Fields Chapter 2 Values 19 2.2 Flagging Uninitialized Local Variables of Primitive Data Types 2.3 Flagging Uninitialized Local Reference Variables 3.1 A JavaBean Chapter 3 39 3.2 Using the this Reference 3.3 Namespaces 3.4 Using Enums 3.5 Declaring Enum Constructors and Members 3.6 Declaring Constant-Specific Class Bodies 3.7 Using Arrays 3.8 Using Anonymous Arrays 3.9 Using Multidimensional Arrays 3.10 Passing Primitive Values 3.11 Passing Reference Values 3.12 Passing Arrays 3.13 Array Elements as Primitive Data Values 3.14 Array Elements as Reference Values 3.15 Calling a Varargs Method 3.16 Passing Program Arguments 4.1 Defining Packages and Using Type Import Chapter 4 103 4.2 Single Static Import 4.3 Avoiding the Interface Constant Antipattern 4.4 Importing Enum Constants 4.5 Shadowing by Importing 4.6 Conflict in Importing Static Method with the Same Signature 4.7 Importing Nested Static Types 4.8 Using Properties 4.9 Class Scope 4.10 Accessibility Modifiers for Classes and Interfaces
4.11 Abstract Classes 4.12 Public Accessibility of Members 4.13 Accessing Static Members 4.14 Accessing Final Members 4.15 Synchronized Methods 5.1 Operand Evaluation Order Chapter 5 159 5.2 Numeric Promotion in Arithmetic Expressions 5.3 Short-Circuit Evaluation Involving Conditional Operators 6.1 Fall Through Chapter 6 203 in a switch Statement 6.2 Using break in a switch Statement 6.3 Nested switch Statement 6.4 Enums in switch Statement 6.5 The break Statement 6.6 Labeled break Statement 6.7 The continue Statement 6.8 Labeled continue Statement 6.9 The return Statement 6.10 Method Execution 6.11 The try-catch Construct 6.12 Exception Propagation 6.13 The try-catch-finally Construct 6.14 The try-finally Construct 6.15 The finally Block and the return Statement 6.16 Throwing Exceptions 6.17 The throws Clause 6.18 Using Assertions 7.1 Extending Classes: Inheritance and Accessibility Chapter 7 283 7.2 Overriding, Overloading, and Hiding 7.3 Using the super Keyword 7.4 Constructor Overloading 7.5 The this() Constructor Call 7.6 The super() Constructor Call 7.7 Interfaces 7.8 Variables in Interfaces 7.9 Assigning and Passing Reference Values 7.10 Choosing the Most Specific Method (Simple Case) 7.11 Overloaded Method Resolution 7.12 The instanceof and Cast Operators 7.13 Using the instanceof Operator 7.14 Polymorphism and Dynamic Method Lookup 7.15 Implementing Data Structures by Inheritance and Aggregation 8.1 Overview of Type Declarations Chapter 8 351 8.2 Static Member Types 8.3 Importing Static Member Types 8.4 Accessing Members in Enclosing Context (Static Member Classes) 8.5 Defining and Instantiating Non-static Member Classes
Special Form of this and new Constructs in Non-static Member Classes 8.7 Inheritance Hierarchy and Enclosing Context 8.8 Extending Inner Classes 8.9 Access in Enclosing Context (Local Classes) 8.10 Instantiating Local Classes 8.11 Objects of Local Classes as Caches 8.12 Defining Anonymous Classes 8.13 Accessing Declarations in Enclosing Context (Anonymous Classes) 9.1 Garbage Collection Eligibility Chapter 9 389 9.2 Using Finalizers 9.3 Invoking Garbage Collection 9.4 Initializer Expression Order and Method Calls 9.5 Exceptions in Initializer Expressions 9.6 Static Initializers and Forward References 9.7 Static Initializer Blocks and Exceptions 9.8 Instance Initializers and Forward References 9.9 Instance Initializer Block in Anonymous Class 9.10 Exception Handling in Instance Initializer Blocks 9.11 Object State Construction 9.12 Initialization under Object State Construction 10.1 Methods the Object class Chapter 10 in 423 10.2 String Representation of Integers 10.3 String Construction and Equality 10.4 Reading Characters from a String 11.1 Listing11 Files Chapter 467Under a Directory 11.2 Copy a File 11.3 Reading and Writing Binary Values 11.4 Demonstrating Readers and Writers, and Character Encoding 11.5 Changing Passwords 11.6 Object Serialization 11.7 Non-Serializable Objects 11.8 Customized Serialization 11.9 Serialization and Inheritance 12.1 Understanding Chapter 12 531 Locales 12.2 Using the Date class 12.3 Using the Calendar Class 12.4 Formatting Date/Time 12.5 Using the DateFormat class 12.6 Using the NumberFormat class 12.7 Splitting 12.8 String Pattern Matching 12.9 Match and Replace 12.10 Tokenizing Mode 12.11 Parsing Primitive Values and Strings 12.12 Using Delimiters and Patterns with a Scanner
12.13 Multi-Line Mode 12.14 Using the format() Method 13.1 Implementing Chapter 13 613 the Runnable Interface 13.2 Extending the Thread Class 13.3 Mutual Exclusion 13.4 Thread States 13.5 Waiting and Notifying 13.6 Joining of Threads 13.7 Thread Termination 13.8 Deadlock 14.1 A Legacy Class Chapter 14 661 14.2 A Generic Class for Nodes 14.3 A Generic Interface and its Implementation 14.4 Extending Generic Types 14.5 Unchecked Warnings 14.6 Illustrating Get and Set Operations Using Parameterized References 14.7 Implementing a Simplified Generic Stack 14.8 Declaring and Calling Generic Methods 14.9 Flexible Comparisons 14.10 Using Recursive Bounds 14.11 Using the @Override Annotation 14.12 Subsignatures 14.13 Overriding from Generic Supertype 14.14 Missing Supertype Parameterization 14.15 Genericity Cannot Be Added to Inherited Methods 14.16 Type Parameter in throws Clause 14.17 Generic Nested Classes 15.1 A Test15 Case for Version Numbers Chapter 747 15.2 Not Overriding the equals() and the hashCode() Methods 15.3 Testing the equals() and the hashCode() Methods 15.4 Implementing the equals() Method 15.5 Implications of Overriding the equals() Method 15.6 Implementing the hashCode() Method 15.7 Implications of Overriding the hashCode() Method 15.8 Implementing the compareTo() Method of the Comparable Interface 15.9 Implications of Implementing the compareTo() Method 15.10 Natural Ordering and Total Ordering 15.11 Using a Comparator for Version Numbers 15.12 Using an Iterator 15.13 Using a for(:) Loop to Iterate Over a Collection 15.14 Converting Collections to Arrays 15.15 Traversing Over Sets 15.16 Using Sets 15.17 Using Navigable Sets 15.18 Using Lists 15.19 Using Priority Queues
Using Deques as a Stack and as a FIFO Queue Using Maps Using Navigable Maps
815 825 831
This page intentionally left blank
Foreword
Consider the following observations: • Software continues to become ever more pervasive, ever more ubiquitous in our lives. • Incompetence seems to be the only thing we can count on in today’s world and, especially, in the domain of software. • The Java programming language has become a lingua franca for programmers all over the world. One can draw varied conclusions from these comments. One of them is that it is of great importance that programmers working with the Java programming language should be as competent as possible. The Java certification program is an important effort aimed at precisely this goal. Practitioners looking to obtain such certification need good quality training materials, which brings us to this book. Programming is still more of an art than a science, and will continue to be so for the foreseeable future. Mastering the intricacies of a large and complex programming language is a challenging task that requires time and effort, and above all experience. Real programming requires more than just mastery of a programming language. It requires mastery of a computing platform, with a rich set of libraries. These libraries are designed to simplify the task of building realistic applications, and they do. Again, the practitioner is faced with a daunting task. To address the clear need for professional training material, a plethora of books have been written purporting to tutor programmers in the programming language and platform skills they require. The choice is as mind boggling as the material within the books themselves. Should one try Java for Frontally Lobotomized Simians or Postmodern Java Dialectics? The readership for these books is largely self selecting. I trust that if you, the reader,
xxxv
xxxvi
FOREWORD
have gotten this far, you are looking for something that is intelligent, yet practical. This book is one of the finest efforts in this crowded arena. It brings a necessary level of academic rigor to an area much in need of it, while retaining an essentially pragmatic flavor. The material in this book is probably all you need to pass the Java certification exam. It certainly isn’t all you need to be a good software engineer. You must continue learning about new technologies. The hardest part of this is dealing with things that are completely different from what you are familiar with. Yet this is what distinguishes the top flight engineer from the mediocre one. Keep an open mind; it pays. Gilad Bracha Computational Theologist Sun Java Software http://java.sun.com/people/gbracha/
Preface
Writing the Third Edition The exam for the Sun Certified Programmer for Java Platform, Standard Edition 6, has changed considerably since the second edition of this book was published. The most noticeable change in the current version of the Sun Certified Java Programmer (SCJP) 1.6 exam is the inclusion of the features of Java 5, and the shifting of emphasis towards analyzing code scenarios, rather than individual language constructs. In our opinion, the new exam demands an even greater understanding and actual experience of the language, rather than mere recitation of facts. Proficiency in the language is the key to success. Since the emphasis of the SCJP 1.6 exam is on the core features of Java, the third edition provides even greater in-depth coverage of the relevant topics. The book covers not just the exam objectives, but also supplementary topics that aid in mastering the exam topics. The third edition is still a one-source guide for the SCJP 1.6 exam: it provides a mixture of theory and practice for the exam. Use the book to learn Java, pass the SCJP 1.6 exam, and afterwards, use it as a handy language guide. The book also has an appendix devoted to the SCJP 1.6 Upgrade exam. We have taken into consideration the feedback we have received from readers. The many hours spent in handling the deluge of e-mail have not been in vain. Every single e-mail is appreciated and is hereby acknowledged. Preparing the third edition dispelled our illusions about newer editions being, to put it colloquially, a piece of cake. Every sentence from the second edition has been weighed carefully, and not many paragraphs have escaped rewriting. UML (Unified Modeling Language) is also extensively employed in this edition. Numerous new review questions have been added. In covering the new topics and expanding the existing ones, new examples, figures, and tables were also specifically created for the third edition.
xxxvii
xxxviii
PREFACE
About This Book This book provides extensive coverage of the Java programming language and its core Application Programming Interfaces (APIs), with particular emphasis on its syntax and usage. The book is primarily intended for professionals who want to prepare for the SCJP 1.6 exam, but it is readily accessible to any programmer who wants to master the language. For both purposes, it provides in-depth coverage of essential features of the language and its core APIs. There is a great and increasing demand for certified Java programmers. Sun Microsystems has defined the SCJP 1.6 exam as one that professionals can take to validate their skills. The certification provides the IT industry with a standard to use for hiring such professionals, and allows the professionals to turn their Java skills into credentials that are important for career advancement. The book provides extensive coverage of all the objectives defined for the exam by Sun. But the exam objectives are selective and do not include many of the essential features of Java. This book covers many additional topics that every Java programmer should master in order to be proficient. In this regard, the book is a comprehensive primer for learning the Java programming language. After mastering the language by working through this book, the reader can confidently sit for the exam. This book is not a complete reference for Java, as it does not attempt to list every member of every class from the Java Development Kit (JDK) API documentation. The purpose is not to document the JDK APIs. This book does not teach programming techniques. The emphasis is on the Java programming language features, their syntax and correct usage through code examples. The book assumes little background in programming. We believe the exam is accessible to any programmer who works through the book. A Java programmer can easily skip over material that is well understood and concentrate on parts that need reinforcing, whereas a programmer new to Java will find the concepts explained from basic principles. Each topic is explained and discussed thoroughly with examples, and backed by review questions and exercises to reinforce the concepts. The book is not biased toward any particular platform, but provides platform-specific details where necessary.
Using the Book The reader can choose a linear or a non-linear route through the book, depending on her programming background. Non-Java programmers wishing to migrate to Java can read Chapter 1, which provides a short introduction to object-oriented programming concepts, and the procedure for compiling and running Java appli-
PREFACE
xxxix
cations. For those preparing for the SCJP 1.6 exam, the book has a separate appendix providing all the pertinent information on taking the exam. The table of contents; listings of tables, examples, and figures; and a comprehensive index facilitate locating topics discussed in the book. In particular, we draw attention to the following features: Exam Objectives 0.1 Exam objectives are stated clearly at the start of every chapter. 0.2 The number in front of the objective identfies the objective as defined by Sun. 0.3 The objectives are organized into major sections, detailing the curriculum for the exam. 0.4 The exam objectives are reproduced in Appendix B where, for each section of the syllabus, references are included to point the reader to relevant topics for the exam.
Supplementary Objectives • Supplementary objectives cover topics that are not on the exam, but which we believe are important for mastering the topics that are on the exam. • Any supplementary objectives are listed as bullets at the beginning of a chapter.
Review Questions Review questions are provided after every major topic, in order to test and reinforce the material. These review questions reflect the kinds of questions that can be asked on the actual exam. Annotated answers to the review questions are provided in a separate appendix. Example 0.1
Example Source Code
We encourage experimenting with the code examples in order to reinforce the material from the book. These can be downloaded from the book Web site (see p. xli). Java code is written in a mono-spaced font. Lines of code in the examples or in code snippets are referenced in the text by a number, which is specified by using a single-line comment in the code. For example, in the following code snippet, the call to the method doSomethingInteresting() hopefully does something interesting at (1).
xl
PREFACE // ... doSomethingInteresting(); // ...
// (1)
Names of classes and interfaces start with an uppercase letter. Names of packages, variables, and methods start with a lowercase letter. Constants are all in uppercase letters. Interface names begin with the prefix 'I'. Coding conventions are followed, except when we have had to deviate in the interest of space or clarity.
Chapter Summary Each chapter concludes with a summary of the topics, pointing out the major concepts discussed in the chapter.
Programming Exercises Programming exercises at the end of each chapter provide the opportunity to put concepts into practice. Solutions to the programming exercises are provided in a separate appendix.
Mock Exam A complete mock exam is provided in a separate appendix, which the reader can try when she is ready.
Java SE API Documentation A vertical gray bar is used to highlight methods and fields found in the classes of the core Java APIs. Any explanation following the API information is also similarly highlighted. In order to obtain optimal benefit from using this book in preparing for the SCJP 1.6 exam, we strongly recommend installing the latest version (1.6 or newer) of the JDK and its accompanying API documentation. The book focuses solely on Java, and does not acknowledge previous versions.
Java Platform Upgrade Exam For those who have taken the Sun Certified Programmer for Java Platform 1.5 Exam, and would like to prepare for the Sun Certified Programmer for Java Platform 1.6 Upgrade Exam, we have provided an appendix with details of the upgrade exam. The appendix contains the upgrade exam objectives, and for each section of the syllabus, references are included to point the reader to topics essential for the upgrade exam.
PREFACE
xli
Book Web Site This book is backed by a Web site providing auxiliary material: http://www.ii.uib.no/~khalid/pgjc3e/
The contents of the Web site include the following: • source code for all the examples and programming exercises in the book • mock exam engine • errata • links to miscellaneous Java resources (certification, discussion groups, tools, etc.) Information about the Java Standard Edition and its documentation can be found at the following Web site: http://java.sun.com/javase/
The current authoritative technical reference for the Java programming language, The Java Language Specification, Third Edition (also published by Addison-Wesley), can be found at this Web site: http://java.sun.com/docs/books/jls/
Request for Feedback Considerable effort has been made to ensure the accuracy of the contents of this book. Several Java professionals have proofread the manuscript. All code examples (including code fragments) have been compiled and tested on various platforms. In the final analysis, any errors remaining are the sole responsibility of the authors. Any questions, comments, suggestions, and corrections are welcome. Let us know whether the book was helpful or detrimental for your purposes. Any feedback is valuable. The authors can be reached by the following e-mail alias: [email protected]
About the Authors Khalid A. Mughal Khalid A. Mughal is an Associate Professor at the Department of Informatics at the University of Bergen, Norway. Professor Mughal is responsible for designing and implementing various courses, which use Java, at the Department of Informatics. Over the years, he has taught Programming Languages (Java, C/C++, Pascal), Software Engineering (Object-Oriented System Development), Data-
xlii
PREFACE
bases (Data Modeling and Database Management Systems), and Compiler Techniques. He has also given numerous courses and seminars at various levels in object-oriented programming and system development, using Java and Javarelated technology, both at the University and for the IT industry. He is the principal author of the book, responsible for writing the material covering the Java topics. Professor Mughal is also the principal author of an introductory Norwegian textbook on programming in Java (Java som første programmeringsspråk/Java as First Programming Language, Third Edition, Cappelen Akademisk Forlag, ISBN-10: 82-0224554-0, 2006), which he co-authored with Torill Hamre and Rolf W. Rasmussen. Together they have also published another textbook for a 2-semester course in programming (Java Actually: A Comprehensive Primer in Programming, Cengage Learning, ISBN-10: 1844809331, 2008). His current work involves applying Object Technology in the development of content management systems for publication on the Web, and security issues related to web applications. For the past seven years he has been responsible for developing and running web-based programming courses in Java, which are offered to offcampus students. He is also a member of the Association for Computing Machinery (ACM).
Rolf W. Rasmussen Rolf W. Rasmussen is the System Development Manager at vizrt, a company that develops solutions for the TV broadcast industry, including real-time 3D graphic renderers, and content and control systems. Rasmussen works mainly on control and automation systems, video processing, typography, and real-time visualization. He has worked on clean room implementations of the Java class libraries in the past, and is a contributor to the Free Software Foundation. Over the years, Rasmussen has worked both academically and professionally with numerous programming languages, including Java. He is primarily responsible for developing the review questions and answers, the programming exercises and their solutions, the mock exam, and all the practical aspects related to taking the SCJP exam presented in this book. As mentioned above, he is also a co-author of two introductory textbooks on programming in Java.
Acknowledgments (First Edition) A small application for drawing simple shapes is used in the book to illustrate various aspects of GUI building. The idea for this application, as far as we know,
PREFACE
xliii
first appeared in Appendix D of Data Structures and Problem Solving Using Java (M.A. Weiss, Addison-Wesley, 1998). At Addison-Wesley-Longman (AWL), we would like to thank Emma Mitchell for the support and the guidance she provided us right from the start of this project, Martin Klopstock at AWL for accommodating the non-standard procedure involved in getting the book to the printing press, Clive Birks at CRB Associates for providing the professional look to the contents of this book, and finally, Sally Mortimore at AWL for seeing us over the finishing line. The efforts of other professionals behind the scenes at AWL are also acknowledged. Many reviewers have been involved during the course of writing this book. First of all, we would like to thank the five anonymous reviewers commissioned by AWL to review the initial draft. Their input was useful in the subsequent writing of this book. Several people have provided us with feedback on different parts of the material at various stages: Jon Christian Lønningdal, Tord Kålsrud, Kjetil Iversen, Roy Oma, and Arne Løkketangen. Their help is hereby sincerely acknowledged. We are also very grateful to Laurence Vanhelsuwé, Kris Laporte, Anita Jacob, and Torill Hamre for taking on the daunting task of reviewing the final draft, and providing us with extensive feedback at such short notice. We would like to thank Marit Mughal for reading the manuscript with the trained eye of a veteran English schoolteacher. We now understand why family members are invariably mentioned in a preface. Without our families’ love, support, and understanding this book would have remained a virtual commodity. Khalid would like to thank Marit, Nina, and Laila for their love, and for being his pillars of support, during the writing of this book. Thanks also to the folks in Birmingham for cheering us on. Rolf would like to thank Liv, Rolf V., Knut, and Elisabeth for enduring the strange working hours producing this book has entailed. A special thanks to Marit for providing us with scrumptious dinners for consumption at the midnight hour.
Acknowledgments (Second Edition) Feedback from many readers helped us to improve the first edition. We would like to thank the following readers for their input in this effort: Michael F. Adolf, Tony Alicea, Kåre Auglænd, Jorge L. Barroso, Andre Beland, Darren Bruning, Paul Campbell, Roger Chang, Joanna Chappel, Laurian M Chirica, Arkadi Choufrine, Barry Colston, John Cotter, Frédéric Demers, Arthur De Souza, djc, William Ekiel, Darryl Failla, John Finlay, Christopher R. Gardner, Marco Garcia, Peter Gieser, George, Paul Graf, Shyamsundar Gururaj, Ray Ho, Leonardo Holanda, Zhu Hongjun, Kara Van Horn, Peter Horst, Nain Hwu, Kent Johnson, Samir Kanparia, Oleksiy Karpenko, Jeffrey Kenyon, Young Jin Kim, Kenneth
xliv
PREFACE
Kisser, Billy Kutulas, Yi-Ming Lai, Robert M. Languedoc, Steve Lasley, Winser Lo, Naga Madipalli, Craig Main, Avinash Mandsaurwale, Thomas Mathai, S. Mehra, Yuan Meng, Simon Miller, William Moore, Anders Morch, George A. Murakami, Sandy Nemecek, Chun Pan, Abigail García Patiño, Anil Philip, Alfred Raouf, Peter Rorden, Christian Seifert, Gurpreet Singh, Christopher Stanwood, Swaminathan Subramanian, Siva Sundaram, Manju Swamy, John Sweeney, Harmon Taylor, Andrew Tolopko, Ravi Verma, Per J. Walstrøm, Chun Wang, James Ward, Winky, Chun Wang, Jimmy Yang, Jennie Yip, Yanqu Zhou, and Yingting Zhou. At the UK office of Addison-Wesley/Pearson Education, we would like to thank our former editor Simon Plumtree for his unceasing support and patience while we slogged on with the second edition. We would also like to acknowledge the help and support of the following professionals, past and present, at the London office: Alison Birtwell, Sally Carter, Karen Sellwood and Katherin Ekstrom. A special thanks to Karen Mosman (who has since moved on to another job) for her encouragement and advice. During the last lap of getting the book to the printing press, we were in the capable hands of Ann Sellers at the US office of Addison-Wesley/Pearson Education. We would like to acknowledge her efforts and that of other professionals—in particular, Greg Doench, Jacquelyn Doucette, Amy Fleischer, Michael Mullen, and Dianne Russell—who helped to get this book through the door and on to the bookshelf. Thanks also to Mike Hendrickson for always lending an ear when we met at the OOPSLA conferences, and pointing us in the right direction with our book plans. We would like to thank the folks at Whizlabs Software for their collaboration in producing the contents for the CD accompanying this book. Those guys certainly know the business of developing exam simulators for certification in Java technology. We were fortunate in having two Java gurus—Laurence Vanhelsuwé and Marcus Green—to do the technical review of the second edition. As he did for the first edition, Laurence came through and provided us with invaluable feedback, from the minutiae of writing technical books to many technical issues relating to the Java programming language. Marcus put the manuscript through his severe certification scrutiny regarding the specifics of the SCJP exam. We are sorry to have upset their plans for Easter holidays, and hasten to thank them most profusely for taking on the task. We cannot thank enough our own in-house, private copy-editor: Marit Seljeflot Mughal. She diligently and relentlessly read numerous drafts of the manuscript, usually at very short notice. Marit claims that if she understood what we had written, then a computer-literate person should have no problem whatsoever. This claim remains to be substantiated. If any commas are not used correctly, then it is entirely our fault, in spite of being repeatedly shown how to use them. We are also indebted to many Java-enabled individuals for providing us valuable feedback on parts of the manuscript for the second edition. This includes Pradeep Chopra, Seema R., and Gaurav Kohli at Whizlabs Software. Unfortunately for us,
PREFACE
xlv
they only had time to read part of the manuscript. Thanks also to Torill Hamre at the Nansen Environmental and Remote Sensing Center, Bergen, for her useful comments and suggestions. We also thank the following Master students at the Department of Informatics, University of Bergen, for providing useful feedback: Mikal Carlsen, Yngve Espelid, Yngve A. Aas, Sigmund Nysæter, Torkel Holm, and Eskil Saatvedt. Family support saw us through this writing project as well. Our families have put up with our odd and long working hours, endured our preoccupation and our absence at the dining table. Khalid would like to acknowledge the love and support of his wife, Marit, and daughters, Nina and Laila, while working on this book. Rolf would like to thank Liv, Rolf V., Knut, and Elisabeth for their love, patience and support.
Acknowledgments (Third Edition) Many readers have sent us e-mails testifying that the Programmer’s Guide contributed toward their success on the exam. That is the best advertisement we can hope for. The feedback we have received since the publication of the second edition has had an impact on improving the third edition. In particular, we would like to thank the following diligent readers for their contributions: Bret ABMac, Einar Andresen, Brian Bradshaw, Nicola Cammillini, Juan Carlos Castro, Sweta Doshi, David Featherstone, Danish Halim, Niels Harremoës, John Holcroft, Leong Jern-Kuan, Rajesh Kesarwani, Ken Kisser, Shampa Kumar, Tony LaPaso, Kaydell Leavitt, Luba Leyzerenok, Adam Lorentzon, Chuck Meier, Philip Mitchell, Sigmund Nysæter, Pat Owens, Sanket Reddy, Raul Saavedra, Oliver Schoettler, Wayne Schroeter, Mark Sullivan, Myoung Son, Bob Souther, Anthony Tang, Frederik Uyttersprot. Erik Ernst was kind enough to review the chapter on Java generics, for which we are very grateful. The generics chapter was also reviewed by Erik Andreas Brandstadmoen and Kristian Berg. Our sincere thanks to all of you. The pages of feedback we received helped to clarify many subtleties, and made us realize that some dark corners of Java generics are best avoided by mere mortals. Selected chapters for the third edition were also vetted by the following Java developers in the Bergen area: Olve Hansen, David J.M. Karlsen and Lars Søraas. Many thanks for taking time out from your busy schedule to provide us with your feedback. Our thanks also to Helge W. Johnsen and Amund Trovåg for feedback on review questions regarding new features in Java 1.5. Our award for Reviewer Par Excellence goes to Jennie Yip. The meticulous notes she provided for the ten chapters of the second edition have had a profound effect on shaping the third edition. Any chance that the feat can be repeated with the third edition? Please name your price.
xlvi
PREFACE
This time around we were again fortunate enough to have Marcus Green as our technical reviewer. We have heeded his feedback that has kept us, we hope, on the straight and narrow as far as the exam is concerned, and curbed our enthusiasm for including every Java topic that we fancied. Our sincere thanks for the review you provided us. At Pearson, we would like to thank Greg Doench and Michelle Housley for managing the publication of this edition. We are also grateful to the people behind the scenes at Pearson who helped get the book to the printing press. Khalid would like to thank the Computer Science Department at Cornell University, where he spent a significant part of his sabbatical (Fall 2007/Spring 2008) working on the third edition. A better place for such an endeavour would be hard to come by. We cannot thank enough Marit Seljeflot Mughal who has been our personal quality controller, acting as an amalgamated draft reader, copy editor, and proofreader. What she sanctioned we could confidently allow to be seen by the light of day, saving us many embarrassing mistakes, both technical and non-technical. We don’t know if it is for us or for the love of Java that you scrutinize the endless drafts that we lay in your path. Any mistakes or errors remaining are an oversight on our part. Rest assured that every possible effort has been made to get the facts straight. Without family support this edition would still be wishful thinking. Khalid would like to thank Marit, Laila, Nina and Kenneth for their love, support and understanding—particularly, while working on this book. —Khalid A. Mughal Rolf W. Rasmussen September 2008 Ithaca, New York, USA Bergen, Norway
Basics of Java Programming
1
Supplementary Objectives • Introduce the basic terminology and concepts in object-oriented programming: classes, objects, references, fields, methods, members, inheritance, aggregation. • Identify the essential elements of a Java program. • Learn how to compile and run a Java program.
1
2
CHAPTER 1: BASICS OF JAVA PROGRAMMING
1.1 Introduction Before embarking on the road to Java programmer certification, it is important to understand the basic terminology and concepts in object-oriented programming (OOP). In this chapter, the emphasis is on providing an introduction rather than exhaustive coverage. In-depth coverage of the concepts follows in subsequent chapters of the book. Java supports the writing of many different kinds of executables: applications, applets, and servlets. The basic elements of a Java application are introduced in this chapter. The old adage that practice makes perfect is certainly true when learning a programming language. To encourage programming on the computer, the mechanics of compiling and running a Java application are outlined.
1.2 Classes One of the fundamental ways in which we handle complexity is in abstractions. An abstraction denotes the essential properties and behaviors of an object that differentiate it from other objects. The essence of OOP is modelling abstractions, using classes and objects. The hard part in this endeavor is finding the right abstraction. A class denotes a category of objects, and acts as a blueprint for creating such objects. A class models an abstraction by defining the properties and behaviors for the objects representing the abstraction. An object exhibits the properties and behaviors defined by its class. The properties of an object of a class are also called attributes, and are defined by fields in Java. A field in a class is a variable which can store a value that represents a particular property of an object. The behaviors of an object of a class are also known as operations, and are defined using methods in Java. Fields and methods in a class declaration are collectively called members. An important distinction is made between the contract and the implementation that a class provides for its objects. The contract defines what services, and the implementation defines how these services are provided by the class. Clients (i.e., other objects) only need to know the contract of an object, and not its implementation, in order to avail themselves of the object’s services. As an example, we will implement different versions of a class that models the abstraction of a stack that can push and pop characters. The stack will use an array of characters to store the characters, and a field to indicate the top element in the stack. Using Unified Modeling Language (UML) notation, a class called CharStack is graphically depicted in Figure 1.1, which models the abstraction. Both fields and method names are shown in Figure 1.1a.
3
1.2: CLASSES Figure 1.1
UML Notation for Classes Class Name Fields
CharStack stackArray
CharStack
topOfStack
Methods
push() pop() peek() isEmpty() isFull()
(b) Abbreviated Form
(a) Expanded Form
Declaring Members: Fields and Methods Example 1.1 shows the declaration of the class CharStack depicted in Figure 1.1. Its intention is to illustrate the salient features of a class declaration in Java, and not the effective implementation of stacks. A class declaration consists of a series of member declarations. In the case of the class CharStack, it has two fields declared at (1): • stackArray, which is an array to hold the elements of the stack (in this case, characters) • topOfStack, which denotes the top element of the stack (i.e., the index of the last character stored in the array) The class CharStack has five methods, declared at (3), that implement the essential operations on a stack: • push() pushes a character on to the stack • pop() removes and returns the top element of the stack • peek() returns the top element of the stack for inspection • isEmpty() determines whether the stack is empty • isFull() determines whether the stack is full The class declaration also has a method-like declaration with the same name as the class, (2). Such declarations are called constructors. As we shall see, a constructor is executed when an object is created from the class. However, the implementation details in the example are not important for the present discussion. Example 1.1
Basic Elements of a Class Declaration //Source Filename: CharStack.java public class CharStack { // Class name // Class Declarations:
4
CHAPTER 1: BASICS OF JAVA PROGRAMMING // Fields: private char[] stackArray; private int topOfStack;
(1) // The array implementing the stack. // The top of the stack.
// Constructor: public CharStack(int capacity) { stackArray = new char[capacity]; topOfStack = -1; } // Methods: public void push(char element) public char pop() public char peek() public boolean isEmpty() public boolean isFull()
1.3 Objects Class Instantiation, Reference Values, and References The process of creating objects from a class is called instantiation. An object is an instance of a class. The object is constructed using the class as a blueprint and is a concrete instance of the abstraction that the class represents. An object must be created before it can be used in a program. A reference value is returned when an object is created. A reference value denotes a particular object. An object reference (or simply reference) is a variable that can store a reference value. A reference thus provides a handle to an object, as it can indirectly denote an object whose reference value it holds. In Java, an object can only be manipulated via its reference value, or equivalently by a reference that holds its reference value. The process of creating objects usually involves the following steps: 1.
Declaration of a variable to store the reference value of an object. This involves declaring a reference variable of the appropriate class to store the reference value of the object. // Declaration of two reference variables that will refer to // two distinct objects, namely two stacks of characters, respectively. CharStack stack1, stack2;
2.
Creating an object. This involves using the new operator in conjunction with a call to a constructor, to create an instance of the class. // Create two distinct stacks of chars.
5
1.3: OBJECTS stack1 = new CharStack(10); // Stack length: 10 chars stack2 = new CharStack(5); // Stack length: 5 chars
The new operator creates an instance of the CharStack class and returns the reference value of this instance. The reference value can be assigned to a reference variable of the appropriate class. The reference variable can then be used to manipulate the object whose reference value is stored in the reference variable. Each object has its own copy of the fields declared in the class declaration. The two stacks, referenced by stack1 and stack2, will have their own stackArray and topOfStack fields. The purpose of the constructor call on the right side of the new operator is to initialize the newly created object. In this particular case, for each new CharStack instance created using the new operator, the constructor creates an array of characters. The length of this array is given by the value of the argument to the constructor. The constructor also initializes the topOfStack field. The declaration of a reference and the instantiation of the class can also be combined, as in the following declaration statement: CharStack stack1 = new CharStack(10), stack2 = new CharStack(5);
Figure 1.2 shows the UML notation for objects. The graphical representation of an object is very similar to that of a class. Figure 1.2 shows the canonical notation, where the name of the reference variable denoting the object is prefixed to the class name with a colon (':'). If the name of the reference variable is omitted, as in Figure 1.2b, this denotes an anonymous object. Since objects in Java do not have names, but are denoted by references, a more elaborate notation is shown in Figure 1.2c, where objects representing references of the CharStack class explicitly refer to CharStack objects. In most cases, the more compact notation will suffice. Figure 1.2
UML Notation for Objects
stack1:CharStack
stack2:CharStack
(a) Standard Notation for Objects :CharStack (b) Anonymous Object
stack1:Ref(CharStack)
:CharStack
stack2:Ref(CharStack)
:CharStack
(c) Explicit References for Java Objects
6
CHAPTER 1: BASICS OF JAVA PROGRAMMING
Object Aliases An object can be referred by several references, meaning that they store the reference value of the same object. Such references are called aliases. The object can be manipulated via any one of its aliases, as each one refers to the same object. // Create two distinct stacks of chars. CharStack stackA = new CharStack(12); // Stack length: 12 chars CharStack stackB = new CharStack(6); // Stack length: 6 chars stackB = stackA; // (1) aliases after assignment // The stack previously referenced by stackB can now be garbage collected.
Two stacks are created in the code above. Before the assignment at (1), the situation is as depicted in Figure 1.3a. After the assignment at (1), the reference variables stackA and stackB will denote the same stack, as depicted in Figure 1.3b. The reference value in stackA is assigned to stackB. The reference variables stackA and stackB are aliases after the assignment, as they refer to the same object. What happens to the stack object that was denoted by the reference variable stackB before the assignment? When objects are no longer in use, their memory is, if necessary, reclaimed and reallocated for other objects. This is called automatic garbage collection. Garbage collection in Java is taken care of by the runtime system. Figure 1.3
Aliases stackA:Ref(CharStack)
:CharStack
stackB:Ref(CharStack)
:CharStack (a) Before
stackA:Ref(CharStack)
:CharStack
stackB:Ref(CharStack)
:CharStack
(b) After Assignment
1.4 Instance Members Each object created will have its own copies of the fields defined in its class. The fields of an object are called instance variables. The values of the instance variables in an object comprise its state. Two distinct objects can have the same state, if their instance variables have the same values. The methods of an object define its behavior. These methods are called instance methods. It is important to note that these methods pertain to each object of the class. This should not be confused with the
7
1.5: STATIC MEMBERS
implementation of the methods, which is shared by all instances of the class. Instance variables and instance methods, which belong to objects, are collectively called instance members, to distinguish them from static members, which belong to the class only. Static members are discussed in Section 1.5.
Invoking Methods Objects communicate by message passing. This means that an object can be made to exhibit a particular behavior by sending the appropriate message to the object. In Java, this is done by calling a method on the object using the binary infix dot ('.') operator. A method call spells out the complete message: the object that is the receiver of the message, the method to be invoked, and the arguments to the method, if any. The method invoked on the receiver can also send information back to the sender, via a single return value. The method called must be one that is defined for the object, otherwise the compiler reports an error. CharStack stack = new CharStack(5); // Create a stack stack.push('J'); // (1) Character 'J' pushed char c = stack.pop(); // (2) One character popped and returned: 'J' stack.printStackElements(); // (3) Compile-time error: No such method in CharStack
The sample code above invokes methods on the object denoted by the reference variable stack. The method call at (1) pushes one character on the stack, and the method call at (2) pops one character off the stack. Both push() and pop() methods are defined in the class CharStack. The push() method does not return any value, but the pop() method returns the character popped. Trying to invoke a method named printStackElements on the stack results in a compile-time error, as no such method is defined in the class CharStack. The dot ('.') notation can also be used with a reference to access the fields of an object. The use of the dot notation is governed by the accessibility of the member. The fields in the class CharStack have private accessibility, indicating that they are not accessible from outside the class: stack.topOfStack++;
// Compile-time error: topOfStack is a private field.
1.5 Static Members In some cases, certain members should only belong to the class, and not be part of any object created from the class. An example of such a situation is when a class wants to keep track of how many objects of the class have been created. Defining a counter as an instance variable in the class declaration for tracking the number of objects created does not solve the problem. Each object created will have its own counter field. Which counter should then be updated? The solution is to declare the counter field as being static. Such a field is called a static variable. It belongs to the class, and not to any object of the class. A static variable is initialized when the class is loaded at runtime. Similarly, a class can have static methods that belong to the
8
CHAPTER 1: BASICS OF JAVA PROGRAMMING
class, and not to any specific objects of the class. Static variables and static methods are collectively known as static members, and are declared with the keyword static. Figure 1.4 shows the class diagram for the class CharStack. It has been augmented by two static members that are shown underlined. The augmented definition of the CharStack class is given in Example 1.2. The field counter is a static variable declared at (1). It will be allocated and initialized to the default value 0 when the class is loaded. Each time an object of the CharStack class is created, the constructor at (2) is executed. The constructor explicitly increments the counter in the class. The method getInstanceCount() at (3) is a static method belonging to the class. It returns the counter value when called. Figure 1.4
Class Diagram Showing Static Members of a Class CharStack stackArray topOfStack counter push() pop() peek() ... getInstanceCount()
Example 1.2
Static Members in Class Declaration //Filename CharStack.java public class CharStack { // Instance variables: private char[] stackArray; private int topOfStack;
// The array implementing the stack. // The top of the stack.
// Static variable private static int counter;
// (1)
// Constructor now increments the counter for each object created. public CharStack(int capacity) { // (2) stackArray = new char[capacity]; topOfStack = -1; counter++; } // Instance methods: public void push(char element) public char pop() public char peek() public boolean isEmpty() public boolean isFull()
1.5: STATIC MEMBERS // Static method public static int getInstanceCount() { return counter; }
(3)
}
Figure 1.5 shows the classification of the members in the class CharStack using the terminology we have introduced so far. Table 1.1 at the end of this section, provides a summary of the terminology used in defining members of a class. Clients can access static members in the class by using the class name. The following code invokes the getInstanceCount() method in the class CharStack: int count = CharStack.getInstanceCount(); // Class name to invoke static method Figure 1.5
Members of a Class Class Name CharStack Instance members belong to objects
Attributes
Static members belong to the class
Instance variables
Static variables
stackArray topOfStack
counter
Fields
Members
Behaviour
Instance methods
Static methods
push() pop() peek() isEmpty() isFull()
getInstanceCount()
Objects
Methods
Class
Static members can also be accessed via object references, but this is considered bad style: CharStack stack1; int count1 = stack1.getInstanceCount();
// Reference invokes static method
Static members in a class can be accessed both by the class name and via object references, but instance members can only be accessed by object references.
10
CHAPTER 1: BASICS OF JAVA PROGRAMMING Table 1.1 Terminology for Class Members
Instance Members
These are instance variables and instance methods of an object. They can only be accessed or invoked through an object reference.
Instance Variable
A field that is allocated when the class is instantiated, i.e., when an object of the class is created. Also called non-static field.
Instance Method
A method that belongs to an instance of the class. Objects of the same class share its implementation.
Static Members
These are static variables and static methods of a class. They can be accessed or invoked either by using the class name or through an object reference.
Static Variable
A field that is allocated when the class is loaded. It belongs to the class and not to any specific object of the class. Also called static field or class variable.
Static Method
A method which belongs to the class and not to any object of the class. Also called class method.
1.6 Inheritance There are two fundamental mechanisms for building new classes from existing ones: inheritance and aggregation. It makes sense to inherit from an existing class Vehicle to define a class Car, since a car is a vehicle. The class Vehicle has several parts; therefore, it makes sense to define a composite object of the class Vehicle that has constituent objects of such classes as Motor, Axle, and GearBox, which make up a vehicle. Inheritance is illustrated by an example that implements a stack of characters that can print its elements on the terminal. This new stack has all the properties and behaviors of the CharStack class, but it also has the additional capability of printing its elements. Given that this printable stack is a stack of characters, it can be derived from the CharStack class. This relationship is shown in Figure 1.6. The class PrintableCharStack is called the subclass, and the class CharStack is called the superclass. The CharStack class is a generalization for all stacks of characters, whereas the Figure 1.6
Class Diagram Depicting Inheritance Relationship Superclass
CharStack
Generalization
Subclass
PrintableCharStack
Specialization
11
1.6: INHERITANCE
class PrintableCharStack is a specialization of stacks of characters that can also print their elements. In Java, deriving a new class from an existing class requires the use of the extends clause in the subclass declaration. A subclass can extend only one superclass. The subclass can inherit members of the superclass. The following code fragment implements the PrintableCharStack class: class PrintableCharStack extends CharStack { // Instance method public void printStackElements() { // ... implementation of the method... }
// (1) // (2)
// The constructor calls the constructor of the superclass explicitly. public PrintableCharStack(int capacity) { super(capacity); } // (3) }
The PrintableCharStack class extends the CharStack class at (1). Implementing the printStackElements() method in the PrintableCharStack class requires access to the field stackArray from the superclass CharStack. However, this field is private and therefore not accessible in the subclass. The subclass can access these fields if the accessibility of the fields is changed to protected in the CharStack class. Example 1.3 uses a version of the class CharStack, which has been modified accordingly. Implementation of the printStackElements() method is shown at (2). The constructor of the PrintableCharStack class at (3) calls the constructor of the superclass CharStack in order to initialize the stack properly. Example 1.3
Defining a Subclass // Source Filename: CharStack.java public class CharStack { // Instance variables protected char[] stackArray; // The array that implements the stack. protected int topOfStack; // The top of the stack. // The rest of the definition is the same as in Example 1.2. }
//Filename: PrintableCharStack.java public class PrintableCharStack extends CharStack {
// (1)
// Instance method public void printStackElements() { // (2) for (int i = 0; i <= topOfStack; i++) System.out.print(stackArray[i]); // print each char on terminal System.out.println(); } // Constructor calls the constructor of the superclass explicitly. PrintableCharStack(int capacity) { super(capacity); } // (3) }
12
CHAPTER 1: BASICS OF JAVA PROGRAMMING
Objects of the PrintableCharStack class will respond just like the objects of the CharStack class, but they will also have the additional functionality defined in the subclass: PrintableCharStack pcStack = new PrintableCharStack(3); pcStack.push('H'); pcStack.push('i'); pcStack.push('!'); pcStack.printStackElements(); // Prints "Hi!" on the terminal
1.7 Aggregation When building new classes from existing classes using aggregation, a composite object is built from the constituent objects that are its parts. Java supports aggregation of objects by reference, since objects cannot contain other objects explicitly. The fields can only contain values of primitive data types or reference values to other objects. Each object of the CharStack class has a field to store the reference value of an array object that holds the characters. Each stack object also has a field of primitive data type int to store the index value that denotes the top of stack. This is reflected in the definition of the CharStack class, which contains an instance variable for each of these parts. In contrast to the constituent objects whose reference values are stored in fields, the values of primitive data types are themselves stored in the fields of the composite object. The aggregation relationship is depicted by the UML diagram in Figure 1.7, showing that each object of the CharStack class will have one array object of type char associated with it.
1.8 Tenets of Java • Code in Java must be encapsulated in classes. • There are two kinds of values in Java: there are objects that are instances of classes or arrays, and there are atomic values of primitive data types. • References denote objects and are used to manipulate objects. • Objects in Java cannot contain other objects; they can only contain references to other objects. • During execution, reclamation of objects that are no longer in use is managed by the runtime system.
Review Questions 1.1
Which statement about methods is true? Select the one correct answer. (a) (b) (c) (d) (e)
1.2
A method is an implementation of an abstraction. A method is an attribute defining the property of a particular abstraction. A method is a category of objects. A method is an operation defining the behavior for a particular abstraction. A method is a blueprint for making operations.
Which statement about objects is true? Select the one correct answer. (a) (b) (c) (d) (e)
1.3
An object is what classes are instantiated from. An object is an instance of a class. An object is a blueprint for creating concrete realization of abstractions. An object is a reference. An object is a variable.
Which is the first line of a constructor declaration in the following code? public class Counter { int current, step; public Counter(int startValue, int setCurrent(startValue); setStep(stepValue); } public int getCurrent() public void setCurrent(int value) public void setStep(int stepValue) }
Select the one correct answer. (a) (b) (c) (d) (e) 1.4
(1) (2) (3) (4) (5)
Given that Thing is a class, how many objects and how many reference variables are created by the following code? Thing item, stuff; item = new Thing(); Thing entity = new Thing();
Select the two correct answers. (a) (b) (c) (d) (e) (f) 1.5
One object is created. Two objects are created. Three objects are created. One reference variable is created. Two reference variables are created. Three reference variables are created.
Which statement about instance members is true? Select the one correct answer. (a) (b) (c) (d) (e)
1.6
An instance member is also called a static member. An instance member is always a field. An instance member is never a method. An instance member belongs to an instance, not to the class as a whole. An instance member always represents an operation.
How do objects communicate in Java? Select the one correct answer. (a) (b) (c) (d)
1.7
They communicate by modifying each other’s fields. They communicate by modifying the static variables of each other’s classes. They communicate by calling each other’s instance methods. They communicate by calling static methods of each other’s classes.
Given the following code, which statements are true? class A { int value1; } class B extends A { int value2; }
1.10: SAMPLE JAVA APPLICATION
15
Select the two correct answers. (a) (b) (c) (d) (e) (f)
Class A extends class B. Class B is the superclass of class A. Class A inherits from class B. Class B is a subclass of class A. Objects of class A have a field named value2. Objects of class B have a field named value1.
1.9 Java Programs A Java source file can contain more than one class declaration. Each source file name has the extension .java. The JDK enforces the rule that any class in the source file that has public accessibility must be declared in its own file; meaning that such a public class must be declared in a source file whose file name comprises the name of this public class with .java as its extension. The above rule implies that a source file can only contain at the most one public class. If the source file contains a public class, the file naming rule must be obeyed. Each class declaration in a source file is compiled into a separate class file, containing Java byte code. The name of this file comprises the name of the class with .class as its extension. The JDK provides tools for compiling and running programs, as explained in the next section. The classes in the Java standard library are already compiled, and the JDK tools know where to find them.
1.10 Sample Java Application An application is just a synonym for a program: source code that is compiled and directly executed. In order to create an application in Java, the program must have a class that defines a method named main, which is the starting point for the execution of any application.
Essential Elements of a Java Application Example 1.4 is an example of an application in which a client uses the CharStack class to reverse a string of characters. Example 1.4
An Application // Source Filename: CharStack.java public class CharStack { // Same as in Example 1.2. }
16
CHAPTER 1: BASICS OF JAVA PROGRAMMING //Filename: Client.java public class Client { public static void main(String[] args) { // Create a stack. CharStack stack = new CharStack(40); // Create a string to push on the stack: String str = "!no tis ot nuf era skcatS"; int length = str.length(); System.out.println("Original string: " + str); // Push the string char by char onto the stack: for (int i = 0; i < length; i++) { stack.push(str.charAt(i)); } System.out.print("Reversed string: "); // Pop and print each char from the stack: while (!stack.isEmpty()) { // Check if the stack is not empty. System.out.print(stack.pop()); } System.out.println(); } }
Output from the program: Original string: !no tis ot nuf era skcatS Reversed string: Stacks are fun to sit on!
The public class Client defines a method with the name main. To start the application, the main() method in this public class is invoked by the Java interpreter, also called the Java Virtual Machine (JVM). The method header of this main() method should be declared as shown in the following method stub: public static void main(String[] args) { /* Implementation */ }
// Method header
The main() method has public accessibility, i.e., it is accessible from any class. The keyword static means the method belongs to the class. The keyword void means the method does not return any value. The parameter list, (String[] args), is an array of strings that can be used to pass information to the main() method when the application is started.
Compiling and Running an Application Java source files can be compiled using the Java compiler tool javac, which is part of the JDK.
1.10: SAMPLE JAVA APPLICATION
17
The source file Client.java contains the declaration of the Client class. The source file can be compiled by giving the following command at the command line. (The character > is the command prompt.) >javac Client.java
This creates the class file Client.class containing the Java byte code for the Client class. The Client class uses the CharStack class, and if the file CharStack.class does not already exist, the compiler will also compile the source file CharStack.java. Compiled classes can be executed by the Java interpreter java, which is also part of the JDK. Example 1.4 can be run by giving the following command in the command line: >java Client
Note that only the name of the class is specified, resulting in the execution starting in the main() method of the specified class. The application in Example 1.4 terminates when the execution of the main() method is completed.
Review Questions 1.8
Which command from the JDK should be used to compile the following source code contained in a file named SmallProg.java? public class SmallProg { public static void main(String[] args) { System.out.println("Good luck!"); } }
Select the one correct answer. (a) java SmallProg (b) javac SmallProg (c) java SmallProg.java (d) javac SmallProg.java (e) java SmallProg main 1.9
Which command from the JDK should be used to execute the main() method of a class named SmallProg? Select the one correct answer. (a) java SmallProg (b) javac SmallProg (c) java SmallProg.java (d) java SmallProg.class (e) java SmallProg.main()
18
CHAPTER 1: BASICS OF JAVA PROGRAMMING
Chapter Summary The following information was included in this chapter: • basic concepts in OOP, and how they are supported in Java • essential elements of a Java application • compiling and running Java applications
Programming Exercise 1.1
Modify the program from Example 1.4 to use the PrintableCharStack class, rather than the CharStack class from Example 1.2. Utilize the printStackElements() method from the PrintableCharStack class. Is the new program behaviorwise any different from Example 1.4?
Language Fundamentals
2
Exam Objectives 1.3 Develop code that declares, initializes, and uses primitives, arrays, enums, and objects as static, instance, and local variables. Also, use legal identifiers for variable names. ❍
For arrays, see Section 3.6, p. 69.
❍
For enums, see Section 3.5, p. 54.
❍
For initializers, see Section 9.7, p. 406.
Supplementary Objectives • Be able to identify the basic elements of the Java programming language: keywords, identifiers, literals and primitive data types. • Understand the scope of variables. • Understand initializing variables with default values.
19
20
CHAPTER 2: LANGUAGE FUNDAMENTALS
2.1 Basic Language Elements Like any other programming language, the Java programming language is defined by grammar rules that specify how syntactically legal constructs can be formed using the language elements, and by a semantic definition that specifies the meaning of syntactically legal constructs.
Lexical Tokens The low-level language elements are called lexical tokens (or just tokens) and are the building blocks for more complex constructs. Identifiers, numbers, operators, and special characters are all examples of tokens that can be used to build high-level constructs like expressions, statements, methods, and classes.
Identifiers A name in a program is called an identifier. Identifiers can be used to denote classes, methods, variables, and labels. In Java, an identifier is composed of a sequence of characters, where each character can be either a letter or a digit. However, the first character in an identifier must be a letter. Since Java programs are written in the Unicode character set (see p. 23), the definitions of letter and digit are interpreted according to this character set. Note that connecting punctuation (such as underscore _) and any currency symbol (such as $, ¢, ¥, or £) are allowed as letters, but should be avoided in identifier names. Identifiers in Java are case sensitive, for example, price and Price are two different identifiers.
Examples of Illegal Identifiers 48chevy, all@hands, grand-sum
The name 48chevy is not a legal identifier as it starts with a digit. The character @ is not a legal character in an identifier. It is also not a legal operator, so that all@hands cannot be interpreted as a legal expression with two operands. The character - is also not a legal character in an identifier. However, it is a legal operator so grandsum could be interpreted as a legal expression with two operands.
Keywords Keywords are reserved words that are predefined in the language and cannot be used to denote other entities. All the keywords are in lowercase, and incorrect usage results in compilation errors.
21
2.1: BASIC LANGUAGE ELEMENTS
Keywords currently defined in the language are listed in Table 2.1. In addition, three identifiers are reserved as predefined literals in the language: the null reference, and the boolean literals true and false (see Table 2.2). Keywords currently reserved, but not in use, are listed in Table 2.3. A reserved word cannot be used as an identifier. The index contains references to relevant sections where currently used keywords are explained. Table 2.1
Table 2.2
Keywords in Java abstract
default
if
private
this
assert
do
implements
protected
throw
boolean
double
import
public
throws
break
else
instanceof
return
transient
byte
enum
int
short
try
case
extends
interface
static
void
catch
final
long
strictfp
volatile
char
finally
native
super
while
class
float
new
switch
continue
for
package
synchronized
Reserved Literals in Java null
Table 2.3
true
false
Reserved Keywords not Currently in Use const
goto
Literals A literal denotes a constant value, i.e., the value that a literal represents remains unchanged in the program. Literals represent numerical (integer or floating-point), character, boolean or string values. In addition, there is the literal null that represents the null reference. Table 2.4
Examples of Literals Integer
2000
0
-7
Floating-point
3.14
-3.14
.5
0.5
Character
'a'
'A'
'0'
':'
Boolean
true
false
String
"abba"
"3.14"
"for"
'-'
')'
"a piece of the action"
22
CHAPTER 2: LANGUAGE FUNDAMENTALS
Integer Literals Integer data types comprise the following primitive data types: int, long, byte, and short (see Section 2.2, p. 28). The default data type of an integer literal is always int, but it can be specified as long by appending the suffix L (or l) to the integer value. Without the suffix, the long literals 2000L and 0l will be interpreted as int literals. There is no direct way to specify a short or a byte literal. In addition to the decimal number system, integer literals can also be specified in octal (base 8) and hexadecimal (base 16) number systems. Octal and hexadecimal numbers are specified with a 0 and 0x (or 0X) prefix respectively. Examples of decimal, octal and hexadecimal literals are shown in Table 2.5. Note that the leading 0 (zero) digit is not the uppercase letter O. The hexadecimal digits from a to f can also be specified with the corresponding uppercase forms (A to F). Negative integers (e.g. -90) can be specified by prefixing the minus sign (-) to the magnitude of the integer regardless of number system (e.g., -0132 or -0X5A). Number systems and number representation are discussed in Appendix G. Java does not support literals in binary notation. Table 2.5
Examples of Decimal, Octal, and Hexadecimal Literals Decimal
Octal
Hexadecimal
8
010
0x8
10L
012L
0xaL
16
020
0x10
27
033
0x1B
90L
0132L
0x5aL
-90
-0132
-0x5A
2147483647 (i.e., 2 -1)
017777777777
0x7fffffff
-2147483648 (i.e., -231)
-020000000000
-0x80000000
1125899906842624L (i.e., 250)
040000000000000000L
0x4000000000000L
31
Floating-Point Literals Floating-point data types come in two flavors: float or double. The default data type of a floating-point literal is double, but it can be explicitly designated by appending the suffix D (or d) to the value. A floating-point literal can also be specified to be a float by appending the suffix F (or f). Floating-point literals can also be specified in scientific notation, where E (or e) stands for Exponent. For example, the double literal 194.9E-2 in scientific notation is interpreted as 194.9 × 10-2 (i.e., 1.949).
23
2.1: BASIC LANGUAGE ELEMENTS
Examples of double Literals 0.0 0.49 49.0 4.9E+1
0.0d .49 49. 4.9E+1D
0D .49D 49D 4.9e1d
4900e-2
.49E2
Examples of float Literals 0.0F 0.49F 49.0F 4.9E+1F
0f .49F 49.F 4900e-2f
49F .49E2F
Note that the decimal point and the exponent are optional and that at least one digit must be specified.
Boolean Literals The primitive data type boolean represents the truth-values true or false that are denoted by the reserved literals true or false, respectively.
Character Literals A character literal is quoted in single-quotes ('). All character literals have the primitive data type char. A character literal is represented according to the 16-bit Unicode character set, which subsumes the 8-bit ISO-Latin-1 and the 7-bit ASCII characters. In Table 2.6, note that digits (0 to 9), upper-case letters (A to Z), and lower-case letters (a to z) have contiguous Unicode values. A Unicode character can always be specified as a fourdigit hexadecimal number (i.e., 16 bits) with the prefix \u. Table 2.6
Examples of Character Literals
Character Literal
Character Literal using Unicode value
Character
' '
'\u0020'
Space
'0'
'\u0030'
0
'1'
'\u0031'
1
'9'
'\u0039'
9
'A'
'\u0041'
A
'B'
'\u0042'
B
'Z'
'\u005a'
Z
'a'
'\u0061'
a
'b'
'\u0062'
b
Continues
24
CHAPTER 2: LANGUAGE FUNDAMENTALS Table 2.6
Examples of Character Literals (Continued)
Character Literal
Character Literal using Unicode value
Character
'z'
'\u007a'
z
'Ñ'
'\u0084'
Ñ
'å'
'\u008c'
å
'ß'
'\u00a7'
ß
Escape Sequences Certain escape sequences define special characters, as shown in Table 2.7. These escape sequences can be single-quoted to define character literals. For example, the character literals '\t' and '\u0009' are equivalent. However, the character literals '\u000a' and '\u000d' should not be used to represent newline and carriage return in the source code. These values are interpreted as line-terminator characters by the compiler, and will cause compile time errors. You should use the escape sequences '\n' and '\r', respectively, for correct interpretation of these characters in the source code. Table 2.7
Escape Sequences Escape Sequence
Unicode Value
Character
\b
\u0008
Backspace (BS)
\t
\u0009
Horizontal tab (HT or TAB)
\n
\u000a
Linefeed (LF) a.k.a. Newline (NL)
\f
\u000c
Form feed (FF)
\r
\u000d
Carriage return (CR)
\'
\u0027
Apostrophe-quote, a.k.a. single quote
\"
\u0022
Quotation mark, a.k.a. double quote
\\
\u005c
Backslash
We can also use the escape sequence \ddd to specify a character literal as an octal value, where each digit d can be any octal digit (0–7), as shown in Table 2.8. The number of digits must be three or fewer, and the octal value cannot exceed \377, i.e., only the first 256 characters can be specified with this notation.
25
2.1: BASIC LANGUAGE ELEMENTS Table 2.8
Examples of Escape Sequence \ddd Escape Sequence \ddd
Character Literal
'\141'
'a'
'\46'
'&'
'\60'
'0'
String Literals A string literal is a sequence of characters which must be enclosed in double quotes and must occur on a single line. All string literals are objects of the class String (see Section 10.4, p. 439). Escape sequences as well as Unicode values can appear in string literals: "Here comes a tab.\t And here comes another one\u0009!" "What's on the menu?" "\"String literals are double-quoted.\"" "Left!\nRight!" "Don't split me up!"
(1) (2) (3) (4) (5)
In (1), the tab character is specified using the escape sequence and the Unicode value, respectively. In (2), the single apostrophe need not be escaped in strings, but it would be if specified as a character literal ('\''). In (3), the double quotes in the string must be escaped. In (4), we use the escape sequence \n to insert a newline. (5) generates a compile time error, as the string literal is split over several lines. Printing the strings from (1) to (4) will give the following result: Here comes a tab. And here comes another one What's on the menu? "String literals are double-quoted." Left! Right!
!
One should also use the escape sequences \n and \r, respectively, for correct interpretation of the characters \u000a (newline) and \u000d (form feed) in string literals.
White Spaces A white space is a sequence of spaces, tabs, form feeds, and line terminator characters in a Java source file. Line terminators can be newline, carriage return, or a carriage return-newline sequence. A Java program is a free-format sequence of characters that is tokenized by the compiler, i.e., broken into a stream of tokens for further analysis. Separators and operators help to distinguish tokens, but sometimes white space has to be inserted
26
CHAPTER 2: LANGUAGE FUNDAMENTALS
explicitly as a separator. For example, the identifier classRoom will be interpreted as a single token, unless white space is inserted to distinguish the keyword class from the identifier Room. White space aids not only in separating tokens, but also in formatting the program so that it is easy to read. The compiler ignores the white spaces once the tokens are identified.
Comments A program can be documented by inserting comments at relevant places in the source code. These comments are for documentation purposes only and are ignored by the compiler. Java provides three types of comments to document a program: • A single-line comment: // ... to the end of the line • A multiple-line comment: /* ... */ • A documentation (Javadoc) comment: /** ... */
Single-Line Comment All characters after the comment-start sequence // through to the end of the line constitute a single-line comment. // This comment ends at the end of this line. int age; // From comment-start sequence to the end of the line is a comment.
Multiple-Line Comment A multiple-line comment, as the name suggests, can span several lines. Such a comment starts with the sequence /* and ends with the sequence */. /* A comment on several lines. */
The comment-start sequences (//, /*, /**) are not treated differently from other characters when occurring within comments, and are thus ignored. This means that trying to nest multiple-line comments will result in a compile time error: /* Formula for alchemy. gold = wizard.makeGold(stone); /* But it only works on Sundays. */ */
The second occurrence of the comment-start sequence /* is ignored. The last occurrence of the sequence */ in the code is now unmatched, resulting in a syntax error.
REVIEW QUESTIONS
27
Documentation Comment A documentation comment is a special-purpose comment that is used by the javadoc tool to generate HTML documentation for the program. Documentation comments are usually placed in front of classes, interfaces, methods, and field definitions. Special tags can be used inside a documentation comment to provide more specific information. Such a comment starts with the sequence /** and ends with the sequence */: /** * This class implements a gizmo. * @author K.A.M. * @version 3.0 */
For details on the javadoc tool, see the tools documentation provided by the JDK.
Review Questions 2.1
Which of the following is not a legal identifier? Select the one correct answer. (a) a2z (b) ödipus (c) 52pickup (d) _class (e) ca$h
2.2
Which statement is true? Select the one correct answer. (a) (b) (c) (d) (e) (f)
2.3
new and delete are keywords in the Java language. try, catch, and thrown are keywords in the Java language. static, unsigned, and long are keywords in the Java language. exit, class, and while are keywords in the Java language. return, goto, and default are keywords in the Java language. for, while, and next are keywords in the Java language.
Which statement about the following comment is true? /* // */
Select the one correct answer. (a) The comment is not valid. The multiple-line comment (/* ... */) does not end correctly, since the comment-end sequence */ is a part of the single-line comment (// ...). (b) It is a completely valid comment. The // part is ignored by the compiler. (c) This combination of comments is illegal, and will result in a compile time error.
28
CHAPTER 2: LANGUAGE FUNDAMENTALS
2.2 Primitive Data Types Figure 2.1 gives an overview of the primitive data types in Java. Primitive data types in Java can be divided into three main categories: • integral types—represent signed integers (byte, short, int, long) and unsigned character values (char) • floating-point types (float, double)—represent fractional signed numbers • boolean type (boolean)—represents logical values Figure 2.1
Primitive Data Types in Java Primitive data types
Boolean type
Numeric types Integral types
Floating-point types
Character type
Integer types
char
byte short int long
boolean
float double
Primitive data values are not objects. Each primitive data type defines the range of values in the data type, and operations on these values are defined by special operators in the language (see Chapter 5). Each primitive data type also has a corresponding wrapper class that can be used to represent a primitive value as an object. Wrapper classes are discussed in Section 10.3, p. 428.
Integer Types Table 2.9
Range of Integer Values
Data Type
Width (bits)
Minimum value MIN_VALUE
Maximum value MAX_VALUE
byte
8
-27 (-128)
27-1 (+127)
short
16
-215 (-32768)
215-1 (+32767)
int
32
-231 (-2147483648)
231-1 (+2147483647)
long
64
-263 (-9223372036854775808L)
263-1 (+9223372036854775807L)
29
2.2: PRIMITIVE DATA TYPES
Integer data types are byte, short, int, and long (see Table 2.9). Their values are signed integers represented by 2’s complement (see Section G.4, p. 1010).
The char Type Table 2.10
Range of Character Values Data Type
Width (bits)
Minimum Unicode value
Maximum Unicode value
char
16
0x0 (\u0000)
0xffff (\uffff)
The data type char represents characters (see Table 2.10). Their values are unsigned integers that denote all the 65536 (216) characters in the 16-bit Unicode character set. This set includes letters, digits, and special characters. The first 128 characters of the Unicode set are the same as the 128 characters of the 7-bit ASCII character set, and the first 256 characters of the Unicode set correspond to the 256 characters of the 8-bit ISO Latin-1 character set. The integer types and the char type are collectively called integral types.
The Floating-Point Types Table 2.11
Range of Floating-Point Values
Data Type
Width (bits)
Minimum Positive Value MIN_VALUE
Maximum Positive Value MAX_VALUE
float
32
1.401298464324817E-45f
3.402823476638528860e+38f
double
64
4.94065645841246544e-324
1.79769313486231570e+308
Floating-point numbers are represented by the float and double data types. Floating-point numbers conform to the IEEE 754-1985 binary floating-point standard. Table 2.11 shows the range of values for positive floating-point numbers, but these apply equally to negative floating-point numbers with the '-' sign as a prefix. Zero can be either 0.0 or -0.0. Since the size for representation is a finite number of bits, certain floating-point numbers can only be represented as approximations. For example, the value of the expression (1.0/3.0) is represented as an approximation due to the finite number of bits used.
30
CHAPTER 2: LANGUAGE FUNDAMENTALS
The boolean Type Table 2.12
Boolean Values Data Type
Width
True Value Literal
False Value Literal
boolean
not applicable
true
false
The data type boolean represents the two logical values denoted by the literals true and false (see Table 2.12). Boolean values are produced by all relational (see Section 5.10, p. 190), conditional (see Section 5.13, p. 196) and boolean logical operators (see Section 5.12, p. 194), and are primarily used to govern the flow of control during program execution. Table 2.13 summarizes the pertinent facts about the primitive data types: their width or size, which indicates the number of the bits required to store a primitive value; their range of legal values, which is specified by the minimum and the maximum values permissible; and the name of the corresponding wrapper class (see Section 10.3, p. 428). Table 2.13
Which of the following do not denote a primitive data value in Java? Select the two correct answers. (a) "t" (b) 'k' (c) 50.5F (d) "hello" (e) false
2.5
Which of the following primitive data types are not integer types? Select the three correct answers. (a) boolean (b) byte (c) float (d) short (e) double
2.6
Which integral type in Java has the exact range from -2147483648 (-231) to 2147483647 (231-1), inclusive?
Select the one correct answer. (a) byte (b) short (c) int (d) long (e) char
2.3 Variable Declarations A variable stores a value of a particular type. A variable has a name, a type, and a value associated with it. In Java, variables can only store values of primitive data types and reference values of objects. Variables that store reference values of objects are called reference variables (or object references or simply references).
Declaring and Initializing Variables Variable declarations are used to specify the type and the name of variables. This implicitly determines their memory allocation and the values that can be stored in them. Examples of declaring variables that can store primitive values: char a, b, c; double area; boolean flag;
// a, b and c are character variables. // area is a floating-point variable. // flag is a boolean variable.
32
CHAPTER 2: LANGUAGE FUNDAMENTALS
The first declaration above is equivalent to the following three declarations: char a; char b; char c;
A declaration can also include an initialization expression to specify an appropriate initial value for the variable: int i = 10, j = 101; long big = 2147483648L;
// i is an int variable with initial value 10. // j is an int variable with initial value 101. // big is a long variable with specified initial value.
Reference Variables An reference variable can store the reference value of an object, and can be used to manipulate the object denoted by the reference value. A variable declaration that specifies a reference type (i.e., a class, an array, or an interface name) declares a reference variable. Analogous to the declaration of variables of primitive data types, the simplest form of reference variable declaration only specifies the name and the reference type. The declaration determines what objects can be referenced by a reference variable. Before we can use a reference variable to manipulate an object, it must be declared and initialized with the reference value of the object. Pizza yummyPizza; // Variable yummyPizza can reference objects of class Pizza. Hamburger bigOne, // Variable bigOne can reference objects of class Hamburger, smallOne; // and so can variable smallOne.
It is important to note that the declarations above do not create any objects of class Pizza or Hamburger. The above declarations only create variables that can store references of objects of the specified classes. A declaration can also include an initializer expression to create an object whose reference value can be assigned to the reference variable: Pizza yummyPizza = new Pizza("Hot&Spicy"); // Declaration with initializer.
The reference variable yummyPizza can reference objects of class Pizza. The keyword new, together with the constructor call Pizza("Hot&Spicy"), creates an object of the class Pizza. The reference value of this object is assigned to the variable yummyPizza. The newly created object of class Pizza can now be manipulated through the reference variable yummyPizza. Initializers for initializing fields in objects, and static variables in classes and interfaces are discussed in Section 9.7, p. 406. Reference variables for arrays are discussed in Section 3.6, p. 69.
33
2.4: INITIAL VALUES FOR VARIABLES
2.4 Initial Values for Variables Default Values for Fields Default values for fields of primitive data types and reference types are listed in Table 2.14. The value assigned depends on the type of the field. Table 2.14
Default Values Data Type
Default Value
boolean
false
char
'\u0000'
Integer (byte, short, int, long)
0L for long, 0 for others
Floating-point (float, double)
0.0F or 0.0D
Reference types
null
If no initialization is provided for a static variable either in the declaration or in a static initializer block (see Section 9.9, p. 410), it is initialized with the default value of its type when the class is loaded. Similarly, if no initialization is provided for an instance variable either in the declaration or in an instance initializer block (see Section 9.10, p. 413), it is initialized with the default value of its type when the class is instantiated. The fields of reference types are always initialized with the null reference value if no initialization is provided. Example 2.1 illustrates default initialization of fields. Note that static variables are initialized when the class is loaded the first time, and instance variables are initialized accordingly in every object created from the class Light. Example 2.1
Default Values for Fields public class Light { // Static variable static int counter;
// Default value 0 when class is loaded.
// Instance variables: int noOfWatts = 100; // Explicitly set to 100. boolean indicator; // Implicitly set to default value false. String location; // Implicitly set to default value null. public static void main(String[] args) { Light bulb = new Light(); System.out.println("Static variable counter: " + Light.counter); System.out.println("Instance variable noOfWatts: " + bulb.noOfWatts);
Output from the program: Static variable counter: 0 Instance variable noOfWatts: 100 Instance variable indicator: false Instance variable location: null
Initializing Local Variables of Primitive Data Types Local variables are variables that are declared in methods, constructors, and blocks (see Chapter 3, p. 39). Local variables are not initialized when they are created at method invocation, that is, when the execution of a method is started. The same applies in constructors and blocks. Local variables must be explicitly initialized before being used. The compiler will report as errors any attempts to use uninitialized local variables. Example 2.2
Flagging Uninitialized Local Variables of Primitive Data Types public class TooSmartClass { public static void main(String[] args) { int weight = 10, thePrice; if (weight < 10) thePrice = 1000; if (weight > 50) thePrice = 5000; if (weight >= 10) thePrice = weight*10; System.out.println("The price is: " + thePrice);
// (1) Local variables
// (2) Always executed. // (3)
} }
In Example 2.2, the compiler complains that the local variable thePrice used in the println statement at (3) may not be initialized. However, it can be seen that at runtime, the local variable thePrice will get the value 100 in the last if-statement at (2), before it is used in the println statement. The compiler does not perform a rigorous analysis of the program in this regard. It only compiles the body of a conditional statement if it can deduce the condition to be true. The program will compile correctly if the variable is initialized in the declaration, or if an unconditional assignment is made to the variable. Replacing the declaration of the local variables at (1) in Example 2.2 with the following declaration solves the problem: int weight = 10, thePrice = 0;
// (1') Both local variables initialized.
2.4: INITIAL VALUES FOR VARIABLES
35
Initializing Local Reference Variables Local reference variables are bound by the same initialization rules as local variables of primitive data types. Example 2.3
Flagging Uninitialized Local Reference Variables public class VerySmartClass { public static void main(String[] args) { String importantMessage; // Local reference variable System.out.println("The message length is: " + importantMessage.length()); } }
In Example 2.3, the compiler complains that the local variable importantMessage used in the println statement may not be initialized. If the variable importantMessage is set to the value null, the program will compile. However, a runtime error (NullPointerException) will occur when the code is executed, since the variable importantMessage will not denote any object. The golden rule is to ensure that a reference variable, whether local or not, is assigned a reference to an object before it is used, that is, ensure that it does not have the value null. The program compiles and runs if we replace the declaration with the following declaration of the local variable, which creates a string literal and assigns its reference value to the local reference variable importantMessage: String importantMessage = "Initialize before use!";
Arrays and their default values are discussed in Section 3.6, p. 69.
Lifetime of Variables The lifetime of a variable, that is, the time a variable is accessible during execution, is determined by the context in which it is declared. The lifetime of a variable is also called scope, and is discussed in more detail in Section 4.6, p. 129. We distinguish between lifetime of variables in three contexts: • Instance variables—members of a class, and created for each object of the class. In other words, every object of the class will have its own copies of these variables, which are local to the object. The values of these variables at any given time constitute the state of the object. Instance variables exist as long as the object they belong to is in use at runtime. • Static variables—also members of a class, but not created for any specific object of the class and, therefore, belong only to the class (see Section 4.6, p. 129). They are created when the class is loaded at runtime, and exist as long as the class is available at runtime.
36
CHAPTER 2: LANGUAGE FUNDAMENTALS
• Local variables (also called method automatic variables)—declared in methods, constructors, and blocks; and created for each execution of the method, constructor, or block. After the execution of the method, constructor, or block completes, local (non-final) variables are no longer accessible.
Review Questions 2.7
Which declarations are valid? Select the three correct answers. (a) char a = '\u0061'; (b) char 'a' = 'a'; (c) char \u0061 = 'a'; (d) ch\u0061r a = 'a'; (e) ch'a'r a = 'a';
2.8
Given the following code within a method, which statement is true? int a, b; b = 5;
Select the one correct answer. (a) (b) (c) (d) (e) 2.9
Local variable a is not declared. Local variable b is not declared. Local variable a is declared but not initialized. Local variable b is declared but not initialized. Local variable b is initialized but not declared.
In which of these variable declarations will the variable remain uninitialized unless it is explicitly initialized? Select the one correct answer. (a) (b) (c) (d) (e)
2.10
Declaration of an instance variable of type int. Declaration of a static variable of type float. Declaration of a local variable of type float. Declaration of a static variable of type Object. Declaration of an instance variable of type int[].
What will be the result of compiling and running the following program? public class Init { String title; boolean published; static int total; static double maxPrice;
Select the one correct answer. (a) (b) (c) (d) (e)
The program will fail to compile. The program will compile, and print |null|false|0|0.0|0.0|, when run. The program will compile, and print |null|true|0|0.0|100.0|, when run. The program will compile, and print | |false|0|0.0|0.0|, when run. The program will compile, and print |null|false|0|0.0|100.0|, when run.
Chapter Summary The following information was included in this chapter: • basic language elements: identifiers, keywords, literals, white space, and comments • primitive data types: integral, floating-point, and boolean • notational representation of numbers in decimal, octal, and hexadecimal systems • declaration and initialization of variables, including reference variables • usage of default values for instance variables and static variables • lifetime of instance variables, static variables, and local variables
Programming Exercise 2.1
The following program has several errors. Modify the program so that it will compile and run without errors. // Filename: Temperature.java PUBLIC CLASS temperature { PUBLIC void main(string args) { double fahrenheit = 62.5; */ Convert /* double celsius = f2c(fahrenheit); System.out.println(fahrenheit + 'F' + " = " + Celsius + 'C'); } double f2c(float fahr) { RETURN (fahr - 32) * 5 / 9; } }
This page intentionally left blank
Declarations
3
Exam Objectives 1.3 Develop code that declares, initializes, and uses primitives, arrays, enums, and objects as static, instance, and local variables. Also, use legal identifiers for variable names. ❍
Enums and arrays are covered in this chapter.
❍
For primitive types, see Section 2.2, p. 28.
❍
For initialization of static, instance, and local variables, see Section 2.3, p. 31.
For initializers, see Section 9.7, p. 406. 1.4 Develop code that declares both static and non-static methods, and—if appropriate—use method names that adhere to the JavaBeans naming standards. Also develop code that declares and uses a variable-length argument list. 1.5 Given a code example, determine if a method is correctly overriding or overloading another method, and identify legal return values (including covariant returns), for the method. ❍
❍
For overloaded method resolution, see Section 7.10, p. 324.
❍
For overriding methods, see Section 7.2, p. 288.
❍
For return values, see Section 6.4, p. 228.
For covariant return, see Section 7.2, p. 290. 1.6 Given a set of classes and superclasses, develop constructors for one or more of the classes. Given a class declaration, determine if a default constructor will be created and, if so, determine the behavior of that constructor. Given a nested or non-nested class listing, write code to instantiate the class. ❍
❍
For constructor chaining, see Section 7.5, p. 302, and Section 9.11, p. 416.
For instantiating nested classes, see Chapter 8. 7.2 Given an example of a class and a command-line, determine the expected runtime behavior. 7.3 Determine the effect upon object references and primitive values when they are passed into methods that perform assignments or other modifying operations on the parameters. ❍
❍
For conversions in assignment and method invocation contexts, see Section 5.2, p. 163. 39
40
CHAPTER 3: DECLARATIONS
3.1 Class Declarations A class declaration introduces a new reference type. It has the following general syntax: class <extends clause> // Class header { // Class body
<method declarations> }
In the class header, the name of the class is preceded by the keyword class. In addition, the class header can specify the following information: • accessibility modifier (see Section 4.7, p. 132) • additional class modifiers (see Section 4.8, p. 135) • a formal type parameter list, if the class is generic (see Section 14.2, p. 663) • any class it extends (see Section 7.1, p. 284) • any interfaces it implements (see Section 7.6, p. 309) The class body can contain member declarations which comprise: • field declarations (see Section 2.3, p. 31) • method declarations (see Section 3.3, p. 44) • nested class, enum, and interface declarations (see Section 8.1, p. 352) Members declared static belong to the class and are called static members. Nonstatic members belong to the objects of the class and are called instance members. In addition, the following can be declared in a class body: • constructor declarations (see Section 3.4, p. 48) • static and instance initializer blocks (see Section 9.7, p. 406) The member declarations, constructor declarations, and initializer blocks can appear in any order in the class body. In order to understand what code can be legally declared in a class, we distinguish between static context and non-static context. A static context is defined by static methods, static field initializers, and static initializer blocks. A non-static context is defined by instance methods, constructors, non-static field initializers, and instance initializer blocks. By static code we mean expressions and statements in a static context, and similarly by non-static code we mean expressions and statements
3.2: JAVABEANS STANDARD
41
in a non-static context. One crucial difference between the two contexts is that static code can only refer to other static members.
3.2 JavaBeans Standard The JavaBeans Standard allows reusable software components to be modelled in Java so that these components can be assembled to create sophisticated applications. In particular, builder tools can take advantage of how these components are specified, in order to build new applications based on these components. The JavaBeans specification specifies the rules for defining such components (called JavaBeans). The interested reader is encouraged to consult this documentation (see http://java.sun.com/javase/technologies/desktop/javabeans/docs/spec.html) for details since we only cover the basic fundamentals for creating JavaBeans .
Naming Patterns for Properties The rules of the JavaBean specification stipulate naming patterns for declaring properties of JavaBeans. A naming pattern defines a standard naming convention. A property of an object is normally defined as a field in the object, which is usually not directly accessible by clients (see Example 3.1). A JavaBean should adhere to the following naming patterns when specifying its properties: • The properties are assumed to be private, and their names start with a lowercase letter. Example 3.1 shows that the JavaBean class Light has three properties. • In order to retrieve and change values of its properties, a JavaBean provides getter and setter methods for them. Example 3.1 shows a JavaBean with three getter and three setter methods for its properties. • For a property, the setter method starts with the prefix set. The rest of the method name is assumed to be a property name, where the first letter of the property name has been converted to uppercase. In Example 3.1, the value of the property noOfWatts can be changed by the setter method setNoOfWatts(). Setter methods are public and void, having a parameter of the same type as that of the property. • For a property, the getter method starts with the prefix get. The rest of the method name is assumed to be a property name, where the first letter of the property name has been converted to uppercase. In Example 3.1, the value of the property noOfWatts can be retrieved by the getter method getNoOfWatts(). For a boolean property, the getter method can start with the prefix get or is. In Example 3.1, the value of the boolean property indicator can be retrieved by the getter method isIndicator(). Getter methods are no-argument public methods that return a value of the same type as the parameter of the corresponding setter method.
42
Example 3.1
CHAPTER 3: DECLARATIONS
A JavaBean public class Light { // Properties: private int noOfWatts; private String location; private boolean indicator;
// wattage // placement // on or off
// Setters public void setNoOfWatts(int noOfWatts) { this.noOfWatts = noOfWatts; } public void setLocation(String location) { this.location = location; } public void setIndicator(boolean indicator) { this.indicator = indicator; } // Getters public int getNoOfWatts() { return noOfWatts; } public String getLocation() { return location; } public boolean isIndicator() { return indicator; } }
Naming Patterns for the Event Model A listener is an object that is interested in being notified when a particular event takes place. The origin of this event is usually an object called the source, which notifies interested listeners when the event occurs. In this setup, a listener can be added to or removed from the list of listeners notified by a source about the occurrence of a particular event. This setup is the basis of the event model which is depicted in Figure 3.1. The JavaBean specification stipulates naming patterns for the event model to facilitate its use by builder tools to assemble event-based applications. Figure 3.1 shows where the naming patterns for handling events of type X are applied: • An event class with the name XEvent, that extends the java.util.EventObject class. public class XEvent extends java.util.EventObject { public XEvent(Object source) { super(source); } }
• A listener interface with the name XListener, that specifies the specific method to be called in a listener when an event of the type XEvent occurs. The listener interface extends the java.util.EventListener interface. public interface XListener extends java.util.EventListener { public void methodAInXListener(XEvent ev); }
A listener interested in XEvents must implement the XListener interface, and must be registered with the source in order to be informed about XEvents.
43
3.2: JAVABEANS STANDARD Figure 3.1 The Event Model
methodAInXListener(XEvent e) A listener interested in XEvent events is registered with the source using the addXListener() method. The listener must implement the XListener interface in order to recieve events of type XEvent. The listener is informed about events of type XEvent via the methodAInXListener() in the XListener interface.
public class ListenerObject implements XListener { public void methodAInXListener(XEvent e) { /* ... */ } }
• A source for XEvent, that implements the methods addXListener() and removeXListener(). These methods are used to add or remove a listener interested in XEvents, respectively. The parameter of these methods is of the type XListener. public class SourceObject { public synchronized void addXListener(XListener listener) { /* ... */ } public synchronized void removeXListener(XListener listener) { /* ... */ } }
Note that there are no naming patterns defined for the names of the source and the listener classes. Neither is there any standard convention for naming the methods specified in the listener interface.
44
CHAPTER 3: DECLARATIONS
3.3 Method Declarations The general syntax of a method declaration is <method modifiers> <method name> () // Method header { // Method body
<statements> }
In addition to the name of the method, the method header can specify the following information: • scope or accessibility modifier (see Section 4.9, p. 138) • additional method modifiers (see Section 4.10, p. 146) • a formal type parameter list, if the declaration is for a generic method (see Section 14.8, p. 697) • the type of the return value, or void if the method does not return any value (see Section 6.4, p. 228) • a formal parameter list (see below) • checked exceptions thrown by the method are specified in a throws clause (see Section 6.9, p. 257) The formal parameter list is a comma-separated list of parameters for passing information to the method when the method is invoked by a method call (see Section 3.7, p. 81). An empty parameter list must be specified by ( ). Each parameter is a simple variable declaration consisting of its type and name: <parameter modifier> <parameter name> The parameter names are local to the method (see Section 4.6, p. 131). The parameter modifier final is discussed in Section 3.7 on page 89. The signature of a method comprises the method name and the formal parameter list only. The method body is a block containing the local declarations and the statements of the method. Local variable declarations are discussed in Section 2.3 on page 31, and nested local class declarations in Section 8.4 on page 371. Like member variables, member methods can be characterized as: • instance methods • static methods, which are discussed in Section 4.10, p. 148.
3.3: METHOD DECLARATIONS
45
Statements Statements in Java can be grouped into various categories. Variable declarations with explicit initialization of the variables are called declaration statements (see Section 2.3, p. 31, and Section 3.6, p. 71). Other basic forms of statements are control flow statements (see Section 6.1, p. 204) and expression statements. An expression statement is an expression terminated by a semicolon. The expression is evaluated for its side effect and its value discarded. Only certain types of expressions have meaning as statements. They include the following: • assignments (see Section 5.5, p. 169) • increment and decrement operators (see Section 5.8, p. 186) • method calls (see Section 3.7, p. 81) • object creation expressions with the new operator (see Section 5.15, p. 201) A solitary semicolon denotes the empty statement that does nothing. A block, {}, is a compound statement which can be used to group zero or more local declarations and statements (see Section 4.6, p. 131). Blocks can be nested, since a block is a statement that can contain other statements. A block can be used in any context where a simple statement is permitted. The compound statement which is embodied in a block, begins at the left brace, {, and ends with a matching right brace, }. Such a block must not be confused with an array initialization block in declaration statements (see Section 3.6, p. 71). Labeled statements are discussed in Section 6.4 on page 223.
Instance Methods and the Object Reference this Instance methods belong to every object of the class and can only be invoked on objects. All members defined in the class, both static and non-static, are accessible in the context of an instance method. The reason is that all instance methods are passed an implicit reference to the current object, that is, the object on which the method is being invoked. The current object can be referenced in the body of the instance method by the keyword this. In the body of the method, the this reference can be used like any other object reference to access members of the object. In fact, the keyword this can be used in any non-static context. The this reference can be used as a normal reference to reference the current object, but the reference cannot be modified—it is a final reference (Section 4.10, p. 148). The this reference to the current object is useful in situations where a local variable hides, or shadows, a field with the same name. In Example 3.2, the two parameters noOfWatts and indicator in the constructor of the Light class have the same names as the fields in the class. The example also declares a local variable location, which has the same name as one of the fields. The reference this can be used to distinguish the fields from the local variables. At (1), the this reference is used to identify the field noOfWatts, which is assigned the value of the parameter noOfWatts. Without
46
CHAPTER 3: DECLARATIONS
the this reference at (2), the value of the parameter indicator is assigned back to this parameter, and not to the field by the same name, resulting in a logical error. Similarly at (3), without the this reference, it is the local variable location that is assigned the value of the parameter site, and not the field by the same name. Example 3.2
Using the this Reference public class Light { // Fields: int noOfWatts; boolean indicator; String location;
(1) Assignment to field. (2) Assignment to parameter. (3) Assignment to local variable. (4) equivalent to call at (4)
} public void superfluous() { System.out.println(this); }
// (5)
public static void main(String[] args) { Light light = new Light(100, true, "loft"); System.out.println("No. of watts: " + light.noOfWatts); System.out.println("Indicator: " + light.indicator); System.out.println("Location: " + light.location); } }
Output from the program: Light@df6ccd Light@df6ccd No. of watts: 100 Indicator: false Location: null
If a member is not shadowed by a local declaration, the simple name member is considered a short-hand notation for this.member. In particular, the this reference can be used explicitly to invoke other methods in the class. This is illustrated at (4) in Example 3.2, where the method superfluous() is called. If, for some reason, a method needs to pass the current object to another method, it can do so using the this reference. This is illustrated at (5) in Example 3.2, where the current object is passed to the println() method.
47
3.3: METHOD DECLARATIONS
Note that the this reference cannot be used in a static context, as static code is not executed in the context of any object.
Method Overloading Each method has a signature, which comprises the name of the method, and the types and order of the parameters in the formal parameter list. Several method implementations may have the same name, as long as the method signatures differ. This is called method overloading. Since overloaded methods have the same name, their parameter lists must be different. Rather than inventing new method names, method overloading can be used when the same logical operation requires multiple implementations. The Java standard library makes heavy use of method overloading. For example, the class java.lang.Math contains an overloaded method min(), which returns the minimum of two numeric values. public public public public
static static static static
double min(double a, double b) float min(float a, float b) int min(int a, int b) long min(long a, long b)
In the following examples, five implementations of the method methodA are shown: void int int long long
methodA(int a, double b) { methodA(int a) { methodA() { methodA(double a, int b) { methodA(int x, double y) {
/* ... return return return return
*/ a; 1; b; x;
} } } } }
// // // // //
(1) (2) (3) (4) (5) Not OK.
The corresponding signatures of the five methods are as follows: methodA(int, double) methodA(int) methodA() methodA(double, int) methodA(int, double)
1' 2': Number of parameters. 3': Number of parameters. 4': Order of parameters. 5': Same as 1'.
The first four implementations of the method named methodA are overloaded correctly, each time with a different parameter list and, therefore, different signatures. The declaration at (5) has the same signature methodA(int, double) as the declaration at (1) and is, therefore, not a valid overloading of this method. void bake(Cake k) { /* ... */ } void bake(Pizza p) { /* ... */ }
// (1) // (2)
int double
// (3) // (4) Not OK. Same signature.
halfIt(int a) { return a/2; } halfIt(int a) { return a/2.0; }
The method named bake is correctly overloaded at (1) and (2), with two different signatures. In the implementation, changing just the return type (as shown at (3) and (4) above), is not enough to overload a method, and will be flagged as a compile-time error. The parameter list in the declarations must be different.
48
CHAPTER 3: DECLARATIONS
Only methods declared in the same class and those that are inherited by the class can be overloaded. Overloaded methods should be considered as individual methods that just happen to have the same name. Methods with the same name are allowed, since methods are identified by their signature. At compile time, the right implementation of an overloaded method is chosen based on the signature of the method call. Details of method overloading resolution can be found in Section 7.10 on page 324. Method overloading should not be confused with method overriding (see Section 7.2, p. 288).
3.4 Constructors The main purpose of constructors is to set the initial state of an object, when the object is created by using the new operator. A constructor has the following general syntax: () // Constructor header { // Constructor body
<statements> }
Constructor declarations are very much like method declarations. However, the following restrictions on constructors should be noted: • Modifiers other than an accessibility modifier are not permitted in the constructor header. For accessibility modifiers for constructors, see Section 4.9 on page 138. • Constructors cannot return a value and, therefore, do not specify a return type, not even void, in the constructor header. But their declaration can use the return statement that does not return a value in the constructor body (Section 6.4, p. 228). • The constructor name must be the same as the class name. Class names and method names exist in different namespaces. Thus, there are no name conflicts in Example 3.3, where a method declared at (2) has the same name as the constructor declared at (1). However, using such naming schemes is strongly discouraged.
49
3.4: CONSTRUCTORS
Example 3.3
Namespaces public class Name { Name() { // (1) System.out.println("Constructor"); } void Name() { // (2) System.out.println("Method"); } public static void main(String[] args) { new Name().Name(); // (3) Constructor call followed by method call. } }
Output from the program: Constructor Method
The Default Constructor A default constructor is a constructor without any parameters, i.e., it is a no-parameter constructor. It has the following signature: () If a class does not specify any constructors, then an implicit default constructor is generated for the class by the compiler. The implicit default constructor is equivalent to the following implementation: () { super(); }
// No parameters. Calls superclass constructor.
The only action taken by the implicit default constructor is to call the superclass constructor. This ensures that the inherited state of the object is initialized properly (see Section 7.5, p. 302). In addition, all instance variables in the object are set to the default value of their type, barring those that are initialized by an initialization expression in their declaration. In the following code, the class Light does not specify any constructors. class Light { // Fields: int noOfWatts; boolean indicator; String location; // No constructors //... }
// wattage // on or off // placement
50
CHAPTER 3: DECLARATIONS class Greenhouse { // ... Light oneLight = new Light(); }
// (1) Call to implicit default constructor.
In the code above, the following implicit default constructor is called when a Light object is created by the object creation expression at (1): Light() { super(); }
Creating an object using the new operator with the implicit default constructor, as at (1), will initialize the fields of the object to their default values (that is, the fields noOfWatts, indicator, and location in a Light object will be initialized to 0, false, and null, respectively). A class can choose to provide an implementation of the default constructor. In the following example, the class Light provides an explicit default constructor at (1). Note that it has the same name as the class, and that it does not specify any parameters. class Light { // ... // Explicit Light() { noOfWatts indicator location } //... }
Default Constructor: // (1) = 50; = true; = "X";
class Greenhouse { // ... Light extraLight = new Light(); }
// (2) Call of explicit default constructor.
The explicit default constructor ensures that any object created with the object creation expression new Light(), as at (2), will have its fields noOfWatts, indicator and location initialized to 50, true and "X", respectively. If a class defines any explicit constructors, it can no longer rely on the implicit default constructor to set the state of its objects. If such a class requires a default constructor, its implementation must be provided. In the example below, the class Light only provides a non-default constructor at (1). It is called at (2) when an object of the class Light is created with the new operator. Any attempt to call the default constructor will be flagged as a compile-time error, as shown at (3). class Light { // ... // Only non-default Constructor: Light(int noOfWatts, boolean indicator, String location) { this.noOfWatts = noOfWatts; this.indicator = indicator; this.location = location; }
// (1)
51
3.4: CONSTRUCTORS //... } class Greenhouse { // ... Light moreLight = new Light(100, true, "Greenhouse"); //Light firstLight = new Light(); error. }
// (2) OK. // (3) Compile-time
Overloaded Constructors Like methods, constructors can also be overloaded. Since the constructors in a class all have the same name as the class, their signatures are differentiated by their parameter lists. In the following example, the class Light now provides both an explicit implementation of the default constructor at (1) and a non-default constructor at (2). The constructors are overloaded, as is evident by their signatures. The non-default constructor is called when an object of the class Light is created at (3), and the default constructor is likewise called at (4). Overloading of constructors allows appropriate initialization of objects on creation, depending on the constructor invoked (see also chaining of constructors in Section 7.5, p. 302.) class Light { // ... // Explicit Light() { noOfWatts indicator location }
Which one of these declarations is a valid method declaration? Select the one correct answer. (a) void method1 { /* (b) void method2() { /* (c) void method3(void) { /* (d) method4() { /* (e) method5(void) { /*
3.2
... ... ... ... ...
*/ */ */ */ */
} } } } }
Which statements, when inserted at (1), will not result in compile-time errors? public class ThisUsage { int planets; static int suns; public void gaze() { int i; // (1) INSERT STATEMENT HERE } }
Select the three correct answers. (a) i = this.planets; (b) i = this.suns; (c) this = new ThisUsage(); (d) this.i = 4; (e) this.suns = planets; 3.3
Given the following pairs of method declarations, which statements are true? void fly(int distance) {} int fly(int time, int speed) { return time*speed; } void fall(int time) {} int fall(int distance) { return distance; } void glide(int time) {} void Glide(int time) {}
Select the two correct answers. (a) The first pair of methods will compile, and overload the method name fly. (b) The second pair of methods will compile, and overload the method name fall. (c) The third pair of methods will compile, and overload the method name glide. (d) The second pair of methods will not compile. (e) The third pair of methods will not compile.
53
3.4: CONSTRUCTORS
3.4
Given a class named Book, which one of these constructor declarations is valid for the class Book? Select the one correct answer. (a) Book(Book b) {} (b) Book Book() {} (c) private final Book() {} (d) void Book() {} (e) public static void Book(String[] args) {} (f) abstract Book() {}
3.5
Which statements are true? Select the two correct answers. (a) (b) (c) (d) (e)
3.6
A class must define a constructor. A constructor can be declared private. A constructor can return a value. A constructor must initialize all fields when a class is instantiated. A constructor can access the non-static members of a class.
What will be the result of compiling the following program? public class MyClass { long var; public void MyClass(long param) { var = param; } public static void main(String[] args) { MyClass a, b; a = new MyClass(); b = new MyClass(5); }
// (1)
// (2) // (3)
}
Select the one correct answer. (a) A compilation error will occur at (1), since constructors cannot specify a return value. (b) A compilation error will occur at (2), since the class does not have a default constructor. (c) A compilation error will occur at (3), since the class does not have a constructor that takes one argument of type int. (d) The program will compile without errors.
54
CHAPTER 3: DECLARATIONS
3.5 Enumerated Types An enumerated type defines a finite set of symbolic names and their values. These symbolic names are usually called enum constants or named constants. One way to define such constants is to declare them as final, static variables in a class (or interface) declaration: public class MachineState public static final int public static final int public static final int }
{ BUSY = 1; IDLE = 0; BLOCKED = -1;
Such constants are not typesafe, as any int value can be used where we need to use a constant declared in the MachineState class. Such a constant must be qualified by the class (or interface) name, unless the class is extended (or the interface is implemented). When such a constant is printed, only its value (for example, 0), and not its name (for example, IDLE) is printed. A constant also needs recompiling if its value is changed, as the values of such constants are compiled into the client code. An enumerated type in Java is much more powerful than the approach outlined above. It is certainly more convenient to use than implementing one from scratch using the typesafe enum pattern (see Effective Java by Josh Bloch, ISBN-10: 0321356683).
Declaring Typesafe Enums The canonical form of declaring an enum type is shown below. enum MachineState { BUSY, IDLE, BLOCKED } // Canonical form
The keyword enum is used to declare an enum type. The basic notation requires the type name and a comma-separated list of enum constants. In this case, the name of the enum type is MachineState. It defines three enum constants. An enum constant can be any legal Java identifier, but the convention is to use uppercase letters in the name. Essentially, an enum declaration defines a reference type that has a finite number of permissible values referenced by the enum constants, and the compiler ensures they are used in a typesafe manner.
Using Typesafe Enums Example 3.4 illustrates using enum constants. An enum type is essentially used as any other reference type, and the restrictions are noted later in this section. Enum constants are in fact final, static variables of the enum type, and they are implicitly initialized with objects of the enum type when the enum type is loaded at runtime. Since the enum constants are static members, they can be accessed using the name of the enum type—analogous to accessing static members in a class.
55
3.5: ENUMERATED TYPES
Example 3.4 shows a machine client that uses a machine whose state is an enum constant. From Example 3.4, we see that an enum constant can be passed as an argument, as shown as (1), and we can declare references whose type is an enum type, as shown as (3), but we cannot create new constants (that is, objects) of the enum type MachineState. An attempt to do so at (5), results in a compile-time error. The string representation of an enum constant is its name, as shown at (4). Note that it is not possible to pass a type of value other than a MachineState enum constant in the call to the method setState() of the Machine class, as shown at (2). Example 3.4
Using Enums // Filename: MachineState.java public enum MachineState { BUSY, IDLE, BLOCKED }
// Filename: Machine.java public class Machine { private MachineState state; public void setState(MachineState state) { this.state = state; } public MachineState getState() { return this.state; } }
// Filename: MachineClient.java public class MachineClient { public static void main(String[] args) { Machine machine = new Machine(); machine.setState(MachineState.IDLE); // machine.setState(1);
// (1) Passed as a value. // (2) Compile-time error!
MachineState state = machine.getState(); // (3) Declaring a reference. System.out.println( "The machine state is: " + state // (4) Printing the enum name. ); // MachineState newState = new MachineState();// (5) Compile-time error! } }
Output from the program: The machine state is: IDLE
Declaring Enum Constructors and Members An enum type declaration is a special kind of reference type declaration. It can declare constructors and other members as in an ordinary class, but the enum constants must be declared before any other declarations (see the declaration of the
56
CHAPTER 3: DECLARATIONS
enum type Meal in Example 3.5). The list of enum constants must be terminated by a semi-colon (;). Each enum constant name can be followed by an argument list that is passed to the constructor of the enum type having the matching parameter signature. In Example 3.5, the enum type Meal contains a constructor declaration at (1) with the following signature: Meal(int, int)
Each enum constant is specified with an argument list with the signature (int, int) that matches the constructor signature. In addition, the enum declaration declares two fields for the meal time at (3), and two instance methods to retrieve the meal time at (4). When the enum type is loaded at runtime, the constructor is run for each enum constant, passing the argument values specified for the enum constant. For the Meal enum type, three objects are created that are initialized with the specified argument values, and are referenced by the three enum constants, respectively. Note that each enum constant is a final, static reference that stores the reference value of an object of the enum type, and methods of the enum type can be called on this object by using the enum constant name. This is illustrated at (5) in Example 3.5 by calling methods on the object referenced by the enum constant Meal.BREAKFAST. An implicit standard constructor is created if no constructors are provided for the enum type. As mentioned earlier, an enum type cannot be instantiated using the new operator. The constructors cannot be called explicitly. The only accessibility modifier allowed for a constructor is private. Example 3.5
Declaring Enum Constructors and Members // Filename: Meal.java public enum Meal { BREAKFAST(7,30), LUNCH(12,15), DINNER(19,45);
// Fields for the meal time: private int hh; private int mm;
(3)
// Instance methods: public int getHour() { return this.hh; } public int getMins() { return this.mm; }
(4)
57
3.5: ENUMERATED TYPES // Filename: MealAdministrator.java public class MealAdministrator { public static void main(String[] args) { System.out.printf( // (5) "Please note that no eggs will be served at %s, %02d:%02d.%n", Meal.BREAKFAST, Meal.BREAKFAST.getHour(), Meal.BREAKFAST.getMins() ); System.out.println("Meal times are as follows:"); Meal[] meals = Meal.values(); for (Meal meal : meals) System.out.printf("%s served at %02d:%02d%n", meal, meal.getHour(), meal.getMins() );
// (6) // (7)
Meal formalDinner = Meal.valueOf("DINNER"); // (8) System.out.printf("Formal dress is required for %s at %02d:%02d.%n", formalDinner, formalDinner.getHour(), formalDinner.getMins() ); } }
Output from the program: Please note that no eggs will be served at BREAKFAST, 07:30. Meal times are as follows: BREAKFAST served at 07:30 LUNCH served at 12:15 DINNER served at 19:45 Formal dress is required for DINNER at 19:45.
Implicit Static Methods for Enum Types All enum types implicitly have the following static methods, and methods with these names cannot be declared in an enum type declaration: static EnumTypeName[] values()
Returns an array containing the enum constants of this enum type, in the order they are specified. static EnumTypeName valueOf(String name)
Returns the enum constant with the specified name. An IllegalArgumentException is thrown if the specified name does not match the name of an enum constant. The specified name is not qualified with the enum type name. The static method values() is called at (6) in Example 3.5 to create an array of enum constants. This array is traversed in the for(:) loop at (7), printing the information about each meal. The for(:) loop is discussed in Section 6.3, p. 220.
58
CHAPTER 3: DECLARATIONS
The static method valueOf() is called at (8) in Example 3.5 to retrieve the enum constant that has the specified name "DINNER". A printf statement is used to print the information about the meal denoted by this enum constant.
Inherited Methods from the Enum Class All enum types are subtypes of the java.lang.Enum class which provides the default behavior. All enum types are comparable (Section 15.1, p. 765) and serializable (Section 11.6, p. 510). All enum types inherit the following final methods from the java.lang.Enum class, and these methods can therefore not be overridden by an enum type: protected final Object clone()
An instance of an enum type cannot be cloned (see Section 10.2, p. 424). The method throws an CloneNotSupportedException. final int compareTo(E o)
The natural order of the enum constants in an enum type is according to their ordinal values (see the ordinal() method below). The compareTo() method in the Comparable interface is discussed in Section 15.1, p. 765. final boolean equals(Object other)
This method returns true if the specified object is equal to this enum constant (Section 15.1, p. 751). protected final void finalize()
An enum constant cannot be finalized, because this final method effectively prevents enum types from implementing their own finalize() method (see Section 9.4, p. 396). final Class<E> getDeclaringClass()
This method returns the Class object corresponding to this enum constant's enum type (see Section 10.2, p. 424). final int hashCode()
This method returns a hash code for this enum constant (see Section 15.1, p. 760). final String name()
This method returns the name of this enum constant, exactly as declared in its enum declaration. final int ordinal()
This method returns the ordinal value of this enum constant (that is, its position in its enum type declaration). The first enum constant is assigned an ordinal value of zero. If the ordinal value of an enum constant is less than the ordinal value of another enum constant of the same enum type, the former occurs before the latter in the enum type declaration.
3.5: ENUMERATED TYPES
59
Note that the equality test implemented by the equals() method is based on reference equality (==) of the enum constants, not on value equality (Section 5.11, p. 193). An enum type has a finite number of distinct objects. Comparing two enum references for equality means determining whether they store the reference value of the same enum contant, i.e., whether the references are aliases. Thus, for any two enum references meal1 and meal2, the expression meal1.equals(meal2) and meal1 == meal2 are equivalent. The Enum class also overrides the toString() method from the Object class (see Section 10.2, p. 424). The toString() method returns the name of the enum constant, but it is not final, and can be overridden by an enum type. Example 3.6 uses some of the methods mentioned in this subsection.
Extending Enum Types: Constant-Specific Class Bodies A review of subtyping (Section 7.1, p. 284), overriding (Section 7.2, p. 288), and anonymous classes (Section 8.5, p. 377) can be helpful before diving into this subsection. Constant-specific class bodies define anonymous classes inside an enum type, i.e., they implicitly extend the enclosing enum type. The enum type Meal in Example 3.6 declares constant-specific class bodies for its constants. The following skeletal code declares the constant-specific class body for the enum constant BREAKFAST: BREAKFAST(7,30) { // (1) Start of constant-specific class body public double mealPrice(Day day) { // (2) Overriding abstract method ... } public String toString() { // (3) Overriding method from the Enum class ... } } // (4) End of constant-specific class body
The constant-specific class body, as the name implies, is a class body that is specific to a particular enum constant. As any class body, it is enclosed in braces, { }. It is declared immediately after the enum constant and any constructor arguments. In the code above, it starts at (1) and ends at (4). Like any class body, it can contain member declarations. In the above case, the body contains two method declarations: an implementation of the method mealPrice() at (2) that overrides the abstract method declaration at (7) in the enclosing enum supertype, and an implementation of the toString() method at (3) that overrides the one inherited by the Meal enum type from the superclass java.lang.Enum. The constant-specific class body is an anonymous class, i.e., a class with no name. Each constant-specific class body defines a distinct, albeit anonymous, subtype of the enclosing enum type. In the code above, the constant-specific class body defines a subtype of the Meal enum type. It inherits members of the enclosing enum supertype, that are not private, overridden, or hidden. When the enum type Meal is loaded at runtime, this constant-specific class body is instantiated, and the reference value of the instance is assigned to the enum constant BREAKFAST. Note that the
60
CHAPTER 3: DECLARATIONS
type of the enum constant is Meal, which is the supertype of the anonymous subtype represented by the constant-specific class body. Since supertype references can refer to subtype objects, the above assignment is legal. Each enum constant overrides the abstract method mealPrice() declared in the enclosing enum supertype, i.e., provides an implementation for the method. The compiler will report an error if this is not the case. Although the enum type declaration specifies an abstract method, the enum type declaration is not declared abstract—contrary to an abstract class. Given that the references meal and day are of the enum types Meal and Day from Example 3.6, respectively, the method call meal.mealPrice(day)
will execute the mealPrice() method from the constant-specific body of the enum constant denoted by the reference meal. Two constant-specific class bodies, associated with the enum constants BREAKFAST and LUNCH, override the toString() method from the Enum class. Note that the toString() method is not overridden in the Meal enum type, but in the anonymous classes represented by two constant-specific class bodies. The third enum constant, DINNER, relies on the toString() method inherited from the Enum class. Constructors, abstract methods, and static methods cannot be declared in a constantspecific class body. Instance methods declared in constant-specific class bodies are only accessible if they override methods in the enclosing enum supertype. Example 3.6
Declaring Constant-Specific Class Bodies // Filename: Day.java public enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
// Filename: Meal.java public enum Meal { // Each enum constant defines a constant-specific class body BREAKFAST(7,30) { public double mealPrice(Day day) { double breakfastPrice = 10.50; if (day.equals(Day.SATURDAY) || day == Day.SUNDAY) breakfastPrice *= 1.5; return breakfastPrice; } public String toString() { return "Breakfast"; } }, LUNCH(12,15) { public double mealPrice(Day day) { double lunchPrice = 20.50; switch (day) { case SATURDAY: case SUNDAY:
// Enum constructor: Meal(int hh, int mm) { assert (hh >= 0 && hh <= 23): "Illegal hour."; assert (mm >= 0 && mm <= 59): "Illegal mins."; this.hh = hh; this.mm = mm; } // Instance fields: Time for the meal. private int hh; private int mm; // Instance methods: public int getHour() { return this.hh; } public int getMins() { return this.mm; } } // Filename: MealPrices.java public class MealPrices { public static void main(String[] args) { System.out.printf( "Please note that %s, %02d:%02d, on %s costs $%.2f.%n", Meal.BREAKFAST.name(), Meal.BREAKFAST.getHour(), Meal.BREAKFAST.getMins(), Day.MONDAY, Meal.BREAKFAST.mealPrice(Day.MONDAY) );
// (8)
// (9)
// (10)
System.out.println("Meal prices on " + Day.SATURDAY + " are as follows:"); Meal[] meals = Meal.values(); for (Meal meal : meals) System.out.printf( "%s costs $%.2f.%n", meal, meal.mealPrice(Day.SATURDAY) // (11)
62
CHAPTER 3: DECLARATIONS ); } }
Output from the program: Please note that BREAKFAST, 07:30, on MONDAY costs $10.50. Meal prices on SATURDAY are as follows: Breakfast costs $15.75. Lunch costs $41.00. DINNER costs $63.75.
In Example 3.6, the mealPrice() method declaration at (2) uses both the equals() method and the == operator to compare enum constants for equality. The mealPrice() method declaration at (5) uses enum constants in a switch statement (Section 6.2, p. 207). Note that the case labels in the switch statement are enum constant names, without the enum type name. The mealPrice() method declaration at (6) uses the compareTo() method to compare enum constants. The main() method at (8) in Example 3.6 demonstrates calling the mealPrice() method in the constant-specific class bodies. The mealPrice() method is called at (10) and (11). Example 3.6 also illustrates the difference between the name() and the toString() methods of the enum types. The name() method is called at (9), and the toString() method is called at (10) and (11). The name() method always prints the enum constant name exactly as it was declared. Which toString() method is executed depends on whether the toString() method in the Enum class is overridden. Only the constant-specific class bodies of the enum constants BREAKFAST and LUNCH override this method. The output from the program confirms this to be the case.
Declaring Typesafe Enums Revisited An enum type can be declared as a top-level type. Enum types can also be nested, but only within other static members, or other top-level type declarations (Section 8.2, p. 355). When nested, it is implicitly static, and can be declared with the keyword static. The following skeletal code shows the two enum types Day and Meal declared as static members in the class MealPrices: public class MealPrices { public enum Day { /* ... */ }
// Static member
public static enum Meal { /* ... */ }
// Static member
public static void main(String[] args) { /* ... */ }
// Static method
}
An enum type cannot be explicitly extended using the extends clause. An enum type is implicitly final, unless it contains constant-specific class bodies. If it declares constant-specific class bodies, it is implicitly extended. No matter what, it cannot be explicitly declared final.
63
3.5: ENUMERATED TYPES
An enum type cannot be declared abstract, regardless of whether each abstract method is overridden in the constant-specific class body of every enum constant. Like a class, an enum can implement interfaces. public interface ITimeInfo { public int getHour(); public int getMins(); } public enum Meal implements ITimeInfo { // ... public int getHour() { return this.hh; } public int getMins() { return this.mm; } }
The Java Collections Framework provides a special purpose set implementation (java.util.EnumSet) and a special purpose map implementation (java.util.EnumMap) for use with enum types. These special purpose implementations provide better performance for enum types than the general purpose counterparts, and are worth checking out.
Review Questions 3.7
Which statements about the enum type are true? Select the three correct answers. (a) An enum type is a subclass of the abstract class java.lang.Enum, hence it is Comparable and Serializable. (b) An enum type can implement interfaces. (c) We can instantiate an enum type using the new operator. (d) An enum type can define constructors. (e) We can explicitly use the extend clause to extend an enum type. (f) Enum types do not inherit members from the Object class.
3.8
What will be the result of attempting to compile and run the following code? public enum Drill { ATTENTION("Attention!"), EYES_RIGHT("Eyes right!"), EYES_LEFT("Eyes left!"), AT_EASE("At ease!"); private String command; Drill(String command) { this.command = command; } public static void main(String[] args) { System.out.println(ATTENTION); System.out.println(AT_EASE); } }
// (1) // (2)
64
CHAPTER 3: DECLARATIONS
Select the one correct answer. (a) The code compiles, but reports a ClassNotFoundException when run, since an enum type cannot be run as a standalone application. (b) The compiler reports errors in (1) and (2), as the constants must be qualified by the enum type name Drill. (c) The compiler reports errors in (1) and (2), as the constants cannot be accessed in a static context. (d) The code compiles and prints: ATTENTION AT_EASE
(e) The code compiles and prints: Attention! At ease!
(f) None of the above. 3.9
What will be the result of compiling and running the following code? import java.util.Arrays; public enum Priority { ONE(1) { public String toString() { return "LOW"; } }, TWO(2), THREE(3) { public String toString() { return "NORMAL"; } }, FOUR(4), FIVE(5) { public String toString() { return "HIGH"; } };
// (1) // (2) // (3)
private int pValue; Priority(int pValue) { this.pValue = pValue; } public static void main(String[] args) { System.out.println(Arrays.toString(Priority.values())); } }
Select the one correct answer. (a) The code compiles, but reports a ClassNotFoundException when run, since an enum type cannot be run as a standalone application. (b) The compiler reports syntax errors in (1), (2), and (3). (c) The code compiles and prints: [LOW, TWO, NORMAL, FOUR, HIGH]
(d) The code compiles and prints: [ONE, TWO, THREE, FOUR, HIGH]
(e) None of the above.
65
3.5: ENUMERATED TYPES
3.10
Which statement about the following program is true? public enum Scale { GOOD('C'), BETTER('B'), BEST('A'); private char grade; Scale(char grade) { this.grade = grade; } abstract public char getGrade(); public static void main (String[] args) { System.out.println (GOOD.getGrade()); }
// (1)
}
Select the one correct answer. (a) Since the enum type declares an abstract method, the enum type must be declared as abstract. (b) The method call GOOD.getGrade() in (1) can be written without the enum type name. (c) An enum type cannot declare an abstract method. (d) An enum type can declare an abstract method, but each enum constant must provide an implementation. 3.11
What will be the result of compiling and running the following code? public enum TrafficLight { RED("Stop"), YELLOW("Caution"), GREEN("Go"); private String action; TrafficLight(String action) { this.action = action; } public static void main(String[] args) { TrafficLight green = new TrafficLight("Go"); System.out.println(GREEN.equals(green)); } }
Select the one correct answer. (a) (b) (c) (d)
The code will compile and print: true. The code will compile and print: false. The code will not compile, as an enum type cannot be instantiated. An enum type does not have the equals() method.
66
CHAPTER 3: DECLARATIONS
3.12
Given the following program: public enum Scale2 { GOOD('C') { public char getGrade() { return grade; } }, BETTER('B') { public char getGrade() { return grade; } }, BEST('A') { public char getGrade() { return grade; } }; private char grade; Scale2(char grade) { this.grade = grade; } // (1) INSERT CODE HERE public static void main (String[] args) { System.out.println(GOOD.getGrade()); } }
Which code, when inserted at (1), will make the program print C? Select the two correct answers. (a) public char getGrade() { return grade; } (b) public int getGrade() { return grade; } (c) abstract public int getGrade(); (d) abstract public char getGrade(); 3.13
Given the following program: enum Scale3 { GOOD(Grade.C), BETTER(Grade.B), BEST(Grade.A); enum Grade {A, B, C} private Grade grade; Scale3(Grade grade) { this.grade = grade; } public Grade getGrade() { return grade; } } public class Scale3Client { public static void main (String[] args) { System.out.println(/* (1) INSERT CODE HERE */); } }
Which code, when inserted at (1), will make the program print true? Select the four correct answers. (a) Scale3.GOOD.getGrade() != Scale3.Grade.C (b) Scale3.GOOD.getGrade().compareTo(Scale3.Grade.C) != 0 (c) Scale3.GOOD.getGrade().compareTo(Scale3.Grade.A) > 0
What will be the result of compiling and running the following code? public enum Scale5 { GOOD, BETTER, BEST; public char getGrade() { char grade = '\u0000'; switch(this){ case GOOD: grade = 'C'; break; case BETTER: grade = 'B'; break; case BEST: grade = 'A'; break; } return grade; } public static void main (String[] args) { System.out.println(GOOD.getGrade()); } }
Select the one correct answer. (a) The program will not compile, as the switch expression is not compatible with the case labels. (b) The program will not compile, as enum constants cannot be used as case labels. (c) The case labels must be qualified with the enum type name. (d) The program compiles, and when run, prints: C (e) The program compiles, and when run, prints: GOOD (f) None of the above. 3.15
Given the following code: package p1; enum March {LEFT, RIGHT} public class Defence { enum March {LEFT, RIGHT} static enum Military { INFANTRY, AIRFORCE; enum March {LEFT, RIGHT} } class Secret { enum March {LEFT, RIGHT} } static class Open { enum March {LEFT, RIGHT} } public static void declareWar() {
// (1) // (2)
// (3)
// (4)
// (5)
68
CHAPTER 3: DECLARATIONS enum March {LEFT, RIGHT} } public void declarePeace() { enum March {LEFT, RIGHT} }
// (6)
// (7)
}
Which enum declarations are not legal? Select the three correct answers. (a) (b) (c) (d) (e) (f) (g) 3.16
The enum declaration at (1) is not legal. The enum declaration at (2) is not legal. The enum declaration at (3) is not legal. The enum declaration at (4) is not legal. The enum declaration at (5) is not legal. The enum declaration at (6) is not legal. The enum declaration at (7) is not legal.
Given the following code: public enum Direction { EAST, WEST, NORTH, SOUTH; public static void main (String[] args) { // (1) INSERT LOOP HERE } }
Which loops, when inserted independently at (1), will give the following output: EAST WEST NORTH SOUTH
Select the three correct answers. (a) for (Direction d : Direction.values()) { System.out.println(d); }
(b) for (Direction d : Direction.values()) { System.out.println(d.name()); }
(c) for (String name : Direction.names()) { System.out.println(name); }
(d) for (Direction d : java.util.Arrays.asList(Direction.values())) { System.out.println(d); }
(e) for (Direction d : java.util.Arrays.asList(Direction.class)) { System.out.println(d); };
69
3.6: ARRAYS
3.17
What will be the result of compiling and running the following code? enum Rank { FIRST(20), SECOND(0), THIRD(8); Rank(int value) { System.out.print(value); } } public class EnumCreation { public static void main (String[] args) { System.out.println("\n" + Rank.values().length); } }
Select the one correct answer. (a) The program will compile and print: 3
(b) The program will compile and print: 2008 3
(c) The program will compile. When run, it will print: 2008
and throw an exception. (d) None of the above.
3.6 Arrays An array is a data structure that defines an indexed collection of a fixed number of homogeneous data elements. This means that all elements in the array have the same data type. A position in the array is indicated by a non-negative integer value called the index. An element at a given position in the array is accessed using the index. The size of an array is fixed and cannot be changed. In Java, arrays are objects. Arrays can be of primitive data types or reference types. In the former case, all elements in the array are of a specific primitive data type. In the latter case, all elements are references of a specific reference type. References in the array can then denote objects of this reference type or its subtypes. Each array object has a final field called length, which specifies the array size, i.e., the number of elements the array can accommodate. The first element is always at index 0 and the last element at index n-1, where n is the value of the length field in the array. Simple arrays are one-dimensional arrays, that is, a simple list of values. Since arrays can store reference values, the objects referenced can also be array objects. Thus, multi-dimensional arrays are implemented as array of arrays.
70
CHAPTER 3: DECLARATIONS
Passing array references as parameters is discussed in Section 3.7. Type conversions for array references on assignment and on method invocation are discussed in Section 7.7, p. 317.
Declaring Array Variables A one-dimensional array variable declaration has either the following syntax: <element type>[] <array name>; or <element type> <array name>[]; where <element type> can be a primitive data type or a reference type. The array variable <array name> has the type <element type>[]. Note that the array size is not specified. This means that the array variable <array name> can be assigned the reference value of an array of any length, as long as its elements have <element type>. It is important to understand that the declaration does not actually create an array. It only declares a reference that can refer to an array object. int anIntArray[], oneInteger; Pizza[] mediumPizzas, largePizzas;
The two declarations above declare anIntArray and mediumPizzas to be reference variables that can refer to arrays of int values and arrays of Pizza objects, respectively. The variable largePizzas can denote an array of pizzas, but the variable oneInteger cannot denote an array of int values—it is a simple variable of the type int . The [] notation can also be specified after a variable name to declare it as an array variable, but then it only applies to this variable. An array variable that is declared as a member of a class, but is not initialized to any array, will be initialized to the default reference value null. This default initialization does not apply to local reference variables and, therefore, does not apply to local array variables either (see Section 2.4, p. 33). This should not be confused with initialization of the elements of an array during array construction.
Constructing an Array An array can be constructed for a fixed number of elements of a specific type, using the new operator. The reference value of the resulting array can be assigned to an array variable of the corresponding type. The syntax of the array creation expression is shown on the right-hand side of the following assignment statement: <array name> = new <element type> [<array size>];
71
3.6: ARRAYS
The minimum value of <array size> is 0, in other words, zero-length arrays can be constructed in Java. If the array size is negative, a NegativeArraySizeException is thrown. Given the following array declarations: int anIntArray[], oneInteger; Pizza[] mediumPizzas, largePizzas;
the arrays can be constructed as follows: anIntArray = new int[10]; mediumPizzas = new Pizza[5]; largePizzas = new Pizza[3];
// array for 10 integers // array of 5 pizzas // array of 3 pizzas
The array declaration and construction can be combined. <element type1>[] <array name> = new <element type2>[<array size>]; Here the array type <element type2>[] must be assignable to the array type <element type1>[] (Section 7.7, p. 317). When the array is constructed, all its elements are initialized to the default value for <element type2>. This is true for both member and local arrays when they are constructed. In all examples below, the code constructs the array, and the array elements are implicitly initialized to their default value. For example, all elements of the array anIntArray get the value 0, and all element of the array mediumPizzas get the value null when the arrays are constructed. int[] anIntArray = new int[10];
// Default element value: 0.
Pizza[] mediumPizzas = new Pizza[5];
// Default element value: null.
// Pizza class extends Object class Object[] objArray = new Pizza[3];
// Default element value: null.
// Pizza class implements Eatable interface Eatable[] eatables = new Pizza[2];
// Default element value: null.
The value of the field length in each array is set to the number of elements specified during the construction of the array; for example, mediumPizzas.length has the value 5. Once an array has been constructed, its elements can also be explicitly initialized individually; for example, in a loop. The examples in the rest of this section make use of a loop to traverse the elements of an array for various purposes.
Initializing an Array Java provides the means of declaring, constructing, and explicitly initializing an array in one declaration statement: <element type>[] <array name> = { <array initialize list> };
72
CHAPTER 3: DECLARATIONS
This form of initialization applies to member as well as local arrays. The <array initialize list> is a comma-separated list of zero or more expressions. Such an array initialization block results in the construction and initialization of the array. int[] anIntArray = {13, 49, 267, 15, 215};
The array anIntArray is declared as an array of ints. It is constructed to hold 5 elements (equal to the length of the list of expressions in the block), where the first element is initialized to the value of the first expression (13), the second element to the value of the second expression (49), and so on. // Pizza class extends Object class Object[] objArray = { new Pizza(), new Pizza(), null };
The array objArray is declared as an array of the Object class, constructed to hold three elements. The initialization code sets the first two elements of the array to refer to two Pizza objects, while the last element is initialized to the null reference. Note that the number of objects created in the above declaration statement is actually three: the array object with three references and the two Pizza objects. The expressions in the <array initialize list> are evaluated from left to right, and the array name obviously cannot occur in any of the expressions in the list. In the examples above, the <array initialize list> is terminated by the right curly bracket, }, of the block. The list can also be legally terminated by a comma. The following array has length two, and not three: Topping[] pizzaToppings = { new Topping("cheese"), new Topping("tomato"), };
The declaration statement at (1) in the following code defines an array of four String objects, while the declaration statement at (2) shows that a String object is not the same as an array of char. // Array with 4 String objects: String[] pets = {"crocodiles", "elephants", "crocophants", "elediles"}; // (1) // Array of 3 characters: char[] charArray = {'a', 'h', 'a'};
// (2) Not the same as "aha".
Using an Array The array object is referenced by the array name, but individual array elements are accessed by specifying an index with the [] operator. The array element access expression has the following syntax: <array name> [] Each individual element is treated as a simple variable of the element type. The index is specified by the , which can be any expression that evaluates to a non-negative int value. Since the lower bound of an array is always 0, the upper bound is one less than the array size, that is, <array name>.length-1. The ith element in the array has index (i-1). At runtime, the index value is automatically checked to ensure that it is within the array index bounds. If the index value
73
3.6: ARRAYS
is less than 0, or greater than or equal to <array name>.length, an ArrayIndexOutOfBoundsException is thrown. A program can either check the index explicitly or catch the exception (see Section 6.5, p. 235), but an illegal index is typically an indication of a program bug. In the array element access expression, the <array name> can be any expression that returns a reference to an array. For example, the following expression returns the character 'H' at index 1 in the character array returned by a call to the toCharArray() method of the String class: "AHA".toCharArray()[1]. The array operator [] is used to declare array types (Topping[]), specify array size (new Topping[3]), and to access array elements (toppings[1]). This operator is not used when the array reference is manipulated, for example, in an array reference assignment (see Section 7.9, p. 320), or when the array reference is passed as an actual parameter in a method call (see Section 3.7, p. 86). Example 3.7 shows traversal of arrays. The loop at (3) initializes the local array trialArray declared at (2) five times with pseudo-random numbers (from 0.0 to 100.0), by calling the method randomize() declared at (5). The minimum value in the array is found by calling the method findMinimum() declared at (6), and is stored in the array storeMinimum declared at (1). The loop at (4) prints the minimum values from the trials. The start value of the loop variable is initially set to 0. The loop condition tests whether the loop variable is less than the length of the array; this guarantees that the index will not go out of bounds. Example 3.7
Using Arrays public class Trials { public static void main(String[] args) { // Declare and construct the local arrays: double[] storeMinimum = new double[5]; double[] trialArray = new double[15]; for (int i = 0; i < storeMinimum.length; ++i) {
// (1) // (2) // (3)
// Initialize the array. randomize(trialArray); // Find and store the minimum value. storeMinimum[i] = findMinimum(trialArray); } // Print the minimum values: for (int i = 0; i < storeMinimum.length; ++i) System.out.printf("%.4f%n", storeMinimum[i]);
(4)
public static void randomize(double[] valArray) { for (int i = 0; i < valArray.length; ++i) valArray[i] = Math.random() * 100.0; }
// (5)
}
74
CHAPTER 3: DECLARATIONS public static double findMinimum(double[] valArray) { // Assume the array has at least one element. double minValue = valArray[0]; for (int i = 1; i < valArray.length; ++i) minValue = Math.min(minValue, valArray[i]); return minValue; }
// (6)
}
Possible output from the program: 6.9330 2.7819 6.7427 18.0849 26.2462
Anonymous Arrays As shown earlier in this section, the following declaration statement <element type1>[] <array name> = new <element type2>[<array size>]; // (1) int[] intArray = new int[5];
can be used to construct arrays using an array creation expression. The size of the array is specified in the array creation expression, which creates the array and initializes the array elements to their default values. On the other hand, the following declaration statement <element type>[] <array name> = { <array initialize list> };
// (2)
int[] intArray = {3, 5, 2, 8, 6};
both creates the array and initializes the array elements to specific values given in the array initializer block. However, the array initialization block is not an expression. Java has another array creation expression, called anonymous array, which allows the concept of the array creation expression from (1) and the array initializer block from (2) to be combined, to create and initialize an array object: new <element type>[] { <array initialize list> } new int[] {3, 5, 2, 8, 6}
The construct has enough information to create a nameless array of a specific type. Neither the name of the array nor the size of the array is specified. The construct returns the reference value of the newly-created array, which can be assigned to references and passed as argument in method calls. In particular, the following two examples of declaration statements are equivalent. int[] intArray = {3, 5, 2, 8, 6};
In (1), an array initializer block is used to create and initialize the elements. In (2), an anonymous array expression is used. It is tempting to use the array initialization block as an expression; for example, in an assignment statement, as a short cut for assigning values to array elements in one go. However, this is illegal—instead, an anonymous array expression should be used. int[] daysInMonth; daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // Not ok. daysInMonth = new int[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // ok.
The concept of anonymous arrays is similar to that of anonymous classes (see Section 8.5, p. 377): they both combine the definition and the creation of objects into one operation. In Example 3.8, an anonymous array is constructed at (1), and passed as a parameter to the static method findMinimum() defined at (2). Note that no array name or array size is specified for the anonymous array. Example 3.8
Using Anonymous Arrays public class AnonArray { public static void main(String[] args) { System.out.println("Minimum value: " + findMinimum(new int[] {3, 5, 2, 8, 6})); } public static int findMinimum(int[] dataSeq) { // Assume the array has at least one element. int min = dataSeq[0]; for (int index = 1; index < dataSeq.length; ++index) if (dataSeq[index] < min) min = dataSeq[index]; return min; }
// (1)
// (2)
}
Output from the program: Minimum value: 2
Multidimensional Arrays Since an array element can be an object reference and arrays are objects, array elements can themselves reference other arrays. In Java, an array of arrays can be defined as follows: <element type>[][]...[] <array name>; or
76
CHAPTER 3: DECLARATIONS
<element type> <array name>[][]...[]; In fact, the sequence of square bracket pairs, [], indicating the number of dimensions, can be distributed as a postfix to both the element type and the array name. Arrays of arrays are also often called multidimensional arrays. The following declarations are all equivalent: int[][] mXnArray; int[] mXnArray[]; int mXnArray[][];
It is customary to combine the declaration with the construction of the multidimensional array. int[][] mXnArray = new int[4][5];
// 4 x 5 matrix of ints
The previous declaration constructs an array mXnArray of four elements, where each element is an array (row) of 5 int values. The concept of rows and columns is often used to describe the dimensions of a 2-dimensional array, which is often called a matrix. However, such an interpretation is not dictated by the Java language. Each row in the previous matrix is denoted by mXnArray[i], where 0 d i 4. Each element in the ith row, mXnArray[i], is accessed by mXnArray[i][j], where 0 d j 5. The number of rows is given by mXnArray.length, in this case 4, and the number of values in the ith row is given by mXnArray[i].length, in this case 5 for all the rows, where 0 d i 4. Multidimensional arrays can also be constructed and explicitly initialized using array initializer blocks discussed for simple arrays. Note that each row is an array which uses an array initializer block to specify its values: double[][] identityMatrix = { {1.0, 0.0, 0.0, 0.0 }, // 1. row {0.0, 1.0, 0.0, 0.0 }, // 2. row {0.0, 0.0, 1.0, 0.0 }, // 3. row {0.0, 0.0, 0.0, 1.0 } // 4. row }; // 4 x 4 Floating-point matrix
Arrays in a multidimensional array need not have the same length, and are often called ragged arrays. The array of arrays pizzaGalore in the code below will have five rows, the first four rows have different lengths but the fifth row is left unconstructed. Pizza[][] pizzaGalore = { { new Pizza(), null, new Pizza() }, { null, new Pizza()}, new Pizza[1], {}, null };
// // // // //
1. 2. 3. 4. 5.
row row row row row
is is is is is
an array of 3 elements. an array of 2 elements. an array of 1 element. an array of 0 elements. not constructed.
When constructing multidimensional arrays with the new operator, the length of the deeply nested arrays may be omitted. In which case, these arrays are left unconstructed. For example, an array of arrays to represent a room on a floor in a
77
3.6: ARRAYS
hotel on a street in a city can have the type HotelRoom[][][][]. From left to right, the square brackets represent indices for street, hotel, floor, and room, respectively. This 4-dimensional array of arrays can be constructed piecemeal, starting with the leftmost dimension and proceeding to the rightmost. HotelRoom[][][][] rooms = new HotelRoom[10][5][][];
// Just streets and hotels.
The above declaration constructs the array of arrays rooms partially with ten streets, where each street has five hotels. Floors and rooms can be added to a particular hotel on a particular street: rooms[0][0] = new HotelRoom[3][]; // 3 floors in 1st. hotel on 1st. street. rooms[0][0][0] = new HotelRoom[8]; // 8 rooms on 1st. floor in this hotel. rooms[0][0][0][0] = new HotelRoom(); // Initializes 1st. room on this floor.
The code below constructs an array of arrays matrix, where the first row has one element, the second row has two elements, and the third row has three elements. Note that the outer array is constructed first. The second dimension is constructed in a loop that constructs the array in each row. The elements in the multidimensional array will be implicitly initialized to the default double value (0.0D). In Figure 3.2, the array of arrays matrix is depicted after the elements have been explicitly initialized. double[][] matrix = new double[3][];
// No. of rows.
for (int i = 0; i < matrix.length; ++i) matrix[i] = new double[i + 1];
// Construct a row.
Two other ways of initializing such an array of arrays are shown below. The first one uses array initializer blocks, and the second one uses an anonymous array of arrays. double[][] matrix2 = { {0.0}, {0.0, 0.0}, {0.0, 0.0, 0.0} }
// // // //
Using array initializer blocks. 1. row 2. row 3. row
double[][] matrix3 = new double[][] { // Using an anonymous array of arrays. {0.0}, // 1. row {0.0, 0.0}, // 2. row {0.0, 0.0, 0.0} // 3. row }
The type of the variable matrix is double[][], i.e., a two-dimensional array of double values. The type of the variable matrix[i] (where 0 d i matrix.length) is double[], i.e., a one-dimensional array of double values. The type of the variable matrix[i][j] (where 0 dimatrix.length and 0 djmatrix[i].length) is double, i.e., a simple variable of type double. Nested loops are a natural match for manipulating multidimensional arrays. In Example 3.9, a rectangular 4 u 3 int matrix is declared and constructed at (1). The program finds the minimum value in the matrix. The outer loop at (2) traverses the rows (mXnArray[i], where 0 dimXnArray.length), and the inner loop at (3) traverses
78
CHAPTER 3: DECLARATIONS Figure 3.2 Array of Arrays
the elements in each row in turn (mXnArray[i][j], where 0 djmXnArray[i].length). The outer loop is executed mXnArray.length times, or 4 times, and the inner loop is executed (mXnArray.length) u (mXnArray[i].length), or 12 times, since all rows have the same length, 3. The for(:) loop also provides a safe and convenient way of traversing an array, and ample examples are provided in Section 6.3, p. 220. The Java standard library also provides the class java.util.Arrays that contains various static methods for manipulating arrays, such as sorting and searching (see Section 15.11, p. 842). Example 3.9
Using Multidimensional Arrays public class MultiArrays { public static void main(String[] args) { // Declare and construct the M X N matrix. int[][] mXnArray = { {16, 7, 12}, // 1. row { 9, 20, 18}, // 2. row {14, 11, 5}, // 3. row { 8, 5, 10} // 4. row }; // 4 x 3 int matrix
// (1)
// Find the minimum value in a M X N matrix: int min = mXnArray[0][0]; for (int i = 0; i < mXnArray.length; ++i) // (2) // Find min in mXnArray[i], i.e. in the row given by index i: for (int j = 0; j < mXnArray[i].length; ++j) // (3) min = Math.min(min, mXnArray[i][j]); System.out.println("Minimum value: " + min); } }
79
3.6: ARRAYS
Output from the program: Minimum value: 5
Review Questions 3.18
Given the following declaration, which expression returns the size of the array, assuming the array has been initialized? int[] array;
Select the one correct answer. (a) array[].length() (b) array.length() (c) array[].length (d) array.length (e) array[].size() (f) array.size() 3.19
Is it possible to create arrays of length zero? Select the one correct answer. (a) (b) (c) (d)
Yes, you can create arrays of any type with length zero. Yes, but only for primitive data types. Yes, but only for arrays of reference types. No, you cannot create zero-length arrays, but the main() method may be passed a zero-length array of Strings when no program arguments are specified. (e) No, it is not possible to create arrays of length zero in Java. 3.20
Which one of the following array declaration statements is not legal? Select the one correct answer. (a) int []a[] = new int [4][4]; (b) int a[][] = new int [4][4]; (c) int a[][] = new int [][4]; (d) int []a[] = new int [4][]; (e) int [][]a = new int [4][4];
3.21
Which of these array declaration statements are not legal? Select the two correct answers. (a) int[] i[] = { { 1, 2 }, { 1 }, {}, { 1, 2, 3 } }; (b) int i[] = new int[2] {1, 2}; (c) int i[][] = new int[][] { {1, 2, 3}, {4, 5, 6} }; (d) int i[][] = { { 1, 2 }, new int[ 2 ] };
80
CHAPTER 3: DECLARATIONS
(e) int i[4] = { 1, 2, 3, 4 }; 3.22
What would be the result of compiling and running the following program? // Filename: MyClass.java class MyClass { public static void main(String[] args) { int size = 20; int[] arr = new int[ size ]; for (int i = 0; i < size; ++i) { System.out.println(arr[i]); } } }
Select the one correct answer. (a) The code will not compile, because the array type int[] is incorrect. (b) The program will compile, but will throw an ArrayIndexOutOfBoundsException when run. (c) The program will compile and run without error, but will produce no output. (d) The program will compile and run without error, and will print the numbers 0 through 19. (e) The program will compile and run without error, and will print 0 twenty times. (f) The program will compile and run without error, and will print null twenty times. 3.23
What would be the result of compiling and running the following program? public class DefaultValuesTest { int[] ia = new int[1]; boolean b; int i; Object o; public static void main(String[] args) { DefaultValuesTest instance = new DefaultValuesTest(); instance.print(); } public void print() { System.out.println(ia[0] + " " + b + " " + i + " " + o); } }
Select the one correct answer. (a) (b) (c) (d) (e) (f)
The program will fail to compile because of uninitialized variables. The program will throw a java.lang.NullPointerException when run. The program will print: 0 false NaN null. The program will print: 0 false 0 null. The program will print: null 0 0 null. The program will print: null false 0 null.
81
3.7: PARAMETER PASSING
3.7 Parameter Passing Objects communicate by calling methods on each other. A method call is used to invoke a method on an object. Parameters in the method call provide one way of exchanging information between the caller object and the callee object (which need not be different). Defining methods is discussed in Section 3.3, p. 44. Invoking static methods on classes is discussed in Section 4.10, p. 147. The syntax of a method call can be any one of the following: