Variables have three fundamental properties: type, scope and lifetime. Scope is a compile-time concept: it is based only on the written text of the program. It is the region of the text in which a variable is defined. Lifetime is a run-time concept: it is the period of time during which a variable exists.
Types are either primitive of reference. Variables of primitive type directly contain their value. The primitive types include int, char and boolean. Variables of reference type contain values what point to other things, generally objects, inside of which other variables or methods are found. Each class is its own type, and classes related by inheritance have types related by the conversions widenning and narrowing.
Examples:
The details:
Scope
Fields and Methods have class scope: they are known throughout the class in which
they are defined.
It does not matter where in the text these variables are defined: a method
or field can be used before it is defined because the compiler will look throughout
the entire body of the class to find the declaration.
Local variables have block scope: they are known from the point of their declaration until the end of their smallest enclosing block. Unlike C, local variables cannot be redeclared inside of a contained block. Parameters have block scope where the block is the method body.
Lifetime
Local variables exist only during the run of the
method call, whereas instance variables have lifetimes exactly that of
its containing object.
Type
All variables have a specfic type, either a primitive type or a reference
type. In a variable are stored values which are either of the same type
as the variable or are compatible with the tye of the variable.
For primitive types, the value must be of exactly the same type
as the variable, else a conversion will occur. For reference types,
the value can be a reference to an object of a class of the variable's type,
or to an object of any subclass of that.
The process of assigning a value to a variable where the variable has a supertype of the value is called widening. Widening never causes any problems. If class A extends class B, an object of class A can be treated as an object of class B in all contexts. Therefore, if a reference to an object of class A is placed in a variable of type B, there will not be any problems.
However, to treat a value in a variable of type B as an A might cause problems if that value is not in fact a reference to an object of class A. To convert towards subclasses like this is called narrowing, and will always require an explicit cast operator and a run-time check. If the check fails, a ClassCastException will be thrown.
Object is the widest of the reference types. Any reference to an object can be stored in a variable of type Object. Null is a type with no name, but there is the null literal which is the only value of null type. It is the narrowest type: it can be assigned to any variable of reference type. In a sense, null multiple inherits from all classes.
Overloading
It is very difficult to understand the results of mixing
widening and method overloading. The compile will automatically
widen references in order to match one of a collection of
overloaded methods. This is done at compile time, and once
done, is fixed for method lookup at run-time.
That is, once the compiler chooses a specific overload, overriding
must be exactly accoding to that overload.
If at run time it turns out that the variable holds a reference
to an object with a better match for the method call, that method will
not be chosen. The compiler has already decided the target cast types
of all parameters.
Ambiguities can occur where several overloads compete equally for the title of best match. In this case the compilation fails. The compiler searches for the the most specific match. This is defined as:
a method of class A with arguments A1, A2, ... , An is MORE SPECIFIC than a method of class B with arguments B1, B2, ... , Bn if A widens to B, A1 widens to B1, ... , An widens to Bn.