- •Contents at a Glance
- •Contents
- •About the Authors
- •About the Technical Reviewer
- •Acknowledgments
- •Introduction
- •Oracle Java Certifications: Overview
- •FAQ 1. What are the different levels of Oracle Java certification exams?
- •FAQ 4. Is OCPJP 7 prerequisite for other Oracle certification exams?
- •FAQ 5. Should I take the OCPJP 7 or OCPJP 6 exam?
- •The OCPJP 7 Exam
- •FAQ 7. How many questions are there in the OCPJP 7 exam?
- •FAQ 8. What is the duration of the OCPJP 7 exam?
- •FAQ 9. What is the cost of the OCPJP 7 exam?
- •FAQ 10. What are the passing scores for the OCPJP 7 exam?
- •FAQ 11. What kinds of questions are asked in the OCPJP 7 exam?
- •FAQ 12. What does the OCPJP 7 exam test for?
- •FAQ 13. I’ve been a Java programmer for last five years. Do I have to prepare for the OCPJP 7 exam?
- •FAQ 14. How do I prepare for the OCPJP 7 exam?
- •FAQ 15. How do I know when I’m ready to take the OCPJP 7 exam?
- •Taking the OCPJP 7 Exam
- •FAQ 16. What are my options to register for the exam?
- •FAQ 17. How do I register for the exam, schedule a day and time for taking the exam, and appear for the exam?
- •The OCPJP 7 Exam: Pretest
- •Answers with Explanations
- •Post-Pretest Evaluation
- •Essentials of OOP
- •FunPaint Application: An Example
- •Foundations of OOP
- •Abstraction
- •Encapsulation
- •Inheritance
- •Polymorphism
- •Class Fundamentals
- •Object Creation
- •Constructors
- •Access Modifiers
- •Public Access Modifier
- •Private Access Modifier
- •Protected and Default Access Modifier
- •Overloading
- •Method Overloading
- •Constructor Overloading
- •Overload resolution
- •Points to Remember
- •Inheritance
- •Runtime Polymorphism
- •An Example
- •Overriding Issues
- •Overriding: Deeper Dive
- •Invoking Superclass Methods
- •Type Conversions
- •Upcasts and Downcasts
- •Casting Between Inconvertible Types
- •Using “instanceof” for Safe Downcasts
- •Java Packages
- •Working with Packages
- •Static Import
- •Summary
- •Abstract Classes
- •Points to Remember
- •Using the “final” Keyword
- •Final Classes
- •Final Methods and Variables
- •Points to Remember
- •Using the “static” Keyword
- •Static Block
- •Points to Remember
- •Flavors of Nested Classes
- •Static Nested Classes (or Interfaces)
- •Points to Remember
- •Inner Classes
- •Points to Remember
- •Local Inner Classes
- •Points to Remember
- •Anonymous Inner Classes
- •Points to Remember
- •Enum Data Types
- •Points to Remember
- •Summary
- •Interfaces
- •Declaring and Using Interfaces
- •Points to Remember
- •Abstract Classes vs. Interfaces
- •Choosing Between an Abstract Class and an Interface
- •Object Composition
- •Composition vs. Inheritance
- •Points to Remember
- •Design Patterns
- •The Singleton Design Pattern
- •Ensuring That Your Singleton Is Indeed a Singleton
- •The Factory Design Pattern
- •Differences Between Factory and Abstract Factory Design Patterns
- •The Data Access Object (DAO) Design Pattern
- •Points to Remember
- •Summary
- •Generics
- •Using Object Type and Type Safety
- •Using the Object Class vs. Generics
- •Container Implementation Using the Object Class
- •Container Implementation Using Generics
- •Creating Generic Classes
- •Diamond Syntax
- •Interoperability of Raw Types and Generic Types
- •Generic Methods
- •Generics and Subtyping
- •Wildcard Parameters
- •Limitations of Wildcards
- •Bounded Wildcards
- •Wildcards in the Collections Class
- •Points to Remember
- •The Collections Framework
- •Why Reusable Classes?
- •Basic Components of the Collections Framework
- •Abstract Classes and Interfaces
- •Concrete Classes
- •List Classes
- •ArrayList Class
- •The ListIterator Interface
- •The LinkedList Class
- •The Set Interface
- •The HashSet Class
- •The TreeSet Class
- •The Map Interface
- •The HashMap Class
- •Overriding the hashCode() Method
- •The NavigableMap Interface
- •The Queue Interface
- •The Deque Interface
- •Comparable and Comparator Interfaces
- •Algorithms (Collections Class)
- •The Arrays Class
- •Methods in the Arrays Class
- •Array as a List
- •Points to Remember
- •Summary
- •Generics
- •Collections Framework
- •Processing Strings
- •String Searching
- •The IndexOf() Method
- •The regionMatches() Method
- •String Parsing
- •String Conversions
- •The Split() Method
- •Regular Expressions
- •Understanding regex Symbols
- •Regex Support in Java
- •Searching and Parsing with regex
- •Replacing Strings with regex
- •String Formatting
- •Format Specifiers
- •Points to Remember
- •Summary
- •Reading and Writing from Console
- •Understanding the Console Class
- •Formatted I/O with the Console Class
- •Special Character Handling in the Console Class
- •Using Streams to Read and Write Files
- •Character Streams and Byte Streams
- •Character Streams
- •Reading Text Files
- •Reading and Writing Text Files
- •“Tokenizing” Text
- •Byte Streams
- •Reading a Byte Stream
- •Data Streams
- •Writing to and Reading from Object Streams: Serialization
- •Serialization: Some More Details
- •Points to Remember
- •Summary
- •A Quick History of I/O APIs
- •Using the Path Interface
- •Getting Path Information
- •Comparing Two Paths
- •Using the Files Class
- •Checking File Properties and Metadata
- •Copying a File
- •Moving a File
- •Deleting a File
- •Walking a File Tree
- •Revisiting File Copy
- •Finding a File
- •Watching a Directory for Changes
- •Points to Remember
- •Summary
- •Introduction to JDBC
- •The Architecture of JDBC
- •Two-Tier and Three-Tier JDBC Architecture
- •Types of JDBC Drivers
- •Setting Up the Database
- •Connecting to a Database Using a JDBC Driver
- •The Connection Interface
- •Connecting to the Database
- •Statement
- •ResultSet
- •Querying the Database
- •Updating the Database
- •Getting the Database Metadata
- •Points to Remember
- •Querying and Updating the Database
- •Performing Transactions
- •Rolling Back Database Operations
- •The RowSet Interface
- •Points to Remember
- •Summary
- •Define the Layout of the JDBC API
- •Connect to a Database by Using a JDBC driver
- •Update and Query a Database
- •Customize the Transaction Behavior of JDBC and Commit Transactions
- •Use the JDBC 4.1 RowSetProvider, RowSetFactory, and RowSet Interfaces
- •Introduction to Exception Handling
- •Throwing Exceptions
- •Unhandled Exceptions
- •Try and Catch Statements
- •Programmatically Accessing the Stack Trace
- •Multiple Catch Blocks
- •Multi-Catch Blocks
- •General Catch Handlers
- •Finally Blocks
- •Points to Remember
- •Try-with-Resources
- •Closing Multiple Resources
- •Points to Remember
- •Exception Types
- •The Exception Class
- •The RuntimeException Class
- •The Error Class
- •The Throws Clause
- •Method Overriding and the Throws Clause
- •Points to Remember
- •Custom Exceptions
- •Assertions
- •Assert Statement
- •How Not to Use Asserts
- •Summary
- •Introduction
- •Locales
- •The Locale Class
- •Getting Locale Details
- •Resource Bundles
- •Using PropertyResourceBundle
- •Using ListResourceBundle
- •Loading a Resource Bundle
- •Naming Convention for Resource Bundles
- •Formatting for Local Culture
- •The NumberFormat Class
- •The Currency Class
- •The DateFormat Class
- •The SimpleDateFormat Class
- •Points to Remember
- •Summary
- •Introduction to Concurrent Programming
- •Important Threading-Related Methods
- •Creating Threads
- •Extending the Thread Class
- •Implementing the Runnable Interface
- •The Start( ) and Run( ) Methods
- •Thread Name, Priority, and Group
- •Using the Thread.sleep() Method
- •Using Thread’s Join Method
- •Asynchronous Execution
- •The States of a Thread
- •Two States in “Runnable” State
- •Concurrent Access Problems
- •Data Races
- •Thread Synchronization
- •Synchronized Blocks
- •Synchronized Methods
- •Synchronized Blocks vs. Synchronized Methods
- •Deadlocks
- •Other Threading Problems
- •Livelocks
- •Lock Starvation
- •The Wait/Notify Mechanism
- •Let’s Solve a Problem
- •More Thread States
- •timed_waiting and blocked States
- •waiting State
- •Using Thread.State enum
- •Understanding IllegalThreadStateException
- •Summary
- •Using java.util.concurrent Collections
- •Semaphore
- •CountDownLatch
- •Exchanger
- •CyclicBarrier
- •Phaser
- •Concurrent Collections
- •Apply Atomic Variables and Locks
- •Atomic Variables
- •Locks
- •Conditions
- •Multiple Conditions on a Lock
- •Use Executors and ThreadPools
- •Executor
- •Callable, Executors, ExecutorService, ThreadPool, and Future
- •ThreadFactory
- •The ThreadLocalRandom Class
- •TimeUnit Enumeration
- •Use the Parallel Fork/Join Framework
- •Useful Classes of the Fork/Join Framework
- •Using the Fork/Join Framework
- •Points to Remember
- •Summary
- •Using java.util.concurrent Collections
- •Applying Atomic Variables and Locks
- •Using Executors and ThreadPools
- •Using the Parallel Fork/Join Framework
- •Chapter 3: Java Class Design
- •Chapter 4: Advanced Class Design
- •Chapter 5: Object-Oriented Design Principles
- •Chapter 6: Generics and Collections
- •Chapter 7: String Processing
- •Chapter 8: Java I/O Fundamentals
- •Chapter 9: Java File I/O (NIO.2)
- •Chapter 10: Building Database Applications with JDBC
- •Chapter 11: Exceptions and Assertions
- •Chapter 12: Localization
- •Chapter 13: Threads
- •Chapter 14: Concurrency
- •OCPJP7 Exam (1Z0-804 a.k.a. Java SE 7 Programmer II) Topics
- •OCPJP 7 Exam (1Z0-805, a.k.a. Upgrade to Java SE 7 Programmer) Topics
- •Answers and Explanations
- •Answer Sheet
- •Answers and Explanations
- •Index
chapter 9 ■ Java File I/O (NIO.2)
} catch (IOException e) { e.printStackTrace();
}
}
}
Let’s execute this program and see whether it works:
D:\> java FileTreeWalkCopy Test Test2 Files copied successfully!
Well, the program copied the Test directory along with the files contained in the Test directory to the Test2 directory. Essentially, what you are doing is quite simple: in the preVisitDirectory() method, you are copying the directory (which is being visited). To retrieve the new destination path, you are using the relativize() method from the Path class. Similarly, you get a new destination path each time you visit a file, which is used to copy the file to the destination directory. That’s it. You’re done.
Finding a File
Once you understand how to walk through the file tree, it is very straightforward and easy to find a desired file. For instance, if you are looking for a particular file/directory, then you may try to match the file/directory name you are looking for with the visitFile() or preVisitDirectory() method. However, if you are looking for all files matching a particular pattern (for instance, all Java source files or xml files) in a file tree, you can use glob or regex to match the names of files. The PathMatcher interface is useful in this context as it will match a path for you once you specified the desired pattern. The PathMatcher interface is implemented for each file system, and you can get an instance of it from the FileSystem class using the getPathMatcher() method.
Before looking at a detailed example, let’s first understand Glob patterns. Glob is a pattern-specifying mechanism where you can specify file matching patterns as strings. Table 9-4 summarizes the supported patterns by the glob syntax.
Table 9-4. Patterns Supported by Glob Syntax
|
|
Pattern |
Description |
* |
Matches any string of any length, even zero length. |
** |
Similar to “*”, but it crosses directory boundaries. |
? |
Matches to any single character, |
[xyz] |
Matches to either x , y, or z. |
[0–5] |
Matches to any character in the range 0 to 5. |
[a–z] |
Matches to any lowercase letter. |
{xyz, abc} |
Matches to either xyz or abc. |
|
|
271
chapter 9 ■ Java File I/O (NIO.2)
Hence, you can specify syntax such as File*.java to match all Java source files that start with the letters "File" or you can have syntax such as program[0–9].class, which will match files such as program0.class, program1.class, and so on.
Let’s try an example that takes a path (a starting path) and a pattern (to find matching files) and then prints the list of files that match the specified pattern. The program is given in Listing 9-14.
Listing 9-14. FileTreeWalkFind.java
import java.io.IOException; import java.nio.file.*;
import java.nio.file.attribute.*;
class MyFileFindVisitor extends SimpleFileVisitor<Path> { private PathMatcher matcher;
public MyFileFindVisitor(String pattern){ try {
matcher = FileSystems.getDefault().getPathMatcher(pattern); } catch(IllegalArgumentException iae) {
System.err.println("Invalid pattern; did you forget to prefix \"glob:\"?
(as in glob:*.java)");
System.exit(−1);
}
}
public FileVisitResult visitFile(Path path, BasicFileAttributes fileAttributes){ find(path);
return FileVisitResult.CONTINUE;
}
private void find(Path path) {
Path name = path.getFileName(); if(matcher.matches(name))
System.out.println("Matching file:" + path.getFileName());
}
public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes fileAttributes){ find(path);
return FileVisitResult.CONTINUE;
}
}
public class FileTreeWalkFind {
public static void main(String[] args) { if(args.length != 2){
System.out.println("usage: FileTreeWalkFind <start-path> <pattern to search>"); System.exit(−1);
}
Path startPath = Paths.get(args[0]); String pattern = args[1];
272
chapter 9 ■ Java File I/O (NIO.2)
try {
Files.walkFileTree(startPath, new MyFileFindVisitor(pattern)); System.out.println("File search completed!");
} catch (IOException e) { e.printStackTrace();
}
}
}
Let’s execute it first and then understand how it works.
d:\> java FileTreeWalkFind ch9-15 glob:File*.java Matching file:FileTreeWalkFind.java
File search completed!
d:\> java FileTreeWalkFind ch9-15 glob:File* Matching file:FileTreeWalkFind.class Matching file:FileTreeWalkFind.class Matching file:FileTreeWalkFind.java
File search completed!
Here’s how it works:
•You define your FileVisitor, MyFileFindVisitor, which overrides two methods, visitFile() and preVisitDirectory().
•In the constructor of your visitor class, you retrieve a PathMatcher instance using a
FileSystem instance.
•The overridden methods call a method of find(); this method creates a Path object from the file name of the passed Path object. This is necessary since you want matcher to match only the file name, not the whole path.
•You start walking through the file tree using the method walkFileTree(); you specify an instance of MyFileFindVisitor as the FileVisitor.
•If the current visiting file/directory matches the pattern, you print the file name. The process of matching the specified pattern is carried out by a PathMatcher instance.
Watching a Directory for Changes
Let’s assume that you have implemented a simple IDE to work on Java programs. You have loaded a Java source file in it and you are working on it. What happens if some other program changes the source file you are working on? You might want to ask the user whether he wants to reload the source file. In fact, many IDEs and other programs shows a message to the user and ask permission from the user to reload the files (see Figure 9-2). However, the key point is: how do you get notified that the file you are working on was modified by some other program?
273
chapter 9 ■ Java File I/O (NIO.2)
Figure 9-2. File change notification shown in Eclipse IDE
Java 7 offers a directory watch service that can achieve exactly the same result. You can register a directory using this service to change event notification, and whenever any change happens in the directory (such as new file
creation, file deletion, and file modification) you will get an event notification about the change. The watch service is a convenient, scalable, and an easy way to keep track of the changes in a directory.
Let’s look at a program in Listing 9-15 first and then see how the watch service API works. Assume that you want to monitor the src directory of your current project. You are interested in file modification events, such that any change in any file of the directory results in an event notification to your program.
Listing 9-15. KeepAnEye.java
import java.io.IOException; import java.nio.file.*;
public class KeepAnEye {
public static void main(String[] args) { Path path = Paths.get("..\\src"); WatchService watchService = null; try {
watchService = path.getFileSystem().newWatchService(); path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
} catch (IOException e1) { e1.printStackTrace();
}
//infinite loop for(;;){
WatchKey key = null; try {
key = watchService.take(); } catch (InterruptedException e) {
e.printStackTrace();
}
// iterate for each event for(WatchEvent<?> event:key.pollEvents()){
switch(event.kind().name()){ case "OVERFLOW":
System.out.println("We lost some events"); break;
case "ENTRY_MODIFY":
274
chapter 9 ■ Java File I/O (NIO.2)
System.out.println("File " + event.context() + " is changed!"); break;
}
}
//resetting the key is important to receive subsequent notifications key.reset();
}
}
}
Execute this program and meanwhile try to change the source file as well as the class file in the src directory. You may get results like this:
d:\workspace\ch9-16\src>java KeepAnEye File KeepAnEye.java is changed!
File KeepAnEye.java is changed! File KeepAnEye.java is changed! File KeepAnEye.class is changed! File KeepAnEye.class is changed!
Well, that’s great—it’s working as intended. Now, let’s understand the program step by step:
•The first thing you need to do is to get an instance of WatchService. You can get a watch service instance using the FileSystem class. Here, you are getting a FileSystem instance using a path instance, and then you are requesting an instance of watch service from the FileSystem. You may also get an instance of the FileSystem from FileSystems
(FileSystems.getDefault()).
•Once you have an instance of the watch service, the next step is to register the directory to the watch service. The Path object provides two methods for registration: the first register() method takes variable arguments (first an instance of watch service and subsequently the kind of watch event in which you are interested). The second register() method takes one additional parameter—the watch event modifier. Here, you are using the first register() method.
•You want to receive an event notification only when a file is modified; thus you specify ENTRY_MODIFY (belonging to StandardWatchEventKinds). Other kinds watch events include ENTRY_CREATE, ENTRY_DELETE, and OVERFLOW. The first three kinds are self-explanatory; OVERFLOW specifies that a few event notifications are discarded or missed. These event kinds can be specified based on the requirements.
•Once the registration is done, you are ready to receive event notifications. You can implement an infinite loop in which you wait for the suitable event to happen.
•In the loop, you need to wait for the event to happen. Here, you can ask the watch service to notify this program when an event occurs. You can do this using three methods:
•The poll() method returns a queued key if available; otherwise it returns immediately.
•The poll(long, TimeUnit) method returns a queued key if available; otherwise it waits for the specified time (the long value) and for the specified time unit. The method returns after the specified time limit is elapsed.
275
