Java Programming   Java Programming
 |Sofia Home | Content Gallery |
Home
Syllabus
Schedule
Lessons
Assignments
Resources

Lesson 7.1 Syntax Errors

Compile-Time Errors

One of Bill McCarty's pet phrases which he repeats in the opening paragraphs of Chapter 7 of the recommended textbook for this course likens the Java compiler to a "...stern and pedantic taskmaster..." that "...tolerates absolutely no departure from the official rules of the Java language." I really like the image that this conveys. I also like to think of the Java compiler as a particularly tough Marine Drill Sergeant. 

The reason that I like both these images is that they both hint at the reason why the compiler is so strict. It is good to learn to speak and think and write well while you are young and in school; doing so opens up opportunities that aren't available otherwise. It is much less costly for a soldier to learn on the training ground than to learn in the heat of battle.

That is why you want your compiler to be as picky as possible. Let me give you an example. The Basic language allows progammers to create variables by simply using them. This is called implicit declaration. If you use implicit declaration, your Basic compiler will not generate a syntax error when it encounters a new variable it hasn't seen before. Your compiler is much friendlier.

This sounds great, but professional programmers almost always turn this feature off, if possible. Why? Because allowing implicitly declared variables in your program also allows misspelled variable names to slip through, and those errors don't show up until your program runs.

Professional programmers quickly learn that it is better to catch every possible error in your program before you ship it off into battle, even if that requires a compiler that seems, well, a little "pedantic".

In this lesson, we're going to look at the kind of errors that the Java compiler is able to find. Such errors are called compile-time or, more generally, syntax errors. Such errors violate the very strict rules governing Java's formal grammar.

Not all errors in your program can be found by the compiler. The most difficult and intractable problems are not errors in expression, but errors in logic. We'll cover those kinds of errors in Lesson 7.2, "Run-time Errors."

Back to Top

Error Messages

The following program, a variation of Listing 7-1 in your textbook, contains several syntax errors. Before you go any further, why don't you just walk through the listing and see how many errors you can identify?
Error1.java
import java.Applet.*;
import java.awt.*;

public class error1 extends Applet
{
  int num;
  num = 32;

  public static void main(String args[])
  (
    int var;
    System.out.println("var = " + var);
    System.out.println("Now " + num);
    return true;
   )
}


When you compile this program with javac, [JDK 1.1.8, Sun Version], you get the five error messages which can be seen here. [click on the image to enlarge]. That doesn't necessarily mean that the program has five errors, however. Often, a single error can cause "cascading errors" as one error leads to more errors, as subsequent lines are misinterpreted.

Error messages appearning in console window when compiling Error1.java

That occurs with the first two errors in this case. Because the name of the java.applet package was accidentally capitalized, the compiler is unable to find the Applet class. Thus, you get two error messages: the first message pointing out the misspelling, and the second message caused by the first.

If you use a different compiler, like javac from the Java2 SE version 1.3, you'll see different errors, as shown here. This version of javac reports seven different messages.

Error messages after compiling with Java 2 compiler 1.3. [click to enlarge]

Remember, though, there aren't necessarily seven errors; one mistake, such as a mistake on the import line, leads the compiler to treat other lines incorrectly.

Back to Top

Common Errors

Although there are a wide variety of error messages, compile-time errors really come in four basic categories:
  1. Structural errors
  2. "Real" syntax errors
  3. Typographical errors
  4. Type errors
Understanding these basic categories, and the different ways to correct them and avoid them, will go a long way toward helping you write code that compiles cleanly the first time. Of course, once you've done that, the real error correction begins.

Let's take a look at these three categories.

Structural Errors

We think of the Java compiler as a machine that takes source code in one end and spits compiled byte-code out the other. The problem with this analogy is that it leads us to think of the compiler like a log chipper processing our source code in a purely sequential manner. In fact, that's not what happens at all.

Before the Java compiler can even think about turning your source code into byte-code, it first must read your entire program, and then organize it. This organization phase is called parsing or tokenizing your program. 

When the compiler parses your code, it breaks the entire program up into individual pieces called tokens. This tokenizing phase is almost exclusively dependent upon the precise matching of pairs of delimiters--single quotes, double quotes, parentheses, and braces. 

For instance, look at this program, which has more syntax errors than Error1.java

  • ink and integer are used as primitive types, instead of int
  • The C++ pointer-to-member operator is used, despite the fact that Java does not have a similar operator.
  • The keywords public and static are both misspelled.
  • A void function returns something called TRUE.
  • An assignment is performed outside of a method.
  • Parentheses are used as delimiters around both the class and method definitions.
  • String constants are not correctly delimited.
Error2.java [Structural Errors]
import java.applet.*;
import java.awt.*;

public class Error2 extends Applet
(
  ink num;
  num = 32;

  Public Static void main(String args[])
  (
    integer var;
    System->out.println("var =  + var);
    System->out.println("Now ' + num);
    return TRUE;
  )
)

Despite all of these problems, when you compile this program, with the JDK 1.1.8 javac compiler, this is what you'll see:

Error messages displayed when compileing Error2.java

The result? Fewer errors than displayed for Error1.java

During the parsing phase, the compiler attempts to organize the program by matching keywords such as class, along with delimiters. When you fail to get the essential organization of your program correct, then the compiler really can't go any further. 

This program doesn't really have only four errors; the remaining errors are hidden by the structural errors. Only after the entire program is organized, can it go through and make sure the right tokens are in the correct order.

Preventing structural errors is really much easier than correcting them afterwards. Here are three things you can do to prevent them from occurring in the first place:

  1. Understand what goes where. Don't just copy code and place it at random in your Java programs. Know where each element belongs. Specifically, make sure you understand that:
    • import and package statements must appear at the beginning of your file.
    • Field definitions must appear inside a class defintion, but outside of a method definition.
    • Method definitions must be inside a class defintion.
    • Program statements, except for field definitions, must appear inside methods. Program statements must end with semicolons.
    • Formal arguments go inside the parentheses following a method name, and must include both a type and an argument name for each argument.

    •  
  2. Write structural elements in one piece. Beginning programmers often start at the beginning of a structural element--a class or method definition, or a selection or iteration statement--and work through to the bottom. Experienced programmers will put the structure in place first--write the header and add the opening and closing braces--before turning to the body of the class or method.
  3. Use a consistent and easily checked indenting style. Your indenting style should allow you to easily tell if all your braces are in the right place. Failing to indent and line up your braces makes your code almost impossible to check.
Back to Top

"Real" Syntax Errors

After the Java compiler has tokenized your code, a second phase of parsing takes place. This is lexical analysis--making sure that the correct kinds of elements [tokens] are in the correct places. Errors in the lexical analysis phase are "really" syntax errors.

Using two keywords in a row, for instance, results in a syntax error like this:

int double a;
Using a keyword when it is not necessary results in a syntax error. This is a common example:
int a = 10, int b = 20;
Omitting a required token results in a similar syntax error, like this:
public void myMethod(int a, b) { }
Using a token in an incorrect context [such as the keyword void] can also result in a syntax error like this:
public void class MyApplet extends Applet

There is no "magic bullet" for detecting and preventing this kind of syntax error. You simply have to learn to put the right keywords and tokens in the correct order.

Back to Top

Typographical Errors

Typographical errors are somewhat easier to deal with than structural errors because the Java compiler usually pinpoints them fairly accurately.

Here are the three most common typographical errors you're likely to make:

  1. Missing semicolon. Let's face it, semicolons are small, and hard to see. Even worse, the colon, which looks almost exactly like a semicolon, is on the same key and is the character you're likely to insert when you mistype.
  2. Using assignment [=] instead of equality [==]. Fortunately, unlike C, Java will usually catch this error. Still, it's annoying to have to go back and fix it.
  3. Using the wrong case. Java is a case sensitive language, and that case sensitivity extends to Java filenames, even on operating systems that are not, themselves case sensitive, like Windows and the Mac.

Type Errors

Remember that a variable is a "bucket" where you store values. When you ask the compiler to create a variable for you, you tell it what kind of values you intend to store in your bucket. This allows Java to prevent you from storing milk or soda in a cardboard box designed to hold dry cereal, so to speak. 

Java's type system is designed to keep you from losing information, not to make life difficult for you. Understanding these three simple rules should help you to go along with the program:

  1. You can store a little value in a big bucket, but not vice-versa. This means you can store byte, short, int, and long values in a long variable, but you cannot store a long value in a byte, short, or int variable.
  2. You can store a less-precise value in a more-precise bucket, but not vice-versa. This means you can store byte, short, int, long, and float values in a double variable, but you cannot store double values in a byte, short, int, long, or float variable; the bucket is incapable of retaining the necessary precision.
  3. You can only store like things in similar buckets. You can store different numeric values in different kinds of numeric variables, but you cannot store boolean values or object references in numeric variables. 

Finding the Error

If your program has only a single compile-time error, and if the error is straightforward, finding and fixing it is usually no problem. The compiler's error message will tell you the name of the file and the line where the error occurred. You just fire up your text editor, fix the problem, and recompile. Problem solved.

What do you do when you get 25 errors, or when the error message produced by Java's compiler doesn't seem to make any sense at all? When this occurs, you really need to have a strategy. Systematically attacking the problem will help you avoid frustration and will help you eliminate syntax errors in record time.

If you have a multitude of errors, often, you can't even see the first one, because it scrolls off the screen as the rest of the errors are displayed. When this happens, you need to "divide and conquer" by "commenting out" sections of code.

Here's how it works. The following applet generates five errors, but the strategy we'll follow will help you regardless of the number of errors:

Error3
import java.applet.*;
import java.awt.*;

public class Error3 extends Applet
{
  integer: first, second;
  double b, double c;
  goButton Button as new Button("Go");

  public viod init(goButton)
  {
    add(goButton);
    new(10, "Bob");
  }

  public int new(int a, string b);
  {
    println("a = ", a, ", b = ", b);
  }
}

Compiling the code, this is what you see:

Errors displayed after compiling Error3.java

Step 1: Structure First

Begin by "commenting out" the entire body of your applet's class. Do that by using the multi-line comment symbols [/*, */] like this:

 

Error3 - Step 1
import java.applet.*;
import java.awt.*;

public class Error3 extends Applet
{
/*
  integer: first, second;
  double b, double c;
  goButton Button as new Button("Go");

  public viod init(goButton)
  {
    add(goButton);
    new(10, "Bob");
  }

  public int new(int a, string b);
  {
    println("a = ", a, ", b = ", b);
  }
*/
}

Now when you compile your program you won't see any errors at all. If any errors did appear, you'd know that you had:

  • made an error in the basic structure of your class.
  • made an error in one of your import statements.
  • made an error in the class header.
Step 2: Reveal the Attributes
Once the basic structure of your applet compiles cleanly, move the opening comment character down to "expose" the applet's attributes, and recompile. When you do this, you'll see the following errors displayed:

Error messages after commenting-out all but the class attributes

Now you have isolated the errors that have to do with defining attributes. Make sure you understand how to define an attribute. For this example, you'd make the following changes:

int first, second;
double b, c;
Button goButton = new Button("Go");
Step 3: Method by Method

Continue this divide and conquer approach by moving the opening comment marker to reveal the first method. Recompile and fix the errors you find. Continue to reveal the rest of the class, method by method, until the entire program compiles without error.

Back to Top

Something to Talk About

What changes need to occur to finish correcting the rest of the errors in Error3.java? 

Please continue to the next section of this lesson.

 

Back to Top

 

Content Developed by Stephen Gilbert, Licensed under a Creative Commons License
Published by the Sofia Open Content Initiative
© 2004 Foothill-De Anza Community College District &The William and Flora Hewlett Foundation