Here we go:
First off,
compiler errors will always arise when your code does not comply with the Java syntax and semantics as described in the
Java Language Specification (the syntax being
whatyou write and the semantics
the "meaning" of what you write).
I tried to come up with an exhaustive list of all situations that will result in a compile-time error but the list was way too long to be posted and there is no good way to present those situations concisely. The best way to get to know those situations is to go through the JLS sectionwise and search for the words "compile-time error".
Some examples of compile-time errors:
- a class tries to extend more than one class
- overloading or overridding is not implemented correctly
- attempt to refer to a variable that is not in the scope of the current block
- an inner class has the same name as one of its enclosing classes
- a class contains one or more abstract methods and the class itself is not declared "abstract"
- a class tries to reference a private member of another class
- trying to create an instance of an abstract class
- trying to change the value of an already initialized constant (final member)
- declare two (class or instance) members with the same name
etc, etc,...
To sum up, whenever your program does not comply with all syntactic and semantics rules mentioned in the JLS, a compile-time error will be thrown. When this is the case, you won't even be able to run your program, which makes sense.
Now, let's say you have corrected all compile-time errors and your code compiles fine, the bytecode has been generated and the program is ready to run. This doesn't mean that your program will actually run. That's when runtime errors (or exceptions) come in. The compiler does a lot of work but it is not able to check everything. For instance, if you don't initialize a member variable, say you just declare it as follows:
String s;
then s will be initialized to null when a particular instance of your class is created. Then, you try to invoke a method on s somewhere in your code like this:
s = s.substring(2);
and s has not been initialized up to that point, then a NullPointerException will be thrown because s is null and the interpreter cannot invoke a method on null. Those situations can only be detected at runtime, because instance method lookup occurs at runtime and not a compile-time.
Note that a runtime error (or exception) is not necessarly of the type RuntimeException (unchecked exception). Checked exceptions also occur at runtime, but you have to explicitely write code that catches them unlike with RuntimeExceptions.
Some examples of runtime errors (or exceptions):
- trying to invoke a method on an uninitialized variable (NullPointerException)
- memory ran out (memory leaks...) (OutOfMemoryError)
- trying to open a file that doesn't exist (FileNotFoundException)
- trying to pass arguments to a method which are not within the accepted bounds (IllegalArgumentException)
- trying to invoke the start() method on a dead
thread (IllegalThreadStateException)
- trying to invoke wait() or notify() on an object without owning the object's monitor (IllegalMonitorStateException)
etc, etc...
To get an exhaustive insight I can't do better than recommend you to code a lot, so that you gain as much experience as possible. You will only be able to learn properly if you do it by yourself. Humans usually learn by experience. Once you have gotten a dozen times a NullPointerException at runtime you are going to start to know why this situation occurs and know how to correct it. You will also be able to recognize a buggy piece of code just by reading it, but for that you have to practice.
There are much too many situations that can lead to an error, be it at compile-time or at runtime, that's why you have to take your time and explore this wonderful labyrinth one step at a time.
I hope this helps.
[ March 18, 2002: Message edited by: Valentin Crettaz ]