Java: Assignments

Before we start talking about assignments I think it is important to quick review some concepts like heap and stack.

Simply put, the heap is a memory section where the JVM keeps the objects it creates. The stack is a memory section that stores method calls and local variables.

Based on that, we can say that:

  • local variables live on the stack
  • objects and instance variables live on the heap

Back to assignments, we can say assignments in Java are all about putting a value into a variable using the operator =, something like x = 2.

Variables are bit holders of a specific type. Values can take the form a literal or a reference to an object. A value is a literal when it has a primitive type, like int or char; and Stings. A value is a reference when it holds a memory reference to something that inherits from Object.

Code samples in this post were compiled with a JDK 6.

Literals

In Java, literals can take the form of numbers, booleans, characters or strings.

Integer/Long Literals

Integer literals can be represented as:

  • octals (base 8)
  • decimals (base 10)
  • and hexadecimals (base 16)

Decimal literals are the most widely used and they are pretty simple.

Octal integers use digits from 0 to 7. We represent octal integers in Java by placing a 0 in front of the number. Removing the digit 0, octal numbers can have up to 21 digits.

Hexadecimals are formed by 16 symbols which are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f. To represent hexadecimals in Java, we need to prefix the literal by 0x. Hexadecimals can hold up to 16 digits, excluding the prefix.

Finally, in order to declare literals of type long, it must have the suffixes l or L.

        int two = 2;    // The literal 2 is a decimal value
        int seven = 07; // The literal 07 is an octal value
        int x = 0x007;  // The literal 0x007 is an hexadecimal
        int y = 0XCAFE; // 0XCAFE is a valid value (capital and lowercase letters are accepted)
        long l = 0x123l;    // An hexadecimal of type long

Floating-Point Literals

Floating-point literals are defined as numbers, decimal symbols, or fractions, like: 123.456. By default, floating-points are defined as doubles (64 bits). To assign a floating-point literal to a float variable, it is necessary to use the suffixes f or F. Otherwise, the compiler will return an error in order to prevent a big number (double by default) being assigned to a small holder (float). It’s also possible to suffix double literals with D or d.

    double d = 123.456; // 123.345 is a floating-point literal
    double s = 12.34d;  // 12.34d is a floating-point suffixed with d
    float  f = 12.50;   // Compiler error: possible loss of precision
    float  f2 = 34.1F;  // A valid floating-point

Boolean Literals

A boolean literal can only be true or false. There is no secret, if you try to put something other than these two values, the compiler will let you know you’re doing something bad.

    boolean TRUE = true;    // Literal true
    boolean FALSE = false;  // Literal false

Character Literals

A literal of type char is represented by single quotes ''. If you want to use an Unicode value, you need to use the unicode notation \u as a prefix.

In Java, the char type can hold up to 16 bits and can also accommodate unsigned integers, since they have 65535 as maximum value.

Finally, to use special characters you need to use the escape code \.

    char a = 'a';           // 'a' is a literal
    char n = '\u004E';      // Unicode representation for the letter N
    char h = 0x8;           // Hexadecimal literal
    char o = (char) 88888;  // A cast is required when the literal is out of range
    char l = '\n';          // New line

Literals for Strings

Even if strings are not primitive types, like in many other languages we can represent strings as literals.

String s = "Learning literals =]";

Casting

Casting or type casting is a mechanism by which values or object references can be converted from one type to another.

Casts can be either implicit or explicit. Implicit casts happen in widening conversions, when putting a small value (like an int) into a big container (like a long). Implicit casts do automatic conversions, and do not require a cast operator. However, explicit casts happen when putting bit values into small containers (like putting an int into a short). In explicit casts, a cast operator is required because the compiler needs to be informed that “you know what you are doing”. That’s because there’s a risk of losing data in the conversion. If we don’t do that, we are going to get a message like “possible lost of precision”.

        long l = 5;         // Implicit casting: int to long
        float f = 100.0F;   // Explicit cast
        short s = (short) f;    // Explicit cast

When a value is narrowed on a type casting operation, Java simply truncates the higher-order bits that won’t fit. In another words, if we cast an int (32-bit signed) into a short (16-bit signed), the 16 bits on the left will just disappear when the cast is done. More details about the capacity of Java primitive data types can be found here.

Reference variable assignments follow the same logic. We can assign a subclass of a type, but not a superclass.

class Vehicle {}

class Car extends Vehicle {}

public class CastingObjects {
    public static void main(String... args) {

        Vehicle car = new Car();    // No problem,
                                    // the Car is a Vehicle

        Car vehicle = new Vehicle();    // Compiler error :
                                        // Incompatible types
    }
}

Variable Scope

Variable scope is another important element when it comes to assignments. It is good practice to keep the scope as small as possible. Variables with a large scope can turn the code vulnerable to bugs, and difficult to read and debug.

In Java, there are four possible scopes to variables (on ascending life time):

  • Variables in a { block of code } live only during the execution of the block
  • Local variables live as long as their method remains on the stack
  • Instance variables are created when an instance is created and they live as long as the instance lives
  • Static variables are created when the class is loaded and they stay alive as long as the class remains in the JVM

Initialization

Instance variables (also called member variables) are initialized with default values, even if there isn’t any explicit initialization. It is good practice to initialize all instance variables explicitly, so the code becomes clearer to other programmers 😉

Here are the default values used to initialize instance variables:

Data Type Default value
byte 0
short 0
int 0
long 0L
float 0.0F
double 0.0
char ‘\u0000’
boolean false

And what happens when the instance variable is an array? Well, since an array is an object, it will be assigned to null. Nevertheless, when an array is explicit initialized, default values are assigned to its elements. An array of ints, for instance, will have all its elements initialized to 0. An array of Objects will have all its elements initialized t0 null, and so on.

On the other hand, local variables must always be initialized. A default value is not given to local variables. Even objects and arrays need to be given a value. When you try to use a variable that has not been initialized, you will get a compilation error.