Technical Support
Discussion Forum
Online Training
Technical Articles
Bug Parade
JDC Resources
DukeDollars
Early Access Tooolbar

Java Cup Logo
java.sun.com


Solaris Developer Connection

JDC Home Page

Log Out

Technical Articles
shadowSearchFAQFeedback

Technical Articles Index | JDC Tech Tips Index

JDC Tech Tips No. 7
February 17, 1998

Welcome to the JavaSM Developer ConnectionSM Tech Tips. This issue covers program assertions, and serialization and transient values. The first technique is useful for checking "should never fail" conditions and terminating an application gracefully. The second is used by Remote Method Invocation to transmit objects over a network. The JDC Team-

JDC Tech Tips
Tips, Techniques, and Sample Code

  • Program Assertions
  • Serialization and Transient Values
Tips, Techniques, and Sample Code: Program Assertions. The languages C and C++ have something called an assert macro, which you can use to check program assertions. For example, a sequence like:

        char* p = (char*)malloc(10);

        assert(p);

checks whether the pointer returned by the storage allocator is non-zero and terminates the program if it is not.
It's interesting to consider one way of adding a similar facility to the Java programming language, for example:
        public class Assert {
                private static void fail()
                {
                        System.err.println("assertion failed:");
        
                        Throwable e = new Throwable();
                        e.printStackTrace();
        
                        System.exit(1);
                }
                public static void assert(boolean b)
                {
                        if (!b)
                                fail();
                }
                public static void assert(long lng)
                {
                        if (lng == 0L)
                                fail();
                }
                public static void assert(double dbl)
                {
                        if (dbl == 0.0)
                                fail();
                }
                public static void assert(Object ref)
                {
                        if (ref == null)
                                fail();
                }
        }

With this class definition, you can write things like the following:
     
        int i = 0;
        Assert.assert(i < 10);          // checks whether i < 10

        Object p = f();
        Assert.assert(p);               // checks that p is non-null

If one of these assertion checks fails, the program will terminate with a stack traceback.

Note that there are several versions of the assert method. This is necessary because the Java language type system is not as loose as C/C++ in converting to/from logical values. For example, i < 10 has a boolean value, which is not convertible to an integer. So one version of assert is given for booleans, another for byte, char, short, int, and long, another for float and double, and a final one for all reference types.

Within the fail method, a new object of type Throwable, the superclass of all exception types, is created as a means of obtaining a current stack traceback. The traceback is printed, and the program terminates.

So if you want to check "should never fail" conditions, and terminate an application gracefully in such an event, try this technique. You should find it useful.

Serialization and Transient Values. The Java programming language incorporates a feature known as serialization, which is used to convert objects (including complex data structures such as lists and trees) into a stream of bytes, for writing to a file or across a network. The stream can later be deserialized and converted back into an object. This feature is very useful for giving objects persistence, and for transmitting them to a remote location.

A simple example of serialization looks like this:

        // write.java
        import java.io.*;
        public class write {
                public static void main(String args[])
                {
                        try {
                                FileOutputStream fos =
                                    new FileOutputStream("file.out");
                                ObjectOutputStream oos =
                                    new ObjectOutputStream(fos);
                                oos.writeObject(new Test("testing", 37));
                                oos.flush();
                                fos.close();
                        }
                        catch (Throwable e) {
                                System.err.println(e);
                        }
                }
        }

        // read.java
        import java.io.*;
        public class read {
                public static void main(String args[])
                {
                        Test testobj = null;
                        try {
                                FileInputStream fis =
                                    new FileInputStream("file.out");
                                ObjectInputStream ois =
                                    new ObjectInputStream(fis);
                                testobj = (Test)ois.readObject();
                                fis.close();
                        }
                        catch (Throwable e) {
                                System.err.println(e);
                        }
                        System.out.println(testobj.str);
                        System.out.println(testobj.ivalue);
                }
        }

        // Test.java
        public class Test implements java.io.Serializable {
                public String str;
                public transient int ivalue;
                public Test(String s, int i)
                {
                        str = s;
                        ivalue = i;
                }
        }


There are two programs in this example: one that serializes an Test object instance, and the other that reads back the serialized bytes and reconstitutes the object. ObjectOutputStream and ObjectInputStream are layered on top of FileOutputStream and FileInputStream to effect the actual serialization and deserialization, using the writeObject and readObject methods.

The Test class implements java.io.Serializable. Serializable is used as a marker interface, that is, it simply flags the fact that a class that implements it has some specific property. Without implementation of this interface, objects of class Test would not be serializable.

This example illustrates an interesting aspect of serialization. A field of a class may be declared as transient, meaning that the field is not serialized. In other words, it's not part of the persistent state of an object. An example of where this situation matters is java.util.Hashtable. When a Hashtable object is serialized, the keys and values are written out as pairs of values, rather than written out as the actual table. This is because the underlying hash codes (see Object.hashCode) may differ when the table is reconstructed. In the above example, ivalue has the default value 0 when the saved object is deserialized.

Finally, serialization is an important technique to understand, especially as it's used by Remote Method Invocation to transmit objects across a network.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

-- Copyright --
Copyright Sun Microsystems, Inc. All rights reserved. 901 San Antonio Road, Palo Alto, California 94303 USA.
This document is protected by copyright. For more information, see:
http://developer.javasoft.com/developer/copyright.html
The JDC Tech Tips are written by Glen McCluskey.





Questions?
23-Feb-98
Copyright © 1996-1998 Sun Microsystems Inc.
All Rights Reserved. Legal Terms. Privacy Policy.
Sun Logo