Java 7: Underscores in Numeric Literals
Reading long numerical values in source code has historically been a challenge for developers. When encountering a number like 1000000000 or a long hexadecimal or binary string, it requires careful counting of zeroes or digits to determine the exact magnitude or significance of the value. This visual clutter can easily lead to subtle bugs, such as mistaking a million for ten million due to a single missing or extra zero.
To solve this readability issue, Java 7 introduced the ability to place underscores (_) between digits in numeric literals. This feature acts similarly to how commas or periods are used in human-written numbers (e.g., 1,000,000) to separate thousands, millions, or logical groups of digits. By allowing these separators, developers can write code that is much easier to read and verify at a glance.
Underscores can be used in any type of numeric literal in Java, including integer, floating-point, hexadecimal, octal, and binary literals. The Java compiler completely ignores these underscores during the compilation phase, meaning they have absolutely no impact on the execution performance or the compiled bytecode. They exist purely as syntactic sugar for developer convenience.
However, there are strict rules governing where underscores can be placed. They can only be placed between digits. You cannot place an underscore at the beginning or end of a number, adjacent to a decimal point, prior to a literal suffix like F or L, or immediately following prefixes like 0x or 0b. Adhering to these rules ensures that the parser can accurately interpret the numeric value.
How it Works
Simply insert one or more underscores between the digits of a numeric literal to group them logically. For example, a credit card number or a large monetary amount can be broken down into recognizable chunks. The compiler strips out the underscores before generating the bytecode.
Java Example
Scenario 1: Large Integer and Floating-Point Numbers
public class UnderscoreLiteralExample1 {
public static void main(String[] args) {
long oneBillion = 1_000_000_000L;
double largeDecimal = 123_456.789_012;
System.out.println("One Billion: " + oneBillion);
System.out.println("Large Decimal: " + largeDecimal);
}
}
Output
One Billion: 1000000000
Large Decimal: 123456.789012
Scenario 2: Hexadecimal and Binary Grouping
public class UnderscoreLiteralExample2 {
public static void main(String[] args) {
int hexColor = 0xFF_AA_CC_00; // Grouping hex by color channels
int binaryMask = 0b1101_0010_1011_0001; // Grouping binary by nibbles
System.out.println("Hex Value: " + Integer.toHexString(hexColor));
System.out.println("Binary Value: " + Integer.toBinaryString(binaryMask));
}
}
Output
Hex Value: ffaacc00
Binary Value: 1101001010110001
Scenario 3: Real-world Groupings (Card Numbers / IDs)
public class UnderscoreLiteralExample3 {
public static void main(String[] args) {
long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityFormat = 999_99_9999L;
System.out.println("Credit Card: " + creditCardNumber);
System.out.println("ID Format: " + socialSecurityFormat);
}
}
Output
Credit Card: 1234567890123456
ID Format: 999999999
Key Points
- Dramatically improves readability of large numerical literals.
- Underscores are ignored by the compiler and do not affect performance.
- Cannot be placed at the start, end, next to a decimal, or next to type suffixes/prefixes.
- Useful for formatting thousands, hex channels, or grouping bits into bytes/nibbles.
Comments