Hmmmm.... I don't have a list, but I have a few comments off the top of my head. But first, a disclaimer -- by the time you are ready for the exam, the issue of which things are runtime vs. compiler problems will probably be clear to you.
But for now, I can offer a few things to think about;
1) The compiler cares deeply about TYPE. If the compiler has enough information to KNOW that you have a type problem, it (the compiler) will complain. And by type I mean reference or primitive type.
Dog d = new Dog(); // no problem
Animal a = new Dog(); // no problem if Dog extends Animal
Dog d = new Animal(); // COMPILER ERROR! The compiler knows that since Dog extends Animal, *not all Animals are Dogs*.
Dog d = new Cat(); // COMPILER ERROR! Duh.
But...
Animal c = new Cat(); // OK, Cat extends Animal
Dog d = c; // COMPILER ERROR! But not because the compiler knows it's a Cat. The compiler sees only the REFERENCE type (Animal) and complains.
You can make the code above succeed at compiler time by using a cast, which tells the compiler, "Trust me, I know this will work at runtime. It really will be a DOG at the other end of the 'c' reference." as follows:
Dog d = (Dog) c; // OK to compiler. Compiler trusts you now. (even though you are lying)
But... since it really is NOT a Dog object (it is a Cat), the code above will produce a runtime error when the JVM realizes that it is NOT a Dog object after all, but a Cat lurking behind the reference named 'c'.
The compiler uses the REFERENCE type (not the OBJECT type) to decide if you can invoke a method. If you invoke a method on a reference type that does not HAVE that method, the compiler complains.
Cat c = new Cat();
c.bark(); // compiler error! Cats don't bark (which means there is no bark() method defined in class Cat)
Animal d = new Dog(); // OK at compile time.
d.bark(); // COMPILER ERROR
Even though you know and I know that it really IS a Dog at the end of 'd', and that Dog does have a bark() method, the compiler sees only that 'd' is of type ANIMAL, and since Animal does not have a bark() method, the compiler will not let you try. To invoke the bark() method, you would have to use a cast:
Dog doggie = (Dog) d; // OK
doggie.bark(); // OK
2) In addition to reference types, the compiler cares also about *primitive* types, in much the same way. If it KNOWS something won't fit into the declared variable type, it will complain.
float f = 23.4; // compiler sees the literal as a double, and knows a double can't fit into a float-sized variable, without a cast.
The rules above for casting primitive and reference types applies to declarations and assignments, as well as return values and arguments. If the return value is declared as type Dog, the compiler won't let you return an object referenced with an Animal reference variable type. Even it really IS a Dog... (without a cast)
public Dog foo() {
return new Animal(); // COMPILER ERROR
}
But this is OK at compile time:
public Dog foo() {
Animal a = new Cat();
return (Dog) a; // compiler trusts you, even though you are lying by indicating that 'a' is a Dog.
}
But the code above will blow up at RUNTIME when the JVM once again discovers a Cat object is at the end of the 'a' reference, and a Cat absolutely can NOT be treated as a Dog. (boy do I know that in *my* house
3) The compiler cares, of course, about syntax errors -- so anything that's missing a semicolon or a curly brace or uses parens instead of brackets for arrays, etc. will cause a compiler error.
4) The compiler cares about checked exceptions, as Jasper mentioned, so it will always be sure that you have followed the "handle or declare" rule by either providing an appropriate catch block, or by declaring the exception.
5) The compiler cares deeply about access levels, so if you try to invoke a private method of another class, the compiler will complain. (Or a default access method in a class from another package, etc.)
6) The compiler cares deeply about matching arguments to parameters, so you cannot invoke a method without sending it the arguments it is expecting.
You cannot call:
x. doStuff(); // no args
If the method is defined:
public void doStuff(int i); // defined with an int parameter
Same goes for constructors.
7) Compiler cares about implementation rules! An interface is a contract. A superclass definition is a contract. If you SAY that you implement an interface (class Foo implements Runnable), the compiler will force you to fulfill the contract by implementing the run() method defined by Runnable interface.
Same goes for abstract classes. If you extend an abstract class with a non-abstract class, the non-abstract class MUST implement all non-abstract methods of the abstract class (including abstract methods defined by any superclasses of the abstract class). The compiler can tell, so the compiler will keep you honest.
Same goes for rules of overriding. If you extend a class and override a method, the compiler KNOWS what you can and cannot do. For example, you CAN make the access level *less* restrictive (from protected to public, for example) and you CAN throw fewer or narrower (or no) checked exceptions. But you cannot do the opposite of those. And you cannot vary the return type or argument list.
The compiler also knows the rules for overloading, so it can tell if you vary JUST the return type of a method (which is illegal, of course, since you must vary the argument list in order to legally overload a method).
There are lots more, but these are the first that come to mind. I would think strongly about these things:
*The compiler cares about the 'contract' you agree to, and wants to keep you doing what you said you would do. This includes rules for checked exception handling, interface implementation, method overriding, and implementing abstract methods.
* The compiler cares about TYPE. Both object and reference type, although there are several places where the compiler cares only about the reference type (rather than the object type, since the compiler often does not know what the actual object is out there on the heap).
Again, after saying all this, by the time you have learned everything you need to pass the exam, you will understand enough to be able to know which are compiler and which are runtime errors. It is very difficult -- actually impossible -- to determine which are compiler errors and which are runtime errors just by trying to memorize a list. You must understand what the compiler is trying to do for you, and why it matters in Java, so that you can answer questions on the exam that may involve scenarios you have never seen before.
Cheers,
Kathy
(it gets easier. it really does.)
