A Programmer's Guide To Java SE 8 Oracle Certified Associate (OCA)
A%20Programmer's%20Guide%20to%20Java%20SE%208%20Oracle%20Certified%20Associate
User Manual: Pdf
Open the PDF directly: View PDF .
Page Count: 2629
Download | |
Open PDF In Browser | View PDF |
WOW! eBook www.wowebook.org About This E-Book EPUB is an open, industry-standard format for e-books. However, support for EPUB and its many features varies across reading devices and applications. Use your device or app settings to customize the presentation to your liking. Settings that you can customize often include font, font size, single or double column, landscape or portrait mode, and figures that you can click or tap to enlarge. For additional information about the settings and features on your reading device or app, visit the device manufacturer’s Web site. Many titles include programming code or configuration examples. To optimize the presentation of these elements, view the e-book in single-column, landscape mode and adjust the font size to the smallest setting. In addition to presenting code and configurations in the reflowable text format, we have included images of the code that mimic the presentation found in the print book; therefore, where the reflowable format may compromise the presentation of the code listing, you will see a “Click here to view code image” link. Click the link to view the print-fidelity code image. To return to the previous page viewed, click the Back button on your device or app. WOW! eBook www.wowebook.org A Programmer’s Guide to Java® SE 8 Oracle Certified Associate (OCA) A Comprehensive Primer Khalid A. Mughal Rolf W. Rasmussen Boston • Columbus • Indianapolis • New York • San Francisco • Amsterdam • Cape Town Dubai • London • Madrid • Milan • Munich • Paris • Montreal • Toronto • Delhi • Mexico City São Paulo • Sydney • Hong Kong • Seoul • Singapore • Taipei • Tokyo WOW! eBook www.wowebook.org 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. For information about buying this title in bulk quantities, or for special sales opportunities (which may include electronic versions; custom cover designs; and content particular to your business, training goals, marketing focus, or branding interests), please contact our corporate sales department at corpsales@pearsoned.com or (800) 382-3419. For government sales inquiries, please contact governmentsales@pearsoned.com. For questions about sales outside the U.S., please contact intlcs@pearson.com. Visit us on the Web: informit.com/aw Library of Congress Control Number: 2016937073 Copyright © 2017 Pearson Education, Inc. All rights reserved. Printed in the United States of America. This publication is protected by copyright, and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise. For information regarding permissions, request forms and the appropriate contacts within the Pearson Education Global Rights & Permissions Department, please visit www.pearsoned.com/permissions/. ISBN-13: 978-0-13-293021-5 ISBN-10: 0-13-293021-8 Text printed in the United States on recycled paper at RR Donnelley in Crawfordsville, Indiana. First printing, July 2016 WOW! eBook www.wowebook.org 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. WOW! eBook www.wowebook.org Contents Overview Figures Tables Examples Foreword Preface 1 Basics of Java Programming 2 Language Fundamentals 3 Declarations 4 Access Control 5 Operators and Expressions 6 Control Flow 7 Object-Oriented Programming 8 Fundamental Classes 9 Object Lifetime 10 The ArrayListClass and Lambda Expressions 11 Date and Time A Taking the Java SE 8 Programmer I Exam B Exam Topics: Java SE 8 Programmer I C Annotated Answers to Review Questions D Solutions to Programming Exercises E Mock Exam: Java SE 8 Programmer I F Annotated Answers to Mock Exam I Index WOW! eBook www.wowebook.org Contents Figures Tables Examples Foreword Preface 1 Basics of Java Programming 1.1 Introduction 1.2 Classes Declaring Members: Fields and Methods 1.3 Objects Class Instantiation, Reference Values, and References Object Aliases 1.4 Instance Members Invoking Methods 1.5 Static Members 1.6 Inheritance 1.7 Associations: Aggregation and Composition 1.8 Tenets of Java Review Questions 1.9 Java Programs 1.10 Sample Java Application Essential Elements of a Java Application Compiling and Running an Application 1.11 Program Output Formatted Output 1.12 The Java Ecosystem Object-Oriented Paradigm Interpreted: The JVM Architecture-Neutral and Portable Bytecode Simplicity WOW! eBook www.wowebook.org Dynamic and Distributed Robust and Secure High Performance and Multithreaded Review Questions Chapter Summary Programming Exercise 2 Language Fundamentals 2.1 Basic Language Elements Lexical Tokens Identifiers Keywords Separators Literals Integer Literals Floating-Point Literals Underscores in Numerical Literals Boolean Literals Character Literals String Literals Whitespace Comments Review Questions 2.2 Primitive Data Types The Integer Types The char Type The Floating-Point Types The boolean Type Review Questions 2.3 Variable Declarations Declaring and Initializing Variables Reference Variables 2.4 Initial Values for Variables WOW! eBook www.wowebook.org Default Values for Fields Initializing Local Variables of Primitive Data Types Initializing Local Reference Variables Lifetime of Variables Review Questions Chapter Summary Programming Exercise 3 Declarations 3.1 Class Declarations 3.2 Method Declarations Statements Instance Methods and the Object Reference this Method Overloading 3.3 Constructors The Default Constructor Overloaded Constructors Review Questions 3.4 Arrays Declaring Array Variables Constructing an Array Initializing an Array Using an Array Anonymous Arrays Multidimensional Arrays Sorting Arrays Searching Arrays Review Questions 3.5 Parameter Passing Passing Primitive Data Values Passing Reference Values Passing Arrays Array Elements as Actual Parameters WOW! eBook www.wowebook.org final Parameters 3.6 Variable Arity Methods Calling a Variable Arity Method Variable Arity and Fixed Arity Method Calls 3.7 The main() Method Program Arguments 3.8 Enumerated Types Declaring Type-safe Enums Using Type-safe Enums Selected Methods for Enum Types Review Questions Chapter Summary Programming Exercise 4 Access Control 4.1 Java Source File Structure 4.2 Packages Defining Packages Using Packages Compiling Code into Packages Running Code from Packages 4.3 Searching for Classes Review Questions 4.4 Scope Rules Class Scope for Members Block Scope for Local Variables 4.5 Accessibility Modifiers for Top-Level Type Declarations 4.6 Non-Accessibility Modifiers for Classes abstract Classes final Classes Review Questions 4.7 Member Accessibility Modifiers public Members WOW! eBook www.wowebook.org protected Members Default Accessibility for Members private Members Review Questions 4.8 Non-Accessibility Modifiers for Members static Members final Members abstract Methods synchronized Methods native Methods transient Fields volatile Fields Review Questions Chapter Summary Programming Exercise 5 Operators and Expressions 5.1 Conversions Widening and Narrowing Primitive Conversions Widening and Narrowing Reference Conversions Boxing and Unboxing Conversions Other Conversions 5.2 Type Conversion Contexts Assignment Context Method Invocation Context Casting Context of the Unary Type Cast Operator: (type) Numeric Promotion Context 5.3 Precedence and Associativity Rules for Operators 5.4 Evaluation Order of Operands Left-Hand Operand Evaluation First Operand Evaluation before Operation Execution Left-to-Right Evaluation of Argument Lists 5.5 Representing Integers WOW! eBook www.wowebook.org Calculating Two’s Complement Converting Binary Numbers to Decimals Converting Decimals to Binary Numbers Relationships among Binary, Octal, and Hexadecimal Numbers 5.6 The Simple Assignment Operator = Assigning Primitive Values Assigning References Multiple Assignments Type Conversions in an Assignment Context Review Questions 5.7 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 5.8 The Binary String Concatenation Operator + 5.9 Variable Increment and Decrement Operators: ++, -The Increment Operator ++ The Decrement Operator -Review Questions 5.10 Boolean Expressions 5.11 Relational Operators: <, <=, >, >= 5.12 Equality Primitive Data Value Equality: ==, != Object Reference Equality: ==, != Object Value Equality WOW! eBook www.wowebook.org 5.13 Boolean Logical Operators: !, ^, &, | Operand Evaluation for Boolean Logical Operators Boolean Logical Compound Assignment Operators: &=, ^=, |= 5.14 Conditional Operators: &&, || Short-Circuit Evaluation 5.15 Integer Bitwise Operators: ~, &, |, ^ Bitwise Compound Assignment Operators: &=, ^=, |= Review Questions 5.16 The Conditional Operator: ?: 5.17 Other Operators: new, [], instanceof, -> Review Questions Chapter Summary Programming Exercise 6 Control Flow 6.1 Overview of Control Flow Statements 6.2 Selection Statements The Simple if Statement The if-else Statement The switch Statement Review Questions 6.3 Iteration Statements The while Statement The do-while Statement The for(;;) Statement The for(:) Statement 6.4 Transfer Statements Labeled Statements The break Statement The continue Statement The return Statement Review Questions WOW! eBook www.wowebook.org 6.5 Stack-Based Execution and Exception Propagation 6.6 Exception Types The Exception Class The RuntimeException Class The Error Class Checked and Unchecked Exceptions Defining Customized Exceptions 6.7 Exception Handling: try, catch, and finally The try Block The catch Clause The finally Clause 6.8 The throw Statement 6.9 The throws Clause Overriding the throws Clause 6.10 Advantages of Exception Handling Review Questions Chapter Summary Programming Exercises 7 Object-Oriented Programming 7.1 Single Implementation Inheritance Relationships: is-a and has-a The Supertype–Subtype Relationship 7.2 Overriding Methods Instance Method Overriding Covariant return in Overriding Methods Overriding versus Overloading 7.3 Hiding Members Field Hiding Static Method Hiding 7.4 The Object Reference super Review Questions WOW! eBook www.wowebook.org 7.5 Chaining Constructors Using this() and super() The this() Constructor Call The super() Constructor Call Review Questions 7.6 Interfaces Defining Interfaces Abstract Methods in Interfaces Implementing Interfaces Extending Interfaces Interface References Default Methods in Interfaces Static Methods in Interfaces Constants in Interfaces Review Questions 7.7 Arrays and Subtyping Arrays and Subtype Covariance Array Store Check 7.8 Reference Values and Conversions 7.9 Reference Value Assignment Conversions 7.10 Method Invocation Conversions Involving References Overloaded Method Resolution 7.11 Reference Casting and the instanceof Operator The Cast Operator The instanceof Operator Review Questions 7.12 Polymorphism and Dynamic Method Lookup 7.13 Inheritance versus Aggregation 7.14 Basic Concepts in Object-Oriented Design Encapsulation Cohesion Coupling Review Questions WOW! eBook www.wowebook.org Chapter Summary Programming Exercises 8 Fundamental Classes 8.1 Overview of the java.lang Package 8.2 The Object Class Review Questions 8.3 The Wrapper Classes Common Wrapper Class Constructors Common Wrapper Class Utility Methods Numeric Wrapper Classes The Character Class The Boolean Class Review Questions 8.4 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 Joining of CharSequence Objects Searching for Characters and Substrings Extracting Substrings Converting Primitive Values and Objects to Strings Formatted Strings Review Questions 8.5 The StringBuilder and StringBuffer Classes Thread-Safety Mutability Constructing String Builders Reading and Changing Characters in String Builders WOW! eBook www.wowebook.org Constructing Strings from String Builders Appending, Inserting, and Deleting Characters in String Builders Controlling String Builder Capacity Review Questions Chapter Summary Programming Exercises 9 Object Lifetime 9.1 Garbage Collection 9.2 Reachable Objects 9.3 Facilitating Garbage Collection 9.4 Object Finalization 9.5 Finalizer Chaining 9.6 Invoking Garbage Collection Programmatically Review Questions 9.7 Initializers 9.8 Field Initializer Expressions Declaration Order of Initializer Expressions 9.9 Static Initializer Blocks Declaration Order of Static Initializers 9.10 Instance Initializer Blocks Declaration Order of Instance Initializers 9.11 Constructing Initial Object State Review Questions Chapter Summary 10 The ArrayList Class and Lambda Expressions 10.1 The ArrayList Class Lists Declaring References and Constructing ArrayLists Modifying an ArrayList Querying an ArrayList Traversing an ArrayList Converting an ArrayList to an Array WOW! eBook www.wowebook.org Sorting an ArrayList Arrays versus ArrayList Review Questions 10.2 Lambda Expressions Behavior Parameterization Functional Interfaces Defining Lambda Expressions Type Checking and Execution of Lambda Expressions Filtering Revisited: The Predicate Functional Interface Review Questions Chapter Summary Programming Exercise 11 Date and Time 11.1 Basic Date and Time Concepts 11.2 Working with Temporal Classes Creating Temporal Objects Querying Temporal Objects Comparing Temporal Objects Creating Modified Copies of Temporal Objects Temporal Arithmetic 11.3 Working with Periods Creating Periods Querying Periods Creating Modified Copies of Periods More Temporal Arithmetic Review Questions 11.4 Formatting and Parsing Default Formatters Predefined Formatters Localized Formatters Customized Formatters Review Questions WOW! eBook www.wowebook.org Chapter Summary Programming Exercise A Taking the Java SE 8 Programmer I Exam A.1 Preparing for the Exam A.2 Registering for the Exam Contact Information Obtaining an Exam Voucher Signing Up for the Test After Taking the Exam A.3 How the Exam Is Conducted The Testing Locations Utilizing the Allotted Time The Exam Program The Exam Result A.4 The Questions Assumptions about the Exam Questions Types of Questions Asked Types of Answers Expected Topics Covered by the Questions B Exam Topics: Java SE 8 Programmer I C Annotated Answers to Review Questions D Solutions to Programming Exercises E Mock Exam: Java SE 8 Programmer I F Annotated Answers to Mock Exam I Index WOW! eBook www.wowebook.org Figures 1.1 UML Notation for Classes 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 Associations 1.8 Class Diagram Depicting Composition 2.1 Primitive Data Types in Java 3.1 Array of Arrays 3.2 Parameter Passing: Primitive Data Values 3.3 Parameter Passing: Reference Values 3.4 Parameter Passing: Arrays 4.1 Java Source File Structure 4.2 Package Hierarchy 4.3 File Hierarchy 4.4 Searching for Classes 4.5 Block Scope 4.6 Public Accessibility for Members 4.7 Protected Accessibility for Members 4.8 Default Accessibility for Members 4.9 Private Accessibility for Members 5.1 Widening Primitive Conversions 5.2 Converting among Binary, Octal, and Hexadecimal Numbers 5.3 Overflow and Underflow in Floating-Point Arithmetic 5.4 Numeric Promotion in Arithmetic Expressions 6.1 Activity Diagram for if Statements 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 WOW! eBook www.wowebook.org 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) 7.1 Inheritance Hierarchy 7.2 Inheritance Hierarchy for Example 7.2 and Example 7.3 7.3 Inheritance Hierarchies 7.4 Inheritance Relationships for Interface Constants 7.5 Reference Type Hierarchy: Arrays and Subtype Covariance 7.6 Type Hierarchy That Illustrates Polymorphism 7.7 Implementing Data Structures by Inheritance and Aggregation 8.1 Partial Inheritance Hierarchy in the java.lang Package 8.2 Converting Values among Primitive, Wrapper, and String Types 9.1 Memory Organization at Runtime 10.1 Partial ArrayList Inheritance Hierarchy WOW! eBook www.wowebook.org Tables 1.1 Terminology for Class Members 1.2 Format Specifier Examples 2.1 Keywords in Java 2.2 Reserved Literals in Java 2.3 Reserved Keywords Not Currently in Use 2.4 Separators in Java 2.5 Examples of Literals 2.6 Examples of Decimal, Binary, Octal, and Hexadecimal Literals 2.7 Examples of Character Literals 2.8 Escape Sequences 2.9 Examples of Escape Sequence \ddd 2.10 Range of Integer Values 2.11 Range of Character Values 2.12 Range of Floating-Point Values 2.13 Boolean Values 2.14 Summary of Primitive Data Types 2.15 Default Values 3.1 Parameter Passing by Value 4.1 Accessing Members within a Class 4.2 Summary of Accessibility Modifiers for Top-Level Types 4.3 Summary of Non-Accessibility Modifiers for Classes 4.4 Summary of Accessibility Modifiers for Members 4.5 Summary of Non-Accessibility Modifiers for Members 5.1 Selected Conversion Contexts and Conversion Categories 5.2 Operator Summary 5.3 Representing Signed byte Values Using Two’s Complement 5.4 Examples of Truncated Values 5.5 Arithmetic Operators 5.6 Examples of Arithmetic Expression Evaluation 5.7 Arithmetic Compound Assignment Operators WOW! eBook www.wowebook.org 5.8 Relational Operators 5.9 Primitive Data Value Equality Operators 5.10 Reference Equality Operators 5.11 Truth Values for Boolean Logical Operators 5.12 Boolean Logical Compound Assignment Operators 5.13 Conditional Operators 5.14 Truth Values for Conditional Operators 5.15 Integer Bitwise Operators 5.16 Result Table for Bitwise Operators 5.17 Examples of Bitwise Operations 5.18 Bitwise Compound Assignment Operators 6.1 The return Statement 7.1 Overriding versus Overloading 7.2 Same Signature for Subclass and Superclass Method 7.3 Types and Values 10.1 Summary of Arrays versus ArrayLists 10.2 Selected Functional Interfaces from the java.util.function Package 11.1 Selected Common Method Prefix of the Temporal Classes 11.2 Selected ISO-Based Predefined Formatters for Date and Time 11.3 Format Styles for Date and Time 11.4 Combination of Format Styles and Localized Formatters 11.5 Selected Date/Time Pattern Letters WOW! eBook www.wowebook.org Examples 1.1 Basic Elements of a Class Declaration 1.2 Static Members in Class Declaration 1.3 Defining a Subclass 1.4 An Application 1.5 Formatted Output 2.1 Default Values for Fields 2.2 Flagging Uninitialized Local Variables of Primitive Data Types 2.3 Flagging Uninitialized Local Reference Variables 3.1 Using the this Reference 3.2 Namespaces 3.3 Using Arrays 3.4 Using Anonymous Arrays 3.5 Using Multidimensional Arrays 3.6 Passing Primitive Values 3.7 Passing Reference Values 3.8 Passing Arrays 3.9 Array Elements as Primitive Data Values 3.10 Array Elements as Reference Values 3.11 Calling a Variable Arity Method 3.12 Passing Program Arguments 3.13 Using Enums 4.1 Defining Packages and Using Type Import 4.2 Single Static Import 4.3 Avoiding the Interface Constant Antipattern 4.4 Importing Enum Constants 4.5 Shadowing Static Import 4.6 Conflict in Importing Static Method with the Same Signature 4.7 Class Scope 4.8 Accessibility Modifiers for Classes and Interfaces 4.9 Abstract Classes WOW! eBook www.wowebook.org 4.10 Public Accessibility of Members 4.11 Accessing Static Members 4.12 Using final Modifier 4.13 Synchronized Methods 5.1 Evaluation Order of Operands and Arguments 5.2 Numeric Promotion in Arithmetic Expressions 5.3 Short-Circuit Evaluation Involving Conditional Operators 5.4 Bitwise Operations 6.1 Fall-Through in a switch Statement 6.2 Using break in a switch Statement 6.3 Nested switch Statement 6.4 Strings in switch Statement 6.5 Enums in switch Statement 6.6 The break Statement 6.7 Labeled break Statement 6.8 continue Statement 6.9 Labeled continue Statement 6.10 The return Statement 6.11 Method Execution 6.12 The try-catch Construct 6.13 Exception Propagation 6.14 The try-catch-finally Construct 6.15 The try-finally Construct 6.16 The finally Clause and the return Statement 6.17 Throwing Exceptions 6.18 The throws Clause 7.1 Extending Classes: Inheritance and Accessibility 7.2 Overriding, Overloading, and Hiding 7.3 Using the super Keyword 7.4 Constructor Overloading 7.5 The this() Constructor Call WOW! eBook www.wowebook.org 7.6 The super() Constructor Call 7.7 Implementing Interfaces 7.8 Default Methods in Interfaces 7.9 Default Methods and Multiple Inheritance 7.10 Static Methods in Interfaces 7.11 Constants in Interfaces 7.12 Inheriting Constants in Interfaces 7.13 Assigning and Passing Reference Values 7.14 Choosing the Most Specific Method (Simple Case) 7.15 Overloaded Method Resolution 7.16 The instanceof and Cast Operators 7.17 Using the instanceof Operator 7.18 Polymorphism and Dynamic Method Lookup 7.19 Implementing Data Structures by Inheritance and Aggregation 8.1 Methods in the Object Class 8.2 String Representation of Integers 8.3 String Construction and Equality 8.4 Reading Characters from a String 9.1 Garbage Collection Eligibility 9.2 Using Finalizers 9.3 Invoking Garbage Collection 9.4 Initializer Expression Order and Method Calls 9.5 Static Initializers and Forward References 9.6 Instance Initializers and Forward References 9.7 Object State Construction 9.8 Initialization Anomaly under Object State Construction 10.1 Using an ArrayList 10.2 Implementing Customized Methods for Filtering an ArrayList 10.3 Implementing an Interface for Filtering an ArrayList 10.4 User-Defined Functional Interface for Filtering an ArrayList 10.5 Using the Predicate Functional Interface for Filtering an ArrayList 10.6 Accessing Members in an Enclosing Object WOW! eBook www.wowebook.org 10.7 Accessing Local Variables in an Enclosing Method 10.8 Filtering an ArrayList 11.1 Creating Temporal Objects 11.2 Using Temporal Objects 11.3 Temporal Arithmetic 11.4 Period-Based Loop 11.5 More Temporal Arithmetic 11.6 Using Default Date and Time Formatters 11.7 Using Predefined Format Styles with Time-Based Values 11.8 Using Predefined Format Styles with Date-Based Values 11.9 Using Predefined Format Styles with Date and Time-Based Values 11.10 Formatting and Parsing with Letter Patterns 11.11 Formatting with Date/Time Letter Patterns WOW! eBook www.wowebook.org Foreword Java is now over twenty years old and the current release, JDK 8, despite its name, is really the eleventh significant release of the platform. Whilst staying true to the original ideas of the platform, there have been numerous developments adding a variety of features to the language syntax as well as a huge number of APIs to the core class libraries. This has enabled developers to become substantially more productive and has helped to eliminate a variety of common situations that can easily result in bugs. Java has continued to grow in popularity, which is in large part attributable to the continued evolution of the platform, which keeps it fresh and addresses things that developers want. According to some sources, there are more than nine million Java programmers across the globe and this number looks set to continue to grow as most universities use Java as a primary teaching language. With so many Java programmers available to employers, how do they ensure that candidates have the necessary skills to develop high-quality, reliable code? The answer is certification: a standardized test of a developer’s knowledge about the wide variety of features and techniques required to use Java efficiently and effectively. Originally introduced by Sun Microsystems, the certification process and exam has been updated to match the features of each release of Java. Oracle has continued this since acquiring Sun in 2010. Taking and passing the exams is not a simple task. To ensure that developers meet a high standard of knowledge about Java, the candidate must demonstrate the ability to understand a wide variety of programming techniques, a clear grasp of the Java syntax, and a comprehensive knowledge of the standard class library APIs. With the release of JDK 8, not only do Java developers need to understand the details of imperative and object-oriented programming, they now need to have a grasp of functional programming so they can effectively use the key new features: lambda expressions and the Streams API. Which is why, ultimately, you need this book to help you prepare for the exam. The authors have done a great job of presenting the material you need to know to pass the exam in an approachable and easy-to-grasp way. The book starts with the fundamental concepts and language syntax and works its way through what you need to know about object-oriented programming before addressing more complex topics like generic types. The latter part of the book addresses the most recent changes in JDK 8, that of lambda expressions, the Streams API, and the new Date and Time API. Having worked with Java almost since it was first released, both at Sun Microsystems and then at Oracle Corporation, I think you will find this book an invaluable guide to help you pass the Oracle Certified Associate Exam for Java SE 8. I wish you the best of luck! —Simon Ritter Deputy CTO, Azul Systems WOW! eBook www.wowebook.org Preface Writing This Book Dear Reader, what you hold in your hand is the result of a meticulous high-tech operation that took many months and required inspecting many parts, removing certain parts, retrofitting some old parts, and adding many new parts to our previous book on an earlier Java programmer certification exam, until we were completely satisfied with the result. After you have read the book and passed the exam, we hope that you will appreciate the TLC (tender loving care) that has gone into this operation. This is how it all came about. Learning the names of Java certifications and the required exams is the first item on the agenda. This book provides coverage for the exam to earn Oracle Certified Associate (OCA), Java SE 8 Programmer Certification (also know as OCAJP8). The exam required for this certification has the name Java SE 8 Programmer I Exam (Exam number 1Z0808). It is the first of two exams required to obtain Oracle Certified Professional (OCP), Java SE 8 Programmer Certification (also known as OCPJP8). The second exam required for this professional certification has the name Java SE 8 Programmer II Exam (Exam number 1Z0-809). To reiterate, this book covers only the topics for the Java SE 8 Programmer I Exam that is required to obtain OCAJP8 certification. A book on the new Java SE 8 certification was a long time coming. The mantle of Java had been passed on to Oracle and Java 7 had hit the newsstand. We started out to write a book to cover the topics for the two exams required to earn the Oracle Certified Professional, Java SE 7 Programmer Certification. Soon after the release of Java 8, Oracle announced the certification for Java SE 8. We decided to switch to the new version. It was not a difficult decision to make. Java 8 marks a watershed when the language went from being a pure object-oriented language to one that also incorporates features of functional-style programming. As the saying goes, Java 8 changed the whole ballgame. Java passed its twentieth birthday in 2015. Java 8, released a year earlier, represented a significant milestone in its history. There was little reason to dwell on earlier versions. The next decision concerned whether it would be best to provide coverage for the two Java SE 8 Programmer Certification exams in one or two books. Pragmatic reasons dictated two books. It would take far too long to complete a book that covered both exams, mainly because the second exam was largely revamped and would require a lot of new material. We decided to complete the book for the first exam. Once that decision was made, our draft manuscript went back on the operating table. Our approach to writing this book has not changed from the one we employed for our previous books, mainly because it has proved successful. No stones were left unturned to create this book, as we explain here. The most noticeable changes in the exam for OCAJP8 are the inclusion of the core classes in the new Date and Time API and the writing of predicates using lambda expressions. The emphasis remains on analyzing code scenarios, rather than individual language constructs. The exam continues to require actual experience with the language, not just mere recitation of facts. We still claim that proficiency in the language is the key to WOW! eBook www.wowebook.org success. Since the exam emphasizes the core features of Java, this book provides in-depth coverage of topics related to those features. As in our earlier books, supplementary topics are also included to aid in mastering the exam topics. This book is no different from our previous books in one other important aspect: It is a one-stop guide, providing a mixture of theory and practice that enables readers to prepare for the exam. It can be used to learn Java and to prepare for the exam. After the exam is passed, it can also come in handy as a language guide. Apart from including coverage of the new topics, our discussions of numerous topics from the previous exam were extensively revised. All elements found in our previous books (e.g., sections, examples, figures, tables, review questions, mock exam questions) were closely scrutinized. New examples, figures, tables, and review questions were specifically created for the new topics as well as for the revised ones. We continue to use UML (Unified Modeling Language) extensively to illustrate concepts and language constructs, and all numbered examples continue to be complete Java programs ready for experimenting. Feedback from readers regarding our previous books was invaluable in shaping this book. Every question, suggestion, and comment received was deliberated upon. We are grateful for every single email we have received over the years; that input proved invaluable in improving this book. Dear Reader, we wish you all the best should you decide to go down the path of Java certification. May your loops terminate and your exceptions get caught! About This Book This book provides extensive coverage of the core features of the Java programming language and its core application programming interface (API), with particular emphasis on its syntax and usage. The book is primarily intended for professionals who want to prepare for the Java SE 8 Programmer I 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 API. The demand for well-trained and highly skilled Java programmers remains unabated. Oracle offers many Java certifications that professionals can take to validate their skills (see http://education.oracle.com). The certification provides members of the IT industry with a standard to use when hiring such professionals, and it allows professionals to turn their Java skills into credentials that are important for career advancement. The book provides extensive coverage of all the objectives defined by Oracle for the Java SE 8 Programmer I exam. The exam objectives are selective, however, and do not include many of the essential features of Java. This book covers many additional topics that every Java programmer should master to be truly 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. WOW! eBook www.wowebook.org This book is not a complete reference for Java, as it does not attempt to list every member of every class from the Java SE platform API documentation. The purpose is not to document the Java SE platform API. The emphasis is more on the Java programming language features—their syntax and correct usage through code examples—and less on teaching programming techniques. 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 This Book The reader can choose a linear or a nonlinear route through the book, depending on his or 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 applications. For those preparing for Java SE 8 Programmer I exam, the book has a separate appendix (Appendix A) providing all the pertinent information on preparing for and taking the exam. Cross-references are provided where necessary to indicate the relationships among the various constructs of the language. To understand a language construct, all pertinent details are provided where the construct is covered, but in addition, cross-references are provided to indicate its relationship to other constructs. Sometimes it is necessary to postpone discussion of certain aspects of a topic if they depend on concepts that have not yet been covered in the book. A typical example is the consequences of object-oriented programming concepts (for example, inheritance) on the member declarations that can occur in a class. This approach can result in forward references in the initial chapters of the book. 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 of the book: WOW! eBook www.wowebook.org Programmer I Exam Objectives 0.1 Exam objectives are stated clearly at the beginning of every chapter. 0.2 The number in front of the objective identifies the exam objective, as defined by Oracle, and can be found in Appendix B. 0.3 The objectives are organized into major sections, detailing the curriculum for the exam. 0.4 The objectives for the Java SE 8 Programmer I exam are reproduced verbatim in Appendix B, where for each section of the syllabus, references are included to point the reader to relevant topics in the book. 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 objective is listed as a bullet at the beginning of the chapter. Review Questions Review questions are provided after every major topic to test and reinforce the material. The review questions predominantly reflect the kind of multiple-choice questions that can be asked on the actual exam. On the exam, the exact number of answers to choose for each question is explicitly stated. The review questions in this book follow that practice. Many questions on the actual exam contain code snippets with line numbers to indicate that complete implementation is not provided, and that the necessary missing code to compile and run the code snippets can be assumed. The review questions in this book provide complete code implementations where possible, so that the code can be readily compiled and run. Annotated answers to the review questions are provided in Appendix C. WOW! eBook www.wowebook.org Example 0.1 Example Source Code We encourage readers to experiment with the code examples to reinforce the material from the book. These examples can be downloaded from the book website (see p. xxxiv). Java code is presented in a monospaced 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 singleline comment in the code. For example, in the following code snippet, the call to the method doSomethingInteresting() at (1) does something interesting: Click here to view code image // … 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 in all uppercase letters. Interface names begin with the prefix I, when it makes sense to distinguish them from class names. Coding conventions are followed, except when we have had to deviate from these conventions in the interest of space or clarity. Chapter Summary Each chapter concludes with a summary of the topics covered in the chapter, pointing out the major concepts that were introduced. 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 Appendix D. Mock Exam The mock exam in Appendix E should be attempted when the reader feels confident about the topics on the exam. It is highly recommended to read Appendix A before attempting the mock exam, as Appendix A contains pertinent information about the questions to expect on the actual exam. Each multiple-choice question in the mock exam explicitly states how many answers are applicable for a given question, as is the case on the actual exam. Annotated answers to the questions in the mock exam are provided in Appendix F. Java SE Platform API Documentation A vertical gray bar is used to highlight methods and fields found in the classes of the Java SE Platform API. Any explanation following the API information is also similarly highlighted. WOW! eBook www.wowebook.org To obtain the maximum benefit from using this book in preparing for the Java SE 8 Programmer I exam, we strongly recommend installing the latest version (Release 8 or newer) of the JDK and its accompanying API documentation. The book focuses solely on Java 8, and does not acknowledge previous versions. Book Website This book is backed by a website providing auxiliary material: www.ii.uib.no/~khalid/ocajp8/ The contents of the website include the following: • Source code for all the examples in the book • Solutions to the programming exercises in the book • Annotated answers to the reviews questions in the book • Annotated answers to the mock exam in the book • Table of contents, sample chapter, and index from the book • Errata for the book • Links to miscellaneous Java resources (e.g., certification, discussion groups, tools) Information about the Java Standard Edition (SE) and its documentation can be found at the following website: www.oracle.com/technetwork/java/javase/overview/index.html The current authoritative technical reference for the Java programming language, The Java® Language Specification: Java SE 8 Edition (also published by Addison-Wesley), can be found at this website: http://docs.oracle.com/javase/specs/index.html Request for Feedback Considerable effort has been made to ensure the accuracy of the content of this book. 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 not) for your purpose. Any feedback is valuable. The principal author can be reached at the following email address: khalid.mughal@uib.no Register your copy of A Programmer’s Guide to Java® SE 8 Oracle Certified Associate (OCA) at informit.com for convenient access to downloads, updates, and corrections as they become available. To start the registration process, go to informit.com/register and log in or create an account. Enter the product ISBN (9780132930215) and click Submit. Once the process is complete, you will find any available bonus content under “Registered WOW! eBook www.wowebook.org Products.” About the Authors Khalid A. Mughal Khalid A. Mughal is an associate professor at the Department of Informatics at the University of Bergen, Norway, where he has been responsible for designing and implementing various courses in informatics. Over the years, he has taught programming (primarily Java), software engineering (object-oriented system development), databases (data modeling and database management systems), compiler techniques, web application development, and software security courses. For 15 years, he was responsible for developing and running web-based programming courses in Java, which were offered to off-campus students. He has also given numerous courses and seminars at various levels in object-oriented programming and system development using Java and Java-related technologies, both at the University of Bergen and for the IT industry. Mughal is the principal author and solely responsible for the contents of this book. He is also the principal author of three books on previous versions of the Java programmer certification—A Programmer’s Guide to Java™ SCJP Certification: A Comprehensive Primer, Third Edition (0321556054); A Programmer’s Guide to Java™ Certification: A Comprehensive Primer, Second Edition (0201728281); and A Programmer’s Guide to Java™ Certification (0201596148)—and three introductory textbooks on programming in Java: Java Actually: A First Course in Programming (1844804186); Java Actually: A Comprehensive Primer in Java Programming (1844809331); and Java som første programmeringsspråk/Java as First Programming Language, Third Edition (8202245540). Mughal currently works on security issues related to mobile data collection systems for delivering health services in low- and middle-income countries. Rolf W. Rasmussen Rolf W. Rasmussen is a 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 was 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 exam in our three previous books on Java programmer certification. Selected earlier content has been utilized in this book. Together with Mughal, he is also a co-author of three introductory textbooks on programming in Java. WOW! eBook www.wowebook.org Acknowledgments At Addison-Wesley, Greg Doench was again our editor, who effectively managed the process of publishing this book. Regular dialog with him in recent months helped to keep this project on track. Julie Nahil was the in-house contact at Addison-Wesley, who professionally managed the production of the book. Anna Popick was the project editor, who diligently handled the day-to-day project management for this book. Jill Hobbs did a truly marvelous job copy editing the book. The folks at The CIP Group performed the typesetting wizardry necessary to materialize the book. We would like to extend our sincere thanks to Greg, Julie, Anna, Jill, the folks at The CIP Group, and all those behind the scenes at Addison-Wesley, who helped to put this publication on the bookshelf. For the technical review of the book, we were lucky that Roel De Nijs agreed to take on the task. If you drop in on CodeRanch.com, you are bound to find him executing his duties as a Sheriff, especially helping greenhorns find their bearing in the Java certification corrals. He is a freelance Java developer with many IT companies as clients and a multitude of Java certification accolades under his belt (SCJA, SCJP, SCJD, OCAJP7). And not least, he is a Technical Reviewer Par Excellence. Without doubt, Roel has a meticulous eye for detail. It is no exaggeration to say that his exhaustive feedback has been invaluable in improving the quality of this book at all levels. Roel, you have our most sincere thanks for your many excellent comments and suggestions, and above all, for weeding out numerous pesky errors in the manuscript. Over the years, we have also been lucky to have our own personal manuscript quality controller: Marit Seljeflot Mughal. As diligently as with our previous books, she tirelessly proofread several chapter drafts for this book, and put her finger on many unmentionable mistakes and errors in the manuscript. Her valuable comments and suggestions have also been instrumental in improving the quality of this book. If Marit, who has no IT background, could make sense of the Java jargon we wrote, then we were confident our readers would as well. Our most sincere thanks. Great effort has been made to eliminate mistakes and errors in this book. We accept full responsibility for any remaining oversights. We hope that when our Dear Readers find any, they will bring them to our attention. Many family occasions have been missed while working on this book. Without family support, this book would not have seen the light of day. Khalid is ever grateful to his family for their love, support, and understanding—but especially when he is working on a book. Now that this book is out the door, he is off to play with his three grandchildren. —Khalid A. Mughal 17 May 2016 Bergen, Norway WOW! eBook www.wowebook.org 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 to OOP, 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 by using abstractions. An abstraction denotes the essential properties and behaviors of an object that differentiate it from other objects. The essence of OOP is modeling abstractions, using classes and objects. The hard part of this endeavor is finding the right abstraction. A class denotes a category of objects, and acts as a blueprint for creating 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 that can store a value that represents a particular WOW! eBook www.wowebook.org 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 which services are provided, and the implementation defines how these services are provided by the class. Clients (i.e., other objects) need to know only the contract of an object, and not its implementation, 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. Figure 1.1 UML Notation for Classes 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, rather than an effective implementation of stacks. The character sequence // in the code indicates the start of a single-line comment that can be used to document the code. All characters after this sequence and to the end of the line are ignored by the compiler. A class declaration contains member declarations that define the fields and the methods of the objects the class represents. 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) WOW! eBook www.wowebook.org 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 at (2) with the same name as the class. 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 Click here to view code image // File: CharStack.java public class CharStack { // Class Declarations: // Class name // 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; } } { { { { { (2) (3) stackArray[++topOfStack] = element; } return stackArray[topOfStack—]; } return stackArray[topOfStack]; } return topOfStack == -1; } return topOfStack == stackArray.length - 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. A variable denotes a location in memory where a value can be stored. An object reference (or simply reference) is a variable that can store a reference value. WOW! eBook www.wowebook.org Thus a reference provides a handle to an object, as it can indirectly denote an object whose reference value it holds. In Java, an object can be manipulated only via its reference value, or equivalently by a reference that holds its reference value. This setup for manipulating objects requires that a reference be declared, a class be instantiated to create an object, and the reference value of the object created be stored in the reference. These steps are accomplished by a declaration statement. Click here to view code image CharStack stack1 = new CharStack(10); // Stack length: 10 chars In the preceding declaration statement, the left-hand side of the = operator declares that stack1 is a reference of class CharStack. The reference stack1, therefore, can refer to objects of class CharStack. The right-hand side of the = operator creates an object of class CharStack. This step involves using the new operator in conjunction with a call to a constructor of the class (new CharStack(10)). The new operator creates an instance of the CharStack class and returns the reference value of this instance. The = operator (called the assignment operator) stores the reference value in the reference stack1 declared on the left-hand side of the assignment operator. The reference stack1 can now be used to manipulate the object whose reference value is stored in it. Analogously, the following declaration statement declares the reference stack2 to be of class CharStack, creates an object of class CharStack, and assigns its reference value to the reference stack2: Click here to view code image CharStack stack2 = new CharStack(5); // Stack length: 5 chars Each object that is created has its own copy of the fields declared in the class declaration in Example 1.1. That is, the two stack objects, referenced by stack1 and stack2, will have their own stackArray and topOfStack fields. The purpose of the constructor call on the right-hand side of the new operator is to initialize the newly created object. In this particular case, for each new CharStack object created using the new operator, the constructor at (2) in Example 1.1 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. 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 denoting the object is prefixed to the class name with a colon (:). If the name of the reference is omitted, as in Figure 1.2b, this denotes an anonymous object. Since objects in Java do not have names, but rather are denoted by references, a more elaborate notation is shown in Figure 1.2c, where references of the CharStack class explicitly refer to CharStack objects. In most cases, the more compact notation will suffice. WOW! eBook www.wowebook.org Figure 1.2 UML Notation for Objects Object Aliases Several references can refer to the same object, 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. Click here to view code image // 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 stack objects are created in the preceding code. Before the assignment at (1), the situation is as depicted in Figure 1.3a. After the assignment at (1), the references stackA and stackB will denote the same stack, as depicted in Figure 1.3b. The reference value in stackA is assigned to stackB. The references 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 stackB before the assignment? When objects are no longer in use, their memory is, if necessary, reclaimed and reallocated for other objects. This process is called automatic garbage collection. Garbage collection in Java is taken care of by the runtime environment. WOW! eBook www.wowebook.org Figure 1.3 Aliases 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 constitute 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; such methods are called instance methods. It is important to note that these methods pertain to each object of the class. In contrast, the implementation of the methods 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 §1.5. Invoking Methods Objects communicate by message passing. As a consequence, 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 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 be passed 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. Click here to view code image 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 given here invokes methods on the object denoted by the reference WOW! eBook www.wowebook.org 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 the 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. 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. Thus the following code in a client of the CharStack class will not compile: Click here to view code image stack.topOfStack++; // Compile-time error: topOfStack is not visible. 1.5 Static Members In some cases, certain members should belong only to the class; that is, they should not be part of any instance of the class. As an example, suppose 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, rather than to any specific 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 class, rather than 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, whose names are 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. WOW! eBook www.wowebook.org Figure 1.4 Example 1.2 Class Diagram Showing Static Members of a Class Static Members in Class Declaration Click here to view code image // File: 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; } { { { { { stackArray[++topOfStack] = element; } return stackArray[topOfStack—]; } return stackArray[topOfStack]; } return topOfStack == -1; } return topOfStack == stackArray.length - // 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 provides a summary of the terminology used in defining members of a class. WOW! eBook www.wowebook.org Figure 1.5 Table 1.1 Members of a Class Terminology for Class Members Clients can access static members in the class by using the class name. The following code invokes the getInstanceCount() method in the class CharStack: Click here to view code image int count = CharStack.getInstanceCount(); // Class name to invoke static method Static members can also be accessed via object references, although doing so is considered bad style: WOW! eBook www.wowebook.org Click here to view code image CharStack myStack = new CharStack(20); int count = myStack.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 be accessed only by object references. 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 Engine, Axle, and GearBox, which make up a vehicle. Inheritance is illustrated here 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, along with 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 class PrintableCharStack is a specialization of stacks of characters that can also print their elements. Figure 1.6 Class Diagram Depicting Inheritance Relationship 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: Click here to view code image class PrintableCharStack extends CharStack { // (1) // Instance method public void printStackElements() { // (2) // … implementation of the method… } // The constructor calls the constructor of the superclass explicitly. public PrintableCharStack(int capacity) { super(capacity); } // (3) } WOW! eBook www.wowebook.org 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 to support this access. Implementation of the printStackElements() method is shown at (2). The constructor of the PrintableCharStack class at (3) calls the constructor of the superclass CharStack to initialize the stack properly. Example 1.3 Defining a Subclass Click here to view code image // File: CharStack.java public class CharStack { // Instance variables protected char[] stackArray; protected int topOfStack; // The array that implements the stack // The top of the stack // The rest of the definition is the same as in Example 1.2. } // File: 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) } Objects of the PrintableCharStack class will respond just like the objects of the CharStack class, but they also have the additional functionality defined in the subclass: Click here to view code image PrintableCharStack pcStack = new PrintableCharStack(3); pcStack.push(‘H’); pcStack.push(‘i’); pcStack.push(‘!’); pcStack.printStackElements(); // Prints “Hi!” on the terminal 1.7 Associations: Aggregation and Composition An association defines a static relationship between objects of two classes. One such association, called aggregation, expresses how an object uses other objects. Java supports aggregation of objects by reference, since objects cannot contain other objects explicitly. The aggregate object usually has fields that denote its constituent objects. A constituent object can be shared with other aggregate objects. WOW! eBook www.wowebook.org For example, an object of class Airplane might have a field that denotes an object of class Pilot. This Pilot object of an Airplane object might be shared among other aggregate objects (not necessarily Airplane objects) once the pilot has finished duty on one airplane. In fact, the Pilot object can still be used even when its Airplane object no longer exists. This aggregation relationship is depicted by the UML diagram in Figure 1.7 (empty diamond), showing that each object of the Airplane class has zero or one object of class Pilot associated with it. Figure 1.7 Class Diagram Depicting Associations The aggregate association can be made stronger if the constituent objects cannot be shared with other aggregate objects—for example, an Airplane object with two Wing objects. The Wing objects cannot be shared and can exist only with their Airplane object; that is, the Airplane object has ownership of its Wing objects. Conversely, the Wing objects are a part of their Airplane object. This stronger aggregation association is called composition and is depicted by the UML diagram in Figure 1.7 (filled diamond), showing that each object of the Airplane class owns two objects of class Wing. In the case of the CharStack class used in the earlier examples, each object of this class has a field to store the reference value of an array object that holds the characters. It would not be a good idea to share this array with other stack objects. The stack owns the array of characters. The relationship between the stack object and its constituent array object can be expressed by composition (Figure 1.8), showing that each object of the CharStack class will own one array object of type char associated with it. Figure 1.8 Class Diagram Depicting Composition 1.8 Tenets of Java • Code in Java must be encapsulated in classes. WOW! eBook www.wowebook.org • There are two kinds of values in Java: objects that are instances of classes or arrays, and atomic values of primitive data types. • References store reference values that denote objects, and are used to manipulate objects. • Objects in Java cannot contain other objects; they can only have references to other objects. • During execution, reclamation of objects that are no longer in use is managed by the runtime environment. Review Questions 1.1 Which statement is true about methods? Select the one correct answer. (a) A method is an implementation of an abstraction. (b) A method is an attribute defining the property of a particular abstraction. (c) A method is a category of objects. (d) A method is an operation defining the behavior for a particular abstraction. (e) A method is a blueprint for making operations. 1.2 Which statement is true about objects? Select the one correct answer. (a) An object is what classes are instantiated from. (b) An object is an instance of a class. (c) An object is a blueprint for creating concrete realization of abstractions. (d) An object is a reference. (e) An object is a variable. 1.3 Which is the first line of a constructor declaration in the following code? Click here to view code image 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) } // (1) stepValue) { // (2) // (3) { return current; } { current = value; } { step = stepValue; } // (4) // (5) // (6) Select the one correct answer. (a) (1) WOW! eBook www.wowebook.org (b) (2) (c) (3) (d) (4) (e) (5) (f) (6) 1.4 Given that Thing is a class, how many objects and how many references are created by the following code? Thing item, stuff; item = new Thing(); Thing entity = new Thing(); Select the two correct answers. (a) One object is created. (b) Two objects are created. (c) Three objects are created. (d) One reference is created. (e) Two references are created. (f) Three references are created. 1.5 Which statement is true about instance members? Select the one correct answer. (a) An instance member is also called a static member. (b) An instance member is always a field. (c) An instance member is never a method. (d) An instance member belongs to an instance, not to the class as a whole. (e) An instance member always represents an operation. 1.6 How do objects communicate in Java? Select the one correct answer. (a) They communicate by modifying each other’s fields. (b) They communicate by modifying the static variables of each other’s classes. (c) They communicate by calling each other’s instance methods. (d) They communicate by calling static methods of each other’s classes. 1.7 Given the following code, which statements are true? class A { protected int value1; } class B extends A { WOW! eBook www.wowebook.org int value2; } Select the two correct answers. (a) Class A extends class B. (b) Class B is the superclass of class A. (c) Class A inherits from class B. (d) Class B is a subclass of class A. (e) Objects of class A have a field named value2. (f) Objects of class B have a field named value1. 1.8 Given the following code, which statements express the most accurate association? Click here to view code image class Carriage { } class TrainDriver { } class Train { private Carriage[] carriages; private TrainDriver driver; Train(TrainDriver trainDriver, int noOfCarriages) { carriages = new Carriage[noOfCarriages]; driver = trainDriver; } void insertCarriage(Carriage newCarriage) { /* … */ } } Select the three correct answers. (a) A Train object has an array of Carriage objects. (b) A Train object owns an array of Carriage objects. (c) A Train object owns Carriage objects. (d) A Train object has a TrainDriver object. (e) A Train object owns a TrainDriver object. (f) A TrainDriver object is part of a Train object. (g) An array of Carriage objects is part of a Train object. (h) Carriage objects are part of a Train object. WOW! eBook www.wowebook.org 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 (Java Development Kit) 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. This rule implies that a source file can contain at most one public class. If the source file contains a public class, the file naming rule is enforced by the JDK. Each class declaration in a source file is compiled into a separate class file, containing Java bytecode. 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 SE platform API are already compiled, and the JDK tools know where to find them. 1.10 Sample Java Application The term application is just a synonym for a program, referring to source code that is compiled and directly executed. 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. WOW! eBook www.wowebook.org Example 1.4 An Application Click here to view code image // File: CharStack.java public class CharStack { // Same as in Example 1.2. } // File: 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”; System.out.println(“Original string: ” + str); int length = str.length(); // (1) // 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: “); // (2) // 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(); // (3) } } Output from the program: Click here to view code image 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 must be declared as shown in the following method stub: Click here to view code image public static void main(String[] args) { /* Implementation */ } // Method header The main() method has public accessibility—that is, it is accessible from any class. The keyword static means the method belongs to the class. The keyword void indicates that the method does not return any value. The parameter args is an array of strings that can be used to pass information to the main() method when execution starts. WOW! eBook www.wowebook.org Compiling and Running an Application Java source files can be compiled using the Java compiler tool javac, which is part of the JDK. The source file Client.java contains the declaration of the Client class. This source file can be compiled by giving the following command at the command line (the character > is the command prompt): >javac Client.java This command creates the class file Client.class containing the Java bytecode 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. To run Example 1.4, give the following command on the command line: Click here to view code image >java Client Original string: !no tis ot nuf era skcatS Reversed string: Stacks are fun to sit on! 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. 1.11 Program Output Data produced by a program is called output. This output can be sent to different devices. The examples presented in this book send their output to a terminal window, where the output is printed as line of characters with a cursor that advances as characters are printed. A Java program can send its output to the terminal window using an object called standard out. This object, which can be accessed using the public static final field out in the System class, is an object of the class java.io.PrintStream that provides methods for printing values. These methods convert values to their string representation and print the resulting string. Example 1.4 illustrates the process of printing values to the terminal window. The argument in the call to the println() method at (1) is first evaluated, and the resulting string is printed to the terminal window. This method always terminates the current line, which results in the cursor being moved to the beginning of the next line: Click here to view code image System.out.println(“Original string: ” + str); // (1) The print() method at (2) prints its argument to the terminal window, but it does not terminate the current line: Click here to view code image System.out.print(“Reversed string: “); WOW! eBook www.wowebook.org // (2) To terminate a line, without printing any values, we can use the no-argument println() method: Click here to view code image System.out.println(); // (3) Formatted Output To have more control over how the values are printed, we can create formatted output. The following method of the java.io.PrintStream class can be used for this purpose: Click here to view code image PrintStream printf(String format, Object… args) The String parameter format specifies how formatting will be done. It contains format specifications that determine how each subsequent value in the parameter args will be formatted and printed. The parameter declaration Object… args represents an array of zero or more arguments to be formatted and printed. The resulting string from the formatting will be printed to the destination stream. (System.out will print to the standard out object.) Any error in the format string will result in a runtime exception. The following call to the printf() method on the standard out object formats and prints three values: Click here to view code image System.out.printf(“Formatted values|%5d|%8.3f|%5s|%n”, // Format string 2016, Math.PI, “Hi”); // Values to format At runtime, the following line is printed in the terminal window: Click here to view code image Formatted values| 2016| 3.142| Hi| The format string is the first argument in the method call. It contains four format specifiers. The first three are %5d, %8.3f, and %5s, which specify how the three arguments should be processed. The letter in the format specifier indicates the type of value to format. Their location in the format string specifies where the textual representation of the arguments should be inserted. The fourth format specifier, %n, is a platform-specific line separator. Its occurrence causes the current line to be terminated, with the cursor moving to the start of the next line. All other text in the format string is fixed, including any other spaces or punctuation, and is printed verbatim. In the preceding example, the first value is formatted according to the first format specifier, the second value is formatted according to the second format specifier, and so on. The | character has been used in the format string to show how many character positions are taken up by the text representation of each value. The output shows that the int value was written right-justified, spanning five character positions using the format specifier %5d; the double value of Math.PI took up eight character positions and was rounded to three decimal places using the format specifier %8.3f; and the String value WOW! eBook www.wowebook.org was written right-justified, spanning five character positions using the format specifier %5s. The format specifier %n terminates the current line. All other characters in the format string are printed verbatim. Table 1.2 shows examples of some selected format specifiers that can be used to format values. Their usage is illustrated in Example 1.5, which prints a simple invoice. Table 1.2 Format Specifier Examples At the top of the invoice printed by Example 1.5, the company name is printed at (1) with a format string that contains only fixed text. The date and time of day are printed on the same line, with leading zeros at (2). A header is then printed at (3). The column names WOW! eBook www.wowebook.org Item, Price, Quantity, and Amount are positioned appropriately with the format specifications %-20s, %7s, %9s, and %8s, respectively. Beneath the heading, the items purchased are printed at (5), (6), and (7) using the same field widths as the column headings. The format for each item is defined by the format string at (4). The item name is printed with the format string "%-20s", resulting in a 20character-wide string, left-justified. The item price and the total amount for each type of item are printed as floating-point values using the format specifications %7.2f and %8.2f, respectively. The quantity is printed as an integer using the format specification %9d. The strings are left-justified, while all numbers are right-justified. The character s is the conversion code for objects, while floating-point and integer values are printed using the codes f and d, respectively. At (8), the total cost of all items is printed using the format specification %8.2f. To position this value correctly under the column Amount, we print the string "Total:" using the format %-36s. The width of 36 characters is found by adding the width of the first three columns of the invoice. WOW! eBook www.wowebook.org Example 1.5 Formatted Output Click here to view code image // File: Invoice.java public class Invoice { public static void main(String[] args) { System.out.printf(“Secure Data Inc. ”); System.out.printf(“%02d/%02d/%04d, %02d:%02d%n%n”, 2, 13, 2016, 11, 5); System.out.printf(“%-20s%7s%9s%8s%n”, “Item”, “Price”, “Quantity”, “Amount”); int quantity = 4; double price = 120.25, amount = quantity*price, total = amount; String itemFormat = “%-20s%7.2f%9d%8.2f%n”; System.out.printf(itemFormat, “FlashDrive, 250GB”, price, quantity, amount); quantity = 2; price = 455.0; amount = quantity*price; total = total + amount; System.out.printf(itemFormat, “Ultra HD, 4TB”, price, quantity, amount); quantity = 1; price = 8.50; amount = quantity*price; total = total + amount; System.out.printf(itemFormat, “USB 3.0 cable”, price, quantity, amount); System.out.printf(“%-36s%8.2f%n”, “Total:”, total); // (1) // (2) // (3) // (4) // (5) // (6) // (7) // (8) } } Output from the program: Click here to view code image Secure Data Inc. Item FlashDrive, 250GB Ultra HD, 4TB USB 3.0 cable Total: 02/13/2016, 11:05 Price Quantity 120.25 4 455.00 2 8.50 1 Amount 481.00 910.00 8.50 1399.50 1.12 The Java Ecosystem Since its initial release as Java Development Kit 1.0 (JDK 1.0) in 1996, the name Java has become synonymous with a thriving ecosystem that provides the components and the tools necessary for developing systems for today’s multicore world. Its diverse community, comprising a multitude of volunteers, organizations, and corporations, continues to fuel its evolution and grow with its success. Many free open-source technologies now exist that are well proven, mature, and supported, making their adoption less daunting. These tools and frameworks provide support for all phases of the software development life cycle and beyond. There are three major Java Platforms for the Java programming language: • Java SE (Standard Edition) • Java EE (Enterprise Edition) WOW! eBook www.wowebook.org • Java ME (Micro Edition) Each platform provides a hardware/operating system–specific JVM and an API (application programming interface) to develop applications for that platform. The Java SE platform provides the core functionality of the language. The Java EE platform is a superset of the Java SE platform and, as the most extensive of the three platforms, targets enterprise application development. The Java ME platform is a subset of the Java SE platform, having the smallest footprint, and is suitable for developing mobile and embedded applications. The upshot of this classification is that a Java program developed for one Java platform will not necessary run under the JVM of another Java platform. The JVM must be compatible with the Java platform that was used to develop the program. The API and the tools for developing and running Java applications are bundled together as JDK. Just the JVM and the runtime libraries are also bundled separately as JRE (Java Runtime Environment). The subject of this book is Java SE 8. We recommend installing the appropriate JDK for Java SE 8 (or a newer version) depending on the hardware and operating system. The rest of this section summarizes some of the factors that have contributed to the evolution of Java from an object-oriented programming language to a full-fledged ecosystem for developing all sorts of systems, including large-scale business systems and embedded systems for portable computing devices. A lot of jargon is used in this section, and might be difficult to understand at the first reading, but we recommend coming back after working through the book to appreciate the factors that have contributed to the success of Java. Object-Oriented Paradigm The Java programming language supports the object-oriented paradigm, in which the properties of an object and its behavior are encapsulated in the object. The properties and the behavior are represented by the fields and the methods of the object, respectively. The objects communicate through method calls in a procedural manner. Encapsulation ensures that objects are immune to tampering except when manipulated through their public interface. Encapsulation exposes only what an object does and not how it does it, so that its implementation can be changed with minimum impact on its clients. Some basic concepts of object-oriented programming, such as inheritance and aggregation, were introduced earlier in this chapter, and subsequent chapters will expand on this topic. Above all, object-oriented system development promotes code reuse where existing objects can be reused to implement new objects. It also facilitates implementation of large systems, allowing their decomposition into manageable subsystems. WOW! eBook www.wowebook.org Interpreted: The JVM Java programs are compiled to bytecode that is interpreted by the JVM. Various optimization technologies (e.g., just-in-time [JIT] delivery) have led to the JVM becoming a lean and mean virtual machine with regard to performance, stability, and security. Many other languages, such as Scala, Groovy, and Clojure, now compile to bytecode and seamlessly execute on the JVM. The JVM has thus evolved into an ecosystem in its own right. Architecture-Neutral and Portable Bytecode The often-cited slogan “Write once, run everywhere” is true only if a compatible JVM is available for the hardware and software platform. In other words, to run Java SE applications under Windows 10 on a 64-bit hardware architecture, the right JVM must be installed. Fortunately, the JVM has been ported to run under most platforms and operative systems that exist today, including hardware devices such as smart cards, mobile devices, and home appliances. The specification of the bytecode is architecture neutral, meaning it is independent of any hardware architecture. It is executed by a readily available hardware and operating system–specific JVM. The portability of the Java bytecode thus eases the burden of crossplatform system development. Simplicity Language design of Java has been driven by a desire to simplify the programming process. Although Java borrows heavily from the C++ programming language, certain features that were deemed problematic were not incorporated into its design. For example, Java does not have a preprocessor, and it does not allow pointer handling, user-defined operator overloading, or multiple class inheritance. Java opted for automatic garbage collection, which frees the programmer from dealing with many issues related to memory management, such as memory leaks. However, the jury is still out on whether the syntax of nested classes or introduction of wild cards for generics can be considered simple. Dynamic and Distributed The JVM can dynamically load class libraries from the local file system as well as from machines on the network, when those libraries are needed at runtime. This feature facilitates linking the code as and when necessary during the execution of a program. It is also possible to query programmatically a class or an object at runtime about its metainformation, such as its methods and fields. Java provides extensive support for networking to build distributed systems, where objects are able to communicate across networks using various communication protocols and technologies, such as Remote Method Invocation (RMI) and socket connections. WOW! eBook www.wowebook.org Robust and Secure Java promotes the development of reliable, robust, and secure systems. It is a strong statically typed language: The compiler guarantees runtime execution if the code compiles without errors. Elimination of pointers, runtime index checks for arrays and strings, and automatic garbage collection are some of the features of Java that promote reliability. The exception handling feature of Java is without doubt the main factor that facilitates the development of robust systems. Java provides multilevel protection from malicious code. The language does not allow direct access to memory. A bytecode verifier determines whether any untrusted code loaded in the JVM is safe. The sandbox model is used to confine and execute any untrusted code, limiting the damage that such code can cause. These features, among others, are provided by a comprehensive Java security model to ensure that application code executes securely in the JVM. High Performance and Multithreaded The performance of Java programs has improved significantly with various optimizations that are applied to the bytecode at runtime by the JVM. The JIT feature monitors the program at runtime to identify performance-critical bytecode (called hotspots) that can be optimized. Such code is usually translated to machine code to boost performance. The performance achieved by the JVM is a balance between native code execution and interpretation of fully scripted languages, which fortunately is adequate for many applications. Java has always provided high-level support for multithreading, allowing multiple threads of execution to perform different tasks concurrently in an application. It has risen to the new challenges that have emerged in recent years to harness the increased computing power made available by multicore architectures. Functional programming, in which computation is treated as side-effects–free evaluation of functions, is seen as a boon to meet these challenges. Java 8 brings elements of functional-style programming into the language, providing language constructs (lambda expressions and functional interfaces) and API support (through its Fork & Join Framework and Stream API) to efficiently utilize the many cores to process large amounts of data in parallel. Review Questions 1.9 Which command from the JDK should be used to compile the following source code contained in a file named SmallProg.java? Click here to view code image public class SmallProg { public static void main(String[] args) { System.out.println(“Good luck!”); } } Select the one correct answer. (a) java SmallProg WOW! eBook www.wowebook.org (b) javac SmallProg (c) java SmallProg.java (d) javac SmallProg.java (e) java SmallProg main 1.10 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() 1.11 Which statement is true about Java? Select the one correct answer. (a) A Java program can be executed by any JVM. (b) Java bytecode cannot be translated to machine code. (c) Only Java programs can be executed by a JVM. (d) A Java program can create and destroy objects. (e) None of the above Chapter Summary The following topics were covered in this chapter: • Essential elements of a Java application • Accessing object fields and calling methods • Compiling and running Java applications • Formatting and printing values to the terminal window • Basic terminology and concepts in OOP, and how these concepts are supported in Java • Factors and features of the Java ecosystem that have contributed to its evolution and success WOW! eBook www.wowebook.org Programming Exercise 1.1 Modify the Client class 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 behavior-wise any different from Example 1.4? WOW! eBook www.wowebook.org 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 always be a letter, as explained later. Since Java programs are written in the Unicode character set (p. 32), characters allowed in identifier names are interpreted according to this character set. Use of the Unicode character set opens up the possibility of writing identifier names in many writing scripts used around the world. As one would expect, the characters A-Z and a-z are letters, and characters from 0-9 are digits. A connecting punctuation character (such as underscore _) and any currency symbol (such as $, ¢, ¥, or £) are also allowed as letters in identifier names, but these characters should be used judiciously. WOW! eBook www.wowebook.org Identifiers in Java are case sensitive. For example, price and Price are two different identifiers. Examples of Legal Identifiers Click here to view code image number, Number, sum_$, bingo, $$_100, _007, mål, grüß Examples of Illegal Identifiers 48chevy, all@hands, grand-sum The name 48chevy is not a legal identifier because 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 not a legal character in an identifier, but it is a legal operator; thus grand-sum 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 Java keywords are lowercase, and incorrect usage results in compile-time errors. 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 (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 Reserved Literals in Java WOW! eBook www.wowebook.org Table 2.3 Reserved Keywords Not Currently in Use Separators Separators (also known as punctuators) are tokens that have meaning depending on the context in which they are used; they aid the compiler in performing syntax and semantic analysis of a program (Table 2.4). Depending on the context, brackets ([]), parentheses (()), and the dot operator (.) can also be interpreted as operators (§5.3, p. 150). See the index entries for these separators for more details. Table 2.4 Separators in Java Literals A literal denotes a constant value; in other words, the value that a literal represents remains unchanged in the program. Literals represent numerical (integer or floatingpoint), character, boolean, or string values. In addition, the literal null represents the null reference. Table 2.5 shows examples of literals in Java. Table 2.5 Examples of Literals Integer Literals Integer data types comprise the following primitive data types: int, long, byte, and short (§2.2, p. 37). 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. The suffix L is often preferred because the suffix l and the digit 1 can be hard to distinguish. 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 be specified in the binary (base 2, digits 0-1), octal (base 8, digits 0-7), and hexadecimal (base 16, digits 0-9 and a-f) number systems. The digits a to f in the hexadecimal system correspond to decimal values 10 to 15. Binary, octal, and hexadecimal numbers are specified with 0b (or 0B), WOW! eBook www.wowebook.org 0, and 0x (or 0X) as the base or radix prefix, respectively. Examples of decimal, binary, octal, and hexadecimal literals are shown in Table 2.6. 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 the number system (e.g., -0b1011010, -0132, or -0X5A). Integer representation is discussed in §5.5, p. 154. Table 2.6 Examples of Decimal, Binary, Octal, and Hexadecimal Literals 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). WOW! eBook www.wowebook.org Examples of double Literals Click here to view code image 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. Also, for the examples of float literals presented here, the suffix F is mandatory; if it was omitted, they would be interpreted as double literals. Underscores in Numerical Literals The underscore character (_) can be used to improve the readability of numerical literals in the source code. Any number of underscores can be inserted between the digits that make up the numerical literal. This rules out underscores adjacent to the sign (+, -), the radix prefix (0b, 0B, 0x, 0X), the decimal point (.), the exponent (e, E), and the data type suffix (l, L, d, D, f, F), as well as before the first digit and after the last digit. Note that octal radix prefix 0 is part of the definition of an octal literal and is therefore considered the first digit of an octal literal. Underscores in identifiers are treated as letters. For example, the names _XXL and _XXL_ are two distinct legal identifiers. In contrast, underscores are used as a notational convenience for numerical literals, being ignored by the compiler when used in such literals. In other words, a numerical literal can be specified in the source code using underscores between digits, such that 2_0_1_5 and 20__15 represent the same numerical literal 2015 in source code. Examples of Legal Use of Underscores in Numerical Literals Click here to view code image 0b0111_1111_1111_1111_1111_1111_1111_1111 0_377_777_777 0xff_ff_ff_ff -123_456.00 1_2.345_678e1_2 2009__08__13 49_03_01d Examples of Illegal Use of Underscores in Numerical Literals Click here to view code image _0_b_01111111111111111111111111111111_ _0377777777_ _0_x_ffffffff_ +_123456_._00_ _12_._345678_e_12_ _20090813_ _490301_d_ WOW! eBook www.wowebook.org Boolean Literals The primitive data type boolean represents the truth values true and false that are denoted by the reserved literals true and 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.7, note that digits (0 to 9), uppercase letters (A to Z), and lowercase letters (a to z) have contiguous Unicode values. A Unicode character can always be specified as a four-digit hexadecimal number (i.e., 16 bits) with the prefix \u. Table 2.7 Examples of Character Literals Escape Sequences Certain escape sequences define special characters, as shown in Table 2.8. 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. WOW! eBook www.wowebook.org Table 2.8 Escape Sequences 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.9. The number of digits must be three or fewer, and the octal value cannot exceed \377; in other words, only the first 256 characters can be specified with this notation. Table 2.9 Examples of Escape Sequence \ddd String Literals A string literal is a sequence of characters that must be enclosed in double quotes and must occur on a single line. All string literals are objects of the class String (§8.4, p. 357). Escape sequences as well as Unicode values can appear in string literals: Click here to view code image “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 WOW! eBook www.wowebook.org 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. The expression in (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: Click here to view code image 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. Whitespace A whitespace is a sequence of spaces, tabs, form feeds, and line terminator characters in a Java source file. Line terminators include the 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— that is, broken into a stream of tokens for further analysis. Separators and operators help to distinguish tokens, but sometimes whitespace has to be inserted explicitly as a separator. For example, the identifier classRoom will be interpreted as a single token, unless whitespace is inserted to distinguish the keyword class from the identifier Room. Whitespace aids not only in separating tokens, but also in formatting the program so that it is easy to read. The compiler ignores the whitespace 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 that can be used 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. Click here to view code image // This comment ends at the end of this line. int age; // From comment-start sequence to the end of the line is a comment. WOW! eBook www.wowebook.org 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, so they are ignored. This means that trying to nest multiple-line comments will result in a compile-time error: Click here to view code image /* 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. Documentation Comment A documentation comment is a special-purpose multiple-line 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 */: Click here to view code image /** * This class implements a gizmo. * @author K.A.M. * @version 4.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 WOW! eBook www.wowebook.org (f) _8to5 2.2 Which of the following are not legal literals in Java? Select the four correct answers. (a) 0Xbad (b) 0B_101_101 (c) 09 (d) +_825 (e) 1_2e4f (f) '\x' (g) "what\'s your fancy?" 2.3 Which statement is true? Select the one correct answer. (a) new and delete are keywords in the Java language. (b) try, catch, and thrown are keywords in the Java language. (c) static, unsigned, and long are keywords in the Java language. (d) exit, class, and while are keywords in the Java language. (e) return, goto, and default are keywords in the Java language. (f) for, while, and next are keywords in the Java language. 2.4 Which of the following is not a legal comment in Java? Select the one correct answer. (a) /* // */ (b) /* */ // (c) // /* */ (d) /* /* */ (e) /* /* */ */ (f) // // 2.2 Primitive Data Types Figure 2.1 gives an overview of the primitive data types in Java. WOW! eBook www.wowebook.org Figure 2.1 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 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 (Chapter 5, p. 143). 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 §8.3, p. 346. The Integer Types The integer data types are byte, short, int, and long (Table 2.10). Their values are signed integers represented by two’s complement (§5.5, p. 155). Table 2.10 The Range of Integer Values Type The data type char represents characters (Table 2.11). Their values are unsigned integers that denote all of the 65536 (216) characters in the 16-bit Unicode character set. This set includes letters, digits, and special characters. WOW! eBook www.wowebook.org Table 2.11 Range of Character Values 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 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.12 shows the range of values for positive floating-point numbers, but these apply equally to negative floating-point numbers with the minus sign (-) as a prefix. Zero can be either 0.0 or -0.0. Table 2.12 Range of Floating-Point Values Since the size for representation is a finite number of bits, certain floating-point numbers can be represented only 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 to represent floating-point numbers. The Type The data type boolean represents the two logical values denoted by the literals true and false (Table 2.13). Table 2.13 Boolean Values Boolean values are produced by all relational (§5.11, p. 180), conditional (§5.14, p. 186), and boolean logical operators (§5.13, p. 184), and are primarily used to govern the flow of control during program execution. Table 2.14 summarizes the pertinent facts about the primitive data types: their width or WOW! eBook www.wowebook.org size, which indicates the number of 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 (§8.3, p. 346). Table 2.14 Summary of Primitive Data Types Review Questions 2.5 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.6 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.7 Which integral type in Java has the exact range from -2147483648 (i.e., -231) to WOW! eBook www.wowebook.org 2147483647 (i.e., 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 store only 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 follow: Click here to view code image 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. The first declaration is equivalent to the following three declarations: char a; char b; char c; A declaration can also be combined with an initialization expression to specify an appropriate initial value for the variable. Such declarations are called declaration statements. Click here to view code image int i = 10, j = 0b101; long big = 2147483648L; value. // i is an int variable with initial value 10. // j is an int variable with initial value 5. // big is a long variable with specified initial Reference Variables A 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, an interface name, or an enum type) declares a reference variable. Analogous to the declaration of variables of primitive data types, the simplest form of reference variable declaration WOW! eBook www.wowebook.org specifies the name and the reference type only. The declaration determines which 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. Click here to view code image 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 preceding declarations do not create any objects of class Pizza or Hamburger. Rather, they simply create variables that can store reference values of objects of the specified classes. A declaration can also be combined with an initializer expression to create an object whose reference value can be assigned to the reference variable: Click here to view code image Pizza yummyPizza = new Pizza(“Hot&Spicy”); // Declaration statement 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. 2.4 Initial Values for Variables This section discusses what value, if any, is assigned to a variable when no explicit initial value is provided in the declaration. Default Values for Fields Default values for fields of primitive data types and reference types are listed in Table 2.15. The value assigned depends on the type of the field. Table 2.15 Default Values If no explicit initialization is provided for a static variable, 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, it is initialized with the default value of its type when the class is WOW! eBook www.wowebook.org instantiated. The fields of reference types are always initialized with the null reference value if no initialization is provided. Example 2.1 illustrates the 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 Click here to view code image 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: System.out.println(“Instance variable noOfWatts: System.out.println(“Instance variable indicator: System.out.println(“Instance variable location: } ” ” ” ” + + + + Light.counter); bulb.noOfWatts); bulb.indicator); bulb.location); } Output from the program: Click here to view code image Static variable counter: Instance variable noOfWatts: Instance variable indicator: Instance variable location: 0 100 false null Initializing Local Variables of Primitive Data Types Local variables are variables that are declared in methods, constructors, and blocks (Chapter 3, p. 47). They are not initialized implicitly when they are allocated memory at method invocation—that is, when the execution of a method begins. The same applies to local variables in constructors and blocks. Local variables must be explicitly initialized before being used. The compiler will report an error only if an attempt is made to use an uninitialized local variable. WOW! eBook www.wowebook.org Example 2.2 Flagging Uninitialized Local Variables of Primitive Data Types Click here to view code image 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); error! } } // (1) Local variables // (2) Always executed // (3) Compile-time In Example 2.2, the compiler complains that the local variable thePrice used in the println statement at (3) may not be initialized. However, 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 compiles the body of a conditional statement only if it can deduce that the condition is 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: Click here to view code image int weight = 10, thePrice = 0; initialized // (1’) Both local variables 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 Click here to view code image public class VerySmartClass { public static void main(String[] args) { String importantMessage; // Local reference variable System.out.println(“The message length is: ” + importantMessage.length()); // Compile-time error! } } 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, because the variable importantMessage will not denote any object. The golden rule is to ensure that a WOW! eBook www.wowebook.org reference variable, whether local or not, is assigned a reference value denoting an object before it is used—that is, to 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: Click here to view code image String importantMessage = “Initialize before use!”; Arrays and their default values are discussed in §3.4, p. 58. 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, which is also called its scope, is discussed in more detail in §4.4, p. 114. We distinguish among the lifetimes of variables in three contexts: • Instance variables—members of a class, which are 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—members of a class, but which are not created for any specific object of the class and, therefore, belong only to the class (§4.4, p. 114). They are created when the class is loaded at runtime, and exist as long as the class is available at runtime. • 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.8 Which of the following 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.9 Given the following code within a method, which statement is true? int i, j; WOW! eBook www.wowebook.org j = 5; Select the one correct answer. (a) Local variable i is not declared. (b) Local variable j is not declared. (c) Local variable i is declared but not initialized. (d) Local variable j is declared but not initialized. (e) Local variable j is initialized but not declared. 2.10 In which of these variable declarations will the variable remain uninitialized unless it is explicitly initialized? Select the one correct answer. (a) Declaration of an instance variable of type int (b) Declaration of a static variable of type float (c) Declaration of a local variable of type float (d) Declaration of a static variable of type Object (e) Declaration of an instance variable of type int[] 2.11 What will be the result of compiling and running the following program? Click here to view code image public class Init { String title; boolean published; static int total; static double maxPrice; public static void main(String[] args) { Init initMe = new Init(); double price; if (true) price = 100.00; System.out.println(“|” + initMe.title + “|” + initMe.published + “|” + Init.total + “|” + Init.maxPrice + “|” + price + “|”); } } Select the one correct answer. (a) The program will fail to compile. (b) The program will compile, and print |null|false|0|0.0|0.0| at runtime. (c) The program will compile, and print |null|true|0|0.0|100.0| at runtime. WOW! eBook www.wowebook.org (d) The program will compile, and print | |false|0|0.0|0.0| at runtime. (e) The program will compile, and print |null|false|0|0.0|100.0| at runtime. Chapter Summary The following topics were covered in this chapter: • Basic language elements: identifiers, keywords, separators, literals, whitespace, and comments • Primitive data types: integral, floating-point, and boolean • Notational representation of numbers in decimal, binary, 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. Click here to view code image // File: 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.0) * 5.0 / 9.0; } } WOW! eBook www.wowebook.org 3. Declarations 3.1 Class Declarations A class declaration introduces a new reference type. For the purpose of this book, we will use the following simplified syntax of a class declaration: WOW! eBook www.wowebook.org Click here to view code image class_modifiers class class_name extends_clause implements_clause // Class header { // Class body field_declarations method_declarations constructor_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: • An accessibility modifier (§4.5, p. 118) • Additional class modifiers (§4.6, p. 120) • Any class it extends (§7.1, p. 264) • Any interfaces it implements (§7.6, p. 290) The class body, enclosed in braces ({}), can contain member declarations. In this book, we discuss the following two kinds of member declarations: • Field declarations (§2.3, p. 40) • Method declarations (§3.2, p. 49) Members declared static belong to the class and are called static members. Non-static members belong to the objects of the class and are called instance members. In addition, the following declarations can be included in a class body: • Constructor declarations (§3.3, p. 53) The declarations can appear in any order in the class body. The only mandatory parts of the class declaration syntax are the keyword class, the class name, and the class body braces ({}), as exemplified by the following class declaration: class X { } To understand which 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, non-static field initializers, instance initializer blocks, and constructors. By static code, we mean expressions and statements in a static context; by non-static code, we mean expressions and statements in a non-static context. One crucial difference between the two contexts is that static code can refer only to other static members. 3.2 Method Declarations For the purpose of this book, we will use the following simplified syntax of a method declaration: Click here to view code image WOW! eBook www.wowebook.org method_modifiers return_type method_name (formal_parameter_list) throws_clause // Method header { // Method body local_variable_declarations statements } In addition to the name of the method, the method header can specify the following information: • Scope or accessibility modifier (§4.7, p. 123) • Additional method modifiers (§4.8, p. 131) • The type of the return value, or void if the method does not return any value (§6.4, p. 224) • A formal parameter list • Any exceptions thrown by the method, which are specified in a throws clause (§6.9, p. 251) 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 (§3.5, p. 72). An empty parameter list must be specified by ( ). Each parameter is a simple variable declaration consisting of its type and name: Click here to view code image optional_parameter_modifier type parameter_name The parameter names are local to the method (§4.4, p. 117). The optional parameter modifier final is discussed in §3.5, p. 80. It is recommended to use the @param tag in a Javadoc comment to document the formal parameters of a method. The signature of a method comprises the name of the method and the types of the formal parameters only. The method body is a block containing the local variable declarations (§2.3, p. 40) and the statements of the method. The mandatory parts of a method declaration are the return type, the method name, and the method body braces ({}), as exemplified by the following method declaration: void noAction() {} Like member variables, member methods can be characterized as one of two types: • Instance methods, which are discussed later in this section • Static methods, which are discussed in §4.8, p. 132 WOW! eBook www.wowebook.org Statements Statements in Java can be grouped into various categories. Variable declarations with explicit initialization of the variables are called declaration statements (§2.3, p. 40, and §3.4, p. 60). Other basic forms of statements are control flow statements (§6.1, p. 200) and expression statements. An expression statement is an expression terminated by a semicolon. Any value returned by the expression is discarded. Only certain types of expressions have meaning as statements: • Assignments (§5.6, p. 158) • Increment and decrement operators (§5.9, p. 176) • Method calls (§3.5, p. 72) • Object creation expressions with the new operator (§5.17, p. 195) A solitary semicolon denotes the empty statement, which does nothing. A block, {}, is a compound statement that can be used to group zero or more local declarations and statements (§4.4, p. 117). 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 that 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 initializer in declaration statements (§3.4, p. 60). Labeled statements are discussed in §6.4 on page 220. Instance Methods and the Object Reference Instance methods belong to every object of the class and can be invoked only 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 (§4.8, p. 133). 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.1, 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 the this reference at (2), the value of the parameter indicator WOW! eBook www.wowebook.org 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 with the same name. Example 3.1 Using the this Reference Click here to view code image public class Light { // Fields: int noOfWatts; boolean indicator; String location; // Wattage // On or off // Placement // Constructor public Light(int noOfWatts, boolean indicator, String site) { String location; this.noOfWatts = noOfWatts; indicator = indicator; location = site; this.superfluous(); superfluous(); // // // // // (1) Assignment to field (2) Assignment to parameter (3) Assignment to local variable (4) equivalent to call at (4) } public void superfluous() { System.out.printf(“Current object: %s%n”, 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); } } Probable output from the program: Current object: Light@1bc4459 Current object: Light@1bc4459 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 usage is illustrated at (4) in Example 3.1, 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 approach is illustrated at (5) in Example 3.1, where the current object is passed to the printf() method. The printf() method prints the string representation of the current object (which comprises the name of the class of the current object and the hexadecimal representation of the current object’s hash code). (The hash code of an object is an int value that can be used to store and retrieve the object WOW! eBook www.wowebook.org from special data structures called hash tables.) 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 plus 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 practice is called method overloading. Because 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 SE platform API 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. Click here to view code image 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: Click here to view code image 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 a; return 1; return b; return x; } } } } // (1) // (2) // (3) // (4) // (5) Not OK. The corresponding signatures of the five methods are as follows: Click here to view code image 1’ 2’: Number of parameters 3’: Number of parameters 4’: Order of parameters 5’: Same as 1’ methodA(int, double) methodA(int) methodA() methodA(double, int) methodA(int, double) 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, therefore, is not a valid overloading of this method. Click here to view code image void bake(Cake k) { /* … */ } void bake(Pizza p) { /* … */ } int halfIt(int a) { return a/2; } double halfIt(int a) { return a/2.0; } signature. // (1) // (2) // (3) // (4) Not OK. Same The method named bake is correctly overloaded at (1) and (2), with two different WOW! eBook www.wowebook.org parameter lists. In the implementation, changing just the return type (as shown at (3) and (4) in the preceding example), 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. Only methods declared in the same class and those that are inherited by the class can be overloaded. Overloaded methods should be considered to be 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 §7.10 on page 316. Method overloading should not be confused with method overriding (§7.2, p. 268). 3.3 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. For the purpose of this book, we will use the following simplified syntax of a constructor: Click here to view code image accessibility_modifier class_name (formal_parameter_list) throws_clause // Constructor header { // Constructor body local_variable_declarations 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 §4.7, p. 123. • 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 (§6.4, p. 224). • 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.2, where a method declared at (2) has the same name as the constructor declared at (1). A method must always specify a return type, whereas a constructor does not. However, using such naming schemes is strongly discouraged. A constructor that has no parameters, like the one at (1) in Example 3.2, is called a noargument constructor. WOW! eBook www.wowebook.org Example 3.2 Namespaces Click here to view code image public class Name { Name() { // (1) No-argument constructor System.out.println(“Constructor”); } void Name() { // (2) Instance method 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 If a class does not specify any constructors, then a default constructor is generated for the class by the compiler. The default constructor is equivalent to the following implementation: Click here to view code image class_name() { super(); } superclass constructor. // No parameters. Calls A default constructor is a no-argument constructor. The only action taken by the default constructor is to call the superclass constructor. This ensures that the inherited state of the object is initialized properly (§7.5, p. 282). 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: Click here to view code image class Light { // Fields: int noOfWatts; boolean indicator; String location; // No constructors //… } // Wattage // On or off // Placement class Greenhouse { // … Light oneLight = new Light(); } // (1) Call to default constructor WOW! eBook www.wowebook.org In this code, the following 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 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 its own constructors, rather than relying on the default constructor. In the following example, the class Light provides a no-argument constructor at (1). Click here to view code image class Light { // … Light() { noOfWatts = 50; indicator = true; location = “X”; } //… } // (1) No-argument constructor class Greenhouse { // … Light extraLight = new Light(); constructor } // (2) Call of explicit default The no-argument 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 constructor, it can no longer rely on the default constructor to set the state of its objects. If such a class requires a no-argument constructor, it must provide its own implementation, as in the preceding example. In the next example the class Light does not provide a no-argument constructor, but rather includes a non-zero argument 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 compiletime error, as shown at (3). Click here to view code image class Light { // … // Only non-zero argument constructor: Light(int noOfWatts, boolean indicator, String location) { this.noOfWatts = noOfWatts; this.indicator = indicator; this.location = location; } //… } class Greenhouse { WOW! eBook www.wowebook.org // (1) // … Light moreLight = new Light(100, true, “Greenhouse”);// (2) OK Light firstLight = new Light(); // (3) Compile-time error } Overloaded Constructors Like methods, constructors can 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 explicit implementation of the noargument constructor at (1) and that of a non-zero argument constructor at (2). The constructors are overloaded, as is evident by their signatures. The non-zero argument constructor at (2) is called when an object of the class Light is created at (3), and the noargument constructor is likewise called at (4). Overloading of constructors allows appropriate initialization of objects on creation, depending on the constructor invoked (see chaining of constructors in §7.5, p. 282). It is recommended to use the @param tag in a Javadoc comment to document the formal parameters of a constructor. Click here to view code image class Light { // … // No-argument constructor: Light() { noOfWatts = 50; indicator = true; location = “X”; } // (1) // Non-zero argument constructor: Light(int noOfWatts, boolean indicator, String location) { // (2) this.noOfWatts = noOfWatts; this.indicator = indicator; this.location = location; } //… } class Greenhouse { // … Light moreLight = new Light(100, true, “Greenhouse”); Light firstLight = new Light(); } Review Questions 3.1 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) { /* ... */ } WOW! eBook www.wowebook.org // (3) OK // (4) OK (d) method4() { /* ... */ } (e) method5(void) { /* ... */ } 3.2 Which statements, when inserted at (1), will not result in compile-time errors? Click here to view code image 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? Click here to view code image 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 first pair of methods will not compile. (e) The second pair of methods will not compile. (f) The third pair of methods will not compile. 3.4 Given a class named Book, which one of these constructor declarations is valid for the class Book? Select the one correct answer. WOW! eBook www.wowebook.org (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) A class must define a constructor. (b) A constructor can be declared private. (c) A constructor can return a value. (d) A constructor must initialize all fields when a class is instantiated. (e) A constructor can access the non-static members of a class. 3.6 What will be the result of compiling the following program? Click here to view code image 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 compile-time error will occur at (1). (b) A compile-time error will occur at (2). (c) A compile-time error will occur at (3). (d) The program will compile without errors. 3.4 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 after the array has been created. In Java, arrays are objects. Arrays can be of primitive data types or reference types. In the WOW! eBook www.wowebook.org 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 public 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, multidimensional arrays are implemented as array of arrays. Passing array references as parameters is discussed in §3.5, p. 72. Type conversions for array references on assignment and on method invocation are discussed in §7.7, p. 309. Declaring Array Variables A one-dimensional array variable declaration has either of the following syntaxes: 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. As a consequence, 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. Instead, it simply declares a reference that can refer to an array object. The [] notation can also be specified after a variable name to declare it as an array variable, but then it applies to just that variable. Click here to view code image int anIntArray[], oneInteger; Pizza[] mediumPizzas, largePizzas; These two declarations 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 Pizza objects, but the variable oneInteger cannot denote an array of int values—it is a simple variable of the type int. An array variable that is declared as a field in a class, but is not explicitly 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 (§2.4, p. 42). This behavior should not be confused with initialization of the elements of an array during array construction. WOW! eBook www.wowebook.org 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: Click here to view code image array_name = new element_type[array_size]; 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 at runtime. Given the declarations Click here to view code image int anIntArray[], oneInteger; Pizza[] mediumPizzas, largePizzas; the three arrays in the declarations can be constructed as follows: Click here to view code image 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. Click here to view code image element_type1[] array_name = new element_type2[array_size]; In the preceding syntax, the array type element_type2[] must be assignable to the array type element_type1[] (§7.7, p. 309). When the array is constructed, all of 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 the next examples, the code constructs the array, and the array elements are implicitly initialized to their default values. For example, all elements of the array anIntArray get the value 0, and all elements of the array mediumPizzas get the value null when the arrays are constructed. Click here to view code image int[] anIntArray = new int[10]; Pizza[] mediumPizzas = new Pizza[5]; null // Default element value: 0 // Default element value: 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 WOW! eBook www.wowebook.org 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: Click here to view code image element_type[] array_name = { array_initialize_list }; This form of initialization applies to fields as well as to local arrays. The array_initialize_list is a comma-separated list of zero or more expressions. Such an array initializer results in the construction and initialization of the array. Click here to view code image int[] anIntArray = {13, 49, 267, 15, 215}; In this declaration statement, the variable anIntArray is declared as a reference to an array of ints. The array initializer results in the construction of an array to hold five 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. Click here to view code image Pizza[] pizzaOrder = { new Pizza(), new Pizza(), null }; In this declaration statement, the variable pizzaOrder is declared as a reference to an array of Pizza objects. The array initializer constructs an array 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. The reference value of the array of Pizza objects is assigned to the reference pizzaOrder. Note also that this declaration statement actually creates three objects: 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 preceding examples, the array_initialize_list is terminated by the right brace, }, of the block. The list can also be legally terminated by a comma. The following array has length 2, and not 3: Click here to view code image 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. Click here to view code image // 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” WOW! eBook www.wowebook.org 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 [index_expression] Each individual element is treated as a simple variable of the element type. The index is specified by the index_expression, whose value should be promotable to an int value; otherwise, a compile-time error is flagged. Since the lower bound of an array index is always 0, the upper bound is 1 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 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 runtime exception (§6.5, p. 230), but an illegal index is typically an indication of a programming error. In the array element access expression, the array_name can be any expression that returns a reference to an array. For example, the expression on the right-hand side of the following assignment statement returns the character 'H' at index 1 in the character array returned by a call to the toCharArray() method of the String class: Click here to view code image char letter = “AHA”.toCharArray()[1]; // ‘H’ The array operator [] is used to declare array types (Topping[]), specify the array size (new Topping[3]), and access array elements (toppings[1]). This operator is not used when the array reference is manipulated, such as in an array reference assignment (§7.9, p. 312), or when the array reference is passed as an actual parameter in a method call (§3.5, p. 77). Example 3.3 shows traversal of arrays using for loops (§6.3, p. 215 and p. 217). A for(;;) loop at (3) in the main() method 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). Both of these methods also use a for(;;) loop. The loop variable is initialized to a start value—0 in (3) and (5), and 1 in (6). The loop condition tests whether the loop variable is less than the length of the array; this guarantees that the loop will terminate when the last element has been accessed. The loop variable is incremented after each iteration to access the next element. A for(:) loop at (4) in the main() method is used to print the minimum values from the trials, as elements are read consecutively from the array, without keeping track of an index value. WOW! eBook www.wowebook.org Example 3.3 Using Arrays Click here to view code image 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) { // Initialize the array. randomize(trialArray); // (1) // (2) // (3) // Find and store the minimum value. storeMinimum[i] = findMinimum(trialArray); } // Print the minimum values: for (double minValue : storeMinimum) System.out.printf(“%.4f%n”, minValue); (4) } public static void randomize(double[] valArray) { for (int i = 0; i < valArray.length; ++i) valArray[i] = Math.random() * 100.0; } // (5) 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) } Probable 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 can be used to construct arrays using an array creation expression: Click here to view code image element_type1[] array_name = new element_type2[array_size]; // (1) int[] intArray = new int[5]; 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. By comparison, the following declaration statement both creates the array and initializes the array elements to specific values given in the array initializer: WOW! eBook www.wowebook.org Click here to view code image element_type[] array_name = { array_initialize_list }; // (2) int[] intArray = {3, 5, 2, 8, 6}; However, the array initializer is not an expression. Java has another array creation expression, called an anonymous array, which allows the concept of the array creation expression from (1) to be combined with the array initializer from (2), so as to create and initialize an array object: Click here to view code image new element_type[] { array_initialize_list } new int[] {3, 5, 2, 8, 6} This 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 declaration statements are equivalent: Click here to view code image int[] intArray = {3, 5, 2, 8, 6}; int[] intArray = new int[] {3, 5, 2, 8, 6}; // (1) // (2) In (1), an array initializer is used to create and initialize the elements. In (2), an anonymous array expression is used. It is tempting to use the array initializer as an expression—for example, in an assignment statement, as a shortcut for assigning values to array elements in one go. However, this is illegal; instead, an anonymous array expression should be used. The concept of the anonymous array combines the definition and the creation of the array into one operation. Click here to view code image int[] daysInMonth; daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // Compile-time error daysInMonth = new int[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // OK In Example 3.4, an anonymous array is constructed at (1), and passed as an actual parameter to the static method findMinimum() defined at (2). Note that no array name or array size is specified for the anonymous array. WOW! eBook www.wowebook.org Example 3.4 Using Anonymous Arrays Click here to view code image 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 refer to other arrays. In Java, an array of arrays can be defined as follows: Click here to view code image element_type[][]...[] array_name; or Click here to view code image 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 often called multidimensional arrays. The following declarations are all equivalent: Click here to view code image int[][] mXnArray; int[] mXnArray[]; int mXnArray[][]; // 2-dimensional array // 2-dimensional array // 2-dimensional array It is customary to combine the declaration with the construction of the multidimensional array. Click here to view code image 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 five 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. WOW! eBook www.wowebook.org However, such an interpretation is not dictated by the Java language. Each row in the previous matrix is denoted by mXnArray[i], where 0 ≤ i < 4. Each element in the ith row, mXnArray[i], is accessed by mXnArray[i][j], where 0 ≤ 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 ≤ i < 4. Multidimensional arrays can also be constructed and explicitly initialized using the array initializers discussed for simple arrays. Note that each row is an array that uses an array initializer to specify its values: Click here to view code image 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; when they do not, they are called ragged arrays. The array of arrays pizzaGalore in the following code has five rows; the first four rows have different lengths but the fifth row is left unconstructed: Click here to view code image 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 such a case, these arrays are left unconstructed. For example, an array of arrays to represent a room on a floor in a 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 successively. Click here to view code image HotelRoom[][][][] rooms = new HotelRoom[10][5][][]; hotels. // Just streets and The preceding 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: Click here to view code image 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 WOW! eBook www.wowebook.org floor. The next code snippet 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.1, the array of arrays matrix is depicted after the elements have been explicitly initialized. Click here to view code image double[][] matrix = new double[3][]; // Number of rows. for (int i = 0; i < matrix.length; ++i) matrix[i] = new double[i + 1]; // Construct a row. Figure 3.1 Array of Arrays Two other ways of initializing such an array of arrays are shown next. The first approach uses array initializers, and the second uses an anonymous array of arrays. Click here to view code image double[][] matrix2 = { {0.0}, {0.0, 0.0}, {0.0, 0.0, 0.0} }; // // // // Using array initializers. 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[][], a two-dimensional array of double values. The type of the variable matrix[i] (where 0 ≤ i< matrix.length) is double[], a one-dimensional array of double values. The type of the variable matrix[i][j] (where 0 ≤ i< matrix.length and 0 ≤ j< matrix[i].length) is double, a simple variable of type double. WOW! eBook www.wowebook.org Nested loops are a natural match for manipulating multidimensional arrays. In Example 3.5, a rectangular 4 × 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 ≤ i< mXnArray.length), and the inner loop at (3) traverses the elements in each row in turn (mXnArray[i][j], where 0 ≤ j< mXnArray[i].length). The outer loop is executed mXnArray.length times, or 4 times, and the inner loop is executed (mXnArray.length) × (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. Several examples of its use are provided in §6.3, p. 217. Example 3.5 Using Multidimensional Arrays Click here to view code image 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 // Find the minimum value in a M X N matrix: int min = mXnArray[0][0]; for (int i = 0; i < mXnArray.length; ++i) // Find min in mXnArray[i], in the row given by index i: for (int j = 0; j < mXnArray[i].length; ++j) min = Math.min(min, mXnArray[i][j]); // (1) // (2) // (3) System.out.println(“Minimum value: ” + min); } } Output from the program: Minimum value: 5 Sorting Arrays Sorting implies ordering the elements according to some ranking criteria, usually based on the values of the elements. The values of numeric data types can be compared and ranked by using the relational operators. For comparing objects of a class, the class typically implements the compareTo() method of the Comparable interface. The ordering defined by this method is called the natural ordering for the objects of the class. The wrapper classes for primitive values and the String class implement the compareTo() method (§8.3, p. 350, and §8.4, p. 363, respectively). The java.util.Arrays class provides many overloaded versions of the sort() method to sort practically any type of array. WOW! eBook www.wowebook.org void sort(type[] array) Permitted type for elements includes byte, char, double, float, int, long, short, and Object. The method sorts the elements in the array according to their natural ordering. In the case of an array of objects being passed as argument, the objects must be mutually comparable; that is, it should be possible to compare any two objects in the array according to the natural ordering defined by the compareTo() method of the Comparable interface. An appropriate import statement should be included in the source code to access the java.util.Arrays class. In the next code snippet, an array of strings is sorted according to natural ordering for strings—that is, based on the Unicode values of the characters in the strings: Click here to view code image String[] strArray = {“biggest”, “big”, “bigger”, “Bigfoot”}; Arrays.sort(strArray); // Natural ordering: [Bigfoot, big, bigger, biggest] The next examples illustrate sorting an array of primitive values (int) at (1), and an array of type Object containing mutually comparable elements (String) at (2). In (3), the numerical values are autoboxed into their corresponding wrapper classes (§8.3, p. 346), but the objects of different wrapper classes and the String class are not mutually comparable. In (4), the numerical values are also autoboxed into their corresponding wrapper classes, but again the objects of different wrapper classes are not mutually comparable. A ClassCastException is thrown when the elements are not mutually comparable. Click here to view code image int[] intArray = {5, 3, 7, 1}; Arrays.sort(intArray); 5, 7] // int // (1) Natural ordering: [1, 3, Object[] objArray1 = {“I”, “am”, “OK”}; Arrays.sort(objArray1); am] // String // (2) Natural ordering: [I, OK, Object[] objArray2 = {23, “ten”, 3.14}; Arrays.sort(objArray2); // Not mutually comparable // (3) ClassCastException Number[] numbers = {23, 3.14, 10L}; Arrays.sort(numbers); // Not mutually comparable // (4) ClassCastException Searching Arrays A common operation on an array is to search the array for a given element, called the key. The java.util.Arrays class provides overloaded versions of the binarySearch() method to search in practically any type of array that is sorted. WOW! eBook www.wowebook.org Click here to view code image int binarySearch(type[] array, type key) Permitted type for elements include byte, char, double, float, int, long, short, and Object. The array must be sorted in ascending order before calling this method, or the results are unpredictable. In the case where an array of objects is passed as argument, the objects must be sorted in ascending order according to their natural ordering, as defined by the Comparable interface. The method returns the index to the key in the sorted array, if the key exists. The index is then guaranteed to be greater or equal to 0. If the key is not found, a negative index is returned, corresponding to –(insertion point + 1), where insertion point is the index of the element where the key would have been found, if it had been in the array. If there are duplicate elements equal to the key, there is no guarantee which duplicate’s index will be returned. The elements and the key must be mutually comparable. An appropriate import statement should be included in the source code to access the java.util.Arrays class. In the code that follows, the return value –3 indicates that the key would have been found at index 2 had it been in the list: Click here to view code image // Sorted String array (natural ordering): // Search in natural ordering: int index1 = Arrays.binarySearch(strArray, int index2 = Arrays.binarySearch(strArray, int index3 = Arrays.binarySearch(strArray, [Bigfoot, big, bigger, biggest] “bigger”); // Successful: 2 “bigfeet”); // Unsuccessful: -3 “bigmouth”); // Unsuccessful: -5 Results are unpredictable if the array is not sorted, or if the ordering used in the search is not the same as the sort ordering. Searching in the strArray using natural ordering when the array is sorted in reverse natural ordering gives the wrong result: Click here to view code image // Sorted String array (inverse natural ordering): [biggest, bigger, big, Bigfoot] // Search in natural ordering: int index4 = Arrays.binarySearch(strArray, “big”); // -1 (INCORRECT) A ClassCastException is thrown if the key and the elements are not mutually comparable: Click here to view code image int index5 = Arrays.binarySearch(strArray, 4); // Key: 4 => ClassCastException However, this incompatibility is caught at compile time in the case of arrays with primitive values: Click here to view code image // Sorted int array (natural ordering): [1, 3, 5, 7] int index6 = Arrays.binarySearch(intArray, 4.5);//Key: 4.5 => compile-time error! The method binarySearch() derives its name from the divide-and-conquer algorithm WOW! eBook www.wowebook.org that it uses to perform the search. It repeatedly divides the remaining elements to be searched into two halves and selects the half containing the key to continue the search in, until either the key is found or there are no more elements left to search. Review Questions 3.7 Given the following declaration, which expression returns the size of the array, assuming that the array reference has been properly 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() (g) array[].size (h) array.size 3.8 Is it possible to create arrays of length zero? Select the one correct answer. (a) Yes, you can create arrays of any type with length zero. (b) Yes, but only for primitive data types. (c) Yes, but only for arrays of reference types. (d) 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.9 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]; WOW! eBook www.wowebook.org 3.10 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 ] }; (e) int i[4] = { 1, 2, 3, 4 }; 3.11 What would be the result of compiling and running the following program? Click here to view code image public 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.12 What would be the result of compiling and running the following program? Click here to view code image 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); WOW! eBook www.wowebook.org } } Select the one correct answer. (a) The program will fail to compile because of uninitialized variables. (b) The program will throw a java.lang.NullPointerException when run. (c) The program will print 0 false NaN null. (d) The program will print 0 false 0 null. (e) The program will print null 0 0 null. (f) The program will print null false 0 null. 3.5 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). Declaring methods is discussed in §3.2, p. 49. Invoking static methods on classes is discussed in §4.8, p. 132. The syntax of a method call can be any one of the following: Click here to view code image object_reference.method_name(actual_parameter_list) class_name.static_method_name(actual_parameter_list) method_name(actual_parameter_list) The object_reference must be an expression that evaluates to a reference value denoting the object on which the method is called. If the caller and the callee are the same, object reference can be omitted (see the discussion of the this reference in §3.2, p. 50). The class_name can be the fully qualified name (§4.2, p. 97) of the class. The actual_parameter_list is comma separated if there is more than one parameter. The parentheses are mandatory even if the actual parameter list is empty. This distinguishes the method call from field access. One can specify fully qualified names for classes and packages using the dot operator (.). Click here to view code image objRef.doIt(time, place); int i = java.lang.Math.abs(-1); int j = Math.abs(-1); someMethod(ofValue); someObjRef.make().make().make(); // // // // // Explicit object reference Fully qualified class name Simple class name Object or class implicitly implied make() returns a reference value The dot operator (.) has left associativity. In the last code line, the first call of the make() method returns a reference value that denotes the object on which to execute the WOW! eBook www.wowebook.org next call, and so on. This is an example of call chaining. Each actual parameter (also called an argument) is an expression that is evaluated, and whose value is passed to the method when the method is invoked. Its value can vary from invocation to invocation. Formal parameters are parameters defined in the method declaration (§3.2, p. 49) and are local to the method (§2.4, p. 44). In Java, all parameters are passed by value—that is, an actual parameter is evaluated and its value is assigned to the corresponding formal parameter. Table 3.1 summarizes the value that is passed depending on the type of the parameters. In the case of primitive data types, the data value of the actual parameter is passed. If the actual parameter is a reference to an object, the reference value of the denoted object is passed and not the object itself. Analogously, if the actual parameter is an array element of a primitive data type, its data value is passed, and if the array element is a reference to an object, then its reference value is passed. Table 3.1 Parameter Passing by Value It should also be stressed that each invocation of a method has its own copies of the formal parameters, as is the case for any local variables in the method (§6.5, p. 230). The order of evaluation in the actual parameter list is always from left to right. The evaluation of an actual parameter can be influenced by an earlier evaluation of an actual parameter. Given the following declaration: int i = 4; the method call leftRight(i++, i); is effectively the same as leftRight(4, 5); and not the same as leftRight(4, 4); An overview of the conversions that can take place in a method invocation context is provided in §5.2, p. 148. Method invocation conversions for primitive values are discussed in the next subsection (p. 73), and those for reference types are discussed in §7.10, p. 315. Calling variable arity methods is discussed in §3.6, p. 81. For the sake of simplicity, the examples in subsequent sections primarily show method invocation on the same object or the same class. The parameter passing mechanism is no different when different objects or classes are involved. WOW! eBook www.wowebook.org Passing Primitive Data Values An actual parameter is an expression that is evaluated first, with the resulting value then being assigned to the corresponding formal parameter at method invocation. The use of this value in the method has no influence on the actual parameter. In particular, when the actual parameter is a variable of a primitive data type, the value of the variable is copied to the formal parameter at method invocation. Since formal parameters are local to the method, any changes made to the formal parameter will not be reflected in the actual parameter after the call completes. Legal type conversions between actual parameters and formal parameters of primitive data types are summarized here from Table 5.1, p. 147: • Widening primitive conversion • Unboxing conversion, followed by an optional widening primitive conversion These conversions are illustrated by invoking the following method Click here to view code image static void doIt(long i) { /* … */ } with the following code: Click here to view code image Integer intRef = 34; Long longRef = 34L; doIt(34); // (1) Primitive widening conversion: long <— int doIt(longRef); // (2) Unboxing: long <— Long doIt(intRef); // (3) Unboxing, followed by primitive widening conversion: // long <— int <— Integer However, for parameter passing, there are no implicit narrowing conversions for integer constant expressions (§5.2, p. 148). WOW! eBook www.wowebook.org Example 3.6 Passing Primitive Values Click here to view code image public class CustomerOne { public static void main (String[] args) { PizzaFactory pizzaHouse = new PizzaFactory(); int pricePrPizza = 15; System.out.println(“Value of pricePrPizza before call: ” + pricePrPizza); double totPrice = pizzaHouse.calcPrice(4, pricePrPizza); // (1) System.out.println(“Value of pricePrPizza after call: ” + pricePrPizza); } } class PizzaFactory { public double calcPrice(int numberOfPizzas, double pizzaPrice) { // (2) pizzaPrice = pizzaPrice / 2.0; // Changes price. System.out.println(“Changed pizza price in the method: ” + pizzaPrice); return numberOfPizzas * pizzaPrice; } } Output from the program: Click here to view code image Value of pricePrPizza before call: 15 Changed pizza price in the method: 7.5 Value of pricePrPizza after call: 15 In Example 3.6, the method calcPrice() is defined in the class PizzaFactory at (2). It is called from the CustomerOne.main() method at (1). The value of the first actual parameter, 4, is copied to the int formal parameter numberOfPizzas. Note that the second actual parameter pricePrPizza is of the type int, while the corresponding formal parameter pizzaPrice is of the type double. Before the value of the actual parameter pricePrPizza is copied to the formal parameter pizzaPrice, it is implicitly widened to a double. The passing of primitive values is illustrated in Figure 3.2. Figure 3.2 Parameter Passing: Primitive Data Values The value of the formal parameter pizzaPrice is changed in the calcPrice() method, but this does not affect the value of the actual parameter pricePrPizza on WOW! eBook www.wowebook.org return: It still has the value 15. The bottom line is that the formal parameter is a local variable, and changing its value does not affect the value of the actual parameter. Passing Reference Values If the actual parameter expression evaluates to a reference value, the resulting reference value is assigned to the corresponding formal parameter reference at method invocation. In particular, if an actual parameter is a reference to an object, the reference value stored in the actual parameter is passed. Consequently, both the actual parameter and the formal parameter are aliases to the object denoted by this reference value during the invocation of the method. In particular, this implies that changes made to the object via the formal parameter will be apparent after the call returns. Type conversions between actual and formal parameters of reference types are discussed in §7.10, p. 315. In Example 3.7, a Pizza object is created at (1). Any object of the class Pizza created using the class declaration at (5) always results in a beef pizza. In the call to the bake() method at (2), the reference value of the object referenced by the actual parameter favoritePizza is assigned to the formal parameter pizzaToBeBaked in the declaration of the bake() method at (3). Example 3.7 Passing Reference Values Click here to view code image public class CustomerTwo { public static void main (String[] args) { Pizza favoritePizza = new Pizza(); // (1) System.out.println(“Meat on pizza before baking: ” + favoritePizza.meat); bake(favoritePizza); // (2) System.out.println(“Meat on pizza after baking: ” + favoritePizza.meat); } public static void bake(Pizza pizzaToBeBaked) { // (3) pizzaToBeBaked.meat = “chicken”; // Change the meat on the pizza. pizzaToBeBaked = null; // (4) } } class Pizza { String meat = “beef”; } // (5) Output from the program: Click here to view code image Meat on pizza before baking: beef Meat on pizza after baking: chicken One particular consequence of passing reference values to formal parameters is that any changes made to the object via formal parameters will be reflected back in the calling method when the call returns. In this case, the reference favoritePizza will show that chicken has been substituted for beef on the pizza. Setting the formal parameter WOW! eBook www.wowebook.org pizzaToBeBaked to null at (4) does not change the reference value in the actual parameter favoritePizza. The situation at method invocation, and just before the return from method bake(), is illustrated in Figure 3.3. Figure 3.3 Parameter Passing: Reference Values In summary, the formal parameter can only change the state of the object whose reference value was passed to the method. The parameter passing strategy in Java is call by value and not call by reference, regardless of the type of the parameter. Call by reference would have allowed values in the actual parameters to be changed via formal parameters; that is, the value in pricePrPizza would be halved in Example 3.6 and favoritePizza would be set to null in Example 3.7. However, this cannot be directly implemented in Java. WOW! eBook www.wowebook.org Passing Arrays The discussion of passing reference values in the previous section is equally valid for arrays, as arrays are objects in Java. Method invocation conversions for array types are discussed along with those for other reference types in §7.10, p. 315. In Example 3.8, the idea is to repeatedly swap neighboring elements in an integer array until the largest element in the array percolates to the last position in the array. Example 3.8 Passing Arrays Click here to view code image public class Percolate { public static void main (String[] args) { int[] dataSeq = {8,4,6,2,1}; // Create and initialize an array. // Write array before percolation: printIntArray(dataSeq); // Percolate: for (int index = 1; index < dataSeq.length; ++index) if (dataSeq[index-1] > dataSeq[index]) swap(dataSeq, index-1, index); // (1) // Write array after percolation: printIntArray(dataSeq); } public static void swap(int[] intArray, int i, int j) { // (2) int tmp = intArray[i]; intArray[i] = intArray[j]; intArray[j] = tmp; } public static void swap(int v1, int v2) { error! int tmp = v1; v1 = v2; v2 = tmp; } public static void printIntArray(int[] array) { for (int value : array) System.out.print(” ” + value); System.out.println(); } // (3) Logical // (4) } Output from the program: 8 4 6 2 1 4 6 2 1 8 Note that in the declaration of the method swap() at (2), the formal parameter intArray is of the array type int[]. The swap() method is called in the main() method at (1), where one of the actual parameters is the array variable dataSeq. The reference value of the array variable dataSeq is assigned to the array variable intArray at method invocation. After return from the call to the swap() method, the array variable dataSeq will reflect the changes made to the array via the corresponding formal parameter. This situation is depicted in Figure 3.4 at the first call and return from WOW! eBook www.wowebook.org the swap() method, indicating how the values of the elements at indices 0 and 1 in the array have been swapped. Figure 3.4 Parameter Passing: Arrays However, the declaration of the swap() method at (3) will not swap two values. The method call Click here to view code image swap(dataSeq[index-1], dataSeq[index]); will have no effect on the array elements, as the swapping is done on the values of the formal parameters. The method printIntArray() at (4) also has a formal parameter of array type int[]. Note that the formal parameter is specified as an array reference using the [] notation, but this notation is not used when an array is passed as an actual parameter. Array Elements as Actual Parameters Array elements, like other variables, can store values of primitive data types or reference values of objects. In the latter case, they can also be arrays—that is, arrays of arrays (§3.4, p. 63). If an array element is of a primitive data type, its data value is passed; if it is a reference to an object, the reference value is passed. The method invocation conversions apply to the values of array elements as well. WOW! eBook www.wowebook.org Example 3.9 Array Elements as Primitive Data Values Click here to view code image public class FindMinimum { public static void main(String[] args) { int[] dataSeq = {6,4,8,2,1}; int minValue = dataSeq[0]; for (int index = 1; index < dataSeq.length; ++index) minValue = minimum(minValue, dataSeq[index]); // (1) System.out.println(“Minimum value: ” + minValue); } public static int minimum(int i, int j) { return (i <= j) ? i : j; } // (2) } Output from the program: Minimum value: 1 In Example 3.9, the value of all but one element of the array dataSeq is retrieved and passed consecutively at (1) to the formal parameter j of the minimum() method defined at (2). The discussion in §3.5, p. 73, on passing primitive values also applies to array elements that have primitive values. In Example 3.10, the formal parameter seq of the findMinimum() method defined at (4) is an array variable. The variable matrix denotes an array of arrays declared at (1) simulating a multidimensional array, which has three rows, where each row is a simple array. The first row, denoted by matrix[0], is passed to the findMinimum() method in the call at (2). Each remaining row is passed by its reference value in the call to the findMinimum() method at (3). WOW! eBook www.wowebook.org Example 3.10 Array Elements as Reference Values Click here to view code image public class FindMinimumMxN { public static void main(String[] args) { int[][] matrix = { {8,4},{6,3,2},{7} }; int min = findMinimum(matrix[0]); for (int i = 1; i < matrix.length; ++i) { int minInRow = findMinimum(matrix[i]); min = Math.min(min, minInRow); } System.out.println(“Minimum value in matrix: ” + min); // (1) // (2) // (3) } public static int findMinimum(int[] seq) { int min = seq[0]; for (int i = 1; i < seq.length; ++i) min = Math.min(min, seq[i]); return min; } // (4) } Output from the program: Minimum value in matrix: 2 Parameters A formal parameter can be declared with the keyword final preceding the parameter declaration in the method declaration. A final parameter is also known as a blank final variable; that is, it is blank (uninitialized) until a value is assigned to it, (e.g., at method invocation) and then the value in the variable cannot be changed during the lifetime of the variable (see also the discussion in §4.8, p. 133). The compiler can treat final variables as constants for code optimization purposes. Declaring parameters as final prevents their values from being changed inadvertently. A formal parameter’s declaration as final does not affect the caller’s code. The declaration of the method calcPrice() from Example 3.6 is shown next, with the formal parameter pizzaPrice declared as final: Click here to view code image public double calcPrice(int numberOfPizzas, final double pizzaPrice) { // (2’) pizzaPrice = pizzaPrice/2.0; // (3) Not allowed return numberOfPizzas * pizzaPrice; } If this declaration of the calcPrice() method is compiled, the compiler will not allow the value of the final parameter pizzaPrice to be changed at (3) in the body of the method. As another example, the declaration of the method bake() from Example 3.7 is shown here, with the formal parameter pizzaToBeBaked declared as final: WOW! eBook www.wowebook.org Click here to view code image public static void bake(final Pizza pizzaToBeBaked) { // (3) pizzaToBeBaked.meat = “chicken”; // (3a) Allowed pizzaToBeBaked = null; // (4) Not allowed } If this declaration of the bake() method is compiled, the compiler will not allow the reference value of the final parameter pizzaToBeBaked to be changed at (4) in the body of the method. Note that this applies to the reference value in the final parameter, but not to the object denoted by this parameter. The state of the object can be changed as before, as shown at (3a). 3.6 Variable Arity Methods A fixed arity method must be called with the same number of actual parameters (also called arguments) as the number of formal parameters specified in its declaration. If the method declaration specifies two formal parameters, every call of this method must specify exactly two arguments. We say that the arity of this method is 2. In other words, the arity of such a method is fixed, and it is equal to the number of formal parameters specified in the method declaration. Java also allows declaration of variable arity methods, meaning that the number of arguments in its call can be varied. As we shall see, invocations of such a method may contain more actual parameters than formal parameters. Variable arity methods are heavily employed in formatting text representation of values, as demonstrated by the variable arity method System.out.printf() that is used in many examples for this purpose. The last formal parameter in a variable arity method declaration is declared as follows: type... formal_parameter_name The ellipsis (...) is specified between the type and the formal_parameter_name. The type can be a primitive type, a reference type, or a type parameter. Whitespace can be specified on both sides of the ellipsis. Such a parameter is usually called a variable arity parameter (also known as varargs). Apart from the variable arity parameter, a variable arity method is identical to a fixed arity method. The method publish() is a variable arity method: Click here to view code image public static void publish(int n, String… data) { // (int, String[]) System.out.println(“n: ” + n + “, data size: ” + data.length); } The variable arity parameter in a variable arity method is always interpreted as having an array type: type[] In the body of the publish() method, the variable arity parameter data has the type String[], so it is a simple array of Strings. Only one variable arity parameter is permitted in the formal parameter list, and it is always the last parameter in the formal parameter list. Given that the method declaration has n WOW! eBook www.wowebook.org formal parameters, and the method call has k actual parameters, k must be equal to or greater than n – 1. The last k – n + 1 actual parameters are evaluated and stored in an array whose reference value is passed as the value of the actual parameter. In the case of the publish() method, n is equal to 2, so k can be 1, 2, 3, and so on. The following invocations of the publish() method show which arguments are passed in each method call: Click here to view code image publish(1); publish(2, “two”); publish(3, “two”, “three”); // (1, new String[] {}) // (2, new String[] {“two”}) // (3, new String[] {“two”, “three”}) Each method call results in an implicit array being created and passed as an argument. This array can contain zero or more argument values that do not correspond to the formal parameters preceding the variable arity parameter. This array is referenced by the variable arity parameter data in the method declaration. The preceding calls would result in the publish() method printing the following output: n: 1, data size: 0 n: 2, data size: 1 n: 3, data size: 2 To overload a variable arity method, it is not enough to change the type of the variable arity parameter to an explicit array type. The compiler will complain if an attempt is made to overload the method transmit(), as shown in the following code: Click here to view code image public static void transmit(String… data) { } // Compile-time error! public static void transmit(String[] data) { } // Compile-time error! These declarations would result in two methods with equivalent signatures in the same class, which is not permitted. Overloading and overriding of methods with variable arity are discussed in §7.10, p. 316. Calling a Variable Arity Method Example 3.11 illustrates various aspects of calling a variable arity method. The method flexiPrint() in the VarargsDemo class has a variable arity parameter: Click here to view code image public static void flexiPrint(Object… data) { // Object[] //… } The variable arity method prints the name of the Class object representing the actual array that is passed at runtime. It prints the number of elements in this array as well as the text representation of each element in the array. The method flexiPrint() is called in the main() method. First with the values of primitive types and Strings ((1) to (8)), then it is called with the program arguments (p. 85) supplied in the command line, ((9) to (11)). Compiling the program results in a warning at (9), which we ignore for the time being. The program can still be run, as shown in Example 3.11. The numbers at the end of the WOW! eBook www.wowebook.org lines in the output relate to numbers in the code, and are not printed by the program. Example 3.11 Calling a Variable Arity Method Click here to view code image public class VarargsDemo { public static void flexiPrint(Object… data) { // Object[] // Print the name of the Class object for the varargs parameter. System.out.print(“Type: ” + data.getClass().getName()); System.out.println(“ No. of elements: ” + data.length); System.out.print(“Element values: “); for(Object element : data) System.out.print(element + ” “); System.out.println(); } public static void int day String monthName int year main(String… args) { = 13; = “August”; = 2009; // Passing primitives and non-array types: flexiPrint(); // (1) new flexiPrint(day); // (2) new {Integer.valueOf(day)} flexiPrint(day, monthName); // (3) new {Integer.valueOf(day), // flexiPrint(day, monthName, year); // (4) new {Integer.valueOf(day), // // System.out.println(); // Passing an array type: Object[] dateInfo = {day, // {Integer.valueOf(day), monthName, // year}; // flexiPrint(dateInfo); // flexiPrint((Object) dateInfo); // dateInfo} flexiPrint(new Object[]{dateInfo});// System.out.println(); // Explicit varargs or non-varargs flexiPrint(args); flexiPrint((Object) args); flexiPrint((Object[]) args); Object[] {} Object[] Object[] monthName} Object[] monthName, Integer.valueOf(year)} (5) new Object[] monthName, Integer.valueOf(year)} (6) Non-varargs call (7) new Object[] {(Object) (8) Non-varargs call call: // (9) Warning! // (10) Explicit varargs call // (11) Explicit non-varargs call } } Compiling the program: Click here to view code image >javac VarargsDemo.java VarargsDemo.java:41: warning: non-varargs call of varargs method with inexact argument type for last parameter; flexiPrint(args); // (9) Warning! WOW! eBook www.wowebook.org ^ cast to Object for a varargs call cast to Object[] for a non-varargs call and to suppress this warning 1 warning Running the program: Click here to view code image >java VarargsDemo To arg or not to arg Type: [Ljava.lang.Object; No. of elements: Element values: Type: [Ljava.lang.Object; No. of elements: Element values: 13 Type: [Ljava.lang.Object; No. of elements: Element values: 13 August Type: [Ljava.lang.Object; No. of elements: Element values: 13 August 2009 0 (1) 1 (2) 2 (3) 3 (4) Type: [Ljava.lang.Object; No. of elements: 3 Element values: 13 August 2009 Type: [Ljava.lang.Object; No. of elements: 1 Element values: [Ljava.lang.Object;@1eed786 Type: [Ljava.lang.Object; No. of elements: 1 Element values: [Ljava.lang.Object;@1eed786 (6) Type: [Ljava.lang.String; No. of elements: 6 Element values: To arg or not to arg Type: [Ljava.lang.Object; No. of elements: 1 Element values: [Ljava.lang.String;@187aeca Type: [Ljava.lang.String; No. of elements: 6 Element values: To arg or not to arg (9) (7) (8) (10) (11) Variable Arity and Fixed Arity Method Calls The calls in (1) to (4) in Example 3.11 are all variable arity calls, as an implicit Object array is created, in which the values of the actual parameters are stored. The reference value of this array is passed to the method. The printout shows that the type of the parameter is actually an array of Objects ([Ljava.lang.Object;). The call at (6) differs from the previous calls, in that the actual parameter is an array that has the same type (Object[]) as the variable arity parameter, without having to create an implicit array. In such a case, no implicit array is created, and the reference value of the array dateInfo is passed to the method. See also the result from this call at (6) in the output. The call at (6) is a fixed arity call (also called a non-varargs call), where no implicit array is created: Click here to view code image flexiPrint(dateInfo); // (6) Non-varargs call However, if the actual parameter is cast to the type Object as in (7), a variable arity call is executed: Click here to view code image flexiPrint((Object) dateInfo); // (7) new Object[] {(Object) dateInfo} The type of the actual argument is now not the same as that of the variable arity parameter, WOW! eBook www.wowebook.org resulting in an array of the type Object[] being created, in which the array dateInfo is stored as an element. The printout at (7) shows that only the text representation of the dateInfo array is printed, and not its elements, as it is the sole element of the implicit array. The call at (8) is a fixed arity call, for the same reason as the call in (6). Now, however, the array dateInfo is explicitly stored as an element in an array of the type Object[] that matches the type of the variable arity parameter: Click here to view code image flexiPrint(new Object[]{dateInfo});// (8) Non-varargs call The output from (8) is the same as the output from (7), where the array dateInfo was passed as an element in an implicitly created array of type Object[]. The compiler issues a warning for the call at (9): Click here to view code image flexiPrint(args); // (9) Warning! The actual parameter args is an array of the type String[], which is a subtype of Object[]—the type of the variable arity parameter. The array args can be passed in a fixed arity call as an array of the type String[], or in a variable arity call as an element in an implicitly created array of the type Object[]. Both calls are feasible and valid in this case. Note that the compiler chooses a fixed arity call rather than a variable arity call, but also issues a warning. The result at (9) confirms this course of action. The array args of the type String[] is explicitly passed as an Object in a variable arity call at (10), similar to the call at (7): Click here to view code image flexiPrint((Object) args); // (10) Explicit varargs call The array args of type String[] is explicitly passed as an array of the type Object[] in a fixed arity call at (11). This call is equivalent to the call at (9), where the widening reference conversion is implicit, but now without a warning at compile time. The two calls print the same information, as is evident from the output at (9) and (11): Click here to view code image flexiPrint((Object[]) args); 3.7 The // (11) Explicit non-varargs call Method The mechanics of compiling and running Java applications using the JDK are outlined in §1.10, p. 16. The java command executes a method called main in the class specified on the command line. Any class can have a main() method, but only the main() method of the class specified in the java command starts the execution of a Java application. The main() method must have public accessibility so that the JVM can call this method (§4.7, p. 123). It is a static method belonging to the class, so that no object of the class is required to start the execution (§4.8, p. 132). It does not return a value; that is, WOW! eBook www.wowebook.org it is declared as void (§6.4, p. 224). It always has an array of String objects as its only formal parameter. This array contains any arguments passed to the program on the command line (see the next subsection). The following method header declarations fit the bill, and any one of them can be used for the main() method: Click here to view code image public static void main(String[] args) public static void main(String… args) // Method header // Method header The three modifiers can occur in any order in the method header. The requirements given in these examples do not exclude specification of additional modifiers (§4.8, p. 131) or any throws clause (§6.9, p. 251). The main() method can also be overloaded like any other method (§3.2, p. 52). The JVM ensures that the main() method having the previously mentioned method header is the starting point of program execution. Program Arguments Any arguments passed to the program on the command line can be accessed in the main() method of the class specified on the command line: >java Colors red green blue These arguments are called program arguments. Note that the command name, java, and the class name Colors are not passed to the main() method of the class Colors, nor are any other options that are specified on the command line passed to this method. Since the formal parameter of the main() method is an array of String objects, individual String elements in the array can be accessed by using the [] operator. In Example 3.12, the three arguments red, green, and blue can be accessed in the main() method of the Colors class as args[0], args[1], and args[2], respectively. The total number of arguments is given by the field length of the String array args. Note that program arguments can be passed only as strings, and must be explicitly converted to other values by the program, if necessary. When no arguments are specified on the command line, an array of zero String elements is created and passed to the main() method. Thus the reference value of the formal parameter in the main() method is never null. Program arguments supply information to the application, which can be used to tailor the runtime behavior of the application according to user requirements. WOW! eBook www.wowebook.org Example 3.12 Passing Program Arguments Click here to view code image public class Colors { public static void main(String[] args) { System.out.println(“No. of program arguments: ” + args.length); for (int i = 0; i < args.length; i++) System.out.println(“Argument no. ” + i + ” (” + args[i] + “) has ” + args[i].length() + ” characters.”); } } Running the program: Click here to view code image >java Colors red green blue No. of program arguments: 3 Argument no. 0 (red) has 3 characters. Argument no. 1 (green) has 5 characters. Argument no. 2 (blue) has 4 characters. 3.8 Enumerated Types In this section we provide a basic introduction to 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 constants is to declare them as final, static variables in a class (or interface) declaration: Click here to view code image 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 type-safe, 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 a special kind of class type that is much more powerful than the approach outlined earlier for defining collections of named constants. Declaring Type-safe Enums The canonical form of declaring an enum type is shown here: Click here to view code image public enum MachineState { BUSY, IDLE, BLOCKED // Enum header // Enum body // Enum constants WOW! eBook www.wowebook.org } The keyword enum is used to declare an enum type, as opposed to the keyword class for a class declaration. The basic notation requires the enum type name in enum header, and a comma-separated list of enum constants can be specified in the enum body. Optionally, an access modifier can also be specified in the enum header, as for a (toplevel) class. In the example enum declaration, the name of the enum type is MachineState. It defines three enum constants with explicit names. 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 type-safe manner. Other member declarations can be specified in the body of an enum type, but the canonical form suffices for the purpose of this book. Analogous to a class declaration, an enum type is compiled to Java bytecode that is placed in a separate class file. The enum types java.time.Month and java.time.DayOfWeek are two examples of enum types from the Java SE platform API. As we would expect, the Month enum type represents the months from JANUARY to DECEMBER, and the DayOfWeek enum type represents the days of the week from MONDAY to SUNDAY. Examples of their usage can be found in §11.2, p. 462. Some additional examples of enum types follow: Click here to view code image public enum MarchingOrders { LEFT, RIGHT } public enum TrafficLightState { RED, YELLOW, GREEN } enum MealType { BREAKFAST, LUNCH, DINNER } Using Type-safe Enums Example 3.13 illustrates the use of enum constants. An enum type is essentially used in the same way as any other reference type. Enum constants are actually public, static, final fields of the enum type, and they are implicitly initialized with instances 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 or an interface. Example 3.13 shows a machine client that uses a machine whose state is an enum constant. In this example, 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). WOW! eBook www.wowebook.org Example 3.13 Using Enums Click here to view code image // File: MachineState.java public enum MachineState { BUSY, IDLE, BLOCKED } // File: Machine.java public class Machine { private MachineState state; public void setState(MachineState state) { this.state = state; } public MachineState getState() { return this.state; } } // File: MachineClient.java public class MachineClient { public static void main(String[] args) { Machine machine = new Machine(); machine.setState(MachineState.IDLE); // machine.setState(1); error! // (1) Passed as a value. // (2) Compile-time MachineState state = machine.getState(); reference. System.out.println( “Current machine state: ” + state name. ); // (3) Declaring a // MachineState newState = new MachineState(); error! // (5) Compile-time // (4) Printing the enum System.out.println(“All machine states:”); for (MachineState ms : MachineState.values()) { // (6) Traversing over enum System.out.println(ms + “:” + ms.ordinal()); // contants. } System.out.println(“Comparison:”); MachineState state1 = MachineState.BUSY; MachineState state2 = state1; MachineState state3 = MachineState.BLOCKED; System.out.println(state1 + ” == ” + state2 + “: ” + (state1 == state2)); System.out.println(state1 + ” is equal to ” + state2 + “: ” + (state1.equals(state2))); System.out.println(state1 + ” is less than ” + state3 + “: ” + (state1.compareTo(state3) < 0)); } } Output from the program: Click here to view code image Current machine state: IDLE All machine states: BUSY:0 IDLE:1 WOW! eBook www.wowebook.org // (7) // (8) // (9) BLOCKED:2 Comparison: BUSY == BUSY: true BUSY is equal to BUSY: true BUSY is less than BLOCKED: true Selected Methods for Enum Types All enum types implicitly have the following useful method: Click here to view code image static EnumTypeName[] values() Returns an array containing the enum constants of this enum type, in the order they are specified. The loop at (6) in Example 3.13 illustrates traversing over all the MachineState enum constants in the order they are specified. An array containing all the MachineState constants is obtained by calling the static method values() on the enum type. All enum types are subtypes of the java.lang.Enum class, which provides the default behavior. All enum types inherit the following selected methods from the java.lang.Enum class: Click here to view code image final boolean equals(Object other) This method returns true if the specified object is equal to this enum constant. final int compareTo(E other) The natural order of the enum constants in an enum type is based on their ordinal values (see the ordinal() method next). The compareTo() method of the Comparable interface returns the value zero if this enum constant is equal to the other enum constant, a value less than zero if this enum constant is less than the other enum constant, or a value greater than zero if this enum constant is greater than the other enum constant. 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. Note that the equality test implemented by the equals() method is based on reference equality (==) of the enum constants, not on value equality. 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 constant—in other words, whether the references are aliases. Thus, for any two enum references state1 WOW! eBook www.wowebook.org and state2, the expressions state1.equals(state2) and state1 == state2 are equivalent, as shown at (7) and (8) in Example 3.13. The ordinal value of the constants in an enum type determines the result of comparing such constants with the compareTo() method, as shown at (9) in Example 3.13. Review Questions 3.13 What will the following program print when run? Click here to view code image public class ParameterPass { public static void main(String[] args) { int i = 0; addTwo(i++); System.out.println(i); } static void addTwo(int i) { i += 2; } } Select the one correct answer. (a) 0 (b) 1 (c) 2 (d) 3 3.14 What will be the result of compiling and running the following program? Click here to view code image public class Passing public static void int a = 0; int b int[] bArr = new { main(String[] args) { = 0; int[1]; bArr[0] = b; inc1(a); inc2(bArr); System.out.println(“a=” + a + ” b=” + b + ” bArr[0]=” + bArr[0]); } public static void inc1(int x) { x++; } public static void inc2(int[] x) { x[0]++; } } Select the one correct answer. (a) The code will fail to compile, since x[0]++; is not a legal statement. (b) The code will compile and will print a=1 b=1 bArr[0]=1 at runtime. (c) The code will compile and will print a=0 b=1 bArr[0]=1 at runtime. (d) The code will compile and will print a=0 b=0 bArr[0]=1 at runtime. WOW! eBook www.wowebook.org (e) The code will compile and will print a=0 b=0 bArr[0]=0 at runtime. 3.15 Which statements, when inserted at (1), will result in a compile-time error? Click here to view code image public class ParameterUse { static void main(String[] args) { int a = 0; final int b = 1; int[] c = { 2 }; final int[] d = { 3 }; useArgs(a, b, c, d); } static void useArgs(final int a, int b, final int[] c, int[] d) { // (1) INSERT STATEMENT HERE. } } Select the two correct answers. (a) a++; (b) b++; (c) b = a; (d) c[0]++; (e) d[0]++; (f) c = d; 3.16 Which of the following method declarations are valid declarations? Select the three correct answers. (a) void compute(int… is) { } (b) void compute(int is…) { } (c) void compute(int… is, int i, String… ss) { } (d) void compute(String… ds) { } (e) void compute(String… ss, int len) { } (f) void compute(char[] ca, int… is) { } 3.17 Given the following code: Click here to view code image public class RQ810A40 { static void print(Object… obj) { System.out.println(“Object…: ” + obj[0]); } public static void main(String[] args) { // (1) INSERT METHOD CALL HERE. } } Which method call, when inserted at (1), will not result in the following output WOW! eBook www.wowebook.org from the program: Object…: 9 Select the one correct answer. (a) print("9", "1", "1"); (b) print(9, 1, 1); (c) print(new int[] {9, 1, 1}); (d) print(new Integer[] {9, 1, 1}); (e) print(new String[] {"9", "1", "1"}); (f) print(new Object[] {"9", "1", "1"}); (g) None of the above. Chapter Summary The following topics were covered in this chapter: • An overview of declarations that can be specified in a class • Declaration of methods, usage of the this reference in an instance method, and method overloading • Declaration of constructors, usage of the default constructor, and overloading of constructors • Explanation of declaration, construction, initialization, and usage of both onedimensional and multidimensional arrays, including anonymous arrays • Sorting and searching arrays • Parameter passing, both primitive values and object references, including arrays and array elements; and declaring final parameters • Declaring and calling methods with variable arity • Declaration of the main() method whose execution starts the application • Passing program arguments to the main() method • Declaring and using simple enum types Programming Exercise 3.1 Write a program to grade a short multiple-choice quiz. The correct answers for the quiz are 1. 2. 3. 4. C A B D 5. 6. 7. 8. B C C A WOW! eBook www.wowebook.org Assume that the passing marks are at least 5 out of 8. The program stores the correct answers in an array. The submitted answers are specified as program arguments. Let X represent a question that was not answered on the quiz. Use an enum type to represent the result of answering a question. Example of running the program: Click here to view code image >java QuizGrader C B B D B C A X Question Submitted Ans. Correct Ans. Result 1 C C CORRECT 2 B A WRONG 3 B B CORRECT 4 D D CORRECT 5 B B CORRECT 6 C C CORRECT 7 A C WRONG 8 X A UNANSWERED No. of correct answers: 5 No. of wrong answers: 2 No. of questions unanswered: 1 The candidate PASSED. WOW! eBook www.wowebook.org 4. Access Control 4.1 Java Source File Structure The structure of a skeletal Java source file is depicted in Figure 4.1. A Java source file can have the following elements that, if present, must be specified in the following order: 1. An optional package declaration to specify a package name. Packages are discussed in §4.2. 2. Zero or more import declarations. Since import declarations introduce type or static member names in the source code, they must be placed before any type declarations. Both type and static import declarations are discussed in §4.2. 3. Any number of top-level type declarations. Class, enum, and interface declarations are collectively known as type declarations. Since these declarations belong to the same package, they are said to be defined at the top level, which is the package level. The type declarations can be defined in any order. Technically, a source file need not have any such declarations, but that is hardly useful. The JDK imposes the restriction that at most one public class declaration per WOW! eBook www.wowebook.org source file can be defined. If a public class is defined, the file name must match this public class. For example, if the public class name is NewApp, the file name must be NewApp.java. Classes are discussed in §3.1, p. 48; enums are discussed in §3.8, p. 87; and interfaces are discussed in §7.6, p. 290. Figure 4.1 Java Source File Structure Note that except for the package and the import statements, all code is encapsulated in classes, interfaces, and enums. No such restriction applies to comments and whitespace. 4.2 Packages A package in Java is an encapsulation mechanism that can be used to group related classes, interfaces, enums, and subpackages. Figure 4.2 shows an example of a package hierarchy, comprising a package called wizard that contains two other packages: pandorasbox and spells. The package pandorasbox has a class called Clown that implements an interface called Magic, also found in the same package. In addition, the package pandorasbox has a class called LovePotion and a subpackage called artifacts containing a class called Ailment. The package spells has two classes: Baldness and LovePotion. The class Baldness is a subclass of class Ailment found in the subpackage artifacts in the package pandorasbox. WOW! eBook www.wowebook.org Figure 4.2 Package Hierarchy The dot (.) notation is used to uniquely identify package members in the package hierarchy. The class wizard.pandorasbox.LovePotion, for example, is different from the class wizard.spells.LovePotion. The Ailment class can be easily identified by the name wizard.pandorasbox.artifacts.Ailment, which is known as the fully qualified name of the type. Note that the fully qualified name of the type in a named package comprises the fully qualified name of the package and the simple name of the type. The simple type name Ailment and the fully qualified package name wizard.pandorasbox.artifacts together define the fully qualified type name wizard.pandorasbox.artifacts.Ailment. Analogously, the fully qualified name of a subpackage comprises the fully qualified name of the parent package and the simple name of the subpackage. Java programming environments usually map the fully qualified name of packages to the underlying (hierarchical) file system. For example, on a Unix system, the class file LovePotion.class corresponding to the fully qualified name wizard.pandorasbox.LovePotion would be found under the directory wizard/pandorasbox. Conventionally, a global naming scheme based on the Internet domain names is used to uniquely identify packages. If the package wizard was implemented by a company called Sorcerers Limited that owns the domain sorcerersltd.com, its fully qualified name would be com.sorcerersltd.wizard Because domain names are unique, packages with this naming scheme are globally identifiable. It is not advisable to use the top-level package names java and sun, as these are reserved for the Java designers. The subpackage wizard.pandorasbox.artifacts could easily have been placed WOW! eBook www.wowebook.org elsewhere, as long as it was uniquely identified. Subpackages in a package do not affect the accessibility of the other package members. For all intents and purposes, subpackages are more an organizational feature rather than a language feature. Accessibility of members in a package is discussed in §4.4. Accessibility of members defined in type declarations is discussed in §4.7. Defining Packages A package hierarchy represents an organization of the Java classes and interfaces. It does not represent the source code organization of the classes and interfaces. The source code is of no consequence in this regard. Each Java source file (also called compilation unit) can contain zero or more type declarations, but the compiler produces a separate class file containing the Java bytecode for each of them. A type declaration can indicate that its Java bytecode should be placed in a particular package, using a package declaration. The package statement has the following syntax: Click here to view code image package fully_qualified_package_name; At most one package declaration can appear in a source file, and it must be the first statement in the source file. The package name is saved in the Java bytecode for the types contained in the package. Java naming conventions recommend writing package names in lowercase letters. Note that this scheme has two consequences. First, all the classes and interfaces in a source file will be placed in the same package. Second, several source files can be used to specify the contents of a package. If a package declaration is omitted in a compilation unit, the Java bytecode for the declarations in the compilation unit will belong to an unnamed package (also called the default package), which is typically synonymous with the current working directory on the host system. Example 4.1 illustrates how the packages in Figure 4.2 can be defined using the package declaration. There are four compilation units. Each compilation unit has a package declaration, ensuring that the type declarations are compiled into the correct package. The complete code can be found in Example 4.8 on page 118. WOW! eBook www.wowebook.org Example 4.1 Defining Packages and Using Type Import Click here to view code image // File name: Clown.java package wizard.pandorasbox; // Package declaration import wizard.pandorasbox.artifacts.Ailment; // Importing specific class public class Clown implements Magic { /* … */ } interface Magic { /* … */ } // File name: LovePotion.java package wizard.pandorasbox; // Package declaration public class LovePotion { /* … */ // File name: Ailment.java package wizard.pandorasbox.artifacts; // Package declaration public class Ailment { /* … */ } // File name: Baldness.java package wizard.spells; // Package declaration import wizard.pandorasbox.*; import wizard.pandorasbox.artifacts.*; // (1) Type-import-on-demand // (2) Import from subpackage public class Baldness extends Ailment { wizard.pandorasbox.LovePotion tlcOne; name LovePotion tlcTwo; // … } // Simple name for Ailment // (3) Fully qualified class // Class in same package class LovePotion { /* … */ } Using Packages The import facility in Java makes it easier to use the contents of packages. This subsection discusses importing reference types and static members of reference types from packages. Importing Reference Types The accessibility of types (classes, interfaces, and enums) in a package determines their access from other packages. Given a reference type that is accessible from outside a package, the reference type can be accessed in two ways. One way is to use the fully qualified name of the type. However, writing long names can become tedious. The second way is to use the import declaration that provides a shorthand notation for specifying the name of the type, often called type import. The import declarations must be the first statement after any package declaration in a source file. The simple form of the import declaration has the following syntax: Click here to view code image WOW! eBook www.wowebook.org import fully_qualified_type_name; This is called single-type-import. As the name implies, such an import declaration provides a shorthand notation for a single type. The simple name of the type (that is, its identifier) can now be used to access this particular type. Given the import declaration Click here to view code image import wizard.pandorasbox.Clown; the simple name Clown can be used in the source file to refer to this class. Alternatively, the following form of the import declaration can be used: Click here to view code image import fully_qualified_package_name.*; This is called type-import-on-demand. It allows any type from the specified package to be accessed by its simple name. An import declaration does not recursively import subpackages. The declaration also does not result in inclusion of the source code of the types; rather, it simply imports type names (that is, it makes type names available to the code in a compilation unit). All compilation units implicitly import the java.lang package (§8.1, p. 342). This is the reason why we can refer to the class String by its simple name, and need not use its fully qualified name java.lang.String all the time. Example 4.1 shows several usages of the import statement. Here we will draw attention to the class Baldness in the file Baldness.java. This class relies on two classes that have the same simple name LovePotion but are in different packages: wizard.pandorasbox and wizard.spells, respectively. To distinguish between the two classes, we can use their fully qualified names. However, since one of them is in the same package as the class Baldness, it is enough to fully qualify the class from the other package. This solution is used in Example 4.1 at (3). Note that the import of the wizard.pandorasbox package at (1) becomes redundant. Such name conflicts can usually be resolved by using variations of the import statement together with fully qualified names. The class Baldness extends the class Ailment, which is in the subpackage artifacts of the wizard.pandorasbox package. The import declaration at (2) is used to import the types from the subpackage artifacts. The following example shows how a single-type-import declaration can be used to disambiguate a type name when access to the type is ambiguous by its simple name. The following import statement allows the simple name List to be used as shorthand for the java.awt.List type as expected: Click here to view code image import java.awt.*; // imports all reference types from java.awt Given the two import declarations Click here to view code image WOW! eBook www.wowebook.org import java.awt.*; import java.util.*; // imports all type names from java.awt // imports all type names from java.util the simple name List is now ambiguous, because both the types java.util.List and java.awt.List match. Adding a single-type-import declaration for the java.awt.List type allows the simple name List to be used as a shorthand notation for this type: Click here to view code image import java.awt.*; import java.util.*; import java.awt.List; explicitly // imports all type names from java.awt // imports all type names from java.util // imports the type List from java.awt Importing Static Members of Reference Types Analogous to the type import facility, Java also allows import of static members of reference types from packages, often called static import. Static import allows accessible static members declared in a type to be imported, so that they can be used by their simple names, and therefore need not be qualified. The import applies to the whole compilation unit, and importing from the unnamed package is not permissible. The two forms of static import are shown here: Click here to view code image // Single-static-import: imports a specific static member from the designated type import static fully_qualified_type_name.static_member_name; // Static-import-on-demand: imports all static members in the designated type import static fully_qualified_type_name.*; Both forms require the use of the keyword import followed by the keyword static, although the feature is called static import. In both cases, the fully qualified name of the reference type we are importing from is required. The first form allows single static import of individual static members, and is demonstrated in Example 4.2. The constant PI, which is a static field in the class java.lang.Math, is imported at (1). Note the use of the fully qualified name of the type in the static import statement. The static method named sqrt from the class java.lang.Math is imported at (2). Only the name of the static method is specified in the static import statement; no parameters are listed. Use of any other static member from the Math class requires that the fully qualified name of the class be specified. Since types from the java.lang package are imported implicitly, the fully qualified name of the Math class is not necessary, as shown at (3). Static import on demand is easily demonstrated by replacing the two import statements in Example 4.2 by the following import statement: Click here to view code image import static java.lang.Math.*; WOW! eBook www.wowebook.org We can also dispense with the use of the class name Math in (3), as all static members from the Math class are now imported: Click here to view code image double hypotenuse = hypot(x, y); Example 4.2 // (3’) Type name can now be omitted. Single Static Import Click here to view code image import static java.lang.Math.PI; // (1) Static field import static java.lang.Math.sqrt; // (2) Static method // Only specified static members are imported. public class Calculate3 { public static void main(String[] args) { double x = 3.0, y = 4.0; double squareroot = sqrt(y); // Simple name of static method double hypotenuse = Math.hypot(x, y); // (3) Requires type name double area = PI * y * y; // Simple name of static field System.out.printf(“Square root: %.2f, hypotenuse: %.2f, area: %.2f%n”, squareroot, hypotenuse, area); } } Output from the program: Click here to view code image Square root: 2.00, hypotenuse: 5.00, area: 50.27 Example 4.3 illustrates how static import can be used to access interface constants (§7.6, p. 302). The static import statement at (1) allows the interface constants in the package mypkg to be accessed by their simple names. The static import facility avoids the MyFactory class having to implement the interface so as to access the constants by their simple name (often referred to as the interface constant antipattern): Click here to view code image public class MyFactory implements mypkg.IMachineState { // … } WOW! eBook www.wowebook.org Example 4.3 Avoiding the Interface Constant Antipattern Click here to view code image package mypkg; public interface IMachineState { // Fields are public, static and final. int BUSY = 1; int IDLE = 0; int BLOCKED = -1; } import static mypkg.IMachineState.*; constants // (1) Static import interface public class MyFactory { public static void main(String[] args) { int[] states = { IDLE, BUSY, IDLE, BLOCKED }; for (int s : states) System.out.print(s + ” “); } } Output from the program: 0 1 0 -1 Static import is ideal for importing enum constants from packages, as such constants are static members of an enum type. Example 4.4 combines type and static imports. The enum constants can be accessed at (5) using their simple names because of the static import statement at (2). The type import at (1) is required to access the enum type State by its simple name at (4) and (6). WOW! eBook www.wowebook.org Example 4.4 Importing Enum Constants Click here to view code image package mypkg; public enum State { BUSY, IDLE, BLOCKED } // File: Factory.java (in unnamed package) import mypkg.State; // (1) Single type import import static mypkg.State.*; import static java.lang.System.out; // (2) Static import on demand // (3) Single static import public class Factory { public static void main(String[] args) { State[] states = { // (4) Using type import implied by (1) IDLE, BUSY, IDLE, BLOCKED // (5) Using static import implied by (2) }; for (State s : states) // (6) Using type import implied by (1) out.print(s + ” “); // (7) Using static import implied by (3) } } Output from the program: IDLE BUSY IDLE BLOCKED Identifiers in a class can shadow static members that are imported. Example 4.5 illustrates the case where the parameter out of the method writeInfo() has the same name as the statically imported field java.lang.System.out. The type of the parameter is ShadowImport and that of the statically imported field is PrintStream. Both classes PrintStream and ShadowImport define the method println() that is called in the program. The only way to access the imported field out in the method writeInfo() is to use its fully qualified name. WOW! eBook www.wowebook.org Example 4.5 Shadowing Static Import Click here to view code image import static java.lang.System.out; // (1) Static import public class ShadowImport { public static void main(String[] args) { out.println(“Calling println() in java.lang.System.out”); ShadowImport sbi = new ShadowImport(); writeInfo(sbi); } // Parameter shadows java.lang.System.out: public static void writeInfo(ShadowImport out) { out.println(“Calling println() in the parameter out”); System.out.println(“Calling println() in java.lang.System.out”); // Qualify } public void println(String msg) { out.println(msg + ” of type ShadowImport”); } } Output from the program: Click here to view code image Calling println() in java.lang.System.out Calling println() in the parameter out of type ShadowImport Calling println() in java.lang.System.out The next code snippet illustrates a common conflict that occurs when a static field with the same name is imported by several static import statements. This conflict is readily resolved by using the fully qualified name of the field. In the case shown here, we can use the simple name of class in which the field is declared, as the java.lang package is implicitly imported by all compilation units. Click here to view code image import static java.lang.Integer.MAX_VALUE; import static java.lang.Double.MAX_VALUE; public class StaticFieldConflict { public static void main(String[] args) { System.out.println(MAX_VALUE); error! System.out.println(Integer.MAX_VALUE); System.out.println(Double.MAX_VALUE); } } // (1) Ambiguous! Compile-time // OK // OK Conflicts can also occur when a static method with the same signature is imported by several static import statements. In Example 4.6, a method named binarySearch is imported 21 times by the static import statements. This method is overloaded twice in the java.util.Collections class and 18 times in the java.util.Arrays class, in addition to one declaration in the mypkg.Auxiliary class. The classes java.util.Arrays and mypkg.Auxiliary have a declaration of this method with WOW! eBook www.wowebook.org the same signature that matches the method call at (2), resulting in a signature conflict that is flagged as a compile-time error. The conflict can again be resolved by specifying the fully qualified name of the method. If the static import statement at (1) is removed, there is no conflict, as only the class java.util.Arrays has a method that matches the method call at (2). If the declaration of the method binarySearch() at (3) is allowed, there is also no conflict, as this method declaration will shadow the imported method whose signature it matches. Example 4.6 Conflict in Importing Static Method with the Same Signature Click here to view code image package mypkg; public class Auxiliary { public static int binarySearch(int[] a, int key) { // Same in java.util.Arrays // Implementation is omitted. return -1; } } // File: MultipleStaticImport.java (in unnamed package) import static java.util.Collections.binarySearch; // 2 overloaded methods import static java.util.Arrays.binarySearch; // + 18 overloaded methods import static mypkg.Auxiliary.binarySearch; // (1) Causes signature conflict public class MultipleStaticImport { public static void main(String[] args) { int index = binarySearch(new int[] {10, 50, 100}, 50); // (2) Ambiguous! System.out.println(index); } //public static int binarySearch(int[] a, int key) { // return -1; //} } // (3) Compiling Code into Packages Conventions for specifying pathnames vary on different platforms. In this chapter, we will use pathname conventions used on a Unix platform. While trying out the examples in this section, attention should be paid to platform dependencies in this regard—especially the fact that the separator characters in file paths for the Unix and Windows platforms are / and \, respectively. As mentioned earlier, a package can be mapped on a hierarchical file system. We can think of a package name as a pathname in the file system. Referring to Example 4.1, the package name wizard.pandorasbox corresponds to the pathname wizard/pandorasbox. The Java bytecode for all types declared in the source files Clown.java and LovePotion.java will be placed in the package directory with the pathname wizard/pandorasbox, as these source files have the following package declaration: WOW! eBook www.wowebook.org package wizard.pandorasbox; The location in the file system where the package directory should be created is specified using the d option (d for destination) of the javac command. The term destination directory is a synonym for this location in the file system. The compiler will create the package directory with the pathname wizard/pandorasbox (including any subdirectories required) under the specified location, and place the Java bytecode for the types declared in the source files Clown.java and LovePotion.java inside the package directory. Assuming that the current directory (.) is the directory /pgjc/work, and the four source files in Example 4.1 are found in this directory, the following command issued in the current directory will create a file hierarchy under this directory (Figure 4.3) that mirrors the package hierarchy in Figure 4.2: Click here to view code image >javac -d . Clown.java LovePotion.java Ailment.java Baldness.java WOW! eBook www.wowebook.org Figure 4.3 File Hierarchy Note the subdirectories that are created for a fully qualified package name, and where the class files are located. In this command line, the space between the -d option and its argument is mandatory. We can specify any relative pathname that designates the destination directory, or its absolute pathname: Click here to view code image >javac -d /pgjc/work Clown.java LovePotion.java Ailment.java Baldness.java We can, of course, specify destinations other than the current directory where the class files with the bytecode should be stored. The following command in the current directory /pgjc/work will create the necessary packages with the class files under the destination WOW! eBook www.wowebook.org directory /pgjc/myapp: Click here to view code image >javac -d ../myapp Clown.java LovePotion.java Ailment.java Baldness.java Without the -d option, the default behavior of the javac compiler is to place all class files directly under the current directory (where the source files are located), rather than in the appropriate subdirectories corresponding to the packages. The compiler will report an error if there is any problem with the destination directory specified with the -d option (e.g., if it does not exist or does not have the right file permissions). Running Code from Packages Referring to Example 4.1, if the current directory has the absolute pathname /pgjc/work and we want to run Clown.class in the directory with the pathname ./wizard/pandorasbox, the fully qualified name of the Clown class must be specified in the java command: Click here to view code image >java wizard.pandorasbox.Clown This will load the bytecode of the class Clown from the file with the pathname ./wizard/pandorasbox/Clown.class, and start the execution of its main() method. 4.3 Searching for Classes The documentation for the JDK tools explains how to organize packages in more elaborate schemes. In particular, the CLASSPATH environment variable can be used to specify the class search path (usually abbreviated to just class path), which are pathnames or locations in the file system where JDK tools should look when searching for classes and other resource files. Alternatively, the -classpath option (often abbreviated to -cp) of the JDK tool commands can be used for the same purpose. The CLASSPATH environment variable is not recommended for this purpose, as its class path value affects all Java applications on the host platform, and any application can modify it. However, the -cp option can be used to set the class path for each application individually. This way, an application cannot modify the class path for other applications. The class path specified in the -cp option supersedes the path or paths set by the CLASSPATH environment variable while the JDK tool command is running. We will not discuss the CLASSPATH environment variable here, and assume it to be undefined. Basically, the JDK tools first look in the directories where the Java standard libraries are installed. If the class is not found in the standard libraries, the tool searches in the class path. When no class path is defined, the default value of the class path is assumed to be the current directory. If the -cp option is used and the current directory should be searched by the JDK tool, the current directory must be specified as an entry in the class path, just like any other directory that should be searched. This is most conveniently done WOW! eBook www.wowebook.org by including '.' as one of the entries in the class path. We will use the file hierarchies shown in Figure 4.4 to illustrate some of the intricacies involved when searching for classes. The current directory has the absolute pathname /top/src, where the source files are stored. The package pkg will be created under the directory with the absolute pathname /top/bin. The source code in the two source files A.java and B.java is also shown in Figure 4.4. Figure 4.4 Searching for Classes The file hierarchy before any files are compiled is shown in Figure 4.4a. Since the class B does not use any other classes, we compile it first with the following command, resulting in the file hierarchy shown in Figure 4.4b: >javac -d ../bin B.java Next, we try to compile the file A.java, and get the following results: >javac -d ../bin A.java A.java:3: cannot find symbol symbol : class B location: class pkg.A public class A { B b; } ^ 1 error The compiler cannot find the class B—that is, the file B.class containing the Java bytecode for the class B. In Figure 4.4b, we can see that it is in the package pkg under the directory bin, but the compiler cannot find it. This is hardly surprising, as there is no WOW! eBook www.wowebook.org bytecode file for the class B in the current directory, which is the default value of the class path. The following command sets the value of the class path to be /top/bin, and compilation is successful (Figure 4.4c): Click here to view code image >javac -cp /top/bin -d ../bin A.java It is very important to understand that when we want the JDK tool to search in a named package, it is the location of the package that is specified; in other words, the class path indicates the directory that contains the first element of the fully qualified package name. In Figure 4.4c, the package pkg is contained under the directory whose absolute path is /top/bin. The following command will not work, as the directory /top/bin/pkg does not contain a package with the name pkg that has a class B: Click here to view code image >javac -cp /top/bin/pkg -d ../bin A.java Also, the compiler is not using the class path to find the source file(s) that are specified in the command line. In the preceding command, the source file has the relative pathname ./A.java. Consequently, the compiler looks for the source file in the current directory. The class path is used to find the classes used by the class A. Given the file hierarchy in Figure 4.3, the following -cp option sets the class path so that all packages (wizard.pandorasbox, wizard.pandorasbox.artifacts, wizard.spells) in Figure 4.3 will be searched, as all packages are located under the specified directory: -cp /pgjc/work However, the following -cp option will not help in finding any of the packages in Figure 4.3, as none of the packages are located under the specified directory: Click here to view code image >java -cp /pgjc/work/wizard pandorasbox.Clown This command also illustrates an important point about package names: The fully qualified package name should not be split. The package name for the class wizard.pandorasbox.Clown is wizard.pandorasbox, and must be specified fully. The following command will search all packages in Figure 4.3 for classes that are used by the class wizard.pandorasbox.Clown: Click here to view code image >java -cp /pgjc/work wizard.pandorasbox.Clown The class path can specify several entries (i.e., several locations), and the JDK tool searches them in the order they are specified, from left to right. -cp /pgjc/work:/top/bin:. We have used the path-separator character ':' for Unix platforms to separate the entries, and also included the current directory (.) as an entry. There should be no whitespace on either side of the path-separator character. The search in the class path entries stops once the required class file is found. Therefore, WOW! eBook www.wowebook.org the order in which entries are specified can be significant. If a class B is found in a package pkg located under the directory /ext/lib1, and also in a package pkg located under the directory /ext/lib2, the order in which the entries are specified in the two cp options shown next is significant. They will result in the class pkg.B being found under /ext/lib1 and /ext/lib2, respectively. -cp /ext/lib1:/ext/lib2 -cp /ext/lib2:/ext/lib1 The examples so far have used absolute pathnames for class path entries. We can, of course, use relative pathnames as well. If the current directory has the absolute pathname /pgjc/work in Figure 4.3, the following command will search the packages under the current directory: Click here to view code image >java -cp . wizard.pandorasbox.Clown If the current directory has the absolute pathname /top/src in Figure 4.4, the following command will compile the file ./A.java: Click here to view code image >javac -cp ../bin -d ../bin A.java If the name of an entry in the class path includes whitespace, the name should be double quoted so that it will be interpreted correctly: -cp “../new bin” Review Questions 4.1 Given the source file A.java: // File: A.java package net.alphabet; import java.util.ArrayList; public class A {} class B {} Select the two correct answers. (a) Both class A and class B will be placed in the package net.alphabet. (b) Only class A will be placed in the package net.alphabet. Class B will be placed in the default package. (c) Both class A and class B can access the imported class java.util.ArrayList by its simple name. (d) Only class A can access the imported class java.util.ArrayList by its simple name. 4.2 Which import statement, when inserted independently at (1), will make the code compile? Click here to view code image // File: Window.java WOW! eBook www.wowebook.org package app; public class Window { final static String frame = “Top-frame”; } ___________________________________________________________________________ // File: Canvas.java package app; // (1) INSERT IMPORT STATEMENT HERE. public class Canvas { private String str = frame; } Select the one correct answer. (a) import app.*; (b) import app.Window; (c) import java.lang.*; (d) import java.lang.String; (e) import static app.Window.frame; 4.3 Which import statements, when inserted independently at (1), will make the code compile? Click here to view code image // File: Window.java package mainpkg.subpkg1; public class Window {} ___________________________________________________________________________ // File: Window.java package mainpkg.subpkg2; public class Window {} ___________________________________________________________________________ // File: Screen.java package mainpkg; // (1) INSERT IMPORT STATEMENTS HERE. public class Screen { private Window win; } Select the four correct answers. (a) import mainpkg.*; (b) import mainpkg.subpkg1.*; (c) import mainpkg.subpkg2.*; (d) Click here to view code image import mainpkg.subpkg1.*; import mainpkg.subpkg2.Window; WOW! eBook www.wowebook.org (e) Click here to view code image import mainpkg.subpkg1.Window; import mainpkg.subpkg2.*; (f) import mainpkg.subpkg1.*; import mainpkg.subpkg2.*; (g) Click here to view code image import mainpkg.subpkg1.Window; import mainpkg.subpkg2.Window; 4.4 Given the following code: Click here to view code image // (1) INSERT ONE IMPORT STATEMENT HERE public class RQ700A20 { public static void main(String[] args) { System.out.println(sqrt(49)); } } Which import statements, when inserted independently at (1), will make the program print 7, when the program is compiled and run? Select the two correct answers. (a) import static Math.*; (b) import static Math.sqrt; (c) import static java.lang.Math.sqrt; (d) import static java.lang.Math.sqrt(); (e) import static java.lang.Math.*; 4.5 Given the source file A.java: package top.sub; public class A {} and the following directory hierarchy: Click here to view code image /proj |– src | |– top | |– sub | |– A.java |– bin Assuming that the current directory is /proj/src, which of the following statements are true? Select the three correct answers. WOW! eBook www.wowebook.org (a) The following command will compile, and place the bytecode of the class top.sub.A under /proj/bin: javac -d . top/sub/A.java (b) The following command will compile, and place the bytecode of the class top.sub.A under /proj/bin: Click here to view code image javac -d /proj/bin top/sub/A.java (c) The following command will compile, and place the bytecode of the class top.sub.A under /proj/bin: Click here to view code image javac -D /proj/bin ./top/sub/A.java (d) The following command will compile, and place the bytecode of the class top.sub.A under /proj/bin: Click here to view code image javac -d ../bin top/sub/A.java (e) After successful compilation, the absolute pathname of the file A.class will be: /proj/bin/A.class (f) After successful compilation, the absolute pathname of the file A.class will be: /proj/bin/top/sub/A.class 4.6 Given the following directory structure: Click here to view code image /top |– wrk |– pkg |– A.java |– B.java Assume that the two files A.java and B.java contain the following code, respectively: Click here to view code image // File: A.java package pkg; class A { B b; } ___________________________________________________________________________ // File: B.java package pkg; class B {…} For which combinations of current directory and command is the compilation successful? Select the two correct answers. (a) WOW! eBook www.wowebook.org Click here to view code image Current directory: /top/wrk Command: javac -cp .:pkg A.java (b) Click here to view code image Current directory: /top/wrk Command: javac -cp . pkg/A.java (c) Current directory: /top/wrk Command: javac -cp pkg A.java (d) Click here to view code image Current directory: /top/wrk Command: javac -cp .:pkg pkg/A.java (e) Click here to view code image Current directory: /top/wrk/pkg Command: javac A.java (f) Click here to view code image Current directory: /top/wrk/pkg Command: javac -cp . A.java 4.7 Given the following directory structure: Click here to view code image /proj |– src | |– A.java | | |– bin |– top |– sub |– A.class Assume that the current directory is /proj/src. Which class path specifications will find the file A.class of the class top.sub.A declared in the file /proj/src/A.java? Select the two correct answers. (a) -cp /proj/bin/top (b) -cp /proj/bin/top/sub (c) -cp /proj/bin/top/sub/A.class (d) -cp .:../bin (e) -cp /proj WOW! eBook www.wowebook.org (f) -cp /proj/bin 4.4 Scope Rules Java provides explicit accessibility modifiers to control the accessibility of members in a class by external clients (§4.7, p. 123), but in two areas access is governed by specific scope rules: • Class scope for members: how member declarations are accessed within the class. • Block scope for local variables: how local variable declarations are accessed within a block. Class Scope for Members Class scope concerns accessing members (including inherited ones) from code within a class. Table 4.1 gives an overview of how static and non-static code in a class can access members of the class, including those that are inherited. Table 4.1 assumes the following declarations: Click here to view code image class SuperName { int instanceVarInSuper; static int staticVarInSuper; void instanceMethodInSuper() { /* … */ } static void staticMethodInSuper() { /* … */ } // … } class ClassName extends SuperName { int instanceVar; static int staticVar; void instanceMethod() { /* … */ } static void staticMethod() { /* … */ } // … } WOW! eBook www.wowebook.org Table 4.1 Accessing Members within a Class The golden rule is that static code can only access other static members by their simple names. Static code is not executed in the context of an object, so the references this and super are not available. An object has knowledge of its class, so static members are always accessible in a non-static context. Note that using the class name to access static members within the class is no different from how external clients access these static members. The following factors can all influence the scope of a member declaration: • Shadowing of a field declaration, either by local variables (§4.4, p. 117) or by declarations in the subclass (§7.3, p. 275) • Overriding an instance method from a superclass (§7.2, p. 268) • Hiding a static method declared in a superclass (§7.3, p. 275) WOW! eBook www.wowebook.org Within a class, references of the class can be declared and used to access all members in the class, regardless of their accessibility modifiers. In Example 4.7, the method duplicateLight at (1) in the class Light has the parameter oldLight and the local variable newLight that are references of the class Light. Even though the fields of the class are private, they are accessible through the two references (oldLight and newLight) in the method duplicateLight() as shown at (2), (3), and (4). Example 4.7 Class Scope Click here to view code image class Light { // Instance variables: private int noOfWatts; private boolean indicator; private String location; // Wattage // On or off // Placement // Instance methods: public void switchOn() { indicator = true; } public void switchOff() { indicator = false; } public boolean isOn() { return indicator; } public static Light duplicateLight(Light oldLight) { Light newLight = new Light(); newLight.noOfWatts = oldLight.noOfWatts; newLight.indicator = oldLight.indicator; newLight.location = oldLight.location; return newLight; } // (1) // (2) // (3) // (4) } Block Scope for Local Variables Declarations and statements can be grouped into a block using braces, {}. Blocks can be nested, and scope rules apply to local variable declarations in such blocks. A local declaration can appear anywhere in a block. The general rule is that a variable declared in a block is in scope inside the block in which it is declared, but it is not accessible outside of this block. It is not possible to redeclare a variable if a local variable of the same name is already declared in the current scope. Local variables of a method include the formal parameters of the method and variables that are declared in the method body. The local variables in a method are created each time the method is invoked, and are therefore distinct from local variables in other invocations of the same method that might be executing (§6.5, p. 230). Figure 4.5 illustrates block scope (also known as lexical scope) for local variables. A method body is a block. Parameters cannot be redeclared in the method body, as shown at (1) in Block 1. WOW! eBook www.wowebook.org Figure 4.5 Block Scope A local variable—already declared in an enclosing block and, therefore, visible in a nested block—cannot be redeclared in the nested block. These cases are shown at (3), (5), and (6). A local variable in a block can be redeclared in another block if the blocks are disjoint— that is, they do not overlap. This is the case for variable i at (2) in Block 3 and at (4) in Block 4, as these two blocks are disjoint. The scope of a local variable declaration begins from where it is declared in the block and ends where this block terminates. The scope of the loop variable index is the entire Block 2. Even though Block 2 is nested in Block 1, the declaration of the variable index at (7) in Block 1 is valid. The scope of the variable index at (7) spans from its declaration to the end of Block 1, and it does not overlap with that of the loop variable index in Block 2. 4.5 Accessibility Modifiers for Top-Level Type Declarations The accessibility modifier public can be used to declare top-level types (that is, classes, enums, and interfaces) in a package to be accessible from everywhere, both inside their own package and inside other packages. If the accessibility modifier is omitted, they will be accessible only in their own package and not in any other packages or subpackages. This is called package or default accessibility. Example 4.8 Accessibility Modifiers for Classes and Interfaces Click here to view code image WOW! eBook www.wowebook.org // File: Clown.java package wizard.pandorasbox; // Package declaration import wizard.pandorasbox.artifacts.Ailment; // Importing class Ailment public class Clown implements Magic { // (1) LovePotion tlc; // Class in same package Ailment problem; // Simple class name Clown() { tlc = new LovePotion(“passion”); problem = new Ailment(“flu”); // Simple class name } @Override public void levitate() { // (2) System.out.println(“Levitating”); } public void mixPotion() { System.out.println(“Mixing ” + tlc); } public void healAilment() { System.out.println(“Healing ” + problem); } public static void main(String[] args) { Clown joker = new Clown(); joker.levitate(); joker.mixPotion(); joker.healAilment(); } } interface Magic { void levitate(); } // (3) // File: LovePotion.java package wizard.pandorasbox; // Package declaration public class LovePotion { // (4) Accessible outside package String potionName; public LovePotion(String name) { potionName = name; } public String toString() { return potionName; } } // File: Ailment.java package wizard.pandorasbox.artifacts; // Package declaration public class Ailment { // Accessible outside package String ailmentName; public Ailment(String name) { ailmentName = name; } public String toString() { return ailmentName; } } // File: Baldness.java package wizard.spells; // Package declaration import wizard.pandorasbox.*; import wizard.pandorasbox.artifacts.*; // Redundant // Import of subpackage public class Baldness extends Ailment { wizard.pandorasbox.LovePotion tlcOne; LovePotion tlcTwo; Baldness(String name) { super(name); tlcOne = new wizard.pandorasbox. LovePotion(“romance”); tlcTwo = new LovePotion(); // Simple name for Ailment // Fully qualified name // Class in same package // Fully qualified name // Class in same package WOW! eBook www.wowebook.org } } class LovePotion /* implements Magic */ { // @Override public void levitate() {} } // (5) Magic is not accessible // (6) Cannot override method Compiling and running the program from the current directory gives the following results: Click here to view code image >javac -d . Clown.java LovePotion.java Ailment.java Baldness.java >java wizard.pandorasbox.Clown Levitating Mixing passion Healing flu In Example 4.8, the class Clown at (1) and the interface Magic at (3) are placed in a package called wizard.pandorasbox. The public class Clown is accessible from everywhere. The Magic interface has default accessibility, and can be accessed only within the package wizard.pandorasbox. It is not accessible from other packages, not even from subpackages. The class LovePotion at (4) is also placed in the package called wizard.pandorasbox. The class has public accessibility and, therefore, is accessible from other packages. The two files Clown.java and LovePotion.java demonstrate how several compilation units can be used to group classes in the same package. In the file Clown.java, the class Clown at (1) implements the interface Magic at (3) from the same package. We have used the annotation @Override in front of the declaration of the levitate() method at (2) so that the compiler can aid in checking that this method is declared correctly as required by the interface Magic. In the file Baldness.java, the class LovePotion at (5) wishes to implement the interface Magic at (3) from the package wizard.pandorasbox, but cannot do so, although the source file imports from this package. The reason is that the interface Magic has default accessibility and can, therefore, be accessed only within the package wizard.pandorasbox. The method levitate() of the Magic interface therefore cannot be overridden in class LovePotion at (6). Just because a reference type is accessible does not necessarily mean that members of the type are also accessible. Member accessibility is governed separately from type accessibility, as explained in §4.7, p. 123. Table 4.2 gives a summary of accessibility modifiers for top-level types. WOW! eBook www.wowebook.org Table 4.2 Summary of Accessibility Modifiers for Top-Level Types 4.6 Non-Accessibility Modifiers for Classes The non-accessibility modifiers abstract and final can be applied to top-level classes. Classes A class can be declared with the keyword abstract to indicate that it cannot be instantiated. A class might choose to do this if the abstraction it represents is so general that it needs to be specialized to be of practical use. The class Vehicle might be specified as abstract to represent the general abstraction of a vehicle, as creating instances of the class would not make much sense. Creating instances of non-abstract subclasses, like Car and Bus, would make more sense, as this would make the abstraction more concrete. Any normal class (that is, a class declared with the keyword class) can be declared as abstract. However, if such a class has one or more abstract methods (§4.8, p. 136), it must be declared as abstract. Obviously, such classes cannot be instantiated, as their implementation might be only partial. A class might choose this strategy to dictate certain behavior, but allow its subclasses the freedom to provide the relevant implementation. In other words, subclasses of the abstract class have to take a stand and provide implementations of any inherited abstract methods before instances can be created. A subclass that does not provide an implementation of its inherited abstract methods must also be declared as abstract or the code will not compile. In Example 4.9, the declaration of the abstract class Light has an abstract method named kwhPrice at (1). This forces its concrete (i.e., non-abstract) subclasses to provide an implementation for this method. Such a class provides implementations of all its methods. The concrete subclass TubeLight provides an implementation for the method kwhPrice() at (2). The class Factory creates an instance of the class TubeLight at (3). References of an abstract class can be declared, as shown at (4), but an abstract class cannot be instantiated, as shown at (5). References of an abstract class can refer to objects of the subclasses, as shown at (6). WOW! eBook www.wowebook.org Example 4.9 Abstract Classes Click here to view code image abstract class Light { // Fields: int noOfWatts; boolean indicator; String location; // Wattage // On or off // Placement // Instance methods: public void switchOn() { indicator = true; } public void switchOff() { indicator = false; } public boolean isOn() { return indicator; } // Abstract instance method public abstract double kwhPrice(); // (1) No method body } //______________________________________________________________________________ class TubeLight extends Light { // Field int tubeLength; // Implementation of inherited abstract method. @Override public double kwhPrice() { return 2.75; } // (2) } //______________________________________________________________________________ public class Factory { public static void main(String[] args) { TubeLight cellarLight = new TubeLight(); // (3) OK Light nightLight; // (4) OK // Light tableLight = new Light(); // (5) Compile-time error nightLight = new TubeLight(); // (6) OK System.out.println(“KWH price: $” + nightLight.kwhPrice()); } } Output from the program: KWH price: $2.75 Classes A class can be declared as final to indicate that it cannot be extended; that is, one cannot declare subclasses of a final class. This implies that one cannot override any methods declared in such a class. In other words, the class behavior cannot be changed by extending the class. A final class marks the lower boundary of its implementation inheritance hier-archy (§7.1, p. 264). Only a concrete class can be declared as final. A final class must be complete, whereas an abstract class is considered incomplete. Classes, therefore, cannot be both final and abstract at the same time. Interfaces are inherently abstract, as they can specify methods that are abstract, and therefore cannot be declared as final. A final class and an interface represent two extremes when it comes to providing an implementation. An abstract class represents a compromise between these two extremes. Table 4.3 provides a summary of non-accessibility modifiers for classes. WOW! eBook www.wowebook.org Table 4.3 Summary of Non-Accessibility Modifiers for Classes The Java SE platform API includes many final classes—for example, the java.lang.String class and the wrapper classes for primitive values. If it is decided that the class TubeLight in Example 4.9 may not be extended, it can be declared as final: Click here to view code image final class TubeLight extends Light { // … } Discussion of final methods, fields, and local variables can be found in §4.8, p. 133. Review Questions 4.8 Given the following class, which of these alternatives are valid ways of referring to the class from outside of the package net.basemaster? package net.basemaster; public class Base { // … } Select the two correct answers. (a) By simply referring to the class as Base (b) By simply referring to the class as basemaster.Base (c) By simply referring to the class as net.basemaster.Base (d) By importing with net.basemaster.*, and referring to the class as Base (e) By importing with net.*, and referring to the class as basemaster.Base 4.9 Which one of the following class declarations is a valid declaration of a class that cannot be instantiated? Select the one correct answer. (a) class Ghost { abstract void haunt(); } (b) abstract class Ghost { void haunt(); } WOW! eBook www.wowebook.org (c) abstract class Ghost { void haunt() {}; } (d) abstract Ghost { abstract void haunt(); } (e) abstract class Ghost { abstract haunt(); } 4.10 Which one of the following class declarations is a valid declaration of a class that cannot be extended? Select the one correct answer. (a) class Link { } (b) abstract class Link { } (c) native class Link { } (d) final class Link { } (e) abstract final class Link { } 4.7 Member Accessibility Modifiers By specifying member accessibility modifiers, a class can control which information is accessible to clients (that is, other classes). These modifiers help a class to define a contract so that clients know exactly which services are offered by the class. The accessibility of members can be one of the following: public protected Default accessibility (also known as package accessibility), meaning that no accessibility modifier is specified private In the following discussion of accessibility modifiers for members of a class, keep in mind that the member accessibility modifier has meaning only if the class (or one of its subclasses) is accessible to the client. Also, note that only one accessibility modifier can be specified for a member. The discussion in this section applies to both instance and static members of top-level classes. It applies equally to constructors as well. In UML notation, the prefixes +, #, and -, when applied to a member name, indicate public, protected, and private member accessibility, respectively. No prefix indicates default or package accessibility. Members Public accessibility is the least restrictive of all the accessibility modifiers. A public member is accessible from anywhere, both in the package containing its class and in other packages where this class is visible. WOW! eBook www.wowebook.org Example 4.10 contains two source files, shown at (1) and (6). The package hierarchy defined by the source files is depicted in Figure 4.6, showing the two packages, packageA and packageB, containing their respective classes. The classes in packageB use classes from packageA. The class SuperclassA in packageA has two subclasses: SubclassA in packageA and SubclassB in packageB. Figure 4.6 Public Accessibility for Members WOW! eBook www.wowebook.org Example 4.10 Public Accessibility of Members Click here to view code image // File: SuperclassA.java package packageA; (1) public class SuperclassA { public int superclassVarA; public void superclassMethodA() {/*…*/} } class SubclassA extends SuperclassA { void subclassMethodA() { superclassVarA = 10; } } class AnyClassA { SuperclassA obj = new SuperclassA(); void anyClassMethodA() { obj.superclassMethodA(); } } // File: SubclassB.java package packageB; import packageA.*; // (2) // (3) // (4) OK // (5) OK (6) public class SubclassB extends SuperclassA { void subclassMethodB() { superclassMethodA(); } } class AnyClassB { SuperclassA obj = new SuperclassA(); void anyClassMethodB() { obj.superclassVarA = 20; } } // (7) OK // (8) OK Accessibility is illustrated in Example 4.10 by the accessibility modifiers for the field superclassVarA and the method superclassMethodA() at (2) and (3), respectively, defined in the class SuperclassA. These members are accessed from four different clients in Example 4.10. • Client 1: From a subclass in the same package, which accesses an inherited field from the class SuperclassA. SubclassA is such a client, and does this at (4). • Client 2: From a non-subclass in the same package, which invokes a method on an instance of the SuperclassA class. AnyClassA is such a client, and does this at (5). • Client 3: From a subclass in another package, which invokes an inherited method from the class SuperclassA. SubclassB is such a client, and does this at (7). • Client 4: From a non-subclass in another package, which accesses a field in an instance of the SuperclassA class. AnyClassB is such a client, and does this at (8). WOW! eBook www.wowebook.org In Example 4.10, the field superclassVarA and the method superclassMethodA() have public accessibility in the SuperclassA class, and are accessible by all four of these clients. Subclasses can access their inherited public members by their simple names, and all clients can access public members in an instance of the SuperclassA class. Public accessibility is depicted in Figure 4.6. Members A protected member is accessible in all classes in the same package, and by all subclasses of its class in any package where this class is visible. In other words, non-subclasses in other packages cannot access protected members from other packages. This kind of accessibility is more restrictive than public member accessibility. In Example 4.10, if the field superclassVarA and the method superclassMethodA() of the class SuperclassA have protected accessibility, they are accessible within packageA, and only accessible by subclasses in any other packages. Click here to view code image public class SuperclassA { protected int superclassVarA; protected void superclassMethodA() {/*…*/} } // (2) Protected member // (3) Protected member Client 4 in packageB cannot access these members, as shown in Figure 4.7. Figure 4.7 Protected Accessibility for Members An important caveat is that a subclass in another package can access only protected members in the superclass via references of its own type or its subtypes. The following new declaration of SubclassB in packageB from Example 4.10 illustrates the point: Click here to view code image // File: SubclassB.java package packageB; import packageA.*; WOW! eBook www.wowebook.org public class SubclassB extends SuperclassA { SuperclassA objRefA = new SuperclassA(); void subclassMethodB(SubclassB objRefB) { objRefB.superclassMethodA(); objRefB.superclassVarA = 5; objRefA.superclassMethodA(); objRefA.superclassVarA = 10; } } // In packageB // (1) // // // // (2) (3) (4) (5) OK OK Not OK Not OK The class SubclassB declares the field objRefA of type SuperclassA at (1). The method subclassMethodB() has the formal parameter objRefB of type SubclassB. Access is permitted to a protected member of SuperclassA in packageA by a reference of the subclass, as shown at (2) and (3), but not by a reference of its superclass, as shown at (4) and (5). Access to the field superclassVarA and the call to the method superclassMethodA() occur in SubclassB. These members are declared in SuperclassA. SubclassB is not involved in the implementation of SuperclassA, which is the type of the reference objRefA. Hence, access to protected members at (4) and (5) is not permitted as these are not members of an object that can be guaranteed to be implemented by the code accessing them. Accessibility to protected members of the superclass would also be permitted via any reference whose type is a subclass of SubclassB. The previously mentioned restriction helps to ensure that subclasses in packages different from their superclass can access protected members of the superclass only in their part of the implementation inheritance hierarchy. In other words, a protected member of a superclass is accessible in a subclass that is in another package only if the member is inherited by an object of the subclass (or by an object of a subclass of this subclass). Default Accessibility for Members When no member accessibility modifier is specified, the member is accessible only to other classes in its own class’s package. Even if its class is visible in another (possibly nested) package, the member is not accessible elsewhere. Default member accessibility is more restrictive than protected member accessibility. In Example 4.10, if the field superclassVarA and the method superclassMethodA() are defined with no accessibility modifier, they are accessible within packageA, but not in any other packages. Click here to view code image public class SuperclassA { int superclassVarA; accessibility void superclassMethodA() {/*…*/} } // (2) Default // (3) Default accessibility The clients in packageB (that is, Clients 3 and 4) cannot access these members. This situation is depicted in Figure 4.8. WOW! eBook www.wowebook.org Figure 4.8 Default Accessibility for Members Members The private modifier is the most restrictive of all the accessibility modifiers. Private members are not accessible from any other classes. This also applies to subclasses, whether they are in the same package or not. Since they are not accessible by their simple names in a subclass, they are also not inherited by the subclass. A standard design strategy for a class is to make all fields private and provide public accessor methods for them. Auxiliary methods are often declared as private, as they do not concern any client. In Example 4.10, if the field superclassVarA and the method superclassMethodA() have private accessibility, they are not accessible by any other clients. Click here to view code image public class SuperclassA { private int superclassVarA; private void superclassMethodA() {/*…*/} } // (2) Private member // (3) Private member None of the clients in Figure 4.9 can access these members. Table 4.4 provides a summary of accessibility modifiers for members. WOW! eBook www.wowebook.org Figure 4.9 Table 4.4 Private Accessibility for Members Summary of Accessibility Modifiers for Members Review Questions 4.11 Given the following declaration of a class, which field is accessible from outside the package com.corporation.project? Click here to view code image package com.corporation.project; public class MyClass { int i; public int j; protected int k; private int l; } Select the one correct answer. (a) Field i is accessible in all classes in other packages. (b) Field j is accessible in all classes in other packages. (c) Field k is accessible in all classes in other packages. WOW! eBook www.wowebook.org (d) Field k is accessible in subclasses only in other packages. (e) Field l is accessible in all classes in other packages. (f) Field l is accessible in subclasses only in other packages. 4.12 How restrictive is the default accessibility compared to public, protected, and private accessibility? Select the one correct answer (a) Less restrictive than public (b) More restrictive than public, but less restrictive than protected (c) More restrictive than protected, but less restrictive than private (d) More restrictive than private (e) Less restrictive than protected from within a package, and more restrictive than protected from outside a package 4.13 Which statement is true about the accessibility of members? Select the one correct answer. (a) A private member is always accessible within the same package. (b) A private member can be accessed only within the class of the member. (c) A member with default accessibility can be accessed by any subclass of the class in which it is declared. (d) A private member cannot be accessed at all. (e) Package/default accessibility for a member can be declared using the keyword default. 4.14 Which lines that are marked will compile in the following code? Click here to view code image // File name: A.java package packageA; public class A { protected int pf; } // File name: B.java package packageB; import packageA.A; public class B extends A { void action(A obj1, B obj2, C obj3) { pf = 10; // (1) obj1.pf = 10; // (2) obj2.pf = 10; // (3) obj3.pf = 10; // (4) } } WOW! eBook www.wowebook.org class C extends B { void action(A obj1, B obj2, C obj3) { pf = 10; // (5) obj1.pf = 10; // (6) obj2.pf = 10; // (7) obj3.pf = 10; // (8) } } class D { void action(A obj1, B obj2, C obj3) { pf = 10; // (9) obj1.pf = 10; // (10) obj2.pf = 10; // (11) obj3.pf = 10; // (12) } } Select the five correct answers. (a) (1) (b) (2) (c) (3) (d) (4) (e) (5) (f) (6) (g) (7) (h) (8) (i) (9) (j) (10) (k) (11) (l) (12) 4.8 Non-Accessibility Modifiers for Members The following keywords can be used to specify certain aspects of members in a type declaration: static final abstract synchronized native WOW! eBook www.wowebook.org transient volatile Members Static members belong to the class in which they are declared and are not part of any instance of the class. The declaration of static members is prefixed by the keyword static to distinguish them from instance members. Depending on the accessibility modifiers of the static members in a class, clients can access these members by using the class name or through object references of the class. The class need not be instantiated to access its static members. Static variables (also called class variables) exist only in the class in which they are defined. They are not instantiated when an instance of the class is created. In other words, the values of these variables are not a part of the state of any object. When the class is loaded, static variables are initialized to their default values if no explicit initialization expression is specified. Static methods are also known as class methods. A static method in a class can directly access other static members in the class. It cannot access instance (i.e., non-static) members of the class directly, as there is no notion of an object associated with a static method. A typical static method might perform some task on behalf of the whole class or for objects of the class. In Example 4.11, the static variable counter keeps track of the number of instances of the Light class that have been created. The example shows that the static method writeCount() can access static members directly, as shown at (2), but not non-static members, as shown at (3). The static variable counter at (1) will be initialized to the default value 0 when the class is loaded at runtime. The main() method at (4) in the class Warehouse shows how static members of the class Light can be accessed using the class name and via object references of the type Light. A summary of how static members are accessed in static and non-static code is given in Table 4.1, p. 115. WOW! eBook www.wowebook.org Example 4.11 Accessing Static Members Click here to view code image class Light { // Fields: int noOfWatts; boolean indicator; String location; // Static variable static int counter; // Wattage // On or off // Placement // Number of Light objects created (1) // Non-zero argument constructor Light(int noOfWatts, boolean indicator, String location) { this.noOfWatts = noOfWatts; this.indicator = indicator; this.location = location; ++counter; // Increment counter. } // Static method public static void writeCount() { System.out.println(“Number of lights: ” + counter); // Compile-time error. Field noOfWatts is not accessible: // System.out.println(“Number of Watts: ” + noOfWatts); } // (2) // (3) } //______________________________________________________________________________ public class Warehouse { public static void main(String[] args) { // (4) Light.writeCount(); // Invoked using class name Light light1 = new Light(100, true, “basement”); // Create an object System.out.println( “Value of counter: ” + Light.counter // Accessed via class name ); Light light2 = new Light(200, false, “garage”); light2.writeCount(); reference Light light3 = new Light(300, true, “kitchen”); System.out.println( “Value of counter: ” + light3.counter reference ); } } Output from the program: Number of lights: Value of counter: Number of lights: Value of counter: 0 1 2 3 WOW! eBook www.wowebook.org // Create another object // Invoked using // Create another object // Accessed via Members A final variable is a constant despite being called a variable. Its value cannot be changed once it has been initialized. Instance and static variables can be declared as final. Note that the keyword final can also be applied to local variables, including formal parameters of a method. Declaring a variable as final has the following implications: • A final variable of a primitive data type cannot change its value once it has been initialized. • A final variable of a reference type cannot change its reference value once it has been initialized. This effectively means that a final reference will always refer to the same object. However, the keyword final has no bearing on whether the state of the object denoted by the reference can be changed. • After the constructor exits, the final fields of a object are all guaranteed to be initialized. The compiler ensures that the class provides the appropriate code to initialize the final fields. A final variable must be explicitly initialized only once with an initializer expression, either in its declaration or in an initializer block (§9.7, p. 399). A final instance variable can also be initialized in a constructor. Note that a final local variable need not be initialized in its declaration, but it must be initialized in the code once before it is used. These variables are also known as blank final variables. For a discussion of final parameters, see §3.5, p. 80. A final method in a class is a concrete method (that is, has an implementation) and cannot be overridden in any subclass (§7.2, p. 268). Variables declared as final ensure that values cannot be changed and methods declared as final ensure that behavior cannot be changed. Classes declared as final are discussed in §4.6, p. 122. The compiler may be able to perform code optimizations for final members, because certain assumptions can be made about such members. Static final variables are commonly used to define manifest constants (also called named constants)—for example, Integer.MAX_VALUE, which is the maximum int value. Variables defined in an interface are implicitly final (§7.6, p. 290). In Example 4.12, the class Light defines two public static final variables at (1) and (2). The public static final variable KWH_PRICE is initialized in the declaration at (1), and the public static final variable MANUFACTURER is initialized in the static initializer block at (3). An attempt to change the value of the public static final variable KWH_PRICE at (9) results in a compile-time error. The class Light also defines two final instance variables at (4) and (5). The final instance variable color is initialized in the instance initializer block at (6), and the WOW! eBook www.wowebook.org final instance variable energyRating is initialized in the constructor at (7). The class Light also defines a final method at (8). The subclass TubeLight attempts to override the final method setWatts() from the superclass Light at (10), which is not permitted. The class Warehouse also defines a final local reference workLight at (11). The state of the object denoted by the reference workLight is changed at (12), but its reference value cannot be changed as attempted at (13). Another final local reference alarmLight is declared at (14), but it is not initialized. The compiler reports an error when an attempt is made to use this reference at (15). Example 4.12 Using final Modifier Click here to view code image class Light { // Static final variables public static final double KWH_PRICE = 3.25; public static final String MANUFACTURER; static { MANUFACTURER = “Ozam”; } // Static initializer block // (3) Initializes (2) // Instance variables int noOfWatts; final String color; final String energyRating; // (4) // (5) { block color = “off white”; } // Instance initializer // (6) Initializes (4) // Constructor Light() { energyRating = “A++”; } // (7) Initializes (5) // Final instance method final public void setWatts(int watt) { noOfWatts = watt; } public void setKWH() { // KWH_PRICE = 4.10; } // (1) // (2) (8) // (9) Not OK. Cannot be changed. } //______________________________________________________________________________ class TubeLight extends Light { // Final method in superclass cannot be overridden. // This method will not compile. /* @Override public void setWatts(int watt) { // (10) Attempt to override. noOfWatts = 2*watt; } */ WOW! eBook www.wowebook.org } //______________________________________________________________________________ public class Warehouse { public static void main(String[] args) { final Light workLight = new Light(); // (11) Final local variable. workLight.setWatts(100); // (12) OK. Changing object state. // workLight = new Light(); // (13) Not OK. Changing final reference. final Light alarmLight; alarmLight.setWatts(200); // // (14) Not initialized. // (15) Not OK. System.out.println(“KWH_PRICE: System.out.println(“MANUFACTURER: System.out.println(“noOfWatts: System.out.println(“color: System.out.println(“energyRating: ” ” ” ” ” + + + + + Light.KWH_PRICE); Light.MANUFACTURER); workLight.noOfWatts); workLight.color); workLight.energyRating); } } Output from the program: KWH_PRICE: MANUFACTURER: noOfWatts: color: energyRating: 3.25 Ozam 100 off white A++ Methods An abstract method in an abstract class has the following syntax: Click here to view code image accessibility_modifier abstract return_type method_name (formal_parameter_list) throws_clause; An abstract method does not have an implementation; that is, no method body is defined for an abstract method, and only the method header is provided in the class declaration. The keyword abstract is mandatory in the header of an abstract method declared in a class. Its class is then incomplete and must be explicitly declared as abstract (§4.6, p. 120). Subclasses of an abstract class must then provide the method implementation; otherwise, they must also be declared as abstract. The accessibility of an abstract method declared in a top-level class cannot be private, as subclasses would not be able to override the method and provide an implementation. See §4.6, where Example 4.9 also illustrates the use of abstract methods. Only an instance method can be declared as abstract. Since static methods cannot be overridden, declaring an abstract static method makes no sense, and the compiler will report an error. A final method cannot be abstract (i.e., cannot be incomplete), and vice versa. The keyword abstract can be specified only in combination with the public or protected accessibility modifier. Abstract methods specified in a top-level interface are implicitly abstract, and the WOW! eBook www.wowebook.org keyword abstract is seldom specified in their method headers. These methods can have only public accessibility. See §7.6, p. 291, for a discussion of abstract methods in top-level interfaces. Methods A thread is an independent path of execution in a program. Several threads can be executing in a program. They might try to execute several methods on the same object simultaneously. Methods can be declared as synchronized if it is desired that only one thread at a time be able to execute a method of the object. Their execution is then mutually exclusive among all threads. At any given time, at most one thread can be executing a synchronized method on an object. This discussion also applies to static synchronized methods of a class. In Example 4.13, both the push() method, declared at (1), and the pop() method, declared at (2), are synchronized in the class StackImpl. Only one thread at a time can execute a synchronized method in an object of the class StackImpl. Consequently, it is not possible for the state of an object of the class StackImpl to be corrupted, for example, while one thread is pushing an element and another is attempting to pop the stack. Example 4.13 Synchronized Methods Click here to view code image class StackImpl { // Non-generic partial implementation private Object[] stackArray; private int topOfStack; // … synchronized public void push(Object elem) { // (1) stackArray[++topOfStack] = elem; } synchronized public Object pop() { Object obj = stackArray[topOfStack]; stackArray[topOfStack] = null; topOfStack—; return obj; } // (2) // Other methods, etc. public Object peek() { return stackArray[topOfStack]; } } WOW! eBook www.wowebook.org Methods Native methods are methods whose implementation is not defined in Java but rather in another programming language, such as C or C++. Such a method can be declared as a member in a Java class declaration. Since its implementation appears elsewhere, only the method header is specified in the class declaration. The keyword native is mandatory in the method header. A native method can also specify checked exceptions in a throws clause (§6.9, p. 251), but the compiler cannot check them, since the method is not implemented in Java. The Java Native Interface (JNI) is a special API that allows Java methods to invoke native functions implemented in C. In the following example, a native method in the class Native is declared at (2). The class also uses a static initializer block to load the native library when the class is loaded. Clients of the Native class can call the native method like any another method, as at (3). Click here to view code image class Native { /* * The static block ensures that the native method library * is loaded before the native method is called. */ static { System.loadLibrary(“NativeMethodLib”); // (1) Load native library. } native void nativeMethod(); // … // (2) Native method header. } class Client { //… public static void main(String[] args) { Native trueNative = new Native(); trueNative.nativeMethod(); } //… } // (3) Native method call. Fields Often it is desirable to save the state of an object—for example, on a file. Such objects are said to be persistent. In Java, the state of an object can be stored using serialization. Serialization transforms objects into an output format that is conducive for storing objects. Objects can later be retrieved in the same state as when they were serialized, meaning that all fields included in the serialization will have the same values as at the time of serialization. Sometimes the value of a field in an object should not be saved, in which case the field can be specified as transient in the class declaration. This designation implies that its value should not be saved when objects of the class are written to persistent storage. In the WOW! eBook www.wowebook.org following example, the field currentTemperature is declared as transient at (1), because the current temperature is most likely to have changed when the object is restored at a later date. However, the value of the field mass, declared at (2), is likely to remain unchanged. When objects of the class Experiment are serialized, the value of the field currentTemperature will not be saved, but that of the field mass will be, as part of the state of the serialized object. Click here to view code image class Experiment implements Serializable { // … // The value of currentTemperature will not persist. transient int currentTemperature; // (1) Transient value. double mass; // (2) Persistent value. } Specifying the transient modifier for static variables is redundant and, therefore, discouraged. Static variables are not part of the persistent state of a serialized object. Fields During execution, compiled code might cache the values of fields for efficiency reasons. Since multiple threads can access the same field, it is vital that caching is not allowed to cause inconsistencies when reading and writing the value in the field. The volatile modifier can be used to inform the compiler that it should not attempt to perform optimizations on the field, which could cause unpredictable results when the field is accessed by multiple threads. In the simple example that follows, the value of the field clockReading might be changed unexpectedly by another thread while one thread is performing a task that involves always using the current value of the field clockReading. Declaring the field as volatile ensures that a write operation will always be performed on the master field variable, and a read operation will always return the correct current value. Click here to view code image class VitalControl { // … volatile long clockReading; // Two successive reads might give different results. } Table 4.5 provides a summary of non-accessibility modifiers for members. WOW! eBook www.wowebook.org Table 4.5 Summary of Non-Accessibility Modifiers for Members Review Questions 4.15 Which statements about the use of modifiers are true? Select the two correct answers. (a) If no accessibility modifier (public, protected, or private) is specified for a member declaration, the member is accessible only by classes in the package of its class and by subclasses of its class in any package. (b) You cannot specify accessibility of local variables. They are accessible only within the block in which they are declared. (c) Subclasses of a class must reside in the same package as the class they extend. (d) Local variables can be declared as static. (e) The objects themselves do not have any accessibility modifiers; only field references do. 4.16 Given the following source code, which comment line can be uncommented without introducing errors? Click here to view code image abstract class MyClass { abstract void f(); final void g() {} //final void h() {} // (1) WOW! eBook www.wowebook.org protected static int i; private int j; } final class MyOtherClass extends MyClass { //MyOtherClass(int n) { m = n; } // (2) public static void main(String[] args) { MyClass mc = new MyOtherClass(); } void void //void //void f() h() k() l() {} {} { i++; } { j++; } // (3) // (4) int m; } Select the one correct answer. (a) (1) (b) (2) (c) (3) (d) (4) 4.17 Which statement is true? Select the one correct answer. (a) A static method can call other non-static methods in the same class by using the this keyword. (b) A class may contain both static and non-static variables, and both static and non-static methods. (c) Each object of a class has its own instance of the static variables declared in the class. (d) Instance methods may access local variables of static methods. (e) All methods in a class are implicitly passed the this reference as an argument, when invoked. 4.18 Which one of these is not a legal member declaration within a class? Select the one correct answer. (a) static int a; (b) final Object[] fudge = { null }; (c) abstract int t; (d) native void sneeze(); (e) static final private double PI = WOW! eBook www.wowebook.org 3.14159265358979323846; 4.19 Which statements about modifiers are true? Select the two correct answers. (a) Abstract classes can declare final methods. (b) Fields can be declared as native. (c) Non-abstract methods can be declared in abstract classes. (d) Classes can be declared as native. (e) Abstract classes can be declared as final. 4.20 Which statement is true? Select the one correct answer. (a) The values of transient fields will not be saved during serialization. (b) Constructors can be declared as abstract. (c) The initial state of an array object constructed with the statement int[] a = new int[10] will depend on whether the array variable a is a local variable or a field. (d) A subclass of a class with an abstract method must provide an implementation for the abstract method. (e) Only static methods can access static members. Chapter Summary The following topics were covered in this chapter: • The structure of a Java source file • Defining, using, and deploying packages • Class scope for members, and block scope for local variables • Accessibility (default, public) and other modifiers (abstract, final) for reference types • Applicability of member accessibility (default, public, protected, private) and other member modifiers (static, final, abstract, synchronized, native, transient, volatile) Programming Exercise 4.1 Design a class for a bank database. The database should support the following operations: Deposit a certain amount into an account WOW! eBook www.wowebook.org Withdraw a certain amount from an account Get the balance (i.e., the current amount) in an account Transfer an amount from one account to another The amount in the transactions is a value of type double. The accounts are identified by instances of the class Account that is in the package com.megabankcorp.records. The database class should be placed in a package called com.megabankcorp.system. The deposit, withdraw, and balance operations should not have any implementation, but should allow subclasses to provide the implementation. The transfer operation should use the deposit and withdraw operations to implement the transfer. It should not be possible to alter this operation in any subclass, and only classes within the package com.megabankcorp.system should be allowed to use this operation. The deposit and withdraw operations should be accessible in all packages. The balance operation should be accessible only in subclasses and classes within the package com.megabankcorp.system. WOW! eBook www.wowebook.org 5. Operators and Expressions 5.1 Conversions In this section we first discuss the different kinds of type conversions that can be applied to values; in the next section we discuss the contexts in which these conversions are permitted. Some type conversions must be explicitly stated in the program, while others are performed implicitly. Some type conversions can be checked at compile time to guarantee their validity at runtime, while others will require an extra check at runtime. WOW! eBook www.wowebook.org Widening and Narrowing Primitive Conversions For the primitive data types, the value of a narrower data type can be converted to a value of a wider data type. This is called a widening primitive conversion. Widening conversions from one primitive type to the next wider primitive type are summarized in Figure 5.1. The conversions shown are transitive. For example, an int can be directly converted to a double without first having to convert it to a long and a float. Figure 5.1 Widening Primitive Conversions Note that the target type of a widening primitive conversion has a wider range of values than the source type—for example, the range of the long type subsumes the range of the int type. In widening conversions between integral types, the source value remains intact, with no loss of magnitude information. However, a widening conversion from an int or a long value to a float value, or from a long value to a double value, may result in a loss of precision. The floating-point value in the target type is then a correctly rounded approximation of the integer value. Note that precision relates to the number of significant bits in the value, and must not be confused with magnitude, which relates how big a value can be represented. Converting from a wider primitive type to a narrower primitive type is called a narrowing primitive conversion; it can result in loss of magnitude information, and possibly in a loss of precision as well. Any conversion that is not a widening primitive conversion according to Figure 5.1 is a narrowing primitive conversion. The target type of a narrowing primitive conversion has a narrower range of values than the source type—for example, the range of the int type does not include all the values in the range of the long type. Note that all conversions between char and the two integer types byte and short are considered narrowing primitive conversions. The reason is that the conversions between the unsigned type char and the signed types byte or short can result in loss of information. These narrowing conversions are done in two steps: first converting the source value to the int type, and then converting the int value to the target type. Widening primitive conversions are usually done implicitly, whereas narrowing primitive conversions usually require a cast (§5.2, p. 148). It is not illegal to use a cast for a widening conversion. However, the compiler will flag any conversion that requires a cast if none has been specified. Regardless of any loss of magnitude or precision, widening and narrowing primitive conversions never result in a runtime exception. Ample examples of widening and narrowing primitive conversions can be found in this chapter. WOW! eBook www.wowebook.org Widening and Narrowing Reference Conversions The subtype–supertype relationship between reference types determines which conversions are permissible between them. Conversions up the type hierarchy are called widening reference conversions (also called upcasting). Such a conversion converts from a subtype to a supertype: Click here to view code image Object obj = “Upcast me”; // Widening: Object <–— String Conversions down the type hierarchy represent narrowing reference conversions (also called downcasting): Click here to view code image String str = (String) obj; // Narrowing requires cast: String <–— Object A subtype is a narrower type than its supertype in the sense that it is a specialization of its supertype. Contexts under which reference conversions can occur are discussed in §7.8, p. 311. Widening reference conversions are usually done implicitly, whereas narrowing reference conversions usually require a cast, as illustrated in the second declaration statement in this subsection. The compiler will reject casts that are not legal or issue an unchecked warning under certain circumstances if type safety cannot be guaranteed. Widening reference conversions do not require any runtime checks and never result in an exception during execution. This is not the case for narrowing reference conversions, which require a runtime check and can throw a ClassCastException if the conversion is not legal. Boxing and Unboxing Conversions Boxing and unboxing conversions allow interoperability between primitive values and their representation as objects of the wrapper types (§8.3, p. 346). A boxing conversion converts the value of a primitive type to a corresponding value of its wrapper type. If p is a value of a primitiveType, boxing conversion converts p into a reference r of the corresponding WrapperType, such that r.primitiveTypeValue() == p. In the code that follows, the int value 10 results in an object of the type Integer implicitly being created; this object contains the int value 10. We say that the int value 10 has been boxed in an object of the wrapper type Integer. The terminology autoboxed is also used for this conversion. Click here to view code image Integer iRef = 10; System.out.println(iRef.intValue() == 10); // Boxing: Integer <–— int // true An unboxing conversion converts the value of a wrapper type to a value of its corresponding primitive type. If r is a reference of a WrapperType, an unboxing conversion converts the reference r into r.primitiveTypeValue(), where primitiveType is the primitive type corresponding to the WrapperType. In the next code snippet, the value in the Integer object referenced by iRef is implicitly converted to the int type. We WOW! eBook www.wowebook.org say that the wrapper object has been unboxed to its corresponding primitive type. Click here to view code image int i = iRef; System.out.println(iRef.intValue() == i); // Unboxing: int <–— Integer // true Note that both boxing and unboxing are done implicitly in the right context. Boxing allows primitive values to be used where an object of their wrapper type is expected, and unboxing allows the converse. Unboxing makes it possible to use a Boolean wrapper object as a boolean value in a boolean expression, and to use an integral wrapper object as an integral primitive value in an arithmetic expression. Unboxing a wrapper reference that has the null value results in a NullPointerException. Ample examples of boxing and unboxing can be found in this chapter and in §7.8, p. 311. Other Conversions We briefly mention some other conversions, and identify where they are covered in this book. • Identity conversions are always permitted, as they allow conversions from a type to that same type. An identity conversion is always permitted. Click here to view code image int i = (int) 10; String str = (String) “Hi”; // int <–- int // String <–- String • String conversions allow a value of any other type to be converted to a String type in the context of the string concatenation operator + (§5.8, p. 174). • Unchecked conversions are permitted to facilitate operability between legacy and generic code (§10.1, p. 416). 5.2 Type Conversion Contexts Selected conversion contexts and the conversions that are applicable in these contexts are summarized in Table 5.1. The conversions shown in each context occur implicitly, without the program having to take any special action. For other conversion contexts, see §5.1, p. 146. WOW! eBook www.wowebook.org Table 5.1 Selected Conversion Contexts and Conversion Categories Assignment Context Assignment conversions that can occur in an assignment context are shown in the second column of Table 5.1. An assignment conversion converts the type of an expression to the type of a target variable. An expression (or its value) is assignable to the target variable, if the type of the expression can be converted to the type of the target variable by an assignment conversion. Equivalently, the type of the expression is assignment compatible with the type of the target variable. For assignment conversion involving primitive data types, see §5.6, p. 158. Note the special case where a narrowing conversion occurs when assigning a non-long integer constant expression: Click here to view code image byte b = 10; // Narrowing conversion: byte <– int WOW! eBook www.wowebook.org For assignment conversions involving reference types, see §7.8, p. 311. Method Invocation Context Method invocation conversions that can occur in a method invocation context are shown in the third column of Table 5.1. Note that method invocation and assignment conversions differ in one respect: Method invocation conversions do not include the implicit narrowing conversion performed for non-long integral constant expressions. Click here to view code image // Assignment: (1) Implicit narrowing followed by (2) boxing. Character space1 = 32; // Character <-(2)— char <-(1)— int // Invocation of method with signature: valueOf(char) Character space2 = Character.valueOf(32); // Compile-time error! // Call signature: valueOf(int) Character space3 = Character.valueOf((char)32); // OK! // Call signature: valueOf(char) A method invocation conversion involves converting each argument value in a method or constructor call to the type of the corresponding formal parameter in the method or constructor declaration. Method invocation conversions involving parameters of primitive data types are discussed in §3.5, p. 73, and those involving reference types are discussed in §7.8, p. 311. Casting Context of the Unary Type Cast Operator: (type) Java, being a strongly typed language, checks for type compatibility (i.e., it checks if a type can substitute for another type in a given context) at compile time. However, some checks are possible only at runtime (e.g., which type of object a reference actually denotes during execution). In cases where an operator would have incompatible operands (e.g., assigning a double to an int), Java demands that a type cast be used to explicitly indicate the type conversion. The type cast construct has the following syntax: (type) expression The cast operator (type) is applied to the value of the expression. At runtime, a cast results in a new value of type, which best represents the value of the expression in the old type. We use the term casting to mean applying the cast operator for explicit type conversion. However, in the context of casting, implicit casting conversions can take place. These casting conversions are shown in the fourth column of Table 5.1. Casting conversions include more conversion categories than the assignment or the method invocation conversions. In the code that follows, the comments indicate the category of the conversion that takes place because of the cast operator on the right-hand side of each assignment—although casts are only necessary for the sake of the assignment at (1) and (2). Click here to view code image WOW! eBook www.wowebook.org long l = (long) 10; // Widening primitive conversion: long <– int int i = (int) l; // (1) Narrowing primitive conversion: int <– long Object obj = (Object) “7Up”; // Widening ref conversion: Object <– String String str = (String) obj; // (2) Narrowing ref conversion: String <– Object Integer iRef = (Integer) i; // Boxing: Integer <– int i = (int) iRef; // Unboxing: int <– Integer A casting conversion is applied to the value of the operand expression of a cast operator. Casting can be applied to primitive values as well as references. Casting between primitive data types and reference types is not permitted, except where boxing and unboxing is applicable. Boolean values cannot be cast to other data values, and vice versa. The reference literal null can be cast to any reference type. Examples of casting between primitive data types are provided in this chapter. Casting reference values is discussed in §7.11, p. 320. Numeric Promotion Context Numeric operators allow only operands of certain types. Numeric promotion results in conversions being applied to the operands to convert them to permissible types. Numeric promotion conversions that can occur in a numeric promotion context are shown in the fifth column of Table 5.1. Permissible conversion categories are widening primitive conversions and unboxing conversions. A distinction is made between unary and binary numeric promotion. Unary Numeric Promotion Unary numeric promotion proceeds as follows: • If the single operand is of type Byte, Short, Character, or Integer, it is unboxed. If the resulting value is narrower than int, it is promoted to a value of type int by a widening conversion. • Otherwise, if the single operand is of type Long, Float, or Double, it is unboxed. • Otherwise, if the single operand is of a type narrower than int, its value is promoted to a value of type int by a widening conversion. • Otherwise, the operand remains unchanged. In other words, unary numeric promotion results in an operand value that is either int or wider. Unary numeric promotion is applied in the following expressions: • Operand of the unary arithmetic operators + and - (§5.7, p. 163) • Array creation expression; for example, new int[20], where the dimension expression (in this case 20) must evaluate to an int value (§3.4, p. 59) • Indexing array elements; for example, objArray['a'], where the index expression (in this case 'a') must evaluate to an int value (§3.4, p. 61) WOW! eBook www.wowebook.org Binary Numeric Promotion Binary numeric promotion implicitly applies appropriate widening primitive conversions so that a pair of operands have the widest numeric type of the two, which is always at least int. If T is the widest numeric type of the two operands after any unboxing conversions have been performed, the operands are promoted as follows during binary numeric promotion: If T is wider than int, both operands are converted to T; otherwise, both operands are converted to int. This means that the resulting type of the operands is at least int. Binary numeric promotion is applied in the following expressions: • Operands of the arithmetic operators *, /, %, +, and - (§5.7, p. 163) • Operands of the relational operators <, <=, >, and >= (§5.11, p. 180) • Operands of the numerical equality operators == and != (§5.12, p. 181) • Operands of the conditional operator ? :, under certain circumstances (§5.16, p. 194) 5.3 Precedence and Associativity Rules for Operators Precedence and associativity rules are necessary for deterministic evaluation of expressions. The operators are summarized in Table 5.2. The majority of them are discussed in subsequent sections in this chapter. See also the index entries for these operators. WOW! eBook www.wowebook.org Table 5.2 Operator Summary The following remarks apply to Table 5.2: • The operators are shown with decreasing precedence from the top of the table. • Operators within the same row have the same precedence. • Parentheses, ( ), can be used to override precedence and associativity. • The unary operators, which require one operand, include the following: the postfix increment (++) and decrement (--) operators from the first row, all the prefix operators (+, -, ++, --, ~, !) in the second row, and the prefix operators (object creation operator new, cast operator (type)) in the third row. • The conditional operator (? :) is ternary—that is, it requires three operands. • All operators not identified previously as unary or ternary are binary—that is, they require two operands. • All binary operators, except for the relational and assignment operators, associate from left to right. The relational operators are nonassociative. • Except for unary postfix increment and decrement operators, all unary operators, all assignment operators, and the ternary conditional operator associate from right to left. WOW! eBook www.wowebook.org Depending on the context, brackets ([]), parentheses (()), colon (:) and the dot operator (.) can also be interpreted as separators (§2.1, p. 29). See the index entries for these separators for more details. Precedence rules are used to determine which operator should be applied first if there are two operators with different precedence, and these operators follow each other in the expression. In such a case, the operator with the highest precedence is applied first. The expression 2 + 3 * 4 is evaluated as 2 + (3 * 4) (with the result 14) since * has higher precedence than +. Associativity rules are used to determine which operator should be applied first if there are two operators with the same precedence, and these operators follow each other in the expression. Left associativity implies grouping from left to right: The expression 7 - 4 + 2 is interpreted as ((7 - 4) + 2), since the binary operators + and - both have same precedence and left associativity. Right associativity implies grouping from right to left: The expression - - 4 is interpreted as (- (- 4)) (with the result 4), since the unary operator - has right associativity. The precedence and associativity rules together determine the evaluation order of the operators. 5.4 Evaluation Order of Operands To understand the result returned by an operator, it is important to understand the evaluation order of its operands. In general, the operands of operators are evaluated from left to right. The evaluation order also respects any parentheses, and the precedence and associativity rules of operators. Examples illustrating how the operand evaluation order influences the result returned by an operator, can be found in §5.6 and §5.9. Left-Hand Operand Evaluation First The left-hand operand of a binary operator is fully evaluated before the right-hand operand is evaluated. The evaluation of the left-hand operand can have side effects that can influence the value of the right-hand operand. For example, in the code Click here to view code image int b = 10; System.out.println((b=3) + b); the value printed will be 6 and not 13. The evaluation proceeds as follows: Click here to view code image (b=3) + b 3 + b 3 + 3 b is assigned the value 3 WOW! eBook www.wowebook.org 6 If evaluation of the left-hand operand of a binary operator throws an exception (§6.5, p. 230), we cannot rely on the presumption that the right-hand operand has been evaluated. Operand Evaluation before Operation Execution Java guarantees that all operands of an operator are fully evaluated before the actual operation is performed. This rule does not apply to the short-circuit conditional operators &&, ||, and ?:. This rule also applies to operators that throw an exception (the integer division operator / and the integer remainder operator %). The operation is performed only if the operands evaluate normally. Any side effects of the right-hand operand will have been effectuated before the operator throws an exception. Example 5.1 illustrates the evaluation order of the operands and precedence rules for arithmetic expressions. We use the eval() method at (3) in Example 5.1 to demonstrate integer expression evaluation. The first argument to this method is the operand value that is returned by the method, and the second argument is a string to identify the evaluation order. The argument to the println() method in the statement at (1) is an integer expression to evaluate 2 + 3 * 4. The evaluation of each operand in the expression at (1) results in a call of the eval() method declared at (3). Click here to view code image out.println(eval(j++, ” + “) + eval(j++, ” * “) * eval(j, “\n”)); // (1) The output from Example 5.1 shows that the operands were evaluated first, from left to right, before operator execution, and that the expression was evaluated as (2 + (3 * 4)), respecting the precedence rules for arithmetic expression evaluation. Note how the value of variable j changes successively from left to right as the first two operands are evaluated. WOW! eBook www.wowebook.org Example 5.1 Evaluation Order of Operands and Arguments Click here to view code image import static java.lang.System.out; public class EvalOrder{ public static void main(String[] args){ int j = 2; out.println(“Evaluation order of operands:”); out.println(eval(j++, ” + “) + eval(j++, ” * “) * eval(j, “\n”)); // (1) int i = 1; out.println(“Evaluation order of arguments:”); add3(eval(i++, “, “), eval(i++, “, “), eval(i, “\n”)); // (2) Three arguments. } public static int eval(int operand, String str) { // (3) out.print(operand + str); // Print int operand and String str. return operand; // Return int operand. } public static void add3(int operand1, int operand2, int operand3) { (4) out.print(operand1 + operand2 + operand3); } } // Output from the program: Evaluation order of operands: 2 + 3 * 4 14 Evaluation order of arguments: 1, 2, 3 6 Left-to-Right Evaluation of Argument Lists In a method or constructor invocation, each argument expression in the argument list is fully evaluated before any argument expression to its right. If evaluation of an argument expression does not complete normally, we cannot presume that any argument expression to its right has been evaluated. We can use the add3() method at (4) in Example 5.1, which takes three arguments, to demonstrate the order in which the arguments in a method call are evaluated. The method call at (2) Click here to view code image add3(eval(i++, “, “), eval(i++, “, “), eval(i, “\n”)); arguments. // (2) Three results in the following output, clearly indicating that the arguments were evaluated from left to right, before being passed to the method: 1, 2, 3 WOW! eBook www.wowebook.org 6 Note how the value of variable i changes successively from left to right as the first two arguments are evaluated. 5.5 Representing Integers Integer data types in Java represent signed integer values, meaning both positive and negative integer values. The values of char type can effectively be regarded as unsigned 16-bit integers. Values of type byte are represented as shown in Table 5.3. A value of type byte requires 8 bits. With 8 bits, we can represent 28 or 256 values. Java uses two’s complement (explained later) to store signed values of integer data types. For the byte data type, this means values are in the range –128 (i.e., –27) to +127 (i.e., 27–1), inclusive. Table 5.3 Representing Signed byte Values Using Two’s Complement Bits in an integral value are usually numbered from right to left, starting with the least significant bit 0 (also called the rightmost bit). The representation of the signed types sets WOW! eBook www.wowebook.org the most significant bit to 1, indicating negative values. Adding 1 to the maximum int value 2147483647 results in the minimum value -2147483648, such that the values wrap around for integers and no overflow or underflow is indicated. Calculating Two’s Complement Before we look at the two’s complement, we need to understand the one’s complement. The one’s complement of a binary integer is computed by inverting the bits in the number. Thus, the one’s complement of the binary number 00101001 is 11010110. The one’s complement of a binary number N2 is denoted as ~N2. The following relations hold between a binary integer N2, its one’s complement ~N2, and its two’s complement –N2: –N2 = ~N2 + 1 0 = –N2 + N2 If N2 is a positive binary integer, then –N2 denotes its negative binary value, and vice versa. The second relation states that adding a binary integer N2 to its two’s complement – N2 equals 0. Given a positive byte value, say 41, the binary representation of -41 can be found as follows: Adding a number N2 to its two’s complement –N2 gives 0, and the carry bit from the addition of the most significant bits (after any necessary extension of the operands) is ignored: Subtraction between two integers is also computed as addition with two’s complement: N2 – M2 = N2 + (–M2) For example, the expression 4110 – 310 (with the correct result 3810) is computed as follows: WOW! eBook www.wowebook.org The previous discussion of byte values applies equally to values of other integer types: short, int, and long. These types have their values represented by two’s complement in 16, 32, and 64 bits, respectively. Converting Binary Numbers to Decimals A binary number can be converted to its equivalent decimal value by computing the positional values of its digits. Each digit in the binary number contributes to the final decimal value by virtue of its position, starting with position 0 (units) for the rightmost digit in the number. The positional value of each digit is given by digit × base position The number 1010012 corresponds to 4110 in the decimal number system: The same technique can be used to convert a number from any base, for example, octal (base 8) or hexadecimal (base 16), to its equivalent representation in the decimal number system. Converting Decimals to Binary Numbers To convert decimals to binaries, we reverse the process outlined previously for converting a binary to a decimal. The divisor used in these steps is the base of the target number system (binary, base 2). The binary value, 1010012, is represented by the remainders, with the last remainder as WOW! eBook www.wowebook.org the leftmost bit. Analogously, we can apply this procedure for converting an octal (base 8) or hexadecimal (base 16) number to its binary equivalent. Relationships among Binary, Octal, and Hexadecimal Numbers We need 3 bits to represent all the octal digits (8 = 23) and 4 bits to represent all the hexadecimal digits (16 = 24). We can use this fact to convert among the binary, octal, and hexadecimal systems, as shown in Figure 5.2. Figure 5.2 Converting among Binary, Octal, and Hexadecimal Numbers The procedure for converting an octal to a binary is shown by the arrow marked (a). We can convert an octal number to its equivalent binary number by replacing each digit in the octal number by its 3-bit equivalent binary value. Analogously, we can convert a hexadecimal number to its equivalent binary number by replacing each digit in the hexadecimal number by its 4-bit equivalent binary value, as shown by the arrow marked (b). To convert a binary to its octal equivalent, we reverse the procedure outlined earlier (arrow marked (c) in Figure 5.2). The bits in the binary number are grouped into 3-bit groups from right to left. Each such group is replaced by its equivalent octal digit. Analogously, we can convert a binary to a hexadecimal number by replacing each 4-bit group by its equivalent hexadecimal digit (arrow marked (d) in Figure 5.2). 5.6 The Simple Assignment Operator The assignment statement has the following syntax: variable = expression which can be read as “the target, variable, gets the value of the source, expression.” The previous value of the target variable is overwritten by the assignment operator =. The target variable and the source expression must be assignment compatible. The target WOW! eBook www.wowebook.org variable must also have been declared. Since variables can store either primitive values or reference values, expression evaluates to either a primitive value or a reference value. Assigning Primitive Values The following examples illustrate assignment of primitive values: Click here to view code image int j = j = k = j, k; 0b10; 5; j; // j gets the value 2. // j gets the value 5. Previous value is overwritten. // k gets the value 5. The assignment operator has the lowest precedence, so that the expression on the righthand side is evaluated before the assignment is done. Click here to view code image int i = i = i = i; 5; i + 1; 20 - i * 2; // i gets the value 5. // i gets the value 6. + has higher precedence than =. // i gets the value 8: (20 - (i * 2)) Assigning References Copying reference values by assignment creates aliases, which are discussed in §1.3, p. 6. The following example recapitulates that discussion: Click here to view code image Pizza pizza1 = new Pizza(“Hot&Spicy”); Pizza pizza2 = new Pizza(“Sweet&Sour”); pizza2 = pizza1; The variable pizza1 is a reference to a pizza that is hot and spicy, and pizza2 is a reference to a pizza that is sweet and sour. Assigning pizza1 to pizza2 means that pizza2 now refers to the same pizza as pizza1, the hot and spicy one. After the assignment, these variables are aliases and either one can be used to manipulate the hot and spicy Pizza object. Assigning a reference value does not create a copy of the source object denoted by the reference variable on the right-hand side. It merely assigns the reference value of the variable on the right-hand side to the variable on the left-hand side, so that they denote the same object. Reference assignment also does not copy the state of the source object to any object denoted by the reference variable on the left-hand side. A more detailed discussion of reference assignment can be found in §7.8, p. 311. Multiple Assignments The assignment statement is an expression statement, which means that application of the binary assignment operator returns the value of the expression on the right-hand side. Click here to view code image int j, k; j = 10; // j gets the value 10, which is returned WOW! eBook www.wowebook.org k = j; returned // k gets the value of j, which is 10, and this value is The last two assignments can be written as multiple assignments, illustrating the right associativity of the assignment operator: Click here to view code image k = j = 10; // (k = (j = 10)) Multiple assignments are equally valid with references: Click here to view code image Pizza pizzaOne, pizzaTwo; pizzaOne = pizzaTwo = new Pizza(“Supreme”); // Aliases The following example shows the effect of operand evaluation order: Click here to view code image int[] a = {10, 20, 30, 40, 50}; // An array of int int index = 4; a[index] = index = 2; // (1) What is the value of index, and which array element a[index] is assigned a value in the multiple assignment statement at (1)? The evaluation proceeds as follows: Click here to view code image a[index] = index = 2; a[4] = index = 2; a[4] = (index = 2); associative. a[4] = 2; // index gets the value 2. = is right // The value of a[4] is changed from 50 to 2. The following declaration statement will not compile, as the variable v2 has not been declared: Click here to view code image int v1 = v2 = 2016; // Only v1 is declared. Compile-time error! Type Conversions in an Assignment Context If the target and the source have the same type in an assignment, then, obviously, the source and the target are assignment compatible and the source value need not be converted. Otherwise, if a widening primitive conversion is permissible, then the widening conversion is applied implicitly; that is, the source type is converted to the target type in an assignment context. Click here to view code image // Widening Primitive Conversions int smallOne = 1234; long bigOne = 2000; double largeOne = bigOne; double hugeOne = (double) bigOne; // // // // No widening necessary. Widening: int to long. Widening: long to double. Cast redundant but allowed. A widening primitive conversion can result in loss of precision. In the next example, the precision of the least significant bits of the long value may be lost when it is converting to a float value: Click here to view code image WOW! eBook www.wowebook.org long bigInteger = 98765432112345678L; float fpNum = bigInteger; // Widening but loss of precision: 9.8765436E16 Additionally, implicit narrowing primitive conversions on assignment can occur in cases where all of the following conditions are fulfilled: • The source is a constant expression of either byte, short, char, or int type. • The target type is either byte, short, or char type. • The value of the source is determined to be in the range of the target type at compile time. A constant expression is an expression that denotes either a primitive or a String literal, and is composed of operands that can be only literals or constant variables, and operators that can be evaluated only at compile time (for example, arithmetic and numerical comparison operators, but not increment/decrement operators and method calls). A constant variable is a final variable of either a primitive type or String type that is initialized with a constant expression. Click here to view code image int result = 100; // Not a constant variable. Not declared final. final char finalGrade = ‘A’; // Constant variable. System.out.printf(“%d%n%s%n%d%n%.2f%n%b%n%d%n%d%n”, 2106, // Constant expression. “Trust ” + “me!”, // Constant expression. 2 + 3 * 4, // Constant expression. Math.PI * Math.PI * 10.0, // Constant expression. finalGrade == ‘A’, // Constant expression. Math.min(2015, 2016), // Not constant expression. Method call. ++result // Not constant expression. Increment operator. ); Here are some examples that illustrate how the conditions mentioned previously affect narrowing primitive conversions: Click here to view code image // Conditions fulfilled short s1 = 10; // short s2 = ‘a’; // char c1 = 32; // char c2 = (byte)35; // cast.) byte b1 = 40; // byte b2 = (short)40; // cast.) final int i1 = 20; // byte b3 = i1; // for implicit narrowing primitive conversions. int value in range. char value in range. int value in range. byte value in range. (int value in range, without int value in range. short value in range. (int value in range, without Constant variable final value of i1 in range. All other narrowing primitive conversions will produce a compile-time error on assignment and will explicitly require a cast. Here are some examples: Click here to view code image // Conditions not fulfilled for implicit narrowing primitive conversions. // A cast is required. int i2 = -20; // i2 is not a constant variable. i2 is not final. final int i3 = i2; // i3 is not a constant variable, since i2 is not. WOW! eBook www.wowebook.org final int i4 = 200; final int i5; short s3 = (short) i2; char c3 = (char) i3; time. char c4 = (char) i2; byte b4 = (byte) 128; byte b5 = (byte) i4; i5 = 100; short s4 = (short) i5; time. // // // // i4 is a constant variable. i5 is not a constant variable. Not constant expression. Final value of i3 not determinable at compile // // // // // Not constant expression. int value not in range. Value of constant variable i4 is not in range. Initialized at runtime. Final value of i5 not determinable at compile Floating-point values are truncated when cast to integral values. Click here to view code image // The value float huge long giant int big short small byte tiny char symbol is truncated to fit the size of = (float) 1.7976931348623157d; = (long) 4415961481999.03D; = (int) giant; = (short) big; = (byte) small; = (char) 112.5F; the target type. // double to float. // (1) double to long. // (2) long to int. // (3) int to short. // (4) short to byte. // (5) float to char. Table 5.4 shows how the values are truncated for assignments from (1) to (5). Table 5.4 Examples of Truncated Values The discussion of numeric assignment conversions also applies to numeric parameter values at method invocation (§3.5, p. 73), except for the narrowing conversions, which always require a cast. The following examples illustrate boxing and unboxing in an assignment context: Click here to view code image Boolean Byte // Byte boolRef = true; bRef = 2; bRef2 = 257; // Boxing. // Constant in range: narrowing, then boxing. // Constant not in range. Compile-time error! short s = 10; // Integer iRef1 = s; Integer iRef3 = (int) s; // Narrowing from int to short. // short not assignable to Integer. // Explicit widening with cast to int and boxing boolean bv1 = boolRef; byte b1 = bRef; int iVal = bRef; // Unboxing. // Unboxing. // Unboxing and widening. Integer iRefVal = null; // int j = iRefVal; if (iRef3 != null) iVal = iRef3; // Always allowed. // NullPointerException at runtime. // Avoid exception at runtime. WOW! eBook www.wowebook.org Review Questions 5.1 Given the following declaration: char c = ‘A’; What is the simplest way to convert the character value in c to an int? Select the one correct answer. (a) int i = c; (b) int i = (int) c; (c) int i = Character.getNumericValue(c); 5.2 What will be the result of compiling and running the following program? Click here to view code image public class Assignment { public static void main(String[] args) { int a, b, c; b = 10; a = b = c = 20; System.out.println(a); } } Select the one correct answer. (a) The program will fail to compile, since the compiler will report that the variable c in the multiple assignment statement a = b = c = 20; has not been initialized. (b) The program will fail to compile, because the multiple assignment statement a = b = c = 20; is illegal. (c) The code will compile, and print 10 at runtime. (d) The code will compile, and print 20 at runtime. 5.3 What will be the result of compiling and running the following program? Click here to view code image public class MyClass { public static void main(String[] args) { String a, b, c; c = new String(“mouse”); a = new String(“cat”); b = a; a = new String(“dog”); c = b; System.out.println(c); } } Select the one correct answer. (a) The program will fail to compile. WOW! eBook www.wowebook.org (b) The program will print mouse at runtime. (c) The program will print cat at runtime. (d) The program will print dog at runtime. (e) The program will randomly print either cat or dog at runtime. 5.7 Arithmetic Operators: , , , , Arithmetic operators are used to construct mathematical expressions as in algebra. Their operands are of numeric type (which includes the char type). Arithmetic Operator Precedence and Associativity In Table 5.5, the precedence of the operators appears in decreasing order, starting from the top row, which has the highest precedence. Unary subtraction has higher precedence than multiplication. The operators in the same row have the same precedence. Binary multiplication, division, and remainder operators have the same precedence. The unary operators have right associativity, and the binary operators have left associativity. Table 5.5 Arithmetic Operators Evaluation Order in Arithmetic Expressions Java guarantees that the operands are fully evaluated from left to right before an arithmetic binary operator is applied. If evaluation of an operand results in an error, the subsequent operands will not be evaluated. In the expression a + b * c, the operand a will always be fully evaluated before the operand b, which will always be fully evaluated before the operand c. However, the multiplication operator * will be applied before the addition operator +, respecting the precedence rules. Note that a, b, and c are arbitrary arithmetic expressions that have been determined to be the operands of the operators. Example 5.1, p. 153, illustrates the evaluation order and precedence rules for arithmetic expressions. Range of Numeric Values As we have seen, all numeric types have a range of valid values (§2.2, p. 37). This range is given by the constants named MAX_VALUE and MIN_VALUE, which are defined in each numeric wrapper type. The arithmetic operators are overloaded, meaning that the operation of an operator varies depending on the type of its operands. Floating-point arithmetic is performed if any WOW! eBook www.wowebook.org operand of an operator is of floating-point type; otherwise, integer arithmetic is performed. Values that are out of range or are the results of invalid expressions are handled differently depending on whether integer or floating-point arithmetic is performed. Integer Arithmetic Integer arithmetic always returns a value that is in range, except in the case of integer division by zero and remainder by zero, which cause an ArithmeticException (see the later discussion of the division operator / and the remainder operator %). A valid value does not necessarily mean that the result is correct, as demonstrated by the following examples: Click here to view code image int tooBig = Integer.MAX_VALUE + 1; Integer.MIN_VALUE. int tooSmall = Integer.MIN_VALUE - 1; Integer.MAX_VALUE. // -2147483648 which is // 2147483647 which is These results should be values that are out of range. However, integer arithmetic wraps round if the result is out of range; that is, the result is reduced modulo in the range of the result type. To avoid wrapping round of out-of-range values, programs should either use explicit checks or a wider type. If the type long is used in the earlier examples, the results would be correct in the long range: Click here to view code image long notTooBig = Integer.MAX_VALUE + 1L; long notTooSmall = Integer.MIN_VALUE - 1L; // 2147483648L in range. // -2147483649L in range. Floating-Point Arithmetic Certain floating-point operations result in values that are out of range. Typically, adding or multiplying two very large floating-point numbers can result in an out of-range value that is represented by infinity (Figure 5.3). Attempting floating-point division by zero also returns infinity. The following examples show how this value is printed as signed infinity: Click here to view code image System.out.println( 4.0 / 0.0); System.out.println(-4.0 / 0.0); // Prints: Infinity // Prints: -Infinity WOW! eBook www.wowebook.org Figure 5.3 Overflow and Underflow in Floating-Point Arithmetic Both positive and negative infinity represent overflow to infinity; that is, the value is too large to be represented as a double or float (Figure 5.3). Signed infinity is represented by the named constants POSITIVE_INFINITY and NEGATIVE_INFINITY in the wrapper classes java.lang.Float and java.lang.Double. A value can be compared with these constants to detect overflow. Floating-point arithmetic can also result in underflow to zero, when the value is too small to be represented as a double or float (Figure 5.3). Underflow occurs in the following situations: • The result is between Double.MIN_VALUE (or Float.MIN_VALUE) and zero, as with the result of (5.1E-324 - 4.9E-324). Underflow then returns positive zero 0.0 (or 0.0F). • The result is between -Double.MIN_VALUE (or -Float.MIN_VALUE) and zero, as with the result of (-Double.MIN_VALUE * 1E-1). Underflow then returns negative zero -0.0 (or -0.0F). Negative zero compares equal to positive zero; in other words, (-0.0 == 0.0) is true. Certain operations have no mathematical result, and are represented by NaN (Not a Number). For example, calculating the square root of -1 results in NaN. Another example is (floating-point) dividing zero by zero: Click here to view code image WOW! eBook www.wowebook.org System.out.println(0.0 / 0.0); // Prints: NaN NaN is represented by the constant named NaN in the wrapper classes java.lang.Float and java.lang.Double. Any operation involving NaN produces NaN. Any comparison (except inequality !=) involving NaN and any other value (including NaN) returns false. An inequality comparison of NaN with another value (including NaN) always returns true. However, the recommended way of checking a value for NaN is to use the static method isNaN() defined in both wrapper classes, java.lang.Float and java.lang.Double. Strict Floating-Point Arithmetic: strictfp Although floating-point arithmetic in Java is defined in accordance with the IEEE-754 32bit (float) and 64-bit (double) standard formats, the language does allow JVM implementations to use other extended formats for intermediate results. This means that floating-point arithmetic can give different results on such JVMs, with possible loss of precision. Such a behavior is termed non-strict, in contrast to being strict and adhering to the standard formats. To ensure that identical results are produced on all JVMs, the keyword strictfp can be used to enforce strict behavior for floating-point arithmetic. The modifier strictfp can be applied to classes, interfaces, and methods. A strictfp method ensures that all code in the method is executed strictly. If a class or interface is declared to be strictfp, then all code (in methods, initializers, and nested classes and interfaces) is executed strictly. If the expression is determined to be in a strictfp construct, it is executed strictly. Strictness, however, is not inherited by the subclasses or subinterfaces. Constant expressions are always evaluated strictly at compile time. Unary Arithmetic Operators: , The unary operators have the highest precedence of all the arithmetic operators. The unary operator - negates the numeric value of its operand. The following example illustrates the right associativity of the unary operators: Click here to view code image int value = - -10; // (-(-10)) is 10 Notice the blank needed to separate the unary operators; otherwise, these would be interpreted as the decrement operator -- (§5.9, p. 176), which would result in a compiletime error because a literal cannot be decremented. The unary operator + has no effect on the evaluation of the operand value. Multiplicative Binary Operators: , , Multiplication Operator: * The multiplication operator * multiplies two numbers. Click here to view code image int sameSigns = -4 * -8; // result: WOW! eBook www.wowebook.org 32 double oppositeSigns = -32.0 int zero = 4 * -8.0; // Widening of int 4 to double. result: 0 * -0; // result: 0 Division Operator: / The division operator / is overloaded. If its operands are integral, the operation results in integer division. Click here to view code image int i1 = 4 / 5; int i2 = 8 / 8; double d1 = 12 / 8; conversion // result: 0 // result: 1 // result: 1.0; integer division, then widening Integer division always returns the quotient as an integer value; that is, the result is truncated toward zero. Note that the division performed is integer division if the operands have integral values, even if the result will be stored in a floating-point type. The integer value is subjected to a widening conversion in the assignment context. An ArithmeticException is thrown when integer division with zero is attempted, meaning that integer division by zero is an illegal operation. If any of the operands is a floating-point type, the operation performs floating-point division, where relevant operand values undergo binary numeric promotion: Click here to view code image double d2 = 4.0 / 8; double d3 = 8 / 8.0; float d4 = 12.0F / 8; // result: 0.5 // result: 1.0 // result: 1.5F double result1 = 12.0 / 4.0 * 3.0; double result2 = 12.0 * 3.0 / 4.0; // ((12.0 / 4.0) * 3.0) which is 9.0 // ((12.0 * 3.0) / 4.0) which is 9.0 Remainder Operator: % In mathematics, when we divide a number (the dividend) by another number (the divisor), the result can be expressed in terms of a quotient and a remainder. For example, when 7 is divided by 5, the quotient is 1 and the remainder is 2. The remainder operator % returns the remainder of the division performed on the operands. Click here to view code image int quotient = 7 / 5; int remainder = 7 % 5; // Integer division operation: 1 // Integer remainder operation: 2 For integer remainder operation, where only integer operands are involved, evaluation of the expression (x % y) always satisfies the following relation: x == (x / y) * y + (x % y) In other words, the right-hand side yields a value that is always equal to the value of the dividend. The following examples show how we can calculate the remainder so that this relation is satisfied: WOW! eBook www.wowebook.org The remainder can be negative only if the dividend is negative, and the sign of the divisor is irrelevant. A shortcut to evaluating the remainder involving negative operands is the following: ignore the signs of the operands, calculate the remainder, and negate the remainder if the dividend is negative. Click here to view code image int r0 int r1 long r2 int r3 long r4 boolean = 7 % 7; // 0 = 7 % 5; // 2 = 7L % -5L; // 2L = -7 % 5; // -2 = -7L % -5L; // -2L relation = -7L == (-7L / -5L) * -5L + r4; // true An ArithmeticException is thrown if the divisor evaluates to zero. Note that the remainder operator accepts not only integral operands, but also floating-point operands. The floating-point remainder r is defined by the relation r == a - (b * q) where a and b are the dividend and the divisor, respectively, and q is the integer quotient of (a/b). The following examples illustrate a floating-point remainder operation: Click here to view code image double float double float double boolean float dr0 = 7.0 % 7.0; // 0.0 fr1 = 7.0F % 5.0F; // 2.0F dr1 = 7.0 % -5.0; // 2.0 fr2 = -7.0F % 5.0F; // -2.0F dr2 = -7.0 % -5.0; // -2.0 fpRelation = dr2 == (-7.0) - (-5.0) * (long)(-7.0 / -5.0); fr3 = -7.0F % 0.0F; // NaN WOW! eBook www.wowebook.org // true Additive Binary Operators: , The addition operator + and the subtraction operator - behave as their names imply: They add and subtract values, respectively. The binary operator + also acts as string concatenation if any of its operands is a string (§5.8, p. 174). Additive operators have lower precedence than all the other arithmetic operators. Table 5.6 includes examples that show how precedence and associativity are used in arithmetic expression evaluation. Table 5.6 Examples of Arithmetic Expression Evaluation Numeric Promotions in Arithmetic Expressions Unary numeric promotion is applied to the single operand of the unary arithmetic operators - and +. When a unary arithmetic operator is applied to an operand whose type is narrower than int, the operand is promoted to a value of type int, with the operation resulting in an int value. If the conditions for implicit narrowing conversion are not fulfilled (p. 160), assigning the int result to a variable of a narrower type will require a cast. This is demonstrated by the following example, where the byte operand b is promoted to an int in the expression (-b): Click here to view code image byte b = 3; b = (byte) -b; // int literal in range. Narrowing conversion. // Cast required on assignment. Binary numeric promotion is applied to operands of binary arithmetic operators. Its application leads to type promotion for the operands, as explained in §5.2, p. 149. The result is of the promoted type, which is always type int or wider. For the expression at (1) in Example 5.2, numeric promotions proceed as shown in Figure 5.4. Note the integer division performed in evaluating the subexpression (c / s). WOW! eBook www.wowebook.org Figure 5.4 Example 5.2 Numeric Promotion in Arithmetic Expressions Numeric Promotion in Arithmetic Expressions Click here to view code image public class NumPromotion { public static void main(String[] args) { byte b = 32; char c = ‘z’; // Unicode value 122 (\u007a) short s = 256; int i = 10000; float f = 3.5F; double d = 0.5; double v = (d * i) + (f * -b) - (c / s); // (1) 4888.0D System.out.println(“Value of v: ” + v); } } Output from the program: Value of v: 4888.0 In addition to the binary numeric promotions in arithmetic expression evaluation, the resulting value can undergo an implicit widening conversion if assigned to a variable. In WOW! eBook www.wowebook.org the first two declaration statements that follow, only assignment conversions take place. Numeric promotions take place in the evaluation of the right-hand expression in the other declaration statements. Click here to view code image Byte b = 10; assignment. Short s = 20; assignment. char c = ‘z’; int i = s * b; widening. long n = 20L + s; float r = s + c; double d = r + i; // Constant in range: narrowing and boxing on // Constant in range: narrowing and boxing on // 122 (\u007a) // Values in s and b promoted to int: unboxing, // // // // // // Value in Value in value in widening Value in widening s promoted to long: unboxing, widening. s is unboxed. This short value and the char c are promoted to int, followed by implicit conversion of int to float on assignment. i promoted to float, followed by implicit conversion of float to double on assignment. Binary numeric promotion for operands of binary operators implies that each operand of a binary operator is promoted to type int or a broader numeric type, if necessary. As with unary operators, care must be exercised in assigning the value resulting from applying a binary operator to operands of these types. Click here to view code image short h = 40; h = h + 2; // OK: int converted to short. Implicit narrowing. // Error: cannot assign an int to short. The value of the expression h + 2 is of type int. Although the result of the expression is in the range of short, this cannot be determined at compile time. The assignment requires a cast. h = (short) (h + 2); // OK Notice that applying the cast operator (short) to the individual operands does not work: Click here to view code image h = (short) h + (short) 2; // The resulting value should be cast. Neither does the following approach, which results in a compile-time error: Click here to view code image h = (short) h + 2; // The resulting value should be cast. In this case, binary numeric promotion leads to an int value as the result of evaluating the expression on the right-hand side and, therefore, requires an additional cast to narrow it to a short value. Arithmetic Compound Assignment Operators: A compound assignment operator has the following syntax: variable op= expression and the following semantics: Click here to view code image WOW! eBook www.wowebook.org , , , , variable = (type) ((variable) op (expression)) The type of the variable is type and the variable is evaluated only once. Note the cast and the parentheses implied in the semantics. Here op= can be any of the compound assignment operators specified in Table 5.2. The compound assignment operators have the lowest precedence of all the operators in Java, allowing the expression on the right-hand side to be evaluated before the assignment. Table 5.7 defines the arithmetic compound assignment operators. Table 5.7 Arithmetic Compound Assignment Operators The implied cast operator, (T), in the compound assignments becomes necessary when the result must be narrowed to the target type. This is illustrated by the following examples: Click here to view code image int i = 2; i *= i + 4; Integer iRef = 2; iRef *= iRef + 4; + 4)). byte b = 2; b += 10; b = b + 10; // (1) Evaluated as i = (int) ((i) * (i + 4)). // (2) Evaluated as iRef = (Integer) ((iRef) * (iRef // (3) Evaluated as b = (byte) (b + 10). // (4) Will not compile. Cast is required. At (1) the source int value is assigned to the target int variable, and the cast operator (int) in this case is an identity conversion (i.e., conversion from a type to the same type). Such casts are permitted. The assignment at (2) entails unboxing to evaluate the expression on the right-hand side, followed by boxing to assign the int value. However, at (3), as the source value is an int value because the byte value in b is promoted to int to carry out the addition, assigning it to a target byte variable requires an implicit narrowing conversion. The situation at (4) with simple assignment will not compile, because implicit narrowing conversion is not applicable. The variable is evaluated only once in the expression, not twice, as one might infer from the definition of the compound assignment operator. In the following assignment, a[i] is evaluated just once: Click here to view code image int[] a = new int[] { 2015, 2016, 2017 }; int i = 2; a[i] += 1; // Evaluates as a[2] = a[2] + 1, and a[2] gets the value WOW! eBook www.wowebook.org 2018. Implicit narrowing conversions are also applied to increment and decrement operators (§5.9, p. 176). Boolean logical compound assignment operators are covered in §5.13, p. 184. Review Questions 5.4 Which of the following expressions will be evaluated using floating-point arithmetic? Select the three correct answers. (a) 2.0 * 3.0 (b) 2 * 3 (c) 2/3 + 5/7 (d) 2.4 + 1.6 (e) 0x10 * 1L * 300.0 5.5 What is the value of the expression (1 / 2 + 3 / 2 + 0.1)? Select the one correct answer. (a) 1 (b) 1.1 (c) 1.6 (d) 2 (e) 2.1 5.6 What will be the result of compiling and running the following program? Click here to view code image public class Integers { public static void main(String[] args) { System.out.println(0x10 + 10 + 010 + 0b10); } } Select the one correct answer. (a) The program will not compile. (b) When run, the program will print 28. (c) When run, the program will print 30. (d) When run, the program will print 34. (e) When run, the program will print 36. (f) When run, the program will print 10101010. WOW! eBook www.wowebook.org 5.7 Which of the following expressions are valid? Select the three correct answers. (a) (- 1 -) (b) (+ + 1) (c) (+-+-+-1) (d) (—1) (e) (1 * * 1) (f) (- -1) 5.8 What is the value of evaluating the following expression: (- -1-3 * 10 / 51)? Select the one correct answer. (a) –8 (b) –6 (c) 7 (d) 8 (e) 10 (f) None of the above 5.9 Which of these assignments are valid? Select the four correct answers. (a) short s = 12; (b) long l = 012; (c) int other = (int) true; (d) float f = -123; (e) double d = 0x12345678; 5.8 The Binary String Concatenation Operator The binary operator + is overloaded in the sense that the operation performed is determined by the type of the operands. When one of the operands is a String object, a string concatenation is performed rather than numeric addition. String concatenation results in a newly created String object in which the characters in the string representation of the left-hand operand precede the characters in the string representation of the right-hand operand. It might be necessary to perform a string conversion on the non-String operand before the string concatenation can be performed. The String class is discussed in §8.4, p. 357. A string conversion is performed on the non-String operand as follows: WOW! eBook www.wowebook.org • For an operand of a primitive data type, its value is converted to a string representation. • For all reference value operands, a string representation is constructed by calling the no-argument toString() method on the referred object. Most classes override this method from the Object class so as to provide a more meaningful string representation of their objects. Discussion of the toString() method can be found in §8.2, p. 342. • Values like true, false, and null have string representations that correspond to their names. A reference variable with the value null also has the string representation "null" in this context. The operator + is left associative and has the same precedence level as the additive operators, whether it is performed as a string concatenation or as a numeric addition. Click here to view code image String strVal = ”” + 2016; String theName = ” Uranium”; theName = ” Pure” + theName; String trademark1 = 100 + “%” + theName; // (1) “2016” // (2) ” Pure Uranium” // (3) “100% Pure Uranium” Since the + operator is left-associative, the evaluation in (3) proceeds as follows: The int value 100 is concatenated with the string literal "%", followed by concatenation with the contents of the String object referred to by theName reference. Note that using the character literal '%', instead of the string literal "%" in line (2), does not give the same result: Click here to view code image String trademark2 = 100 + ‘%’ + theName; // (4) “137 Pure Uranium” Integer addition is performed by the first + operator: 100 + '%'; that is, (100 + 37). Caution should be exercised because the + operator might not be applied as intended, as shown by the following example: Click here to view code image System.out.println(“We can put two and two together and get ” + 2 + 2); (5) // This statement prints "We can put two and two together and get 22". String concatenation proceeds from left to right: The String literal is concatenated with the first int literal 2, followed by concatenation with the second int literal 2. Both occurrences of the + operator are treated as string concatenation. To convey the intended meaning of the sentence, parentheses are necessary: Click here to view code image System.out.println(“We can put two and two together and get ” + (2 + 2)); // (6) This statement prints "We can put two and two together and get 4", since the parentheses enforce integer addition in the expression (2 + 2) before string concatenation is performed with the contents of the String operand. WOW! eBook www.wowebook.org The following statement will print the correct result, even without the parentheses, because the * operator has higher precedence than the + operator: Click here to view code image System.out.println(“2 * 2 = ” + 2 * 2); // (7) 2 * 2 = 4 Creation of temporary String objects might be necessary to store the results of performing successive string concatenations in a String-valued expression. For a String-valued constant expression ((1), (5), (6) and (7) in the preceding examples), the compiler computes such an expression at compile time, and the result is treated as a string literal in the program. The compiler uses a string builder to avoid the overhead of temporary String objects when applying the string concatenation operator (+) in String-valued non-constant expressions ((2), (3) and (4) in the preceding examples), as explained in §8.5, p. 378. 5.9 Variable Increment and Decrement Operators: , Variable increment (++) and decrement (--) operators come in two flavors: prefix and postfix. These unary operators have the side effect of changing the value of the arithmetic operand, which must evaluate to a variable. Depending on the operator used, the variable is either incremented or decremented by 1. These operators cannot be applied to a variable that is declared final and that has been initialized, as the side effect would change the value in such a variable. These operators are very useful for updating variables in loops where only the side effect of the operator is of interest. The Increment Operator The prefix increment operator has the following semantics: ++i adds 1 to the value in i, and stores the new value in i. It returns the new value as the value of the expression. It is equivalent to the following statements: i += 1; result = i; return result; The postfix increment operator has the following semantics: j++ adds 1 to the value in j, and stores the new value in j. It returns the original value that was in j as the value of the expression. It is equivalent to the following statements: result = j; j += 1; return result; The Decrement Operator The prefix decrement operator has the following semantics: --i subtracts 1 from the value of i, and stores the new value in i. It returns the new value as the value of the expression. It is equivalent to the following statements: i -= 1; WOW! eBook www.wowebook.org result = i; return result; The postfix decrement operator has the following semantics: j-- subtracts 1 from the value of j, and stores the new value in j. It returns the original value that was in j as the value of the expression. It is equivalent to the following statements: result = j; j -= 1; return result; This behavior of decrement and increment operators applies to any variable whose type is a numeric primitive type or its corresponding numeric wrapper type. Necessary numeric promotions are performed on the value 1 and the value of the variable. Before the new value is assigned to the variable, it is subjected to any narrowing primitive conversion and/or boxing that might be necessary. Here are some examples that illustrate the behavior of increment and decrement operators: Click here to view code image // (1) Prefix order: increment/decrement operand before use. int i = 10; int k = ++i + —i; // ((++i) + (—i)). k gets the value 21 and i becomes 10. —i; // Only side effect utilized. i is 9. (expression statement) Integer iRef = 11; // Boxing on assignment —iRef; // Only side effect utilized. iRef refers // object with the value 10. (expression k = ++iRef + —iRef;// ((++iRef) + (—iRef)). k gets the value // iRef refers to an Integer object with to an Integer statement) 21 and the value 10. // (2) Postfix order: increment/decrement operand after use. long j = 10; long n = j++ + j—; // ((j++) + (j—)). n gets the value 21L and j becomes 10L. j++; // Only side effect utilized. j is 11L. (expression statement) An increment or decrement operator, together with its operand, can be used as an expression statement (§3.2, p. 50). Execution of the assignment in the second declaration statement under (1) proceeds as follows: Execution of the expression statement --iRef; under (1) proceeds as follows: • The value in the Integer object referred to by the reference iRef is unboxed, resulting in the int value 11. • The value 11 is decremented, resulting in the value 10. • The value 10 is boxed in an Integer object, and this object’s reference value is assigned to the reference iRef. WOW! eBook www.wowebook.org • The int value 10 of the expression statement is discarded. Expressions where variables are modified multiple times during the evaluation should be avoided, because the order of evaluation is not always immediately apparent. We cannot associate increment and decrement operators. Given that a is a variable, we cannot write (++(++a)). The reason is that any operand to ++ must evaluate to a variable, but the evaluation of (++a) results in a value. In the next example, both binary numeric promotion and an implicit narrowing conversion are performed to achieve the side effect of modifying the value of the operand. The int value of the expression (++b) (that is, 11), is assigned to the int variable i. The side effect of incrementing the value of the byte variable b requires binary numeric promotion to perform int addition, followed by an implicit narrowing conversion of the int value to byte to perform the assignment. Click here to view code image byte b = 10; int i = ++b; // i is 11, and so is b. The following example illustrates applying the increment operator to a floating-point operand. The side effect of the ++ operator is overwritten by the assignment. Click here to view code image double x = 4.5; x = x + ++x; // x gets the value 10.0. Review Questions 5.10 Which statements are true? Select the three correct answers. (a) The expression (1 + 2 + "3") evaluates to the string "33". (b) The expression ("1" + 2 + 3) evaluates to the string "15". (c) The expression (4 + 1.0f) evaluates to the float value 5.0f. (d) The expression (10/9) evaluates to the int value 1. (e) The expression ('a' + 1) evaluates to the char value 'b'. 5.11 What happens when you try to compile and run the following program? Click here to view code image public class Prog1 { public static void main(String[] args) { int k = 1; int i = ++k + k++ + + k; // (1) System.out.println(i); } } Select the one correct answer. WOW! eBook www.wowebook.org (a) The program will not compile, because of errors in the expression at (1). (b) The program will compile and print the value 3 at runtime. (c) The program will compile and print the value 4 at runtime. (d) The program will compile and print the value 7 at runtime. (e) The program will compile and print the value 8 at runtime. 5.12 Which is the first line that will cause a compile-time error in the following program? Click here to view code image public class MyClass { public static void main(String[] args) { char c; int i; c = ‘a’; // (1) i = c; // (2) i++; // (3) c = i; // (4) c++; // (5) } } Select the one correct answer. (a) (1) (b) (2) (c) (3) (d) (4) (e) (5) (f) None of the above. The compiler will not report any errors. 5.13 What is the result of compiling and running the following program? Click here to view code image public class Cast { public static void main(String[] args) { byte b = 128; int i = b; System.out.println(i); } } Select the one correct answer. (a) The program will not compile, because a byte value cannot be assigned to an int variable without using a cast. (b) The program will compile, and print 128 at runtime. (c) The program will not compile, because the value 128 is not in the range of values for the byte type. WOW! eBook www.wowebook.org (d) The program will compile, but will throw a ClassCastException at runtime. (e) The program will compile, and print 255 at runtime. 5.14 What will be the result of compiling and running the following program? Click here to view code image public class EvaluationOrder { public static void main(String[] args) { int[] array = { 4, 8, 16 }; int i = 1; array[++i] = —i; System.out.println(array[0] + array[1] + array[2]); } } Select the one correct answer. (a) 13 (b) 14 (c) 20 (d) 21 (e) 24 5.10 Boolean Expressions As the name implies, a boolean expression has the boolean data type and can evaluate to only the values true or false. Boolean expressions, when used as conditionals in control statements, allow the program flow to be controlled during execution. Boolean expressions can be formed using relational operators (§5.11, p. 180), equality operators (§5.12, p. 181), boolean logical operators (§5.13, p. 184), conditional operators (§5.14, p. 186), the assignment operator (§5.6, p. 158), and the instanceof operator (§7.11, p. 321). 5.11 Relational Operators: , , , Given that a and b represent numeric expressions, the relational (also called comparison) operators are defined as shown in Table 5.8. Table 5.8 Relational Operators All relational operators are binary operators and their operands are numeric expressions. Binary numeric promotion is applied to the operands of these operators. The evaluation WOW! eBook www.wowebook.org results in a boolean value. Relational operators have precedence lower than arithmetic operators, but higher than that of the assignment operators. Click here to view code image double hours = 45.5; Double time = 18.0; boolean overtime = hours >= 35; int. boolean beforeMidnight = time < reference. char letterA = ‘A’; boolean order = letterA < ‘a’; char. // Boxing of double value. // true. Binary numeric promotion: double <— 24.0;// true. Unboxing of value in time // true. Binary numeric promotion: int <— Relational operators are nonassociative. Mathematical expressions like a ≤ b ≤ c must be written using relational and boolean logical/conditional operators. Click here to view code image int a = 1, b = 7, c = 10; boolean illegal = a <= b <= c; boolean valid2 = a <= b && b <= c; // (1) Illegal. // (2) OK. Since relational operators have left associativity, the evaluation of the expression a <= b <= c at (1) in these examples would proceed as follows: ((a <= b) <= c). Evaluation of (a <= b) would yield a boolean value that is not permitted as an operand of a relational operator; that is, (boolean value <= c) would be illegal. 5.12 Equality We distinguish between primitive data equality, object reference equality, and object value equality. The equality operators have lower precedence than the relational operators, but higher precedence than the assignment operators. Primitive Data Value Equality: , Given that a and b represent operands of primitive data types, the primitive data value equality operators are defined as shown in Table 5.9. Table 5.9 Primitive Data Value Equality Operators The equality operator == and the inequality operator != can be used to compare primitive data values, including boolean values. Binary numeric promotion is applied to the nonboolean operands of these equality operators. Click here to view code image int year = 2002; boolean isEven = year % 2 == 0; // true. WOW! eBook www.wowebook.org boolean compare = ‘1’ == 1; applied. boolean test = compare == false; // false. Binary numeric promotion // true. Care must be exercised when comparing floating-point numbers for equality, as an infinite number of floating-point values can be stored only as approximations in a finite number of bits. For example, the expression (1.0 - 2.0/3.0 == 1.0/3.0) returns false, although mathematically the result should be true. Analogous to the discussion for relational operators, mathematical expressions like a = b = c must be written using relational and logical/conditional operators. Since equality operators have left associativity, the evaluation of the expression a == b == c would proceed as follows: ((a == b) == c). Evaluation of (a == b) would yield a boolean value that is permitted as an operand of a data value equality operator, but (boolean value == c) would be illegal if c had a numeric type. This problem is illustrated in the following examples. The expression at (1) is illegal, but those at (2) and (3) are legal. Click here to view code image int a, b, c; a = b = c = 5; boolean illegal = a == b == c; boolean valid2 = a == b && b == c; boolean valid3 = a == b == true; Object Reference Equality: // (1) Illegal. // (2) Legal. // (3) Legal. , The equality operator == and the inequality operator != can be applied to reference variables to test whether they refer to the same object. Given that r and s are reference variables, the reference equality operators are defined as shown in Table 5.10. Table 5.10 Reference Equality Operators The operands must be cast compatible: It must be possible to cast the reference value of the one into the other’s type; otherwise, it is a compile-time error. Casting of references is discussed in §7.8, p. 311. Click here to view code image Pizza pizzaA = new Pizza(“Sweet&Sour”); Pizza pizzaB = new Pizza(“Sweet&Sour”); Pizza pizzaC = new Pizza(“Hot&Spicy”); // new object // new object // new object String banner = “Come and get it!”; // new object boolean test = banner == pizzaA; boolean test1 = pizzaA == pizzaB; boolean test2 = pizzaA == pizzaC; // (1) Compile-time error // false // false pizzaA = pizzaB; // Denote the same object; are WOW! eBook www.wowebook.org aliases boolean test3 = pizzaA == pizzaB; // true The comparison banner == pizzaA in (1) is illegal, because the String and Pizza types are not related and therefore the reference value of one type cannot be cast to the other type. The values of test1 and test2 are false because the three references denote different objects, regardless of the fact that pizzaA and pizzaB are both sweet and sour pizzas. The value of test3 is true because now both pizzaA and pizzaB denote the same object. The equality and inequality operators are applied to object references to check whether two references denote the same object. The state of the objects that the references denote is not compared. This is the same as testing whether the references are aliases, meaning that they denote the same object. The null literal can be assigned to any reference variable, and the reference value in a reference variable can be compared for equality with the null literal. The comparison can be used to avoid inadvertent use of a reference variable that does not denote any object. if (objRef != null) { // … use objRef … } Note that only when the type of both operands is either a reference type or the null type, do these operators test for object reference equality. Otherwise, they test for primitive data equality (see also §8.3, p. 350). In the following code snippet, binary numeric promotion involving unboxing is performed at (2): Click here to view code image Integer boolean boolean boolean iRef b1 = b2 = b3 = = 10; iRef == null; iRef == 10; null == 10; // (1) Object reference equality // (2) Primitive data equality // Compile-time error! Object Value Equality The Object class provides the method public boolean equals(Object obj), which can be overridden (§7.2, p. 268) to give the right semantics of object value equality. The default implementation of this method in the Object class returns true only if the object is compared with itself, as if the equality operator == had been used to compare aliases of an object. Consequently, if a class does not override the semantics of the equals() method from the Object class, object value equality is the same as object reference equality. Certain classes in the standard API override the equals() method, such as java.lang.String and the wrapper classes for the primitive data types. For two String objects, value equality means they contain identical character sequences. For the wrapper classes, value equality means that the wrapper objects have the same primitive value and are of the same wrapper type (see also §8.3, p. 350). Click here to view code image WOW! eBook www.wowebook.org // Equality for String movie1 = String movie2 = String movie3 = boolean test0 = boolean test1 = String objects means identical character sequences. new String(“The Revenge of the Exception Handler”); new String(“High Noon at the Java Corral”); new String(“The Revenge of the Exception Handler”); movie1.equals(movie2); // false. movie1.equals(movie3); // true. // Equality for Boolean flag1 = Boolean flag2 = boolean test2 = boolean test3 = value. wrapper classes means same type and same primitive value. true; // Boxing. false; // Boxing. flag1.equals(“true”); // false. Not same type. flag1.equals(!flag2); // true. Same type and Integer iRef = 100; Short sRef = 100; boolean test4 = iRef.equals(100); value. boolean test5 = iRef.equals(sRef); boolean test6 = iRef.equals(3.14); // Boxing. // Boxing. // true. Same type and // false. Not same type. // false. Not same type. // The Pizza class does not override the equals() method, so we can use either // equals() method inherited from the Object class or equality operator ==. Pizza pizza1 = new Pizza(“VeggiesDelight”); Pizza pizza2 = new Pizza(“VeggiesDelight”); Pizza pizza3 = new Pizza(“CheeseDelight”); boolean test7 = pizza1.equals(pizza2); // false. boolean test8 = pizza1.equals(pizza3); // false. boolean test9 = pizza1 == pizza2; // false. pizza1 = pizza2; // Creates aliases. boolean test10 = pizza1.equals(pizza2); // true. boolean test11 = pizza1 == pizza2; // true. 5.13 Boolean Logical Operators: , , , Boolean logical operators include the unary operator ! (logical complement) and the binary operators & (logical AND), | (logical inclusive OR), and ^ (logical exclusive OR, also called logical XOR). These operators can be applied to boolean or Boolean operands, returning a boolean value. The operators &, |, and ^ can also be applied to integral operands to perform bitwise logical operations, but are not in the scope of this book. Given that x and y represent boolean expressions, the boolean logical operators are defined in Table 5.11. The precedence of the operators decreases from left to right in the table. Table 5.11 Truth Values for Boolean Logical Operators WOW! eBook www.wowebook.org These operators always evaluate both the operands, unlike their counterpart conditional operators && and || (§5.14, p. 186). Unboxing is applied to the operand values, if necessary. Truth values for boolean logical operators are shown in Table 5.11. Operand Evaluation for Boolean Logical Operators In the evaluation of boolean expressions involving boolean logical AND, XOR, and OR operators, both the operands are evaluated. The order of operand evaluation is always from left to right. Click here to view code image if (i > 0 & i++ < 10) {/*…*/} // i will be incremented, regardless of value in i. The binary boolean logical operators have precedence lower than the arithmetic and relational operators, but higher precedence than the assignment, conditional AND, and OR operators (§5.14, p. 186). This is illustrated in the following examples: Click here to view code image boolean b1, b2, b3 = false, b4 = false; Boolean b5 = true; b1 = 4 == 2 & 1 < 4; // false, evaluated as (b1 = ((4 == 2) & (1 < 4))) b2 = b1 | !(2.5 >= 8); // true b3 = b3 ^ b5; // true, unboxing conversion on b5 b4 = b4 | b1 & b2; // false Here, the order of evaluation is illustrated for the last expression statement: Click here to view code image (b4 = (b4 | (b1 (b4 = (false | (b4 = (false | (b4 = (false | (b4 = (false | (b4 = false) false & b2))) (b1 & b2))) (false & b2))) (false & true))) false)) Note that b2 was evaluated although, strictly speaking, it was not necessary. This behavior is guaranteed for boolean logical operators. Boolean Logical Compound Assignment Operators: , ,| Compound assignment operators for the boolean logical operators are defined in Table 5.12. The left-hand operand must be a boolean variable, and the right-hand operand must be a boolean expression. An identity conversion is applied implicitly on assignment. These operators can also be applied to integral operands to perform bitwise compound assignments, but are not covered in this book. See also the discussion on arithmetic compound assignment operators in §5.7, p. 172. WOW! eBook www.wowebook.org Table 5.12 Boolean Logical Compound Assignment Operators Here are some examples to illustrate the behavior of boolean logical compound assignment operators: Click here to view code image boolean b1 = false, b2 = true, b3 = false; Boolean b4 = false; b1 |= true; // true b4 ^= b1; // (1) true, unboxing in (b4 ^ (b1)), boxing on assignment b3 &= b1 | b2; // (2) false, b3 = (b3 & (b1 | b2)) b3 = b3 & b1 | b2; // (3) true, b3 = ((b3 & b1) | b2) The assignment at (1) entails unboxing to evaluate the expression on the right-hand side, followed by boxing to assign the boolean result. It is also instructive to compare how the assignments at (2) and (3) are performed, as they lead to different results with the same values of the operands, showing how the precedence affects the evaluation. 5.14 Conditional Operators: , The conditional operators && and || are similar to their counterpart logical operators & and |, except that their evaluation is short-circuited. Given that x and y represent values of boolean or Boolean expressions, the conditional operators are defined in Table 5.13. In the table, the operators are listed in decreasing precedence order. Table 5.13 Conditional Operators Unlike their logical counterparts & and |, which can also be applied to integral operands for bitwise operations, the conditional operators && and || can be applied only to boolean operands. Their evaluation results in a boolean value. Truth values for conditional operators are shown in Table 5.14. Not surprisingly, the conditional operators have the same truth values as their counterpart logical operators. However, unlike with their logical counterparts, there are no compound assignment operators for the conditional operators. WOW! eBook www.wowebook.org Table 5.14 Truth Values for Conditional Operators Short-Circuit Evaluation In evaluation of boolean expressions involving conditional AND and OR, the left-hand operand is evaluated before the right-hand operand, and the evaluation is short-circuited (i.e., if the result of the boolean expression can be determined from the left-hand operand, the right-hand operand is not evaluated). In other words, the right-hand operand is evaluated conditionally. The binary conditional operators have lower precedence than the arithmetic, relational, and logical operators, but higher precedence than the assignment operators. Unboxing of the operand value takes place when necessary, before the operation is performed. The following examples illustrate usage of conditional operators: Click here to view code image Boolean b1 = 4 == 2 && 1 < 4; boolean b2 = !b1 || 2.5 > 8; Boolean b3 = !(b1 && b2); boolean b4 = b1 || !b3 && b2; // // // // // // // false, short-circuit evaluated as (b1 = ((4 == 2) && (1 < 4))) true, short-circuit evaluated as (b2 = ((!b1) || (2.5 > 8))) true false, short-circuit evaluated as (b4 = (b1 || ((!b3) && b2))) The order of evaluation for computing the value stored in the boolean variable b4 proceeds as follows: Click here to view code image (b4 = (b1 || ((!b3) && b2))) (b4 = (false || ((!b3) && b2))) (b4 = (false || ((!true) && b2))) (b4 = (false || ((false) && b2))) (b4 = (false || false)) (b4 = false) Note that b2 is not evaluated, short-circuiting the evaluation. Example 5.3 illustrates the short-circuit evaluation of the initialization expressions in the declaration statements given in the earlier code snippet. In addition, it shows an evaluation (see the declaration of b5) involving boolean logical operators that always evaluate both operands. See also Example 5.1, p. 153, which uses a similar approach to illustrate the order of operand evaluation in arithmetic expressions. WOW! eBook www.wowebook.org Example 5.3 Short-Circuit Evaluation Involving Conditional Operators Click here to view code image public class ShortCircuit { public static void main(String[] args) { // Boolean b1 = 4 == 2 && 1 < 4; Boolean b1 = operandEval(1, 4 == 2) && operandEval(2, 1 < 4); System.out.println(); System.out.println(“Value of b1: ” + b1); // boolean b2 = !b1 || 2.5 > 8; boolean b2 = !operandEval(1, b1) || operandEval(2, 2.5 > 8); System.out.println(); System.out.println(“Value of b2: ” + b2); // Boolean b3 = !(b1 && b2); Boolean b3 = !(operandEval(1, b1) && operandEval(2, b2)); System.out.println(); System.out.println(“Value of b3: ” + b3); // boolean b4 = b1 || !b3 && b2; boolean b4 = operandEval(1, b1) || !operandEval(2, b3) && operandEval(3, b2); System.out.println(); System.out.println(“Value of b4: ” + b4); // boolean b5 = b1 | !b3 & b2; // Using boolean logical operators boolean b5 = operandEval(1, b1) | !operandEval(2, b3) & operandEval(3, b2); System.out.println(); System.out.println(“Value of b5: ” + b5); } static boolean operandEval(int opNum, boolean operand) { (1) System.out.print(opNum); return operand; } } // Output from the program: 1 Value 1 Value 1 Value 12 Value 123 Value of b1: false of b2: true of b3: true of b4: false of b5: false Short-circuit evaluation can be used to ensure that a reference variable denotes an object before it is used. Click here to view code image if (objRef != null && objRef.equals(other)) { /*…*/ } The method call is now conditionally dependent on the left-hand operand and will not be WOW! eBook www.wowebook.org executed if the variable objRef has the null reference. If we use the logical & operator and the variable objRef has the null reference, evaluation of the right-hand operand will result in a NullPointerException. In summary, we employ the conditional operators && and || if the evaluation of the righthand operand is conditionally dependent on the left-hand operand. We use the boolean logical operators & and | if both operands must be evaluated. The subtlety of conditional operators is illustrated by the following examples: Click here to view code image if (i > 0 && i++ < 10) {/*…*/} if (i > 0 || i++ < 10) {/*…*/} // i is not incremented if i > 0 is false. // i is not incremented if i > 0 is true. 5.15 Integer Bitwise Operators: , , , A review of integer representation (§5.5, p. 154) is recommended before continuing with this section on how integer bitwise operators can be applied to values of integral data types. Integer bitwise operators include the unary operator ~ (bitwise complement) and the binary operators & (bitwise AND), | (bitwise inclusive OR), and ^ (bitwise exclusive OR, also known as bitwise XOR). The operators &, |, and ^ are overloaded, as they can be applied to boolean or Boolean operands to perform boolean logical operations (§5.13, p. 184). Although the integer bitwise operators are not on the OCAJP 8 exam, they are included here to contrast their evaluation with that of their boolean counterparts. The binary bitwise operators perform bitwise operations between corresponding individual bit values in the operands. Unary numeric promotion is applied to the operand of the unary bitwise complement operator ~, and binary numeric promotion is applied to the operands of the binary bitwise operators. The result is a new integer value of the promoted type, which can be either int or long. Given that A and B are corresponding bit values (either 0 or 1) in the left-hand and righthand operands, respectively, these bitwise operators are defined as shown in Table 5.15. The operators are listed in decreasing precedence order. Table 5.15 Integer Bitwise Operators The result of applying bitwise operators between two corresponding bits in the operands is shown in Table 5.16, where A and B are corresponding bit values in the left-hand righthand operands, respectively. Table 5.16 is analogous to Table 5.11 for boolean logical WOW! eBook www.wowebook.org operators, if we consider bit value 1 to represent true and bit value 0 to represent false. Table 5.16 Result Table for Bitwise Operators Examples of Bitwise Operator Application Click here to view code image char v1 = ‘)’; byte v2 = 13; // Unicode value 41 int int int int // // // // result1 result2 result3 result4 = = = = ~v1; v1 & v2; v1 | v2; v1 ^ v2; -42 9 45 36 Table 5.17 shows how the result is calculated. Unary and binary numeric promotions are applied first, converting the operands to int in these cases. Note that the operator semantics is applied to corresponding individual bits—that is, first bit of left-hand operand and first bit of right-hand operand, second bit of left-hand operand and second bit of righthand operand, and so on. Table 5.17 Examples of Bitwise Operations It is instructive to run examples and print the result of a bitwise operation in different notations, as shown in Example 5.4. The integer bitwise operators support a programming technique called bit masking. The value v2 is usually called a bit mask. Depending on the bitwise operation performed on the value v1 and the mask v2, we see how the resulting value reflects the bitwise operation performed between the individual corresponding bits of the value v1 and the mask v2. By choosing appropriate values for the bits in the mask v2 and the right bitwise operation, it is possible to extract, set, and toggle specific bits in the value v1. Methods for converting integers to strings in different notations can be found in the WOW! eBook www.wowebook.org Integer class (§8.3, p. 353). Converting integers to different number systems is discussed in §5.5, p. 154. Example 5.4 Bitwise Operations Click here to view code image public class BitOperations { public static void main(String[] char v1 = ‘)’; byte v2 = 13; printIntToStr(“v1:”, v1); printIntToStr(“v2:”, v2); printIntToStr(“~v1:”, ~v1); printIntToStr(“v1 & v2:”, v1 & printIntToStr(“v1 | v2:”, v1 | printIntToStr(“v1 ^ v2:”, v1 ^ } args) { // Unicode value 41 // // // v2); // v2); // v2); // 41 13 -42 9 45 36 public static void printIntToStr(String label, int result) { System.out.println(label); System.out.println(“ Binary: ” + Integer.toBinaryString(result)); System.out.println(“ Hex: ” + Integer.toHexString(result)); System.out.println(“ Decimal: ” + result); } } Output from the program: Click here to view code image v1: Binary: 101001 Hex: 29 Decimal: 41 v2: Binary: 1101 Hex: d Decimal: 13 ~v1: Binary: Hex: Decimal: v1 & v2: Binary: Hex: Decimal: v1 | v2: Binary: Hex: Decimal: v1 ^ v2: Binary: Hex: Decimal: 11111111111111111111111111010110 ffffffd6 -42 1001 9 9 101101 2d 45 100100 24 36 WOW! eBook www.wowebook.org Bitwise Compound Assignment Operators: , , Bitwise compound assignment operators for the bitwise operators are defined in Table 5.18. Type conversions for these operators, when applied to integral operands, are the same as for other compound assignment operators: an implicit narrowing conversion is performed on assignment when the destination data type is either byte, short, or char. These operators can also be applied to boolean operands to perform logical compound assignments (§5.13, p. 185). Table 5.18 Bitwise Compound Assignment Operators Examples of Bitwise Compound Assignment Click here to view code image int v0 = -42; char v1 = ‘)’; byte v2 = 13; // 41 v0 &= 15; v1 |= v2; // 1…1101 0110 & 0…0000 1111 => 0…0000 0110 (= 6) // (1) 0…0010 1001 | 0…0000 1101 => 0…0010 1101 (= 45, ‘-‘) At (1) in these examples, both the char value in v1 and the byte value in v2 are first promoted to int. The result is implicitly narrowed to the destination type char on assignment. Review Questions 5.15 Which of the following expressions evaluate to true? Select the two correct answers. (a) (false | true) (b) (null != null) (c) (4 <= 4) (d) (!true) (e) (true & false) 5.16 Which of the following statements are true? Select the two correct answers. (a) The remainder operator % can be used only with integral operands. (b) Short-circuit evaluation occurs with boolean logical operators. WOW! eBook www.wowebook.org (c) The arithmetic operators *, /, and % have the same level of precedence. (d) A short value ranges from -128 to +127, inclusive. (e) (+15) is a legal expression. 5.17 Which statements are true about the lines of output printed by the following program? Click here to view code image public class BoolOp { static void op(boolean a, boolean b) { boolean c = a != b; boolean d = a ^ b; boolean e = c == d; System.out.println(e); } public static void main(String[] args) { op(false, false); op(true, false); op(false, true); op(true, true); } } Select the three correct answers. (a) All lines printed are the same. (b) At least one line contains false. (c) At least one line contains true. (d) The first line contains false. (e) The last line contains true. 5.18 What is the result of running the following program? Click here to view code image public class OperandOrder { public static void main(String[] args) { int i = 0; int[] a = {3, 6}; a[i] = i = 9; System.out.println(i + ” ” + a[0] + ” ” + a[1]); } } Select the one correct answer. (a) When run, the program throws an ArrayIndexOutOfBoundsException. (b) When run, the program will print 9 9 6. (c) When run, the program will print 9 0 6. (d) When run, the program will print 9 3 6. (e) When run, the program will print 9 3 9. WOW! eBook www.wowebook.org 5.19 Which statements are true about the output from the following program? Click here to view code image public class Logic { public static void main(String[] args) { int i = 0; int j = 0; boolean t = true; boolean r; r = (t & 0 < (i+=1)); r = (t && 0 < (i+=2)); r = (t | 0 < (j+=1)); r = (t || 0 < (j+=2)); System.out.println(i + ” ” + j); } } Select the two correct answers. (a) The first digit printed is 1. (b) The first digit printed is 2. (c) The first digit printed is 3. (d) The second digit printed is 1. (e) The second digit printed is 2. (f) The second digit printed is 3. 5.16 The Conditional Operator: The ternary conditional operator ?: allows conditional expressions to be defined. The conditional expression has the following syntax: Click here to view code image condition ? expression1 : expression2 It is called ternary because it has three operands. If the boolean expression condition is true, then expression1 is evaluated; otherwise, expression2 is evaluated. Both expression1 and expression2 must evaluate to values that can be converted to the type of the conditional expression. This type is determined from the types of the two expressions. The value of the expression evaluated is converted to the type of the conditional expression, and may involve autoboxing and unboxing. Evaluation of a conditional expression is an example of short-circuit evaluation. As only one of the two expressions is evaluated, one should be wary of any side effects in a conditional expression. In the following code snippet at (1), both expressions in the conditional expression are of type byte. The type of the conditional expression is therefore byte. That a value of type byte can be converted to an int by an implicit widening numeric conversion to be assignment compatible with the int variable daysInFebruary is secondary in WOW! eBook www.wowebook.org determining the type of the conditional expression. Note that the conditional operator at (1) has higher precedence than the assignment operator =, making it unnecessary to enclose the conditional expression in parentheses. Click here to view code image boolean leapYear = false; byte v29 = 29; byte v28 = 28; int daysInFebruary = leapYear ? v29 : v28; // (1) The following examples illustrate the use of conditional expressions. The type of the conditional expression at (2) is int, and no conversion of any expression value is necessary. The type of the conditional expression at (3) is double, due to binary numeric promotion: The int value of the first expressions is promoted to a double. The compiler reports an error because a double cannot be assigned to an int variable. The type of the conditional expression at (4) is also double as in (3), but now the double value is assignment compatible with the double variable minDoubleValue. Click here to view code image int i = 3; int j = 4; int minValue1 = i < j ? i : j; // (2) int int minValue2 = i < j ? i : Double.MIN_VALUE; // (3) double. Not OK. double minDoubleValue = i < j ? i : Double.MIN_VALUE; // (4) double In the following code snippet in (5), the primitive values of the expressions can be boxed and assigned to an Object reference. In (6), the int value of the first expression can be boxed in an Integer. The println() method creates and prints a string representation of any object whose reference value is passed as parameter. Click here to view code image // Assume i and j are of type int and initialized correctly. Object obj = i < j ? i : true; // (5) value of i boxed in Integer or // literal true boxed in Boolean System.out.println(i < j ? i : “Hi”); // (6) value of i boxed in Integer or // String object “Hi” The conditional expression is not an expression statement. The following code will not compile: Click here to view code image (i < j) ? i : j; // Compile-time error! The conditional expression can be nested, and the conditional operator associates from right to left. Click here to view code image a?b:c?d:e?f:g evaluates as (a?b:(c?d:(e?f:g))) The value of this conditional expression is g if, and only if, a, c, and e are false. A nested conditional expression is used in the next example. As a convention, the condition in a conditional expression is enclosed in parentheses to aid reading the code. Typically, a conditional expression is used when it makes the code easier to read, especially when the expressions are short and without side effects. WOW! eBook www.wowebook.org Click here to view code image int n = 3; String msg = (n==0) ? “no cookies.” : (n==1) ? “one cookie.” : “many cookies.”; System.out.println(“You get ” + msg); // You get many cookies. The conditional operator is the expression equivalent of the if-else statement (§6.2, p. 201). 5.17 Other Operators: , , , -> The new operator is used to create objects, such as instances of classes and arrays. It is used with a constructor call to instantiate classes (§3.3, p. 53) and with the [] notation to create arrays (§3.4, p. 59). It is also used to instantiate anonymous arrays (§3.4, p. 63). Click here to view code image Pizza onePizza = new Pizza(); // Create an instance of the Pizza class. The [] notation is used to declare and construct arrays, and is also to access array elements (§3.4, p. 58). Click here to view code image int[] anArray = new int[5];// Declare and construct an int array of 5 elements. anArray[4] = anArray[3]; // Element at index 4 gets value of element at index 3. The boolean, binary, and infix operator instanceof is used to test the type of an object (§7.11, p. 320). Click here to view code image Pizza myPizza boolean test1 boolean test2 Pizza. boolean test3 instance. = new Pizza(); = myPizza instanceof Pizza; // true. = “Pizza” instanceof Pizza; // Compile error. String is not = null instanceof Pizza; // Always false. null is not an The arrow operator -> is used in the definition of a lambda expression (§10.2, p. 444). Click here to view code image java.util.function.Predicate predicate = str -> str.length() % 2 == 0; boolean test4 = predicate.test(“The lambda strikes back!”); // true. Review Questions 5.20 Which of the following are not operators in Java? Select the two correct answers. (a) % (b) && (c) %= WOW! eBook www.wowebook.org (d) &&= (e) <= (f) %% (g) -> 5.21 Which statements when inserted at (1) will not result in a compile-time error? Click here to view code image public class RQ05A200 { public static void main(String[] args) { int i = 20; int j = 30; // (1) INSERT STATEMENT HERE. } } Select the three correct answers. (a) int result1 = i < j ? i : j * 10D; (b) int result2 = i < j ? { ++i } : { ++j }; (c) Number number = i < j ? i : j * 10D; (d) System.out.println(i < j ? i); (e) System.out.println(i < j ? ++i : ++j); (f) System.out.println(i == j ? i == j : "i not equal to j"); 5.22 Which statements are true about the following code? Click here to view code image public class RQ05A100 { public static void main(String[] args) { int n1 = 10, n2 = 10; int m1 = 20, m2 = 30; int result = n1 != n2? n1 : m1 != m2? m1 : m2; System.out.println(result); } } Select the one correct answer. (a) The program will not compile. (b) When run, the program throws an ArithmeticException at runtime. (c) When run, the program will print 10. (d) When run, the program will print 20. (e) When run, the program will print 30. WOW! eBook www.wowebook.org Chapter Summary The following topics were covered in this chapter: • Type conversion categories and conversion contexts, and which conversions are permissible in each conversion context. • Defining and evaluating arithmetic and boolean expressions, and the order in which operands and operators are evaluated. • Representing integers in different number systems and in memory. • Operators in Java, including precedence and associativity rules. Programming Exercise 5.1 The following program is supposed to calculate and print the time it takes for light to travel from the sun to the earth. It contains some logical errors. Fix the program so that it will compile, compute, and print the correct result when run. Click here to view code image // File: Sunlight.java public class Sunlight { public static void main(String[] args) { // Distance from sun (150 million kilometers) int kmFromSun = 150_000_000; int lightSpeed = 299_792_458; // meters per second // Convert distance to meters. int mFromSun = kmFromSun * 1000; int seconds = mFromSun / lightSpeed; System.out.print(“Light will use “); printTime(seconds); System.out.println(” to travel from the sun to the earth.”); } public static void printTime(int sec) { int min = sec / 60; sec = sec - (min * 60); System.out.print(min + ” minute(s) and ” + sec + ” second(s)”); } } WOW! eBook www.wowebook.org 6. Control Flow 6.1 Overview of Control Flow Statements Control flow statements govern the flow of control in a program during execution, meaning the order in which statements are executed in a running program. There are three main categories of control flow statements: WOW! eBook www.wowebook.org • Selection statements: if, if-else, and switch. • Iteration statements: while, do-while, basic for, and enhanced for. • Transfer statements: break, continue, return, try-catch-finally, throw, and assert. Only the basic form of the try-catch-finally construct is covered here, and the assert facility is not in the scope of this book. 6.2 Selection Statements Java provides selection statements that allow the program to choose between alternative actions during execution. The choice is based on criteria specified in the selection statement. These selection statements are • Simple if statement • if-else statement • switch statement The Simple Statement The simple if statement has the following syntax: if (condition) statement It is used to decide whether an action is to be performed or not, based on a condition. The action to be performed is specified by statement, which can be a single statement or a code block. The condition must evaluate to a boolean or Boolean value. In the latter case, the Boolean value is unboxed to the corresponding boolean value. The semantics of the simple if statement are straightforward. The condition is evaluated first. If its value is true, statement (called the if block) is executed and then execution continues with the rest of the program. If the value is false, the if block is skipped and execution continues with the rest of the program. The semantics are illustrated by the activity diagram in Figure 6.1a. WOW! eBook www.wowebook.org Figure 6.1 Activity Diagram for if Statements In the following examples of the if statement, it is assumed that the variables and the methods have been appropriately defined: Click here to view code image if (emergency) operate(); // emergency is a boolean variable if (temperature > critical) soundAlarm(); if (isLeapYear() && endOfCentury()) celebrate(); if (catIsAway()) { getFishingRod(); goFishing(); } // Block Note that statement can be a block, and the block notation is necessary if more than one statement is to be executed when the condition is true. Since the condition evaluates to a boolean value, it avoids a common programming error: using an expression of the form (a=b) as the condition, where inadvertently an assignment operator is used instead of a relational operator. The compiler will flag this as an error, unless both a and b are boolean. Note that the if block can be any valid statement. In particular, it can be the empty statement (;) or the empty block ({}). A common programming error is inadvertent use of the empty statement. Click here to view code image if (emergency); // Empty if block operate(); // Executed regardless of whether it was an emergency The Statement The if-else statement is used to decide between two actions, based on a condition. It has the following syntax: WOW! eBook www.wowebook.org if (condition) statement1 else statement2 The condition is evaluated first. If its value is true (or unboxed to true), statement1 (the if block) is executed and then execution continues with the rest of the program. If the value is false (or unboxed to false), statement2 (the else block) is executed and then execution continues with the rest of the program. In other words, one of two mutually exclusive actions is performed. The else clause is optional; if omitted, the construct is equivalent to the simple if statement. The semantics are illustrated by the activity diagram in Figure 6.1b. In the following examples of the if-else statement, it is assumed that all variables and methods have been appropriately defined: if (emergency) operate(); else joinQueue(); if (temperature > critical) soundAlarm(); else businessAsUsual(); if (catIsAway()) { getFishingRod(); goFishing(); } else playWithCat(); Since actions can be arbitrary statements, the if statements can be nested. Click here to view code image if (temperature >= upperLimit) { if (danger) soundAlarm(); if (critical) evacuate(); else turnHeaterOff(); } else turnHeaterOn(); // (1) // (2) Simple if. // (3) // Goes with if at (3). // Goes with if at (1). The use of the block notation, {}, can be critical to the execution of if statements. The if statements (A) and (B) in the following examples do not have the same meaning. The if statements (B) and (C) are the same, with extra indentation used in (C) to make the meaning evident. Leaving out the block notation in this case could have catastrophic consequences: The heater could be turned on when the temperature is above the upper limit. Click here to view code image // (A): if (temperature > upperLimit) { if (danger) soundAlarm(); // (1) Block notation. // (2) WOW! eBook www.wowebook.org } else turnHeaterOn(); // (B): if (temperature > upperLimit) if (danger) soundAlarm(); else turnHeaterOn(); // (C): if (temperature > upperLimit) if (danger) soundAlarm(); else turnHeaterOn(); // Goes with if at (1). // (1) Without block notation. // (2) // Goes with if at (2). // (1) // (2) // Goes with if at (2). The rule for matching an else clause is that an else clause always refers to the nearest if that is not already associated with another else clause. Block notation and proper indentation can be used to make the meaning obvious. Cascading if-else statements comprise a sequence of nested if-else statements where the if block of the next if-else statement is joined to the else clause of the previous one. The decision to execute a block is then based on all the conditions evaluated so far. Click here to view code image if (temperature >= upperLimit) { soundAlarm(); turnHeaterOff(); } else if (temperature < lowerLimit) { soundAlarm(); turnHeaterOn(); } else if (temperature == (upperLimit-lowerLimit)/2) { doingFine(); } else noCauseToWorry(); // (1) // (2) // (3) // (4) The block corresponding to the first if condition that evaluates to true is executed, and the remaining if statements are skipped. In the preceding example, the block at (3) will execute only if the conditions at (1) and (2) are false and the condition at (3) is true. If none of the conditions is true, the block associated with the last else clause is executed. If there is no last else clause, no actions are performed. The Statement Conceptually, the switch statement can be used to choose one among many alternative actions, based on the value of an expression. Its general form is as follows: switch (switch_expression) { case label1: statement1 case label2: statement2 … case labeln: statementn default: statement } // end switch The syntax of the switch statement comprises a switch expression followed by the WOW! eBook www.wowebook.org switch body, which is a block of statements. The switch expression must evaluate to a value of the following types: • One of the following primitive data types: char, byte, short, or int • One of the following wrapper types: Character, Byte, Short, or Integer • String type • An enumerated type Note that the type of the switch expression cannot be boolean, long, or floatingpoint. The statements in the switch body can be labeled, thereby defining entry points in the switch body where control can be transferred depending on the value of the switch expression. The execution of the switch statement is as follows: • The switch expression is evaluated first. If the value is a wrapper type, an unboxing conversion is performed. • The value of the switch expression is compared with the case labels. Control is transferred to the statement associated with the case label that is equal to the value of the switch expression. After execution of the associated statement, control falls through to the next statement unless this was the last statement declared or control was transferred out of the switch statement. • If no case label is equal to the value of the switch expression, the statement associated with the default label is executed. After execution of the associated statement, control falls through to the next statement unless this was the last statement declared or control was transferred out of the switch statement. Figure 6.2 illustrates the flow of control through a switch statement where the default label is declared last. Figure 6.2 Activity Diagram for a switch Statement All labels (including the default label) are optional, and can be defined in any order in WOW! eBook www.wowebook.org the switch body. At most one default label can be present in a switch statement. If no valid case labels are found and the default label is omitted, the whole switch statement is skipped. The case labels are constant expressions whose values must be unique, meaning no duplicate values are allowed. In fact, a case label must be a compile-time constant expression whose value is assignable to the type of the switch expression (§5.2, p. 147). In particular, all case label values must be in the range of the type of the switch expression. The type of the case label cannot be boolean, long, or floating-point. The compiler is able to generate efficient code for a switch statement, as this statement only tests for equality between the switch expression and the constant value of the case labels, so as to determine which code to execute at runtime. In contrast, a sequence of if statements determines the flow of control at runtime, based on arbitrary conditions whose value might be possible to determine only at runtime. In Example 6.1, depending on the value of the howMuchAdvice parameter, different advice is printed in the switch statement at (1) in the method dispenseAdvice(). The example shows the output when the value of the howMuchAdvice parameter is LOTS_OF_ADVICE. In the switch statement, the associated statement at (2) is executed, giving one piece of advice. Control then falls through to the statement at (3), giving the second advice. Control next falls through to (4), dispensing the third piece of advice, and finally execution of the break statement at (5) causes control to exit the switch statement. Without the break statement at (5), control would continue to fall through the remaining statements—in this case, to the statement at (6) being executed. Execution of the break statement in a switch body transfers control out of the switch statement (§6.4, p. 221). If the parameter howMuchAdvice has the value MORE_ADVICE, then the advice at both (3) and (4) is given. The value LITTLE_ADVICE results in only one piece of advice at (4) being given. Any other value results in the default action, which announces that there is no advice. The associated statement of a case label can be a list of statements (which need not be a statement block). The case label is prefixed to the first statement in each case. This is illustrated by the associated statement for the case label LITTLE_ADVICE in Example 6.1, which comprises statements (4) and (5). WOW! eBook www.wowebook.org Example 6.1 Fall-Through in a switch Statement Click here to view code image public class Advice { private static final int LITTLE_ADVICE = 0; private static final int MORE_ADVICE = 1; private static final int LOTS_OF_ADVICE = 2; public static void main(String[] args) { dispenseAdvice(LOTS_OF_ADVICE); } public static void dispenseAdvice(int howMuchAdvice) { switch (howMuchAdvice) { // (1) case LOTS_OF_ADVICE: System.out.println(“See no evil.”); // (2) case MORE_ADVICE: System.out.println(“Speak no evil.”); // (3) case LITTLE_ADVICE: System.out.println(“Hear no evil.”); // (4) break; // (5) default: System.out.println(“No advice.”); // (6) } } } Output from the program: See no evil. Speak no evil. Hear no evil. Example 6.2 makes use of a break statement inside a switch statement to convert a char value representing a digit to the corresponding word in English. Note that the break statement is the last statement in the list of statements associated with each case label. It is easy to think that the break statement is a part of the switch statement syntax, but technically it is not. WOW! eBook www.wowebook.org Example 6.2 Using break in a switch Statement Click here to view code image public class Digits { public static void main(String[] args) { System.out.println(digitToString(‘7’) + ” ” + digitToString(‘8’) + ” ” + digitToString(‘6’)); System.out.println(digitToString(‘2’) + ” ” + digitToString(‘a’) + ” ” + digitToString(‘5’)); } public static String digitToString(char digit) { String str = ””; switch(digit) { case ‘1’: str = “one”; break; case ‘2’: str = “two”; break; case ‘3’: str = “three”; break; case ‘4’: str = “four”; break; case ‘5’: str = “five”; break; case ‘6’: str = “six”; break; case ‘7’: str = “seven”; break; case ‘8’: str = “eight”; break; case ‘9’: str = “nine”; break; case ‘0’: str = “zero”; break; default: System.out.println(digit + ” is not a digit!”); } return str; } } Output from the program: seven eight six a is not a digit! two five Several case labels can prefix the same statement. They will all result in the associated statement being executed. This behavior is illustrated in Example 6.3 for the switch statement at (1). The first statement in the switch body must have a case or default label, or otherwise it will be unreachable. This statement will never be executed, because control can never be transferred to it. The compiler will flag this as an error. An empty switch block is perfectly legal, but not of much use. Since each action associated with a case label can be an arbitrary statement, it can be another switch statement. In other words, switch statements can be nested. Since a switch statement defines its own local block, the case labels in an inner block do not conflict with any case labels in an outer block. Labels can be redefined in nested blocks; in contrast, variables cannot be redeclared in nested blocks (§4.4, p. 117). In Example 6.3, an inner switch statement is defined at (2), which allows further refinement of the action to take on the value of the switch expression in cases where multiple labels are used in the outer switch statement. A break statement terminates the innermost switch statement in which it is executed. WOW! eBook www.wowebook.org Example 6.3 Nested switch Statement Click here to view code image public class Seasons { public static void main(String[] args) { int monthNumber = 11; switch(monthNumber) { // (1) Outer case 12: case 1: case 2: System.out.println(“Snow in the winter.”); break; case 3: case 4: case 5: System.out.println(“Green grass in the spring.”); break; case 6: case 7: case 8: System.out.println(“Sunshine in the summer.”); break; case 9: case 10: case 11: // (2) switch(monthNumber) { // Nested switch (3) Inner case 10: System.out.println(“Halloween.”); break; case 11: System.out.println(“Thanksgiving.”); break; } // End nested switch // Always printed for case labels 9, 10, 11 System.out.println(“Yellow leaves in the fall.”); // (4) break; default: System.out.println(monthNumber + ” is not a valid month.”); } } } Output from the program: Thanksgiving. Yellow leaves in the fall. Example 6.4 illustrates using strings in a switch statement. The thing to note is what constitutes a constant string expression that can be used as a case label. The case labels in (3), (4), (5), and (6) are all valid constant string expressions, as the compiler can figure out their values at compile time. String literals, used in (3) and (6), and constant field values, declared in (1) and (2a), and used in (4) and (5), are all valid case labels. In contrast, the HOT reference from declarations (2b) and (2c) cannot be used as a case label. From the declaration in (2a), the compiler cannot guarantee that the value of the reference will not change at runtime. From the declaration in (2c), it cannot deduce the value at compile time, as the constructor must be run to construct the value. Switching on strings is essentially based on equality comparison of integer values that are hash values of strings, followed by an object equality test to rule out the possibility of collision between two different strings having the same hash value. Switching on strings should be used judiciously, as it is less efficient than switching on integers. Switching on strings is not advisable if the values being switched on are not already strings. WOW! eBook www.wowebook.org Example 6.4 Strings in switch Statement Click here to view code image public class SwitchingOnAString { public static final String MEDIUM = “Medium”; // public static final String HOT = “Hot”; // //public static String HOT = “Hot”; // lablel //public static final String HOT = new String(“Hot”);// lablel public static void main(String[] args) { String spiceLevel = “Medium_Hot”; switch (spiceLevel) { case “Mild”: case MEDIUM + “_” + HOT: System.out.println(“Enjoy your meal!”); break; case HOT: System.out.println(“Have fun!”); break; case “Suicide”: System.out.println(“Good luck!”); break; default: System.out.println(“You being funny?”); } } (1) (2a) (2b) Not OK as case (2c) Not OK as case // (3) // (4) // (5) // (6) } Output from the program: Enjoy your meal! Example 6.5 illustrates the use of enum types in a switch statement. The enum type SpiceGrade is defined at (1). The type of the switch expression is SpiceGrade. Note that the enum constants are not specified with their fully qualified name (see (2a)). Using the fully qualified name results in a compile-time error, as shown at (2b). Only enum constants that have the same enum type as the switch expression can be specified as case label values. The semantics of the switch statement are the same as described earlier. However, if a switch expression evaluates to the null reference, a NullPointerException will be thrown. Switching on enumerated values is essentially based on equality comparison of unique integer values that are ordinal values assigned by the compiler to the constants of an enum type. WOW! eBook www.wowebook.org Example 6.5 Enums in switch Statement Click here to view code image enum SpiceGrade { MILD, MEDIUM_HOT, HOT, SUICIDE; } // (1) public class SwitchingFun { public static void main(String[] args) { SpiceGrade spicing = SpiceGrade.HOT; switch (spicing) { case HOT: // (2a) OK! // case SpiceGrade.HOT: // (2b) Compile-time error! System.out.println(“Have fun!”); break; case SUICIDE: System.out.println(“Good luck!”); break; default: // Can only be MILD or MEDIUM_HOT. System.out.println(“Enjoy you meal!”); } } } Output from the program: Have fun! Review Questions 6.1 What will be the result of attempting to compile and run the following class? Click here to view code image public class IfTest { public static void main(String[] args) { if (true) if (false) System.out.println(“a”); else System.out.println(“b”); } } Select the one correct answer. (a) The code will fail to compile because the syntax of the if statement is incorrect. (b) The code will fail to compile because the compiler will not be able to determine which if statement the else clause belongs to. (c) The code will compile correctly, and display the letter a at runtime. (d) The code will compile correctly, and display the letter b at runtime. (e) The code will compile correctly, but will not display any output. WOW! eBook www.wowebook.org 6.2 Which of the following statements are true? Select the three correct answers. (a) The condition in an if statement can have method calls. (b) If a and b are of type boolean or Boolean, the expression (a = b) can be the condition of an if statement. (c) An if statement can have either an if clause or an else clause. (d) The statement if (false) ; else ; is illegal. (e) Only expressions that evaluate or can be unboxed to a boolean value can be used as the condition in an if statement. 6.3 What, if anything, is wrong with the following code? void test(int x) { switch (x) { case 1: case 2: case 0: default: case 4: } } Select the one correct answer. (a) The variable x does not have the right type for a switch expression. (b) The case label 0 must precede the case label 1. (c) Each case section must end with a break statement. (d) The default label must be the last label in the switch statement. (e) The body of the switch statement must contain at least one statement. (f) There is nothing wrong with the code. 6.4 What will be the result of attempting to compile and run the following program? Click here to view code image public class Switching { public static void main(String[] args) { final int iLoc = 3; switch (6) { case 1: case iLoc: case 2 * iLoc: System.out.println(“I am not OK.”); default: System.out.println(“You are OK.”); case 4: System.out.println(“It’s OK.”); } } } WOW! eBook www.wowebook.org Select the one correct answer. (a) The code will fail to compile because of the case label value 2 * iLoc. (b) The code will fail to compile because the default label is not specified last in the switch statement. (c) The code will compile correctly and will print the following at runtime: I am not OK. You are OK. It’s OK. (d) The code will compile correctly and will print the following at runtime: You are OK. It’s OK. (e) The code will compile correctly and will print the following at runtime: It’s OK. 6.5 What will be the result of attempting to compile and run the following program? Click here to view code image public class MoreSwitching { public static void main(String[] args) { final int iLoc = 3; Integer iRef = 5; switch (iRef) { default: System.out.println(“You are OK.”); case 1: case iLoc: case 2 * iLoc: System.out.println(“I am not OK.”); break; case 4: System.out.println(“It’s OK.”); } } } Select the one correct answer. (a) The code will fail to compile because the type of the switch expression is not valid. (b) The code will compile correctly and will print the following at runtime: You are OK. I am not OK. (c) The code will compile correctly and will print the following at runtime: You are OK. I am not OK. It’s OK. (d) The code will compile correctly and will print the following at runtime: It’s OK. 6.6 Which case label declaration can be inserted at (1) so that the following program WOW! eBook www.wowebook.org will compile, run, and print Hi, TomTom!? Click here to view code image public class Switcheroo { public static void main(String[] args) { final String TOM1 = “Tom”; String TOM2 = “Tom”; final String TOM3 = new String(“Tom”); switch (“TomTom”) { default: System.out.println(“Whatever!”); break; // (1) INSERT case LABEL DECLARATION HERE. System.out.println(“Hi, TomTom!”); } } } Select the four correct answers. (a) case "TomTom": (b) case TOM1 + TOM1: (c) case TOM1 + TOM2: (d) case TOM1 + TOM3: (e) case TOM2 + TOM3: (f) case "Tom" + TOM1: (g) case "Tom" + TOM2: (h) case "Tom" + TOM3: (i) case 'T' + 'o' + 'm' + TOM1: (j) case "T" + 'o' + 'm' + TOM1: 6.3 Iteration Statements Loops allow a block of statements to be executed repeatedly (that is, iterated). A boolean condition (called the loop condition) is commonly used to determine when to terminate the loop. The statements executed in the loop constitute the loop body. The loop body can be a single statement or a block. Java provides three language constructs for loop construction: • The while statement • The do-while statement • The basic for statement These loops differ in the order in which they execute the loop body and test the loop condition. The while loop and the basic for loop test the loop condition before executing the loop body, whereas the do-while loop tests the loop condition after WOW! eBook www.wowebook.org execution of the loop body. In addition to the basic for loop, a specialized loop called the enhanced for loop (also called the for-each loop) simplifies iterating over arrays and collections. We will use the notations for(;;) and for(:) to designate the basic for loop and the enhanced for loop, respectively. The Statement The syntax of the while loop is while (loop_condition) loop_body The loop condition is evaluated before executing the loop body. The while statement executes the loop body as long as the loop condition is true. When the loop condition becomes false, the loop is terminated and execution continues with the statement immediately following the loop. If the loop condition is false to begin with, the loop body is not executed at all. In other words, a while loop can execute zero or more times. The loop condition must evaluate to a boolean or a Boolean value. In the latter case, the reference value is unboxed to a boolean value. The flow of control in a while statement is shown in Figure 6.3. Figure 6.3 Activity Diagram for the while Statement The while statement is normally used when the number of iterations is not known. while (noSignOfLife()) keepLooking(); Since the loop body can be any valid statement, inadvertently terminating each line with the empty statement (;) can give unintended results. Always using a block statement as the loop body helps to avoid such problems. Click here to view code image while (noSignOfLife()); keepLooking(); The // Empty statement as loop body! // Statement not in the loop body. Statement The syntax of the do-while loop is do loop_body while (loop_condition); In a do-while statement, the loop condition is evaluated after executing the loop body. WOW! eBook www.wowebook.org The loop condition must evaluate to a boolean or Boolean value. The value of the loop condition is subjected to unboxing if it is of the type Boolean. The do-while statement executes the loop body until the loop condition becomes false. When the loop condition becomes false, the loop is terminated and execution continues with the statement immediately following the loop. Note that the loop body is executed at least once. Figure 6.4 illustrates the flow of control in a do-while statement. Figure 6.4 Activity Diagram for the do-while Statement The loop body in a do-while loop is invariably a statement block. It is instructive to compare the while and do-while loops. In the examples that follow, the mice might never get to play if the cat is not away, as in the loop at (1). The mice do get to play at least once (at the peril of losing their life) in the loop at (2). Click here to view code image while (cat.isAway()) { mice.play(); } // (1) do { mice.play(); } while (cat.isAway()); // (2) The Statement The for(;;) loop is the most general of all the loops. It is mostly used for countercontrolled loops, in which the number of iterations is known beforehand. The syntax of the loop is as follows: Click here to view code image for (initialization; loop_condition; update_expression) loop_body The initialization usually declares and initializes a loop variable that controls the execution of the loop body. The loop condition must evaluate to a boolean or Boolean value. In the latter case, the reference value is converted to a boolean value by unboxing. The loop condition usually involves the loop variable, and if the loop condition is true, the loop body is executed; otherwise, execution continues with the statement following the for(;;) loop. After each iteration (that is, execution of the loop body), the update expression is executed. This usually modifies the value of the loop variable to ensure eventual loop termination. The loop condition is then tested to determine whether the loop body should be executed again. Note that the initialization is executed only once, WOW! eBook www.wowebook.org on entry into the loop. The semantics of the for(;;) loop are illustrated in Figure 6.5, and are summarized by the following equivalent while loop code template: initialization while (loop_condition) { loop_body update_expression } Figure 6.5 Activity Diagram for the for Statement The following code creates an int array and sums the values in the array: Click here to view code image int sum = 0; int[] array = {12, 23, 5, 7, 19}; for (int index = 0; index < array.length; index++) sum += array[index]; // (1) The loop variable index is declared and initialized in the initialization section of the loop. It is incremented in the update expression section. This loop is an example of a forward for(;;) loop, where the loop variable is incremented. The next code snippet is an example of a backward for(;;) loop, where the loop variable is decremented to sum the values in the array: Click here to view code image int sum = 0; int[] array = {12, 23, 5, 7, 19}; for (int index = array.length - 1; index >= 0; index—) sum += array[index]; It is instructive to compare the specification of the loop header in the forward and backward for(;;) loops in these examples. The for(;;) loop defines a local block so that the scope of this declaration is the for(;;) block, which comprises the initialization, the loop condition, the loop body, and the update expression sections. Any variable declared in the for(;;) block, therefore, is not accessible after the for(;;) loop terminates. The loop at (1) earlier showed how a declaration statement can be specified in the initialization section. Such a WOW! eBook www.wowebook.org declaration statement can also specify a comma-separated list of variables: Click here to view code image for (int i = 0, j = 1, k = 2; … ; …) …; // (2) The variables i, j, and k in the declaration statement all have type int. All variables declared in the initialization section are local variables in the for(;;) block and obey the scope rules for local blocks. The following code will not compile, however, as variable declarations of different types (in this case, int and String) require declaration statements that are terminated by semicolons: Click here to view code image for (int i = 0, String str = “@”; … ; …) …; // (3) Compile-time error The initialization section can also be a comma-separated list of expression statements (§3.2, p. 50). Any value returned by an expression statement is discarded. For example, the loop at (2) can be rewritten by factoring out the variable declaration: Click here to view code image int i, j, k; // Variable declaration for (i = 0, j = 1, k = 2; … ; …) …; // (4) Only initialization The initialization section is now a comma-separated list of three expressions. The expressions in such a list are always evaluated from left to right, and their values are discarded. Note that the variables i, j, and k at (4) are not local to the loop. Declaration statements cannot be mixed with expression statements in the initialization section, as is the case at (5) in the following example. Factoring out the variable declaration, as at (6), leaves a legal comma-separated list of expression statements. Click here to view code image // (5) Not legal and ugly: for (int i = 0, System.out.println(“This won’t do!”); flag; i++) { // Error! // loop body } // (6) Legal, but still ugly: int i; // Declaration factored out. for (i = 0, System.out.println(“This is legal!”); flag; i++) { // OK. // loop body } The update expression can also be a comma-separated list of expression statements. The following code specifies a for(;;) loop that has a comma-separated list of three variables in the initialization section, and a comma-separated list of two expressions in the update expression section: Click here to view code image // Legal usage but not recommended. int[][] sqMatrix = { {3, 4, 6}, {5, 7, 4}, {5, 8, 9} }; for (int i = 0, j = sqMatrix[0].length - 1, asymDiagonal = 0; // initialization i < sqMatrix.length; // loop condition i++, j—) // update expression asymDiagonal += sqMatrix[i][j]; // loop body WOW! eBook www.wowebook.org All sections in the for(;;) header are optional. Any or all of them can be left empty, but the two semicolons are mandatory. In particular, leaving out the loop condition signifies that the loop condition is true. The “crab”, (;;), can be used to construct an infinite loop, where termination is presumably achieved through code in the loop body (see the next section on transfer statements): Click here to view code image for (;;) doProgramming(); The // Infinite loop Statement The enhanced for loop is convenient when we need to iterate over an array or a collection, especially when some operation needs to be performed on each element of the array or collection. In this section we discuss iterating over arrays; in §10.1, p. 423, we take a look at the for(:) loop for iterating over ArrayLists. Earlier in this chapter we used a for(;;) loop to sum the values of elements in an int array: Click here to view code image int sum = 0; int[] intArray = {12, 23, 5, 7, 19}; for (int index = 0; index < intArray.length; index++) { // (1) using for(;;) loop sum += intArray[index]; } The for(;;) loop at (1) is rewritten using the for(:) loop in Figure 6.6. Figure 6.6 Enhanced for Statement The body of the loop is executed for each element in the array, where the variable element successively denotes the current element in the array intArray. When the loop terminates, the variable sum will contain the sum of all elements in the array. We do not care about the position of the elements in the array, just that the loop iterates over all elements of the array. From Figure 6.6 we see that the for(:) loop header has two parts. The expression must evaluate to a reference value that refers to an array—that is, the array we want to iterate over. The array can be an array of primitive values or objects, or even an array of arrays. The expression is evaluated only once. The element declaration specifies a local variable that can be assigned a value of the element type of the array. The type of the array intArray in Figure 6.6 is int[], and the element type is int. The element variable of type int can be assigned int values from the array of int. However, this assignment might require either a boxing or an unboxing conversion, with optional widening WOW! eBook www.wowebook.org conversion. The element variable is local to the loop block and is not accessible after the loop terminates. Also, changing the value of the current variable does not change any value in the array. The loop body, which can be a simple statement or a statement block, is executed for each element in the array and there is no danger of any out-of-bounds errors. The for(:) loop has its limitations. Specifically, we cannot change element values, and this kind of loop does not provide any provision for positional access using an index. The for(:) loop only increments by one and always in a forward direction. It does not allow iterations over several arrays simultaneously. Under such circumstances, the for(;;) loop can be more convenient. Here are some code examples of legal for(:) loops: Click here to view code image // Some 1-dim arrays: int[] intArray = {10, 20, 30}; Integer[] intObjArray = {10, 20, 30}; String[] strArray = {“one”, “two”}; // Some 2-dim arrays: Object[][] objArrayOfArrays = {intObjArray, strArray}; Number[][] numArrayOfArrays = {{1.5, 2.5}, intObjArray, {100L, 200L}}; int[][] intArrayOfArrays = {{20}, intArray, {40}}; // Iterate over an array of Strings. // Expression type is String[], and element type is String. // String is assignable to Object (widening conversion). for (Object obj : strArray) {} // Iterate over an array of ints. // Expression type is int[], and element type is int. // int is assignable to Integer (boxing conversion) for (Integer iRef : intArrayOfArrays[0]){} // Iterate over an array of Integers. // Expression type is Integer[], and element type is Integer. // Integer is assignable to int (unboxing conversion) for (int i : intObjArray){} // Iterate over a 2-dim array of ints. // Outer loop: expression type is int[][], and element type is int[]. // Inner loop: expression type is int[], and element type is int. for (int[] row : intArrayOfArrays) for (int val : row) {} // Iterate over a 2-dim array of Numbers. // Outer loop: expression type is Number[][], and element type is Number[]. // Outer loop: Number[] is assignable to Object[] (widening conversion). // Inner loop: expression type is Object[], and element type is Object. for (Object[] row : numArrayOfArrays) for (Object obj : row) {} // Outer loop: expression type is Integer[][], and element type is Integer[]. // Outer loop: Integer[] is assignable to Number[]. // Inner loop: expression type is int[], and element type is int. // Inner loop: int is assignable to double. for (Number[] row : new Integer[][] {intObjArray, intObjArray, intObjArray}) WOW! eBook www.wowebook.org for (double num : new int[] {}) {} Here are some code examples of for(:) loops that are not legal: Click here to view code image // Expression type is Number[][], and element type is Number[]. // Number[] is not assignable to Number. for (Number num : numArrayOfArrays) {} // Compile-time error. // Expression type is Number[], and element type is Number. // Number is not assignable to int. for (int row : numArrayOfArrays[0]) {} // Compile-time error. // Outer loop: expression type is int[][], and element type is int[]. // int[] is not assignable to Integer[]. for (Integer[] row : intArrayOfArrays) // Compile-time error. for (int val : row) {} // Expression type is Object[][], and element type is Object[]. // Object[] is not assignable to Integer[]. for (Integer[] row : objArrayOfArrays) {} // Compile-time error. // Outer loop: expression type is String[], and element type is String. // Inner loop: expression type is String, which is not legal here. Not an array. for (String str : strArray) for (char val : str) {} // Compile-time error. When using the for(:) loop to iterate over an array, the two main causes of errors are an expression in the loop header that does not represent an array and/or an element type of the array that is not assignable to the local variable declared in the loop header. 6.4 Transfer Statements Java provides six language constructs for transferring control in a program: • break • continue • return • try-catch-finally • throw • assert This section discusses the first three statements. Except for the assert statement (not on the OCAJP8 exam), the remaining statements are discussed in subsequent sections. Note that Java does not have a goto statement, although goto is a reserved word. Labeled Statements A statement may have a label: label : statement WOW! eBook www.wowebook.org A label is any valid identifier; it always immediately precedes the statement. Label names exist in their own namespace, so that they do not conflict with names of packages, classes, interfaces, methods, fields, and local variables. The scope of a label is the statement prefixed by the label, meaning that it cannot be redeclared as a label inside the labeled statement—analogous to the scope of local variables. Click here to view code image L1: if (i > 0) { L1: System.out.println(i); } // (1) Not OK. Label redeclared. L1: while (i < 0) { L2: System.out.println(i); } // (2) OK. L1: { int j = 10; System.out.println(j); } // (3) OK. Labeled block. L1: try { // (4) OK. Labeled try-catch-finally block. int j = 10, k = 0; L2: System.out.println(j/k); } catch (ArithmeticException ae) { L3: ae.printStackTrace(); } finally { L4: System.out.println(“Finally done.”); } A statement can have multiple labels: Click here to view code image LabelA: LabelB: System.out.println(“Mutliple labels. Use judiciously.”); A declaration statement cannot have a label: Click here to view code image L0: int i = 0; // Compile-time error! A labeled statement is executed as if it was unlabeled, unless it is the break or continue statement. This behavior is discussed in the next two subsections. The Statement The break statement comes in two forms: unlabeled and labeled. Click here to view code image break; break label; // the unlabeled form // the labeled form The unlabeled break statement terminates loops (for(;;), for(:), while, dowhile) and switch statements, and transfers control out of the current context (i.e., the closest enclosing block). The rest of the statement body is skipped, and execution continues after the enclosing statement. In Example 6.6, the break statement at (1) is used to terminate a for(;;) loop. Control is transferred to (2) when the value of i is equal to 4 at (1), skipping the rest of the loop WOW! eBook www.wowebook.org body and terminating the loop. Example 6.6 also shows that the unlabeled break statement terminates only the innermost loop or switch statement that contains the break statement. The break statement at (3) terminates the inner for(;;) loop when j is equal to 2, and execution continues in the outer switch statement at (4) after the for(;;) loop. Example 6.6 The break Statement Click here to view code image class BreakOut { public static void main(String[] args) { System.out.println(“i sqrt(i)”); for (int i = 1; i <= 5; ++i) { if (i == 4) break; // (1) Terminate loop. Control to (2). // Rest of loop body skipped when i gets the value 4. System.out.printf(“%d %.2f%n”, i, Math.sqrt(i)); } // end for // (2) Continue here. int n = 2; switch (n) { case 1: System.out.println(n); break; case 2: System.out.println(“Inner for(;;) loop: “); for (int j = 0; j <= n; j++) { if (j == 2) break; // (3) Terminate loop. Control to (4). System.out.println(“j = ” + j); } default: System.out.println(“default: n = ” + n); // (4) Continue here. } } } Output from the program: i sqrt(i) 1 1.00 2 1.41 3 1.73 Inner for(;;) loop: j = 0 j = 1 default: n = 2 A labeled break statement can be used to terminate any labeled statement that contains the break statement. Control is then transferred to the statement following the enclosing labeled statement. In the case of a labeled block, the rest of the block is skipped and execution continues with the statement following the block: Click here to view code image WOW! eBook www.wowebook.org out: { // … if (j == 10) break out; System.out.println(j); // … } // (3) Continue here. // Label. // (1) Labeled block. // (2) Terminate block. Control to (3). // Rest of the block not executed if j == 10. In Example 6.7, the program continues to add the elements below the diagonal of a square matrix until the sum is greater than 10. Two nested for loops are defined at (1) and (2). The outer loop is labeled outer at (1). The unlabeled break statement at (3) transfers control to (5) when it is executed; that is, it terminates the inner loop and control is transferred to the statement after the inner loop. The labeled break statement at (4) transfers control to (6) when it is executed; that is, it terminates both the inner and the outer loop, transferring control to the statement after the loop labeled outer. Example 6.7 Labeled break Statement Click here to view code image class LabeledBreakOut { public static void main(String[] args) { int[][] squareMatrix = {{4, 3, 5}, {2, 1, 6}, {9, 7, 8}}; int sum = 0; outer: for (int i = 0; i < squareMatrix.length; ++i){ // (1) label for (int j = 0; j < squareMatrix[i].length; ++j) { // (2) if (j == i) break; // (3) Terminate inner loop. Control to (5). System.out.println(“Element[” + i + “, ” + j + “]: ” + squareMatrix[i][j]); sum += squareMatrix[i][j]; if (sum > 10) break outer; // (4) Terminate both loops. Control to (6). } // end inner loop // (5) Continue with update expression in the outer loop header. } // end outer loop // (6) Continue here. System.out.println(“sum: ” + sum); } } Output from the program: Element[1, 0]: 2 Element[2, 0]: 9 sum: 11 The Statement Like the break statement, the continue statement comes in two forms: unlabeled and labeled. Click here to view code image continue; continue label; // the unlabeled form // the labeled form The continue statement can be used only in a for(;;), for(:), while, or doWOW! eBook www.wowebook.org while loop to prematurely stop the current iteration of the loop body and proceed with the next iteration, if possible. In the case of the while and do-while loops, the rest of the loop body is skipped—that is, the current iteration is stopped, with execution continuing with the loop condition. In the case of the for(;;) loop, the rest of the loop body is skipped, with execution continuing with the update expression. In Example 6.8, an unlabeled continue statement is used to skip an iteration in a for(;;) loop. Control is transferred to (2) when the value of i is equal to 4 at (1), skipping the rest of the loop body and continuing with the update expression in the for(;;) statement. Example 6.8 continue Statement Click here to view code image class Skip { public static void main(String[] args) { System.out.println(“i sqrt(i)”); for (int i = 1; i <= 5; ++i) { if (i == 4) continue; // (1) Control to (2). // Rest of loop body skipped when i has the value 4. System.out.printf(“%d %.2f%n”, i, Math.sqrt(i)); // (2) Continue with update expression in the loop header. } // end for } } Output from the program: i 1 2 3 5 sqrt(i) 1.00 1.41 1.73 2.24 A labeled continue statement must occur within a labeled loop that has the same label. Execution of the labeled continue statement then transfers control to the end of that enclosing labeled loop. In Example 6.9, the unlabeled continue statement at (3) transfers control to (5) when it is executed; that is, the rest of the loop body is skipped and execution continues with the update expression in the inner loop. The labeled continue statement at (4) transfers control to (6) when it is executed; that is, it terminates the inner loop but execution continues with the update expression in the loop labeled outer. It is instructive to compare the output from Example 6.7 (labeled break) and that from Example 6.9 (labeled continue). WOW! eBook www.wowebook.org Example 6.9 Labeled continue Statement Click here to view code image class LabeledSkip { public static void main(String[] args) { int[][] squareMatrix = {{4, 3, 5}, {2, 1, 6}, {9, 7, 8}}; int sum = 0; outer: for (int i = 0; i < squareMatrix.length; ++i){ // (1) label for (int j = 0; j < squareMatrix[i].length; ++j) { // (2) if (j == i) continue; // (3) Control to (5). System.out.println(“Element[” + i + “, ” + j + “]: ” + squareMatrix[i][j]); sum += squareMatrix[i][j]; if (sum > 10) continue outer; // (4) Control to (6). // (5) Continue with update expression in the inner loop header. } // end inner loop // (6) Continue with update expression in the outer loop header. } // end outer loop System.out.println(“sum: ” + sum); } } Output from the program: Element[0, Element[0, Element[1, Element[1, Element[2, sum: 25 The 1]: 2]: 0]: 2]: 0]: 3 5 2 6 9 Statement The return statement is used to stop execution of a method (or a constructor) and transfer control back to the calling code (also called the caller or invoker). The usage of the two forms of the return statement is dictated by whether that statement is used in a void or a non-void method (Table 6.1). The first form does not return any value to the calling code, but the second form does. Note that the keyword void does not represent any type. Table 6.1 The return Statement In Table 6.1, the expression must evaluate to a primitive value or a reference value, and its type must be assignable to the return type specified in the method header (§5.6, p. 158, and §7.9, p. 312). See also the discussion on covariant return in connection with method overriding in §7.2, p. 268. WOW! eBook www.wowebook.org As can be seen from Table 6.1, a void method need not have a return statement—in which case the control typically returns to the caller after the last statement in the method’s body has been executed. However, a void method can specify only the first form of the return statement. This form of the return statement can also be used in constructors, as they likewise do not return a value. Table 6.1 also shows that the first form of the return statement is not allowed in a nonvoid method. The second form of the return statement is mandatory in a non-void method, if the method execution is not terminated programmatically—for example, by throwing an exception. Example 6.10 illustrates the use of the return statement summarized in Table 6.1. A recommended best practice is to document the value returned by a method in a Javadoc comment using the @return tag. Example 6.10 The return Statement Click here to view code image public class ReturnDemo { public static void main (String[] args) { // (1) void method can use return. if (args.length == 0) return; output(checkValue(args.length)); } static void output(int value) { System.out.println(value); return ‘a’; } static int checkValue(int i) { statement // (2) void method need not use return. // Not OK. Cannot return a value. // (3) Non-void method: Any return // if (i > 3) return i; else return 2.0; must return a value. // OK. // Not OK. double not assignable to int. } static int absentMinded() { throw new RuntimeException(); // (4) Non-void method. // OK: No return statement provided, but // method terminates by throwing an exception. } } Review Questions 6.7 What will be the result of attempting to compile and run the following code? Click here to view code image class MyClass { public static void main(String[] args) { boolean b = false; int i = 1; do { WOW! eBook www.wowebook.org i++; b = ! b; } while (b); System.out.println(i); } } Select the one correct answer. (a) The code will fail to compile because b is an invalid condition for the dowhile statement. (b) The code will fail to compile because the assignment b = ! b is not allowed. (c) The code will compile without error, and will print 1 at runtime. (d) The code will compile without error, and will print 2 at runtime. (e) The code will compile without error, and will print 3 at runtime. 6.8 What will be the output when running the following program? Click here to view code image public class StillMyClass { public static void main(String[] args) { int i = 0; int j; for (j = 0; j < 10; ++j) { i++; } System.out.println(i + ” ” + j); } } Select the two correct answers. (a) The first number printed will be 9. (b) The first number printed will be 10. (c) The first number printed will be 11. (d) The second number printed will be 9. (e) The second number printed will be 10. (f) The second number printed will be 11. 6.9 Which of the following for statements is valid? Select the one correct answer. (a) int j = 10; for (int i = 0, j += 90; i < j; i++) { j-; } (b) for (int i = 10; i = 0; i--) {} (c) for (int i = 0, j = 100; i < j; i++, --j) {;} (d) int i, j; for (j = 100; i < j; j--) { i += 2; } (e) int i = 100; for ((i > 0); i--) {} WOW! eBook www.wowebook.org 6.10 What will be the result of attempting to compile and run the following program? Click here to view code image class AnotherClass { public static void main(String[] int i = 0; for (; i < 10; i++) ; // for (i = 0;; i++) break; // for (i = 0; i < 10;) i++; // for (;;) ; // } } args) { (1) (2) (3) (4) Select the one correct answer. (a) The code will fail to compile because of errors in the for loop at (1). (b) The code will fail to compile because of errors in the for loop at (2). (c) The code will fail to compile because of errors in the for loop at (3). (d) The code will fail to compile because of errors in the for loop at (4). (e) The code will compile without error, and the program will run and terminate without any output. (f) The code will compile without error, but will never terminate when run. 6.11 Which of the following statements are valid when occurring on their own? Select the three correct answers. (a) while () break; (b) do { break; } while (true); (c) if (true) { break; } (d) switch (1) { default: break; } (e) for (;true;) break; 6.12 Given the following code fragment, which of the following lines will be a part of the output? Click here to view code image outer: for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { if (i == j) { continue outer; } System.out.println(“i=” + i + “, j=” + j); } } Select the two correct answers. (a) i=1, j=0 (b) i=0, j=1 WOW! eBook www.wowebook.org (c) i=1, j=2 (d) i=2, j=1 (e) i=2, j=2 (f) i=3, j=3 (g) i=3, j=2 6.13 What will be the result of attempting to compile and run the following code? Click here to view code image class MyClass { public static void main(String[] args) { for (int i = 0; i < 10; i++) { switch(i) { case 0: System.out.println(i); } if (i) { System.out.println(i); } } } } Select the one correct answer. (a) The code will fail to compile because of an illegal switch expression in the switch statement. (b) The code will fail to compile because of an illegal condition in the if statement. (c) The code will compile without error, and will print the numbers 0 through 10 at runtime. (d) The code will compile without error, and will print the number 0 at runtime. (e) The code will compile without error, and will print the number 0 twice at runtime. (f) The code will compile without error, and will print the numbers 1 through 10 at runtime. 6.14 Which declarations, when inserted at (1), will result in the program compiling and printing 90 at runtime? Click here to view code image public class RQ400A10 { public static void main(String[] args) { // (1) INSERT DECLARATION HERE int sum = 0; for (int i : nums) sum += i; System.out.println(sum); } } WOW! eBook www.wowebook.org Select the two correct answers. (a) Object[] nums = {20, 30, 40}; (b) Number[] nums = {20, 30, 40}; (c) Integer[] nums = {20, 30, 40}; (d) int[] nums = {20, 30, 40}; (e) None of the above 6.15 Which method declarations, when inserted at (1), will result in the program compiling and printing 90 when run? Click here to view code image public class RQ400A30 { public static void main(String[] args) { doIt(); } // (1) INSERT METHOD DECLARATION HERE. } Select the two correct answers. (a) public static void doIt() { int[] nums = {20, 30, 40}; for (int sum = 0, i : nums) sum += i; System.out.println(sum); } (b) Click here to view code image public static void doIt() { for (int sum = 0, i : {20, 30, 40}) sum += i; System.out.println(sum); } (c) public static void doIt() { int sum = 0; for (int i : {20, 30, 40}) sum += i; System.out.println(sum); } (d) Click here to view code image public static void doIt() { int sum = 0; for (int i : new int[] {20, 30, 40}) sum += i; System.out.println(sum); } WOW! eBook www.wowebook.org (e) public static void doIt() { int[] nums = {20, 30, 40}; int sum = 0; for (int i : nums) sum += i; System.out.println(sum); } 6.5 Stack-Based Execution and Exception Propagation An exception in Java signals the occurrence of an error situation due to the violation of some semantic constraint of the Java programming language during execution—for example, a requested file cannot be found, an array index is out of bounds, or a network link failed. Explicit checks in the code for such situations can easily result in incomprehensible code. Java provides an exception handling mechanism for systematically dealing with such error situations. The exception mechanism is built around the throw-and-catch paradigm. To throw an exception is to signal that an unexpected condition has occurred. To catch an exception is to take appropriate action to deal with the exception. An exception is caught by an exception handler, and the exception need not be caught in the same context in which it was thrown. The runtime behavior of the program determines which exceptions are thrown and how they are caught. The throw-and-catch principle is embedded in the trycatch-finally construct. Several threads can be executing at the same time in the JVM. Each thread has its own JVM stack (also called a runtime stack, call stack, and invocation stack in the literature) that is used to handle execution of methods. Each element on the stack (called an activation frame or a stack frame) corresponds to a method call. Each new call results in a new activation frame being pushed on the stack, which stores all the pertinent information such as the local variables. The method with the activation frame on the top of the stack is the one currently executing. When this method finishes executing, its activation frame is popped from the top of the stack. Execution then continues in the method corresponding to the activation frame that is now uncovered on the top of the stack. The methods on the stack are said to be active, as their execution has not completed. At any given time, the active methods on a JVM stack make up what is called the stack trace of a thread’s execution. Example 6.11 is a simple program to illustrate method execution. It calculates the average for a list of integers, given the sum of all the integers and the number of integers. It uses three methods: • The method main() calls the method printAverage() with parameters giving the total sum of the integers and the total number of integers, (1). • The method printAverage() in turn calls the method computeAverage(), (3). • The method computeAverage() uses integer division to calculate the average and returns the result, (7). WOW! eBook www.wowebook.org Example 6.11 Method Execution Click here to view code image public class Average1 { public static void main(String[] args) { printAverage(100, 20); System.out.println(“Exit main().”); } // (1) // (2) public static void printAverage(int totalSum, int totalNumber) { int average = computeAverage(totalSum, totalNumber); // (3) System.out.println(“Average = ” + // (4) totalSum + ” / ” + totalNumber + ” = ” + average); System.out.println(“Exit printAverage().”); // (5) } public static int computeAverage(int sum, int number) { System.out.println(“Computing average.”); return sum/number; } // (6) // (7) } Output of program execution: Computing average. Average = 100 / 20 = 5 Exit printAverage(). Exit main(). Execution of Example 6.11 is illustrated in Figure 6.7. Each method execution is shown as a box with the local variables declared in the method. The height of the box indicates how long a method is active. Before the call to the method System.out.println() at (6) in Figure 6.7, the stack trace comprises the three active methods: main(), printAverage(), and computeAverage(). The result 5 from the method computeAverage() is returned at (7) in Figure 6.7. The output from the program corresponds with the sequence of method calls in Figure 6.7. As the program terminates normally, this program behavior is called normal execution. WOW! eBook www.wowebook.org Figure 6.7 Method Execution If the method call at (1) in Example 6.11 Click here to view code image printAverage(100, 20); // (1) is replaced with Click here to view code image printAverage(100, 0); // (1) and the program is run again, the output is as follows: Click here to view code image Computing average. Exception in thread “main” java.lang.ArithmeticException: / by zero at Average1.computeAverage(Average1.java:18) at Average1.printAverage(Average1.java:10) at Average1.main(Average1.java:5) Figure 6.8 illustrates the program execution when the method printAverage() is called with the arguments 100 and 0 at (1). All goes well until the return statement at (7) in the method computeAverage() is executed. An error condition occurs in calculating the expression sum/number, because integer division by 0 is an illegal WOW! eBook www.wowebook.org operation. This error condition is signaled by the JVM by throwing an ArithmeticException (§6.6, p. 233). This exception is propagated by the JVM through the JVM stack as explained next. Figure 6.8 Exception Propagation Figure 6.8 illustrates the case where an exception is thrown and the program does not take any explicit action to deal with the exception. In Figure 6.8, execution of the computeAverage() method is suspended at the point where the exception is thrown. The execution of the return statement at (7) never gets completed. Since this method does not have any code to deal with the exception, its execution is likewise terminated abruptly and its activation frame popped. We say that the method completes abruptly. The exception is then offered to the method whose activation is now on the top of the stack (method printAverage()). This method does not have any code to deal with the exception either, so its execution completes abruptly. The statements at (4) and (5) in the method printAverage() never get executed. The exception now propagates to the last active method (method main()). This does not deal with the exception either. The main() method also completes abruptly. The statement at (2) in the main() method never gets executed. Since the exception is not caught by any of the active methods, it is dealt with by the main thread’s default exception handler. The default exception handler WOW! eBook www.wowebook.org usually prints the name of the exception, with an explanatory message, followed by a printout of the stack trace at the time the exception was thrown. An uncaught exception, as in this case, results in the death of the thread in which the exception occurred. If an exception is thrown during the evaluation of the left-hand operand of a binary expression, then the right-hand operand is not evaluated. Similarly, if an exception is thrown during the evaluation of a list of expressions (e.g., a list of actual parameters in a method call), evaluation of the rest of the list is skipped. If the line numbers in the stack trace are not printed in the output as shown previously, use the following command to run the program: Click here to view code image >java -Djava.compiler=NONE Average1 6.6 Exception Types Exceptions in Java are objects. All exceptions are derived from the java.lang.Throwable class. Figure 6.9 shows a partial hierarchy of classes derived from the Throwable class. The two main subclasses Exception and Error constitute the main categories of throwables, the term used to refer to both exceptions and errors. Figure 6.9 also shows that not all exception classes are found in the java.lang package. WOW! eBook www.wowebook.org Figure 6.9 Partial Exception Inheritance Hierarchy The Throwable class provides a String variable that can be set to provide a detail message when an exception is constructed. The purpose of the detail message is to provide more information about the actual exception. All classes of throwables define a oneparameter constructor that takes a string as the detail message. The class Throwable provides the following common methods to query an exception: WOW! eBook www.wowebook.org String getMessage() Returns the detail message. void printStackTrace() Prints the stack trace on the standard error stream. The stack trace comprises the method invocation sequence on the JVM stack when the exception was thrown. The stack trace can also be written to a PrintStream or a PrintWriter by supplying such a destination as an argument to one of the two overloaded printStackTrace() methods. Any suppressed exceptions associated with an exception on the stack trace are also printed. It will also print the cause of an exception (which is also an exception) if one is available. String toString() Returns a short description of the exception, which typically comprises the class name of the exception together with the string returned by the getMessage() method. In dealing with throwables, it is important to recognize situations in which particular throwables can occur, and the source that is responsible for throwing them. By source we mean: • The JVM that is responsible for throwing the throwable, or • The throwable that is explicitly thrown programmatically by the code in the application or by any API used by the application. In further discussion of exception types, we provide an overview of situations in which selected throwables can occur and the source responsible for throwing them. The Class The class Exception represents exceptions that a program would normally want to catch. Its subclass RuntimeException represents many common programming errors that can manifest at runtime (see the next subsection). Other subclasses of the Exception class define other categories of exceptions, such as I/O-related exceptions in the java.io package (IOException, FileNotFoundException, EOFException) and database-related exceptions in the java.sql package (SQLException). ClassNotFoundException The subclass ClassNotFoundException signals that the JVM tried to load a class by its string name, but the class could not be found. A typical example of this situation is when the class name is misspelled while starting program execution with the java command. The source in this case is the JVM throwing the exception to signal that the class cannot be found and therefore execution cannot be started. WOW! eBook www.wowebook.org The Class Runtime exceptions are all subclasses of the java.lang.RuntimeException class, which is a subclass of the Exception class. As these runtime exceptions are usually caused by program bugs that should not occur in the first place, it is usually more appropriate to treat them as faults in the program design and let them be handled by the default exception handler. ArithmeticException This exception represents situations where an illegal arithmetic operation is attempted, such as integer division by 0. It is typically thrown by the JVM. See Chapter 5 for details on illegal arithmetic operations. ArrayIndexOutOfBoundsException Java provides runtime checking of the array index value, meaning out-of-bounds array indices. The subclass ArrayIndexOutOfBoundsException represents exceptions thrown by the JVM that signal out-of-bound errors specifically for arrays—that is, an error in which an invalid index is used to access an element in the array. The index value must satisfy the relation 0 ≤ index value < length of the array. See §3.4, p. 58, covering arrays. ClassCastException This exception is thrown by the JVM to signal that an attempt was made to cast a reference value to a type that was not legal, such as casting the reference value of an Integer object to the Long type. Casting reference values is discussed in §7.11, p. 320. IllegalArgumentException and NumberFormatException The IllegalArgumentException is thrown programmatically to indicate that a method was called with an illegal or inappropriate argument. For example, the ofPattern(String pattern) method in the java.time.format.DateTimeFormatter class throws an IllegalArgumentException when the letter pattern passed as an argument is invalid (§11.4, p. 495). The class NumberFormatException is a subclass of the IllegalArgumentException class, and is specialized to signal problems when converting a string to a numeric value if the format of the characters in the string is not appropriate for the conversion. This exception is also thrown programmatically. The numeric wrapper classes all have methods that throw this exception when conversion from a string to a numeric value is not possible (§8.3, p. 346). WOW! eBook www.wowebook.org NullPointerException This exception is typically thrown by the JVM when an attempt is made to use the null value as a reference value to refer to an object. This might involve calling an instance method using a reference that has the null value, or accessing a field using a reference that has the null value. This programming error has made this exception one of the exceptions most often thrown by the JVM. The Class The class Error and its subclasses define errors that are invariably never explicitly caught and are usually irrecoverable. Not surprisingly, most such errors are signaled by the JVM. Apart from the subclasses mentioned in the following subsections, other subclasses of the java.lang.Error class define errors that indicate class linkage (LinkageError), thread (ThreadDeath), and virtual machine (VirtualMachineError) problems. AssertionError The subclass AssertionError of the java.lang.Error class is used by the Java assertion facility. This error is thrown by the JVM in response to the condition in the assert statement evaluating to false. The assertion facility is not discussed in this book. StackOverflowError This error occurs when the JVM stack has no more room for new method activation frames. In such a case, we say that the stack has overflowed. This situation can occur when method execution in an application recurses too deeply. Here is a recursive method to illustrate stack overflow: Click here to view code image public void callMe() { System.out.println(“Don’t do this at home!”); callMe(); } Once this method is called, it will keep on calling itself until the JVM stack is full, resulting in the StackOverflowError being thrown by the JVM. Checked and Unchecked Exceptions Except for RuntimeException, Error, and their subclasses, all exceptions are checked exceptions. That is, the compiler ensures that if a method can throw a checked exception, directly or indirectly, the method must explicitly deal with it. The method must either catch the exception and take the appropriate action, or pass on the exception to its caller (§6.9, p. 251). Exceptions defined by the Error and RuntimeException classes and their subclasses are known as unchecked exceptions, meaning that a method is not obliged to deal with these kinds of exceptions (shown with gray color in Figure 6.9). They are either WOW! eBook www.wowebook.org irrecoverable (exemplified by the Error class), in which case the program should not attempt to deal with them, or they are programming errors (exemplified by the RuntimeException class) and should usually be dealt with as such, and not as exceptions. Defining Customized Exceptions Customized exceptions are usually defined to provide fine-grained categorization of error situations, instead of using existing exception classes with descriptive detail messages to differentiate among the various situations. New customized exceptions are usually defined by either extending the Exception class or one of its checked subclasses, thereby making the new exceptions checked, or extending the RuntimeException subclass to create new unchecked exceptions. As exceptions are defined by classes, they can declare fields and methods, thereby providing more information as to their cause and remedy when they are thrown and caught. The super() call can be used to set the detail message for the exception. Note that the exception class must be instantiated to create an exception object that can be thrown and subsequently caught and dealt with. The following code sketches a class declaration for an exception that can include all pertinent information about the exception. At a minimum, the new exception class should provide a constructor to set the detail message. Click here to view code image public class EvacuateException extends Exception { // Data Date date; Zone zone; TransportMode transport; // Constructor public EvacuateException(Date d, Zone z, TransportMode t) { // Call the constructor of the superclass super(“Evacuation of zone ” + z); // … } // Methods // … } Several examples in subsequent sections illustrate exception handling. 6.7 Exception Handling: , , and The mechanism for handling exceptions is embedded in the try-catch-finally construct, which has the following basic form: Click here to view code image try { statements } catch (exception_type1 parameter1) { statements } … // try block // uni-catch clause WOW! eBook www.wowebook.org catch (exception_typen parametern) { statements } finally { statements } // uni-catch clause // finally clause A few aspects about the syntax of this construct should be noted. For each try block, there can be zero or more catch clauses (i.e., it can have multiple catch clauses), but only one finally clause. The catch clauses and the finally clause must always appear in conjunction with a try block, and in the right order. A try block must be followed by at least one catch clause, or a finally clause must be specified. In addition to the try block, each catch clause and the finally clause specify a block, { }. The block notation is mandatory. Exceptions thrown during execution of the try block can be caught and handled in a catch clause. Each catch clause defines an exception handler. The header of the catch clause specifies exactly one exception parameter. The exception type must be of the Throwable class or one of its subclasses. The type of the exception parameter of a catch clause is specified by a single exception type in the syntax given earlier, and such a catch clause is called a uni-catch clause. A finally clause is guaranteed to be executed, regardless of the cause of exit from the try block, or whether any catch clause was executed. Figure 6.10 shows three typical scenarios of control flow through the try-catch-finally construct. Figure 6.10 The try-catch-finally Construct The try block, the catch clause, and the finally clause of a try-catchfinally construct can contain arbitrary code, which means that a try-catchWOW! eBook www.wowebook.org finally construct can be nested in any block of the try-catch-finally construct. However, such nesting can easily make the code difficult to read and is best avoided, if possible. The Block The try block establishes a context for exception handling. Termination of a try block occurs as a result of encountering an exception, or from successful execution of the code in the try block. The catch clauses are skipped for all normal exits from the try block when no exceptions are thrown, and control is transferred to the finally clause if one is specified ((1) in Figure 6.10). For all exits from the try block resulting from exceptions, control is transferred to the catch clauses—if any such clauses are specified—to find a matching catch clause ((2) in Figure 6.10). If no catch clause matches the thrown exception, control is transferred to the finally clause if one is specified ((3) in Figure 6.10). The Clause Only an exit from a try block resulting from an exception can transfer control to a catch clause. A catch clause can catch the thrown exception only if the exception is assignable to the parameter in the catch clause (§7.8, p. 311). The code of the first such catch clause is executed, and all other catch clauses are ignored. On exit from a catch clause, normal execution continues unless there is any uncaught exception that has been thrown and not handled. If this is the case, the method is aborted and the exception is propagated up the JVM stack as explained in §6.5, p. 230. After a catch clause has been executed, control is always transferred to the finally clause if one is specified. This is always true as long as there is a finally clause, regardless of whether the catch clause itself throws an exception. In Example 6.12, the method printAverage() calls the method computeAverage() in a try-catch construct at (4). The catch clause is declared to catch exceptions of type ArithmeticException. The catch clause handles the exception by printing the stack trace and some additional information at (7) and (8), respectively. Normal execution of the program is illustrated in Figure 6.11, which shows that the try block is executed but no exceptions are thrown, with normal execution continuing after the try-catch construct. This corresponds to Scenario 1 in Figure 6.10. WOW! eBook www.wowebook.org Figure 6.11 Exception Handling (Scenario 1) WOW! eBook www.wowebook.org Example 6.12 The try-catch Construct Click here to view code image public class Average2 { public static void main(String[] args) { printAverage(100, 20); System.out.println(“Exit main().”); } public static void printAverage(int totalSum, int totalNumber) { try { int average = computeAverage(totalSum, totalNumber); System.out.println(“Average = ” + totalSum + ” / ” + totalNumber + ” = ” + average); } catch (ArithmeticException ae) { ae.printStackTrace(); System.out.println(“Exception handled in printAverage().”); } System.out.println(“Exit printAverage().”); } public static int computeAverage(int sum, int number) { System.out.println(“Computing average.”); return sum/number; } // (1) // (2) // (3) // (4) // (5) // (6) // (7) // (8) // (9) // (10) // (11) } Output from the program, with call printAverage(100, 20) at (1): Computing average. Average = 100 / 20 = 5 Exit printAverage(). Exit main(). Output from the program, with call printAverage(100, 0) at (1): Click here to view code image Computing average. java.lang.ArithmeticException: / by zero at Average2.computeAverage(Average2.java:23) at Average2.printAverage(Average2.java:11) at Average2.main(Average2.java:5) Exception handled in printAverage(). Exit printAverage(). Exit main(). However, if we run the program in Example 6.12 with the following call in (1): printAverage(100, 0) an ArithmeticException is thrown by the integer division operator in the method computeAverage(). In Figure 6.12 we see that the execution of the method compute-Average() is stopped and the exception propagated to method printAverage(), where it is handled by the catch clause at (6). Normal execution of the method continues at (9) after the try-catch construct, as witnessed by the output from the statements at (9) and (2). This corresponds to Scenario 2 in Figure 6.10. WOW! eBook www.wowebook.org Figure 6.12 Exception Handling (Scenario 2) In Example 6.13, the main() method calls the printAverage() method in a trycatch construct at (1). The catch clause at (3) is declared to catch exceptions of type ArithmeticException. The printAverage() method calls the computeAverage() method in a try-catch construct at (7), but here the catch clause is declared to catch exceptions of type IllegalArgumentException. Execution of the program is illustrated in Figure 6.13, which shows that the ArithmeticException is first propagated to the catch clause in the printAverage() method. Because this catch clause cannot handle this exception, it is propagated further to the catch clause in the main() method, where it is caught and handled. Normal execution continues at (6) after the exception is handled. WOW! eBook www.wowebook.org Figure 6.13 Exception Handling (Scenario 3) Note that the execution of the try block at (7) in the printAverage() method is never completed: The statement at (9) is never executed. The catch clause at (10) is skipped. The execution of the printAverage() method is aborted: The statement at (13) is never executed, and the exception is propagated. This corresponds to Scenario 3 in Figure 6.10. WOW! eBook www.wowebook.org Example 6.13 Exception Propagation Click here to view code image public class Average3 { public static void main(String[] args) { try { printAverage(100, 0); } catch (ArithmeticException ae) { ae.printStackTrace(); System.out.println(“Exception handled in main().”); } System.out.println(“Exit main().”); } public static void printAverage(int totalSum, int totalNumber) { try { int average = computeAverage(totalSum, totalNumber); System.out.println(“Average = ” + totalSum + ” / ” + totalNumber + ” = ” + average); } catch (IllegalArgumentException iae) { iae.printStackTrace(); System.out.println(“Exception handled in printAverage().”); } System.out.println(“Exit printAverage().”); } public static int computeAverage(int sum, int number) { System.out.println(“Computing average.”); return sum/number; } // // // // // (1) (2) (3) (4) (5) // (6) // (7) // (8) // (9) // (10) // (11) // (12) // (13) // (14) // (15) } Output from the program: Click here to view code image Computing average. java.lang.ArithmeticException: / by zero at Average3.computeAverage(Average3.java:28) at Average3.printAverage(Average3.java:16) at Average3.main(Average3.java:6) Exception handled in main(). Exit main(). The scope of the parameter name in the catch clause is the clause itself. As mentioned earlier, the type of the exception object must be assignable to the type of the argument in the catch clause (§7.8, p. 311). In the body of the catch clause, the exception object can be queried like any other object by using the parameter name. The javac compiler also complains if a catch clause for a superclass exception shadows the catch clause for a subclass exception, as the catch clause of the subclass exception will never be executed (a situation known as unreachable code). The following example shows incorrect order of the catch clauses at (1) and (2), which will result in a compile-time error: The superclass Exception will shadow the subclass ArithmeticException. Click here to view code image … // Compiler-time error at (1). WOW! eBook www.wowebook.org catch (Exception e) { System.out.println(e); } catch (ArithmeticException e) { System.out.println(e); } … The // (1) superclass // (2) subclass Clause If the try block is executed, then the finally clause is guaranteed to be executed, regardless of whether any catch clause was executed. Since the finally clause is always executed before control transfers to its final destination, the finally clause can be used to specify any clean-up code (e.g., to free resources such as files and net connections). However, the try-with-resources statement provides a better solution for handling resources, and eliminates use of the finally clause in many cases. But that is a story for another day, as this topic is not on the OCAJP8 exam. A try-finally construct can be used to control the interplay between two actions that must be executed in the correct order, possibly with other intervening actions. In the following code, the operation in the calculateAverage() method is dependent on the success of the sumNumbers() method, which is checked by the value of the sum variable before calling the calculateAverage() method: Click here to view code image int sum = 0; try { sum = sumNumbers(); // other actions } finally { if (sum > 0) calculateAverage(); } This code guarantees that if the try block is entered, the sumNumbers() method will be executed first, and later the calculateAverage() method will be executed in the finally clause, regardless of how execution proceeds in the try block. We can, if desired, include any catch clauses to handle any exceptions. If the finally clause neither throws an exception nor executes a control transfer statement like a return or a labeled break, the execution of the try block or any catch clause determines how execution proceeds after the finally clause (Figure 6.10, p. 239). • If no exception is thrown during execution of the try block or the exception has been handled in a catch clause, normal execution continues after the finally clause. • If there is any uncaught exception (either because no catch clause was found or because the catch clause threw an exception), the method is aborted and the exception is propagated after the execution of the finally clause. The output of Example 6.14 shows that the finally clause at (9) is executed, regardless of whether an exception is thrown in the try block at (3). If an exception is thrown, it is WOW! eBook www.wowebook.org caught and handled by the catch clause at (6). After the execution of the finally clause at (9), normal execution continues at (10). Example 6.14 The try-catch-finally Construct Click here to view code image public class Average4 { public static void main(String[] args) { printAverage(100, 20); System.out.println(“Exit main().”); } public static void printAverage(int totalSum, int totalNumber) { try { int average = computeAverage(totalSum, totalNumber); System.out.println(“Average = ” + totalSum + ” / ” + totalNumber + ” = ” + average); } catch (ArithmeticException ae) { ae.printStackTrace(); System.out.println(“Exception handled in printAverage().”); } finally { System.out.println(“Finally done.”); } System.out.println(“Exit printAverage().”); } public static int computeAverage(int sum, int number) { System.out.println(“Computing average.”); return sum/number; } // (1) // (2) // (3) // (4) // (5) // // // // (6) (7) (8) (9) // (10) // (11) // (12) } Output from the program, with the call printAverage(100, 20) at (1): Computing average. Average = 100 / 20 = 5 Finally done. Exit printAverage(). Exit main(). Output from the program, with the call printAverage(100, 0) at (1): Click here to view code image Computing average. java.lang.ArithmeticException: / by zero at Average4.computeAverage(Average4.java:25) at Average4.printAverage(Average4.java:11) at Average4.main(Average4.java:5) Exception handled in printAverage(). Finally done. Exit printAverage(). Exit main(). On exiting from the finally clause, if there is any uncaught exception, the method is aborted and the exception propagated as explained earlier. This is illustrated in Example 6.15. The method printAverage() is aborted after the finally clause at (6) has been executed, as the ArithmeticException thrown at (9) is not handled by WOW! eBook www.wowebook.org any method. In this case, the exception is handled by the default exception handler. Notice the difference in the output from Example 6.14 and Example 6.15. Example 6.15 The try-finally Construct Click here to view code image public class Average5 { public static void main(String[] args) { printAverage(100, 0); System.out.println(“Exit main().”); } // (1) // (2) public static void printAverage(int totalSum, int totalNumber) { try { // (3) int average = computeAverage(totalSum, totalNumber); // (4) System.out.println(“Average = ” + // (5) totalSum + ” / ” + totalNumber + ” = ” + average); } finally { // (6) System.out.println(“Finally done.”); } System.out.println(“Exit printAverage().”); // (7) } public static int computeAverage(int sum, int number) { System.out.println(“Computing average.”); return sum/number; } // (8) // (9) } Output from the program: Click here to view code image Computing average. Finally done. Exception in thread “main” java.lang.ArithmeticException: / by zero at Average5.computeAverage(Average5.java:22) at Average5.printAverage(Average5.java:11) at Average5.main(Average5.java:4) If the finally clause executes a control transfer statement, such as a return or a labeled break, this control transfer statement determines how the execution will proceed —regardless of how the try block or any catch clause was executed. In particular, a value returned by a return statement in the finally clause will supercede any value returned by a return statement in the try block or a catch clause. Example 6.16 shows how the execution of a control transfer statement such as a return in the finally clause affects the program execution. The first output from the program shows that the average is computed but the value returned is from the return statement at (8) in the finally clause, not from the return statement at (6) in the try block. The second output shows that the ArithmeticException thrown in the computeAverage() method and propagated to the printAverage() method is nullified by the return statement in the finally clause. Normal execution continues after the return statement at (8), with the value 0 being returned from the WOW! eBook www.wowebook.org printAverage() method. If the finally clause throws an exception, this exception is propagated with all its ramifications—regardless of how the try block or any catch clause was executed. In particular, the new exception overrules any previously uncaught exception. Example 6.16 The finally Clause and the return Statement Click here to view code image public class Average6 { public static void main(String[] args) { System.out.println(“Average: ” + printAverage(100, 20)); System.out.println(“Exit main().”); } public static int printAverage(int totalSum, int totalNumber) { int average = 0; try { average = computeAverage(totalSum, totalNumber); System.out.println(“Average = ” + totalSum + ” / ” + totalNumber + ” = ” + average); return average; } finally { System.out.println(“Finally done.”); return average*2; } } public static int computeAverage(int sum, int number) { System.out.println(“Computing average.”); return sum/number; } // (1) // (2) // (3) // (4) // (5) // (6) // (7) // (8) // (9) // (10) } Output from the program, with call printAverage(100, 20) in (1): Computing average. Average = 100 / 20 = 5 Finally done. Average: 10 Exit main(). Output from the program, with call printAverage(100, 0) in (1): Computing average. Finally done. Average: 0 Exit main(). 6.8 The Statement Earlier examples in this chapter have shown how an exception can be thrown implicitly by the JVM during execution. Now we look at how an application can programmatically throw an exception using the throw statement. The general format of the throw statement is as follows: Click here to view code image WOW! eBook www.wowebook.org throw object_reference_expression; The compiler ensures that the object reference expression is of the type Throwable class or one of its subclasses. At runtime a NullPointerException is thrown by the JVM if the object reference expression is null. This ensures that a Throwable will always be propagated. A detail message is often passed to the constructor when the exception object is created. Click here to view code image throw new ArithmeticException(“Integer division by 0”); When an exception is thrown, normal execution is suspended. The runtime system proceeds to find a catch clause that can handle the exception. The search starts in the context of the current try block, propagating to any enclosing try blocks and through the JVM stack to find a handler for the exception. Any associated finally clause of a try block encountered along the search path is executed. If no handler is found, then the exception is dealt with by the default exception handler at the top level. If a handler is found, normal execution resumes after the code in its catch clause has been executed, barring any rethrowing of an exception. In Example 6.17, an exception is thrown using a throw statement at (17). This exception is propagated to the main() method, where it is caught and handled by the catch clause at (3). Note that the finally clauses at (6) and (14) are executed. Execution continues normally at (7). WOW! eBook www.wowebook.org Example 6.17 Throwing Exceptions Click here to view code image public class Average7 { public static void main(String[] args) { try { printAverage(100, 0); } catch (ArithmeticException ae) { ae.printStackTrace(); System.out.println(“Exception handled in main().”); } finally { System.out.println(“Finally in main().”); } System.out.println(“Exit main().”); } public static void printAverage(int totalSum, int totalNumber) { try { int average = computeAverage(totalSum, totalNumber); System.out.println(“Average = ” + totalSum + ” / ” + totalNumber + ” = ” + average); } catch (IllegalArgumentException iae) { iae.printStackTrace(); System.out.println(“Exception handled in printAverage().”); } finally { System.out.println(“Finally in printAverage().”); } System.out.println(“Exit printAverage().”); } public static int computeAverage(int sum, int number) { System.out.println(“Computing average.”); if (number == 0) throw new ArithmeticException(“Integer division by 0”); return sum/number; } // // // // // (1) (2) (3) (4) (5) // (6) // (7) // (8) // (9) // (10) // (11) // (12) // (13) // (14) // (15) // (16) // (17) // (18) } Output from the program: Click here to view code image Computing average. Finally in printAverage(). java.lang.ArithmeticException: Integer division by 0 at Average7.computeAverage(Average7.java:33) at Average7.printAverage(Average7.java:18) at Average7.main(Average7.java:6) Exception handled in main(). Finally in main(). Exit main(). 6.9 The Clause A throws clause can be specified in a method or a constructor header to declare any checked exceptions that can be thrown by a statement in the body of a method or a constructor. It is declared immediately preceding the body of the method or the constructor. WOW! eBook www.wowebook.org Click here to view code image … throws ExceptionType1, ExceptionType2,…, ExceptionTypen { … } Each ExceptionTypei is an exception type, although usually only checked exceptions are specified. The compiler enforces that if a checked exception can be the result of executing a method or a constructor, then either the exception type of this exception or a supertype of its exception type is specified in the throws clause of the method or the constructor. The throws clause can specify unchecked exceptions, but this is seldom done and the compiler does not enforce any restrictions on their usage. The throws clause is part of the contract that a method or a constructor offers to its clients. The throws clause can specify any number of exception types, even those that are not thrown by the method or the constructor. The compiler simply ensures that any checked exception that can actually be thrown in the method or constructor body is covered by the throws clause. Of course, the clients cannot ignore the checked exceptions in the throws clause. In a method or a constructor, a checked exception can be thrown directly by a throw statement, or indirectly by calling other methods or constructors that can throw a checked exception. If a checked exception is thrown, it must be handled in one of three ways: • By using a try block and catching the exception in a handler and dealing with it • By using a try block and catching the exception in a handler, but throwing another exception that is either unchecked or declared in its throws clause • By explicitly allowing propagation of the exception to its caller by declaring it in the throws clause of its header This mechanism (also known as catch-or-declare) ensures that a checked exception will be dealt with, regardless of the path of execution. This aids development of robust programs, as allowance can be made for many contingencies. Native methods can also declare checked exceptions in their throws clause, but the compiler is not able to check them for consistency. In Example 6.18, a new checked exception is defined, where the checked exception class IntegerDivisionByZero extends the Exception class. The method main() calls the method printAverage() in a try block at (1). In the if statement at (9), the method computeAverage() throws the checked exception IntegerDivisionByZero. Neither the computeAverage() method nor the printAverage() method catches the exception, but instead throws it to the caller, as declared in the throws clauses in their headers at (6) and (8). The exception propagates to the main() method. Since the printAverage() method was called from the context of the try block at (1) in the main() method, the exception is successfully caught by its catch clause at (3). The exception is handled and the finally clause at (4) executed, with normal execution resuming from (5). If the method main() did not catch the exception, it would have to declare this exception in a throws clause. In that case, the exception would end up being handled by the default exception handler. WOW! eBook www.wowebook.org Example 6.18 The throws Clause Click here to view code image // File: IntegerDivisionByZero.java public class IntegerDivisionByZero extends Exception { IntegerDivisionByZero(String str) { super(str); } } // File: Average8.java public class Average8 { public static void main(String[] args) { try { printAverage(100, 0); } catch (IntegerDivisionByZero idbz) { idbz.printStackTrace(); System.out.println(“Exception handled in main().”); } finally { System.out.println(“Finally done in main().”); } System.out.println(“Exit main().”); } public static void printAverage(int totalSum, int totalNumber) throws IntegerDivisionByZero { int average = computeAverage(totalSum, totalNumber); System.out.println(“Average = ” + totalSum + ” / ” + totalNumber + ” = ” + average); System.out.println(“Exit printAverage().”); } // (1) // (2) // (3) // (4) // (5) // (6) // (7) public static int computeAverage(int sum, int number) throws IntegerDivisionByZero { // (8) System.out.println(“Computing average.”); if (number == 0) // (9) throw new IntegerDivisionByZero(“Integer Division By Zero”); return sum/number; // (10) } } Output from the program: Click here to view code image Computing average. IntegerDivisionByZero: Integer Division By Zero at Average8.computeAverage(Average8.java:27) at Average8.printAverage(Average8.java:17) at Average8.main(Average8.java:5) Exception handled in main(). Finally done in main(). Exit main(). As mentioned earlier, the exception type specified in the throws clause can be a superclass of the actual exceptions thrown; that is, the exceptions thrown must be assignable to the type of the exceptions specified in the throws clause. If a method or a constructor can throw a checked exception, then the throws clause must declare its exception type or a supertype of its exception type; otherwise, a compile-time error will occur. In the printAverage() method, the method header could specify the superclass WOW! eBook www.wowebook.org Exception of the subclass IntegerDivisionByZero in the throws clause. This would also entail that the main() method either catch an Exception or declare it in a throws clause. Click here to view code image public static void main(String[] args) throws Exception { /* … */ } public static void printAverage(int totalSum, int totalNumber) throws Exception { /* … */ } It is generally considered bad programming style to specify exception superclasses in the throws clause of the header when the actual exceptions thrown are instances of their subclasses. It is also recommended to use the @throws tag in a Javadoc comment to document the checked exceptions that a method or a constructor can throw, together with any unchecked exceptions that might also be relevant to catch. Overriding the Clause A subclass can override a method defined in its superclass by providing a new implementation (§7.2, p. 268). What happens when a superclass method with a list of exceptions in its throws clause is overridden in a subclass? The method declaration in the subclass need not specify a throws clause if it does not throw any checked exceptions, and if it does, it can specify only checked exception classes that are already in the throws clause of the superclass method, or that are subclasses of the checked exceptions in the throws clause of the superclass method. As a consequence, an overriding method cannot allow more checked exceptions in its throws clause than the superclass method does. Allowing more checked exceptions in the overriding method would create problems for clients who already deal with the exceptions specified in the superclass method. Such clients would be ill prepared if an object of the subclass threw a checked exception they were not prepared for. However, there are no restrictions on specifying unchecked exceptions in the throws clause of the overriding method. The preceding discussion also applies to methods from an interface that a class implements, as these methods are overridden by any class implementing the interface. In the following code, the method superclassMethodX in superclass A is overridden in subclass B. The throws clause of the method in subclass B at (2) specifies a subset of the checked exceptions specified in the throws clause at (1) and adds the more specific subclass exception, SubFirstException, of the superclass exception, FirstException, specified in the throws clause at (1). Click here to view code image // New exception classes: class FirstException extends class SecondException extends class ThirdException extends class SubFirstException extends Exception { } Exception { } Exception { } FirstException { } WOW! eBook www.wowebook.org // Superclass class A { // … protected void superclassMethodX() throws FirstException, SecondException, ThirdException {/* … */} // (1) // … } // Subclass class B extends A { // … @Override protected void superclassMethodX() throws FirstException, ThirdException, SubFirstException { /* … */ } // (2) // … } 6.10 Advantages of Exception Handling Robustness refers to the ability of a software system to respond to errors during execution. A system should respond to unexpected situations at runtime in a responsible way. Applications that provide the user with frequent cryptic messages with error codes or that repeatedly give the user the silent treatment when something goes wrong can hardly be considered robust. The exception handling mechanism in Java offers the following advantages that facilitate developing robust applications in Java: • Separation of Exception Handling Code The code for handling error situations can be separated from the code for the program logic by using the exception handling constructs provided by the language. Code that can result in error situations is confined in the try block, and their handling in the catch clause. • Transparent Exception Propagation Propagation of a checked exception in the JVM stack cannot be ignored by an active method. The method must comply with the catch-or-declare requirement: either catch and handle the exception, or propagate it by declaring it in the method’s throws clause. Error situations causing exception propagation are thus always detected, and can be caught and remedied. • Exception Categorization and Specialization The exception and error classes in the Java SE platform API are organized in an inheritance hierarchy (Figure 6.9, p. 234). Classes higher up in this hierarchy represent categories of exceptions and errors (Exception, RuntimeException, IO-Exception, Error), whereas classes lower in this hierarchy represent more specific exceptions and errors (NullPointerException, FileNotFoundException, AssertionError). The try-catch construct allows flexibility in catching and handling exceptions. A catch clause can specify an exception category for coarsegrained exception handling, as the exception category class will subsume its more WOW! eBook www.wowebook.org specific exception subclasses, or it can specify a more specific exception class for fine-grained exception handling. Best practice dictates that fine-grained exception handling be used. Review Questions 6.16 Which digits, and in which order, will be printed when the following program is run? Click here to view code image public class DemoClass { public static void main(String[] args) { int k=0; try { int i = 5/k; } catch (ArithmeticException e) { System.out.println(“1”); } catch (RuntimeException e) { System.out.println(“2”); return; } catch (Exception e) { System.out.println(“3”); } finally { System.out.println(“4”); } System.out.println(“5”); } } Select the one correct answer. (a) The program will only print 5. (b) The program will only print 1 and 4, in that order. (c) The program will only print 1, 2, and 4, in that order. (d) The program will only print 1, 4, and 5, in that order. (e) The program will only print 1, 2, 4, and 5, in that order. (f) The program will only print 3 and 5, in that order. 6.17 Given the following program, which statements are true? Click here to view code image public class Exceptions { public static void main(String[] args) { try { if (args.length == 0) return; System.out.println(args[0]); } finally { System.out.println(“The end”); } } } Select the two correct answers. WOW! eBook www.wowebook.org (a) If run with no arguments, the program will produce no output. (b) If run with no arguments, the program will print The end. (c) The program will throw an ArrayIndexOutOfBoundsException. (d) If run with one argument, the program will simply print the given argument. (e) If run with one argument, the program will print the given argument followed by "The end". 6.18 Which of the following statements are true? Select the two correct answers. (a) If an exception is not caught in a method, the method will terminate and normal execution will resume. (b) An overriding method must declare that it throws the same exception classes as the method it overrides. (c) The main() method of a program can declare that it throws checked exceptions. (d) A method declaring that it throws an exception of a certain class may throw instances of any subclass of that exception class. (e) finally clauses are executed if, and only if, an exception gets thrown while inside the corresponding try block. 6.19 Which digits, and in which order, will be printed when the following program is run? Click here to view code image public class RQ6A19 { public static void main(String[] args) throws InterruptedException { try { throwIt(); System.out.println(“1”); } finally { System.out.println(“2”); } System.out.println(“3”); } // InterruptedException is a direct subclass of Exception. static void throwIt() throws InterruptedException { throw new InterruptedException(“Time to go home.”); } } Select the one correct answer. (a) The program will print 2 and throw InterruptedException. (b) The program will print 1 and 2, in that order. (c) The program will print 1, 2, and 3, in that order. WOW! eBook www.wowebook.org (d) The program will print 2 and 3, in that order. (e) The program will print 3 and 2, in that order. (f) The program will print 1 and 3, in that order. 6.20 What is wrong with the following code? Click here to view code image public class RQ6A20 { public static void main(String[] args) throws A { try { action(); } finally { System.out.println(“Done.”); } catch (A e) { throw e; } } public static void action() throws B { throw new B(); } } class A extends Throwable {} class B extends A {} Select the one correct answer. (a) The main() method must declare that it throws B. (b) The finally clause must follow the catch clause in the main() method. (c) The catch clause in the main() method must declare that it catches B rather than A. (d) A single try block cannot be followed by both catch and finally clauses. (e) The declaration of class A is illegal. 6.21 Which throws clause should be inserted at (1) for the overriding method compute() in the following code so that the code will compile without errors? Click here to view code image class A { // InterruptedException is a direct subclass of Exception. void compute() throws ArithmeticException, InterruptedException { div(5, 5); } int div(int i, int j) throws ArithmeticException { return i/j; } } public class Client extends A { void compute() /* (1) INSERT throws CLAUSE HERE. */ { try { div(5, 0); WOW! eBook www.wowebook.org } catch (ArithmeticException e) { return; } throw new RuntimeException(“ArithmeticException was expected.”); } } Select the one correct answer. (a) No throws clause is necessary. (b) throws ArithmeticException (c) throws InterruptedException (d) throws RuntimeException (e) throws ArithmeticException, InterruptedException Chapter Summary The following information was covered in this chapter: • The selection statements: if, if-else, switch • The iteration statements: for(;;), for(:), while, do-while • The transfer statements: break, continue, return • Exception handling and exception classes in the core API • Defining customized exception types • The try-catch-finally construct and control flow paths through the construct • Using multiple catch clauses with the try statement • Throwing exceptions programmatically with the throw statement • Using the throws clause to specify checked exceptions Programming Exercises 6.1 Create different versions of a program that finds all the primes smaller than 100. Create one version that uses only the for(;;) loop (i.e., no while or dowhile). Create another version that uses only the while loop. 6.2 Here is a skeleton of a system for simulating a nuclear power plant. Implement the methods in the class named Control. Modify the method declarations if necessary. The Javadoc comments for each method give a description of what the implementation should do. Some of the methods in the other classes have unspecified implementations. Assume that these methods have been properly implemented and provide hooks to the rest of the system. Click here to view code image package energy; WOW! eBook www.wowebook.org /** A PowerPlant with a reactor core. */ public class PowerPlant { /** Each power plant has a reactor core. This field has package accessibility so that the Control class, defined in the same package, can access it. */ final Reactor core; /** Initializes the power plant, creates a reactor core. */ public PowerPlant() { core = new Reactor(); } /** Sounds the alarm to evacuate the power plant. */ public void soundEvacuateAlarm() { // … implementation unspecified … } /** @return the level of reactor output that is most desirable at this time. (Units are unspecified.) */ public int getOptimalThroughput() { // … implementation unspecified … return 0; } /** The main entry point of the program: sets up a PowerPlant object and a Control object and lets the Control object run the power plant. */ public static void main(String[] args) { PowerPlant plant = new PowerPlant(); Control ctrl = new Control(plant); ctrl.runSystem(); } } //______________________________________________________________________________ /** A reactor core that has a throughput that can be either decreased or increased. */ class Reactor { /** @return the current throughput of the reactor. (Units are unspecified.) */ public int getThroughput() { // … implementation unspecified … return 0; } /** @return true if the reactor status is critical, false otherwise. */ public boolean isCritical() { // … implementation unspecified … return false; } /** Asks the reactor to increase throughput. */ void increaseThroughput() throws ReactorCritical { // … implementation unspecified … } /** Asks the reactor to decrease throughput. */ void decreaseThroughput() { // … implementation unspecified … } WOW! eBook www.wowebook.org } //______________________________________________________________________________ /** This exception class should be used to report that the reactor status is critical. */ class ReactorCritical extends Exception {} //______________________________________________________________________________ /** A controller that will manage the power plant to make sure that the reactor runs with optimal throughput. */ class Control { private final PowerPlant thePlant; static final int TOLERANCE = 10; /** @param p the power plant to control */ public Control(PowerPlant p) { thePlant = p; } /** Runs the power plant by continuously monitoring the optimal throughput and the actual throughput of the reactor. If the throughputs differ by more than 10 units (i.e. tolerance), adjust the reactor throughput. If the reactor goes critical, the evacuate alarm is sounded and the reactor is shut down. The runSystem() method calls the methods needAdjustment(), adjustThroughput(), and shutdown(). */ public void runSystem() { // … provide implementation here … } /** Reports whether the throughput of the reactor needs adjusting. This method should also monitor and report if the reactor goes critical. @param target the desired throughput. @return true if the optimal and actual throughput values differ by more than 10 units. */ public boolean needAdjustment(int target) { // … provide implementation here … return true; } /** Adjusts the throughput of the reactor by calling increaseThroughput() and decreaseThroughput() methods until the actual throughput is within 10 units of the target throughput. @param target the desired throughput. */ public void adjustThroughput(int target) { // … provide implementation here … } /** Shuts down the reactor by lowering the throughput to 0. */ public void shutdown() { // … provide implementation here … } } WOW! eBook www.wowebook.org 7. Object-Oriented Programming 7.1 Single Implementation Inheritance Inheritance is one of the fundamental mechanisms for code reuse in object-oriented programming (OOP). It allows new classes to be derived from existing ones. The new class (also called a subclass, subtype, derived class, or child class) can inherit members from the old class (also called a superclass, supertype, base class, or parent class). The subclass can add new behavior and properties and, under certain circumstances, modify its inherited behavior. In Java, implementation inheritance (also known as class inheritance) is achieved by extending classes (i.e., adding new fields and methods) and modifying inherited members (§7.2, p. 268). Inheritance of members is closely tied to their declared accessibility. If a superclass member is accessible by its simple name in the subclass (without the use of any extra syntax like super), that member is considered inherited. Conversely, private, overridden, and hidden members of the superclass are not inherited. Inheritance should not WOW! eBook www.wowebook.org be confused with the existence of such members in the state of a subclass object (Example 7.1). A subclass specifies the name of its superclass in the subclass header using the extends clause. Click here to view code image class TubeLight extends Light { … } // TubeLight is a subclass of Light. The subclass specifies only the additional new and modified members in its class body. The rest of its declaration is made up of its inherited members. If no extends clause is specified in the header of a class declaration, the class implicitly inherits from the java.lang.Object class (§8.2, p. 342). This implicit inheritance is assumed in the declaration of the Light class at (1) in Example 7.1. Also in Example 7.1, the subclass TubeLight at (2) explicitly uses the extends clause and specifies only members other than those that it already inherits from the superclass Light (which, in turn, inherits from the Object class). Members of the superclass Light, which are accessible by their simple names in the subclass TubeLight, are inherited by the subclass, as evident from the output in Example 7.1. Private members of the superclass are not inherited by the subclass and can be accessed only indirectly. The private field indicator of the superclass Light is not inherited, but exists in the subclass object and is indirectly accessible through public methods. Using appropriate accessibility modifiers, the superclass can limit which members can be accessed directly and, therefore, inherited by its subclasses (§4.7, p. 123). As shown in Example 7.1, the subclass can use the inherited members as if they were declared in its own class body. This is not the case for members that are declared as private in the superclass. Members that have package accessibility in the superclass are also not inherited by subclasses in other packages, as these members are accessible by their simple names only in subclasses within the same package as the superclass. Since constructors (§7.5, p. 282) are not members of a class, they are not inherited by a subclass. Example 7.1 Extending Classes: Inheritance and Accessibility Click here to view code image // File: Utility.java class Light { // Instance fields: int noOfWatts; private boolean indicator; protected String location; // Static field: private static int counter; // (1) // Wattage // On or off // Placement // Number of Light objects created // No-argument constructor: Light() { noOfWatts = 50; indicator = true; location = “X”; WOW! eBook www.wowebook.org counter++; } // Instance methods: public void switchOn() { indicator = true; } public void switchOff() { indicator = false; } public boolean isOn() { return indicator; } private void printLocation() { System.out.println(“Location: ” + location); } // Static methods: public static void writeCount() { System.out.println(“Number of lights: ” + counter); } //… } //______________________________________________________________________________ class TubeLight extends Light { // (2) Subclass uses the extends clause. // Instance fields: private int tubeLength = 54; private int colorNo = 10; // Instance methods: public int getTubeLength() { return tubeLength; } public void printInfo() { System.out.println(“From the subclass:”); System.out.println(“Tube length: “ + tubeLength); System.out.println(“Color number: ” + colorNo); System.out.println(“Tube length: “ + getTubeLength()); System.out.println(); System.out.println(“From the superclass:”); System.out.println(“Wattage: “ + noOfWatts); // Inherited. // System.out.println(“Indicator: “ + indicator); // Not inherited. System.out.println(“Location: “ + location); // Inherited. // System.out.println(“Counter: “ + counter); // Not inherited. switchOn(); // Inherited switchOff(); // Inherited System.out.println(“Indicator: “ + isOn()); // Inherited. // printLocation(); // Not inherited. writeCount(); // Inherited. } // … } //______________________________________________________________________________ public class Utility { // (3) public static void main(String[] args) { new TubeLight().printInfo(); } } Output from the program: From the subclass: Tube length: 54 Color number: 10 Tube length: 54 From the superclass: Wattage: 50 Location: X WOW! eBook www.wowebook.org Indicator: false Number of lights: 1 In Java, a class can extend only one class; that is, it can have only one immediate superclass. This kind of inheritance is sometimes called single or linear implementation inheritance. The name is appropriate, as the subclass inherits the implementations of its superclass members. The inheritance relationship can be depicted as an inheritance hierarchy (also called a class hierarchy). Classes higher up in the hierarchy are more generalized (often called broader), as they abstract the class behavior. Classes lower down in the hierarchy are more specialized (often called narrower), as they customize the inherited behavior by additional properties and behavior. Figure 7.1 illustrates the inheritance relationship between the class Light, which represents the more general abstraction, and its more specialized subclasses. The java.lang.Object class is always at the top (the root) of any Java inheritance hierarchy, as all classes, with the exception of the Object class itself, inherit (either directly or indirectly) from this class. Figure 7.1 Inheritance Hierarchy WOW! eBook www.wowebook.org Relationships: is-a and has-a Inheritance defines the relationship is-a (also called the superclass–subclass relationship) between a superclass and its subclasses. Thus, an object of a subclass is-a superclass object, and can be used wherever an object of the superclass can be used. This criterion is often employed as a litmus test for choosing inheritance in object-oriented design. It has particular consequences for how objects can be used. An object of the TubeLight class is-an object of the superclass Light. Referring to Figure 7.1, an object of the TubeLight class can be used wherever an object of the superclass Light can be used. The inheritance relationship is transitive: If class B extends class A and class C extends class B, then class C will also inherit from class A via class B. An object of the SpotLightBulb class is-an object of the class Light. The is-a relationship does not hold between peer classes: An object of the LightBulb class is not an object of the class TubeLight, and vice versa. Whereas inheritance defines the relationship is-a between a superclass and its subclasses, aggregation defines the relationship has-a (also called the whole–part relationship) between an instance of a class and its constituents (also called parts). Aggregation comprises the usage of objects. An instance of class Light has (or uses) the following parts: a field to store its wattage (noOfWatts), a field to store whether it is on or off (indicator), and a String object to store its location (denoted by the field reference location). In Java, a composite object cannot contain other objects. It can store only reference values of its constituent objects in its fields. This relationship defines an aggregation hierarchy (also called object hierarchy) that embodies the has a relationship. As explained in §1.7, p. 12, constituent objects can be shared between objects. If their lifetimes are dependent on the lifetime of the composite object, then this relationship is called composition, and implies strong ownership of the parts by the composite object. Inheritance and aggregation are compared in §7.13, p. 331. The Supertype–Subtype Relationship A class defines a reference type, a data type whose objects can be accessed only by references. Therefore the inheritance hierarchy can be regarded as a type hierarchy, embodying the supertype–subtype relationship between reference types. In the context of Java, the supertype–subtype relationship implies that the reference value of a subtype object can be assigned to a supertype reference, because a subtype object can be substituted for a supertype object. This assignment involves a widening reference conversion (§5.1, p. 145), as references are assigned up the inheritance hierarchy. Using the reference types in Example 7.1, the following code assigns the reference value of an object of the subtype TubeLight to the reference light of the supertype Light: Click here to view code image Light light = new TubeLight(); conversion // (1) widening reference An implicit widening conversion takes place under assignment, as the reference value of a narrower type (subtype TubeLight) object is being assigned to a reference of broader type (supertype Light). We can now use the reference light to invoke those methods WOW! eBook www.wowebook.org on the subtype object that are inherited from the supertype Light: Click here to view code image light.switchOn(); // (2) Note that the compiler knows about only the declared type (static type) of the reference light, which is Light, and ensures that only methods from this type can be called using the reference light. However, at runtime, the reference light will refer to an object of the subtype TubeLight when the call to the method switchOn() is executed. It is the type of the object that the reference refers to at runtime that determines which method is executed. The subtype object inherits the switchOn() method from its supertype Light, so this method is executed. The type of the object that the reference refers to at runtime is often called the dynamic type of the reference. One might be tempted to invoke methods exclusive to the TubeLight subtype via the supertype reference light: Click here to view code image light.getTubeLength(); // (3) Not OK. This code will not work, as the compiler does not know which object the reference light will denote at runtime; it merely knows the declared type of the reference. As the declaration of the class Light does not have a method called getTubeLength(), this method call at (3) results in a compile-time error. As we shall see later in this chapter, eliciting subtype-specific behavior using a supertype reference requires a narrowing reference conversion with an explicit cast (§7.11, p. 320). The rest of this chapter elaborates on various aspects of OOP. Understanding them is fundamental in understanding the consequences of the subtype–supertype relationship. 7.2 Overriding Methods Instance Method Overriding Under certain circumstances, a subclass can override instance methods that it inherits from its superclass. Overriding such a method allows the subclass to provide its own implementation of the method. The overridden method in the superclass is not inherited by the subclass. When the method is invoked on an object of the subclass, it is the method implementation in the subclass that is executed. The new method in the subclass must abide by the following rules of method overriding: • The new method definition in the subclass must have the same method signature. In other words, the method name, and the types and the number of parameters, including their order, must be the same as in the overridden method of the superclass. Whether parameters in the overriding method should be final is at the discretion of the subclass (§3.7, p. 86). A method’s signature does not comprise the final modifier of parameters, only their types and order. • The return type of the overriding method can be a subtype of the return type of the WOW! eBook www.wowebook.org overridden method (called covariant return, p. 273). • The new method definition cannot narrow the accessibility of the method, but it can widen it (§4.7, p. 123). • The new method definition can throw either all or none, or a subset of the checked exceptions (including their subclasses) that are specified in the throws clause of the overridden method in the superclass (§6.9, p. 253). These requirements also apply to interfaces, where a subinterface can override abstract and default method declarations from its superinterfaces (§7.6, p. 290). Example 7.2 illustrates overriding, overloading, and hiding of members in a class. Figure 7.2 gives an overview of the two main classes in Example 7.2. The new definition of the energyCost() method at (7) in the subclass TubeLight has the same signature and the same return type as the method at (2) in the superclass Light. The new definition specifies a subset of the exceptions (ZeroHoursException) thrown by the overridden method (the exception class InvalidHoursException is a superclass of NegativeHoursException and ZeroHoursException). The new definition also widens the accessibility (public) from what it was in the overridden definition (protected). The overriding method declares the parameter to be final, but this has no bearing in overriding the method. Figure 7.2 Inheritance Hierarchy for Example 7.2 and Example 7.3 The astute reader will have noticed the @Override annotation preceding the method definition at (7). The compiler will now report an error if the method definition at (7) does not override an inherited method. The annotation helps to ensure that the method definition overrides the inherited method, rather than overloading another method silently. WOW! eBook www.wowebook.org Invocation of the method energyCost() on an object of subclass TubeLight using references of the subclass and the superclass at (15) and (16) results in the new definition at (7) being executed, since both references are aliases of the TubeLight object created at (12). Click here to view code image tubeLight.energyCost(50); light1.energyCost(50); // (15) Invokes method at (7). // (16) Invokes method at (7). Not surprisingly, the invocation of the method energyCost() on an object of superclass Light, using a reference of the superclass at (17), results in the overridden definition at (2) being executed: Click here to view code image light2.energyCost(50); Example 7.2 // (17) Invokes method at (2). Overriding, Overloading, and Hiding Click here to view code image // File: Client2.java // Exceptions class InvalidHoursException extends Exception {} class NegativeHoursException extends InvalidHoursException {} class ZeroHoursException extends InvalidHoursException {} class Light { protected String lightType = “Generic Light”; // (1) Instance field protected double energyCost(int noOfHours) // (2) Instance method throws InvalidHoursException { System.out.print(“>> Light.energyCost(int): “); if (noOfHours < 0) throw new NegativeHoursException(); double cost = 00.20 * noOfHours; System.out.println(“Energy cost for ” + lightType + “: ” + cost); return cost; } public Light makeInstance() { // (3) Instance method System.out.print(“>> Light.makeInstance(): “); return new Light(); } public void showSign() { System.out.print(“>> Light.showSign(): “); System.out.println(“Let there be light!”); } // (4) Instance method public static void printLightType() { // (5) Static method System.out.print(“>> Static Light.printLightType(): “); System.out.println(“Generic Light”); } } //______________________________________________________________________________ class TubeLight extends Light { public static String lightType = “Tube Light”; WOW! eBook www.wowebook.org // (6) Hiding field at (1). @Override public double energyCost(final int noOfHours) // (7) Overriding instance throws ZeroHoursException { // method at (2). System.out.print(“>> TubeLight.energyCost(int): “); if (noOfHours == 0) throw new ZeroHoursException(); double cost = 00.10 * noOfHours; System.out.println(“Energy cost for ” + lightType + “: ” + cost); return cost; } public double energyCost() { // (8) Overloading method at (7). System.out.print(“>> TubeLight.energyCost(): “); double flatrate = 20.00; System.out.println(“Energy cost for ” + lightType + “: ” + flatrate); return flatrate; } @Override public TubeLight makeInstance() { // (9) Overriding instance method at (3). System.out.print(“>> TubeLight.makeInstance(): “); return new TubeLight(); } public static void printLightType() { // (10) Hiding static method at (5). System.out.print(“>> Static TubeLight.printLightType(): “); System.out.println(lightType); } } //______________________________________________________________________________ public class Client2 { public static void main(String[] args) // (11) throws InvalidHoursException { TubeLight tubeLight = new TubeLight(); Light light1 = tubeLight; Light light2 = new Light(); // (12) // (13) Aliases. // (14) System.out.println(“Invoke overridden instance method:”); tubeLight.energyCost(50); // (15) Invokes method at (7). light1.energyCost(50); // (16) Invokes method at (7). light2.energyCost(50); // (17) Invokes method at (2). System.out.println( ”\nInvoke overridden instance method with covariant return:”); System.out.println( light2.makeInstance().getClass()); // (18) Invokes method at (3). System.out.println( tubeLight.makeInstance().getClass()); // (19) Invokes method at (9). System.out.println(“\nAccess hidden field:”); System.out.println(tubeLight.lightType); // (20) Accesses field at (6). System.out.println(light1.lightType); // (21) Accesses field at (1). System.out.println(light2.lightType); // (22) Accesses field at (1). System.out.println(“\nInvoke hidden static method:”); tubeLight.printLightType(); // (23) Invokes method at (10). light1.printLightType(); // (24) Invokes method at (5). light2.printLightType(); // (25) Invokes method at (5). WOW! eBook www.wowebook.org System.out.println(“\nInvoke overloaded method:”); tubeLight.energyCost(); // (26) Invokes method at (8). } } Output from the program: Click here to view code image Invoke overridden instance method: >> TubeLight.energyCost(int): Energy cost for Tube Light: 5.0 >> TubeLight.energyCost(int): Energy cost for Tube Light: 5.0 >> Light.energyCost(int): Energy cost for Generic Light: 10.0 Invoke overridden instance method with covariant return: >> Light.makeInstance(): class Light >> TubeLight.makeInstance(): class TubeLight Access hidden field: Tube Light Generic Light Generic Light Invoke hidden static method: >> Static TubeLight.printLightType(): Tube Light >> Static Light.printLightType(): Generic Light >> Static Light.printLightType(): Generic Light Invoke overloaded method: >> TubeLight.energyCost(): Energy cost for Tube Light: 20.0 Here are a few more facts to note about overriding. First, a subclass must use the keyword super to invoke an overridden method in the superclass (p. 276). Second, a final method cannot be overridden, because the modifier final prevents method overriding. An attempt to override a final method will result in a compile-time error. An abstract method, in contrast, requires the non-abstract subclasses to override the method, so as to provide an implementation. Third, the accessibility modifier private for a method means that the method is not accessible outside the class in which it is defined; therefore, a subclass cannot override it. However, a subclass can give its own definition of such a method, which may have the same signature as the method in its superclass. Fourth, a subclass within the same package as the superclass can override any non-final and non-private methods declared in the superclass. However, a subclass in a different package can override only the non-final methods that are declared as either public or protected in the superclass. Fifth, an instance method in a subclass cannot override a static method in the superclass. The compiler will flag such an attempt as an error. A static method is classspecific and not part of any object, while overriding methods are invoked on behalf of objects of the subclass. However, a static method in a subclass can hide a static method in the superclass, as we shall see (p. 275). Constructors, since they are not methods, cannot be overridden. WOW! eBook www.wowebook.org Covariant in Overriding Methods In Example 7.2, the definition of the method makeInstance() at (9) overrides the method definition at (3). Note that the method signatures are the same, but the return type at (9) is a subtype of the return type at (3). The method at (9) returns an object of the subtype TubeLight, whereas the method at (3) returns an object of the supertype Light. This is an example of covariant return. Depending on whether we call the method makeInstance() on an object of the subtype TubeLight or an object of the supertype Light, the respective method definition will be executed. The code at (18) and (19) illustrates which object is returned by the method, depending on which method definition is executed. Note that covariant return applies only to reference types, not to primitive types. For example, changing the return type of the energyCost() method at (7) to float will result in a compile-time error. There is no supertype–subtype relationship between primitive types. Overriding versus Overloading Method overriding should not be confused with method overloading (§3.2, p. 52). Method overriding always requires the same method signature (name and parameter types) and the same or covariant return types. Overloading occurs when the method names are the same, but the parameter lists differ. Therefore, to overload methods, the parameters must differ in either type, order, or number. As the return type is not a part of the method signature, just having different return types is not enough to overload methods. Only non-final instance methods in the superclass that are directly accessible from the subclass using their simple name can be overridden. Both instance and static methods can be overloaded in the class they are defined in or in a subclass of their class. Invoking an overridden method in the superclass from a subclass requires a special syntax (e.g., the keyword super). This is not necessary for invoking an overloaded method in the superclass from a subclass. If the right kinds of arguments are passed in the method call occurring in the subclass, the overloaded method in the superclass will be invoked. In Example 7.2, the method energyCost() at (2) in class Light is overridden in class TubeLight at (7) and overloaded at (8). When invoked at (26), the overloaded definition at (8) is executed. For overloaded methods, which method implementation will be executed at runtime is determined at compile time (§7.10, p. 316). In contrast, for overridden methods, the method implementation to be executed is determined at runtime (§7.12, p. 329). Table 7.1 provides a comparison between overriding and overloading. WOW! eBook www.wowebook.org Table 7.1 Overriding versus Overloading 7.3 Hiding Members Field Hiding A subclass cannot override inherited fields of the superclass, but it can hide them. The subclass can define fields with the same name as in the superclass. If this is the case, the fields in the superclass cannot be accessed in the subclass by their simple names; therefore, they are not inherited by the subclass. A hidden static field can always be invoked by using the superclass name in the subclass declaration. Additionally, the keyword super can be used in non-static code in the subclass declaration to access hidden static fields (§7.4, p. 276). The following distinction between invoking instance methods on an object and accessing fields of an object must be noted. When an instance method is invoked on an object using a reference, it is the dynamic type of the reference (i.e., the type of the current object denoted by the reference at runtime), not the declared type of the reference, that determines which method implementation will be executed. In Example 7.2 at (15), (16), WOW! eBook www.wowebook.org and (17), this is evident from invoking the overridden method energyCost(): The method from the class corresponding to the current object is executed, regardless of the declared reference type. When a field of an object is accessed using a reference, it is the declared type of the reference, not the type of the current object denoted by the reference, that determines which field will actually be accessed. In Example 7.2 at (20), (21), and (22), this is evident from accessing the hidden field lightType: The field accessed is the one declared in the class corresponding to the declared reference type, regardless of the object denoted by the reference at runtime. In contrast to method overriding, where an instance method cannot override a static method, there are no such restrictions on the hiding of fields. The field lightType is static in the subclass, but not in the superclass. The declared type of the fields need not be the same either—only the field name matters in the hiding of fields. Static Method Hiding A static method in a subclass cannot override an instance method from the superclass, but it can hide a static method from the superclass if the exact requirements for overriding instance methods are fulfilled (§7.2, p. 268). A hidden superclass static method is not inherited. The compiler will flag the code as containing an error if the signatures are the same, but the other requirements regarding return type, throws clause, and accessibility are not met. If the signatures are different, the method name is overloaded, not hidden. A call to a static or final method is bound to a method implementation at compile time (private methods are implicitly final). Example 7.2 illustrates invocation of static methods. Analogous to accessing fields, the static method invoked in (23), (24), and (25) is determined by the declared type of the reference. In (23), the declared reference type is TubeLight; therefore, the static method printLightType() at (10) in this class is invoked. In (24) and (25), the declared reference type is Light, and the hidden static method printLightType() at (5) in that class is invoked. This is borne out by the output from the program. Analogous to hidden fields, a hidden static method can always be invoked by using the superclass name or by using the keyword super in non-static code in the subclass declaration (§7.4, p. 276). Table 7.2 summarizes the consequences when a subclass method has the same signature as a method in the superclass. Table 7.2 Same Signature for Subclass and Superclass Method WOW! eBook www.wowebook.org 7.4 The Object Reference The this reference can be used in non-static code to refer to the current object (§3.2, p. 50). The keyword super, in contrast, can be used in non-static code to access fields and invoke methods from the superclass (Table 4.1, p. 115). The keyword super provides a reference to the current object as an instance of its superclass. In method invocations with super, the method from the superclass is invoked regardless of what the actual type of the current object is or whether the current class overrides the method. This approach is typically used to invoke methods that are overridden, and to access members that are hidden in the subclass. Unlike the this keyword, the super keyword cannot be used as an ordinary reference. For example, it cannot be assigned to other references or cast to other reference types. Example 7.3 uses the classes Light and TubeLight from Example 7.2, which are also shown in Figure 7.2. In Example 7.3, the class NeonLight extends the class TubeLight. The declaration of the method demonstrate() at (11) in the class NeonLight makes use of the super keyword to access members higher up in its inheritance hierarchy. This is the case when the showSign() method is invoked at (12). This method is defined at (4) in the class Light, rather than in the immediate superclass TubeLight of the subclass NeonLight. The overridden method energyCost() at (7) and its overloaded version at (8) in the class TubeLight are invoked, using the object reference super at (13) and (14), respectively. The superclass Light has a field named lightType and a method named energyCost defined at (1) and (2), respectively. One might be tempted to use the syntax super.super.energyCost(20) in the subclass NeonLight to invoke this method, but this is not a valid construct. One might also be tempted to cast the this reference to the class Light and try again, as shown at (15). The output shows that the method energyCost() at (7) in the class TubeLight was executed, not the one from the class Light. The reason is that a cast simply changes the type of the reference (in this case to Light), not the class of the object (which is still NeonLight). Method invocation is determined by the class of the current object, resulting in the inherited method energyCost() in the class TubeLight being executed. There is no way to invoke the method energyCost() in the class Light from the subclass NeonLight, without declaring a reference of the type Light. At (16), the keyword super is used to access the field lightType at (6) in the class TubeLight, but is redundant in this case. At (17), the field lightType from the class Light is accessed successfully by casting the this reference, because it is the type of the reference that determines which field is accessed. From non-static code in a subclass, it is possible to directly access fields in a class higher up in the inheritance hierarchy by casting the this reference. However, it is futile to cast the this reference to invoke instance methods in a class higher up in the inheritance hierarchy, as illustrated earlier for the overridden method energyCost(). Finally, the calls to the static methods at (18) and (19) using the super and this WOW! eBook www.wowebook.org references, respectively, exhibit runtime behavior analogous to accessing fields, as discussed previously. Example 7.3 Using the super Keyword Click here to view code image // File: Client3.java //Exceptions class InvalidHoursException extends Exception {} class NegativeHoursException extends InvalidHoursException {} class ZeroHoursException extends InvalidHoursException {} class Light { protected String lightType = “Generic Light”; // (1) Instance field protected double energyCost(int noOfHours) // (2) Instance method throws InvalidHoursException { System.out.print(“>> Light.energyCost(int): “); if (noOfHours < 0) throw new NegativeHoursException(); double cost = 00.20 * noOfHours; System.out.println(“Energy cost for ” + lightType + “: ” + cost); return cost; } public Light makeInstance() { // (3) Instance method System.out.print(“>> Light.makeInstance(): “); return new Light(); } public void showSign() { System.out.print(“>> Light.showSign(): “); System.out.println(“Let there be light!”); } // (4) Instance method public static void printLightType() { // (5) Static method System.out.print(“>> Static Light.printLightType(): “); System.out.println(“Generic Light”); } } //______________________________________________________________________________ class TubeLight extends Light { public static String lightType = “Tube Light”; // (6) Hiding field at (1). @Override public double energyCost(final int noOfHours) // (7) Overriding instance throws ZeroHoursException { // method at (2). System.out.print(“>> TubeLight.energyCost(int): “); if (noOfHours == 0) throw new ZeroHoursException(); double cost = 00.10 * noOfHours; System.out.println(“Energy cost for ” + lightType + “: ” + cost); return cost; } public double energyCost() { // (8) Overloading method at (7). System.out.print(“>> TubeLight.energyCost(): “); double flatrate = 20.00; System.out.println(“Energy cost for ” + lightType + “: ” + flatrate); WOW! eBook www.wowebook.org return flatrate; } @Override public TubeLight makeInstance() { // (9) Overriding instance method at (3). System.out.print(“>> TubeLight.makeInstance(): “); return new TubeLight(); } public static void printLightType() { // (10) Hiding static method at (5). System.out.print(“>> Static TubeLight.printLightType(): “); System.out.println(lightType); } } //______________________________________________________________________________ class NeonLight extends TubeLight { // … public void demonstrate() // (11) throws InvalidHoursException { super.showSign(); // (12) Invokes method at (4) super.energyCost(50); // (13) Invokes method at (7) super.energyCost(); // (14) Invokes method at (8) ((Light) this).energyCost(50); // (15) Invokes method at System.out.println(super.lightType); // (16) Accesses field at (7) (6) System.out.println(((Light) this).lightType); // (17) Accesses field at (1) super.printLightType(); // (18) Invokes method at (10) ((Light) this).printLightType(); // (19) Invokes method at (5) } } //______________________________________________________________________________ public class Client3 { public static void main(String[] args) throws InvalidHoursException { NeonLight neonRef = new NeonLight(); neonRef.demonstrate(); } } Output from the program: Click here to view code image >> Light.showSign(): Let there be light! >> TubeLight.energyCost(int): Energy cost for Tube Light: 5.0 >> TubeLight.energyCost(): Energy cost for Tube Light: 20.0 >> TubeLight.energyCost(int): Energy cost for Tube Light: 5.0 Tube Light Generic Light >> Static TubeLight.printLightType(): Tube Light >> Static Light.printLightType(): Generic Light WOW! eBook www.wowebook.org Review Questions 7.1 Which of the following statements are true? Select the two correct answers. (a) In Java, the extends clause is used to specify the inheritance relationship. (b) The subclass of a non-abstract class can be declared as abstract. (c) All members of the superclass are inherited by the subclass. (d) A final class can be abstract. (e) A class in which all the members are declared private cannot be declared as public. 7.2 Which of the following statements are true? Select the two correct answers. (a) A class can be extended by only one class. (b) Every Java object has a public method named equals. (c) Every Java object has a public method named length. (d) A class can extend any number of classes. (e) A non-final class can be extended by any number of classes. 7.3 Given the following classes and declarations, which statements are true? Click here to view code image // Classes class Foo { private int i; public void f() { /* … */ } public void g() { /* … */ } } class Bar extends Foo { public int j; public void g() { /* … */ } } // Declarations: Foo a = new Bar(); Bar b = new Bar(); Select the three correct answers. (a) The Bar class is a subclass of Foo. (b) The statement b.f(); is legal. (c) The statement a.j = 5; is legal. (d) The statement a.g(); is legal. WOW! eBook www.wowebook.org (e) The statement b.i = 3; is legal. 7.4 Given classes A, B, and C, where B extends A, and C extends B, and where all classes implement the instance method void doIt(), how can the doIt() method in A be called from an instance method in C? Select the one correct answer. (a) doIt(); (b) super.doIt(); (c) super.super.doIt(); (d) this.super.doIt(); (e) A.this.doIt(); (f) ((A) this).doIt(); (g) It is not possible. 7.5 What would be the result of compiling and running the following program? Click here to view code image public class UserClass { public static void main(String[] args) { B b = new C(); System.out.println(b.max(13, 29)); } } class A { int max(int x, int y) { if (x>y) return x; else return y; } } class B extends A { int max(int x, int y) { return super.max(y, x) - 10; } } class C extends B { int max(int x, int y) { return super.max(x+10, y+10); } } Select the one correct answer. (a) The code will fail to compile. (b) The code will compile, but throw an exception at runtime. (c) The code will compile, and print 13 at runtime. (d) The code will compile, and print 23 at runtime. (e) The code will compile, and print 29 at runtime. (f) The code will compile, and print 39 at runtime. 7.6 Which is the simplest expression that can be inserted at (1), so that the program prints the value of the text field from the Message class? WOW! eBook www.wowebook.org Click here to view code image // File: MyClass.java class Message { // The message that should be printed: String text = “Hello, world!”; } class MySuperclass { Message msg = new Message(); } public class MyClass extends MySuperclass { public static void main(String[] args) { MyClass object = new MyClass(); object.print(); } public void print() { System.out.println( /* (1) INSERT THE SIMPLEST EXPRESSION HERE */ ); } } Select the one correct answer. (a) text (b) Message.text (c) msg.text (d) this.msg.text (e) super.msg.text (f) this.super.msg.text 7.7 What would be the result of compiling and running the following program? Click here to view code image class Vehicle { static public String getModelName() { return “Volvo”; } public long getRegNo() { return 12345; } } class Car extends Vehicle { static public String getModelName() { return “Toyota”; } public long getRegNo() { return 54321; } } public class TakeARide { public static void main(String[] args) { Car c = new Car(); Vehicle v = c; System.out.println(“|” + v.getModelName() + “|” + c.getModelName() + ”|” + v.getRegNo() + “|” + c.getRegNo() + “|”); } } Select the one correct answer. WOW! eBook www.wowebook.org (a) The code will fail to compile. (b) The code will compile, and print |Toyota|Volvo|12345|54321| at runtime. (c) The code will compile, and print |Volvo|Toyota|12345|54321| at runtime. (d) The code will compile, and print |Toyota|Toyota|12345|12345| at runtime. (e) The code will compile, and print |Volvo|Volvo|12345|54321| at runtime. (f) The code will compile, and print |Toyota|Toyota|12345|12345| at runtime. (g) The code will compile, and print |Volvo|Toyota|54321|54321| at runtime. 7.5 Chaining Constructors Using and Constructors are discussed in §3.3, p. 53. Other uses of the keywords this and super can be found in §7.2, p. 268. The Constructor Call Constructors cannot be inherited or overridden. They can be overloaded, but only in the same class. Since a constructor always has the same name as the class, each parameter list must be different when defining more than one constructor for a class. In Example 7.4, the class Light has three overloaded constructors. In the constructor at (3), the this reference is used to access the fields shadowed by the parameters. In the main() method at (4), the appropriate constructor is invoked depending on the arguments in the constructor call, as illustrated by the program output. WOW! eBook www.wowebook.org Example 7.4 Constructor Overloading Click here to view code image // File: DemoConstructorCall.java class Light { // Fields: private int noOfWatts; // wattage private boolean indicator; // on or off private String location; // placement // Constructors: Light() { // (1) No-argument constructor noOfWatts = 0; indicator = false; location = “X”; System.out.println(“Returning from no-argument constructor no. 1.”); } Light(int watts, boolean onOffState) { // (2) noOfWatts = watts; indicator = onOffState; location = “X”; System.out.println(“Returning from constructor no. 2.”); } Light(int noOfWatts, boolean indicator, String location) { // (3) this.noOfWatts = noOfWatts; this.indicator = indicator; this.location = location; System.out.println(“Returning from constructor no. 3.”); } } //______________________________________________________________________________ public class DemoConstructorCall { public static void main(String[] args) { // (4) System.out.println(“Creating Light object no. 1.”); Light light1 = new Light(); System.out.println(“Creating Light object no. 2.”); Light light2 = new Light(250, true); System.out.println(“Creating Light object no. 3.”); Light light3 = new Light(250, true, “attic”); } } Output from the program: Click here to view code image Creating Light Returning from Creating Light Returning from Creating Light Returning from object no. 1. no-argument constructor no. 1. object no. 2. constructor no. 2. object no. 3. constructor no. 3. Example 7.5 illustrates the use of the this() construct, which is used to implement local chaining of constructors in the class when an instance of the class is created. The first two constructors at (1) and (2) from Example 7.4 have been rewritten using the this() construct in Example 7.5 at (1) and (2), respectively. The this() construct can be regarded as being locally overloaded, since its parameters (and hence its signature) can vary, as shown in the body of the constructors at (1) and (2). The this() call invokes the WOW! eBook www.wowebook.org local constructor with the corresponding parameter list. In the main() method at (4), the appropriate constructor is invoked depending on the arguments in the constructor call when each of the three Light objects are created. Calling the no-argument constructor at (1) to create a Light object results in the constructors at (2) and (3) being executed as well. This is confirmed by the output from the program. In this case, the output shows that the constructor at (3) completed first, followed by the constructor at (2), and finally by the no-argument constructor at (1) that was called first. Bearing in mind the definition of the constructors, the constructors are invoked in the reverse order; that is, invocation of the no-argument constructor immediately leads to invocation of the constructor at (2) by the call this(0, false), and its invocation leads to the constructor at (3) being called immediately by the call this(watt, ind, "X"), with the completion of the execution in the reverse order of their invocation. Similarly, calling the constructor at (2) to create an instance of the Light class results in the constructor at (3) being executed as well. Java requires that any this() call must occur as the first statement in a constructor. The this() call can be followed by any other relevant code. This restriction is due to Java’s handling of constructor invocation in the superclass when an object of the subclass is created. This mechanism is explained in the next subsection. WOW! eBook www.wowebook.org Example 7.5 The this() Constructor Call Click here to view code image // File: DemoThisCall.java class Light { // Fields: private int noOfWatts; private boolean indicator; private String location; // Constructors: Light() { // (1) No-argument constructor this(0, false); System.out.println(“Returning from no-argument constructor no. 1.”); } Light(int watt, boolean ind) { // (2) this(watt, ind, “X”); System.out.println(“Returning from constructor no. 2.”); } Light(int noOfWatts, boolean indicator, String location) { // (3) this.noOfWatts = noOfWatts; this.indicator = indicator; this.location = location; System.out.println(“Returning from constructor no. 3.”); } } //______________________________________________________________________________ public class DemoThisCall { public static void main(String[] args) { // (4) System.out.println(“Creating Light object no. 1.”); Light light1 = new Light(); // (5) System.out.println(“Creating Light object no. 2.”); Light light2 = new Light(250, true); // (6) System.out.println(“Creating Light object no. 3.”); Light light3 = new Light(250, true, “attic”); // (7) } } Output from the program: Click here to view code image Creating Light Returning from Returning from Returning from Creating Light Returning from Returning from Creating Light Returning from object no. 1. constructor no. 3. constructor no. 2. no-argument constructor no. 1. object no. 2. constructor no. 3. constructor no. 2. object no. 3. constructor no. 3. WOW! eBook www.wowebook.org The Constructor Call The super() construct is used in a subclass constructor to invoke a constructor in the immediate superclass. This allows the subclass to influence the initialization of its inherited state when an object of the subclass is created. A super() call in the constructor of a subclass will result in the execution of the relevant constructor from the superclass, based on the signature of the call. Since the superclass name is known in the subclass declaration, the compiler can determine the superclass constructor invoked from the signature of the parameter list. A constructor in a subclass can access the class’s inherited members by their simple names. The keyword super can also be used in a subclass constructor to access inherited members via its superclass. One might be tempted to use the super keyword in a constructor to specify initial values for inherited fields. However, the super() construct provides a better solution to initialize the inherited state. In Example 7.6, the constructor at (3) of the class Light has a super() call (with no arguments) at (4). Although the constructor is not strictly necessary, as the compiler will insert one—as explained later—it is included here for expositional purposes. The constructor at (6) of the class TubeLight has a super() call (with three arguments) at (7). This super() call will match the constructor at (3) of the superclass Light. This is evident from the program output. Example 7.6 The super() Constructor Call Click here to view code image // File: Chaining.java class Light { // Fields: private int noOfWatts; private boolean indicator; private String location; // Constructors: Light() { // (1) No-argument constructor this(0, false); System.out.println( “Returning from no-argument constructor no. 1 in class Light”); } Light(int watt, boolean ind) { // (2) this(watt, ind, “X”); System.out.println( “Returning from constructor no. 2 in class Light”); } Light(int noOfWatts, boolean indicator, String location) { // (3) super(); // (4) this.noOfWatts = noOfWatts; this.indicator = indicator; this.location = location; System.out.println( “Returning from constructor no. 3 in class Light”); } } //______________________________________________________________________________ class TubeLight extends Light { WOW! eBook www.wowebook.org // Instance variables: private int tubeLength; private int colorNo; // Constructors: TubeLight(int tubeLength, int colorNo) { // (5) this(tubeLength, colorNo, 100, true, “Unknown”); System.out.println( “Returning from constructor no. 1 in class TubeLight”); } TubeLight(int tubeLength, int colorNo, int noOfWatts, boolean indicator, String location) { // (6) super(noOfWatts, indicator, location); // (7) this.tubeLength = tubeLength; this.colorNo = colorNo; System.out.println( “Returning from constructor no. 2 in class TubeLight”); } } //______________________________________________________________________________ public class Chaining { public static void main(String[] args) { System.out.println(“Creating a TubeLight object.”); TubeLight tubeLightRef = new TubeLight(20, 5); // (8) } } Output from the program: Click here to view code image Creating a TubeLight object. Returning from constructor no. 3 in class Light Returning from constructor no. 2 in class TubeLight Returning from constructor no. 1 in class TubeLight The super() construct has the same restrictions as the this() construct: If used, the super() call must occur as the first statement in a constructor, and it can only be used in a constructor declaration. This implies that this() and super() calls cannot both occur in the same constructor. The this() construct is used to chain constructors in the same class. The constructor at the end of such a chain can invoke a superclass constructor using the super() construct. Just as the this() construct leads to chaining of constructors in the same class, so the super() construct leads to chaining of subclass constructors to superclass constructors. This chaining behavior guarantees that all superclass constructors are called, starting with the constructor of the class being instantiated, all the way to the top of the inheritance hierarchy, which is always the Object class. Note that the body of the constructor is executed in the reverse order to the call order, as the super() call can occur only as the first statement in a constructor. This order of execution ensures that the constructor from the Object class is completed first, followed by the constructors in the other classes down to the class being instantiated in the inheritance hierarchy. It is called (subclass–superclass) constructor chaining. The output from Example 7.6 clearly illustrates this chain of events when an object of the class TubeLight is created. If a constructor at the end of a this() chain (which may not be a chain at all if no WOW! eBook www.wowebook.org this() call is invoked) does not have an explicit call to super(), the call super() (without the parameters) is implicitly inserted by the compiler to invoke the no-argument constructor of the superclass. In other words, if a constructor has neither a this() call nor a super() call as its first statement, the compiler inserts a super() call to the noargument constructor in the superclass. The code Click here to view code image class A { A() {} // … } class B extends A { // … } // No-argument constructor. // No constructors. is equivalent to Click here to view code image class A { A() { super(); } inserted. // … } class B extends A { B() { super(); } // … } // (1) Call to no-argument superclass constructor // (2) Default constructor inserted. where the compiler inserts a super() call in the no-argument constructor for class A at (1) and inserts the default constructor for class B at (2). The super() call at (2) will result in a call to the no-argument constructor in B at (1), and the super() call at (1) will result in a call to the no-argument constructor in the superclass of A—that is, the Object class. If a superclass defines just non-zero argument constructors (i.e., only constructors with parameters), its subclasses cannot rely on the implicit super() call being inserted. This will be flagged as a compile-time error. The subclasses must then explicitly call a superclass constructor, using the super() construct with the right arguments. Click here to view code image class NeonLight extends TubeLight { // Field String sign; NeonLight() { super(10, 2, 100, true, “Roof-top”); sign = “All will be revealed!”; } // … // (1) // (2) Cannot be commented out. } The preceding declaration of the subclass NeonLight provides a no-argument constructor at (1). The call of the constructor at (2) in the superclass TubeLight cannot be omitted. If it is omitted, any insertion of a super() call (with no arguments) in this constructor will try to match a no-argument constructor in the superclass TubeLight, WOW! eBook www.wowebook.org which provides only non-zero argument constructors. The class NeonLight will not compile unless an explicit valid super() call is inserted at (2). If the superclass provides just non-zero argument constructors (i.e., it does not have a noargument constructor), this has implications for its subclasses. A subclass that relies on its default constructor will fail to compile, because the default constructor of the subclass will attempt to call the (nonexistent) no-argument constructor in the superclass. A constructor in a subclass must explicitly use the super() call, with the appropriate arguments, to invoke a non-zero argument constructor in the superclass. This call is necessary because the constructor in the subclass cannot rely on an implicit super() call to the noargument constructor in the superclass. Review Questions 7.8 Which constructors can be inserted at (1) in MySub without causing a compiletime error? Click here to view code image class MySuper { int number; MySuper(int i) { number = i; } } class MySub extends MySuper { int count; MySub(int count, int num) { super(num); this.count = count; } // (1) INSERT CONSTRUCTOR HERE } Select the one correct answer. (a) MySub() {} (b) MySub(int count) { this.count = count; } (c) MySub(int count) { super(); this.count = count; } (d) MySub(int count) { this.count = count; super(count); } (e) MySub(int count) { this(count, count); } (f) MySub(int count) { super(count); this(count, 0); } 7.9 Which of the following statements is true? Select the one correct answer. (a) A super() or this() call must always be provided explicitly as the first statement in the body of a constructor. (b) If both a subclass and its superclass do not have any declared constructors, the WOW! eBook www.wowebook.org implicit default constructor of the subclass will call super() when run. (c) If neither super() nor this() is specified as the first statement in the body of a constructor, this() will implicitly be inserted as the first statement. (d) If super() is the first statement in the body of a constructor, this() can be declared as the second statement. (e) Calling super() as the first statement in the body of a constructor of a subclass will always work, since all superclasses have a default constructor. 7.10 What will the following program print when run? Click here to view code image public class MyClass { public static void main(String[] args) { B b = new B(“Test”); } } class A { A() { this(“1”, “2”); } A(String s, String t) { this(s + t); } A(String s) { System.out.println(s); } } class B extends A { B(String s) { System.out.println(s); } B(String s, String t) { this(t + s + “3”); } B() { super(“4”); }; } Select the one correct answer. (a) It will just print Test. (b) It will print Test followed by Test. (c) It will print 123 followed by Test. (d) It will print 12 followed by Test. (e) It will print 4 followed by Test. 7.6 Interfaces Extending classes using single implementation inheritance creates new class types. A superclass reference can refer to objects of its own type and its subclasses strictly according to the inheritance hierarchy. Because this relationship is linear, it rules out multiple implementation inheritance, in which a subclass inherits from more than one superclass. Instead Java provides interfaces, which not only allow new named reference types to be introduced, but also permit multiple interface inheritance. WOW! eBook www.wowebook.org Defining Interfaces A top-level interface has the following simplified syntax, which will suffice for the purposes of this book: Click here to view code image accessibility_modifier interface interface_name extends_interface_clause // Interface header { // Interface body abstract_method_declarations default_method_declarations static_method_declarations constant_declarations } In the interface header, the name of the interface is preceded by the keyword interface. The interface name can also include a list of formal type parameters for declaring a generic interface. In addition, the interface header can specify the following information: • The accessibility modifier must be public, and the lack of an accessibility modifier implies package accessibility, as one would expect (§4.5, p. 118). • The extends interface clause specifies a comma-separated list of any superinterfaces that the interface extends (p. 294). The interface body can contain member declarations that include any of the following: • Abstract method declarations (p. 291) • Default method declarations (p. 297) • Static method declarations (p. 300) • Constant declarations (p. 302) An interface is abstract by definition, which means that it cannot be instantiated. Declaring an interface as abstract is superfluous and seldom done in practice. It is the only non-accessibility modifier that can be specified for a top-level interface (apart from the keyword strictfp). The member declarations can appear in any order in the interface body, which can be empty. Since interfaces are meant to be implemented by classes, interface members implicitly have public accessibility and the public modifier can be omitted. The following declaration is an example of a bare-bones interface that has an empty body: interface Playable { } Interfaces with empty bodies can be used as markers to tag classes as having a certain property or behavior. Such interfaces are also called ability interfaces. The Java SE platform API provides several examples of such marker interfaces—namely, java.lang.Cloneable, java.io.Serializable, and WOW! eBook www.wowebook.org java.util.EventListener. Abstract Methods in Interfaces An interface defines a contract by specifying a set of abstract and default method declarations, but provides implementations only for the default methods—not for the abstract methods. The abstract methods in an interface are all implicitly abstract and public by virtue of their definitions. Only the modifiers abstract and public are allowed, but these are invariably omitted. An abstract method declaration has the following simple form in a top-level interface: Click here to view code image return_type method_name (formal_parameter_list) throws_clause; An abstract method declaration is essentially a method header terminated by a semicolon (;). Note that an abstract method is an instance method whose implementation will be provided by a class that implements the interface in which the abstract method is declared. The throws clause is discussed in §6.9, p. 251. The interface Playable shown next declares an abstract method startPlaying(). That it is public and abstract is implicitly implied. Click here to view code image interface Playable { void startPlaying(); } // Abstract method: no implementation An interface that has no direct superinterfaces implicitly declares a public abstract method for each public instance method in the java.lang.Object class. In contrast to the syntax of abstract methods in top-level interfaces, abstract methods in top-level classes must be explicitly specified with the keyword abstract, and can have public, protected, and package accessibility (§4.8, p. 136). Functional interfaces, meaning interfaces with a single abstract method, are discussed together with lambda expressions in §10.2, p. 442. The rest of this chapter provides numerous examples of using interfaces. Implementing Interfaces A class can implement, wholly or partially, zero or more interfaces. A class specifies the interfaces it implements as a comma-separated list of unique interface names in an implements clause in the class header. The interface methods must all have public accessibility when implemented in the class (or its subclasses). A class can neither narrow the accessibility of an interface method nor specify new exceptions in the method’s throws clause, as attempting to do so would amount to altering the interface’s contract, which is illegal. The criteria for overriding methods also apply when implementing abstract methods (§7.2, p. 268). A class can provide implementations of methods declared in an interface. To reap the benefits of interfaces, however, the class must also specify the interface name in its WOW! eBook www.wowebook.org implements clause. In Example 7.7, the class StackImpl implements the interface IStack. It both specifies the interface name using the implements clause in its class header at (2) and provides the implementation for the abstract methods in the interface at (3) and (4). Changing the public accessibility of these methods in the class will result in a compiletime error, as this would narrow their accessibility. Example 7.7 Implementing Interfaces Click here to view code image // File: RetailSeller.java interface IStack { // (1) void push(Object item); Object pop(); } //______________________________________________________________________________ class StackImpl implements IStack { // (2) protected Object[] stackArray; protected int tos; // top of stack public StackImpl(int capacity) { stackArray = new Object[capacity]; tos = -1; } @Override public void push(Object item) { stackArray[++tos] = item; } @Override public Object pop() { Object objRef = stackArray[tos]; stackArray[tos] = null; tos—; return objRef; } // (3) // (4) public Object peek() { return stackArray[tos]; } } //______________________________________________________________________________ interface ISafeStack extends IStack { // (5) boolean isEmpty(); boolean isFull(); } //______________________________________________________________________________ class SafeStackImpl extends StackImpl implements ISafeStack { // (6) public SafeStackImpl(int capacity) { super(capacity); } @Override public boolean isEmpty() { return tos < 0; } // (7) @Override public boolean isFull() { return tos >= stackArray.length-1; }// (8) } //______________________________________________________________________________ public class StackUser { public static void main(String[] args) { SafeStackImpl safeStackRef = new SafeStackImpl(10); StackImpl stackRef = safeStackRef; WOW! eBook www.wowebook.org // (9) ISafeStack IStack Object isafeStackRef = safeStackRef; istackRef = safeStackRef; objRef = safeStackRef; safeStackRef.push(“Dollars”); stackRef.push(“Kroner”); System.out.println(isafeStackRef.pop()); System.out.println(istackRef.pop()); System.out.println(objRef.getClass()); // (10) } } Output from the program: Kroner Dollars class SafeStackImpl A class can choose to implement only some of the abstract methods of its interfaces (i.e., give a partial implementation of its interfaces). The class must then be declared as abstract (§4.6, p. 120). Note that abstract methods cannot be declared as static, because they comprise the contract fulfilled by the objects of the class implementing the interface. Abstract methods are always implemented as instance methods. The interfaces that a class implements and the classes that it extends (directly or indirectly) are called supertypes of the class. Conversely, the class is a subtype of its supertypes. Classes implementing interfaces introduce multiple interface inheritance into their implementation inheritance hierarchy. Even so, regardless of how many interfaces a class implements directly or indirectly, it provides just a single implementation of any abstract method declared in multiple interfaces. Single implementation of an abstract method is illustrated by the following code, where the Worker class at (5) provides only one implementation of the doIt() method that is declared in both interfaces, at (1) and (2). The class Worker fulfills the contract for both interfaces, as the doIt() method declarations at (1) and (2) have the same method signature and return type. However, the class Combined at (3) declares that it implements the two interfaces, but does not provide any implementation of the doIt() method; consequently, it must be declared as abstract. Click here to view code image interface IA { int doIt(); } // (1) interface IB { int doIt(); } // (2) abstract class Combined implements IA, IB { } // (3) public class Worker implements IA, IB { @Override public int doIt() { return 0; } } // (4) // (5) If the doIt() methods in the two interfaces at (1) and (2) had the same signatures but different return types, the Worker class would not be able to implement both interfaces. This is illustrated by the next code snippet. The doIt() methods at (1) and (2) have the same signature, but different return types. The Worker class provides two WOW! eBook www.wowebook.org implementations of the doIt() method at (5) and (6), which results in compile-time errors, because a class cannot have two methods with the same signature but different return types. Removing either implementation from the Worker class will be flagged as a compile-time error, because the Worker class will not be implementing both interfaces. There is no way the Worker class can implement both interfaces, given the declarations shown in the code. In addition, the abstract class Combined at (3) will not compile, because it will be inheriting two methods with conflicting abstract method declarations. In fact, the compiler complains of duplicate methods. Click here to view code image interface IA { int doIt(); } // (1) interface IB { double doIt(); } // (2) abstract class Combined implements IA, IB { } error. // (3) Compile-time public class LameWorker implements IA, IB { @Override public int doIt() { return 0; } error. @Override public double doIt() { error. System.out.println(“Sorry!”); return = 0.0; } } // (4) // (5) Compile-time // (6) Compile-time Extending Interfaces An interface can extend other interfaces, using the extends clause. Unlike when extending classes, an interface can extend several interfaces. The interfaces extended by an interface (directly or indirectly) are called superinterfaces. Conversely, the interface is a subinterface of its superinterfaces. Since interfaces define new reference types, superinterfaces and subinterfaces are also supertypes and subtypes, respectively. A subinterface inherits from its superinterfaces, all members of those superinterfaces, except for the following: • Abstract or default methods that it overrides (p. 297) • Any static methods declared in its superinterfaces (p. 300) • Any constants that it hides (p. 302) Barring any conflicts, a subinterface inherits abstract and default method declarations that are not overridden, as well as constants that it does not hide in its superinterfaces. Abstract, static, and default method declarations can also be overloaded, analogous to method overloading in classes. Example 7.7 illustrates the relationships between classes and interfaces. In Example 7.7, the interface ISafeStack extends the interface IStack at (5). The class SafeStackImpl both extends the StackImpl class and implements the WOW! eBook www.wowebook.org ISafeStack interface at (6). Both the implementation and the interface inheritance hierarchies for classes and interfaces defined in Example 7.7 are shown in Figure 7.3. Figure 7.3 Inheritance Hierarchies In UML, an interface resembles a class. One way to differentiate between them is to use an «interface» stereotype, as in Figure 7.3. Interface inheritance is depicted in a similar manner to implementation inheritance, but is indicated by an unbroken inheritance arrow. Thinking in terms of types, every reference type in Java is a subtype of the Object class. In turn, any interface type is also a subtype of the Object class, but it does not inherit from the Object class. An interface that has no direct superinterfaces implicitly declares a public abstract method for each public instance method in the Object class. These abstract method declarations are inherited by all subinterfaces of such an interface. Note that this does not mean the implementation is inherited. The implicit public abstract method declarations in an interface allow public instance methods in the Object class to be invoked on objects referred to by an interface reference. All classes implement these methods, whether they are inherited or overridden from the Object class. Any interface can also provide explicit public abstract method declarations for non-final public instance methods in the Object class. Click here to view code image interface IStack { void push(Object item); // (1) WOW! eBook www.wowebook.org Object pop(); @Override boolean equals(Object other); // public method in Object class. @Override String toString(); // public method in Object class. //@Override Class getClass(); // Compile-time error! final method in Object class. } It is instructive to consider how the class SafeStackImpl implements the IStack interface: It inherits the implementations of the push() and pop() methods from its superclass StackImpl, which itself implements the IStack interface in which these two methods are declared. The class SafeStackImpl also implements the IStack interface via the ISafeStack interface. The class SafeStackImpl provides its own implementation of the isFull() and isEmpty() methods declared in the ISafeStack interface, and has inherited implementations of the push() and pop() methods whose declarations the ISafeStack interface inherits from its superinterface IStack. This is readily evident from the diamond shape of the inheritance hierarchy in Figure 7.3. Note that there is only one single implementation inheritance into the class SafeStackImpl—from its superclass StackImpl. Java does not support multiple implementation inheritance. The association between a class and any interface it implements is called a realization in UML. In Figure 7.3, there are three realizations: The class SafeStackImpl implements the ISafeStack interface and also implicitly implements the IStack interface, and the class StackImpl implements the IStack interface. Thus, three different inheritance relations are at work when defining inheritance among classes and interfaces: 1. Single implementation inheritance hierarchy between classes: a class extends another class (subclasses–superclasses). 2. Multiple inheritance hierarchy between interfaces: an interface extends other interfaces (subinterfaces–superinterfaces). 3. Multiple interface inheritance hierarchy between classes and interfaces: a class implements interfaces (realization). Interface References Although interfaces cannot be instantiated, references of an interface type can be declared. The reference value of an object can be assigned to references of the object’s supertypes. In Example 7.7, an object of the class SafeStackImpl is created in the main() method of the class StackUser at (9). The reference value of the object is assigned to references of all the object’s supertypes, which are used to manipulate the object. The references are aliases to the same SafeStackImpl object, but they can only be used to manipulate this object as an object of the reference type. For example, calling the method isFull() on this object using the stackRef reference will be flagged as a compiletime error, as the class StackImpl does not provide such a method. Polymorphic behavior of supertype references is discussed in §7.12, p. 329. WOW! eBook www.wowebook.org Default Methods in Interfaces Only interfaces can define default methods. A default method is an instance method declared with the keyword default and whose implementation is provided by the interface. However, a default method in a top-level interface always has public accessibility, whether the keyword public is specified or not. Click here to view code image default return_type method_name (formal_parameter_list) throws_clause { implementaion_of_method_body } A class implementing an interface can optionally decide to override any default method in the interface. If the class does not override a default method to provide a new implementation, the default implementation provided by the interface is inherited by the class. No other non-accessibility modifiers, such as abstract, final, or static, are allowed in a default method declaration, except the keyword strictfp. A default method is not abstract because it provides an implementation; is not final because it can be overridden; and is not static because it can be invoked only on instances of a class that implements the interface in which the default method is declared. Example 7.8 illustrates the use of default methods. The default method printSlogan() at (1) in the interface ISlogan is overridden at (2) in the class JavaGuru, and inherited by the class JavaGeek at (3). The output from the program shows that this is the case. WOW! eBook www.wowebook.org Example 7.8 Default Methods in Interfaces Click here to view code image // File: JavaParty.java interface ISlogan { default void printSlogan() { // (1) System.out.println(“Happiness is getting certified!”); } } //_______________________________________________________________________________ class JavaGuru implements ISlogan { @Override public void printSlogan() { // (2) overrides (1) System.out.println(“Happiness is catching all the exceptions!”); } } //_______________________________________________________________________________ class JavaGeek implements ISlogan { } // (3) inherits (1) //_______________________________________________________________________________ public class JavaParty { public static void main(String[] args) { JavaGuru guru = new JavaGuru(); guru.printSlogan(); // (4) JavaGeek geek = new JavaGeek(); geek.printSlogan(); // (5) } } Output from the program: Click here to view code image Happiness is catching all the exceptions! Happiness is getting certified! The keyword default in the context of a default method should not be confused with default or package accessibility of a method in a class, which is implied in the absence of any accessibility modifier. The keyword default is also used only for default method declarations in interfaces that provide an implementation for such methods, and not by classes that override them. Overriding a default method from an interface does not necessarily imply that a new implementation is being provided. The default method can also be overridden by providing an abstract method declaration, as illustrated by the next code snippet. The default method printSlogan() at (1) in the interface ISlogan is overridden by an abstract method declaration at (2) and (3) in the interface INewSlogan and the abstract class JavaMaster, respectively. This strategy effectively forces the subtypes of the interface INewSlogan and of the abstract class JavaMaster to provide a new concrete implementation for the method, as one would expect for an abstract method. Click here to view code image interface ISlogan { default void printSlogan() { // (1) Default method. System.out.println(“Happiness is getting certified!”); } } WOW! eBook www.wowebook.org interface INewSlogan extends ISlogan { @Override abstract void printSlogan(); // (2) overrides (1) with abstract method. } abstract class JavaMaster implements ISlogan { @Override public abstract void printSlogan(); // (3) overrides (1) with abstract method. } Problems with multiple inheritance can arise when default methods are inherited from multiple interfaces. Example 7.9 illustrates one such case. The default method printSlogan() is declared at (1) and (2) in the interfaces ICheapSlogan and IFunny-Slogan, respectively. The two method declarations have the same signature. The interface IAvailableSlogan at (3) tries to extend the two interfaces ICheapSlogan and IFunnySlogan. If this was allowed, the interface IAvailableSlogan would inherit two implementations of methods that have the same signature, which of course is not allowed—so the compiler flags it as an error. By the same token, the compiler flags an error at (4), indicating that the abstract class Wholesaler cannot inherit two methods with the same signature. The way out of this dilemma is to override the conflicting methods. The abstract class RetailSeller that implements the interfaces ICheapSlogan and IFunnySlogan overrides the conflicting methods by providing an abstract method declaration of the default method printSlogan() at (5). Similarly, the class NetSeller that implements the interfaces ICheapSlogan and IFunnySlogan overrides the conflicting methods by providing an implementation of the default method printSlogan() at (6). The upshot of this solution is that clients of the classes RetailSeller and NetSeller now have to deal with the new declarations of the printSlogan() method provided by these classes. One such client is the class MutlipleInheritance at (10), which calls the method printSlogan() on an instance of class NetSeller at (11). Not surprisingly, the program output shows that the method in the NetSeller class was executed. What if the class NetSeller wanted to invoke the default method printSlogan() in the interfaces it implements? The overridden default method can be called by the overriding subtype (in this case, NetSeller) using the keyword super in conjunction with the fully qualified name of the interface and the name of the method, as shown at (8) and (9). This syntax works for calling overridden default methods in the direct superinterface, but not at any higher level in the inheritance hierarchy. The class NetSeller can call only default methods in its direct superinterfaces ICheapSlogan and IFunnySlogan. It would not be possible for the class NetSeller to call any default methods inherited by these superinterfaces, even if they had any. WOW! eBook www.wowebook.org Example 7.9 Default Methods and Multiple Inheritance Click here to view code image // File: MultipleInheritance.java interface ICheapSlogan { default void printSlogan() { // (1) System.out.println(“Override, don’t overload.”); } } //_______________________________________________________________________________ interface IFunnySlogan { default void printSlogan() { // (2) System.out.println(“Catch exceptions, not bugs.”); } } //_______________________________________________________________________________ interface IAvailableSlogan // (3) Compile-time error. extends ICheapSlogan, IFunnySlogan { } //_______________________________________________________________________________ abstract class Wholesaler // (4) Compile-time error. implements ICheapSlogan, IFunnySlogan { } //_______________________________________________________________________________ abstract class RetailSeller implements ICheapSlogan, IFunnySlogan { @Override // Abstract method. public abstract void printSlogan(); // (5) overrides (1) and (2). } //_______________________________________________________________________________ class NetSeller implements ICheapSlogan, IFunnySlogan { @Override // Concrete method. public void printSlogan() { // (6) overrides (1) and (2). System.out.println(“Think outside of the class.”); } public void invokeDirect() { // (7) ICheapSlogan.super.printSlogan(); // (8) calls ICheapSlogan.printSlogan() IFunnySlogan.super.printSlogan(); // (9) calls IFunnySlogan.printSlogan() } } //_______________________________________________________________________________ public class MultipleInheritance { // (10) public static void main(String[] args) { NetSeller seller = new NetSeller(); seller.printSlogan(); // (11) seller.invokeDirect(); } } Output from the program: Think outside of the class. Override, don’t overload. Catch exceptions, not bugs. WOW! eBook www.wowebook.org Static Methods in Interfaces An interface can also declare static methods. Static method declarations in a top-level interface are analogous to static method declarations in a class (§4.8, p. 132). However, a static method in a top-level interface always has public accessibility, whether the keyword public is specified or not. As with static methods in a class, the keyword static is mandatory; otherwise, the code will not compile. Without the keyword static, the method declaration is identical to that of an instance method, but such instance methods cannot be declared in an interface. Click here to view code image static return_type method_name (formal_parameter_list) throws_clause { implementaion_of_method_body } Static methods in an interface differ from those in a class in one important respect: Static methods in an interface cannot be inherited, unlike static methods in classes. This essentially means that such methods cannot be invoked directly by calling the method in subinterfaces or in classes that extend or implement interfaces containing such methods, respectively. A static method can be invoked only by using its qualified name—that is, the name of the interface in which it is declared—together with its simple name, using the dot notation (.). Example 7.10 illustrates the use of static methods in interfaces. The static method getNumOfCylinders() at (1) is declared in the IMaxEngineSize interface. There are two implementations of the method getEngineSize(), at (2) and (3), in the interface IMaxEngineSize and its subinterface INewEngineSize, respectively. The class CarRace implements the subinterface INewEngineSize. It is not possible to invoke the method getNumOfCylinders() directly, as shown at (4). It is also not possible to invoke directly the method getEngineSize() from either interface, as shown at (6). The respective implementations of the static methods can be invoked only by using their qualified names, as shown at (5), (7) and (8). It does not matter that a static method is redeclared in a subinterface; the static method is not inherited. Each static method declaration in Example 7.10 is a new method. WOW! eBook www.wowebook.org Example 7.10 Static Methods in Interfaces Click here to view code image // File: CarRace.java import static java.lang.System.out; interface IMaxEngineSize { static int getNumOfCylinders() { return 6; } // (1) Static method static double getEngineSize() { return 1.6; } // (2) Static method } //_______________________________________________________________________________ interface INewEngineSize extends IMaxEngineSize { static double getEngineSize() { return 2.4; } // (3) Static method } //_______________________________________________________________________________ public class CarRace implements INewEngineSize { public static void main(String[] args) { // out.println(“No. of cylinders: ” + // getNumOfCylinders()); // (4) Compile-time error. out.println(“No. of cylinders: ” + IMaxEngineSize.getNumOfCylinders()); // (5) // out.println(“Engine size: ” + getEngineSize()); // (6) Compile-time error. out.println(“Max engine size: ” + IMaxEngineSize.getEngineSize()); // (7) out.println(“New engine size: ” + INewEngineSize.getEngineSize()); // (8) } } Output from the program: No. of cylinders: 6 Max engine size: 1.6 New engine size: 2.4 Constants in Interfaces An interface can also define named constants. Naming conventions recommend using uppercase letters for their names, with multiple words in the name being separated by underscores. Such constants are defined by field declarations and are considered to be public, static, and final. These modifiers can be omitted from the declaration. Such a constant must be initialized with an initializer expression. An interface constant can be accessed by any client (a class or interface) using its qualified name, regardless of whether the client extends or implements its interface. However, if the client is a class that implements this interface or is an interface that extends this interface, then the client can also access such constants directly by their simple names. Such a client inherits the interface constants. Typical usage of constants in interfaces is illustrated in Example 7.11, showing access both by the constant’s simple name and its qualified name in the print statements at (1) and (2), respectively. WOW! eBook www.wowebook.org Example 7.11 Constants in Interfaces Click here to view code image // File: Client.java interface Constants { double PI_APPROXIMATION = 3.14; String AREA_UNITS = “sq.cm.”; String LENGTH_UNITS = “cm.”; } //______________________________________________________________________________ public class Client implements Constants { public static void main(String[] args) { double radius = 1.5; // (1) Using simple name: System.out.printf(“Area of circle is %.2f %s%n”, PI_APPROXIMATION * radius*radius, AREA_UNITS); // (2) Using qualified name: System.out.printf(“Circumference of circle is %.2f %s%n”, 2.0 * Constants.PI_APPROXIMATION * radius, Constants.LENGTH_UNITS); } } Output from the program: Click here to view code image Area of circle is 7.06 sq.cm. Circumference of circle is 9.42 cm. Extending an interface that has constants is analogous to extending a class having static variables. This is illustrated by Figure 7.4 and Example 7.12. Note the diamond shape of the inheritance hierarchy, indicating the presence of multiple inheritance paths through which constants can be inherited. The constants IDLE and BUSY at (1) and (2) in the interface IBaseStates are inherited by the subinterface IAllStates via both the interface IExtStatesA and the interface IExtStatesB. In such cases, the constant is considered to be inherited only once, and can be accessed by its simple name, as shown at (12) in Example 7.12. WOW! eBook www.wowebook.org Figure 7.4 Inheritance Relationships for Interface Constants Constants can be hidden by the subinterfaces. The declaration of the constant BLOCKED at (6) in the interface IAllStates hides the declaration of the constant at (2) in the interface IBaseStates. The new declaration can be accessed by its simple name in a class implementing the interface IAllStates, as shown at (10) in Example 7.12. The hidden constant declaration can always be accessed by using its qualified name as shown at (11) in Example 7.12. In the case of multiple inheritance of interface constants, any name conflicts can be resolved by using the qualified name to access the constants. This is illustrated by the constant DISMANTLED, which is declared in both the IExtStatesA and IExtStatesB interfaces. Both declarations are inherited by the subinterface IAllStates. Such declarations are said to be ambiguous. The compiler will report an error only if such constants are accessed by their simple names, as shown at (7) and (8) for the constant DISMANTLE. Only the qualified name can be used to disambiguate such constants and resolve the conflict, as shown at (7a) and (8a) for the constant DISMANTLE. When defining a set of related constants, the recommended practice is to use an enumerated type, rather than named constants in an interface. WOW! eBook www.wowebook.org Example 7.12 Inheriting Constants in Interfaces Click here to view code image // File: Factory.java interface IBaseStates { String IDLE = “idle”; // (1) String BUSY = “busy”; // (2) String BLOCKED = “blocked”; // (3) } //_______________________________________________________________________________ interface IExtStatesA extends IBaseStates { String DISMANTLED = “dismantled”; // (4) } //_______________________________________________________________________________ interface IExtStatesB extends IBaseStates { String DISMANTLED = “kaput”; // (5) } //_______________________________________________________________________________ interface IAllStates extends IExtStatesB, IExtStatesA { String BLOCKED = “out of order”; // (6) hides (3) //String ABSOLETE = BLOCKED + “, ” + // DISMANTLED + ” and scrapped.”; // (7) Ambiguous String ABSOLETE = BLOCKED + “, ” + IExtStatesB.DISMANTLED + ” and scrapped”; // (7a) } //_______________________________________________________________________________ public class Factory implements IAllStates { public static void main(String[] args) { // System.out.println(“Machine A is ” + DISMANTLED); // (8) Ambiguous. System.out.println(“Machine A is ” + IExtStatesB.DISMANTLED);// (8a) System.out.println(“Machine B is ” + ABSOLETE); // (9) IAllStates.ABSOLETE System.out.println(“Machine C is ” + BLOCKED); // (10) IAllStates.BLOCKED System.out.println(“Machine D is ” + IBaseStates.BLOCKED); // (11) System.out.println(“Machine E is ” + BUSY); // (12) Simple name } } Output from the program: Click here to view code image Machine Machine Machine Machine Machine A B C D E is is is is is kaput out of order, kaput and scrapped out of order blocked busy Review Questions 7.11 Which of the following statements about interfaces are true? Select the two correct answers. (a) Interfaces allow multiple implementation inheritance. (b) Interfaces can be extended by any number of interfaces. WOW! eBook www.wowebook.org (c) Interfaces can extend any number of interfaces. (d) Members of an interface are never static. (e) Members of an interface can always be declared static. 7.12 Which modifiers can methods declared in a top-level interface specify? Select the four correct answers. (a) public (b) protected (c) private (d) default (e) abstract (f) static (g) final 7.13 Which modifiers are implicitly implied for interface variables? Select the three correct answers. (a) public (b) protected (c) private (d) default (e) abstract (f) static (g) final 7.14 How many errors will the compiler report for the following code? Click here to view code image public interface Vehicle { final static int NUMBER_OF_HEADLIGHTS; // (1) void increaseSpeed(int increment) { // (2) System.out.println(“Increasing speed by ” + increment); } static void reduceSpeed(int decrement); // (3) final default void stop() { // (4) System.out.println(“Slamming the brakes!”); } } Select the one correct answer. (a) No errors (b) 1 error WOW! eBook www.wowebook.org (c) 2 errors (d) 3 errors (e) More than 3 errors 7.15 Which method calls can be inserted at both (1) and (2), so that the following code will still compile? Click here to view code image // File: Company.java interface ISlogan { String SLOGAN = “Happiness shared is happiness doubled!”; default void printSlogan() { System.out.println(SLOGAN); } } //_____________________________________________________________________________ public class Company implements ISlogan { public static void main(String[] args) { Company co = new Company(); ISlogan sl = co; // (1) INSERT THE METHOD CALL HERE. } public void testSlogan() { Company co = new Company(); ISlogan sl = co; // (2) INSERT THE METHOD CALL HERE. } } Select the two correct answers. (a) printSlogan(); (b) co.printSlogan(); (c) sl.printSlogan(); (d) Company.printSlogan(); (e) ISlogan.printSlogan(); 7.16 Which method call can be inserted at both (1) and (2), so that the following code will still compile? Click here to view code image // File: Firm.java interface INewSlogan { String SLOGAN = “Trouble shared is trouble halved!”; static void printSlogan() { System.out.println(SLOGAN); } } //_____________________________________________________________________________ public class Firm implements INewSlogan { public static void main(String[] args) { Firm co = new Firm(); INewSlogan sl = co; // (1) INSERT THE STATEMENT EXPRESSION HERE. } void testSlogan() { Firm co = new Firm(); WOW! eBook www.wowebook.org INewSlogan sl = co; // (2) INSERT THE STATEMENT EXPRESSION HERE. } } Select the one correct answer. (a) printSlogan(); (b) co.printSlogan(); (c) sl.printSlogan(); (d) Firm.printSlogan(); (e) INewSlogan.printSlogan(); 7.17 What will the following program print when compiled and run? Click here to view code image // File: RaceA.java interface IJogger { default boolean justDoIt(String msg) { return false; } static boolean justDoIt(int i) { return true; } } class Athlete implements IJogger { public boolean justDoIt(String msg) public boolean justDoIt(int i) } { return true; } { return false; } public class RaceA { public static void main(String[] args) { Athlete athlete = new Athlete(); IJogger jogger = athlete; System.out.print(jogger.justDoIt(“Run”)); System.out.println(“|” + athlete.justDoIt(10)); } } Select the one correct answer. (a) The program will not compile. (b) true|true (c) true|false (d) false|true (e) false|false 7.18 What will the following program print when compiled and run? Click here to view code image // File: HouseC interface ISwitch { default boolean isOn() { return false; } } class Light implements ISwitch { boolean isOn() { return true; } WOW! eBook www.wowebook.org // (1) // (2) // (1) // (2) // (3) // (4) // (5) // (6) } public class HouseC { public static void main(String[] args) { ISwitch lightswitch = new Light(); System.out.println(lightswitch.isOn()); } } Select the one correct answer. (a) The program will not compile. (b) The program will compile, and print true when run. (c) The program will compile, and print false when run. (d) The program will compile, and throw an exception when run. 7.19 Which of these field declarations are legal within the body of an interface? Select the three correct answers. (a) public static int ANSWER = 42; (b) int ANSWER; (c) static final int ANSWER = 42; (d) public int ANSWER = 42; (e) private static final int ANSWER = 42; 7.20 Which statements about the keywords extends and implements are true? Select the two correct answers. (a) The keyword extends is used to specify that an interface inherits from another interface. (b) The keyword extends is used to specify that a class implements an interface. (c) The keyword implements is used to specify that an interface inherits from another interface. (d) The keyword implements is used to specify that a class inherits from an interface. (e) The keyword implements is used to specify that a class inherits from another class. 7.21 Which statement is true about the following code? Click here to view code image // File: abstract public public } MyClass.java class MyClass implements Interface1, Interface2 { void f() { } void g() { } interface Interface1 { WOW! eBook www.wowebook.org int VAL_A = 1; int VAL_B = 2; void f(); void g(); } interface Interface2 { int VAL_B = 3; int VAL_C = 4; void g(); void h(); } Select the one correct answer. (a) MyClass implements only Interface1; the implementation for void h() from Interface2 is missing. (b) The declarations of void g() in the two interfaces are in conflict, so the code will not compile. (c) The declarations of int VAL_B in the two interfaces are in conflict, so the code will not compile. (d) Nothing is wrong with the code; it will compile without errors. 7.22 Which declaration can be inserted at (1) without resulting in a compile-time error? interface MyConstants { int R = 42; int S = 69; // (1) INSERT CODE HERE } Select the two correct answers. (a) final double CIRCUMFERENCE = 2 * Math.PI * R; (b) int TOTAL = TOTAL + R + S; (c) int AREA = R * S; (d) public static MAIN = 15; (e) protected int CODE = 13082009; 7.7 Arrays and Subtyping Table 7.3 summarizes the types found in Java. Only primitive data and reference values can be stored in variables. Only class and array types can be explicitly instantiated to create objects. WOW! eBook www.wowebook.org Table 7.3 Types and Values Arrays and Subtype Covariance Arrays are objects in Java. Array types (boolean[], Object[], StackImpl[]) implicitly augment the inheritance hierarchy. The inheritance hierarchy depicted in Figure 7.3, for example, can be augmented by the corresponding array types to produce the type hierarchy shown in Figure 7.5. An array type is shown as a “class” with the [] notation appended to the name of the element type. The class SafeStackImpl is a subclass of the class StackImpl. The corresponding array types, SafeStackImpl[] and StackImpl[], are shown as the subtype and the supertype, respectively, in the type hierarchy. Figure 7.5 also shows array types corresponding to some of the primitive data types. Figure 7.5 Reference Type Hierarchy: Arrays and Subtype Covariance From the type hierarchy in Figure 7.5, the following facts are apparent: • All reference types are subtypes of the Object type. This applies to classes, interfaces, enums, and array types, as these are all reference types. • All arrays of reference types are also subtypes of the array type Object[], but arrays of primitive data types are not. Note that the array type Object[] is also a subtype of the Object type. • If a non-generic reference type is a subtype of another non-generic reference type, WOW! eBook www.wowebook.org the corresponding array types also have an analogous subtype–supertype relationship. This is called the subtype covariance relationship. • There is no subtype–supertype relationship between a type and its corresponding array type. We can create an array of an interface type, but we cannot instantiate an interface (as is the case with abstract classes). In the following declaration statement, the reference iSafeStackArray has type ISafeStack[] (i.e., an array of the interface type ISafeStack): Click here to view code image ISafeStack[] iSafeStackArray = new ISafeStack[5]; The array creation expression creates an array whose element type is ISafeStack. The array object can accommodate five references of the type ISafeStack. The declaration statement does not initialize these references to refer to any objects; instead, they are initialized to the default value null. Array Store Check An array reference exhibits polymorphic behavior like any other reference, subject to its location in the type hierarchy (§7.12, p. 329). However, a runtime check is necessary when objects are inserted in an array, as the next example illustrates. The following assignment is valid, as a supertype reference (StackImpl[]) can refer to objects of its subtype (SafeStackImpl[]): Click here to view code image StackImpl[] stackImplArray = new SafeStackImpl[2]; // (1) Since StackImpl is a supertype of SafeStackImpl, the following assignment is also valid: Click here to view code image stackImplArray[0] = new SafeStackImpl(10); // (2) The assignment at (2) assigns the reference value of a new SafeStackImpl object to the reference at index 0 in the SafeStackImpl[] object (i.e., the array of SafeStackImpl) created at (1). Since the type of stackImplArray[i], (0 ≤ i < 2), is StackImpl, it should be possible to make the following assignment as well: Click here to view code image stackImplArray[1] = new StackImpl(20); ArrayStoreException // (3) At compile time there are no problems, as the compiler cannot deduce that the array variable stackImplArray will actually denote a SafeStackImpl[] object at runtime. However, the assignment at (3) results in an ArrayStoreException being thrown at runtime, because an array of SafeStackImpl objects cannot possibly contain objects of its supertype StackImpl. WOW! eBook www.wowebook.org The array store check at runtime ensures that an object being stored in the array is assignment compatible (p. 314) with the element type of the array. To make the array store check feasible at runtime, the array retains information about its declared element type at runtime. 7.8 Reference Values and Conversions A review of conversions (§5.1, p. 144) is recommended before proceeding with this section. Reference values, like primitive values, can be assigned, cast, and passed as arguments. Conversions can occur in the following contexts: • Assignment • Method invocation • Casting The rule of thumb for the primitive data types is that widening conversions are permitted, but narrowing conversions require an explicit cast. The rule of thumb for reference values is that widening conversions up the type hierarchy are permitted, but narrowing conversions down the hierarchy require an explicit cast. In other words, conversions that are from a subtype to its supertypes are allowed, but other conversions require an explicit cast or are otherwise illegal. There is no notion of promotion for reference values. 7.9 Reference Value Assignment Conversions In the context of assignments, the following conversions are permitted (Table 5.1, p. 147): • Widening primitive and reference conversions (long ← int, Object ← String) • Boxing conversion of primitive values, followed by optional widening reference conversion (Integer ← int, Number ← Integer ← int) • Unboxing conversion of a primitive value wrapper object, followed by optional widening primitive conversion (long ← int ← Integer) For assignment conversions only, the following conversions are also possible: • Narrowing conversion for constant expressions of non-long integer types, with optional boxing (Byte ← byte ← int) Note that these rules imply that a widening conversion cannot be followed by any boxing conversion, but the converse is permitted. Widening reference conversions typically occur during assignment up the type hierarchy, with implicit conversion of the source reference value to that of the destination reference type: Click here to view code image Object obj = “Up the tree”; // Widening reference conversion: Object <— String String str1 = obj; // Not OK. Narrowing reference conversion requires a WOW! eBook www.wowebook.org cast. String str2 = new Integer(10); // Illegal. No relation between String and Integer. The source value can be a primitive value, in which case the value is boxed in a wrapper object corresponding to the primitive type. If the destination reference type is a supertype of the wrapper type, a widening reference conversion can occur: Click here to view code image Integer iRef = 10; Number num = 10L; Object obj = 100; // Only boxing // Boxing, followed by widening: Number <– Long <– long // Boxing, followed by widening: Object <– Integer <– int More examples of boxing during assignment can be found in §5.1, p. 145. Example 7.13 Assigning and Passing Reference Values Click here to view code image interface IStack interface ISafeStack extends IStack class StackImpl implements IStack class SafeStackImpl extends StackImpl implements ISafeStack { /* From Example 7.7 */ } { /* From Example 7.7 */ } { /* From Example 7.7 */ } { /* From Example 7.7 */ } public class ReferenceConversion { public static void main(String[] args) { // Reference declarations: Object objRef; StackImpl stackRef; SafeStackImpl safeStackRef; IStack iStackRef; ISafeStack iSafeStackRef; // SourceType safeStackRef objRef stackRef iStackRef iSafeStackRef ISafeStack is a class type: = new SafeStackImpl(10); = safeStackRef; // (1) = safeStackRef; // (2) = stackRef; // (3) = safeStackRef; // (4) Always possible Subclass to superclass assignment StackImpl implements IStack SafeStackImpl implements // SourceType is an interface type: objRef = iStackRef; // (5) Always possible iStackRef = iSafeStackRef; // (6) Sub- to super-interface assignment // SourceType is an array type: Object[] objArray StackImpl[] stackArray SafeStackImpl[] safeStackArray ISafeStack[] iSafeStackArray int[] intArray // Reference objRef = objRef = objArray = objArray = objRef = // objArray value assignments: objArray; stackArray; stackArray; iSafeStackArray; intArray; = intArray; = = = = = new new new new new // // // // // // Object[3]; StackImpl[3]; SafeStackImpl[5]; ISafeStack[5]; int[10]; (7) Always possible (8) Always possible (9) Always possible (10) Always possible (11) Always possible (12) Compile-time error: WOW! eBook www.wowebook.org // int[] not subtype of Object[] stackArray = safeStackArray; // (13) Subclass array to superclass array iSafeStackArray = safeStackArray;// (14) SafeStackImpl implements ISafeStack // Method invocation conversions: System.out.println(“First call:”); sendParams(stackRef, safeStackRef, iStackRef, safeStackArray, iSafeStackArray); // (15) // Call Signature: sendParams(StackImpl, SafeStackImpl, IStack, // SafeStackImpl[], ISafeStack[]); System.out.println(“Second call:”); sendParams(iSafeStackArray, stackRef, iSafeStackRef, stackArray, safeStackArray); // (16) // Call Signature: sendParams(ISafeStack[], StackImpl, ISafeStack, // StackImpl[], SafeStackImpl[]); } public static void sendParams(Object objRefParam, StackImpl stackRefParam, IStack iStackRefParam, StackImpl[] stackArrayParam, IStack[] iStackArrayParam) { // (17) // Signature: sendParams(Object, StackImpl, IStack, StackImpl[], IStack[]) // Print class name of object denoted by the reference at runtime. System.out.println(objRefParam.getClass()); System.out.println(stackRefParam.getClass()); System.out.println(iStackRefParam.getClass()); System.out.println(stackArrayParam.getClass()); System.out.println(iStackArrayParam.getClass()); } } Output from the program: First call: class SafeStackImpl class SafeStackImpl class SafeStackImpl class [LSafeStackImpl; class [LSafeStackImpl; Second call: class [LSafeStackImpl; class SafeStackImpl class SafeStackImpl class [LSafeStackImpl; class [LSafeStackImpl; The rules for reference value assignment are stated in this section, based on the following code: Click here to view code image SourceType srcRef; // srcRef is appropriately initialized. DestinationType destRef = srcRef; If an assignment is legal, the reference value of srcRef is said to be assignable (or assignment compatible) to the reference of DestinationType. The rules are illustrated by concrete cases from Example 7.13. Note that the code in Example 7.13 uses reference types from Example 7.7, p. 292. WOW! eBook www.wowebook.org • If the SourceType is a class type, the reference value in srcRef may be assigned to the destRef reference, provided the DestinationType is one of the following: DestinationType is a superclass of the subclass SourceType. DestinationType is an interface type that is implemented by the class SourceType. Click here to view code image objRef stackRef iStackRef iSafeStackRef ISafeStack = = = = safeStackRef; safeStackRef; stackRef; safeStackRef; // // // // (1) (2) (3) (4) Always possible Subclass to superclass assignment StackImpl implements IStack SafeStackImpl implements • If the SourceType is an interface type, the reference value in srcRef may be assigned to the destRef reference, provided the DestinationType is one of the following: DestinationType is the Object class. DestinationType is a superinterface of the subinterface SourceType. Click here to view code image objRef = iStackRef; // (5) Always possible iStackRef = iSafeStackRef; // (6) Subinterface to superinterface assignment • If the SourceType is an array type, the reference value in srcRef may be assigned to the destRef reference, provided the DestinationType is one of the following: DestinationType is the Object class. DestinationType is an array type, where the element type of the SourceType is assignable to the element type of the DestinationType. Click here to view code image objRef = objRef = objArray = objArray = objRef = // objArray objArray; stackArray; stackArray; iSafeStackArray; intArray; = intArray; // // // // // // // // (7) Always possible (8) Always possible (9) Always possible (10) Always possible (11) Always possible (12) Compile-time error: int[] not subtype of Object[] (13) Subclass array to superclass stackArray = safeStackArray; array iSafeStackArray = safeStackArray;// (14) SafeStackImpl implements ISafeStack The rules for assignment are enforced at compile time, guaranteeing that no type conversion error will occur during assignment at runtime. Such conversions are type-safe. The reason the rules can be enforced at compile time is that they concern the declared type of the reference (which is always known at compile time) rather than the actual type of the object being referenced (which is known at runtime). WOW! eBook www.wowebook.org 7.10 Method Invocation Conversions Involving References The conversions for reference value assignment are also applicable to method invocation conversions, except for the narrowing conversion for constant expressions of non-long integer type (Table 5.1, p. 147). This is reasonable, as parameters in Java are passed by value (§3.5, p. 72), requiring that values of the actual parameters must be assignable to formal parameters of the compatible types. In Example 7.13, the method sendParams() at (17) has the following signature, showing the types of the formal parameters: Click here to view code image sendParams(Object, StackImpl, IStack, StackImpl[], IStack[]) The method call at (15) has the following signature, showing the types of the actual parameters: Click here to view code image sendParams(StackImpl, SafeStackImpl, IStack, SafeStackImpl[], ISafeStack[]); Note that the assignment of the values of the actual parameters to the corresponding formal parameters is legal, according to the rules for assignment discussed earlier. The method call at (16) provides another example of the parameter passing conversion. It has the following signature: Click here to view code image sendParams(ISafeStack[], StackImpl, ISafeStack, StackImpl[], SafeStackImpl[]); Analogous to assignment, the rules for parameter passing conversions are based on the reference type of the parameters and are enforced at compile time. The output in Example 7.13 shows the class of the actual objects referenced by the formal parameters at runtime, which in this case turns out to be either SafeStackImpl or SafeStackImpl[]. The characters [L in the output indicate a one-dimensional array of a class or interface type (see the Class.getName() method in the Java SE platform API documentation). Overloaded Method Resolution In this subsection, we take a look at some aspects regarding overloaded method resolution —namely, how the compiler determines which overloaded method will be invoked by a given method call at runtime. Resolution of overloaded methods selects the most specific method for execution. One method is considered more specific than another method if all actual parameters that can be accepted by the one method can be accepted by the other method. If more than one such method is present, the call is described as ambiguous. The following overloaded methods illustrate this situation: Click here to view code image private static void out.println(str } private static void out.println(str flipFlop(String str, int i, Integer iRef) { // (1) + ” ==> (String, int, Integer)”); flipFlop(String str, int i, int j) { + ” ==> (String, int, int)”); WOW! eBook www.wowebook.org // (2) } Their method signatures follow: Click here to view code image flipFlop(String, int, Integer) flipFlop(String, int, int) // See (1) // See (2) The following method call is ambiguous: Click here to view code image flipFlop(“(String, Integer, int)”, new Integer(4), 2004); call // (3) Ambiguous It has the call signature: Click here to view code image flipFlop(String, Integer, int) // See (3) The method at (1) can be called with the second argument unboxed and the third argument boxed, as can the method at (2) with only the second argument unboxed. In other words, for the call at (3), none of the methods is more specific than the others. Example 7.14 illustrates a simple case of how method resolution is done to choose the most specific overloaded method. The method testIfOn() is overloaded at (1) and (2) in the class Overload. The call client.testIfOn(tubeLight) at (3) satisfies the parameter lists in both implementations given at (1) and (2), as the reference tubeLight can also be assigned to a reference of its superclass Light. The most specific method, (2), is chosen, resulting in false being written on the terminal. The call client.testIfOn(light) at (4) satisfies only the parameter list in the implementation given at (1), resulting in true being written on the terminal. This is also the case at (5). The object referred to by the argument in the call is irrelevant; rather, it is the type of the argument that is important for overloaded method resolution. WOW! eBook www.wowebook.org Example 7.14 Choosing the Most Specific Method (Simple Case) Click here to view code image class Light { /* … */ } class TubeLight extends Light { /* … */ } public class Overload { boolean testIfOn(Light aLight) { return true; } boolean testIfOn(TubeLight aTubeLight) { return false; } // (1) // (2) public static void main(String[] args) { TubeLight tubeLight = new TubeLight(); Light light = new Light(); Light light2 = new TubeLight(); Overload client = new Overload(); System.out.println(client.testIfOn(tubeLight)); // (3) ==> method at (2) System.out.println(client.testIfOn(light)); // (4) ==> method at (1) System.out.println(client.testIfOn(light2)); // (5) ==> method at (2) } } Output from the program: false true true The algorithm used by the compiler for the resolution of overloaded methods incorporates the following phases: 1. The compiler performs overload resolution without permitting boxing, unboxing, or the use of a variable arity call. 2. If phase (1) fails, the compiler performs overload resolution allowing boxing and unboxing, but excluding the use of a variable arity call. 3. If phase (2) fails, the compiler performs overload resolution combining a variable arity call, boxing, and unboxing. Example 7.15 provides some insight into how the compiler determines the most specific overloaded method using these three phases. The example has six overloaded declarations of the method action(). The signature of each method is given by the local variable signature in each method. The first formal parameter of each method is the signature of the call that invoked the method. The printout from each method allows us to see which method call resolved to which method. The main() method contains 10 calls, (8) to (17), of the action() method. In each call, the first argument is the signature of that method call. An important point to note is that the compiler chooses a fixed arity call over a variable arity call, as seen in the calls from (8) to (12): WOW! eBook www.wowebook.org An unboxing conversion (Integer to int) takes place for the call at (10). A widening primitive conversion (byte to int) takes place for the call at (11). Variable arity calls are chosen from (13) to (17): When a variable arity call is chosen, the method determined has the most specific variable arity parameter that is applicable for the actual argument. For example, in the method call at (14), the type Integer[] is more specific than either Number[] or Object[]. Note also the boxing of the elements of the implicitly created array in the calls from (13) to (17). Example 7.15 Overloaded Method Resolution Click here to view code image import static java.lang.System.out; class OverloadResolution { public void action(String str) { String signature = “(String)”; out.println(str + ” => ” + signature); } // (1) public void action(String str, int m) { String signature = “(String, int)”; out.println(str + ” => ” + signature); } // (2) public void action(String str, int m, int n) { String signature = “(String, int, int)”; out.println(str + ” => ” + signature); } // (3) public void action(String str, Integer… data) { // (4) String signature = “(String, Integer[])”; out.println(str + ” => ” + signature); } public void action(String str, Number… data) { String signature = “(String, Number[])”; out.println(str + ” => ” + signature); } // (5) public void action(String str, Object… data) { // (6) WOW! eBook www.wowebook.org String signature = “(String, Object[])”; out.println(str + ” => ” + signature); } public static void main(String[] args) { OverloadResolution ref = new OverloadResolution(); ref.action(“(String)”); (1) ref.action(“(String, int)”, 10); (2) ref.action(“(String, Integer)”, new Integer(10)); (2) ref.action(“(String, int, byte)”, 10, (byte)20); (3) ref.action(“(String, int, int)”, 10, 20); (3) ref.action(“(String, int, long)”, 10, 20L); (5) ref.action(“(String, int, int, int)”, 10, 20, 30); (4) ref.action(“(String, int, double)”, 10, 20.0); (5) ref.action(“(String, int, String)”, 10, “what?”); (6) ref.action(“(String, boolean)”, false); (6) } } // (8) calls // (9) calls // (10) calls // (11) calls // (12) calls // (13) calls // (14) calls // (15) calls // (16) calls // (17) calls Output from the program (with remarks to the output on the right): Click here to view code image (String) (String, (String, (String, (String, (String, (String, (String, (String, (String, => (String) int) => (String, int) Integer) => (String, int) int, byte) => (String, int, int) int, int) => (String, int, int) int, long) => (String, Number[]) int, int, int) => (String, Integer[]) int, double) => (String, Number[]) int, String) => (String, Object[]) boolean) => (String, Object[]) 7.11 Reference Casting and the (8) calls (1) (9) calls (2) (10) calls (2) (11) calls (3) (12) calls (3) (13) calls (5) (14) calls (4) (15) calls (5) (16) calls (6) (17) calls (6) Operator The Cast Operator The type cast expression for reference types has the following syntax: Click here to view code image (destination_type) reference_expression where the reference expression evaluates to a reference value of an object of some reference type. A type cast expression checks that the reference value refers to an object whose type is compatible with the destination type, meaning that its type is a subtype of the destination type. The construct (destination_type) is usually called the cast operator. The result of a type cast expression for references is always a reference value of an object. WOW! eBook www.wowebook.org The literal null can be cast to any reference type. The next code snippet illustrates the various scenarios that arise when using the cast operator. In this discussion, it is the type cast expression that is important, not the evaluation of the assignment operator in the declaration statements. In (1), the cast is from the superclass Object to the subclass String; the code compiles and at runtime this cast is permitted, as the reference obj will denote an object of class String. In (2), the cast is from the superclass Object to the subclass Integer; the code compiles, but at runtime this cast results in a ClassCastException, since the reference obj will denote an object of class String, which cannot be converted to an Integer. In (3), the cast is from the class String to the class Integer. As these two classes are unrelated, the compiler flags an error for the cast. Click here to view code image Object obj = new String(“Cast me!”); String str = (String) obj; // (1) Cast from Object to String. Integer iRef1 = (Integer) obj; // (2) Cast from Object to Integer, but // ClassCastException at runtime. Integer iRef2 = (Integer) str; // (3) Compile-time error! // Cast between unrelated types. The following conversions can be applied to the operand of a cast operator: • Both widening and narrowing reference conversions, followed optionally by an unchecked conversion • Both boxing and unboxing conversions Boxing and unboxing conversions that can occur during casting are illustrated by the following code. Again, it is the type cast expression that is important in this discussion, rather than whether the assignment operator requires one in the declaration statements. Click here to view code image // (1) Number // (2) Object // (3) double Boxing and casting: Number <— Integer <— int: num = (Number) 100; Casting, boxing, casting: Object <— Integer <— int <— double: obj = (Object) (int) 10.5; Casting, unboxing, casting: double <– int <— Integer <— Object: d = (double) (Integer) obj; Note that the resulting object in (1) and (2) is an Integer, but the resulting value in (3) is a double. The boxing conversions from int to Integer in (1) and (2) are implicit, and the unboxing conversion from Integer to int in (3) is also implicit. The Operator The binary instanceof operator can be used for comparing types. It has the following syntax (note that the keyword is composed of lowercase letters only): Click here to view code image reference_expression instanceof destination_type The instanceof operator returns true if the left-hand operand (i.e., the reference value that results from the evaluation of reference expression) can be a subtype of the WOW! eBook www.wowebook.org right-hand operand (destination_type). It always returns false if the left-hand operand is null. If the instanceof operator returns true, the corresponding type cast expression will always be valid. Both the type cast expression and the instanceof operators require a compile-time check and a runtime check, as explained later in this section. The compile-time check determines whether there is a subtype–supertype relationship between the source and destination types. Given that the type of the reference expression is source type, the compiler determines whether a reference of source type and a reference of destination type can refer to objects of a reference type that are a common subtype of both source type and destination type in the type hierarchy. If this is not the case, then obviously there is no relationship between the types, and neither the cast nor the instanceof operator application would be valid. At runtime, the reference expression evaluates to a reference value of an object. It is the type of the actual object that determines the outcome of the operation, as explained earlier. With the classes Light and String as source type and destination type, respectively, there is no subtype–supertype relationship between source type and destination type. The compiler would reject casting a reference of type Light to type String or applying the instanceof operator, as shown at (2) and (3) in Example 7.16. References of the classes Light and TubeLight can refer to objects of the class TubeLight (or its subclasses) in the inheritance hierarchy depicted in Figure 7.1. Therefore, it makes sense to apply the instanceof operator or cast a reference of the type Light to the type TubeLight as shown at (4) and (5), respectively, in Example 7.16. At runtime, the result of applying the instanceof operator at (4) is false, because the reference light1 of the class Light will actually denote an object of the subclass LightBulb, and this object cannot be denoted by a reference of the peer class TubeLight. Applying the cast at (5) results in a ClassCastException for the same reason. This is the reason why cast conversions are said to be unsafe, as they may throw a ClassCastException at runtime. Note that if the result of the instanceof operator is false, the cast involving the operands will throw a ClassCastException. In Example 7.16, the result of applying the instanceof operator at (6) is also false, because the reference light1 will still denote an object of the class LightBulb, whose objects cannot be denoted by a reference of its subclass SpotLightBulb. Thus applying the cast at (7) causes a ClassCastException to be thrown at runtime. The situation shown at (8), (9), and (10) illustrates typical usage of the instanceof operator to determine which object a reference is denoting, so that it can be cast for the purpose of carrying out some specific action. The reference light1 of the class Light is initialized to an object of the subclass NeonLight at (8). The result of the instanceof operator at (9) is true, because the reference light1 will denote an object of the subclass NeonLight, whose objects can also be denoted by a reference of its superclass TubeLight. By the same token, the cast at (10) is valid. If the result of the WOW! eBook www.wowebook.org instanceof operator is true, the cast involving the operands will be valid as well. Example 7.16 The instanceof and Cast Operators Click here to view code image // See Figure 7.1, p. 267, for inheritance hierarchy. class Light { /* … */ } class LightBulb extends Light { /* … */ } class SpotLightBulb extends LightBulb { /* … */ } class TubeLight extends Light { /* … */ } class NeonLight extends TubeLight { /* … */ } public class WhoAmI { public static void main(String[] args) { boolean result1, result2, result3; Light light1 = new LightBulb(); // String str = (String) light1; error! // result1 = light1 instanceof String; error! result2 = light1 instanceof TubeLight; class. // TubeLight tubeLight1 = (TubeLight) light1; ClassCastException! // (1) // (2) Compile-time // (3) Compile-time // (4) false: peer // (5) result3 = light1 instanceof SpotLightBulb; // (6) false: superclass. // SpotLightBulb spotRef = (SpotLightBulb) light1;// (7) ClassCastException! light1 = new NeonLight(); // (8) if (light1 instanceof TubeLight) { // (9) true. TubeLight tubeLight2 = (TubeLight) light1; // (10) OK. // Can now use tubeLight2 to access an object of the class NeonLight, // but only those members that the object inherits or overrides // from the class TubeLight. } } } As we have seen, the instanceof operator effectively determines whether the reference value in the reference on the left-hand side refers to an object whose class is a subtype of the type of the reference specified on the right-hand side. At runtime, it is the type of the actual object denoted by the reference on the left-hand side that is compared with the type specified on the right-hand side. In other words, what matters at runtime is the type of the actual object denoted by the reference, not the declared type of the reference. Example 7.17 provides more examples of the instanceof operator. It is instructive to go through the print statements and understand why those results printed out. The literal null is not an instance of any reference type, as shown in the print statements at (1), (2), and (16). An instance of a superclass is not an instance of its subclass, as shown in the print statement at (4). An instance of a class is not an instance of a totally unrelated class, as shown in the print statement at (10). An instance of a class is not an instance of an interface type that the class does not implement, as shown in the print statement at (6). Any array of non-primitive type is an instance of both Object and Object[] types, as WOW! eBook www.wowebook.org shown in the print statements at (14) and (15), respectively. Example 7.17 Using the instanceof Operator Click here to view code image // See Figure 7.3, p. 295, for inheritance interface IStack { /* interface ISafeStack extends IStack { /* class StackImpl implements IStack { /* class SafeStackImpl extends StackImpl implements ISafeStack { /* hierarchy. From Example 7.7 */ } From Example 7.7 */ } From Example 7.7 */ } From Example 7.7 */ } public class Identification { public static void main(String[] args) { Object obj = new Object(); StackImpl stack = new StackImpl(10); SafeStackImpl safeStack = new SafeStackImpl(5); IStack iStack; String strFormat = “(%d) %-25s instance of %-25s: %s%n”; System.out.printf(strFormat, 1, null, Object.class, null instanceof Object); // Always false. System.out.printf(strFormat, 2, null, IStack.class, null instanceof IStack); // Always false. System.out.printf(strFormat, 3, stack.getClass(), Object.class, stack instanceof Object); // true: instance of subclass of Object. System.out.printf(strFormat, 4, obj.getClass(), StackImpl.class, obj instanceof StackImpl); // false: Object not subtype of StackImpl. System.out.printf(strFormat, 5, stack.getClass(), StackImpl.class, stack instanceof StackImpl); // true: instance of StackImpl. System.out.printf(strFormat, 6, obj.getClass(), IStack.class, obj instanceof IStack); // false: Object does not implement IStack. System.out.printf(strFormat, 7, safeStack.getClass(), IStack.class, safeStack instanceof IStack); // true: SafeStackImpl implements IStack. obj = stack; // No cast required: assigning subclass to superclass. System.out.printf(strFormat, 8, obj.getClass(), StackImpl.class, obj instanceof StackImpl); // true: instance of StackImpl. System.out.printf(strFormat, 9, obj.getClass(), IStack.class, obj instanceof IStack); // true: StackImpl implements IStack. System.out.printf(strFormat, 10, obj.getClass(), String.class, obj instanceof String); // false: no relationship. iStack = (IStack) obj; // Cast required: assigning superclass to WOW! eBook www.wowebook.org subclass. System.out.printf(strFormat, 11, iStack.getClass(), Object.class, iStack instanceof Object); // true: instance of subclass of Object. System.out.printf(strFormat, 12, iStack.getClass(), StackImpl.class, iStack instanceof StackImpl); // true: instance of StackImpl. String[] strArray = new String[10]; // System.out.printf(strFormat, 13, // strArray.getClass(), String.class, // strArray instanceof String); // Compile-time error: no relationship. System.out.printf(strFormat, 14, strArray.getClass(), Object.class, strArray instanceof Object); // true: array subclass of Object. System.out.printf(strFormat, 15, strArray.getClass(), Object[].class, strArray instanceof Object[]); // true: array subclass of Object[]. System.out.printf(strFormat, 16, strArray[0], Object.class, strArray[0] instanceof Object); // false: strArray[0] is null. System.out.printf(strFormat, 17, strArray.getClass(), String[].class, strArray instanceof String[]); // true: array of String. strArray[0] = “Amoeba strip”; System.out.printf(strFormat, 18, strArray[0].getClass(), String.class, strArray[0] instanceof String); // true: strArray[0] instance of String. } } Output from the program: Click here to view code image (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (14) (15) (16) (17) (18) null instance of class java.lang.Object : false null instance of interface IStack : false class StackImpl instance of class java.lang.Object : true class java.lang.Object instance of class StackImpl : false class StackImpl instance of class StackImpl : true class java.lang.Object instance of interface IStack : false class SafeStackImpl instance of interface IStack : true class StackImpl instance of class StackImpl : true class StackImpl instance of interface IStack : true class StackImpl instance of class java.lang.String : false class StackImpl instance of class java.lang.Object : true class StackImpl instance of class StackImpl : true class [Ljava.lang.String; instance of class java.lang.Object : true class [Ljava.lang.String; instance of class [Ljava.lang.Object;: true null instance of class java.lang.Object : false class [Ljava.lang.String; instance of class [Ljava.lang.String;: true class java.lang.String instance of class java.lang.String : true Review Questions 7.23 Which statement about the following program is true? WOW! eBook www.wowebook.org Click here to view code image public class MyClass { public static void main(String[] args) { A[] arrA; B[] arrB; arrA arrB arrA arrB arrA arrB = = = = = = new A[10]; new B[20]; arrB; // (1) (B[]) arrA; // (2) new A[10]; (B[]) arrA; // (3) } } class A {} class B extends A {} Select the one correct answer. (a) The program will fail to compile because of the assignment at (1). (b) When run, the program will throw a java.lang.ClassCastException in the assignment at (2). (c) When run, the program will throw a java.lang.ClassCastException in the assignment at (3). (d) The program will compile and run without errors, even if the cast operator (B[]) in the statements at (2) and (3) is removed. (e) The program will compile and run without errors, but will not do so if the cast operator (B[]) in statements at (2) and (3) is removed. 7.24 Which statements will cause a compile-time error in the following code? Click here to view code image public class MyClass { public static void main(String[] args) { MyClass a; MySubclass b; a = new MyClass(); b = new MySubclass(); // (1) // (2) a = b; b = a; // (3) // (4) a = new MySubclass(); b = new MyClass(); // (5) // (6) } } class MySubclass extends MyClass {} Select the two correct answers. (a) (1) (b) (2) WOW! eBook www.wowebook.org (c) (3) (d) (4) (e) (5) (f) (6) 7.25 Given the following type and reference declarations, which assignment is legal? Click here to view code image // Type declarations: interface I1 {} interface I2 {} class C1 implements I1 {} class C2 implements I2 {} class C3 extends C1 implements I2 {} // Reference declarations: C1 obj1 = null; C2 obj2 = null; C3 obj3 = null; Select the one correct answer. (a) obj2 = obj1; (b) obj3 = obj1; (c) obj3 = obj2; (d) I1 a = obj2; (e) I1 b = obj3; (f) I2 c = obj1; 7.26 Given the following class and reference declarations, what can be said about the statement y = (Sub) x? // Class declarations: class Super {} class Sub extends Super {} // Reference declarations: Super x = null; Sub y = null; Select the one correct answer. (a) It is illegal at compile time. (b) It is legal at compile time, but might be illegal at runtime. (c) It is definitely legal at runtime, but the cast operator (Sub) is not strictly needed. (d) It is definitely legal at runtime, and the cast operator (Sub) is needed. 7.27 Given three classes A, B, and C, where B is a subclass of A, and C is a subclass of B, which one of these boolean expressions is true only when the reference o WOW! eBook www.wowebook.org refers to an object of class B, and not to an object of class A or class C? Select the one correct answer. (a) (o instanceof B) && (!(o instanceof A)) (b) (o instanceof B) && (!(o instanceof C)) (c) !((o instanceof A) || (o instanceof B)) (d) (o instanceof B) (e) (o instanceof B) && !((o instanceof A) || (o instanceof C)) 7.28 What will the following program print when run? Click here to view code image public class RQ07A100 { public static void main(String[] args) { I x = new D(); if (x instanceof I) System.out.print(“I”); if (x instanceof J) System.out.print(“J”); if (x instanceof C) System.out.print(“C”); if (x instanceof D) System.out.print(“D”); System.out.println(); } } interface I{} interface J{} class C implements I {} class D extends C implements J {} Select the one correct answer. (a) The program will not print any letters. (b) ICD (c) IJD (d) IJCD (e) ID 7.29 What is the result of compiling and running the following program? Click here to view code image class YingYang { void yingyang(Integer i) { System.out.println(“Integer: ” + i); } void yingyang(Integer[] ints) { System.out.println(“Integer[]: ” + ints[0]); } void yingyang(Integer… ints) { System.out.println(“Integer…: ” + ints[0]); } } WOW! eBook www.wowebook.org public class RQ800A50 { public static void main(String[] args) { YingYang yy = new YingYang(); yy.yingyang(10); yy.yingyang(10,12); yy.yingyang(new Integer[] {10, 20}); yy.yingyang(new Integer(10), new Integer(20)); } } Select the one correct answer. (a) The program will not compile because of errors. (b) The program will compile, but throw an exception at runtime. (c) The program will compile and print: Integer: 10 Integer…: 10 Integer…: 10 Integer…: 10 (d) The program will compile and print: Integer: 10 Integer…: 10 Integer[]: 10 Integer…: 10 7.30 What will be the result of compiling and running the following program? Click here to view code image public class RQ800A20 { static void compute(int… ia) { System.out.print(“|”); for(int i : ia) { System.out.print(i + “|”); } System.out.println(); } static void compute(int[] ia1, int… ia2) { compute(ia1); compute(ia2); } static void compute(int[] ia1, int[]… ia2d) { for(int[] ia : ia2d) { compute(ia); } } public static void main(String[] args) { compute(new int[] {10, 11}, new int[] {12, 13, 14}); compute(15, 16); compute(new int[] {17, 18}, new int[][] {{19}, {20}}); compute(null, new int[][] {{21}, {22}}); } } // (1) // (2) // (3) // // // // (4) (5) (6) (7) Select the one correct answer. (a) The program does not compile because of errors in one or more calls to the compute() method. WOW! eBook www.wowebook.org (b) The program compiles, but throws a NullPointerException when run. (c) The program compiles and prints: |10|11| |12|13|14| |15|16| |19| |20| |21| |22| (d) The program compiles and prints: |12|13|14| |15|16| |10|11| |19| |20| |21| |22| 7.12 Polymorphism and Dynamic Method Lookup Which object a reference will actually denote during runtime cannot always be determined at compile time. Polymorphism allows a reference to denote objects of different types at different times during execution. A supertype reference exhibits polymorphic behavior since it can denote objects of its subtypes. When a non-private instance method is invoked on an object, the method definition actually executed is determined both by the type of the object at runtime and by the method signature. Dynamic method lookup (also known as late binding, dynamic binding, and virtual method invocation) is the process of determining which method definition a method signature denotes during runtime, based on the type of the object. However, a call to a private instance method is not polymorphic. Such a call can occur only within the class and gets bound to the private method implementation at compile time. The inheritance hierarchy depicted in Figure 7.6 is implemented in Example 7.18. The implementation of the method draw() is overridden in all subclasses of the class Shape. The invocation of the draw() method in the two loops at (3) and (4) in Example 7.18 relies on the polymorphic behavior of references and dynamic method lookup. The array shapes holds Shape references denoting a Circle, a Rectangle, and a Square, as shown at (1). At runtime, dynamic lookup determines the draw() implementation that will execute, based on the type of the object denoted by each element in the array. This is also the case for the elements of the array drawables at (2), which holds IDrawable references that can be assigned the reference value of any object of a class that implements the IDrawable interface. The first loop will still work without any change if objects of new subclasses of the class Shape are added to the array shapes. If they did not override the draw() method, an inherited version of the method would be executed. This polymorphic behavior applies to the array drawables, where the subtype objects are guaranteed to have implemented the IDrawable interface. WOW! eBook www.wowebook.org Figure 7.6 Type Hierarchy That Illustrates Polymorphism Polymorphism and dynamic method lookup form a powerful programming paradigm that simplifies client definitions, encourages object decoupling, and supports dynamically changing relationships between objects at runtime. WOW! eBook www.wowebook.org Example 7.18 Polymorphism and Dynamic Method Lookup Click here to view code image // File: PolymorphRefs.java interface IDrawable { void draw(); } //_______________________________________________________________________________ class Shape implements IDrawable { @Override public void draw() { System.out.println(“Drawing a Shape.”); } } //_______________________________________________________________________________ class Circle extends Shape { @Override public void draw() { System.out.println(“Drawing a Circle.”); } } //_______________________________________________________________________________ class Rectangle extends Shape { @Override public void draw() { System.out.println(“Drawing a Rectangle.”); } } //_______________________________________________________________________________ class Square extends Rectangle { @Override public void draw() { System.out.println(“Drawing a Square.”); } } //_______________________________________________________________________________ class Graph implements IDrawable { @Override public void draw() { System.out.println(“Drawing a Graph.”); } } //_______________________________________________________________________________ public class PolymorphRefs { public static void main(String[] args) { Shape[] shapes = {new Circle(), new Rectangle(), new Square()}; // (1) IDrawable[] drawables = {new Shape(), new Rectangle(), new Graph()}; // (2) System.out.println(“Draw shapes:”); for (Shape shape : shapes) // (3) shape.draw(); System.out.println(“Draw drawables:”); for (IDrawable drawable : drawables) (4) drawable.draw(); } } Output from the program: Draw shapes: Drawing a Circle. Drawing a Rectangle. Drawing a Square. Draw drawables: Drawing a Shape. Drawing a Rectangle. Drawing a Graph. WOW! eBook www.wowebook.org // 7.13 Inheritance versus Aggregation Figure 7.7 is a UML class diagram showing several aggregation relationships and one inheritance relationship. This class diagram shows a queue defined by aggregation and a stack defined by inheritance, both of which are based on linked lists. A linked list, in turn, is defined by aggregation. Example 7.19 shows a non-generic implementation of these data structures. The purpose of the example is to illustrate inheritance and aggregation, not industrial-strength implementation of queues and stacks. The class Node at (1) is straightforward, defining two fields: one denoting the data and the other denoting the next node in the list. The class LinkedList at (2) keeps track of the list by managing head and tail references. Nodes can be inserted in the front or back, but deleted only from the front of the list. Figure 7.7 Example 7.19 Implementing Data Structures by Inheritance and Aggregation Implementing Data Structures by Inheritance and Aggregation Click here to view code image class Node { private Object data; private Node next; // (1) // Data // Next node // Constructor for initializing data and reference to the next node. Node(Object data, Node next) { this.data = data; this.next = next; } // Methods: public void public Object public void public Node setData(Object obj) getData() setNext(Node node) getNext() { { { { data = return next = return obj; } data; } node; } next; } } //______________________________________________________________________________ class LinkedList { // (2) protected Node head = null; protected Node tail = null; // Methods: public boolean isEmpty() { return head == null; } public void insertInFront(Object dataObj) { if (isEmpty()) head = tail = new Node(dataObj, null); WOW! eBook www.wowebook.org else head = new Node(dataObj, head); } public void insertAtBack(Object dataObj) { if (isEmpty()) head = tail = new Node(dataObj, null); else { tail.setNext(new Node(dataObj, null)); tail = tail.getNext(); } } public Object deleteFromFront() { if (isEmpty()) return null; Node removed = head; if (head == tail) head = tail = null; else head = head.getNext(); return removed.getData(); } } //______________________________________________________________________________ class QueueByAggregation { // (3) private LinkedList qList; // Constructor QueueByAggregation() { qList = new LinkedList(); } // Methods: public boolean isEmpty() { return qList.isEmpty(); } public void enqueue(Object item) { qList.insertAtBack(item); } public Object dequeue() { if (qList.isEmpty()) return null; return qList.deleteFromFront(); } public Object peek() { return (qList.isEmpty() ? null : qList.head.getData()); } } //______________________________________________________________________________ class StackByInheritance extends LinkedList { // (4) public void push(Object item) { insertInFront(item); } public Object pop() { if (isEmpty()) return null; return deleteFromFront(); } public Object peek() { return (isEmpty() ? null : head.getData()); } } //______________________________________________________________________________ public class Client { // (5) public static void main(String[] args) { String string1 = “Queues are boring to stand in!”; int length1 = string1.length(); QueueByAggregation queue = new QueueByAggregation(); for (int i = 0; i getClass() Returns the runtime class of the object, which is represented by an object of the class java.lang.Class at runtime. Click here to view code image protected Object clone() throws CloneNotSupportedException New objects that are exactly the same (i.e., have identical states) as the current object can be created by using the clone() method; that is, primitive values and reference values are copied. This is called shallow copying. A class can override this method to provide its own notion of cloning. For example, cloning a composite object by recursively cloning the constituent objects is called deep copying. WOW! eBook www.wowebook.org When overridden, the method in the subclass is usually declared as public to allow any client to clone objects of the class. If the overriding clone() method in the subclass relies on the clone() method in the Object class (i.e., a shallow copy), the subclass must implement the Cloneable marker interface to indicate that its objects can be safely cloned. Otherwise, the clone() method in the Object class will throw a checked CloneNotSupportedException. String toString() If a subclass does not override this method, it returns a textual representation of the object, which has the following format: Click here to view code image “ @ ” Since the default hash value of an object is an int value, this value is printed as a hexadecimal number (e.g., 3e25a5). This method is usually overridden. The method call System.out.println(objRef) will implicitly convert its argument to a textual representation by calling the toString() method on the argument. See also the binary string concatenation operator +, discussed in §5.7 on page 169. Click here to view code image protected void finalize() throws Throwable This method is discussed in connection with garbage collection (§9.4, p. 390). It is called on an object just before it is garbage collected, so that any cleaning up can be done. However, the default finalize() method in the Object class does not do anything useful. In addition, the Object class provides support for thread communication in synchronized code, through the following methods. This important topic is beyond the scope of this book. Click here to view code image final final final final final void void void void void wait(long timeout) throws InterruptedException wait(long timeout, int nanos) throws InterruptedException wait() throws InterruptedException notify() notifyAll() A thread invokes these methods on the object whose lock it holds. A thread waits for notification by another thread. Example 8.1 Methods in the Object Class Click here to view code image // File: ObjectMethods.java class MyClass implements Cloneable { @Override public MyClass clone() { MyClass obj = null; WOW! eBook www.wowebook.org try { obj = (MyClass) super.clone(); } // Calls overridden method. catch (CloneNotSupportedException e) { System.out.println(e);} return obj; } } //______________________________________________________________________________ public class ObjectMethods { public static void main(String[] args) { // Two objects of MyClass. MyClass obj1 = new MyClass(); MyClass obj2 = new MyClass(); // Two strings. String str1 = new String(“WhoAmI”); String str2 = new String(“WhoAmI”); // Method hashCode() overridden in String class. // Strings that are equal have the same hash code. System.out.println(“hash code for str1: ” + str1.hashCode()); System.out.println(“hash code for str2: ” + str2.hashCode() + “\n”); // Hash codes are different for different MyClass objects. System.out.println(“hash code for MyClass obj1: ” + obj1.hashCode()); System.out.println(“hash code for MyClass obj2: ” + obj2.hashCode()+”\n”); // Method equals() overridden in the String class. System.out.println(“str1.equals(str2): ” + str1.equals(str2)); System.out.println(“str1 == str2: ” + (str1 == str2) + “\n”); // Method equals() from the Object class called. System.out.println(“obj1.equals(obj2): ” + obj1.equals(obj2)); System.out.println(“obj1 == obj2: ” + (obj1 == obj2) + “\n”); // The runtime object that represents the class of an object. Class rtStringClass = str1.getClass(); Class rtMyClassClass = obj1.getClass(); // The name of the class represented by the runtime object. System.out.println(“Class for str1: ” + rtStringClass); System.out.println(“Class for obj1: ” + rtMyClassClass + “\n”); // The toString() method is overridden in the String class. String textRepStr = str1.toString(); String textRepObj = obj1.toString(); System.out.println(“Text representation of str1: ” + textRepStr); System.out.println(“Text representation of obj1: ” + textRepObj + “\n”); // Shallow copying of arrays. MyClass[] array1 = {new MyClass(), new MyClass(), new MyClass()}; MyClass[] array2 = array1.clone(); // Array objects are different, but share the element objects. System.out.println(“array1 == array2: ” + (array1 == array2)); for(int i = 0; i < array1.length; i++) { System.out.println(“array1[” + i + “] == array2[” + i + “] : ” + (array1[i] == array2[i])); } System.out.println(); // Clone an object of MyClass. MyClass obj3 = obj1.clone(); System.out.println(“hash code for MyClass obj3: ” + obj3.hashCode()); System.out.println(“obj1 == obj3: ” + (obj1 == obj3)); WOW! eBook www.wowebook.org } } Probable output from the program: Click here to view code image hash code for str1: -1704812257 hash code for str2: -1704812257 hash code for MyClass obj1: 25669322 hash code for MyClass obj2: 14978587 str1.equals(str2): true str1 == str2: false obj1.equals(obj2): false obj1 == obj2: false Class for str1: class java.lang.String Class for obj1: class MyClass Text representation of str1: WhoAmI Text representation of obj1: MyClass@187aeca array1 == array1[0] array1[1] array1[2] array2: false == array2[0] : true == array2[1] : true == array2[2] : true hash code for MyClass obj3: 19770577 obj1 == obj3: false Review Questions 8.1 What is the return type of the hashCode() method in the Object class? Select the one correct answer. (a) String (b) int (c) long (d) Object (e) Class 8.2 Which of the following statements is true? Select the one correct answer. (a) If the references x and y denote two different objects, the expression x.equals(y) is always false. (b) If the references x and y denote two different objects, the expression (x.hashCode() == y.hashCode()) is always false. (c) The hashCode() method in the Object class is declared as final. WOW! eBook www.wowebook.org (d) The equals() method in the Object class is declared as final. (e) All arrays have a method named clone. 8.3 Which exception can the clone() method of the Object class throw? Select the one correct answer. (a) CloneNotSupportedException (b) NotCloneableException (c) IllegalCloneException (d) NoClonesAllowedException 8.3 The Wrapper Classes Wrapper classes were introduced with the discussion of the primitive data types (§2.2, p. 37), and also in connection with boxing and unboxing of primitive values (§5.1, p. 145). Primitive values in Java are not objects. To manipulate these values as objects, the java.lang package provides a wrapper class for each of the primitive data types (shown in the bottom left of Figure 8.2). The name of the wrapper class is the name of the primitive data type with a uppercase letter, except for int (Integer) and char (Character). All wrapper classes are final. The objects of all wrapper classes that can be instantiated are immutable; in other words, the value in the wrapper object cannot be changed. WOW! eBook www.wowebook.org Figure 8.2 Converting Values among Primitive, Wrapper, and String Types Although the Void class is considered a wrapper class, it does not wrap any primitive value and is not instantiable (i.e., has no public constructors). It just denotes the Class object representing the keyword void. The Void class will not be discussed further in this section. In addition to the methods defined for constructing and manipulating objects of primitive values, the wrapper classes define useful constants, fields, and conversion methods. Common Wrapper Class Constructors The Character class has only one public constructor, taking a char value as its parameter. The other wrapper classes all have two public one-argument constructors: one takes a primitive value and the other takes a string. WrapperType(type v) WrapperType(String str) The type is a primitive data type. The string argument is converted to a primitive value that corresponds to the WrapperType. An unchecked NumberFormatException is thrown if the string cannot be converted to a primitive value that corresponds to a numeric WrapperType. WOW! eBook www.wowebook.org Wrapping Primitive Values in Objects Boxing is a convenient way to wrap a primitive value in an object ((1a) in Figure 8.2 and §5.1, p. 145). Character Boolean Integer Double charObj1 boolObj1 intObj1 doubleObj1 = = = = ‘\n’; true; 2014; 3.14; A constructor that takes a primitive value can be used to create wrapper objects ((1b) in Figure 8.2). Click here to view code image Character Boolean Integer Double charObj1 boolObj1 intObj1 doubleObj1 = = = = new new new new Character(‘\n’); Boolean(true); Integer(2014); Double(3.14); We can also use the valueOf() method that takes the primitive value to wrap as an argument ((1c) in Figure 8.2). Click here to view code image Character Boolean Integer Double charObj1 boolObj1 intObj1 doubleObj1 = = = = Character.valueOf(‘\n’); Boolean.valueOf(true); Integer.valueOf(2014); Double.valueOf(3.14); Converting Strings to Wrapper Objects A constructor that takes a String object representing the primitive value can also be used to create wrapper objects. The constructors for the numeric wrapper types throw an unchecked NumberFormatException if the String parameter does not parse to a valid number ((2a) in Figure 8.2). Click here to view code image Boolean Boolean Integer Double Long boolObj2 boolObj3 intObj2 doubleObj2 longObj1 = = = = = new new new new new Boolean(“TrUe”); Boolean(“XX”); Integer(“2014”); Double(“3.14”); Long(“3.14”); // case ignored: true // false // NumberFormatException Common Wrapper Class Utility Methods Converting Strings to Wrapper Objects Each wrapper class (except Character) defines the static method valueOf(String str) that returns the wrapper object corresponding to the primitive value represented by the String object passed as an argument ((6b) in Figure 8.2). This method for the numeric wrapper types also throws a NumberFormatException if the String parameter is not a valid number. Click here to view code image static WrapperType valueOf(String str) WOW! eBook www.wowebook.org Click here to view code image Boolean boolObj4 = Boolean.valueOf(“false”); Integer intObj3 = Integer.valueOf(“1949”); Double doubleObj3 = Double.valueOf(“-3.0”); In addition to the one-argument valueOf() method, the integer wrapper classes define an overloaded static valueOf() method that can take a second argument. This argument specifies the base (or radix) in which to interpret the string representing the signed integer in the first argument. Click here to view code image static IntegerWrapperType valueOf(String str, int base) throws NumberFormatException Click here to view code image Byte Short 10. Integer -10. Long -10L. byteObj1 = Byte.valueOf(“1010”, 2); shortObj2 = Short.valueOf(“12”, 8); // Decimal value 10 // Not “\012”. Decimal value intObj4 = Integer.valueOf(“-a”, 16); // Not “-0xa”. Decimal value longObj2 = Long.valueOf(“-a”, 16); // Not “-0xa”. Decimal value Converting Wrapper Objects to Strings Each wrapper class overrides the toString() method from the Object class. The overriding method returns a String object containing the string representation of the primitive value in the wrapper object ((3) in Figure 8.2). String toString() Click here to view code image String String String String charStr boolStr intStr doubleStr = = = = charObj1.toString(); boolObj2.toString(); intObj1.toString(); doubleObj1.toString(); // // // // “\n” “true” “2014” “3.14” Converting Primitive Values to Strings Each wrapper class defines a static method toString(type v) that returns the string corresponding to the primitive value of type, which is passed as an argument ((6a) in Figure 8.2). Click here to view code image static String toString(type v) Click here to view code image String String String String charStr2 boolStr2 intStr2 doubleStr2 = = = = Character.toString(‘\n’); Boolean.toString(true); Integer.toString(2014); Double.toString(3.14); // // // // “\n” “true” Base 10. “2014” “3.14” For integer primitive types, the base is assumed to be 10. For floating-point numbers, the WOW! eBook www.wowebook.org textual representation (decimal form or scientific notation) depends on the sign and the magnitude (absolute value) of the number. The NaN value, positive infinity, and negative infinity will result in the strings "NaN", "Infinity", and "-Infinity", respectively. In addition, the wrapper classes Integer and Long define methods for converting integers to string representations in decimal, binary, octal, and hexadecimal notation (p. 353). Converting Wrapper Objects to Primitive Values Unboxing is a convenient way to unwrap the primitive value in a wrapper object ((4a) in Figure 8.2 and §5.1, p. 145). Click here to view code image char boolean int double c b i d = = = = charObj1; boolObj2; intObj1; doubleObj1; // // // // ‘\n’ true 2014 3.14 Each wrapper class defines a typeValue() method that returns the primitive value in the wrapper object ((4b) in Figure 8.2). type typeValue() Click here to view code image char boolean int double c b i d = = = = charObj1.charValue(); boolObj2.booleanValue(); intObj1.intValue(); doubleObj1.doubleValue(); // // // // ‘\n’ true 2014 3.14 In addition, each numeric wrapper class defines typeValue() methods for converting the primitive value in the wrapper object to a value of any numeric primitive data type. These methods are discussed later. Wrapper Comparison, Equality, and Hash Code Each wrapper class implements the Comparable interface, which defines the following method: int compareTo(Type obj2) This method returns a value that is less than, equal to, or greater than zero, depending on whether the primitive value in the current wrapper Type object is less than, equal to, or greater than the primitive value in the wrapper Type object denoted by argument obj2. Click here to view code image // Comparisons based on objects created earlier Character charObj2 = ‘a’; int result1 = charObj1.compareTo(charObj2); int result2 = intObj1.compareTo(intObj3); int result3 = doubleObj1.compareTo(doubleObj2); int result4 = doubleObj1.compareTo(intObj1); WOW! eBook www.wowebook.org // result1 < 0 // result2 > 0 // result == 0 // Compile-time error! Each wrapper class overrides the equals() method from the Object class. The overriding method compares two wrapper objects for object value equality. boolean equals(Object obj2) Click here to view code image // Comparisons based boolean charTest = boolean boolTest = boolean intTest = boolean doubleTest = boolean test = on objects created earlier charObj1.equals(charObj2); boolObj2.equals(Boolean.FALSE); intObj1.equals(intObj2); doubleObj1.equals(doubleObj2); intObj1.equals(new Long(2014)); // // // // // false false true true false. Not same type. The following values are interned when they are wrapped during boxing. That is, only one wrapper object exists in the program for these primitive values when boxing is applied: • The boolean values true or false • A byte • A char with a Unicode value in the interval [\u0000, \u007f] (i.e., decimal interval [0, 127]) • An int or short value in the interval [-128, 127] If references w1 and w2 refer to two wrapper objects that box the same value, which is among the ones mentioned here, then w1 == w2 is always true. In other words, for the values listed previously, object equality and reference equality give the same result. Click here to view code image // Reference and object equality Byte bRef1 = 10; Byte bRef2 = 10; System.out.println(bRef1 == bRef2); System.out.println(bRef1.equals(bRef2)); // true // true Integer iRef1 = 1000; Integer iRef2 = 1000; System.out.println(iRef1 == iRef2); System.out.println(iRef1.equals(iRef2)); // false // true Each wrapper class also overrides the hashCode() method in the Object class. The overriding method returns a hash value based on the primitive value in the wrapper object. int hashCode() Click here to view code image int index = charObj1.hashCode(); // 10 (‘\n’) Numeric Wrapper Classes The numeric wrapper classes Byte, Short, Integer, Long, Float, and Double are all subclasses of the abstract class Number (Figure 8.1). Each numeric wrapper class defines an assortment of constants, including the minimum WOW! eBook www.wowebook.org and maximum values of the corresponding primitive data type: NumericWrapperType.MIN_VALUE NumericWrapperType.MAX_VALUE The following code retrieves the minimum and maximum values of various numeric types: Click here to view code image byte minByte = Byte.MIN_VALUE; int maxInt = Integer.MAX_VALUE; double maxDouble = Double.MAX_VALUE; // -128 // 2147483647 // 1.7976931348623157e+308 Converting Numeric Wrapper Objects to Numeric Primitive Types Each numeric wrapper class defines the following set of typeValue() methods for converting the primitive value in the wrapper object to a value of any numeric primitive type: byte short int long float double byteValue() shortValue() intValue() longValue() floatValue() doubleValue() See also (4b) in Figure 8.2. The following code shows conversion of values in numeric wrapper objects to any numeric primitive type: Click here to view code image Byte byteObj2 = new Byte((byte) 16); Integer intObj5 = new Integer(42030); Double doubleObj4 = new Double(Math.PI); // Cast mandatory short long int double // (1) shortVal longVal intVal doubleVal = = = = intObj5.shortValue(); byteObj2.longValue(); doubleObj4.intValue(); intObj5.doubleValue(); // (2) Truncation Notice the potential for loss of information at (1) and (2), when the primitive value in a wrapper object is converted to a narrower primitive data type. Converting Strings to Numeric Values Each numeric wrapper class defines a static method parseType(String str), which returns the primitive numeric value represented by the String object passed as an argument. The Type in the method name parseType stands for the name of a numeric wrapper class, except for the name of the Integer class, which is abbreviated to Int. These methods throw a NumberFormatException if the String parameter is not a valid argument ((5) in Figure 8.2.) WOW! eBook www.wowebook.org Click here to view code image static type parseType(String str) throws NumberFormatException Click here to view code image byte int int double value1 value2 value3 value4 = = = = Byte.parseByte(“16”); Integer.parseInt(“2010”); Integer.parseInt(“7UP”); Double.parseDouble(“3.14”); // parseInt, not parseInteger // NumberFormatException For the integer wrapper types, the overloaded static method parseType() can additionally take a second argument, which can specify the base in which to interpret the string representing the signed integer in the first argument: Click here to view code image type parseType(String str, int base) throws NumberFormatException Click here to view code image byte short 10. int long value6 = Byte.parseByte(“1010”, 2); // Decimal value 10. value7 = Short.parseShort(“12”, 8); // “012”, not “\012”. Decimal value value8 = Integer.parseInt(“-a”, 16);// Not “-0xa”. Decimal value -10. value9 = Long.parseLong(“-a”, 16); // Not “-0xa”. Decimal value -10L. Converting Integer Values to Strings in Different Notations The wrapper classes Integer and Long provide static methods for converting integers to string representations in decimal, binary, octal, and hexadecimal notation. Some of these methods from the Integer class are listed here, but analogous methods are also defined in the Long class. Example 8.2 demonstrates the use of these methods. Click here to view code image static String toBinaryString(int i) static String toHexString(int i) static String toOctalString(int i) These three methods return a string representation of the integer argument as an unsigned integer in base 2, 16, and 8, respectively, with no extra leading zeroes. Click here to view code image static String toString(int i, int base) static String toString(int i) The first method returns the minus sign '-' as the first character if the integer i is negative. In all cases, it returns the string representation of the magnitude of the integer i in the specified base. The last method is equivalent to the method toString(int i, int base), where the base has the value 10, and which returns the string representation as a signed decimal ((6a) in Figure 8.2). WOW! eBook www.wowebook.org Example 8.2 String Representation of Integers Click here to view code image public class IntegerRepresentation { public static void main(String[] args) { int positiveInt = +41; // 0b101001, 051, 0x29 int negativeInt = -41; // 0b11111111111111111111111111010111, -0b101001, // 037777777727, -051, 0xffffffd7, -0x29 System.out.println(“String representation for decimal value: ” + positiveInt); integerStringRepresentation(positiveInt); System.out.println(“String representation for decimal value: ” + negativeInt); integerStringRepresentation(negativeInt); } public static void integerStringRepresentation(int i) { System.out.println(“ Binary: ” + Integer.toBinaryString(i)); System.out.println(“ Octal: ” + Integer.toOctalString(i)); System.out.println(“ Hex: ” + Integer.toHexString(i)); System.out.println(“ Decimal: ” + Integer.toString(i)); System.out.println(“ System.out.println(“ System.out.println(“ System.out.println(“ System.out.println(“ Using toString(int i, int base) method:”); Base 2: ” + Integer.toString(i, 2)); Base 8: ” + Integer.toString(i, 8)); Base 16: ” + Integer.toString(i, 16)); Base 10: ” + Integer.toString(i, 10)); } } Output from the program: Click here to view code image String representation for decimal value: 41 Binary: 101001 Octal: 51 Hex: 29 Decimal: 41 Using toString(int i, int base) method: Base 2: 101001 Base 8: 51 Base 16: 29 Base 10: 41 String representation for decimal value: -41 Binary: 11111111111111111111111111010111 Octal: 37777777727 Hex: ffffffd7 Decimal: -41 Using toString(int i, int base) method: Base 2: -101001 Base 8: -51 Base 16: -29 Base 10: -41 The Class The Character class defines a myriad of constants, including the following, which represent the minimum and the maximum values of the char type (§2.2, p. 38): WOW! eBook www.wowebook.org Character.MIN_VALUE Character.MAX_VALUE The Character class also defines a plethora of static methods for handling various attributes of a character, and case issues relating to characters, as defined by the Unicode standard: Click here to view code image static static static static static static static static static static int boolean boolean boolean boolean boolean boolean char char char getNumericValue(char ch) isLowerCase(char ch) isUpperCase(char ch) isTitleCase(char ch) isDigit(char ch) isLetter(char ch) isLetterOrDigit(char ch) toUpperCase(char ch) toLowerCase(char ch) toTitleCase(char ch) The following code converts a lowercase character to an uppercase character: Click here to view code image char ch = ‘a’; if (Character.isLowerCase(ch)) ch = Character.toUpperCase(ch); The Class In addition to the common utility methods for wrapper classes discussed earlier in this section, the Boolean class defines the following wrapper objects to represent the primitive values true and false, respectively: Boolean.TRUE Boolean.FALSE Converting Strings to Boolean Values The wrapper class Boolean defines the following static method, which returns the boolean value true only if the String argument is equal to the string "true", ignoring the case; otherwise, it returns the boolean value false. Note that this method does not throw any exceptions, as its numeric counterparts do. Click here to view code image static boolean parseBoolean(String str) Click here to view code image boolean boolean boolean boolean boolean b1 b2 b3 b4 b5 = = = = = Boolean.parseBoolean(“TRUE”); Boolean.parseBoolean(“true”); Boolean.parseBoolean(“false”); Boolean.parseBoolean(“FALSE”); Boolean.parseBoolean(“not true”); WOW! eBook www.wowebook.org // // // // // true. true. false. false. false. Review Questions 8.4 Which of the following are wrapper classes? Select the three correct answers. (a) java.lang.Void (b) java.lang.Int (c) java.lang.Boolean (d) java.lang.Long (e) java.lang.String 8.5 Which of the following classes do not extend the java.lang.Number class? Select the two correct answers. (a) java.lang.Float (b) java.lang.Byte (c) java.lang.Character (d) java.lang.Boolean (e) java.lang.Short 8.6 Which of these classes define immutable objects? Select the three correct answers. (a) Character (b) Byte (c) Number (d) Short (e) Object 8.7 Which of these classes have a single-parameter constructor taking a string? Select the two correct answers. (a) Void (b) Integer (c) Boolean (d) Character (e) Object 8.8 Which of the wrapper classes have a booleanValue() method? WOW! eBook www.wowebook.org Select the one correct answer (a) All wrapper classes (b) All wrapper classes except Void (c) All wrapper classes that also implement the compareTo() method (d) All wrapper classes extending Number (e) Only the class Boolean 8.9 Which statements are true about wrapper classes? Select the two correct answers. (a) String is a wrapper class. (b) Double has a compareTo() method. (c) Character has a intValue() method. (d) Byte extends Number. 8.10 What will the following program print when compiled and run? Click here to view code image public class RQ200A70 { public static void main(String[] args) { Integer i = new Integer(-10); Integer j = new Integer(-10); Integer k = -10; System.out.print((i==j) + “|”); System.out.print(i.equals(j) + “|”); System.out.print((i==k) + “|”); System.out.print(i.equals(k)); } } Select the one correct answer. (a) false|true|false|true (b) true|true|true|true (c) false|true|true|true (d) true|true|false|true (e) None of the above. WOW! eBook www.wowebook.org 8.4 The Class Handling character sequences is supported through three final classes: String, StringBuilder, and StringBuffer. The Java platform uses the variable-length UTF-16 encoding to store characters in char arrays and in the string handling classes. The UTF-16 encoding allows characters whose Unicode values are in the range 0000 to 10FFFF. The char type represents Unicode values in the range 0000 to FFFF—that is, characters that can be represented in a single 16-bit word. As a consequence, the supplementary characters are represented by multiple char values, or multiple 16-bit words, when these are stored in a string or a char array. The string handling classes provide methods to handle the full range of characters in the UTF-16 encoding, but we will not dwell on the subject in this book. Immutability The String class implements immutable character strings, which are read-only once the string has been created and initialized. Operations on a String object that modify the characters return a new String object. The StringBuilder class implements dynamic character strings. The StringBuffer class is a thread-safe version of the StringBuilder class. This section discusses the class String that provides facilities for creating, initializing, and manipulating character strings. The next section discusses the StringBuilder and StringBuffer classes. Creating and Initializing Strings String Literals Revisited The easiest way to create a String object is to use a string literal: Click here to view code image String str1 = “You cannot change me!”; A string literal is a reference to a String object. The value in the String object is the character sequence enclosed in the double quotes of the string literal. Since a string literal is a reference, it can be manipulated like any other String reference. The reference value of a string literal can be assigned to another String reference: The reference str1 will denote the String object with the value "You cannot change me!" after the preceding assignment. A string literal can be used to invoke methods on its String object: Click here to view code image int strLength = “You cannot change me!”.length(); // 21 The compiler optimizes handling of string literals (and compile-time constant expressions that evaluate to strings): Only one String object is shared by all string-valued constant expressions with the same character sequence. Such strings are said to be interned, meaning that they share a unique String object if they have the same content. The WOW! eBook www.wowebook.org String class maintains a string literal pool where such strings are interned. Click here to view code image String str2 = “You cannot change me!”; // Already interned. Both String references str1 and str2 denote the same interned String object initialized with the character string: "You cannot change me!" So does the reference str3 in the following code. The compile-time evaluation of the constant expression involving the two string literals results in a string that is already interned: Click here to view code image String str3 = “You cannot” + ” change me!”; // Compile-time constant expression In the following code, both the references can1 and can2 denote the same interned String object, which contains the string "7Up": Click here to view code image String can1 = 7 + “Up”; String can2 = “7Up”; // Value of compile-time constant expression: “7Up” // “7Up” However, in the following code, the reference can4 denotes a new String object that will have the value "7Up" at runtime: Click here to view code image String word = “Up”; String can4 = 7 + word; // Not a compile-time constant expression. The sharing of String objects between string-valued constant expressions poses no problem, since the String objects are immutable. Any operation performed on one String reference will never have any effect on the usage of other references denoting the same object. The String class is also declared as final, so that no subclass can override this behavior. String Constructors The String class has numerous constructors to create and initialize String objects based on various types of arguments. Here we present a few selected constructors: WOW! eBook www.wowebook.org String() This constructor creates a new String object, whose content is the empty string, "". String(String str) This constructor creates a new String object, whose contents are the same as those of the String object passed as argument. Click here to view code image String(char[] value) String(char[] value, int offset, int count) These constructors create a new String object, whose contents are copied from a char array. The second constructor allows extraction of a certain number of characters (count) from a given offset in the array. String(StringBuilder builder) String(StringBuffer buffer) These constructors allow interoperability with the StringBuilder and StringBuffer classes, respectively. Note that using a constructor creates a brand-new String object; using a constructor does not intern the string. A reference to an interned string can be obtained by calling the intern() method in the String class—although in practice, there is usually no reason to do so. In the following code, the String object denoted by str4 is different from the interned String object passed as an argument: Click here to view code image String str4 = new String(“You cannot change me!”); Constructing String objects can also be done from arrays of bytes, arrays of characters, or string builders: Click here to view code image byte[] bytes = {97, 98, 98, 97}; char[] characters = {‘a’, ‘b’, ‘b’, ‘a’}; StringBuilder strBuilder = new StringBuilder(“abba”); //… String byteStr = new String(bytes); // Using array of bytes: “abba” String charStr = new String(characters); // Using array of chars: “abba” String buildStr = new String(strBuilder); // Using string builder: “abba” In Example 8.3, note that the reference str1 does not denote the same String object as the references str4 and str5. Using the new operator with a String constructor always creates a new String object. The expression "You cannot" + words is not a constant expression and, therefore, results in the creation of a new String object. The local references str2 and str3 in the main() method and the static reference str1 in the Auxiliary class all denote the same interned string. Object value equality is hardly surprising between these references. Indeed, it might be tempting to use the operator == WOW! eBook www.wowebook.org for object value equality of string literals, but this is not advisable. Example 8.3 String Construction and Equality Click here to view code image // File: StringConstruction.java class Auxiliary { static String str1 = “You cannot change me!”; // Interned } //______________________________________________________________________________ public class StringConstruction { static String str1 = “You cannot change me!”; // Interned public static void main(String[] args) { String emptyStr = new String(); System.out.println(“emptyStr: "” + emptyStr + “"”); // ”” String str2 = “You cannot change me!”; String str3 = “You cannot” + ” change me!”; String str4 = new String(“You cannot change me!”); // Interned // Interned // New String object String words = ” change me!”; String str5 = “You cannot” + words; // New String object System.out.println(“str1 == str2: ” + (str1 == str2)); // (1) str1.equals(str2)); // (2) true System.out.println(“str1.equals(str2): ” + true System.out.println(“str1 == str3: ” + (str1 == str3)); // (3) true System.out.println(“str1.equals(str3): ” + str1.equals(str3)); // (4) true System.out.println(“str1 == str4: ” + (str1 == str4)); false System.out.println(“str1.equals(str4): ” + str1.equals(str4)); true // (5) System.out.println(“str1 == str5: ” + (str1 == str5)); false System.out.println(“str1.equals(str5): ” + str1.equals(str5)); true // (7) System.out.println(“str1 == Auxiliary.str1: ” + (str1 == Auxiliary.str1)); // (9) true System.out.println(“str1.equals(Auxiliary.str1): ” + str1.equals(Auxiliary.str1)); // (10) true System.out.println(“"You cannot change me!".length(): ” + “You cannot change me!”.length());// (11) 21 } } Output from the program: Click here to view code image emptyStr: ”” str1 == str2: true str1.equals(str2): true WOW! eBook www.wowebook.org // (6) // (8) str1 == str3: true str1.equals(str3): true str1 == str4: false str1.equals(str4): true str1 == str5: false str1.equals(str5): true str1 == Auxiliary.str1: true str1.equals(Auxiliary.str1): true “You cannot change me!”.length(): 21 The Interface This interface defines a readable sequence of char values. It is implemented by all three classes: String, StringBuilder, and StringBuffer. Many methods in these classes accept arguments of this interface type, and specify it as their return type. This interface facilitates interoperability between these classes. It defines the following methods: char charAt(int index) A character at a particular index in a sequence can be read using the charAt() method. The first character is at index 0 and the last one at index 1 less than the number of characters in the string. If the index value is not valid, an IndexOutOfBoundsException is thrown. int length() This method returns the number of char values in this sequence. Click here to view code image CharSequence subSequence(int start, int end) This method returns a new CharSequence that is a subsequence of this sequence. Characters from the current sequence are read from index start to the index end-1, inclusive. String toString() This method returns a string containing the characters in this sequence in the same order as this sequence. Reading Characters from a String The following methods can be used for character-related operations on a string: WOW! eBook www.wowebook.org char charAt(int index) This method is defined in the CharSequence interface, which the String class implements (p. 360). char[] toCharArray() This method returns a new character array, with length equal to the length of this string, that contains the characters in this string. Click here to view code image void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) This method copies characters from the current string into the destination character array. Characters from the current string are read from index srcBegin to the index srcEnd-1, inclusive. They are copied into the destination array (dst), starting at index dstBegin and ending at index dstbegin+(srcEnd-srcBegin)-1. The number of characters copied is (srcEnd-srcBegin). An Index-OutOfBoundsException is thrown if the indices do not meet the criteria for the operation. int length() This method is defined in the CharSequence interface, which the String class implements (p. 360). boolean isEmpty() This method returns true if the length of the string is 0, and false otherwise. Example 8.4 uses some of these methods at (3), (4), (5), and (6). The program prints the frequency of a character in a string and illustrates copying from a string into a character array. WOW! eBook www.wowebook.org Example 8.4 Reading Characters from a String Click here to view code image public class ReadingCharsFromString { public static void main(String[] args) { int[] frequencyData = new int [Character.MAX_VALUE]; String str = “You cannot change me!”; // (1) // (2) // Count the frequency of each character in the string. for (int i = 0; i < str.length(); i++) { // (3) try { frequencyData[str.charAt(i)]++; // (4) } catch(StringIndexOutOfBoundsException e) { System.out.println(“Index error detected: “+ i +” not in range.”); } } // Print the character frequency. System.out.println(“Character frequency for string: "” + str + “"”); for (int i = 0; i < frequencyData.length; i++) { if (frequencyData[i] != 0) System.out.println((char)i + ” (code “+ i +”): ” + frequencyData[i]); } System.out.println(“Copying into a char array:”); char[] destination = new char [str.length() - 3]; less. str.getChars( 0, 7, destination, 0); str.getChars(10, str.length(), destination, 7); me!” // 3 characters // (5) “You can” // (6) ” change // “not” not copied. // Print the character array. for (int i = 0; i < destination.length; i++) { System.out.print(destination[i]); } System.out.println(); } } Output from the program: Click here to view code image Character Frequency for string: “You cannot change me!” (code 32): 3 ! (code 33): 1 Y (code 89): 1 a (code 97): 2 c (code 99): 2 e (code 101): 2 g (code 103): 1 h (code 104): 1 m (code 109): 1 n (code 110): 3 o (code 111): 2 t (code 116): 1 u (code 117): 1 Copying into a char array: You can change me! WOW! eBook www.wowebook.org In Example 8.4, the frequencyData array at (1) stores the frequency of each character that can occur in a string. The string in question is declared at (2). Since a char value is promoted to an int value in arithmetic expressions, it can be used as an index in an array. Each element in the frequencyData array functions as a frequency counter for the character corresponding to the index value of the element: Click here to view code image frequencyData[str.charAt(i)]++; // (4) The calls to the getChars() method at (5) and (6) copy particular substrings from the string into designated places in the destination array, before printing the whole character array. Comparing Strings Characters are compared based on their Unicode values. Click here to view code image boolean test = ‘a’ < ‘b’; // true since 0x61 < 0x62 Two strings are compared lexicographically, as in a dictionary or telephone directory, by successively comparing their corresponding characters at each position in the two strings, starting with the characters in the first position. The string "abba" is less than "aha", since the second character 'b' in the string "abba" is less than the second character 'h' in the string "aha". The characters in the first position in each of these strings are equal. The following public methods can be used for comparing strings: Click here to view code image boolean equals(Object obj) boolean equalsIgnoreCase(String str2) The String class overrides the equals() method from the Object class. The String class equals() method implements String object value equality as two String objects having the same sequence of characters. The equalsIgnoreCase() method does the same, but ignores the case of the characters. int compareTo(String str2) The String class implements the Comparable interface. The compareTo() method compares the two strings, and returns a value based on the outcome of the comparison: • The value 0, if this string is equal to the string argument • A value less than 0, if this string is lexicographically less than the string argument • A value greater than 0, if this string is lexicographically greater than the string argument WOW! eBook www.wowebook.org Here are some examples of string comparisons: Click here to view code image String strA = new String(“The Case was thrown out of Court”); String strB = new String(“the case was thrown out of court”); boolean b1 = strA.equals(strB); boolean b2 = strA.equalsIgnoreCase(strB); // false // true String str1 = new String(“abba”); String str2 = new String(“aha”); int compVal1 = str1.compareTo(str2); // negative value => str1 < str2 Character Case in a String Click here to view code image String String String String toUpperCase() toUpperCase(Locale locale) toLowerCase() toLowerCase(Locale locale) Note that the original string is returned if none of the characters needs its case changed, but a new String object is returned if any of the characters needs its case changed. These methods delegate the character-by-character case conversion to corresponding methods from the Character class. These methods use the rules of the (default) locale (returned by the method Locale.getDefault()), which embodies the idiosyncrasies of a specific geographical, political, or cultural region regarding number/date/currency formats, character classification, alphabet (including case idiosyncrasies), and other localizations. Example of case in strings: Click here to view code image String strA = new String(“The Case was thrown out of Court”); String strB = new String(“the case was thrown out of court”); String strC = strA.toLowerCase(); String strD = strB.toLowerCase(); object String strE = strA.toUpperCase(); boolean test1 = strC == strA; boolean test2 = strD == strB; boolean test3 = strE == strA; // Case conversion => New String object: // “the case was thrown out of court” // No case conversion => Same String // Case conversion => New String object: // “THE CASE WAS THROWN OUT OF COURT” // false // true // false WOW! eBook www.wowebook.org Concatenation of Strings Concatenation of two strings results in a new string that consists of the characters of the first string followed by the characters of the second string. The overloaded operator + for string concatenation is discussed in §5.7, p. 169. In addition, the following method can be used to concatenate two strings: String concat(String str) The concat() method does not modify the String object on which it is invoked, as String objects are immutable. Instead, the concat() method returns a reference to a brand-new String object: Click here to view code image String billboard = “Just”; billboard.concat(” lost in space.”); // (1) Returned reference value not stored. System.out.println(billboard); // (2) “Just” billboard = billboard.concat(” advertise”).concat(” here.”); // (3) Chaining. System.out.println(billboard); // (4) “Just advertise here.” At (1), the reference value of the String object returned by the method concat() is not stored. This String object becomes inaccessible after (1). We see that the reference billboard still denotes the string literal "Just" at (2). At (3), two method calls to the concat() method are chained. The first call returns a reference value to a new String object, whose content is "Just advertise". The second method call is invoked on this String object using the reference value that was returned in the first method call. The second call results in yet another new String object, whose content is "Just advertise here.". The reference value of this String object is assigned to the reference billboard. Because String objects are immutable, the creation of the temporary String object with the content "Just advertise" is inevitable at (3). Some more examples of string concatenation follow: Click here to view code image String motto = new String(“Program once”); motto += “, execute everywhere.”; motto = motto.concat(” Don’t bet on it!”); // (1) // (2) // (3) Note that a new String object is assigned to the reference motto each time in the assignments at (1), (2), and (3). The String object with the contents "Program once" becomes inaccessible after the assignment at (2). The String object with the contents "Program once, execute everywhere." becomes inaccessible after (3). The reference motto denotes the String object with the following contents after execution of the assignment at (3): Click here to view code image “Program once, execute everywhere. Don’t bet on it!” WOW! eBook www.wowebook.org Joining of Objects One operation commonly performed on a sequence of strings is to format them so that each string is separated from the next one by a delimiter. For example, given the following sequence of strings: “2014” “January” “11” we wish to format them so that individual strings are separated by the delimiter “/”: “2014/January/11” The following static methods in the String class can be used for this purpose: Click here to view code image static String join(CharSequence delimiter, CharSequence… elements) static String join(CharSequence delimiter, Iterable elements) Both static methods return a new String composed of copies of the CharSequence elements joined together with a copy of the specified CharSequence delimiter. Thus, the resulting string is composed of textual representations of the elements separated by the textual representation of the specified delimiter. If an element is null, the string "null" is added as its textual representation. If the delimiter is null, a NullPointerException is thrown. Note that both the individual strings and the delimiter string are CharSequence objects. The examples in this section use String and StringBuilder objects that implement the CharSequence interface (p. 360). An Iterable provides an iterator to traverse over its elements. The following examples use an ArrayList (§10.1, p. 423) that implements the Iterable interface. The second join() method is then able to traverse the Iterable using the iterator. This method will accept only an Iterable whose elements are either of type CharSequence or subtypes of CharSequence. The first example shows joining of String objects. The first join() method is called in this case. Click here to view code image // (1) Joining individual String objects: String dateStr = String.join(“/”, “2014”, “January”, “11”); System.out.println(dateStr); // 2014/January/11 The second example shows joining of elements in a StringBuilder array. Again the first join() method is called, with the array being passed as the second parameter. Click here to view code image // (2) Joining elements in a StringBuilder array: StringBuilder left = new StringBuilder(“Left”); WOW! eBook www.wowebook.org StringBuilder right = new StringBuilder(“Right”); StringBuilder[] strBuilders = { left, right, left }; String march = String.join(“—>”, strBuilders); System.out.println(march); // Left—>Right—>Left The third example shows joining of elements in an ArrayList of StringBuilder. The second join() method is called, with the ArrayList being passed as the second parameter. Note that some of the elements of the ArrayList are null. Click here to view code image // (3) Joining elements in a StringBuilder list: ArrayList sbList = new ArrayList<>(); sbList.add(right); sbList.add(null); sbList.add(left); sbList.add(null); String resultStr = “[” + String.join(“, “, sbList) + “]”; System.out.println(resultStr); // [Right, null, Left, null] The last example shows joining of elements in an ArrayList of CharSequence. Again the second join() method is called, with the ArrayList being passed as the second parameter. Note that elements of the ArrayList are String and StringBuilder objects that are also of type CharSequence. Click here to view code image // (4) Joining elements in a CharSequence list: ArrayList charSeqList = new ArrayList<>(); charSeqList.add(right); charSeqList.add(left); // Add StringBuilder objects. charSeqList.add(“Right”); charSeqList.add(“Left”); // Add String objects. String resultStr2 = “<” + String.join(“; “, charSeqList) + “>”; System.out.println(resultStr2); // Searching for Characters and Substrings The following overloaded methods can be used to find the index of a character or the start index of a substring in a string. These methods search forward toward the end of the string. In other words, the index of the first occurrence of the character or substring is found. If the search is unsuccessful, the value –1 is returned. WOW! eBook www.wowebook.org Click here to view code image int indexOf(int ch) int indexOf(int ch, int fromIndex) The first method finds the index of the first occurrence of the argument character in a string. The second method finds the index of the first occurrence of the argument character in a string, starting at the index specified in the second argument. If the index argument is negative, the index is assumed to be 0. If the index argument is greater than the length of the string, it is effectively considered to be equal to the length of the string, resulting in the value -1 being returned. Click here to view code image int indexOf(String str) int indexOf(String str, int fromIndex) The first method finds the start index of the first occurrence of the substring argument in a string. The second method finds the start index of the first occurrence of the substring argument in a string, starting at the index specified in the second argument. The String class also defines a set of methods that search for a character or a substring, but the search is backward toward the start of the string. In other words, the index of the last occurrence of the character or substring is found. Click here to view code image int int int int lastIndexOf(int ch) lastIndexOf(int ch, int fromIndex) lastIndexOf(String str) lastIndexOf(String str, int fromIndex) The following methods can be used to create a string in which all occurrences of a character or a subsequence in a string have been replaced with another character or subsequence: Click here to view code image String replace(char oldChar, char newChar) String replace(CharSequence target, CharSequence replacement) The first method returns a new String object that is the result of replacing all occurrences of the oldChar in the current string with the newChar. The current string is returned if no occurrences of the oldChar can be found. The second method returns a new String object that is the result of replacing all occurrences of the character sequence target in the current string with the character sequence replacement. The current string is returned if no occurrences of the target can be found. The following methods can be used to test whether a string satisfies a given criterion: WOW! eBook www.wowebook.org Click here to view code image boolean contains(CharSequence cs) This method returns true if the current string contains the specified character sequence, and false otherwise. Click here to view code image boolean startsWith(String prefix) This method returns true if the current string starts with the character sequence specified by parameter prefix, and false otherwise. Click here to view code image boolean startsWith(String prefix, int index) This method returns true if the substring of the current string at the specified index starts with the character sequence specified by parameter prefix, and false otherwise. Click here to view code image boolean endsWith(String suffix) This method returns true if the current string ends with the character sequence specified by parameter suffix, and false otherwise. Examples of search and replace methods: Click here to view code image String funStr = “Java Jives”; // 0123456789 int int int int jInd1a jInd1b jInd2a jInd2b = = = = funStr.indexOf(‘J’); funStr.indexOf(‘J’, 1); funStr.lastIndexOf(‘J’); funStr.lastIndexOf(‘J’, 4); // // // // 0 5 5 0 // // // // // // 0 9 9 9 0 0 String banner = “One man, One vote”; // 01234567890123456 int int int int int int subInd1a subInd1b subInd2a subInd2b subInd2c subInd2d = = = = = = banner.indexOf(“One”); banner.indexOf(“One”, 3); banner.lastIndexOf(“One”); banner.lastIndexOf(“One”, 10); banner.lastIndexOf(“One”, 8); banner.lastIndexOf(“One”, 2); String newStr = funStr.replace(‘J’, ‘W’); String newBanner = banner.replace(“One”, “No”); boolean found1 = banner.contains(“One”); boolean found2 = newBanner.contains(“One”); // // // // “Wava Wives” “No man, No vote” true false String song = “Start me up!”; // 012345677890 boolean found3 = song.startsWith(“Start”); boolean notFound1 = song.startsWith(“start”); boolean found4 = song.startsWith(“me”, 6); boolean found5 = song.endsWith(“up!”); // // // // true false true true WOW! eBook www.wowebook.org boolean notFound2 = song.endsWith(“up”); // false Extracting Substrings String trim() This method can be used to create a string where whitespace (in fact, all characters with values less than or equal to the space character '\u0020') has been removed from the front (leading) and the end (trailing) of a string. Click here to view code image String substring(int startIndex) String substring(int startIndex, int endIndex) The String class provides these overloaded methods to extract substrings from a string. A new String object containing the substring is created and returned. The first method extracts the string that starts at the given index startIndex and extends to the end of the string. The end of the substring can be specified by using a second argument endIndex that is the index of the first character after the substring—that is, the last character in the substring is at index endIndex-1. If the index value is not valid, an IndexOutOfBoundsException is thrown. Examples of extracting substrings: Click here to view code image String utopia utopia String utopia = “\t\n Java Nation \n\t ”; = utopia.trim(); = utopia.substring(5); radioactive = utopia.substring(3,6); // “Java Nation” // “Nation” // “ion” Converting Primitive Values and Objects to Strings The String class overrides the toString() method in the Object class and returns the String object itself: String toString() This method is defined in the CharSequence interface, which the String class implements (p. 360). The String class also defines a set of static overloaded valueOf() methods to convert objects and primitive values into strings: WOW! eBook www.wowebook.org Click here to view code image static static static static String String String String valueOf(Object obj) valueOf(char[] charArray) valueOf(boolean b) valueOf(char c) All of these methods return a string representing the given parameter value. A call to the method with the parameter obj is equivalent to obj.toString() when obj is not null; otherwise, the "null" string is returned. The boolean values true and false are converted into the strings "true" and "false". The char parameter is converted to a string consisting of a single character. Click here to view code image static static static static String String String String valueOf(int i) valueOf(long l) valueOf(float f) valueOf(double d) The static valueOf() method, which accepts a primitive value as an argument, is equivalent to the static toString() method in the corresponding wrapper class for each of the primitive data types ((6a) and (6b) in §8.3, p. 347). Note that there are no valueOf() methods that accept a byte or a short. Examples of string conversions: Click here to view code image String anonStr = String.valueOf(“Make me a string.”); // “Make me a string.” String charStr = String.valueOf(new char[] {‘a’, ‘h’, ‘a’});// “aha” String boolTrue = String.valueOf(true); // “true” String doubleStr = String.valueOf(Math.PI); // “3.141592653589793” Formatted Strings We have used the System.out.printf() method to format values and print them to the terminal window (§1.9, p. 15). To just create the string with the formatted values, but not print the formatted result, we can use the following static method from the String class. It accepts the same arguments as the printf() method, and uses the same format specifications (Table 1.2, p. 20). Click here to view code image static String format(String format, Object… args) The method returns a string with the result of formatting the values in the varargs parameter args according to the String parameter format. The format string contains format specifications that determine how each subsequent value in the varargs parameter args will be formatted. Any error in the format string will result in a runtime exception. The following call to the format() method creates a formatted string with the three WOW! eBook www.wowebook.org values formatted according to the specified format string: Click here to view code image String formattedStr = String.format(“Formatted values|%5d|%8.3f|%5s|”, 2016, Math.PI, “Hi”); System.out.println(formattedStr); // Formatted values| 2016| 3.142| Hi| formattedStr = formattedStr.toUpperCase(); System.out.println(formattedStr); // FORMATTED VALUES| 2016| 3.142| HI| Other miscellaneous methods exist in the String class for pattern matching (matches()), splitting strings (split()), and converting a string to an array of bytes (getBytes()). The method hashCode() can be used to compute a hash value based on the characters in the string. Consult the Java SE platform API documentation for more details. Review Questions 8.11 Which of the following operators cannot have an operand of type String? Select the two correct answers. (a) + (b) (c) += (d) . (e) & 8.12 Which expression will extract the substring "kap", given the following declaration: String str = “kakapo”; Select the one correct answer. (a) str.substring(2, 2) (b) str.substring(2, 3) (c) str.substring(2, 4) (d) str.substring(2, 5) (e) str.substring(3, 3) 8.13 What will be the result of attempting to compile and run the following code? Click here to view code image class MyClass { public static String str1 String str2 String str3 void main(String[] args) { = “str1”; = “str2”; = “str3”; str1.concat(str2); WOW! eBook www.wowebook.org System.out.println(str3.concat(str1)); } } Select the one correct answer. (a) The code will fail to compile because the expression str3.concat(str1) will not result in a valid argument for the println() method. (b) The program will print str3str1str2 at runtime. (c) The program will print str3 at runtime. (d) The program will print str3str1 at runtime. (e) The program will print str3str2 at runtime. 8.14 Which statement about the trim() method of the String class is true? Select the one correct answer. (a) It returns a string where the leading whitespace of the original string has been removed. (b) It returns a string where the trailing whitespace of the original string has been removed. (c) It returns a string where both the leading and trailing whitespace of the original string has been removed. (d) It returns a string where all the whitespace of the original string has been removed. (e) None of the above. 8.15 Which of the following statements are true? Select the two correct answers. (a) String objects are immutable. (b) Subclasses of the String class can be mutable. (c) All wrapper classes are declared as final. (d) All objects have a public method named clone. (e) The expression ((new char[] {'o', 'k'}) instanceof String) is always true. 8.16 What will be the result of attempting to compile and run the following program? Click here to view code image public class RefEq { public static void main(String[] args) { String s = “ab” + “12”; String t = “ab” + 12; String u = new String(“ab12”); System.out.println((s==t) + ” ” + (s==u)); } WOW! eBook www.wowebook.org } Select the one correct answer. (a) The program will fail to compile. (b) The program will print false false at runtime. (c) The program will print false true at runtime. (d) The program will print true false at runtime. (e) The program will print true true at runtime. 8.17 Which of these parameter lists can be found in a constructor of the String class? Select the five correct answers. (a) () (b) (int capacity) (c) (char[] data) (d) (String str) (e) (CharSequence cs) (f) (StringBuilder sb) (g) (char c) (h) (Object o) (i) (String str, int beginIndex, int endIndex) (j) (char[] data, int offset, int count) 8.18 Which of the following methods is not defined in the String class? Select the one correct answer. (a) trim() (b) length() (c) concat(String) (d) hashCode() (e) reverse() 8.19 What will the following program print when run? Click here to view code image public class Uppity { public static void main(String[] args) { String str1 = “lower”, str2 = “LOWER”, str3 = “UPPER”; str1.toUpperCase(); str1.replace(“LOWER”,“UPPER”); System.out.println((str1.equals(str2)) + ” ” + (str1.equals(str3))); WOW! eBook www.wowebook.org } } Select the one correct answer. (a) The program will print false true. (b) The program will print false false. (c) The program will print true false. (d) The program will print true true. (e) The program will fail to compile. (f) The program will compile, but throw an exception at runtime. 8.20 What will the following program print when run? Click here to view code image public class FunCharSeq { private static void putO(String s1) { s1 = s1.trim(); s1 += “O”; } public static void main(String[] args) { String s1 = ” W “; putO(s1); s1.concat(“W”); System.out.println(“|” + s1 + “|”); } } Select the one correct answer. (a) |WOW| (b) | W W| (c) |WO| (d) | W | (e) The program will fail to compile. (f) The program will compile, but throw an exception at runtime. WOW! eBook www.wowebook.org 8.5 The and Classes Thread-Safety The classes StringBuilder and StringBuffer implement mutable sequences of characters. Both classes support the same operations, but the StringBuffer class is the thread-safe analog of the StringBuilder class. Certain operations on a string buffer are synchronized, so that when used by multiple threads, these operations are performed in an orderly manner. Note that a String object is also thread-safe—because it is immutable, a thread cannot change its state. String builders are preferred when heavy modification of character sequences is involved and synchronization of operations is not important. Although the rest of this section focuses on string builders, it is equally applicable to string buffers. Mutability In contrast to the String class, which implements immutable character sequences, the StringBuilder class implements mutable character sequences. Not only can the character sequences in a string builder be changed, but the capacity of the string builder can also change dynamically. The capacity of a string builder is the maximum number of characters that a string builder can accommodate before its size is automatically augmented. Although there is a close relationship between objects of the String and StringBuilder classes, these are two independent final classes, both directly extending the Object class. Hence, String references cannot be stored (or cast) to StringBuilder references, and vice versa. However, both classes implement the CharSequence interface (p. 360). The StringBuilder class provides various facilities for manipulating string builders: • Constructing string builders • Changing, deleting, and reading characters in string builders • Constructing strings from string builders • Appending, inserting, and deleting in string builders • Controlling string builder capacity Constructing String Builders The final class StringBuilder provides four constructors that create and initialize StringBuilder objects and set their initial capacity. WOW! eBook www.wowebook.org Click here to view code image StringBuilder(String str) StringBuilder(CharSequence charSeq) The contents of the new StringBuilder object are the same as the contents of the String object or the character sequence passed as an argument. The initial capacity of the string builder is set to the length of the argument sequence, plus room for 16 more characters. Click here to view code image StringBuilder(int initialCapacity) The new StringBuilder object has no content. The initial capacity of the string builder is set to the value of the argument, which cannot be less than 0. StringBuilder() This constructor also creates a new StringBuilder object with no content. The initial capacity of the string builder is set to 16 characters. Examples of StringBuilder object creation and initialization: Click here to view code image StringBuilder strBuilder1 = new StringBuilder(“Phew!”); 21 StringBuilder strBuilder2 = new StringBuilder(10); StringBuilder strBuilder3 = new StringBuilder(); // “Phew!”, capacity // ””, capacity 10 // ””, capacity 16 Reading and Changing Characters in String Builders Returns the number of characters in the string builder. These methods read and change the character at a specified index in the string builder, respectively. The first character is at index 0, and the last one is at index 1 less than the number of characters in the string builder. A IndexOutOfBounds– Exception is thrown if the index is not valid. Click here to view code image CharSequence subSequence(int start, int end) This method is implemented as part of the CharSequence interface (p. 360). The following is an example of reading and changing string builder contents: Click here to view code image StringBuilder strBuilder = new StringBuilder(“Javv”); // “Javv”, capacity 20 strBuilder.setCharAt(strBuilder.length()-1, strBuilder.charAt(1)); // “Java” WOW! eBook www.wowebook.org Constructing Strings from String Builders The StringBuilder class overrides the toString() method from the Object class (see also the CharSequence interface, p. 360). It returns the contents of a string builder in a String object. Click here to view code image String fromBuilder = strBuilder.toString(); // “Java” Differences between the String and StringBuilder Classes Since the StringBuilder class does not override the equals() method from the Object class, nor does it implement the Comparable interface, the contents of string builders should be converted to String objects for string comparison. The StringBuilder class also does not override the hashCode() method from the Object class. Again, a string builder can be converted to a String object to obtain a hash value. Appending, Inserting, and Deleting Characters in String Builders Appending, inserting, and deleting characters automatically results in adjustment of the string builder’s structure and capacity, if necessary. The indices passed as arguments in the methods must be equal to or greater than 0. An IndexOutOfBounds–Exception is thrown if an index is not valid. Note that the methods in this subsection return the reference value of the modified string builder, making it convenient to chain calls to these methods. Appending Characters to a String Builder The overloaded method append() can be used to append characters at the end of a string builder. WOW! eBook www.wowebook.org Click here to view code image StringBuilder append(Object obj) The obj argument is converted to a string as if by the static method call String.valueOf(obj), and this string is appended to the current string builder. Click here to view code image StringBuilder StringBuilder StringBuilder StringBuilder StringBuilder StringBuilder append(String str) append(CharSequence charSeq) append(CharSequence charSeq, int start, int end) append(char[] charArray) append(char[] charArray, int offset, int length) append(char c) These methods allow characters from various sources to be appended to the end of the current string builder. Click here to view code image StringBuilder StringBuilder StringBuilder StringBuilder StringBuilder append(boolean b) append(int i) append(long l) append(float f) append(double d) These methods convert the primitive value of the argument to a string by applying the static method String.valueOf() to the argument, before appending the result to the string builder. Inserting Characters in a String Builder The overloaded method insert() can be used to insert characters at a given position in a string builder. Click here to view code image StringBuilder StringBuilder StringBuilder StringBuilder StringBuilder StringBuilder StringBuilder StringBuilder StringBuilder StringBuilder StringBuilder insert(int insert(int insert(int insert(int insert(int insert(int insert(int insert(int insert(int insert(int insert(int offset, Object obj) dstOffset, CharSequence seq) dstOffset, CharSequence seq, int start, int end) offset, String str) offset, char[] charArray) offset, char c) offset, boolean b) offset, int i) offset, long l) offset, float f) offset, double d) The argument is converted, if necessary, by applying the static method String.valueOf(). The offset argument specifies where the characters are to be inserted in the string builder, and must be greater than or equal to 0. WOW! eBook www.wowebook.org Deleting Characters in a String Builder The following methods can be used to delete characters from specific positions in a string builder: Click here to view code image StringBuilder deleteCharAt(int index) StringBuilder delete(int start, int end) The first method deletes a character at a specified index in the string builder, contracting the string builder by one character. The second method deletes a substring, which is specified by the start index (inclusive) and the end index (exclusive), contracting the string builder accordingly. Among other miscellaneous methods included in the class StringBuilder is the following method, which reverses the contents of a string builder: StringBuilder reverse() Examples of appending, inserting, and deleting in string builders: Click here to view code image StringBuilder builder = new StringBuilder(“banana split”); split” builder.delete(4,12); builder.append(42); builder.insert(4,“na”); builder.reverse(); builder.deleteCharAt(builder.length()-1); builder.append(‘s’); // “banana // // // // // // “bana” “bana42” “banana42” “24ananab” “24anana” “24ananas” All of the previously mentioned methods modify the contents of the string builder and return a reference value denoting the current string builder. This allows chaining of method calls. The method calls invoked on the string builder denoted by the reference builder can be chained as follows, giving the same result: Click here to view code image builder.delete(4,12).append(42).insert(4,“na”).reverse(). deleteCharAt(builder.length()-1).append(‘s’); // “24ananas” The method calls in the chain are evaluated from left to right, so that the previous chain of calls is interpreted as follows: Click here to view code image (((((builder.delete(4,12)).append(42)).insert(4,“na”)).reverse()). deleteCharAt(builder.length()-1)).append(‘s’); // “24ananas” Each method call returns the reference value of the modified string builder, which is then used to invoke the next method. The string builder remains denoted by the reference builder. The compiler uses string builders to implement string concatenation with the + operator in String-valued non-constant expressions. The following code illustrates this optimization: WOW! eBook www.wowebook.org Click here to view code image String theChosen = “U”; String str1 = 4 + theChosen + “Only”; expression. // (1) Non-constant The assignment statement at (1) is equivalent to the following code using one string builder: Click here to view code image String str2 = new StringBuilder(). append(4).append(theChosen).append(“Only”).toString(); // (2) The code at (2) does not create any temporary String objects when concatenating several strings, since a single StringBuilder object is modified and finally converted to a String object having the string content "4UOnly". Controlling String Builder Capacity The following methods can be used to control various capacity-related aspects of a string builder: int capacity() Returns the current capacity of the string builder, meaning the number of characters the current builder can accommodate without allocating a new, larger array to hold characters. Click here to view code image void ensureCapacity(int minCapacity) Ensures that there is room for at least a minCapacity number of characters. It expands the string builder, depending on the current capacity of the builder. void trimToSize() Attempts to reduce the storage used for the character sequence. It may affect the capacity of the string builder. void setLength(int newLength) Ensures that the actual number of characters—that is, the length of the string builder—is exactly equal to the value of the newLength argument, which must be greater than or equal to 0. This operation can result in the string being truncated or padded with null characters ('\u0000'). This method affects the capacity of the string builder only if the value of the parameter newLength is greater than the current capacity. One use of this method is to clear the string builder: Click here to view code image builder.setLength(0); // Empty the builder. WOW! eBook www.wowebook.org Review Questions 8.21 What will be the result of attempting to compile and run the following program? Click here to view code image public class MyClass { public static void main(String[] args) { String s = “hello”; StringBuilder sb = new StringBuilder(s); sb.reverse(); if (s == sb) System.out.println(“a”); if (s.equals(sb)) System.out.println(“b”); if (sb.equals(s)) System.out.println(“c”); } } Select the one correct answer. (a) The program will fail to compile. (b) The program will compile, but throw an exception at runtime. (c) The program will compile, but will not print anything. (d) The program will compile, and will print abc. (e) The program will compile, and will print bc. (f) The program will compile, and will print a. (g) The program will compile, and will print b. (h) The program will compile, and will print c. 8.22 What will be the result of attempting to compile and run the following program? Click here to view code image public class MyClass { public static void main(String[] args) { StringBuilder sb = new StringBuilder(“have a nice day”); sb.setLength(6); System.out.println(sb); } } Select the one correct answer. (a) The code will fail to compile, because there is no method named setLength in the StringBuilder class. (b) The code will fail to compile, because the StringBuilder reference sb is not a legal argument to the println() method. (c) The program will throw a StringIndexOutOfBoundsException at runtime. (d) The program will print have a nice day at runtime. (e) The program will print have a at runtime. WOW! eBook www.wowebook.org (f) The program will print ce day at runtime. 8.23 Which of these parameter lists can be found in a constructor of the StringBuilder class? Select the four correct answers. (a) () (b) (int capacity) (c) (char[] data) (d) (String str) (e) (CharSequence cs) (f) (StringBuilder sb) (g) (char c) (h) (Object o) (i) (String str, int beginIndex, int endIndex) (j) (char[] data, int offset, int count) 8.24 Which of the following methods is not defined in the StringBuilder class? Select the one correct answer. (a) trim() (b) length() (c) append(String) (d) reverse() (e) setLength(int) 8.25 What will the following program print when run? Click here to view code image public class PeskyCharSeq { public static void main (String[] args) { StringBuilder sb1 = new StringBuilder(“WOW”); StringBuilder sb2 = new StringBuilder(sb1); System.out.println((sb1==sb2) + ” ” + sb1.equals(sb2)); } } Select the one correct answer. (a) The program will print false true. (b) The program will print false false. (c) The program will print true false. (d) The program will print true true. WOW! eBook www.wowebook.org (e) The program will fail to compile. (f) The program will compile, but throws an exception at runtime. 8.26 What will the following program print when run? Click here to view code image public class MoreCharSeq { public static void main (String[] args) { String s1 = “WOW”; StringBuilder s2 = new StringBuilder(s1); String s3 = new String(s2); System.out.println((s1.hashCode() == s2.hashCode()) + ” ” + (s1.hashCode() == s3.hashCode())); } } Select the one correct answer. (a) The program will print false true. (b) The program will print false false. (c) The program will print true false. (d) The program will print true true. (e) The program will fail to compile. (f) The program will compile, but throw an exception at runtime. 8.27 What will the following program print when run? Click here to view code image public class Appendage { private static void putO(StringBuilder s1) { s1.append(“O”); } public static void main(String[] args) { StringBuilder s1 = new StringBuilder(“W”); putO(s1); s1.append(“W!”); System.out.println(s1); } } Select the one correct answer. (a) The program will print WW!. (b) The program will print WOW!. (c) The program will print W. (d) The program will print WO. (e) The program will fail to compile. (f) The program will compile, but throw an exception at runtime. WOW! eBook www.wowebook.org Chapter Summary The following topics were covered in this chapter: • The Object class, which is the most fundamental class in Java • Wrapper classes, which not only allow primitive values to be treated as objects, but also contain useful methods for converting values • The String class, including how immutable strings are created and used • The StringBuilder class, including how dynamic strings are created and manipulated • Comparison of the String, StringBuilder, and StringBuffer classes Programming Exercises 8.1 Create a class named Pair, which aggregates two arbitrary objects. Implement the equals() and hashCode() methods in such a way that a Pair object is identical to another Pair object if, and only if, the pair of constituent objects are identical. Make the toString() implementation return the textual representation of both the constituent objects in a Pair object. Objects of the Pair class should be immutable. 8.2 A palindrome is a text phrase that is spelled the same way backward and forward. The word redivider is a palindrome, since the word would be spelled the same even if the character sequence were reversed. Write a program that takes a string as an argument and reports whether the string is a case-sensitive palindrome. For example, the word Redivider would not be a palindrome in this case, since we distinguish between uppercase and lowercase letters. WOW! eBook www.wowebook.org 9. Object Lifetime 9.1 Garbage Collection Efficient memory management is essential in a runtime system. Storage for objects is allocated in a designated part of the memory called the heap, which has a finite size. Garbage collection is a process of managing the heap efficiently, by reclaiming memory occupied by objects that are no longer needed and making it available for new objects. Java provides automatic garbage collection, meaning that the runtime environment can take care of memory management without the program having to take any special action. Objects allocated on the heap (through the new operator) are administered by the automatic garbage collector. The automatic garbage collection scheme guarantees that a reference to an object is always valid while the object is needed by the program. Specifically, the object will not be reclaimed, leaving the reference dangling. Having an automatic garbage collector frees the programmer from the responsibility of writing code for deleting objects. By relying on the automatic garbage collector, a Java program also forfeits any significant influence on the garbage collection of its objects (p. 393). However, this price is insignificant when compared to the cost of putting the code for object management in place and plugging all the memory leaks. Time-critical applications should recognize that the automatic garbage collector runs as a background task and may have a negative impact on their performance. WOW! eBook www.wowebook.org 9.2 Reachable Objects An automatic garbage collector essentially performs two tasks: • Decides if and when memory needs to be reclaimed • Finds objects that are no longer needed by the program and reclaims their storage A program has no guarantees that the automatic garbage collector will be run during its execution. Consequently, a program should not rely on the scheduling of the automatic garbage collector for its behavior (p. 393). To understand how the automatic garbage collector finds objects whose storage should be reclaimed, we need to look at the activity happening in the JVM. Java provides threadbased multitasking, meaning that several threads can be executing concurrently in the JVM, each doing its own task. A thread is an independent path of execution through the program code. A thread is alive if it has not completed its execution. Each live thread has its own JVM stack, as explained in §6.5, p. 230. The JVM stack contains activation frames of methods that are currently active. Local references declared in a method can always be found in the method’s activation frame, stored on the JVM stack associated with the thread in which the method is called. Objects, in contrast, are always created on the heap. If an object has a field reference, the field will be found inside the object in the heap, and the object denoted by the field reference will also be found in the heap. An example of how memory is organized during execution is depicted in Figure 9.1, which shows two live threads (t1 and t2) and their respective JVM stacks with the activation frames. The diagram indicates which objects in the heap are referenced by local references in the method activation frames. It also identifies field references in objects, which refer to other objects in the heap. Some objects have several aliases. WOW! eBook www.wowebook.org Figure 9.1 Memory Organization at Runtime An object in the heap is said to be reachable if it is referenced by any local reference in a JVM stack. Likewise, any object that is denoted by a reference in a reachable object is said to be reachable. Reachability is a transitive relationship. Thus, a reachable object has at least one chain of reachable references from the JVM stack. Any reference that makes an object reachable is called a reachable reference. An object that is not reachable is said to be unreachable. WOW! eBook www.wowebook.org A reachable object is alive, and is accessible by a live thread. Note that an object can be accessible by more than one thread. Any object that is not accessible by a live thread is a candidate for garbage collection. When an object becomes unreachable and is waiting for its memory to be reclaimed, it is said to be eligible for garbage collection. An object is eligible for garbage collection if all references denoting it are in eligible objects. Eligible objects do not affect the future course of program execution. When the garbage collector runs, it finds and reclaims the storage of eligible objects, although garbage collection does not necessarily occur as soon as an object becomes unreachable. In Figure 9.1, the objects o4, o5, o11, o12, o14, and o15 all have reachable references. Objects o13 and o16 have no reachable references and, therefore, are eligible for garbage collection. From the preceding discussion we can conclude that if a composite object becomes unreachable, its constituent objects also become unreachable, barring any reachable references to the constituent objects. Although the objects o1, o2, and o3 in Figure 9.1 form a circular list, they do not have any reachable references. Thus, these objects are all eligible for garbage collection. Conversely, the objects o5, o6, and o7 form a linear list, but they are all reachable, as the first object in the list, o5, is reachable. The objects o8, o10, o11, and o9 also form a linear list (in that order), but not all objects in the list are reachable. Only the objects o9 and o11 are reachable, as object o11 has a reachable reference. The objects o8 and o10 are eligible for garbage collection. The lifetime of an object is the time from its creation to the time it is garbage collected. Under normal circumstances, an object is accessible from the time when it is created to the time when it becomes unreachable. The lifetime of an object can also include a period when it is eligible for garbage collection, waiting for its storage to be reclaimed. The finalization mechanism (p. 390) in Java does provide a means for resurrecting an object after it is eligible for garbage collection, but the finalization mechanism is rarely used for this purpose. 9.3 Facilitating Garbage Collection The automatic garbage collector determines which objects are not reachable and, therefore, eligible for garbage collection. It will certainly go to work if there is an imminent memory shortage. Even so, automatic garbage collection should not be perceived as a license for creating a plethora of objects and then forgetting about them. Nevertheless, certain programming practices can help in minimizing the overhead associated with garbage collection during program execution. Certain objects, such as files and network connections, can tie up resources and should be disposed of properly when they are no longer needed. In most cases, the try-withresources statement (not in the scope of this book) provides a convenient facility for such purposes, as it will always ensure proper closing of the Auto-Closeable resources. To optimize its memory footprint, a live thread should retain access to an object as long for only as the object is needed for its execution. The program can allow objects to become eligible for garbage collection as early as possible by removing all references to an object when it is no longer needed. WOW! eBook www.wowebook.org Objects that are created and accessed by local references in a method are eligible for garbage collection when the method terminates, unless reference values to these objects are exported out of the method. This can occur if a reference value is returned from the method, passed as argument to another method that records the reference value, or thrown as an exception. However, a method need not always leave objects to be garbage collected after its termination. It can facilitate garbage collection by taking suitable action—for example, by nulling references. Click here to view code image import java.io.*; class WellBehavedClass { // … void wellBehavedMethod() { File aFile; long[] bigArray = new long[20000]; // … uses local variables … // Does clean-up (before starting something extensive) aFile = null; // (1) bigArray = null; // (2) // Start some other extensive activity // … } // … } In this code, the local variables are set to null after use at (1) and (2), before starting some other extensive activity. This makes the objects denoted by the local variables eligible for garbage collection from this point onward, rather than after the method terminates. This optimization technique of nulling references should be used only as a last resort when resources are scarce. Here are some other techniques to facilitate garbage collection: • When a method returns a reference value and the object denoted by the value is not needed, not assigning this value to a reference facilitates garbage collection. • If a reference is assigned a new value, the object that was previously denoted by the reference can become eligible for garbage collection. • Removing reachable references to a composite object can make the constituent objects become eligible for garbage collection, as explained earlier. Example 9.1 illustrates how a program can influence garbage collection eligibility. The class HeavyItem represents objects with a large memory footprint, for which we want to monitor garbage collection. Each composite HeavyItem object has a reference to a large array. The class overrides the finalize() method from the Object class to print out an ID when the object is finalized. This method is always called on an eligible object before it is destroyed (p. 390). We use it to indicate in the output if and when a HeavyItem is reclaimed. To illustrate the effect of garbage collection on object hierarchies, each HeavyItem object may also have a reference to another HeavyItem. In Example 9.1, the class RecyclingBin defines a method createHeavyItem() at WOW! eBook www.wowebook.org (4). In this method, the HeavyItem created at (5) is eligible for garbage collection after the reassignment of reference itemA at (6), as this object will then have no references. The HeavyItem created at (6) is accessible on return from the method. Its fate depends on the code that calls this method. In Example 9.1, the class RecyclingBin also defines a method createList() at (8). It returns the reference value in the reference item1, which denotes the first item in a list of three HeavyItem objects. Because of the list structure, none of the HeavyItem objects in the list is eligible for garbage collection on return from the method. Again, the fate of the objects in the list is decided by the code that calls this method. It is enough for the first item in the list to become unreachable, so that all objects in the list become eligible for garbage collection (barring any reachable references). Example 9.1 Garbage Collection Eligibility Click here to view code image // File: RecyclingBin.java class HeavyItem { int[] itemBody; String itemID; HeavyItem nextItem; // (1) HeavyItem(String ID, HeavyItem itemRef) { itemBody = new int[1_000_000]; itemID = ID; nextItem = itemRef; } @Override protected void finalize() throws Throwable { System.out.println(itemID + “: recycled.”); super.finalize(); } // (2) // (3) } //______________________________________________________________________________ public class RecyclingBin { public static HeavyItem createHeavyItem(String itemID) { // HeavyItem itemA = new HeavyItem(itemID + “: local item”, null);// itemA = new HeavyItem(itemID, null); // System.out.println(“Return from creating HeavyItem ” + itemID); return itemA; // } (4) (5) (6) public static HeavyItem createList(String listID) { HeavyItem item3 = new HeavyItem(listID + “: item3”, null); HeavyItem item2 = new HeavyItem(listID + “: item2”, item3); HeavyItem item1 = new HeavyItem(listID + “: item1”, item2); System.out.println(“Return from creating list ” + listID); return item1; } // // // // (8) (9) (10) (11) public static void main(String[] args) { HeavyItem list = createList(“X”); list = createList(“Y”); // (13) // (14) // (15) HeavyItem itemOne = createHeavyItem(“One”); WOW! eBook www.wowebook.org (7) // (12) // (16) HeavyItem itemTwo = createHeavyItem(“Two”); itemOne = null; createHeavyItem(“Three”); createHeavyItem(“Four”); System.out.println(“Return from main().”); // // // // (17) (18) (19) (20) } } Probable output from the program: Click here to view code image Return from creating list X Return from creating list Y X: item3: recycled. X: item2: recycled. X: item1: recycled. Return from creating HeavyItem Return from creating HeavyItem Return from creating HeavyItem Three: local item: recycled. Three: recycled. Two: local item: recycled. Return from creating HeavyItem One: local item: recycled. One: recycled. Return from main(). One Two Three Four In Example 9.1, the main() method at (13) in the class RecyclingBin uses the methods createHeavyItem() and createList(). It creates a list X at (14), but the reference to its first item is reassigned at (15), making objects in list X eligible for garbage collection after (15). The first item of list Y is stored in the reference list, making this list non-eligible for garbage collection during the execution of the main() method. The main() method creates two items at (16) and (17), storing their reference values in the references itemOne and itemTwo, respectively. The reference itemOne is nulled at (18), making the HeavyItem object with identity One eligible for garbage collection. The two calls to the createHeavyItem() method at (19) and (20) return reference values to HeavyItem objects, which are not stored, making each object eligible for garbage collection immediately after their respective method calls return. The output from the program bears out the observations made earlier. Objects in list Y (accessible through the reference list) and the HeavyItem object with identity Two (accessible through the reference itemTwo) remain non-eligible while the main() method executes. Although the output shows that the HeavyItem object with identity Four was never garbage collected, it is not accessible once it becomes eligible for garbage collection at (20). Any objects in the heap after the program terminates are reclaimed by the operating system. WOW! eBook www.wowebook.org 9.4 Object Finalization Object finalization provides an object with a last resort to undertake any action before its storage is reclaimed. The automatic garbage collector calls the finalize() method in an object that is eligible for garbage collection before actually destroying the object. The finalize() method is defined in the Object class: Click here to view code image protected void finalize() throws Throwable An implementation of the finalize() method is called a finalizer. A subclass can override the finalizer from the Object class so as to take more specific and appropriate action before an object of the subclass is destroyed. Note that the overriding method cannot narrow the visibility of the overridden method and must be declared as either protected or public. A finalizer, like any other method, can catch and throw exceptions (§6.7, p. 238). When a finalizer is called explicitly by the program code, exception handling is no different during execution of a finalizer than during execution of any other method. However, any exception thrown but not caught by a finalizer that is called by the garbage collector is ignored, and the finalization of this object is terminated. The finalizer is called only once on an object, regardless of whether any exception is thrown during its execution. In case of finalization failure, the object remains eligible for disposal at the discretion of the garbage collector (unless it has been resurrected, as explained later in this section). Since there is no guarantee that the garbage collector will ever run, there is also no guarantee that the finalizer will ever be called. In the following code, the finalizer at (1) will take appropriate action if and when called on objects of the class before they are garbage collected, ensuring that the resource is freed. Since it is not guaranteed that the finalizer will ever be called at all, a program should not rely on the finalization to do any critical operations. Click here to view code image public class AnotherWellBehavedClass { SomeResource objRef; // … @Override protected void finalize() throws Throwable { try { if (objRef != null) objRef.close(); } finally { super.finalize(); } } } // (1) // (2) // (3) // (4) The finalizer in a subclass should explicitly call the finalizer in its superclass as its last action, as shown at (4). The call to the finalizer of the superclass is in a finally block at (3), which is guaranteed to execute regardless of any exceptions thrown by the code in the try block at (2). (Another example of finalizer chaining is provided in Example 9.2 in the next section.) WOW! eBook www.wowebook.org The finalizer of an object can make the object non-eligible again (i.e., resurrect it), thereby avoiding garbage collection of the object. One simple technique is to assign the object’s this reference to a static field, which then becomes a reachable reference for the object. Since a finalizer is called only once on an object before it is garbage collected, an object can be resurrected only once. In other words, if the object again becomes eligible for garbage collection and the garbage collector runs, the finalizer will not be called. Such object resurrections are not recommended, as they undermine the purpose of the finalization mechanism. Note that an enum type cannot declare a finalizer. Therefore, an enum constant may never be finalized. 9.5 Finalizer Chaining Unlike subclass constructors, overridden finalizers are not implicitly chained (§7.5, p. 282). Chaining of finalizers requires an explicit call to the overridden finalizer. Example 9.2 illustrates the process of programmatically chaining finalizers. It creates a userspecified number of large objects of a user-specified size; the number and size are provided through command-line program arguments. The loop at (7) in the main() method creates Blob objects, but does not store any references to them. Objects created are instances of the class Blob defined at (3). The Blob constructor at (4) initializes the field size by constructing a large array of integers. The Blob class extends the BasicBlob class, which assigns each blob a unique number (blobId) and keeps track of the number of blobs (population) not yet garbage collected. Creation of each Blob object by the constructor at (4) prints the ID number of the object and the message "Hello". The finalize() method at (5) is called before a Blob object is garbage collected. It prints the inherited field blobId of the Blob object and the message "Bye", before calling the finalize() method in the superclass BasicBlob at (2), which decrements the population count. The program output shows that two blobs were not garbage collected at the time the print statement at (8) was executed. It is evident from the number of "Bye" messages that three blobs were garbage collected before all five blobs were created in the loop at (7). Example 9.2 Using Finalizers Click here to view code image // File: Finalizers.java class BasicBlob { private static int idCounter; private static int population; // (1) protected int blobId; BasicBlob() { blobId = idCounter++; ++population; } @Override protected void finalize() throws Throwable { WOW! eBook www.wowebook.org // (2) —population; super.finalize(); } public static int getPopulation() { return population; } } //______________________________________________________________________________ class Blob extends BasicBlob { // (3) private int[] size; Blob(int bloatedness) { size = new int[bloatedness]; System.out.println(blobId + “: Hello”); } @Override protected void finalize() throws Throwable { System.out.println(blobId + “: Bye”); super.finalize(); } // (4) // (5) } //______________________________________________________________________________ public class Finalizers { public static void main(String[] args) { // (6) int blobsRequired, blobSize; try { blobsRequired = Integer.parseInt(args[0]); blobSize = Integer.parseInt(args[1]); } catch(IndexOutOfBoundsException e) { System.out.println(“Too few program arguments.”); System.out.println(“Usage: Finalizers ”); return; } catch(NumberFormatException e) { System.out.println(“Illegal program argument.”); System.out.println(“Usage: Finalizers ”); return; } for (int i = 0; i < blobsRequired; ++i) { // (7) new Blob(blobSize); } System.out.println(BasicBlob.getPopulation() + ” blobs alive”); // (8) } } Probable output from running the program with the following command: >java Finalizers 5 500000 0: Hello 1: Hello 2: Hello 0: Bye 1: Bye 2: Bye 3: Hello 4: Hello 2 blobs alive WOW! eBook www.wowebook.org 9.6 Invoking Garbage Collection Programmatically Although Java provides facilities to invoke the garbage collection explicitly, there are no guarantees that it will be run. The program can request that garbage collection be performed, but there is no way to force garbage collection to be activated. The System.gc() method can be used to request garbage collection, and the System.runFinalization() method can be called to suggest that any pending finalizers be run for objects eligible for garbage collection. static void gc() Requests that garbage collection be run. static void runFinalization() Requests that any pending finalizers be run for objects eligible for garbage collection. Alternatively, corresponding methods in the Runtime class can be used. A Java application has a unique Runtime object that can be used by the application to interact with the JVM. An application can obtain this object by calling the method Runtime.getRuntime(). The Runtime class provides several methods related to memory issues: static Runtime getRuntime() Returns the Runtime object associated with the current application. void gc() Requests that garbage collection be run. However, it is recommended to use the more convenient static method System.gc(). void runFinalization() Requests that any pending finalizers be run for objects eligible for garbage collection. Again, it is more convenient to use the static method System.runFinalization(). long freeMemory() Returns the amount of free memory (bytes) in the JVM that is available for new objects. long totalMemory() Returns the total amount of memory (bytes) available in the JVM, including both memory occupied by current objects and memory available for new objects. Example 9.3 illustrates the process of invoking garbage collection. The class MemoryCheck is an adaptation of the class Finalizers from Example 9.2. The RunTime object for the application is obtained at (7). This object is used to get information regarding total memory and free memory in the JVM at (8) and (9), respectively. Blobs are created in the loop at (10). The amount of free memory after blob creation is printed at WOW! eBook www.wowebook.org (11). From the program output, it is apparent that some blobs were already garbage collected before the execution reached (11). A request for garbage collection is made at (12). Checking free memory after this request shows that more memory has become available, indicating that the request was honored. It is instructive to run the program without the method call System.gc() at (12) to compare the results with and without this call. WOW! eBook www.wowebook.org Example 9.3 Invoking Garbage Collection Click here to view code image class BasicBlob { /* See Example 9.2. */ } class Blob extends BasicBlob { /* See Example 9.2. */ } //______________________________________________________________________________ public class MemoryCheck { public static void main(String[] args) { // (6) int blobsRequired, blobSize; try { blobsRequired = Integer.parseInt(args[0]); blobSize = Integer.parseInt(args[1]); } catch(IndexOutOfBoundsException e) { System.out.println(“Too few program arguments.”); System.out.println(“Usage: MemoryCheck ”); return; } catch(NumberFormatException e) { System.out.println(“Illegal program argument.”); System.out.println(“Usage: MemoryCheck ”); return; } Runtime environment = Runtime.getRuntime(); // (7) System.out.println(“Total memory: ” + environment.totalMemory());// (8) System.out.println(“Free memory before blob creation: ” + environment.freeMemory()); // (9) for (int i = 0; i < blobsRequired; ++i) { // (10) new Blob(blobSize); } System.out.println(“Free memory after blob creation: ” + environment.freeMemory()); // (11) System.gc(); // (12) System.out.println(“Free memory after requesting GC: ” + environment.freeMemory()); // (13) System.out.println(BasicBlob.getPopulation() + ” blobs alive”); // (14) } } Probable output from running the program with the following command: Click here to view code image >java MemoryCheck 5 100000 Total memory: 2031616 Free memory before blob creation: 1773192 0: Hello 1: Hello 2: Hello 1: Bye 2: Bye 3: Hello 0: Bye 3: Bye 4: Hello Free memory after blob creation: 818760 4: Bye Free memory after requesting GC: 1619656 0 blobs alive The following points regarding automatic garbage collection should be noted: • There are no guarantees that the finalizers of objects eligible for garbage collection WOW! eBook www.wowebook.org will be executed. Garbage collection might not even be run if the program execution does not warrant it. Thus, any memory allocated during program execution might remain allocated after program termination, but will eventually be reclaimed by the operating system. • There are also no guarantees about the order in which the objects will be garbage collected, or the order in which their finalizers will be executed. Therefore, the program should not make any assumptions based on these criteria. • Garbage collection does not guarantee that there will be enough memory for the program to run. A program can rely on the garbage collector to run when memory gets very low, and it can expect an OutOfMemoryException to be thrown if its memory demands cannot be met. Review Questions 9.1 Which of the following statements is true? Select the one correct answer. (a) Objects can be explicitly destroyed using the keyword delete. (b) An object will be garbage collected immediately after it becomes unreachable. (c) If object obj1 is accessible from object obj2, and object obj2 is accessible from obj1, then obj1 and obj2 are not eligible for garbage collection. (d) Once an object has become eligible for garbage collection, it will remain eligible until it is destroyed. (e) If object obj1 can access object obj2 that is eligible for garbage collection, then obj1 is also eligible for garbage collection. 9.2 Identify the location in the following program where the object, initially referenced by arg1, is eligible for garbage collection. Click here to view code image public class MyClass { public static void main(String[] args) { String msg; String pre = “This program was called with “; String post = ” as first argument.”; String arg1 = new String((args.length > 0) ? ”’” + args[0] + ”’” : ” ”); msg = arg1; arg1 = null; // (1) msg = pre + msg + post; // (2) pre = null; // (3) System.out.println(msg); msg = null; // (4) post = null; // (5) args = null; // (6) } } Select the one correct answer. WOW! eBook www.wowebook.org (a) After the line labeled (1) (b) After the line labeled (2) (c) After the line labeled (3) (d) After the line labeled (4) (e) After the line labeled (5) (f) After the line labeled (6) 9.3 How many objects are eligible for garbage collection when control reaches (1)? Click here to view code image public class Eligible { public static void main(String[] args) { for (int i = 0; i < 5; i++) { Eligible obj = new Eligible(); new Eligible(); } System.gc(); // (1) } } Select the one correct answer. (a) 0 (b) 5 (c) 10 (d) Hard to say 9.4 How many objects are eligible for garbage collection when control reaches (1)? Click here to view code image public class Link { private Link next; Link(Link next) { this.next = next; } public void finialize() { System.out.print(“X”); } public static void main(String[] args) { Link p = null; for (int i = 0; i < 5; i++) { p = new Link(p); } System.gc(); // (1); } } Select the one correct answer. (a) 0 (b) 5 (c) 10 (d) Hard to say 9.5 Which of the following statements is true? WOW! eBook www.wowebook.org Select the one correct answer. (a) If an exception is thrown during execution of the finalize() method of an eligible object, the exception is ignored and the object is destroyed. (b) All objects have a finalize() method. (c) Objects can be destroyed by explicitly calling the finalize() method. (d) The finalize() method can be declared with any accessibility. (e) The compiler will fail to compile code that defines an overriding finalize() method that does not explicitly call the overridden finalize() method from the superclass. 9.6 Which of the following statements is true? Select the one correct answer. (a) The compiler will fail to compile code that explicitly tries to call the finalize() method. (b) The finalize() method can be overridden, but it must be declared with protected accessibility. (c) An overriding finalize() method in any class can always throw checked exceptions. (d) The finalize() method can be overloaded. (e) The body of the finalize() method can access only other objects that are eligible for garbage collection. 9.7 Which method headers will result in a correct implementation of a finalizer for the following class? Click here to view code image public class Curtain { // (1) INSERT METHOD HEADER HERE … { System.out.println(“Final curtain”); super.finalize(); } } Select the two correct answers. (a) void finalize() throws Throwable (b) void finalize() throws Exception (c) void finalize() (d) protected void finalize() throws Throwable (e) protected void finalize() throws Exception (f) protected void finalize() WOW! eBook www.wowebook.org (g) public void finalize() throws Throwable (h) public void finalize() throws Exception (i) public void finalize() (j) private void finalize() throws Throwable (k) private void finalize() throws Exception (l) private void finalize() 9.8 Which scenario can definitely not be the result of compiling and running the following program? Click here to view code image public class Grade { private char grade; Grade(char grade) { this.grade = grade; } public void finalize() throws Throwable { System.out.print(grade); super.finalize(); } public static void main(String[] args) { new Grade(‘A’); new Grade(‘F’); System.gc(); } } Select the one correct answer. (a) The program may print AF. (b) The program may print FA. (c) The program may print A. (d) The program may print F. (e) The program may print AFA. (f) The program may not print anything. 9.9 Which scenarios can be the result of compiling and running the following program? Click here to view code image public class MyString { private String str; MyString(String str) { this.str = str; } public void finalize() throws Throwable { System.out.print(str); super.finalize(); } public void concat(String str2) { this.str.concat(str2); } WOW! eBook www.wowebook.org public static void main(String[] args) { new MyString(“A”).concat(“B”); System.gc(); } } Select the two correct answers. (a) The program may print AB. (b) The program may print BA. (c) The program may print A. (d) The program may print B. (e) The program may not print anything. 9.7 Initializers Initializers can be used to set initial values for fields in objects and classes. There are three kinds of initializers: • Field initializer expressions • Static initializer blocks • Instance initializer blocks Subsequent sections in this chapter provide details on these initializers, concluding with a discussion of the procedure involved in constructing the state of an object when the object is created by using the new operator. 9.8 Field Initializer Expressions Initialization of fields can be specified in field declaration statements using initializer expressions. The value of the initializer expression must be assignment compatible with the declared field (see §5.6, p. 158 and §7.9, p. 312). We distinguish between static and non-static field initializers. Click here to view code image class ConstantInitializers { int minAge = 12; // (1) Non-static static double pensionPoints = 10.5; // (2) Static // … } The fields of an object are initialized with the values of initializer expressions when the object is created by using the new operator. In the previous example, the declaration at (1) will result in the field minAge being initialized to 12 in every object of the class ConstantInitializers created with the new operator. If no explicit initializer expressions are specified, default values (§2.4, p. 42) are assigned to the fields. When a class is loaded, it is initialized, meaning its static fields are initialized with the values of the initializer expressions. The declaration at (2) will result in the static field pensionPoints being initialized to 10.5 when the class is loaded by the JVM. Again, WOW! eBook www.wowebook.org if no explicit initializers are specified, default values are assigned to the static fields. An initializer expression for a static field cannot refer to non-static members by their simple names. The keywords this and super cannot occur in a static initializer expression. Since a class is always initialized before it can be instantiated, an instance initializer expression can always refer to any static member of a class, regardless of the member declaration order. In the following code, the instance initializer expression at (1) refers to the static field NO_OF_WEEKS declared and initialized at (2). Such a forward reference is legal. More examples of forward references are given in the next subsection. Click here to view code image class MoreInitializers { int noOfDays = 7 * NO_OF_WEEKS; static int NO_OF_WEEKS = 52; // … } // (1) Non-static // (2) Static Initializer expressions can also be used to define constants in interfaces (§7.6, p. 302). Such initializer expressions are implicitly static, as they define values of static final fields. Initializer expressions are used to initialize local variables as well (§2.3, p. 40). A local variable is initialized with the value of the initializer expression every time the local variable declaration is executed. Declaration Order of Initializer Expressions When an object is created using the new operator, instance initializer expressions are executed in the order in which the instance fields are declared in the class. Java requires that the declaration of a field must occur before its usage in any initializer expression if the field is used on the right-hand side of an assignment in the initializer expression. This essentially means that the declaration of a field must occur before the value of the field is read in an initializer expression. Using the field on the left-hand side of an assignment in the initializer expression does not violate the declaration-beforereading rule, as this constitutes a write operation. This rule applies when the usage of the field is by its simple name. There is one caveat to the declaration-before-reading rule: It does not apply if the initializer expression defines an anonymous class, as the usage then occurs in a different class that has its own accessibility rules in the enclosing context. The restrictions outlined earlier help to detect initialization anomalies at compile time. In the next code example, the initialization at (2) generates a compile-time error, because the field width in the initializer expression violates the declaration-before-reading rule. Because the usage of the field width in the initializer expression at (2) does not occur on the left-hand side of the assignment, this is an illegal forward reference. To remedy the error, the declaration of the field width at (4) can be moved in front of the declaration at (2). In any case, we can use the keyword this as shown at (3), but it will read the default value 0 in the field width. WOW! eBook www.wowebook.org Click here to view code image class NonStaticInitializers { int length = 10; //double area = length * width; reference. double area = length * this.width; 0. int width = 10; int sqSide = height = 20; int height; // (1) // (2) Not OK. Illegal forward // (3) OK, but width has default value // (4) // (5) OK. Legal forward reference. // (6) } The forward reference at (5) is legal. The usage of the field height in the initializer expression at (5) occurs on the left-hand side of the assignment. The initializer expression at (5) is evaluated as (sqSide = (height = 20)). Every object of the class NonStaticInitializers will have the fields height and sqSide set to the value 20. The declaration-before-reading rule is equally applicable to static initializer expressions when static fields are referenced by their simple names. Example 9.4 shows why the order of field initializer expressions can be important. The initializer expressions in this example are calls to methods defined in the class, and methods are not subject to the same access rules as initializer expressions. The call at (2) to the method initMaxGuests() defined at (4) is expected to return the maximum number of guests, but the field occupancyPerRoom at (3) will not have been explicitly initialized at this point; therefore, its default value 0 will be used in the method initMaxGuests(), which will return an incorrect value. The program output shows that after object creation, the occupancy per room is correct, but the maximum number of guests is wrong. WOW! eBook www.wowebook.org Example 9.4 Initializer Expression Order and Method Calls Click here to view code image // File: TestOrder.java class Hotel { private int noOfRooms = 12; private int maxNoOfGuests = initMaxGuests(); Bug private int occupancyPerRoom = 2; // (1) // (2) // (3) public int initMaxGuests() { // (4) System.out.println(“occupancyPerRoom: ” + occupancyPerRoom); System.out.println(“maxNoOfGuests: ” + noOfRooms * occupancyPerRoom); return noOfRooms * occupancyPerRoom; } public int getMaxGuests() { return maxNoOfGuests; } // (5) public int getOccupancy() { return occupancyPerRoom; } // (6) } //________________________________________________________________________ public class TestOrder { public static void main(String[] args) { Hotel hotel = new Hotel(); // (7) System.out.println(“After object creation: “); System.out.println(“occupancyPerRoom: ” + hotel.getOccupancy()); // (8) System.out.println(“maxNoOfGuests: ” + hotel.getMaxGuests()); // (9) } } Output from the program: occupancyPerRoom: 0 maxNoOfGuests: 0 After object creation: occupancyPerRoom: 2 maxNoOfGuests: 0 9.9 Static Initializer Blocks Java allows static initializer blocks to be defined in a class. Although such blocks can include arbitrary code, they are primarily used for initializing static fields. The code in a static initializer block is executed only once, when the class is loaded and initialized. The syntax of a static initializer block comprises the keyword static followed by a local block that can contain arbitrary code, as shown at (3). Click here to view code image class StaticInitializers { static final int ROWS = 12, COLUMNS = 10; static long[][] matrix = new long[ROWS][COLUMNS]; // … static { initializer for (int i = 0; i < matrix.length; i++) for (int j = 0; j < matrix[i].length; j++) matrix[i][j] = 2*i + j; } WOW! eBook www.wowebook.org // (1) // (2) // (3) Static // … } When the class StaticInitializers is first loaded in the previous example, the static final fields at (1) are initialized. Then the array of arrays matrix of specified size is created at (2), followed by the execution of the static block at (3). If a class relies on native method implementations, a static initializer can be used to load any external libraries that the class needs (§4.8, p. 137). Note that the static initializer block is not contained in any method. A class can have more than one static initializer block. Initializer blocks are not members of a class, and they cannot have a return statement because they cannot be called directly. When a class is initialized, the initializer expressions in static field declarations and static initializer blocks are executed in the order in which they are specified in the class. In the previous example, the initializer expressions at (1) and (2) are executed before the static initializer block at (3). Similar restrictions apply to static initializer blocks as for static initializer expressions: The keywords this and super cannot occur in a static initializer block, because such a block defines a static context. Declaration Order of Static Initializers When making forward references using simple names, code in a static initializer block is also subject to the declaration-before-reading rule discussed in the previous subsection. Example 9.5 illustrates forward references and the order of execution for static initializer expressions and static initializer blocks. An illegal forward reference occurs at (4), where an attempt is made to read the value of the field sf1 before its declaration. At (11), the read operation occurs after the declaration, so it is allowed. Forward reference made on the left-hand side of the assignment is always allowed, as shown at (2), (5), and (7). The initializers are executed in their declaration order. A static field has the value that it was last assigned in an initializer. If there is no explicit assignment, the field has the default value of its type. WOW! eBook www.wowebook.org Example 9.5 Static Initializers and Forward References Click here to view code image public class StaticForwardReferences { static { // (1) Static initializer block. sf1 = 10; // (2) OK. Assignment to sf1 allowed. // sf1 = if1; // (3) Not OK. Non-static field access in static context. // int a = 2 * sf1; // (4) Not OK. Read operation before declaration. int b = sf1 = 20; // (5) OK. Assignment to sf1 allowed. int c = StaticForwardReferences.sf1; // (6) OK. Not accessed by simple name. } static int sf1 = sf2 = 30; static int sf2; int if1 = 5; // (7) Static field. Assignment to sf2 allowed. // (8) Static field. // (9) Non-static field. static { int d = 2 * sf1; int e = sf1 = 50; } // (10) Static initializer block. // (11) OK. Read operation after declaration. // (12) OK. Assignment to sf1 allowed. public static void main(String[] args) { System.out.println(“sf1: ” + StaticForwardReferences.sf1); System.out.println(“sf2: ” + StaticForwardReferences.sf2); } } Output from the program: sf1: 50 sf2: 30 9.10 Instance Initializer Blocks Just as static initializer blocks can be used to initialize static fields in a named class, so Java provides the ability to initialize fields during object creation using instance initializer blocks. In this respect, such blocks serve the same purpose as constructors during object creation. The syntax of an instance initializer block is the same as that of a local block, as shown at (2) in the following code. The code in the local block is executed every time an instance of the class is created. Click here to view code image class InstanceInitializers { long[] squares = new long[10]; // (1) // … { // (2) Instance Initializer for (int i = 0; i < squares.length; i++) squares[i] = i*i; } // … } The array squares of specified length is first created at (1); its creation is followed by the execution of the instance initializer block at (2) every time an instance of the class WOW! eBook www.wowebook.org InstanceInitializers is created. Note that the instance initializer block is not contained in any method. A class can have more than one instance initializer block, and these (and any instance initializer expressions in instance field declarations) are executed in the order they are specified in the class. Declaration Order of Instance Initializers Analogous to the other initializers discussed earlier, an instance initializer block cannot make a forward reference to a field that violates the declaration-before-reading rule. In Example 9.6, an illegal forward reference occurs in the code at (4), which attempts to read the value of the field nsf1 before it is declared. The read operation at (11) occurs after the declaration and, therefore, is allowed. Forward reference made on the left-hand side of the assignment is always allowed, as shown at (2), (3), (5), and (7). As in an instance initializer expression, the keywords this and super can be used to refer to the current object in an instance initializer block. As in a static initializer block, the return statement is not allowed in instance initializer blocks. An instance initializer block can be used to factor out common initialization code that will be executed regardless of which constructor is invoked. Example 9.6 Instance Initializers and Forward References Click here to view code image public class NonStaticForwardReferences { { nsf1 = 10; nsf1 = sf1; context. // int a = 2 * nsf1; int b = nsf1 = 20; int c = this.nsf1; } int nsf1 = nsf2 = 30; allowed. int nsf2; static int sf1 = 5; { int d = 2 * nsf1; int e = nsf1 = 50; // (1) Instance initializer block. // (2) OK. Assignment to nsf1 allowed. // (3) OK. Static field access in non-static // (4) Not OK. Read operation before declaration. // (5) OK. Assignment to nsf1 allowed. // (6) OK. Not accessed by simple name. // (7) Non-static field. Assignment to nsf2 // (8) Non-static field. // (9) Static field. // (10) Instance initializer block. // (11) OK. Read operation after declaration. // (12) OK. Assignment to nsf1 allowed. } public static void main(String[] args) { NonStaticForwardReferences objRef = new NonStaticForwardReferences(); System.out.println(“nsf1: ” + objRef.nsf1); System.out.println(“nsf2: ” + objRef.nsf2); } } Output from the program: nsf1: 50 nsf2: 30 WOW! eBook www.wowebook.org 9.11 Constructing Initial Object State Object initialization involves constructing the initial state of an object when it is created by the new operator. First the fields are initialized to their default values (§2.4, p. 42)— whether they are subsequently given non-default initial values or not—and then the constructor is invoked. This can lead to local chaining of constructors. The invocation of the constructor at the end of the local chain of constructor invocations results in the following actions, before the constructor’s execution resumes: • Implicit or explicit invocation of the superclass constructor. Constructor chaining ensures that the inherited state of the object is constructed first (§7.5, p. 282). • Initialization of the instance fields by executing their instance initializer expressions and any instance initializer blocks, in the order they are specified in the class declaration. Example 9.7 illustrates object initialization. The new operator is used at (8) to create an object of SubclassB. The no-argument constructor SubclassB() at (2) uses the this() construct to locally chain to the non-zero argument constructor at (3). This constructor then leads to an implicit call of the superclass constructor. As can be seen from the program output, the execution of the superclass’s constructor at (1) reaches completion first. This is followed by the execution of the instance initializer block at (4) and the instance initializer expression at (6). Then the execution of the body of the non-zero argument constructor at (3) resumes. Finally, the no-argument constructor completes its execution, thereby completing the construction of the object state. Note that the instance initializers are executed in the order they are specified in the class declaration. The forward reference to the field value at (5) is legal, because the usage of the field value is on the left-hand side of the assignment (it does not violate the declaration-before-reading rule). The default value of the field value is overwritten by the instance initializer block at (5). The field value is again overwritten by the instance initializer expression at (6), and finally by the non-zero argument constructor at (3). WOW! eBook www.wowebook.org Example 9.7 Object State Construction Click here to view code image // File: ObjectConstruction.java class SuperclassA { public SuperclassA() { // (1) System.out.println(“Constructor in SuperclassA”); } } //_______________________________________________________________________________ class SubclassB extends SuperclassA { SubclassB() { // (2) No-argument constructor this(3); System.out.println(“No-argument constructor in SubclassB”); } SubclassB(int i) { // (3) Non-zero argument constructor System.out.println(“Non-zero argument constructor in SubclassB”); value = i; } { // (4) Instance initializer block System.out.println(“Instance initializer block in SubclassB”); value = 2; // (5) } int value = initializerExpression(); // (6) private int initializerExpression() { // (7) System.out.println(“Instance initializer expression in SubclassB”); return 1; } } //_______________________________________________________________________________ public class ObjectConstruction { public static void main(String[] args) { SubclassB objRef = new SubclassB(); // (8) System.out.println(“value: ” + objRef.value); } } Output from the program: Click here to view code image Constructor in SuperclassA Instance initializer block in SubclassB Instance initializer expression in SubclassB Non-zero argument constructor in SubclassB No-argument constructor in SubclassB value: 3 Some care should be exercised when writing constructors for non-final classes, since the object that is constructed might be a subclass instance. Example 9.8 shows a situation where use of overridden methods in superclass initializers and constructors can give unexpected results. The example intentionally uses the this reference to underline that the instance methods and constructors are invoked on the current object, and that the WOW! eBook www.wowebook.org constructor call results in the initialization of the object state, as expected. The program output from Example 9.8 shows that the field superValue at (1) in SuperclassA never gets initialized explicitly when an object of SubclassB is created at (8). The SuperclassA constructor at (2) does have a call to a method that has the name doValue at (3). A method with such a name is defined in SuperclassA at (4), but is also overridden in SubclassB at (7). The program output indicates that the method doValue() from SubclassB is called at (3) in the SuperclassA constructor. The implementation of the method doValue() at (4) never gets executed when an object of SubclassB is created. Method invocation always determines the implementation of the method to be executed, based on the actual type of the object. Keeping in mind that it is an object of SubclassB that is being initialized, the call to the method named doValue at (3) results in the method from SubclassB being executed. This can lead to unintended results. The overriding method doValue() at (7) in SubclassB can access the field value declared at (5) before its initializer expression has been executed; thus, the method invoked can access the state of the object before this has been completely initialized. The value 0 is then printed, as the field value has not yet been initialized with the value 800 when the superclass constructor is executed. WOW! eBook www.wowebook.org Example 9.8 Initialization Anomaly under Object State Construction Click here to view code image // File: ObjectInitialization.java class SuperclassA { protected int superValue; // (1) SuperclassA() { // (2) System.out.println(“Constructor in SuperclassA”); this.doValue(); // (3) } void doValue() { // (4) this.superValue = 911; System.out.println(“superValue: ” + this.superValue); } } //_______________________________________________________________________________ class SubclassB extends SuperclassA { private int value = 800; // (5) SubclassB() { // (6) System.out.println(“Constructor in SubclassB”); this.doValue(); System.out.println(“superValue: ” + this.superValue); } @Override void doValue() { // (7) System.out.println(“value: ” + this.value); } } //_______________________________________________________________________________ public class ObjectInitialization { public static void main(String[] args) { System.out.println(“Creating an object of SubclassB.”); new SubclassB(); // (8) } } Output from the program: Click here to view code image Creating an Constructor value: 0 Constructor value: 800 superValue: object of SubclassB. in SuperclassA in SubclassB 0 Class initialization takes place before any instance of the class can be created or a static method of the class can be invoked. A superclass is initialized before its subclasses are initialized. Initializing a class involves initialization of the static fields by executing their static initializer expressions and any static initializer blocks. Initialization of an interface involves execution of any static initializer expressions for the static fields declared in the interface. An interface cannot specify instance initializer expressions, because it has no instance fields, nor can it specify any initializer blocks, because it cannot be instantiated. WOW! eBook www.wowebook.org Review Questions 9.10 Given the following class, which of these static initializer blocks can be independently inserted at (1)? Click here to view code image public class MyClass { private static int count = 5; static final int STEP = 10; boolean alive; // (1) INSERT STATIC INITIALIZER BLOCK HERE } Select the three correct answers. (a) static { alive = true; count = 0; } (b) static { STEP = count; } (c) static { count += STEP; } (d) static ; (e) static {;} (f) static { count = 1; } 9.11 What will be the result of compiling and running the following program? Click here to view code image public class MyClass { public static void main(String[] args) { MyClass obj = new MyClass(n); } static int i = 5; static int n; int j = 7; int k; public MyClass(int m) { System.out.println(i + “, ” + j + “, ” + k + “, ” + n + “, ” + m); } { j = 70; n = 20; } // Instance initializer block static { i = 50; } // Static initializer block } Select the one correct answer. (a) The code will fail to compile, because of the instance initializer block. (b) The code will fail to compile, because of the static initializer block. (c) The code will compile, and print 50, 70, 0, 20, 0 at runtime. (d) The code will compile, and print 50, 70, 0, 20, 20 at runtime. WOW! eBook www.wowebook.org (e) The code will compile, and print 5, 70, 0, 20, 0 at runtime. (f) The code will compile, and print 5, 70, 0, 20, 20 at runtime. (g) The code will compile, and print 5, 7, 0, 20, 0 at runtime. (h) The code will compile, and print 5, 7, 0, 20, 20 at runtime. 9.12 Given the following class, which instance initializer block inserted independently at (1) will allow the class to be compiled? public class FirstClass { static int gap = 10; double length; final boolean active; // (1) INSERT CODE HERE } Select the one correct answer. (a) instance { active = true; } (b) FirstClass { gap += 5; } (c) { gap = 5; length = (active ? 100 : 200) + gap; } (d) { ; } (e) { length = 4.2; } (f) { active = (gap > 5); length = 5.5 + gap;} 9.13 What will be the result of compiling and running the following program? Click here to view code image public class Initialization { private static String msg(String msg) { System.out.println(msg); return msg; } public Initialization() { m = msg(“1”); } { m = msg(“2”); } String m = msg(“3”); public static void main(String[] args) { Object obj = new Initialization(); } } Select the one correct answer. (a) The program will fail to compile. (b) The program will compile, and print 1, 2, and 3 at runtime. (c) The program will compile, and print 2, 3, and 1 at runtime. (d) The program will compile, and print 3, 1, and 2 at runtime. WOW! eBook www.wowebook.org (e) The program will compile, and print 1, 3, and 2 at runtime. 9.14 Which of the labeled lines in the following code can be independently uncommented by removing the // characters, such that the code will still compile? Click here to view code image class GeomInit { //int width = 14; { // area = width * height; } int width = 37; { // height = 11; } int height, area; //area = width * height; { // int width = 15; area = 100; } } /* Line A */ /* Line B */ /* Line C */ /* Line D */ /* Line E */ Select the two correct answers. (a) Line A (b) Line B (c) Line C (d) Line D (e) Line E Chapter Summary The following topics were covered in this chapter: • Automatic garbage collection, including the workings of the garbage collector and guidelines for facilitating garbage collection • Object finalization and chaining as part of garbage collection • Static and instance initializers, both as initializer expressions and as initializer blocks • The role played by initializers in initializing objects, classes, and interfaces WOW! eBook www.wowebook.org 10. The ArrayList Class and Lambda Expressions 10.1 The Class A program manipulates data, so organizing and using data efficiently are naturally important in a program. Data structures are ways to organize data. Java uses the term collection to mean a data structure that can maintain a group of objects so that the objects can be manipulated as a single entity or unit. Objects can be stored, retrieved, and manipulated as elements of a collection. The term container is also used in the literature for such data structures. Arrays are an example of one kind of collection. Other examples include lists, sets, queues, and stacks, among many others. WOW! eBook www.wowebook.org Lists Once an array is created, its length cannot be changed. This inflexibility can be a significant drawback when the amount of data to be stored in an array is not known a priori. In Java, the structures known as lists alleviate this shortcoming. Lists are collections that maintain their elements in order and can contain duplicates. The order of elements in a list is positional order, and individual elements can be accessed according to their position in the list. Each element, therefore, has a position in the list. A zero-based index can be used to access the element at the position designated by the index value, analogous to accessing elements in an array. However, unlike in an array, the position of an element in a list can change as elements are inserted or deleted from the list—that is, as the list is changed structurally. Sorting implies ordering the elements in a collection according to some ranking criteria, usually based on the values of the elements. However, elements is an ArrayList are maintained in the order they are inserted in the list, known as the insertion order. The elements in such a list are therefore ordered, but they are not sorted, as it is not the values of the elements that determine their ranking in the list. Thus, ordering does not necessarily imply sorting. The Java Collections Framework The Collection interface in the java.util package (also known as the Java Collections Framework) defines the general operations that a collection should provide. Other subinterfaces in the Java Collections Framework augment this interface to provide specific operations for particular kinds of collections. The java.util.List interface extends the java.util.Collection interface with the necessary operations to maintain the collection as a list (see Figure 10.1). In addition to the operations inherited from the java.util.Collection interface, the java.util.List interface defines operations that work specifically on lists: position-based access of the list elements, searching in a list, operations on parts of a list (called open range-view operations), and creation of customized iterators to traverse a list. WOW! eBook www.wowebook.org Figure 10.1 Partial ArrayList Inheritance Hierarchy The concrete class java.util.ArrayList implements the java.util.List interface. In Figure 10.1, the type parameter E in angular brackets (<>) after a reference type name indicates that the reference type is a generic type. The type parameter E represents the type of the element in the collection. Use of a generic type requires a concrete reference type to be substituted for the type parameter E. Examples in this section will make amply clear how to use a generic type, and in particular, the class ArrayList. The ArrayList class is a dynamically resizable implementation of the List interface using arrays (also known as dynamic arrays), providing fast random access (i.e., positionbased access in constant time) and fast list traversal—very much like using an ordinary array. The ArrayList class is not thread-safe; that is, its integrity can be jeopardized by concurrent access. The Java Collections Framework provides other implementations of the List interface, but in most cases the ArrayList implementation is the overall best choice for implementing lists. This section covers the basics of using an ArrayList. The Java Collections Framework is an extensive topic, far beyond the scope of this book. However, diving deep into the Java Collections Framework is a beneficial exercise that is highly recommended for all Java programmers. Declaring References and Constructing s In the discussion that follows, we assume that any class or interface used from the java.util package has been imported with an appropriate import statement. The following declaration statement shows how we can declare a reference that can refer to an ArrayList of a specific element type. It also illustrates how we can create an empty ArrayList of a specific element type, and assign its reference value to a reference: WOW! eBook www.wowebook.org Click here to view code image ArrayList palindromes = new ArrayList (); // (1) As this code indicates, the element type is specified using angular brackets (<>). The reference palindromes can refer to any ArrayList whose element type is String. The type parameter E of the class ArrayList in Figure 10.1 is replaced by the concrete class String. The compiler ensures that the reference palindromes can only refer to an ArrayList whose elements are of type String, and any operations on this list via this reference are type-safe. The simplest way to construct an ArrayList is to use the default constructor to create an empty ArrayList, as shown in the previous declaration. The default constructor creates an empty list with the initial capacity of 10. The capacity of a list refers to how many elements it can contain at any given time, not how many elements are actually in the list (called the size). The capacity of a list and its size can change dynamically as the list is manipulated. The ArrayList created in (1) can contain only elements of type String. The assignment in the declaration statement (1) is valid because the types on both sides are assignment compatible—an ArrayList of String. The reference palindromes can now be used to manipulate the ArrayList that was created. The Diamond Operator: <> The element type within the angular brackets (<>) can be omitted in the ArrayList creation expression on the right-hand side of the declaration statement. In this particular context, the compiler can infer the element type of the ArrayList from the declaration of the reference type on the left-hand side. Click here to view code image ArrayList palindromes = new ArrayList<>(); // Using the diamond operator The empty angular brackets, <>, are commonly referred to as the diamond operator. This operator must be used with the new operator when constructing an object of a generic type, like ArrayList, where the type information for its usage can be inferred by the compiler from the context, as in the preceding declaration statement. However, if the diamond operator is omitted, the compiler will issue an unchecked conversion warning, as shown at (2) in the next code snippet. A new ArrayList is created based on an ArrayList of Integer that is passed as an argument to the constructor. The ArrayList of Integer is created at (1). The reference newList1 of type ArrayList refers to an ArrayList whose element type is Integer, not String. The code at (2) compiles, but we get a ClassCastException at runtime at (3) when we retrieve an element from this list. The get() method call at (3) expects a String in the ArrayList, but gets an Integer. If the diamond operator is used, as shown at (4), the compiler reports a compile-time error, and the problem described at (3) cannot occur at runtime. By issuing an unchecked conversion warning at (2), the compiler alerts us to the fact that it cannot guarantee type-safety of the list created at (2). WOW! eBook www.wowebook.org Click here to view code image ArrayList intList = new ArrayList<>(); Integer intList.add(10); intList.add(100); intList.add(1000); // (1) ArrayList of ArrayList newList1 = new ArrayList(intList); conversion // (2) Unchecked System.out.println(newList1.get(0)); ClassCastException! // warning // (3) ArrayList newList2 = new ArrayList<>(intList); // (4) Compile-time error! Best practices advocate programming to an interface. In practical terms, this means using references of an interface type to manipulate objects of a concrete class that implement this interface. Since the class java.util.ArrayList implements the java.util.List interface, the declaration (1) can be written as shown in the next code snippet. This declaration is valid, since the reference value of a subtype object (ArrayList ) can be assigned to a reference of its supertype (List ). Click here to view code image List palindromes = new ArrayList<>(); // (2) List reference This best practice provides great flexibility in substituting other objects for a task when necessary. The current concrete class can easily be replaced by another concrete class that implements the same interface. Only code creating objects needs to be changed. As it happens, the Java Collections Framework provides another implementation of lists: the java.util.LinkedList class, which also implements the List interface. If this class is found to be more conducive for maintaining palindromes in a list, we need simply change the name of the class in declaration (2), and continue using the reference palindromes in the program: Click here to view code image List palindromes = new LinkedList<>(); // Changing implementation. The ArrayList class also provides a constructor that allows an empty ArrayList to be created with a specific initial capacity. Click here to view code image List palindromes = new ArrayList<>(20); // Initial capacity is 20. The ArrayList class provides the add(E) method to append an element to the list. This object is added after the last element in the list, thereby increasing the list size by 1. Click here to view code image palindromes.add(“level”); palindromes.add(“Ada”); palindromes.add(“kayak”); System.out.println(palindromes); The print statement calls the toString() method in the ArrayList class to print the elements in the list. This toString() method applies the toString() method of the individual elements to create a textual representation in the following default format: [level, Ada, kayak] WOW! eBook www.wowebook.org A third constructor allows an ArrayList to be constructed from another collection. The following code creates a list of words from a list of palindromes. The order of the elements in the new ArrayList is the same as that in the ArrayList that was passed as an argument in the constructor. Click here to view code image List wordList = new ArrayList<>(palindromes); System.out.println(wordList); // [level, Ada, kayak] wordList.add(“Naan”); System.out.println(wordList); // [level, Ada, kayak, Naan] The next examples illustrate the creation of empty lists of different types of elements. The compiler ensures that operations on the ArrayList are type-safe with respect to the element type. Declaration (3) shows how we can create nested list structures (i.e., list of lists), analogous to an array of arrays. Note that the diamond operator is not nested in (3). Declaration (4) shows that the element type cannot be a primitive type; rather, it must be a reference type. Click here to view code image List synonyms List attendance List > listOfLists String List
frequencies error! = new ArrayList<>(); // List of StringBuilder = new ArrayList<>(); // List of Integer = new ArrayList<>(); // (3) List of List of = new ArrayList<>(); // (4) Compile-time When comparing arrays and ArrayLists, there is one other significant difference that concerns the subtype relationship. Click here to view code image Object[] objArray = new String[10]; // (5) OK! In declaration (5), since String is a subtype of Object, String[] is a subtype of Object[]. Thus we can manipulate the array of String using the objArray reference. Click here to view code image objArray[2] = “Green”; objArray[1] = new Integer(2016); // (6) OK! // ArrayStoreException! The preceding assignment requires a runtime check to guarantee that the assignment is type compatible. Otherwise, an ArrayStoreException is thrown at runtime. For the ArrayList, the following declarations will not compile: Click here to view code image ArrayList
Source Exif Data:
File Type : PDF File Type Extension : pdf MIME Type : application/pdf PDF Version : 1.4 Linearized : No Has XFA : No XMP Toolkit : Adobe XMP Core 5.4-c005 78.147326, 2012/08/23-13:03:03 Format : application/pdf Description : Title : A Programmer's Guide to Java SE 8 Oracle Certified Associate (OCA) Subject : Publisher : Pearson Education Creator : Khalid A Mughal, Rolf W Rasmussen Date : 2016:07:22 00:00:00+02:00 Language : en Metadata Date : 2016:08:08 01:23:04+05:00 Modify Date : 2016:08:08 01:23:04+05:00 Identifier : B01ITNCBVK Identifier Scheme : mobi-asin Timestamp : 2016:07:25 08:33:02.971174+02:00 Author sort : Mughal, Khalid A Document ID : uuid:a7b02f63-1504-4658-97b1-9c532d9fcd34 Instance ID : uuid:5114c955-4e0a-4e11-991f-bf833cb4ba27 Page Count : 2629 Author : Khalid A Mughal Create Date : 2016:07:25 06:33:20+00:00 Producer : calibre 2.35.0 [http://calibre-ebook.com]EXIF Metadata provided by EXIF.tools