Saturday, October 29, 2011

Java Bugs & Solution


Welcome to the Java Mistakes Page!

    As a Java programming instructor, you see a lot of strange errors. Eventually, you start to recognize certain patterns or favorite ways that novice programmers get mixed up. The mistakes documented on this page are all ones that I've seen a dozen times or more. They're all easy to fix, if you can recognize them! This page is designed to be a resource both for new Java programmers and for new Java instructors. Click on the 'Java Page' banner for my main Java page, where you can find a few applets and links to lots of Java resources. JavaSoft has a nice 'New to Java' section in their training web site, but I think you have to join JDC to use it (free). Check it out: New2Java at java.sun.com . If you have a common kind of mistake that you would like me to analyze and add to this page, please send mail to ziring@home.com . Jump right to a particular mistake:
    1. Naming the Class Differently from its File Name
        All Java systems I've used, including all the Javasoft JDKs, expect the source code of a public class to be stored in a file with the same name as the class, plus the extension .java. Neglecting to follow this convention can lead to a variety of problems, all of which crop of during compiliation.Beginning students often forget this guideline, and name their file something about their assignment, like Lab6.java.
          Mistake Example
          File Lab6.java -
          public class Airplane extends Vehicle
                Seat pilot;
                public Airplane() { pilot = new Seat(); }
          }
          
          Corrected Example
          File Airplane.java -
          public class Airplane extends Vehicle
                Seat pilot;
                public Airplane() { pilot = new Seat(); }
          }
        Note that the name of a class is supposed to start with a uppercase letter. On operating systems that enforce case-sensitive file names, this can cause an additional problem, especially for students learning Java on Unix when they are accustomed to using DOS or DOS-derivative file names. The class MotorVehicle must be stored in the file MotorVehicle.java, notmotorvehicle.java.
    2. Comparing Strings with ==
        Java strings are objects of the class java.lang.String. The Java == operator, when applied to objects, merely tests the references for equality! Sometimes, students misunderstand the semantics of the == and try to use it for comparing the contents of strings.
          Mistake Example
          // find out if the first argument is "-a"
          if (args[0] == "-a") {
              optionsAll = true;
          }
        The proper way to compare two strings for equality is to the use java.lang.String equals() method. It returns true if the the strings are the same length and contain the same characters.
          Corrected Example
          // find out if the first argument is "-a"
          if ("-a".equals(args[0])) {
              optionsAll = true;
          }
        This mistake is particularly nasty because it is legal Java code; it just doesn't do what the student expects. Some students also try to use comparison operators like > and <=, instead of the correct java.lang.String method compareTo(). This mistake is much simpler to catch, because it causes a compile-time error.
    3. Forgetting to Initialize Object Arrays
        A Java array declared for an object type is really an array of object references. Creating the array, with new, simply creates empty references. The elements of the array are all set to null. To actually fill the array, you have to set each element to a real object reference. Many students misunderstand this; they think that creating an object array creates all the actual objects. (In many cases, this is a concept that the student carries over from C++, where creating an array of objects does create all the objects by calling their default constructor.)In the example below, the student wants to create three StringBuffer objects to hold some data. This code will compile, but will throw a NullPointerException on the last line, where one of the (non-existent) objects is used.
          Mistake Example
          // Create an array of data buffers
          StringBuffer [] myTempBuffers;
          myTempBuffers = new StringBuffer[3];
          myTempBuffers[0].add(data);
          
        To correct this problem, it is important to remember to initialize the elements of any Java array.
          Mistake Example
          // Create an array of data buffers and initialize it.
          StringBuffer [] myTempBuffers;
          myTempBuffers = new StringBuffer[3];
          for (int ix = 0; ix < myTempBuffers.length; ix++) 
               myTempBuffers[ix] = new StringBuffer();
          myTempBuffers[0].add(data);
          
    4. Putting Several Public Classes in One File
        Java source code files have a special relationship to the classes in those files. The rules can be summarized as:
        1. Any Java class is defined in exactly one source file
        2. At most one public class may be defined by any one source file
        3. If a public class appears in a source file, the name of the file and the name of the class must be the same.
        Sometimes, a student will forget the second rule in their rush to code up a lab exercise or project. They'll put two or more public classes into a source file. The error message for breaking rules 2 and 3 are the same.
    5. Shadowing an Attribute with a Local Variable
        Java permits the programmer to declare local variables inside methods that have the same name as class attributes. The local variable takes precedence, and is used instead of the attribute everywhere it appears; the local variable is said to 'shadow' the attribute.Occasionally, this mistake will cause a compiler error, usually when the shadowed attribute and the local variable are different types. More commonly, this mistake will cause no errors or exceptions, and the student will be left mystified about their unexpected results.
          Mistake Example
          public class Point3 {
              int i = 0;    int j = 0;    int k = 0;
              public boolean hits(Point [] p2list) 
              {
                  for(int i = 0; i < p2list.length; i++) {
               Point p2 = p2list[i];
               if (p2.x == i && p2.y == j)
                   return true;
           }
                  return false;
              }
          }
          
        There are two ways to fix this problem. The simple way is to refer to the attribute with the construct this.attribute. The better way is to rename the attribute or the local variable so that shadowing does not occur.
          Corrected Examples
          // One way to fix the problem
              int i = 0;    int j = 0;    int k = 0;
              public boolean hits(Point [] p2list) 
              {
                  for(int i = 0; i < p2list.length; i++) {
               Point p2 = p2list[i];
               if (p2.x == this.i && p2.y == this.j)
                   return true;
           }
                  return false;
              }
          
          // A better way to fix the problem
              int x = 0;    int y = 0;    int z = 0;
              public boolean hits(Point [] p2list) 
              {
                  for(int i = 0; i < p2list.length; i++) {
               Point p2 = p2list[i];
               if (p2.x == x && p2.y == y)
                   return true;
           }
                  return false;
              }
          
        Another very common manifestation of this mistake is giving a method parameter the same name as an attribute. Some programmers do this as a matter of course for constructors, but it is not really appropriate for regular methods.
    6. Forgetting to call a Superclass Constructors
        When a Java class extends another class, every constructor of the subclass must call some constructor of the superclass (somehow). Usually, this is accomplished by putting a call to the superclass constructor into the subclass constructor as the first line, something like super(x). If the first line of the subclass constructor is not a call to a superclass constructor, the compiler inserts such a call, but with no parameters: super().Sometimes, a student will forget this requirement. Usually, this is not a problem: the superclass constructor call inserted by the compiler works just fine. However, if the superclass does not have an accessible default no-parameter constructor, then the compiler will complain. In the example below, all constructors of the superclass java.io.File take 1 or 2 parameters.
          Mistake Example
          public class JavaClassFile extends File {
              String classname;
              public JavaClassFile(String cl) {
                  classname = cl;
              }
          }
          
        The solution is usually to insert an explicit call to the correct superclass constructor.
          Corrected Example
          public class JavaClassFile extends File {
              String classname;
              public JavaClassFile(String cl) {
                  super(cl + ".class");
                  classname = cl;
              }
          }
          
        A more subtle situation can come up when the superclass has a constructor that takes no parameters, but doesn't fully initialize the object. Then, the code will compile, but the program will give strange results or throw an exception.
    7. Catching Exceptions Incorrectly
        Java's exception handling system is sophisticated and rich, but difficult for novice programmers to conceptualize right away. Students with a strong C++ or Ada background will usually have no trouble, but C and Fortran programmers often will. The examples below show several different exception coding mistakes.Here, the exception is missing a name. This mistake is caught by the compiler, and the student will usually fix the problem by themselves.
          Mistake Example
          try {
              stream1 = new FileInputStream("data.txt");
          }
          catch (IOException) { 
              message("Could not open data.txt");
          }
          
          Corrected Example
          try {
              stream1 = new FileInputStream("data.txt");
          }
          catch (IOException ie) { 
              message("Could not open data.txt: " + ie);
          }
          
        The order of the catch clauses in a try-catch block define a precedence for catching exceptions, but each such clause will catch all exceptions of the declared class or any of its subclasses. Forgetting this can lead to an unreachable catch clause, which the Java compiler treats as a serious error. In the example below, SocketException is a subclass of
          Mistake Example
          try {
              serviceSocket.setSoTimeout(1000);
              newsock = serviceSocket.accept();
          }
          catch (IOException ie) { 
              message("Error accepting connection.");
          }
          catch (SocketException se) {
              message("Error setting time-out.");
          }
          
          Corrected Example
          try {
              serviceSocket.setSoTimeout(1000);
              newsock = serviceSocket.accept();
          }
          catch (SocketException se) {
              message("Error setting time-out.");
          }
          catch (IOException ie) { 
              message("Error accepting connection.");
          }
          
        If a piece of code throws an exception, and the exception is not caught by any try-catch block, then the method in which the code appears must declare the exception. (Note that exceptions that are subclasses of RuntimeException are exempt from this rule.). Students sometimes forget that a given method call will throw an exception. The usual fix is to build in a try-catch block.
          Mistake Example
          public void waitFor(int sec) {
              Thread.sleep(sec * 1000);
          }
          
          Corrected Example
          public void waitFor(int sec) throws InterruptedException {
              Thread.sleep(sec * 1000);
          }
          
    8. Returning void from an Accessor method
        This is a very simple mistake, the student creates a method to get a value (an accessor) but gives it the return type void. The fix is to give the accessor method the type of the data it is expected to return.
          Mistake Example
          public class Line {
              private Point start, end;
              public void getStart() { return start; }
          }
          
          Corrected Example
          public class Line {
              private Point start, end;
              public Point getStart() { return start; }
          }
          
        This is one common manifestation of a broader class of mistake: giving methods incorrect return types. Usually, the Java compiler can catch these kinds of errors and report them; most students can interpret the error messages and fix the problems on their own.
    9. Calling Instance Methods from main()
        The initial entry point for a Java program must be a public static method named main. Because main is a static method, it cannot call instance methods directly. Students sometimes get confused about main's status, and try to call regular methods without creating objects. This kind of mistake is especially prevalent early in a Java programming class, when the students are writing very small programs.
          Mistake Example
          public class DivTest {
              boolean divisible(int x, int y) {
                  return (x % y == 0);
              }
              public static void main(String [] args) {
                  int v1 = 14;
           int v2 = 9;
           // next line causes a compiler error message
                  if (divisible(v1,v2)) {
               System.out.println(v1 + " is a multiple of " + v2);
           } else {
               System.out.println(v2 + " does not divide " + v1);
                  }
              }
          }
          
        The fix for this problem is almost always one of two choices: make the method in question static, or create an object and call the method on it. To help them decide between these two approaches, ask the student whether the method they want to call uses any attributes or other methods of the object. If it does, then they should create and initialize an object, then call the method. If not, then they should make the method static.
          Corrected Example 1
          public class DivTest {
              int modulus;
              public DivTest(int m) { modulus = m; }
              boolean divisible(int x) {
                  return (x % modulus == 0);
              }
              public static void main(String [] args) {
                  int v1 = 14;
           int v2 = 9;
           DivTest tester = new DivTest(v2);
           // next line causes a compiler error message
                  if (tester.divisible(v1) {
               System.out.println(v1 + " is a multiple of " + v2);
           } else {
               System.out.println(v2 + " does not divide " + v1);
                  }
              }
          }
          
          Corrected Example 2
          public class DivTest {
              static boolean divisible(int x, int y) {
                  return (x % y == 0);
              }
              public static void main(String [] args) {
                  int v1 = 14;
           int v2 = 9;
           // next line causes a compiler error message
                  if (divisible(v1,v2)) {
               System.out.println(v1 + " is a multiple of " + v2);
           } else {
               System.out.println(v2 + " does not divide " + v1);
                  }
              }
          }
          
    10. Treating Strings as In/Out Parameters
        Java's string class, java.lang.String, provides a good encapsulation of string data. However, Java strings are (1) immutable, and (2) objects. Therefore, they cannot be treated as simple character buffers; they must be treated as immutable opaque objects. Sometimes, a student will attempt to treat a String parameter to a method as if it were a character array passed by reference (as in a C char array, or a C++ STL string object). This is a fairly subtle mistake, but one which usually passes the compiler.
          Mistake Example
          public static void main(String args[]) {
             String test1 = "Today is ";
             appendTodaysDate(test1);
             System.out.println(test1);
          }
          public void appendTodaysDate(String line) {
              line = line + (new Date()).toString();
          }
          
        In the example above, the student is expecting to change the value of main's local variable test1 by assigning a value to the parameter line in the appendTodaysDate method. Of course, this won't work; the local value of line will change, but the string test1 will be unaffected. This mistake can come up if the student has not yet accepted the fact that (1) Java objects are always passed by handle, and (2) Java strings are immutable. As the instructor, you have to explain Java parameter passing, and emphasize that String objects never change their value, but instead all String operations merely create new String objects. For the particular problem shown above, the fix is either to return a string value from the method, or to pass a StringBuffer object instead of a String object.
          Corrected Example 1
          public static void main(String args[]) {
             String test1 = "Today is ";
             test1 = appendTodaysDate(test1);
             System.out.println(test1);
          }
          public String appendTodaysDate(String line) {
              return (line + (new Date()).toString());
          }
          
          Corrected Example 2
          public static void main(String args[]) {
             StringBuffer test1 = new StringBuffer("Today is ");
             appendTodaysDate(test1);
             System.out.println(test1.toString());
          }
          public void appendTodaysDate(StringBuffer line) {
              line.append((new Date()).toString());
          }
          
    11. Declaring a Constructor as a Method
        Java object constructors look a lot like Java object methods. The only only obvious syntactic difference is the constructors' lack of a return type, and the fact that the constructors have the same name as the class. Unfortunately, Java permits normal methods to have the same name as their class as well.In the example below, the student expects the attribute list to be initialized to a fresh empty Vector object. This does not happen, because the method named 'IntList' is not a constructor.
          Mistake Example
          public class IntList {
              Vector list;
              // This may look like a constructor, but it is a method
              public void IntList() {
                  list = new Vector();
              }
              public append(int n) {
                  list.addElement(new Integer(n));
              }
          }
          
        The code will throw a null pointer exception the first time that append is called. This mistake is usually a tough one for a beginning programmer to figure out on their own, but the fix is actually quite simple: remove the return type.
          Corrected Example
          public class IntList {
              Vector list;
              // This is a real constructor
              public IntList() {
                  list = new Vector();
              }
              public append(int n) {
                  list.addElement(new Integer(n));
              }
          }
          
    12. Forgetting to Cast Object Data Types
        Like almost all object-oriented languages, Java permits an object of a subclass type to be treated as an object of any superclass type. This is called upcasting, and in Java upcasting is automatic; it need to be explicitly specified with a cast. However, if a variable, parameter, or return value is declared with a particular superclass type, attributes and methods of subclasses will not be visible. Treating a superclass object as its real subclass is called 'downcasting', and in Java all downcasting must be explicit.Students often forget about the explicit downcast requirement. This is most common when they use Object arrays or the java.util collection classes, like Vector or Hashtable. which will store any kind of object and return type Object. In the example below, a String object is put into an array, and then retreived from the array to do a string comparison operation. The compiler will catch this and refuse to compile the code until a cast is applied.
          Mistake Example
          Object arr[] = new Object[10];
          arr[0] = "m"; 
          arr[1] = new Character('m');
          String arg = args[0];
          if (arr[0].compareTo(arg) < 0) {
              System.out.println(arg + " comes before " + arr[0]);
          }
          
        The whole concept of type casting becomes very difficult for some students. Often, it is Java's dynamic methods that cause the confusion. In the example above, has the method equalsbeen used instead of compareTo, the compiler would not have complained, and the code would have worked because the actual string equality method would have been called. It is important to explain to the student that run-time method lookup is distinct from downcasting.
          Corrected Example
          Object arr[] = new Object[10];
          arr[0] = "m"; 
          arr[1] = new Character('m');
          String arg = args[0];
          if ( ((String)arr[0]).compareTo(arg) < 0) {
              System.out.println(arg + " comes before " + arr[0]);
          }
          
    13. Extending Interfaces
        For many students, the distinction between classes and interfaces is not immediately clear. Therefore, some students will try to implement an interface like Observer or Runnable using the keyword extends instead of the correct keyword implements. The fix is to replace the keyword.
          Mistake Example
          public class SharkSim extends Runnable {
              float length;
              ...
          }
          
          Corrected Example
          public class SharkSim implements Runnable {
              float length;
              ...
          }
          
        There is a related mistake that occasionally confuses students: mis-ordering extends and implements clauses. The Java language specification states that the superclass declaration (extends) must come before any interface declarations (implements). Also, for interfaces, the keyword implements should appear only once; multiple interfaces must be separated by commas.
          More Mistake Examples
          // Out of order
          public class SharkSim implements Swimmer extends Animal {
              float length;
              ...
          }
          
          // multiple implements keywords
          public class DiverSim implements Swimmer implements Runnable {
              int airLeft;
              ...
          }
          
          Corrected Examples
          // Correct order
          public class SharkSim extends Animal implements Swimmer {
              float length;
              ...
          }
          
          // Multiple interfaces separated by commas
          public class DiverSim implements Swimmer, Runnable {
              int airLeft;
              ...
          }
          
    14. Calling Superclass Methods and Forgetting to Use the Return Value
        Java allows a subclass method to call the same method in the superclass by using the keyword super. Sometimes, a student will know that they have to call a superclass method, but won't use the return value for anything. This is very confusing for some students, usually because they haven't become comfortable with methods and return values yet.In the example below, the student wants to include the superclass toString() result in the subclass toString() result. The mistake is forgetting to capture and use the return value from super().
          Mistake Example
          public class GraphicalRectangle extends Rectangle {
                Color fillColor;
                boolean beveled;
                ...
                public String toString() {
                    super();
                    return("color=" + fillColor + ", beveled=" + beveled);
                }
          }
          
        The fix for this problem is usually to assign the result of super() to a local variable, and then use that local variable as part of the computation of the subclass method return value.
          Corrected Example
          public class GraphicalRectangle extends Rectangle {
                Color fillColor;
                boolean beveled;
                ...
                public String toString() {
                    String rectStr = super();
                    return(rectStr + " - " +
                    "color=" + fillColor + ", beveled=" + beveled);
                }
          }
          
    15. Forgetting to Add AWT Components
        The Java Abstract Window Toolkit (AWT) has a simple model for building graphical user interfaces: each component of the interface must first be created by calling its constructor, then second incorporated into the GUI by calling the add() method of its parent component. In this way, an AWT interface is built up hierarchically.Students will sometimes forget this two-step process. They will create a component correctly, then forget to incorporate it into the interface. This omission will not trigger any compile-time error messages or any run-time exceptions; the components will simply not be visible.
          Mistake Example
          public class TestFrame 
              extends Frame implements ActionListener
          {
              public Button exit;
              public TestFrame() {
                  super("Test Frame");
                  exit = new Button("Quit");
              }
          }
          
        The fix, of course, is to add the component or components in question. The example below shows how to do this. Note that a student that forgets to add a component will often also forget to set up appropriate event handling for that component. I always check for proper event handling when a student shows me some AWT code, because it is easy to forget event handling when crafting a GUI.
          Corrected Example
          public class TestFrame 
              extends Frame implements ActionListener
          {
              public Button exit;
              public TestFrame() {
                  super("Test Frame");
                  exit = new Button("Quit");
           Panel controlPanel = new Panel();
           controlPanel.add(exit);
           add("Center", controlPanel);
           exit.addActionListener(this);
              }
              public void actionPerformed(ActionEvent e) {
                  System.exit(0);
              }
          }
          
    16. Neglecting Import Statements
        Java supports a hierarchical package structure for classes. If a class refers to another class from a different package, it can be made accessible with an import statement. Most students readily understand the notion of separate packages and import statements.Classes from the package java.lang are imported automatically into every Java program. Some students may forget that classes from other Java standard packages, such as java.net, are not imported automatically, but must be explicitly imported. In the example below, the Canvas superclass is not defined. This mistake will be caught by the compiler, but the error message will simply point out that the class is undefined, and will not say anything about the necessity for an import statement. Similarly, the class Vector is not defined either.
          Mistake Example
          public class MyCanvas extends Canvas {
              int x;
              Vector stuff;
          }
          
        To correct the mistake, the class Canvas must be imported from the java.awt package, or it must be referred to by its full name.
          Corrected Example 1
          import java.awt.*;
          import java.util.*;
          
          public class MyCanvas extends Canvas {
              int x;
              Vector stuff;
          }
          
          Corrected Example 2
          public class MyCanvas extends java.awt.Canvas {
              int x;
              java.util.Vector stuff;
          }
          
    17. Forgetting to Start Threads
        Java supports multi-threading with the java.lang.Thread class. The lifecycle of a thread consists of four states: initialized, running, blocked, and dead. A newly created thread is always in the initialized state; to move it to the running state, the programmer must explicitly call the thread's start() method. Sometimes a student will create a thread but forget to start it. Usually, this mistake stems from lack of experience with parallel or multi-threaded programming. The only fix is to be sure to start the thread. For simple classroom exercises, it is usually okay to simply start the thread immediately after creating it.In the example below, the student wants to do some animation using a Runnable, but has forgotten to start their thread.
          Mistake Example
          public class AnimCanvas extends Canvas implements Runnable
          {
              protected Thread myThread;
              public AnimCanvas() {
                   myThread = new Thread(this);
              }
              // the run() method never gets called because the
              // thread never gets started.
              public void run() {
                 for(int n = 0; n < 10000; n++) {
                     try { Thread.sleep(100); }
              catch (InterruptedException e) { }
              animateStep(n);
                 }
              }
              ...
          }
          
          Corrected Example
          public class AnimCanvas extends Canvas implements Runnable
          {
              static final int LIMIT = 10000;
              protected Thread myThread;
              public AnimCanvas() {
                   myThread = new Thread(this);
            myThread.start();
              }
              public void run() {
                 for(int n = 0; n < LIMIT; n++) {
                     try { Thread.sleep(100); }
              catch (InterruptedException e) { }
              animateStep(n);
                 }
              }
              ...
          }
          
        The thread lifecycle and the relationship of threads to Runnables is an important practical topic for Java instruction. It is a good idea to show students several examples of simple tasks implemented as both Thread subclasses and as Runnables.
    18. Using Deprecated java.io.DataInputStream readLine()
        In Java 1.0, the correct way to read a line of text was to use the readLine() method of java.io.DataInputStream. In Java 1.1, a whole set of new IO classes were added, designed to support character and text IO: Readers and Writers. Therefore, in Java 1.1 and later, the correct way to read a line of text is to use the readLine() method of java.io.BufferedReader. Students may not know about this change, especially if they have old Java books or if the class uses out-dated examples.The old readLine() method is still part of the JDK, but the compiler flags it as deprecated. This is often very confusing to students. It is important to explain that using the old java.io.DataInputStream readLine() method is not wrong, it is just old-fashioned. Encourage the student to use BufferedReader instead.
          Mistake Example
          public class LineReader {
              private DataInputStream dis;
              public LineReader(InputStream is) {
                  dis = new DataInputStream(is);
              }
              public String getLine() { 
                  String ret = null;
                  try {
               ret = dis.readLine();  // Warning!  Deprecated!
           } catch (IOException ie) { }
                  return ret;
              }
          }
          
          Corrected Example
          public class LineReader {
              private BufferedReader br;
              public LineReader(InputStream is) {
                  br = new BufferedReader(new InputStreamReader(is));
              }
              public String getLine() { 
                  String ret = null;
                  try {
               ret = br.readLine(); 
           } catch (IOException ie) { }
                  return ret;
              }
          }
          
        There are several other features from Java 1.0 that are officially deprecated in later Java releases, but this is the one that hits new students the most often.
    19. Assigning and passing double literals as floats
        Like almost all computer languages, Java supports floating-point (aka decimals, aka rationals) numbers. Java has two primitive types for floating-point numbers: double for 64-bit IEEE double-precision, and float for 32-bit IEEE single-precision. The confusion about this issue for novice programmers relates to decimal literals. Decimal literals, like 1.75 or 12.9e17 or -0.00003, are treated by the compiler as type double.Now, Java does not permit implicit data type casting when the operation might lose information. Such casting must be explicit. For example, Java will not allow assignment of an int literal to a byte variable without a cast, as shown in the example below.
            byte byteValue1 = 17;       /* WRONG! */
            byte byteValue2 = (byte)19; /* OKAY */ 
            
        Because a numeric literal with a decimal point in it represents a double, and because assigning a double to a float could lose precision, the Java compiler complain about any attempt to use such a numeric literal as a float. Therefore, simple statements like this one can prevent a class from compiling. Here is an example like the one above.
            float realValue1 = -1.7;          /* WRONG! */
            float realValue2 = (float)(-1.9); /* OKAY */ 
            
        Programmers accustomed to the forgiving data type strictures of C and C++ will often be stymied by this problem. It is understandable, and the burden sits squarely on the Java programming instructor to give his or her students clear instruction about data types and how to handle them. There are three ways to fix this particular problem.
        1. Use a variable of type double instead of type float. This is usually the simplest solution. In fact, there is little motivation to use floats in a Java program; any minor performance gain that a program might reap from using 32-bit math over 64-bit math will be vastly overshadowed by the JVM's overhead (Note: many modern CPUs convert all floating point numbers, of any size, into IEEE 80-bit register format before operating on them). The only real reason to use floats rather than doubles is when you are storing a lot of them, and need to save space.
        2. Use a literal marker to inform the Java compiler that you want your number to be treated as type float rather than type double. The marker for floats is 'f'. So, the value 1.75 is a double in Java, but the value 1.75f is a float. Here is a simple example:
            float realValue1 = 1.7;    /* WRONG! */
            float realValue2 = 1.9f;   /* OKAY */ 
            
        3. Use an explicit cast. This is the least elegant method, but useful when you need to convert a variable of type double into a variable of type float. Here is an example:
            float realValue1 = 1.7f; 
            double realValue2 = 1.9;
            realValue1 = (float)realValue2;

Compiler Problems

'javac' is not recognized as an internal or external command, operable program or batch file
If you receive this error, Windows cannot find the compiler (javac).
Here's one way to tell Windows where to find javac. Suppose you installed the JDK in C:\jdk1.7.0. At the prompt you would type the following command and press Enter:


C:\jdk1.7.0\bin\javac HelloWorldApp.java
If you choose this option, you'll have to precede your javac and java commands with C:\jdk1.7.0\bin\ each time you compile or run a program. To avoid this extra typing

Updating the PATH Environment Variable (Optional)

You can run the JDK without setting the PATH environment variable, or you can optionally set it so that you can conveniently run the JDK executable files (javac.exejava.exejavadoc.exe, and so forth) from any directory without having to type the full path of the command. If you do not set the PATH variable, you need to specify the full path to the executable file every time you run it, such as:
C:\> "C:\Program Files\Java\jdk1.7.0\bin\javac" MyClass.java
It is useful to set the PATH variable permanently so it will persist after rebooting.
To set the PATH variable permanently, add the full path of the jdk1.7.0\bin directory to the PATH variable. Typically, this full path looks something like C:\Program Files\Java\jdk1.7.0\bin. Set the PATH variable as follows on Microsoft Windows:
  1. Click Start, then Control Panel, then System.
  2. Click Advanced, then Environment Variables.
  3. Add the location of the bin folder of the JDK installation for the PATH variable in System Variables. The following is a typical value for the PATH variable:
    C:\WINDOWS\system32;C:\WINDOWS;C:\Program Files\Java\jdk1.7.0\bin
Note:
  • The PATH environment variable is a series of directories separated by semicolons (;) and is not case-sensitive. Microsoft Windows looks for programs in the PATH directories in order, from left to right.
  • You should only have one bin directory for a JDK in the path at a time. Those following the first instance are ignored.
  • If you are not sure where to add the path, add it to the right of the value of the PATH variable.
  • The new path takes effect in each new command window you open after setting the PATH variable.
2.Class names, 'HelloWorldApp', are only accepted if annotation processing is explicitly requested If you receive this error, you forgot to include the .java suffix when compiling the program. Remember, the command is javac HelloWorldApp.java not javac HelloWorldApp.Syntax Errors (All Platforms) If you mistype part of a program, the compiler may issue a syntax error. The message usually displays the type of the error, the line number where the error was detected, the code on that line, and the position of the error within the code. Here's an error caused by omitting a semicolon (;) at the end of a statement:
testing.java:14: `;' expected.
System.out.println("Input has " + count + " chars.")
                                                     ^
1 error
Sometimes the compiler can't guess your intent and prints a confusing error message or multiple error messages if the error cascades over several lines. For example, the following code snippet omits a semicolon (;) from the bold line:
while (System.in.read() != -1)
    count++
System.out.println("Input has " + count + " chars."); 
When processing this code, the compiler issues two error messages:
testing.java:13: Invalid type expression.
        count++
                 ^
testing.java:14: Invalid declaration.
    System.out.println("Input has " + count + " chars.");
                      ^
2 errors
The compiler issues two error messages because after it processes count++, the compiler's state indicates that it's in the middle of an expression. Without the semicolon, the compiler has no way of knowing that the statement is complete. If you see any compiler errors, then your program did not successfully compile, and the compiler did not create a .class file. Carefully verify the program, fix any errors that you detect, and try again. Semantic Errors In addition to verifying that your program is syntactically correct, the compiler checks for other basic correctness. For example, the compiler warns you each time you use a variable that has not been initialized:
testing.java:13: Variable count may not have been initialized.
        count++
        ^
testing.java:14: Variable count may not have been initialized.
    System.out.println("Input has " + count + " chars.");
                                       ^
2 errors
Again, your program did not successfully compile, and the compiler did not create a .class file. Fix the error and try again.

Runtime Problems

Error Messages on Microsoft Windows Systems
Exception in thread "main" java.lang.NoClassDefFoundError: HelloWorldApp
If you receive this error, java cannot find your bytecode file, HelloWorldApp.class. One of the places java tries to find your .class file is your current directory. So if your .class file is in C:\java, you should change your current directory to that. To change your directory, type the following command at the prompt and press Enter:
cd c:\java
The prompt should change to C:\java>. If you enter dir at the prompt, you should see your .java and .class files. Now enter java HelloWorldApp again. If you still have problems, you might have to change your CLASSPATH variable. To see if this is necessary, try clobbering the classpath with the following command.
set CLASSPATH=
Now enter java HelloWorldApp again. If the program works now, you'll have to change your CLASSPATH variable. To set this variable, consult the Updating the PATH variable section in the JDK 7 installation instructions. The CLASSPATH variable is set in the same manner. Exception in thread "main" java.lang.NoClassDefFoundError: HelloWorldApp/class A common mistake made by beginner programmers is to try and run the java launcher on the .class file that was created by the compiler. For example, you'll get this error if you try to run your program with java HelloWorldApp.class instead of java HelloWorldApp. Remember, the argument is the name of the class that you want to use, not the filename. Exception in thread "main" java.lang.NoSuchMethodError: main The Java VM requires that the class you execute with it have a main method at which to begin execution of your application.

Error Recovery

Error Recovery - Introduction

Error recovery consists of :- a)   Catching the errors b)   Restoring the application to some stable state from which the user can continue.
When programming a user interface you should spend 50% (or more) of your time on error recovery. This should include explicit and informative error messages, the suppression of non-critical errors, and the recovery from all errors.

Non-Critical versus Critical Errors

Non-critical errors are ones the user can safely ignore. Critical errors require you to notify the user, either because they corrupt basic data or the program has not be able to perform a user request. For both non-critical and critical errors, you should take steps to recover from the error by restoring the application to some stable state and allow the user to continue.

Non-Critical Errors

Non-Critical Errors are ones that can be safely ignored (as far as the user in concerned) and should just be logged in an error log file. For example:- If an error occurs when saving the cursor position on application exit, don't prompt the user with an error message, just log it and continue. If an error occurs on reloading the cursor position on application start-up (probably due to the error above), don't prompt the user, just log the error and continue.
Not having the cursor in the last position may be a slight annoyance to the user but it does not stop them from doing useful work. It is not worthy of notifying the user every time they load a file. Of course you need to exercise some judgement about what is a non-critical error.

Critical Errors

Critical Errors should be notified to the user but almost never require aborting the application
This is because modern applications provide the user with a number of functions, a failure of one of the functions should not require the entire application to shut down. Even an OutOfMemoryError is not fatal. For example JEdit  recovers if you run out of memory when trying to open a large file. Parallel also recovers from out of memory errors.
If the error occurs as a result of a user request, such as an undo/redo request, politeness requires that you inform the user that the application could not complete the request, so this is a critical error.

Checked and Unchecked Exceptions.

The standard method, in Java, of notifying the calling method that an error has occurred is by throwing a Throwable or one of its sub-classes (hereafter referred to a an exception with a small 'e').
There are two basic types of exceptions[i] in Java , checked and unchecked exceptions. Throwables that are checked must be declared in a throws clause of any method that could throw them. Unchecked exceptions need not be declared. The Throwables class has two sub-classes, java.lang.Error and java.lang.Exception. All java.lang.Errors are unchecked and need not be declared in a throws clause. Some java.lang.Exceptions are checked e.g. IOException while some are unchecked e.g. IllegalArgumentException.
In the earlier versions of Java (prior to V1.4) there was a clear distinction between checked and unchecked exceptions. Checked exceptions were thrown when some error outside the program's control occurred, such as a file error, while unchecked exceptions where thrown due to some program error, such as an illegal argument. However with the introduction of the chained exception facility in Java V1.4 this general rule disappeared. One of the reasons given in the java.lang.Throwable  documentation
".. is that the method that throws it (the exception) must conform to a general-purpose interface that does not permit the method to throw the cause directly. For example, suppose a persistent collection conforms to the Collection interface, and that its persistence is implemented atop java.io. Suppose the internals of the put method can throw an IOException. The implementation can communicate the details of the IOException to its caller while conforming to the Collection interface by wrapping the IOException in an appropriate unchecked exception. (The specification for the persistent collection should indicate that it is capable of throwing such exceptions.)"
That is, the Java class designers found they need to overcome failings in the interface specification. The result of this is that from Java V1.4 onwards you cannot rely on the distinction between unchecked and checked exceptions. This means you now have to catch all exceptions and look at the cause to see how they should be handled. The first four coding rules below cover this in more detail.
Actually I would argue that even without this loss of distinction between checked and unchecked exceptions, you should have been catching all exceptions even prior to Java V1.4.
So the general rule is catch and recover from all exceptions (i.e. Throwables) and only tell the user if you have to.
Note: All exceptions includes java.lang.Error as well as java.lang.Exception. As noted above an OutOfMemoryError is not necessarly fatal and an AssertionError can also be recovered from. You should at least try and log and recover from all java.lang.Error exceptions. If the whole application really fails you are no worse off and often the application won't completely fail when ajava.lang.Error is thrown.
For more details on what Sun got wrong with exceptions see Unified Error/Information Messages and Help

Coding Rules for Error Recovery

Rule 1: Catch All Throwables that get to main()
If an exception ever reaches your application's main() method then your error recovery has failed its job. The best you can do is make sure the user can tell you where the error occurred.
In order to find out what went wrong you must catch the exception in main and write the stack trace to a log file. Printing the stack trace to the terminal window is not sufficient. If your application is not attached to a terminal window then the stack trace will be lost. Even if your application is attached to a terminal window, writing the errors to a well documented log file makes it easier for the user to send you the output so you can fix the error.
So your main() method should always look something like this.
static {    // set up logging for errors
    LogStdStreams.initializeErrorLogging("applicationLog.log",  "Log File for Application "+ new Date(), true, true); 
   // redirect System.out as well as System.err and append to existing log
}                                        

public static void main(String[] args) {
    try{
     ....
      // the real code of your application goes here
     ...
    } catch (Throwable t) {
       System.err.println(StringUtils.toString(t));
    exit(1);
}
The LogStdStreams class allows you to redirect System.err and System.out to a file so that in the catch clause above you can just say  System.err.println(StringUtils.toString(t));  The StringUtils class shows you how to get the stack trace of an exception as a String that you can write to a file.
Rule 2: Don't let Throwables escape actionPerformed() methods
Typical user interaction with your application will be via menu items or button actions. The associated actionPerformed(ActionEvent e) methods are called on the Swing event thread. (java.awt.EventDispatchThread). You should never let actionPerformed(ActionEvent e) methods throw any exception. If the actionPerformed(ActionEvent e) method throws an exception thejava.awt.EventDispatchThread.run() method terminates and the Java Virtual Machine tries to print the stack trace to the terminal window (if there is one) and then exits.
Note: The net result is that your program exits, but not through your main() method, so Rule 1 does not help you with errors.
You should catch all exceptions here and give the user a useful message telling them why their requested action was not completed successfully.
Your actionPerformed(ActionEvent e) method should always look something like this.
public void actionPerformed(ActionEvent e) {
   try {
     ...
     // handle action here
     ...
    } catch (Throwable t) {
      // recover from error here
       Application.showErrorMessage(t);
      // if this action is associated with a particular window then use
      // Application.showErrorMessage(window,t);    instead
    }                   
}
Here the Application.showErrorMessage(t1); statement handles the displaying of the message to the user and/or, as appropriate, the logging of the error to the log file.
The ExampleShowErrorMessage.java.txt file contains the outline of a static application wide showErrorMessage() method. Unified Error/Information Messages and Help shows you a method of generating user error messages from exceptions and integrating them into the JavaHelp system.
Running the ExampleShowErrorMessage application displays a dialog box like this
exampleShowErrorMessage.gif
Note that this error message clearly tells the user what has happened and what you intend to do about it and invites the user to continue to use the application. The help button should link to more detailed help. See Why you should not use Dialog boxes to Interrupt the User for further discussion of dialog boxes.
Rule 3: Don't let Throwables escape listener methods
As an extension of rule 2 above, you should catch all exceptions in listener methods. Typical Java library code for calling listeners is
for (int i = listeners.length - 2; i >= 0; i -= 2) {
    if (listeners[i] == ListDataListener.class) {
      if (e == null) {
         e = new ListDataEvent(source, ListDataEvent.CONTENTS_CHANGED, index0, index1);
      }
      ((ListDataListener)listeners[i+1]).contentsChanged(e);
    }          
  }
If any of the listeners throws an exception none of the following listeners is called. Since there is no guarantee of the order in which listeners are called you cannot be sure which ones are called first.
So you should write all listener methods like this (using contentsChanged() as an example):-
public void contentsChanged(ListDataEvent e) {
  try {
    ..
    // handle event here
    ...
   } catch (Throwable t) {
     // handle errors and recover from errors here
     // and either log error or if important display message to the user.
   }
}   
Rule 4: Catch all Throwables that get to the Thread run() method
User interfaces should make liberal use of threads to keep the user interface responsive. If you don't catch the exceptions thrown in the run() method, then the thread terminates and the Java Virtual Machine tries to print the stack trace to the terminal window (if there is one) and the application continues to run.
You should catch all exceptions that get to the run() method and take appropriate action. This action may be giving the user a useful message telling them what happened, or more commonly you will need to pass this error information back to the application so that it can take recovery action and inform the user.
Programming with Java Threads provides a complete package for starting, stopping and handling errors in Java Threads. It shows you how to pass these errors back.
At the very least, your Thread's run() method code should include the following.
public void run() {
   try {
     ...
     // do thread work here
     ...
    } catch (Throwable t) {
       // do error recovery here
       Application.showErrorMessage(t);
       // or log the error if not critical to the user.
    }                   
}
Rule 5: Recover from Throwables
While rules 1 to 4 above are designed to catch all the errors, this rule is about recovering from them.
As mentioned above, Error Recovery is about "restoring the application to some stable state from which the user can continue". Depending on the application and the error this may be simple or complicated. Basically it involves:-  a) Recovering resources left over after the error  b) Restoring the application to some stable state. Ideally this will be the state it was in prior to the action that caused the error.
Both of these actions can often be carried out in the finally clause of a try/catch/finally block. If at all possible you should clean up at the bottom of the method that allocated the resources or changed the state, as this makes for cleaner procedural programming.
For example the actionPerformed() method of the File, SaveAs menu item should look something like this
// action for File SaveAs menu item
public void actionPerformed(ActionEvent e) {
   String saveFileName = null;   // mark as not collected yet
   FileOutputStream fos = null;  // mark resource as not allocated yet
 
   try {
     // pick up the saveFileName from user here via FileChooser dialog

     fos = new FileOutputStream(saveFileName);
     //  save to fos here
     ...
     // close output file
     fos.close();
     fos = null;  // mark as closed (resource released)

    } catch (IOException ex) {
        // handle error here  in this case rethrow it with a better message
        // See "Unified error messages" for a better way to handle this exception
       throw new IOException("Error saving file to "+saveFileName+"\n"+ex.getMessage());

     } finally {
       // always release resources i.e. close the files
       try {
           if (fos != null) {
               fos.close();
           }
        } catch (IOException ex1) { 
           // ignore this error as if fos != null we already are handling an error
        }       
       // do other clean up here
    }                   
}
In a similar way, the application state should be saved at the top of the method and then restored if an error occurs.
Rule 6: Don't just Catch and Print Throwables
Inexperienced programmers will often write code like the following in low level utility methods
private Icon makeIcon(final String gifFile) throws IOException {
    InputStream resource = HTMLEditorKit.getResourceAsStream(gifFile);
    if (resource == null) {
         System.err.println(ImageView.class.getName() + "/" +  gifFile + " not found.");
         return null; 
    }
      ......
     // rest of method here
     ....
}
Here the error is that the gifFile could not be found. There are two problems with printing to System.err.:-  1) If the application is not attached to a terminal window, the output of System.err gets lost (see LogStdStreams.java.txt for a solution to this).  2) Printing an error message to System.err and returning null limits your options for error reporting, error recovery and displaying user help.
A better approach is to write
private Icon makeIcon(final String gifFile) throws IOException {
    InputStream resource = HTMLEditorKit.getResourceAsStream(gifFile);
    if (resource == null) {
         throw new FileNotFoundException(ImageView.class.getName() + "/" +  gifFile);
    }
      ......
     // rest of method here
     ....
}
This then lets the calling method know exactly what went wrong and give it the chance to take appropriate action, for example using a default icon or displaying a detailed error message with a connected help topic.
This example is actually from the javax.swing.text.html.ImageView class. There are many places in the Java library source were errors are "handled" by just writing a message to System.err and in some cases dumping the stack trace. Some of these are just bad programming and hopefully will disappear over time. However some are due to the lack of a built in method of passing back exceptions from threads.
For your own threads you can use the thread package described in Programming with Java Threads to handle exceptions in threads but for the Java library's System.err.println() statements you will need to use LogStdStreams.java.txt to at least insure the errors are logged.
Another version of this problem is
public void doSomeThing(String input){
    ...
    try {
    // call some method here that throws a checked exception such as a BackingStoreException
    preferences.flush();
    // this method, doSomeThing, does not throw BackingStoreException so the temptation is to just catch an ignore
    } catch {BackingStoreException ex) {
       // don't know what to do so ignore
    }    
}
In this case the BackingStoreException should be wrapped in an unchecked exception such as a RuntimeException, using Java V1.4 exception chaining, ie.
public void doSomeThing(String input){
    ...
    try {
    // call some method here that throws a checked exception such as a BackingStoreException
    preferences.flush();
    } catch {BackingStoreException ex) {
       throw new RuntimeException("Error flushing preferences",ex);
    }    
}
Then you have to catch all exceptions and check their causes to see what really happened.
Bruce Eckel also mentions this temptation to “swallow exceptions” in his discussion on CheckedExceptions  . He argues that checked exceptions actually encourage programmers to write code that catches and ignores exceptions.
Rule 7: Debugging code should never throw Throwables
Debugging code is supposed to assist you in checking the operation of your application and in finding and correcting errors. It is not much use if it itself throws exceptions.
The most common problem is in a class's toString() method. When an error occurs in the class, some of its member objects may be left in an invalid state (often null). Typical toString() methods try and print out the value of the class's members using object.toString().
Obviously this will throw a NullPointerException if the object is null. The preferred way of converting member objects to strings for printing is to use the StringUtils.toString() method  StringUtils.toString(object)
This method checks for null objects and then calls object.toString() and catches and returns as a String any exceptions thrown that method might throw.

No comments:

Post a Comment