The Java 2 Platform just gets better and better; or it would, anyways, if Apple wasn't six months behind in delivering it. (We just got 1.4.2, whereas the rest of the world has had it for yonks.)
Gripes aside, though, an awful lot of people are looking forward to Java 1.5. Spurred on by a spate of comparisons to .NET's "simplicity", it aims to bring in a wealth of new syntax aimed at:
- Removing that redundant code you write over and over again
- Improving the language's ability to catch problems at compile time
- Providing .NET-like functionality
- Improving performance
What follows is a feature review, and a list of references for further reading on the specifications that are worth reviewing for further information.
Feature Review
Here's a feature review of some of the features you'll find added to 1.5, along with my opinions on why they're there, and whether or not they're actually going to be of any use to you.
Chipset Support
Claims AMD64 support for Linux and Windows 2003. What this isn't telling you is that Java probably has the same old problem with heap size not getting over 2GB happily on some platforms...
Current beta runs on SuSE Linux 8; not SP3, or Redhat AS 3; next beta, supposedly, will. Final, I imagine, will support all the big commercial distros, as this is probably kernel related.
Java won't support Sparc V8 processors for the server VM. All those old SPARC boxes just became useless.
Metadata
This is all about ways of getting your toolchain to work together. Whether you're an XDoclet fan, a Castor Framework user, or implement a system that relies on Hibernate or Prevayler, this stands to make your life easier.
The purpose of it is to provide a way to insert, directly into the java class, all of the information that any non-java tools (whether they be mechanisms that you build into your application to auto-discover and auto-load plugins, source generation frameworks, or data binding layers) can use to configure themselves. Instead of a config file that tells Hibernate what database fields map to your object, your object contains the metainformation needed for Hibernate to do that.
This is a good thing: Standardised use of this will mean that we can benefit from frameworks that do more - they embed into our software better, and they provide better abstractions for our work, ultimately helping us produce better software with less difficulty.
This falls squarely into "providing .NET functionality", as .NET's C# framework had this feature first; it doesn't necessarily implement it in quite as powerful a way, but it's the basics that really matter most here.
Generic Types
This is a long time coming. Generic types have a single purpose: To get you a version of a class whose API is specifically tuned to accept and allow a specific, single object. This has two effects:
- An ArrayList<Integer> will only accept an Integer argument. The compiler will throw an error if you try at any point to provide an object which doesn't meet this interface requirement.
- It removes all of the ugly typecasting that littered your codebase before this feature existed.
While there are circumstances under which collections which take any object you can throw at it are useful, most of our real-world usage of these things involves specific types; these APIs, historically, have been a nightmare for clean architecture - weak typing at this level costs CPU time and manpower, and makes our interfaces and implementations ugly. Very rarely does a developer build an API whose arguments are objects - and yet at the core of 1.4 and before, all we could ever do was deal in those kinds of collections. We rarely built those kinds of APIs ourselves, but we relied on them throughout our codebases, often for little or no good reason.
This is a great opportunity to go through the codebase and start placing restrictions, and clean up typing and structural issues in the code to a more rigorous standard, both internally and externally. It promises cleaner, more readable code, and a reduction in the likelihood (when implemented properly across the codebase) of ending up with ClassCastExceptions that didn't get caught at compile time due to this obfuscation of types. It eliminates a lot of the ugly assumptions we make every day from our code.
In short, regardless of whether or not you think they went far enough to provide us with the Java equivalent of C++ templates, it's hard to think of this feature as being anything other than a Good Thing.
This is just generally an improvement to the language's ability to catch problems; it may also come with performance benefits, and its nearest parental relationship is to C++ templates, not .NET.
Autoboxing
This is, in short, another joy from the lands of .NET's Common Language Runtime(CLR) - whose runtime does this transparently, and fundamentally.
Java's 1.5 incarnation of this feature is, in fact, a bit of a hack, auto-promoting and demoting an integer, for example, in native "int" form, to a wrapped Integer object of its matching object type. To CLR, there's no difference; in Java, there's compiler magic involved.
This means that anywhere you once had:
ArrayList list = new ArrayList(); list.add(0, new Integer(42)); int total = ((Integer)list.get(0)).intValue();
You can now do:
ArrayList<Integer> list = new ArrayList<Integer>(); list.add(0, 42); int total = list.get(0);
The compiler will automatically "autobox" an int into an Integer, and auto-unbox an Integer into an int. Efficiently. (At least in theory)
This is syntactic code sugar aimed directly at Microsoft's claims about the complexity of syntax of simple Java code, solved in an elegant and obvious manner.
Enhanced For Loop
Also aimed squarely at that box is a change to the way we all iterate. You will, by now, be very familiar with:
ArrayList list = new ArrayList();
for (Iterator i = list.iterator(); i.hasNext();) {
Integer value=(Integer)i.next();
}
Now, add a generic type, so that the ArrayList accepts and returns only your object type, instead of Object, and use the new syntax, casting free:
ArrayList<Integer> list = new ArrayList<Integer>();
for (Integer i : list) { ... }
The "for i in list" construction doesn't look like it shaves off much, but it makes entirely transparent to the syntax of the language the use of the iterator to iterate through a list. Coupled to generics, it removes all of the casting and all of the syntactic garbage from what is semantically accurate in both - improving code readability.
Real enumerated types!
Gone are the days of those horriffic, public static variables. You know the ones - the ones you built whole objects to house because they were unwieldy at best, the ones that are peppered haphazardly all over your codebase.
public enum StopLight { red, amber, green };
and poof!, java gains a basic language feature that's been around for donkey's years in every other language. All through compile-time magic.
This will have an immediate and obvious impact on code-quality anywhere anyone uses tokenized parameters of any kind to do any work whatsoever. Implementing it is transparent, and means that where you once took an int or a string, you can now take a solid type.
Static Import
Now here's a useless tidbit if you ever saw it. The sole purpose of this functionality is to get rid of having to pepper someone else's statics all over your codebase; instead, you can declare them as yours.
import static java.awt.BorderLayout.*; getContentPane().add(new JPanel(), CENTER);
Syntactic fluff, and yet useful, somehow, at the same time. You can import specific constants, instead of *, but then you're just moving the contextual fluff into the header, aren't you. ;)
This one's basically there to pretty up code, much as some of the other ones are. While they may have use in some architectures, one can probably successfully argue that if you can really, really get a lot of mileage out of this, your architecture has something wrong with it in the first place. Few people have a real need for this kind of token import.
Formatted Input
A console reader. This will, like, so not shatter your world.
I'm sure that someone fought hard to get this in. If you discover who they are, mail me their address and a gun without serial numbers that hasn't previously been used in a crime, and I'll take care of them for the sin of having diverted development from truly useful things.
Varargs
Now, I'd like to say something about this. You'll almost never need it. And if you think you can use it, you're probably right, and I'm probably also right in saying you shouldn't.
Varargs, in a good interface, has almost
never
got a useful purpose. The only possible argument for why one would use this is almost inevitably for logging, debugging, formatting, and/or printing through formatters. Traditionally, to do these things, you assemble your objects into an array of objects (with all the syntactic overhead and anonymous mess that goes with doing so) and have your API declare that it takes the array of objects as an argument. Only once in a very blue moon should you even consider introducing API that uses this into your applications. Ask yourself this:- how often is it, really, that you don't know how many things you're passing into your API
- ...and why wouldn't you want the caller to know.
For debugging and printf, this is a godsend. It will make formatters much cleaner, syntactically, and this is a good use of that. This is not, in any way, a good use of just about anything else I can come up with . If you think you've found a wonderful use for it outside of that kind of scope, I assure you, you're almost guaranteed to be wrong. Think long and hard about introducing a completely free-form API that you can never undo or change the contract of, ever, for the rest of your life, for anything other than another varargs.
Concurrency Utilities
In other words, the basic tools you'll find in any real operating system for implementing thread concurrency and parallelism. These include...
- Queues
A wide variety of queue types, to boot. Choose blocking or non-blocking; blocking include array-based, priority-based, and synchronous varieties, in addition to a couple of others. Can we all say it's about time - we're all pretty tired of relying on the Jakarta Commons for basic OS functionality. - ConcurrentHashMap
A non-locking, thread-safe version of a Hashtable. The usefulness of this is that you can have as many non-blocking retrieval operations as you like, and blockers will write based on a tunable number of simultaneous updaters. For high-performance java, or areas of heavy execution, this is a godsend. Also includes a CopyOnWriteArrayList which copies the underlying data anytime it's modified to prevent anyone currently iterating from having a problem; ditto for the CopyOnWriteArraySet, to be used only for small arrays where read is many times more common and important than write. Benchmark, benchmark, benchmark. - Task Structures
A CancellableTask type that lets you cancel a task if it hasn't run already, or even interrupt it if it's executing, and find out whether it completed normally or was cancelled during execution. Also includes FutureTask for supporting allowing to start and stop computations, querying to see if computations are complete, and retrieving results; it implements a new Future interface gets you access to pending results of asynchronous tasks. An Executor which allows you to create a thread pool and stick a bunch of things into it for execution either synchronously or asynchronously, and a ThreadPoolExecutor that lets you tune and modify the way that the thread pool behaves in various circumstances. For anyone who needs to kick off background processes to do computation or time-delayed tasks, this is manna from heaven, and will replace a lot of custom cruft in a lot of places, I suspect. For those who have intermittent monitoring and housekeeping tasks, there's even a ScheduledExecutor which can support calling a task on a regular basis or a specific point in the future. - Semaphores and Latches
Synchronization primitives available to everyone else but a java user on the planet. A CountDownLatch is known to mortals as a condition variable, and is basically a way of implement a single-write, multi-read semaphore, a safe way for threads to return when all workers are done, and a broad range of other clever uses. A CyclicBarrier implements a way for lots of threads to synchronize on a barrier location; it's a nice way of doing shared state updates, where everyone gets stuck at a safe location before modification of shared data takes place. An Exchanger provides a ready-built synchronization point at which two threads can exchange objects, such as buffer swapping from one thread filling a buffer to another thread draining it. And best of all, a real semaphore. A way to restrict access to a data structure without the ugliness of a synchronize block that blocks all access until the safe return. It can be implemented as an on/off mutual exclusion lock, where only one party can acquire the lock at any time, or on multiple users. It, by default, allows 'barging', where the first guy to ask for one isn't necessarily the first guy to get one, or you can tell it to be fair, and use a FIFO queue. Threads waiting for access will block until it's available. Just try and build a decent pool implementation without one of these. And last but not least, it supports a mechanism to get all of the available units, providing the perfect, tunable implementation of a reader-weighted write lock. The only thing that isn't clear is whether there's a way to tip the balance - to tell the primitive that it should prefer people who ask for the whole enchilada rather than a single individual one - but fair mode comes close, pushing out all remaining reads ahead of the writer's request to obtain the whole lock.
Serializaton
If you weren't already setting serialVersionUIDs in your serializable classes, start now.
Major work on RMI
In a document entitled RMI Updates in 1.5, they go through extreme length to explain how you can dynamically generate stub classes, instead of using the stub compiler. This means that they're constructing a java.lang.reflect.Proxy instance as a dynamically generated class, and have finally sorted out instantiation and use of dynamic classes in the runtime. No more java.rmi.StubNotFoundException, and you can chuck pretty much anything to any RMI client without needing to fiddle with whether or not you've got identical class descriptors.
This is both a time-saver and a cleanup of the code, ensuring that RMI users get a more dynamic and flexible development experience, while having little impact on real-life use of the technology.
JVM: Memory Footprint, Scalability, and Performance
Here's an awful lot of nothing. They say very little about what's been done, but the list includes:
- "An emphasis on startup time and memory footprint"
Ok, so how many years have they been saying this for. What's most dreadful about this whole thing is that every time, they're right - it's as if there's an infinite amount of cruft that can be eliminated from Java if they'd just spend a few more resources. All the more reason Sun should open source it, of course. - Java Heap Self-Tuning
Asking the heap to self-tune itself will now allow specifying performance goals, so you can tune the heap to meet those goals with the smallest footprint that meets those goals. Basically, you can tell it "tune yourself aggressively, until you start swapping, and then back the hell off." - An Improved Parallel GC
This promises to give us an improved system that we don't have to mess with setting memory parameters for. Default JVM uses UseParallelGC now. You can control through parameters how and when it can throw OutOfMemoryException when it spends too much time collecting small amounts of the heap. UseAdaptiveSizePolicy now can be provided with a maximum GC pause goal, an application througput goal, or a minimum footprint. Set the app throughput goal too high, and the heap will grow to max - sounds like a feature to me. It does, however, appear that Java is getting better at a 'run me and ignore me' set of policies that actually work, unlike we have today. And they'll provide more context-sensitive OutOfMemoryExceptions, as well, so we're not all at a loss to figure out what's gone wrong where. - Class data sharing in the Hotspot JVM
Already implemented in some proprietary environments for Java, this shares read-only data between multiple running JVMs and allows 'packing' of core classes into a mappable format. Apple's VM, for example, has done this for ages. Similar to the way a DLL is loaded into memory, the JVM will ship with a 'shared archive' of system classes that go directly into memory, pre-parsed, without going through the class-loading nightmare that a normal pre-1.5 startup does. It also means that classes running under one process will be shared with classes running under other processes accessing the same files - multiple JVM instances sharing their common class data.
Instrumentation and Profiling
This is another performance headliner, really. Everything from a management API that lets you get JVM instrumentation via JMX or SNMP, to a new profiling API for profiling, monitoring, and debugging. Supports bytecode instrumentation, and access to instrumented code.
Code instrumentation, of course, is done through java.lang.instrument, and is a new set of API which allows you to do a host of useful things - grabbing stack traces, inserting instrumentation code into an object at runtime, so profilers can literally introduce code which enables profiling of specific items without incurring huge VM overhead.
UI
It's called Ocean, and... well, it's another bloody theme, isn't it. Looks like they finally figured out that Metal was pig-ugly.
In addition, they've updated the XP and GTK skinnable look and feels. X11 users get the joy of drag-and-drop support with XDND in Swing.
OpenGL Acceleration for Java2D
For anyone doing client stuff, this is pretty much a godsend: Java2D accleration via OpenGL, the same way that Mac users get acceleration for their desktop today. Java2D surfaces become OpenGL surfaces, with all of the joy and wonder this entails. Difference being, of course, that ours has taken two versions to get right. Might want to leave this disabled for a bit. ;)
Still, this will eventually lead to the same kind of near-native performance that Mac OS X users get with Java graphics performance for all platforms, and is a good thing.
XML
Updates to XML 1.1 and Namespace, XML Schema, SAX 2.0.1, XSLT, and the fast XSLTC compiler. DOM level 3 support. In other words, Sun comes up out of the stone ages.
In addition, JAXP has been updated from JAXP 1.1 (Crimson) to 1.3 (Xerces), a completely different implementation of JAXP. For anyone using the standard JAXP API who wasn't already explicitly using Xerces, you could be in for a shocker. There's a JAXP Compatibility Guide available for your perusal.
JDBC
A CachedRowSet, implementing an in-memory collection of rows which is disconnected from the user, and a WebRowSet for database rows in XML you can use directly in a JSP. We've all seen them from our driver manufacturers, and now we get them from Sun. This is really just a coalescing of best practice from JDBC land into the core, as is the XML stuff, above.
Conclusion
An awful lot of what's there is syntactic sugar, but a lot of that syntactic sugar is welcome, quite frankly. A lot of the big, new things, like Generics, we'll all be wondering how we lived without. And like much of the Concurrency framework, and some of the really basic updates to primitives, it's stuff we've been bitching about for years, something every other native operating system provides out of the box on day one - something missing in Java, and therefore, something of a nightmare.
Those nightmares are becoming a lot fewer and further between; the JCP process shows real sign of doing positive work, and really advancing the java platform in a way that, unlike many previous releases before it, has actually provided the platform with exactly what it needs to both become current in specific areas and stay relevant in the space it's currently dominant in.
The changes are more than just welcome: they're vital. And they're just in time, as well. IDE support, I expect, will be a bit slow; there's a lot of pretty fundamental change that really has the ability to impact an IDE's stability dangerously, and IBM has already stated that it's not in the Eclipse 3.0 plans to ensure 1.5 compatibility when the shiny new version of Eclipse comes out the doors later this year.
References
- J2SE 1.5 in a Nutshell
- New Language Features for Ease of Development in the Java 2 Platform, Standard Edition 1.5
- J2SE 1.5.0 Release Notes
- J2SE 1.5.0 Serialization Changes
- J2SE (TM) 1.5.0 New Features
- Java RMI Release Notes
- Garbage Collection Ergonomics
- Bugs fixed in J2SE (TM) 1.5.0 Beta 1
- J2SE 1.5.0 Compatibility with Previous Releases
Documents
- 003 Java Management Extensions (JMX) Specification
- 013 Decimal Arithmetic Enhancement
- 014 Add Generic Types To The Java Programming Language
- 028 Java SASL Specification
- 114 JDBC Rowset Implementations
- 133 Java Memory Model and Thread Specification Revision
- 160 Java Management Extensions (JMX) Remote API 1.0
- 163 Java Platform Profiling Architecture
- 166 Concurrency Utilities
- 174 Monitoring and Management Specification for the Java Virtual Machine
- 175 A Metadata Facility for the Java Programming Language
- 200 Network Transfer Format for Java Archives
- 201 Extending the Java Programming Language with Enumerations, Autoboxing, Enhanced for Loops and Static Import
- 204 Unicode Supplementary Character Support
- 206 Java API for XML Processing (JAXP) 1.3
Comments (1)
hi,
Nice work on the post. We seem to think alike (different) although i don't own a mac...
i've read several articles about 1.5 improvements and this is the best, so far. Of course, the content is similar but i appreciate your non-PDF presentation style.
Syntax sugar, varargs, monitoring, printf, OpenGL, XML = check
i'm not sure that i see the point of cached result sets, though. i've been lucky so far using a group of hashmaps stuffed into the servlet context to act like a cache. One global DB extraction is performed when the webapp is intialized and create/update is done on both context/DB data. Granted, only a couple dozen folks are using the app (help desk issue tracker) so there's not a great risk of collision. However, with the concurrent hashmap, it would seem that this should be a thread ("executor")-safe approach.
Thanks again for the info and feel free to contact me - neptune7678@hotmail.com
i'm curious about using the new Java2D utilities. My latest hope was embedding SVG into Swing but they gave up on the cool scripting capabilities. i've been experiencing OGL in games for years so i can imagine the possibilities :)
havagood1,
D
Posted by DMJG | March 20, 2004 1:12 AM
Posted on March 20, 2004 01:12