Literals

Diagram showing literal.
Diagram showing literal.

Any constant value that can be assigned to a variable is called a literal.

Integral literals

import java.lang.*;

class Test {
   public static void main(String[] args) {
       // decimal form literals: (base 10)
       int x = 10;

       // octal form literals: (base 8)
       // note: to make JVM treat the number as octal, prefix the number with '0'.
       int x = 010;

       // Hexadecimal form literals: (base 16)
       // note: to make JVM treat a number as hexadecimal, prefix the number with "0x".
       int x = 0x10;
   }
}

For integral data types (byte, short, int, long), we can specify literal values in the following ways:

decimaloctalhexadecimal
base10816
allowed values0-90-70-9, a-f
prefixn/a00x

These are the only possible ways to specify literal values for integral data types.

Java is case sensitive, however for using hexadecimal characters a-f or 'x' in the prefix, both upper and lower case characters are allowed. This is one of very few areas where Java is not case sensitive:

import java.lang.*;

class Test {
    public static void main(String[] args) {
        int x = 10; // VALID
        int x = 0786; // INVALID: integer value too large compile-time error
        int x = 0777; // VALID
        int x = 0XFace; // VALID
        int x = 0xBeef; // VALID
        int x = 0xBeer; // INVALID: ';' expected (instead of 'r') compile-time error
    }
}

A programmer has the choice to provide a integral literal in decimal, octal and hexadecimal format, but the JVM will always return the value in decimal form:

import java.lang.*;

class Test {
    public static void main(String[] args) {
        int x = 10;
        int y = 010;
        int z = 0x10;
        System.out.println(x); // 10
        System.out.println(y); // 0 * 8⁰ + 1 * 8¹ = 8
        System.out.println(z); // 0 * 16⁰ + 1 * 16¹ = 16
    }
}

Every integral literal is by default an "int" data type. To explicity specify as long type by adding 'l' or 'L' suffix to the value:

import java.lang.*;

class Test {
    public static void main(String[] args) {
        int x = 10; // VALID
        long l = 10L; // VALID
        int y = 10L; // INVALID: possible loss of precision, found: long, required int
        long z = 10; // VALID
    }
}

There is no way to explicitly specify a byte or short data type by using 'b', 'B' for byte and 's' or 'S' for short. However, we can indirectly infer the type of a "byte" and "short" data type by using correct data type:

import java.lang.*;

class Test {
    public static void main(String[] args) {
        byte x = 10; // VALID
        byte l = 127; // VALID
        byte y = 128; // INVALID: possible loss of precision, found: int, required byte
        short a = 32767; // VALID
        short c = 32768; // INVALID: possible loss of precision, found: int, required short
    }
}

If an integral literal value is assigned to a byte variable, and if the value of the literal is within the range of byte, then the compiler automatically treats it as a byte variable. The same rule applies to short data type, if an integral literal value is assgined to a short variable, and the value of the literal is within the range of short, then the compiler automatically treats it as a short variable.

Floating-point literals

By default every floating point literal is of double data type, hence we can't assign directly to a float variable. However we can specify floating point literal as float data type by using suffix 'F' or 'f':

import java.lang.*;

class Test {
    public static void main(String[] args) {
        float a = 123.256; // INVALID: possible loss of precision, found: double, required float
        float b = 123.456F; // VALID
        double c = 123.456; // VALID
        double d = 123.456D; // VALID
    }
}

By default every floating point literal is of double data type, but we can still explicitly specify as "double" data type by using suffix 'd' or 'D'. This convention is not required because by default it is already "double" data type:

import java.lang.*;

class Test {
   public static void main(String[] args) {
       double d = 123.456D; // VALID
       float f = 123.256D; // INVALID: possible loss of precision, found: double, required float
   }
}

We can specify floating point literals can only be specified in decimal form and we can't specify in octal and hexadecimal form:

import java.lang.*;

class Test {
   public static void main(String[] args) {
       double a = 123.456; // VALID
       double b = 0123.456; // VALID - treated as decimal value (123.456)
       double c = 0x123.456; // INVALID: malformed floating point literal compile time error
   }
}

We can assign integral literal directly to floating-point variables and that integral literal can be specified either in decimal, or hexadecimal or octal forms:

import java.lang.*;

class Test {
    public static void main(String[] args) {
        double a = 0786; /*INVALID - integer number too large compile time error
                     (treated as octal integral, but wrong because 8 is not allowed as octal value)*/
        double b = 0xFace; // VALID - treated as decimal value (64206.0)
        double c = 0786.0; // VALID - treated as decimal value (786.0)
        double d = 0xFace.0; // INVALID - we can't specify floating-point value in hexadecimal form
        double e = 10; // VALID - (10.0)
        double f = 0777; // VALID - (511.0)
    }
}

We can't assign floating point literals to integral types:

import java.lang.*;

class Test {
    public static void main(String[] args) {
        double a = 10; // VALID
        int b = 10.0; // INVALID - posibble loss of precision compile time error, found: double, required: int
    }
}

We can specify floating-point literal even in exponential form (scientific notation):

import java.lang.*;

class Test {
    public static void main(String[] args) {
        double a = 1.2e3; // VALID (1.2*10³ = 1.2*1000 = 1200.0)
        float b = 1.2e3; // INVALID - possible loss of precision compile time error, found: double, required: float
        float b = 1.2e3F; // VALID
    }
}

Boolean literals

The only values allowed for "boolean" data types are "true" or "false".

import java.lang.*;

class Test {
    public static void main(String[] args) {
        boolean a = true; // VALID
        boolean b = 0; // INVALID - incompatible types compile time error, found: int, required: boolean
        boolean c = True; // INVALID - cannot find symbol compile time error, symbol: variable True, location: class Test
        boolean d = "true"; // INVALID - incompatible types compile time error, found: String, required: boolean
    }
}
import java.lang.*;

class Test {
    public static void main(String[] args) {
        int x = 0;

        if(x) { // incompatible types compile time error, found: int, required: boolean
            System.out.println("Hello");
        } else {
            System.out.println("Hi");
        }

        while(1) { // incompatible types compile time error, found: int, required: boolean
            System.out.println("Hello");
        }
    }
}

The example above would be valid in C or C++; where any value >0 is considered true, and any value <=0 is considered false.

Char literals

There are several ways to specify a literal value for character literal.

Specifying char literal a single character within single quotes

We can specify char literal as single character within single quotes:

import java.lang.*;

class Test {
    public static void main(String[] args) {
        char a = 'a'; // VALID
        char b = a; // INVALID - cannot find symbol compile time error, symbol: variable a, location: class Test
        char c = "a"; // INVALID - incompatible types compile time error, found j.l.String, required: char
        char d = 'ab'; // INVALID - unclosed char literal CTE twice(for each char) and not a statement CTE
    }
}

Specifying char literal as integral literal

Each character has a corresponding unicode value. We can specify char literal as integral literal which represents unicode value of that character. The integral literal can be specified in octal, hexadecimal or decimal forms. The allowed range for the integral literal should be 0 - 65535:

import java.lang.*;

class Test {
    public static void main(String[] args) {
        char a = 97; // VALID
        char b = 0xFace; // VALID
        char c = 0777; // VALID
        char d = 65535; // VALID
        char e = 65536; // INVALID - CTE: possible loss of precision, found: int, required: char
    }
}

When printing char literals that are specified using integral, sometimes a '?' will be displayed, this is because the required font to display that character may not be installed in the system. To check the correspondig character, see unicode.org

Specifying char literal in unicode representation

We can represent char literals in unicode representation ('\uxxxx' - 4 digit hexadecimal number):

import java.lang.*;

 class Test {
    public static void main(String[] args) {
        char a = '\u0061'; // VALID - unicode value for 'a'
        char b = '\u0062'; // VALID - unicode value for 'b'
    }
 }

Specifying char literal as escape character

Every escape character is a char literal:

import java.lang.*;

class Test {
    public static void main(String[] args) {
        char a = '\n'; // VALID
        char b = '\t'; // VALID
        char c = '\m'; // INVALID - Illegal escape character CTE
    }
}

There are a total of 8 escape characters in Java.

Escape characterDescription
\nnew line
\thorizontal tab
\rcarriage return
\bbackspace
\fform feed
\'single quote
\"double quote
\\backslash
import java.lang.*;

class Test {
    public static void main(String[] args) {
        System.out.println("' is a quote"); // INVALID
        System.out.println("\' is a quote"); // VALID
        System.out.println("" is a double quote"); // INVALID
        System.out.println("\" is a double quote"); // VALID
        System.out.println("\ is a backslash"); // INVALID
        System.out.println("\\ is a backslash"); // VALID
    }
}
import java.lang.*;

class Test {
    public static void main(String[] args) {
        char a = 65536; // INVALID - 65536 out of range
        char b = 0XBeer; // INVALID - 'r' is not a hexadecimal value
        char c = \uface; // INVALID - single quotes are missing
        char d = '\ubeef'; // VALID
        char e = '\m'; // INVLID - illegal escape character
        char f = '\iface'; // INVALID \i not valid
    }
}

String literals

Any sequence of characters within double quotes is treated as a string literal.

import java.lang.*;

class Test {
    public static void main(String[] args) {
        String a = "durga";
    }
}

1.7 enhancement with respect to literals

Binary literals

From Java 1.7 we can also specify integral literals in binary form. To specify a number a binary, it has to be prefixed with "0B" or "0b" and only contain 0's and 1's.

import java.lang.*;

class Test {
    public static void main(String[] args) {
        int a = 0B1111;
        int b = 0b1011;
    }
}

Therefore, until Java 1.6, there were only three ways (decimal, hexadecimal and octal) to specify integral values, but from Java 1.7, with the addition of binary literals, we could specify integrals in four different ways (binary, decimal, hexadecimal and octal)

Usage of underscore withing digits of numeric literals

Another enhancement of literals in Java 1.7 is usage of underscore character between digits of numberic literals. The main advantage of this approach is code readability is improved. At the time of compilation, the underscore characters will be removed automatically, hence after compilation the values will be as though they never had the underscore characters.

import java.lang.*;

class Test {
    public static void main(String[] args) {
        long a = 1_234_567_890L; // after compilation: 1234567890
        int b = 1_234_567; // after compilation: 1234567
        double c = 1_234_567.0_9_8_7; // after compilation: 1234567.0987
    }
}

We can use more than one underscore character between the digits if required to make code even more readable.

import java.lang.*;

class Test {
    public static void main(String[] args) {
        long a = 1__234__567__890L; // after compilation: 1234567890
        int b = 1__234__567; // after compilation: 1234567
        double c = 1__234__567.0__9__8__7; // after compilation: 1234567.0987
    }
}

We can use underscore character only between the digits. If underscore character is used anywhere else, we will get a compile time error.

import java.lang.*;

class Test {
    public static void main(String[] args) {
        long a = _1__234__567__890L; // INVALID
        int b = 1__234__567_; // INVALID
        double c = 1__234__567_.0__9__8__7; // INVALID
    }
}

Summary

We can assign lower data type value to higher data type variable.

Diagram showing that you can assign lower data type to higher data type variable.
Diagram showing that you can assign lower data type to higher data type variable.

8-byte long value can be assigned to 4-byte float variable because both are following different memory representations internally.

import java.lang.*;

class Test {
    public static void main(String[] args) {
        float a = 10L;

        System.out.println(a); // 10.0
    }
}

Even though short and char are both 2 bytes, they cannot be assigned to each other interchangeably, because short is signed, and byte is unsigned. Meaning they accept different range values, short accepts range -32,768 to 32,767, and char accepts range 0 to 65535.