Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Steer clear of Java pitfalls

Three tips to keep you from falling into a Java trap

  • Print
  • Feedback

Page 2 of 2

    // String tokenizer with current behavior
    public static String [] tokenize(String input, char [] delimiters)
    {
        return tokenize(input, new String(delimiters), false);
    }
    public static String [] tokenize(String input, String delimiters, 
               boolean delimiterAsGroup)
    {
        Vector v = new Vector();
        String toks[] = null;
        if (!delimiterAsGroup)
        {
            StringTokenizer t = new StringTokenizer(input, delimiters);
            while (t.hasMoreTokens())
                v.addElement(t.nextToken());
        }
        else
        {
            int start = 0;
            int end = input.length();
            while (start < end)
            {
                    int delimIdx = input.indexOf(delimiters,start);
                    if (delimIdx < 0)
                    {
                            String tok = input.substring(start);
                            v.addElement(tok);
                            start = end;
                    }
                    else
                    {
                            String tok = input.substring(start, delimIdx);
                            v.addElement(tok);
                            start = delimIdx + delimiters.length();
                    }
            }
        }
        int cnt = v.size();
        if (cnt > 0)
        {
            toks = new String[cnt];
            v.copyInto(toks);
        }
        
        return toks;
    }


Below is an applet demonstrating the new static method, tokenize(), that treats the token String ### as a single delimiter.

While some may consider the above pitfall relatively harmless, the next is extremely dangerous and should be seriously considered in any Java development project.

Pitfall 3: Don't mix floats and doubles when generating text or XML messages

While developing an order execution system for an online brokerage, I stumbled across a serious bug that incorrectly converted certain values from doubles to strings. Here is the scenario: The Website presents a stock-trade form to the user. A Java servlet processes the form and sends the trade information to the order execution server, a Java RMI server. The Java RMI server formats the message as either XML or another text format -- the common message switch (CMS) format, for example -- and passes it to one of several executing agents. One of the fields in the stock-trade message is the stock price, which is stored as a double. For certain double values, the Java platform incorrectly converts the price when formatting the order message, and the trade is rejected. Customers don't like that!

What if this was embedded software in a medical device, and the double value represented the amount of radiation administered to a patient? A low-level bug like this can be extremely dangerous.

Below is an applet that simulates the above scenario and generates two stock transaction messages. The first price formats correctly, while the second value -- 100.28 -- formats incorrectly.

I originally labeled this problem a bug, and I did find that Sun's JDC bug database reports it several times, in numbers 4030639, 4087318, 4068996, and 4169083. Unfortunately, these similar bugs are described inconsistently. Some are reported fixed, others are not even considered bugs, and one is labeled as a bug "in progress." Unfortunately for the many application developers who must generate XML messages that contain double values, this bug exists in JDK 1.1, 1.2, and 1.3. Thus I consider it a pitfall.

The following is a simple example of the bug. The problem lies in converting a float to a double, prior to converting to a String. This occurred in the brokerage software when one developer primarily used floats for decimals, but another implemented doubles. That caused the bug to surface; this crashed the executing agent's stock trading system, which received our incorrectly formatted trade message. Notice that the method genTradeMessage() uses a float to represent the price. The purpose of genTradeMessage() is to generate a simple text XML order message. In turn, genTradeMessage() calls formatStockPrice(), which takes a double as the price parameter. Here is the invocation of genTradeMessage() that fails:

String msg2 = genTradeMessage("SUNW", "BUY", 1000, 100.28f);

Notice that a float of 100.28 is passed into genTradeMessage(). Here is the code for genTradeMessage() and formatStockPrice():

    public String genTradeMessage(String symbol,
                                  String action,
                                  int shares,
                                  float price)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        pw.println("<TRADE>");
        pw.println("\t<ACTION>" + action + "</ACTION>");
        pw.println("\t<SYMBOL>" + symbol + "</SYMBOL>");        
        pw.println("\t<QUANTITY>" + shares + "</QUANTITY>");
        pw.println("\t" + formatStockPrice(price));        
        pw.println("</TRADE>");
        return sw.toString();
    }
    
    public String formatStockPrice(double d)
    {
        return "<PRICE>" + d + "</PRICE>";    
    }


There are two workarounds to this problem. One solution is to implement only doubles or only floats in your APIs. That would mean rewriting formatStockPrice() to use a float. Here is that code:

    public String formatStockPrice(float f)
    {
        return "<PRICE>" + f + "</PRICE>";    
    }


Below is the GoodTradeMessageApplet that features the revised formatStockPrice().

Another potential workaround is to calculate using doubles, but cast to float when converting to a String. Of course, this solution is only viable if you are not losing precision on the cast.

Here is that version of formatStockPrice():

    public String formatStockPrice(double d)
    {
        float f = (float) d;
        return "<PRICE>" + f + "</PRICE>";    
    }


This pitfall can also trip you up when you use JDBC. In the aforementioned order execution system, trades were stored in an SQL server database. When retrieving the day's trades to format a report, the getString() method would incorrectly format the price value -- as an 8-byte SQL float type -- from the database. Here is the JDBC call that would return the erroneous value:

String sprice = rslt.getString("price");


A run of the program produced the following results:

  • Getting a connection to jdbc:odbc:CustomerTrades...
  • Symbol: SUNW
  • Action: BUY
  • Price as String: 91.090000000000003


The solution is to use the getFloat() method in the ResultSet class to retrieve the value as a float, which then properly converts to a String. Here is the replaced code:

float price = rslt.getFloat("price");


A run of TestGetPrice2.java produces:

  • Getting a connection to jdbc:odbc:CustomerTrades...
  • Symbol: SUNW
  • Action: BUY
  • Price as Float: 91.09


Editor's note: Michael makes a correction to this pitfall in "When Runtime.exec() won't."

Conclusion

Each pitfall discussed in this article was found in valid Java code that compiled well. However, when executed, the code produced erroneous results. There are many pitfalls in every Java package; however, many Java beginners are unaware of them. I hope senior Java developers will use the Web, newsgroups, and publications like JavaWorld to educate junior and midlevel developers about these frustrating, time-wasting problems.

About the author

Michael C. Daconta is the director of Web and technology services for McDonald Bradley, Inc (MBI), where he conducts training seminars and develops advanced systems with Java, JavaScript, and XML. In the last 15 years, Daconta has held every major development position, including chief scientist, technical director, chief developer, team leader, systems analyst, and programmer. He is a Sun-certified Java programmer, the author of C++ Pointers and Dynamic Memory Management, and the coauthor of three books: Java Pitfalls, Java 2 and JavaScript for C and C++ Programmers, and the forthcoming XML Development with Java 2, to be published by Sams Publishing in September.

Read more about Core Java in JavaWorld's Core Java section.

  • Print
  • Feedback

Resources