Letters to the Editor

New Media Programming columnist Bill Day does his honorable best to answer all his mail -- praise, suggestions and grumbles alike. Plus: Bill Venners fields a slew of tough Design Techniques-related questions, Mark Johnson sets the record straight on serializing class objects, and more

"Improve your programs with practical audio" by Dan Becker

Read

Audio apps

Dan,

In your article you claim to have shown how to put sound in an application. I read the article carefully, but I still don't see how to put sound in an application. I probably missed something, but I'm not sure what.

I wrote a nice Draw Poker program that deals five cards and allows the player to draw as many as five cards, then wins or loses depending on the resulting hand. I wrote it as both an application and an applet. I can get the sounds of coins being returned and the deck being shuffled in the applet, but I can't get the same sounds into the application. Can you give me some advice?

Sam Rhoads

Sam, Use the streams method of loading and playing a sound. This method does not use or depend on any classes in

java.applet.Applet

. Here is a code sample for playing a sound from a given file:

   import java.io.*;
                                       import sun.audio.*;
                                       // Get sound from file stream.
                                       FileInputStream fis = new FileInputStream( new File( "spacemusic.au") );
                                       AudioStream as = new AudioStream( fis ); // header plus audio data
                                       AudioData ad = as.getData(); // audio data only, no header
                                       AusioDataStream audioDataStream = new AudioDataStream( ad );
                                       continuousAudioDataStream = new ContinuousAudioDataStream( ad );
                                       audioPlayer.start( audioDataStream );
                                      

Use the following calls to stop (pause) and reset the stream:

         audioPlayer.stop( audioDataStream );
                                       audioDataStream.reset();
                                      

Dan Becker

Media Programming: "Introduction to Java media programming" by Bill Day

Read

Media server support

Bill,

I am a student in Israel and I am trying to create a streaming video application using the Java Media Framework. If I use PushDataStream, does it follow that I have to prepare the file I'm streaming (AVI in our case) and send the frames via DataOutputStream? Or can the JMF help me implement a stream from the server?

Ikaros

Ikaros, The JMF is (at least so far) a client-side only framework. It helps you build media players for audio and video, but it does not provide any media server support. You will need to build your own server services or use third-party servers. If you're able to use its codecs instead of AVI, Real Networks has free but fairly powerful versions of its encoders and servers. Real Networks has also promised to make JMF-based versions of its RealPlayer available at some point but it has yet to deliver them. If you want a JMF player now, you'll have to build your own using the C++ interface. If you need to stick with AVI format and you're serving from a Windows NT system, you could possibly use a Netshow server and then use the JMF for the client playback. And yes, your server needs to handle the control events. Your JMF player, in turn, needs to have a DataSource that uses the server's events to keep the playback in synch. Also, the premier article in my new monthly Media Programming column contains some information on the JMF, and will be followed, a few columns down the road, by a JMF-specific article. Bill Day

Media programming with MS-Java

Bill,

I would like you to cover both Sun's and Microsoft's media APIs in your new column. Comparison between the various technologies would be useful.

And, for your information, Microsoft's IE 4.0 ships with DirectSound for Java, DirectAnimation, Direct3D for Java, and DirectDraw for Java.

Sean Sullivan

Sean, Thanks for the input. It's actually quite timely, as I have been thinking a lot recently about how much I should personally learn about MS-Java related technologies and wondering how much of that readers would like to see in the column. I'm planning to pose that question to my readers in an upcoming column. A compare/contrast of Java Direct3D with Java 3D, for instance, might be a good thing to write about, but if more people are interested in learning about Java OpenGL bindings, I would like to cover that, too (or instead of Direct3D). My other problem with DirectX is that I am simply not an expert on this technology. As I mentioned before, I've been getting up to speed as quickly as possible, but I'm sure there are a lot of good references I'm unaware of. If you have a short list of DirectX-meets-Java references, please pass them on. I'd love to hear more specific ideas for things you'd like to see in the column, Microsoft media APIs or otherwise. Two specific things I'm thinking about discussing related to DirectX/MS-Java technology are WFC graphical services as compared with the Sun Java AWT, and Java 2D and Direct3D as compared with Java 3D. Any other comparison-type articles you'd like to see? Bill Day

SGI MediaBase update

Bill,

I've been in love with SGI technology since a second mortgage bought my Indy. I'm looking to begin implementing a video-based intranet for a client. I'm curious, does MediaBase have a set of its own Java APIs? Does MediaBase adhere to/implement the Java Media Framework? Do I have to pick from MediaBase/C++ (hope not!) or MediaBase/Java (preferable) or roll my own (Java/JMF)?

Bob Namestka An avid Bill Day reader

Bob, Thanks for that line about being an avid reader! I haven't worked with MediaBase since it was in an earlier version, so I checked with the development team. Following are the answers I received from a member of the team:

  1. Does MediaBase have any Java-based APIs? Yes. We did have a Java-based player API (working with a Java version of OCS). It corresponds to the MediaBase 1.0 version of movie library. Technically, it should still work with MediaBase 3.0 servers (since our servers support such backward compatibility). We haven't tried it in a long time.
  2. Does MediaBase use Java Media Framework? The JMF implementation also corresponds to version 1.0 of MediaBase.

I should also point out that Silicon Graphics announced in November of last year that all future work on JMF for IRIX was being discontinued. (This affects JMF players for MediaBase for other platforms, I believe.) You can read this announcement from the jmf-interest archive at

http://java.sun.com/products/java-media/mail-archive/Framework/0653.html

So, where does this leave you? You can probably use the Java-OCS support (though I don't have any specifics on this) to roll your own MediaBase player. If this doesn't work for you, you probably need to use MediaBase with the C++ players or roll your own Java-based players. I would strongly suggest that you contact the MediaBase team (via your SGI contact or via e-mail addresses on the SGI Web page) and let them know that you want to see Java-based players in MediaBase. If you think they should build these using JMF, let them know that, too. I believe they are very receptive to customer suggestions and requests, but they do need to be convinced, so be eloquent when you contact them. Bill Day

JMF compatibility issues

Bill,

I'm glad to see that someone intends to keep up with the growth of media extensions for Java.

I'm having a problem with the lack of compatibility between the two versions of JMF, Sun's and Intel's.

Do you intend to address this issue in the future? Do you know very much about the differences and how programmers may come to deal with them?

Phillip Dukes

Phillip, I will be discussing the various JMF implementations in an upcoming column or series of columns. These implementations will likely include Sun's (the reference) as well as Intel's, Real Network's (JMF RealPlayer in RealSystem G2), and possibly SGI's (though it is now somewhat obsolete). There are some mechanisms for dealing with these differences. The best one is probably to simply write robust Java code using try-catch blocks. If something fails, fall back and try another method by catching the exception, figuring out what went wrong, and then calling the appropriate follow-up method. For example, an attempt to create a player for an RTP-based protocol (which Sun's implementation supports, at least in part) will fail in Intel's implementation. But, if I wrap the attempted Manager.createPlayer() inside a try, the catch allows me to attempt to create a player for another, more standard protocol like HTTP (which both Sun and Intel support fairly well). I hope this gives you an idea of how I would approach this problem in general. If you have more specific questions, please let me know and I'll see that they make it into my column and/or my O'Reilly book on the JMF, Java Media Players due in October of this year. Bill Day

Introducing: JIMI

Bill,

Congratulations on your new column. I hope it will be a great channel for getting more people involved with all aspects of Java media.

My company, Activated Intelligence, has just completed its first product, JIMI, and it might be of interest to you. In essence, JIMI is an all-Java toolkit that makes it really easy for programmers to read and write a wide variety of graphics file formats.

Rick Ross

Rick, I've been lurking on the java2d-interest mailing list for a while and observing the discussion regarding JPEG codec support vs. more general frameworks. JIMI has come up several times, and I've briefly checked out Activated's JIMI page. I'm interested in knowing more. In fact, my current column details Java 2D. Several follow-up columns will also cover this topic. If you have a specific pointer that you feel will give me maximum bang for my buck with regard to learning about JIMI (say a really good whitepaper or doc with code in it, demonstrating the basics of JIMI's use), please send it my way. Bill Day

A note from the author

While the majority of reader response to last month's Media Programming debut was positive, we can't ignore the grumblers. At least two readers, who remain anonymous, felt that the introductory column was "content free." Another complained that the column was about "what we don't and can't have." Author Bill Day responds below. --Editor

This column is in some sense a strategic one, pointing the way towards up-and-coming technologies. At the same time, I don't want to turn this column into a forum for marketing hype and not-ready-for-primetime material. I have every intention of providing real-world tips with current, available Java-based technologies. I'll also be discussing Microsoft technologies, i.e., Java DirectX bindings, which should lend some credence to the availability and practicality of Java media programming. My current column kicks off a series on Java 2D. Java 2D is available now in the 1.2 betas and will be a part of the core platform when 1.2 releases later this year. Because a 1.2 runtime version (JRE) will be available for developers to ship with their Java-based products, I would argue that this is definitely something that at least Java application programmers can and will have at their disposal sooner rather than later. The introductory column was not overly technical by necessity. It was designed to set the stage, and as such I believe it succeeded. I hope you will come back and read the next and subsequent columns and then let me know what you think about the practicality of Java-based media programming and the level of technical content in the column. Bill Day

Disappointed beginner

Bill,

I thought this article was supposed to be for beginners or for folks who want to get into Java programming, but it wasn't. I'm disappointed.

John Hedge

John, The column is targeted to people who are familiar with Java programming, but not yet familiar with multimedia programming in Java. I will provide an introduction to the basics of each new topic before I move into more advanced information on it; however, I won't provide readers with information on the more fundamental parts of Java. If you're looking for general information on getting started with Java, I suggest that you check out the Getting Started trail in the Java Tutorial from Sun. The General Documentation should be useful to you as well. Bill Day

On the lookout for an all-Java MPEG encoder

Bill,

Great kick off. Good luck! Do you know of an MPEG encoder written in Java, perchance?

Nick Didkovsky

Nick, Of late, there has been some discussion about this on the jmf-interest mailing list. In fact, I see that you've posted to the list on this topic, so I hope my reply won't be old hat to you. The specifications for MPEG codecs are asymmetric. That is to say, it takes much more work to encode an MPEG bitstream than it does to decode it. This is by design, so that decoders can be simpler and hopefully much faster for end users. So, software MPEG decoders should be, and in fact are, more common than software encoders. As Andrew Wason mentioned in

his response

to your jmf-interest post, a number of Java-based MPEG decoders are available. Of the choices he presented, the

Inline MPEG - 1 - player in Java

looks to be the most mature. You might try contacting the people responsible for this decoder and asking them if they have an encoder in the works or are aware of one. You might also try the following:

  • Ask Digital Bitcasting's MPEG Advisor.
  • Check out Digigami's MegaPEG, an all-software (though not all Java) MPEG-1 and MPEG-2 encoder. (It takes AVI and QuickTime as input. You can download a demo version.)
  • Visit Ligos Technology, which also has a software-only MPEG-1/2 encoder. The company just released a new version for Windows NT/95 that takes advantage of Intel MMX capabilities, if your system has it. You can download a demo version here, too.
  • Search through mpeg.org and especially its Search MPEG Software and Products page.

I haven't used any of the encoders mentioned above, so I can't make any statement about the quality of their output. (I use an SGI utility, dmconvert, on my IRIX system to encode MPEG-1 from QuickTime or various other formats.) In theory, if you can find an all-software MPEG encoder that exposes an API for your use, you should at least be able to wrap the encoder to provide Java bindings to it. It's not an all-Java solution, but it could give you access to an encoder from within Java. I hope this information helps. I would be interested to know what you find out about this, so please do keep jmf-interest informed if you find an all-Java MPEG encoder. Bill Day

Design Techniques: "Object finalization and cleanup" by Bill Venners

Read

Down to the nitty gritty

Bill,

Great article! After reading it, I did some experiments to see for myself how finalizers behave on my platform. I got one result I just don't understand, and I can't find anything specific in the JLS to explain it.

In the first experiment, main calls a method that creates three objects that circularly reference each other. No references are kept. main then asks for garbage collection (lucky for me I actually get it), and all three objects are destroyed before main exits. Great! JVM isn't fooled by circular references.

In the second experiment, main creates the three objects in a nested block. The objects are not garbage collected until main exits, which surprised me. My thinking was that the variables would fall out of scope when the inner block ended, and would be eligible for immediate garbage collection. Instead, it appears that the JVM is keeping a reference around until main exits. Is that what's happening? Am I getting a peek at the nitty gritty of how the JVM handles local variables?

I have my doubts that the code will survive whatever formatting this Web form is going to do to it. I appreciate any time you can spare to help me understand what's happening here.

Wayne Conrad

Wayne, Yes, you probably are witnessing a nitty-gritty implementation detail. You are right that local variables go out of scope after the closing curly brace of the block in which they are declared. You are also right that this means the VM could garbage collect those things. But JVMs get to decide when to initiate garbage collection. As you seem to be aware, System.gc() doesn't force JVMs to garbage collect. What I imagine is going on inside the JVM you are using is that all the words of the current stack frame are counted as valid root nodes for the garbage collector. Even though one or two of the words contain references that have already gone out of scope, the VM doesn't bother to keep track of that or to take the time to null out those local variables when they go out of scope. The VM could reuse those slots in the local variables section of the Java stack frame after the variables go out of scope, but in this case it probably didn't. Once main() exits, the stack frame containing all those local variable references is popped, and they are no longer root nodes for garbage collection. Thus, the objects will be garbage collected only after the method returns. Bill Venners

Multiple-transaction vs. single-transaction log files

Bill,

A couple of comments on your article.

First, I think the writeToFile() method in LogFileManager and LogFileTransaction should check that logFileOpen is true.

Second, I failed to see the difference between LogFileManager and LogFileTransaction (other than the fact that LogFileManager has a default constructor). Could you elaborate on the difference?

Roque Oliveira

Roque, On your first comment, that LogFileManager.writeToFile() should check that logFileOpen is true, you may have a good point. Basically what I want this method to do if the log file is not open is throw an exception, to indicate that the class isn't being used correctly. Currently it throws IOException if the log file isn't open. A better approach may be to define a LogFileNotOpenException class, check logFileOpen first in writeToFile(), and if logFileOpen is false, throw LogFileNotOpenException. This more explicit exception could communicate better that the error was an improper use of the class, rather than some generic IOError. In my current column, which covers exceptions, I recommend making a separate exception class for each kind of exception you want to report, so it seems I haven't followed my own advice. As for your second question, the big difference between LogFileManager and LogFileTransaction is not the default constructor, but the fact that LogFileTransaction doesn't have an openLogFile() method and LogFileManager does. Because of this, LogFileTransaction can be used for one log file transaction and then must be thrown away. To do another transaction, you have to create a new LogFileTransaction object. With a single LogFileManager instance, however, you can do multiple transactions with the same (or different) log files. As I mentioned at the end of the article, most of the classes in the java.io package use the model demonstrated by LogFileTransaction. Bill Venners

Beating the splitName() method blues

Bill,

Thanks a lot for your inspiring and interesting articles. I'm very interested in good styles of coding, structure, and design and it's always good to see the principles explicated so nicely.

One thing has bothered me with Java, though, and I thought that you might have some thoughts on the subject. The thing is that Java is the only language I know of that will not allow parameters passed by reference.

I am currently working on a project where the "other guys" have decided on a design scheme that works, but it makes me feel uneasy, because I know it's wrong. It's not the way it's supposed to be -- a feeling, I believe you know!

An example header would look like this:

 
                                       public void getAccount(Transaction t,
                                       String accountName,
                                       StringBuffer created,
                                       StringBuffer modified) throws TransactionException
                                      

The two StringBuffers are used to return values. The trick is that you have to pass to this method two empty but non-null StringBuffers. The dates will be encoded into these in some well-defined way, and you can then decode the dates back again. Because you can only append to a StringBuffer, and not delete from it, it's crucial that the StringBuffers are empty on entry. (Well, it isn't really; you just have to know when you handle the result.) The method can't do something like

 
                                       created = new StringBuffer( /* the return value goes here */ );
                                      

because the caller's "create" StringBuffer would still point to the old StringBuffer. The return value will be lost, and at some point garbage collected.

Another common technique in this scenario is to pass empty but non-null collections, of some sort, that the method can fill in. In some places they've even constructed a method that returns two lists that are parallel, so that the first element of list A is one attribute of something, and the first element of list B is another attribute of the same thing.

This is clearly very ugly, and not the way things were intended to be in Java. I see two solutions, and neither is good:

  1. I could encapsulate all the return values we want for some specific method into a simple wrapper class, complete with constructor(s), get methods, and set methods, just like in JavaBeans. This would be conceptually nice, but in our case there would be loads of them. Just about 50 different classes. That's a lot of classes to write. Which brings me to my second, and preferred, solution:

  2. I could split up the 65 methods, so that each returns one thing. This could be done in one of two ways:

    • Each method could be set to perform the operations necessary and return just one value

    • We could introduce a "master" method to perform the operation, and one additional method for each value that needed to be returned

The first solution is almost impossible due to performance issues: the data are retrieved from a database using a single SQL query that returns just the values needed and the very same values that are returned by the method. The second solution is not conceptually sound, violating your "minimize coupling" mantra.

The problem here clearly stems from the design problems of coupling object-oriented Java with relational databases. And from the fact that the API we're designing is supposed to work with programs written in other languages, most notably C, that are not object-oriented. And from the fact that the parties involved cannot agree on a common object model.

It may be that I'm the only one experiencing the problem of not being able to return, for instance, two strings. The issue can be cut down to the following:

Consider a method that takes a string name as input parameter, parses it, and wants to return it in the following parts: title, first name, middle initial, and last name. The choice is between:

  • public (String, String, String, String) splitName(String name) -- which is not possible;

  • public SplitName splitName(String name) -- where we have to define a class SplitName that contains the four String fields.

  • public String getTitle(String name)
                                           public String getFirstName(String name)
                                           ...  
                                          

    -- where there is a performance penalty; and

  • public void splitName(String name)
                                           public String getTitle()
                                           ... 
                                          
    -- which is just as ugly as the StringBuffers.

Do you have any solutions to the problem that I might have missed? Or have you come across existing articles that deal with the issue? I would be delighted to hear from you, as it's painful to me to keep looking at this code.

Lars Pind

Lars, Yours is a good question because it's something every Java programmer will encounter from time to time. There is no right answer, but I can talk a bit about what I would do in this situation and why. First, Java's garbage collector is one big reason we can be more productive programming in Java than in non-garbage-collected languages such as C++. Because of the garbage collector, we don't have to worry about chasing down subtle memory bugs. But the garbage collector does one other thing that makes our lives more pleasant in Java as compared to C++: It makes it OK for a method to allocate memory for an object and pass it back to the caller. If I were to ask you for a pen, and you were in the mood to give me one, you would likely reach into your pocket and pull out a pen, then give it to me. I would then most likely use it, forget that it was yours, and leave it somewhere. This is a real-life scenario. In C++, if I encountered an analogous arrangement in someone's code, I would frown. In other words, if I encountered a function, say

borrowPen()

, that allocated memory for a new

Pen

object and returned it, I would find this to be less-than-admirable design. Why? Because I feel that in C++, classes that allocate memory need to take some responsibility for freeing that memory. If I were designing a class that needed to return a

Pen

object from a

borrowPen()

method, I would usually try to either:

  • Have the caller to the method pass in a pointer or reference to memory of a Pen object that my method was to fill with state. (Then the caller would be responsible for freeing the memory.)
  • Allocate memory for a new Pen object in borrowPen() and have a returnPen() method in the class as well. (Once again it would be partly the caller's responsibility to free the memory by invoking returnPen() sometime later, but at least there is some hint that a second method returnPen() has to be invoked. On the other hand, returnPen() could keep a bundle of pens internally, so it may not free them necessarily as they come in, which means my class is taking some responsibility for deciding when to free the memory.)

In Java, however, such design became a lot easier and more like real life. The

borrowPen()

method can just create a new

Pen

object (pull it out of its pocket), return a reference to the object, and forget about it. The caller can use the

Pen

object, and then it too can forget about it. The garbage collector will take care of freeing the memory occupied by the

Pen

object once no one needs it anymore. So, that is how I like to return stuff in Java. Therefore, I would solve your

splitName

issue as follows:

package com.artima.somelib;
                                       public class MyLib {
                                       public static class SplitNameReturnVal {
                                       private String title;
                                       private String first;
                                       private String middle;
                                       private String last;
                                       private SplitNameReturnVal(String title, String first, String
                                       middle,
                                       String last) {
                                       this.title = title;
                                       this.first = first;
                                       this.middle = middle;
                                       this.last = last;
                                       }
                                       public String getTitle() {
                                       return title;
                                       }
                                       public String getFirst() {
                                       return first;
                                       }
                                       public String getMiddle() {
                                       return middle;
                                       }
                                       public String getLast() {
                                       return last;
                                       }
                                       }
                                       public static SplitNameReturnVal splitName(String name) {
                                       // Do processing to actually split the name into its
                                       // component parts. For now, just return Jane Q. Public.
                                       return new SplitNameReturnVal("Ms.", "Jane", "Q.", "Public");
                                       }
                                       }
                                       -----------------------
                                       import com.artima.somelib.MyLib;
                                       class Test {
                                       public static void main(String[] args) {
                                       MyLib.SplitNameReturnVal sn =
                                       MyLib.splitName("Mr. F. Scott Fitzgerald");
                                       System.out.println(sn.getLast() + sn.getMiddle() + sn.getFirst()
                                       + sn.getTitle());
                                       }
                                       }
                                      

As you can see, I'm in the create-a wrapper-object-and-return-it camp, which you mentioned is your preferred solution. However, I made it an immutable static inner class with a private constructor. This means only

MyLib

and its inner classes can instantiate a new wrapper object, and once it's created it can't be changed. The sole purpose of the wrapper object's short life is to return data from

splitName()

to its caller. This design does require that you write a lot of classes (in your case 50 of them) just to return data, but at least they're inner classes, so they sit in the same source file as the method whose data they're returning. This design also means you're creating a lot of short-lived objects at runtime, which may cause you to worry about performance. For my thoughts on the performance subject, see my article "

The Hotspot virtual machine

at developer.com. As I said at the start, there is no right or wrong answer to this design problem. The above code just shows how I would approach the particular problem of

splitName()

. Bill Venners

Under the Hood: "Security and the class verifier" by Bill Venners

Read

Illegal cast

Bill,

I noticed your article about class loaders on the JavaWorld site. I've tried to implement my own class loader for an applet but I've run into a problem: I'm getting a ClassCastException during the instantiation of the object loaded from my classloader. For example:

 
                                       Class c = myloader.loadClass("com.foo.MyClass");
                                       // This gets the ClassCastException
                                       com.foo.MyClass myclass = (com.foo.MyClass)c.newInstance();
                                      

I've tried to load MyClass with the primordial classloader and I still get the ClassCastException problem. Any ideas or debug tips?

Michael Maresca

Michael, The exception you are getting just means that you are attempting an illegal cast. The type of reference you are trying to cast from is not castable to the reference you are trying to cast to. The above code doesn't tell me exactly what the problem is. One thing that comes to mind is that if your class loader is loading MyClass itself (instead of calling findSystemClass() and getting it from the system loader) the cast won't work. Even though your class loader loaded the same class file as the system loader, your class loader called defineClass() to import the type. This results in two copies of the class data for the MyClass type, one for each name space. In other words, the system class loader name space has one copy of the class data for MyClass. The name space for your class loader uses a different copy of the class data for MyClass. When you try to cast an instance of MyClass (defined in your class loader's name space) to a reference of type MyClass (defined in the system class loader's name space), you'll get a ClassCastException. Even though the two copies of the MyClass class data are identical, the VM doesn't bother to figure that out. They have to be the same class data. To solve this problem, call findSystemClass() in your class loader before trying to import the type in your custom way. Bill Venners

JavaBeans: The beans-serialization series by Mark Johnson

ReadReadRead

Class objects: completely serializable

Is it true that you cannot serialize class objects, only instances of existing (that is, already loaded) classes? I've been told so, but I couldn't find this restriction spelled out in the specification.

Olivier Lefevre

Olivier, According to the Java 1.1.4 documentation,

class

objects are serializable. And everything seems to work fine. Also, it doesn't matter if the class is already loaded or not: If it's not loaded,

java.Class.forName()

tries to load and link it. See the example below:

// Sample class "SerClass"
                                       import java.io.*;
                                       public class SerClass {
                                           private static void Usage()
                                           {
                                               System.out.println("Usage:\n\tSerClass w file classname\n\tSerClass
                                       r file");
                                               System.exit(1);
                                           }
                                           public static void main(String[] args)
                                           {
                                               String cmd = args[0];
                                               try {
                                                   if (cmd.compareTo("w") == 0) {
                                                       if (args.length != 3)  { Usage(); }
                                                       String classname = args[2];
                                                       Class   theClass = Class.forName(classname);
                                                       FileOutputStream f = new FileOutputStream(args[1]);
                                                       ObjectOutputStream s = new ObjectOutputStream(f);
                                                       System.out.println("Writing class " + classname);
                                                       s.writeObject(theClass);
                                                       s.flush();
                                                   }
                                                   else if (cmd.compareTo("r") == 0) {
                                                       if (args.length != 2) { Usage(); }
                                                       FileInputStream f = new FileInputStream(args[1]);
                                                       ObjectInputStream s = new ObjectInputStream(f);
                                                       Class theClass = (Class) s.readObject();
                                                       System.out.println("Read a class object for class " +
                                                                          theClass.getName());
                                                   }
                                                   else {
                                                       System.err.println("Unknown command " + cmd);
                                                       Usage();
                                                   }
                                               }
                                               catch (IOException ex) {
                                                   System.out.println("IO Exception:");
                                                   System.out.println(ex.getMessage());
                                                   ex.printStackTrace();
                                               }
                                               catch (ClassNotFoundException ex) {
                                                   System.out.println("ClassNotFound Exception:");
                                                   System.out.println(ex.getMessage());
                                                   ex.printStackTrace();
                                               }
                                           }
                                       };
                                       // END OF CLASS FILE
                                      

OUTPUT:

======= C:\>java SerClass w x java.lang.Integer

Writing class java.lang.Integer C:\>java SerClass r x

Read a class object for class java.lang.Integer Hope this helps. Mark Johnson

Letters to the editor

Sun: Quit the courtroom and cooperate

To the editor:

When Java first came out, I think I was the first one in the Middle East to buy a how-to Java book. I thought -- and still think -- that it is the greatest idea in the computer world. But, I am continually surprised by Sun's attempts to kill Windows through the court system instead of through innovation. I always thought that the best thing for Sun to do, if it really wanted to spread the Java gospel, was to cooperate with Microsoft. The latter is a company with a huge experience in developing superior applications that meet customer needs. Its secret, I think, is that it listen to its customers. Sun, is slow in listening to its customers. In addition, Sun has no experience in producing programs for the wide market Java is targeting, while Microsoft does.

I would like to say that Microsoft's existence and competitiveness have always been great factors enhancing competition in the industry. Other companies in the sector have always worked very hard to keep up with MS and overcome it. I think this applies to Sun. It seems to me that Mr. McNealy's enthusiasm is spurred by his desire to replace Windows. Thus, I think that Sun's Java wouldn't have progressed as fast as it has without Microsoft in the game.

Java is still far from perfect. I think Java would advance much faster if we quit the courtroom and cooperated. That is, if we're really interested the consumer's benefit, as Mr. McNealy likes to say.

Jabra Ghneim

Amman, Jordan

Reprints

To the editor:

What is JavaWorld's official position on readers printing JavaWorld articles and sharing them with colleagues?

Carlos Valcarcel

Carlos, For distribution to a few friends and colleagues, feel free. For wider distribution -- or for any distribution designed to help promote your commercial endeavors, products, or services -- please consult with our reprints group: Reprint Management Services

717-560-2001

Charles Calta

sales@rmsreprints.com

Jeff Martin, reprint operations specialist, 717-560-2001 ext. 31

http://www.rmsreprints.com Michael O'Connell

Editor-in-Chief

michael.oconnell@javaworld.com