Top Ten Cool New Features of Java 2SE 1.4
Pages: 1, 2
4. FileChannel
The most important new feature of Java 1.4 is probably the New I/O API. This new API is defined in the java.nio package and its sub-packages, and provides high-performance input and output using an architecture that is mostly unrelated to the existing java.io API. One of the new classes, java.nio.channels.FileChannel, defines a new way to read and write files. Reading and writing files isn't very cool in and of itself, since we could already do that with existing java.io classes. What is cool about FileChannel, however, is that it provides facilities for memory mapping and locking files. Memory mapping allows you to view the contents of a file as if they were stored in an array directly in memory. The FileChannel API doesn't use arrays directly, but instead uses a java.nio.ByteBuffer. Here is code that uses memory mapping:
import java.io.*; // For FileInputStream
import java.nio.*; // For ByteBuffer
import java.nio.channels.*; // For FileChannel
// Create a FileChannel, get the file size and map the file to a ByteBuffer
FileChannel in = new FileInputStream("test.data").getChannel();
int filesize = (int)in.size();
ByteBuffer mappedfile = in.map(FileChannel.MapMode.READ_ONLY, 0, filesize);
// Assume the file contains fixed-size records of binary data.
static final int RECORDSIZE = 80; // The size of each record
int recordNumber = 1; // This is the record we want to read
byte[] recorddata = new byte[RECORDSIZE]; // An array to hold record data
mappedfile.position(recordNumber*RECORDSIZE); // Start of desired record
mappedfile.get(recorddata); // Fill the array from the buffer
Memory mapping can be lots of fun, but there is an overhead associated with it. In typical Java implementations, it is usually more efficient to just read small (< 100Kb, perhaps) files than it is to use memory mapping.
The other cool feature of FileChannel that we'll consider here is its ability to lock a file or a portion of a file. Locks can be used to prevent all concurrent access to the file (an exclusive lock) or to prevent concurrent writes (a shared lock). (Note that some operating systems strictly enforce all locks. Others only provide an advisory locking facility that requires programs to cooperate and to attempt to acquire a lock before reading or writing portions of a shared file.) Here is code you might use to obtain a shared lock on a region of a file in order to prevent any other (cooperating) programs from writing to that region while you were reading from it:
FileChannel in; // Initialized elsewhere
FileLock lock = in.lock(recordNumber*RECORDSIZE, // Start of locked region
RECORDSIZE, // Length of locked region
true); // Shared lock: prevent concurrent updates
// Now read the desired record, then release the lock when done
lock.release()
3. Non-Blocking I/O
For programmers writing high-performance network servers, the most important feature of the New I/O API is the ability to perform non-blocking I/O. The SocketChannel class of java.nio.channels allows you to read and write from a network connection; it is the New I/O equivalent of java.net.Socket.
SocketChannel and other SelectableChannel subclasses can be put into non-blocking mode and registered with a Selector object. The Selector class defines a method named select() that does the block; this method monitors one or more channels, and returns when one (or more) of them is ready for I/O. With a single Selector object, a server can monitor any number of SocketChannel client connections. Prior to the introduction of this new API, each client connection had to be handled with its own thread and blocking Socket object. This was a heavy-weight solution, and did not scale well for large servers. The code below shows the basic loop used in servers that perform non-blocking I/O:
import java.nio.channels.*;
import java.util.*;
// Create a selector object to monitor all open client connections.
Selector selector = Selector.open();
// Create a new non-blocking ServerSocketChannel, bind it to port 8000, and
// register it with the Selector
ServerSocketChannel server = ServerSocketChannel.open(); // Open channel
server.configureBlocking(false); // Non-blocking
server.socket().bind(new java.net.InetSocketAddress(8000)); // Bind to port
SelectionKey serverkey = server.register(selector, SelectionKey.OP_ACCEPT);
for(;;) { // The main server loop. The server runs forever.
// This call blocks until there is activity on one of the
// registered channels. This is the key method in non-blocking I/O.
selector.select();
// Get a java.util.Set containing the SelectionKey objects for
// all channels that are ready for I/O.
Set keys = selector.selectedKeys();
// Use a java.util.Iterator to loop through the selected keys
for(Iterator i = keys.iterator(); i.hasNext(); ) {
SelectionKey key = (SelectionKey) i.next();
i.remove(); // Remove the key from the set of selected keys
// Check whether this key is the SelectionKey we got when
// we registered the ServerSocketChannel.
if (key == serverkey) {
// Activity on the ServerSocketChannel means a client
// is trying to connect to the server.
if (key.isAcceptable()) {
// Accept the client connection
SocketChannel client = server.accept();
// Put the client channel in non-blocking mode.
client.configureBlocking(false);
// Now register the client channel with the Selector object
SelectionKey clientkey =
client.register(selector, SelectionKey.OP_READ);
}
}
else { // Otherwise, there must be activity on a client channel
// Double-check that there is data to read
if (!key.isReadable()) continue;
// Get the client channel that has data to read
SocketChannel client = (SocketChannel) key.channel();
// Now read bytes from the client into a buffer allocated elsewhere
int bytesread = client.read(buffer);
// If read() returns -1, it indicates end-of-stream, which
// means the client has disconnected, so de-register the
// selection key and close the channel.
if (bytesread == -1) {
key.cancel();
client.close();
continue;
}
}
}
}
2. Regular Expressions
Number two on the top ten list of cool new features in Java 1.4 is regular expressions. A regular expression is a way of describing a pattern of text. The java.util.regex package allows you to compare strings to regular expressions to determine if they match, and, if so, to determine exactly what parts of the string matched the pattern (and possibly its sub-patterns). Regular expressions also provide a powerful search-and-replace capability.
Regular expressions are heavily used in the Perl programming language, and Java 1.4 has adopted the regular expression pattern syntax of Perl 5. This means that Perl programmers can easily learn to use regular expressions in Java. In addition to the java.util.regex package, the String class has had regular-expression utility methods added that are simpler to use, in some cases. The following code shows some of the cool things you can do in Java with regular expressions. (Unfortunately, there is no room here to explain regular expression syntax, so if you are not already familiar with how regular expressions work in Perl or a similar language, this code won't make much sense to you.)
// Use a String convenience method to replace all occurrences of the word
// "java" (with any capitalization) with the correctly capitalized "Java"
s = s.replaceAll("(?i)\\bjava\\b", // The pattern: "java", case-insensitive
"Java"); // The replacement string
// Here's a more complex use of regular expressions, using the Pattern and
// Matcher classes from java.util.regex.
// Create a pattern that describes any word beginning with "Java", and
// which also "captures" whatever suffix follows the "Java" prefix. It
// uses the CASE_INSENSITIVE flag, so it matches any capitalization
Pattern p = Pattern.compile("\\bJava(\\w*)", Pattern.CASE_INSENSITIVE);
// This is a sentence we want to compare the pattern to.
String text = "Java is fun; JavaScript is funny.";
// Create a Matcher object to compare the pattern to the text
Matcher m = p.matcher(text);
// Now loop to find all matches of the pattern in the text
while(m.find()) {
// For each match, print the text that matched the pattern, and its
// position within the string
System.out.println("Found '" + m.group(0) + "' at position " + m.start(0));
// Also, if there was a suffix, print the suffix.
if (m.start(1) < m.end(1)) System.out.println("Suffix is " + m.group(1));
}
1. Assertions
And the number one cool feature of Java 1.4 is... the assert statement!
An assert statement is used to document and verify design assumptions in Java code. An assertion consists of the assert keyword, followed by a boolean expression that the programmer believes should always evaluate to true. By default, assertions are not enabled, and the assert statement does not actually do anything. It is possible to enable assertions as a debugging and testing tool, however, and when this is done, the assert statement evaluates the expression. If it is indeed true, then assert does nothing. On the other hand, if the expression evaluates to false, then the assertion fails, and the assert statement throws a java.lang.AssertionError.
The assert statement may optionally include a second expression, separated from the first by a colon. When assertions are enabled, and the first expression evaluates to false, the value of the second expression is taken as an error code or error message of some sort, and is passed to the AssertionError() constructor. The full syntax of the statement is:
assert assertion ;
or:
assert assertion : errorcode ;
It is important to remember that the assertion expression must be a boolean expression, which typically means that it contains a comparison operator or invokes a boolean-valued method. The errorcode expression may have any value.
Now let's consider how assert can be used in real code. Suppose you are writing a method in which you believe that the variable x will always have a value of 0 or 1. You can state this belief explicitly with an assertion:
if (x == 0) {
...
}
else {
assert x == 1 : x; // x must be 0 or 1.
...
}
A similar technique works with switch statements. If you ever find yourself writing a switch that does not have a default case, use an assertion to encode your assumption that all the possible cases are explicitly enumerated. For example:
switch(x) {
case -1: return LESS;
case 0: return EQUALS;
case 1: return GREATER;
default: assert false:x; // throw AssertionError if x is not -1, 0, or 1.
}
Note that assert false; always fails. This form of the statement is a useful "dead end" statement when you believe that the statement can never be reached.
The examples above are only two of the common idioms in which assertions are useful. There are other important ones as well, such as testing class invariants and testing preconditions for private methods. Learning to use assertions well takes practice, and you can read more about the theory and practical applications of assertions in Chapter 2 of Java in a Nutshell, 4th Edition.
|
Related Reading Java In a Nutshell |
Note that assert was not a reserved word prior to Java 1.4, so the javac compiler does not recognize the assert statement by default. To compile code that contains assertions, you must pass the argument -source 1.4 to javac. This means that existing Java code that happens to use assert as an identifier will still compile.
As noted above, assertions must be enabled in order to be tested at run time. You do this with the -ea argument to the java interpreter. This argument by itself enables assertions in all non-system classes. You can also enable on a per-class or per-package basis:
java -ea:com.example.sorters.MergeSort // Enable assertions for one class
java -ea:com.example.sorters... // Enable assertions for a package
You can also use the -da argument to selectively disable assertions in classes or packages.
David Flanagan is the author of a number of O'Reilly books, including Java in a Nutshell, Java Examples in a Nutshell, Java Foundation Classes in a Nutshell, JavaScript: The Definitive Guide, and JavaScript Pocket Reference.
Return to ONJava.com.
- Trackback from http://vader/CS/blogs/rherbst/archive/2005/07/18/250.aspx
Java SSL Socket Code
2005-07-18 13:13:49 [View]
- Trackback from http://www.davidflanagan.com/blog/000014.html
Failure to override
2004-03-07 21:27:23 [View]
-
10 new features in Java1.4
2003-10-29 08:25:18 anonymous2 [View]
-
Why would small files be more efficient?
2003-10-10 10:38:12 anonymous2 [View]
-
Comment on Top Ten Cool New Features of Java 2SE 1.4
2003-04-08 00:10:16 anonymous2 [View]
-
Wrapped Exceptions!!!
2002-06-07 02:46:27 silentj [View]
-
What's up with the IBM ad
2002-03-14 11:58:32 sheltonn [View]
-
top 10 new features in Java 2SE 1.4
2002-03-14 08:33:46 lu [View]
