For this kind of purpose, it is useful to distinguish between the type of variable set in its declaration and the class of the object created when the object was created.
The variable is allowed to be null or point to any object whose class corresponds to its type, or is a direct or indirect subclass of this class. Because String is a subclass of Object , a variable of type Object can point to an object of class String .
In your example, the compiler knows that the String expression must be null or point to a String object, so at compile time Object o = new String("Hello"); .
On the other hand, an expression or variable of type Object may refer to String, but may refer to something completely different. The compiler requires an explicit cast to say that the conversion is in order.
public class Test { public static void main(String[] args) { Object stringObject = "xyzzy"; Object integerObject = new Integer(3); String test1 = (String)stringObject; System.out.println(test1); String test2 = (String) integerObject; System.out.println(test2); } }
This program compiles because I told the compiler that the conversions are ok with my clicks. It goes through the first call to println because stringObject really points to String . It fails with a ClassCastException on the line String test2 = (String) integerObject; . As far as the compiler knows, integerObject can point to a String , so it took my action. At run time, the JVM finds that it really points to Integer and throws an exception.
Using the Object type for integerObject is significant. This does the conversion String test2 = (String) integerObject; expressions of type Object for type String, a conversion that can be successful, depending on what integerObject actually refers to. If integerObject declared type Integer , the compiler would reject the program. There are no objects that Integer and String expressions can reference.
source share