DEV Community

Mayur Bodhare
Mayur Bodhare

Posted on

Java Variables, Data Types and Type Casting: A Simple Guide with Examples

Every program needs to remember things: a user's age, a price, a name, a yes/no answer. In Java, you do this with variables. But Java is strict. Before you store anything, you have to say what kind of thing it is.

That strictness confused me at first. Why can't I just put a decimal number into an int? Why does byte c = a + b give an error? In this post, we'll go through all of this step by step, with small examples you can run yourself.

What is a variable?

A variable is a named box in memory that holds a value. To make one, you write the type, then the name, then (optionally) the value.

int age = 25;
double price = 99.50;
String name = "Mayur";
boolean isStudent = false;
Enter fullscreen mode Exit fullscreen mode

You can also declare first and give the value later:

int score;      // declaration
score = 90;     // initialization
Enter fullscreen mode Exit fullscreen mode

One rule to remember: a local variable (one inside a method) must be given a value before you use it. Java will not guess a default for you.

int x;
System.out.println(x); // Error: variable x might not have been initialized
Enter fullscreen mode Exit fullscreen mode

Naming rules

  • Can contain letters, digits, _ and $
  • Cannot start with a digit
  • Cannot be a Java keyword (like class or int)
  • Is case sensitive (age and Age are different)

By convention, Java uses camelCase for variables, like studentAge or totalPrice.

Constants with final

If a value should never change, add final:

final double PI = 3.14159;
PI = 3.14; // Error: cannot assign a value to final variable
Enter fullscreen mode Exit fullscreen mode

Data types in Java

Java has two groups of data types:

  1. Primitive types: simple values stored directly (numbers, characters, true/false)
  2. Reference types: objects, like String, arrays and classes

There are exactly 8 primitive types:

Type Size What it stores Range / example
byte 8 bits Tiny whole numbers -128 to 127
short 16 bits Small whole numbers -32,768 to 32,767
int 32 bits Normal whole numbers about -2.1 billion to 2.1 billion
long 64 bits Very big whole numbers about -9.2 × 10^18 to 9.2 × 10^18
float 32 bits Decimals (about 6-7 digits of precision) 3.14f
double 64 bits Decimals (about 15-16 digits of precision) 3.14159265358979
char 16 bits A single character 'A', '7', '$'
boolean not fixed true or false true, false

Some quick tips:

  • Use int for whole numbers and double for decimals unless you have a reason not to.
  • Use long when numbers can go beyond about 2 billion, like timestamps or phone-call counters.
  • String is not a primitive. It's a class, which is why it starts with a capital S.

Literals: the values you type in code

A literal is a fixed value written directly in your code. In int age = 25;, the 25 is a literal.

int a = 100;            // integer literal
long b = 3000000000L;   // long literal (note the L)
float c = 3.14f;        // float literal (note the f)
double d = 3.14;        // double literal (decimals are double by default)
char e = 'A';           // char literal (single quotes)
boolean f = true;       // boolean literal
String g = "Hello";     // String literal (double quotes)
Enter fullscreen mode Exit fullscreen mode

Two mistakes beginners make all the time:

long big = 3000000000;  // Error: integer number too large
float pi = 3.14;        // Error: possible lossy conversion from double to float
Enter fullscreen mode Exit fullscreen mode

The reason: Java treats whole-number literals as int and decimal literals as double by default. So 3000000000 is too big for an int, and 3.14 is a double. Fix them by adding L and f:

long big = 3000000000L;
float pi = 3.14f;
Enter fullscreen mode Exit fullscreen mode

Underscores for readability

Big numbers are hard to read. Java lets you add underscores, and it ignores them:

int million = 1_000_000;
long phone = 98_7654_3210L;
Enter fullscreen mode Exit fullscreen mode

Binary, octal and hex literals

Java lets you write whole numbers in other number systems. You just add a prefix.

System Prefix Digits used Example Value in decimal
Decimal none 0-9 25 25
Binary 0b 0, 1 0b1010 10
Octal 0 0-7 017 15
Hex 0x 0-9, A-F 0xFF 255
int dec = 10;
int bin = 0b1010;      // binary for 10
int oct = 017;         // octal for 15 (careful: leading zero!)
int hex = 0xFF;        // hex for 255

System.out.println(dec); // 10
System.out.println(bin); // 10
System.out.println(oct); // 15
System.out.println(hex); // 255
Enter fullscreen mode Exit fullscreen mode

Watch out for the leading zero. 017 is not seventeen. It's octal, so it equals 15. This catches people out.

You can also use underscores in binary and hex to make them easier to read:

int mask = 0b1111_0000;        // 240
int color = 0xFF_A5_00;        // an orange color code
Enter fullscreen mode Exit fullscreen mode

Why would you ever use binary or hex?

  • Binary is great when you care about individual bits, like flags and permissions.
  • Hex is a short way to write binary. One hex digit equals exactly 4 bits, so 0xFF is 1111 1111. You'll see hex in color codes, memory addresses and file formats.

Converting numbers to binary and hex strings

Java gives you ready-made methods to see a number in another system:

System.out.println(Integer.toBinaryString(10));  // 1010
System.out.println(Integer.toHexString(255));    // ff
System.out.println(Integer.toOctalString(15));   // 17
Enter fullscreen mode Exit fullscreen mode

And to go the other way, from a string to a number:

int n = Integer.parseInt("1010", 2);   // binary string to int -> 10
int m = Integer.parseInt("ff", 16);    // hex string to int -> 255
Enter fullscreen mode Exit fullscreen mode

A small surprise with negative numbers

Java stores negative whole numbers using a method called two's complement. You don't need to know the math. Just know this: -1 in an int is all 32 bits set to 1.

System.out.println(Integer.toHexString(-1));  // ffffffff
System.out.println(Integer.toBinaryString(-1)); // 32 ones
Enter fullscreen mode Exit fullscreen mode

Type conversion (widening)

Sometimes you put a smaller type into a bigger one. Java does this for you automatically, with no extra code. This is called type conversion, or widening, or implicit conversion.

Think of pouring water from a small cup into a big jug. Nothing is lost, so Java says yes.

The widening order is:

byte -> short -> int -> long -> float -> double
              char -> int
Enter fullscreen mode Exit fullscreen mode

Examples:

int i = 100;
long l = i;       // int to long: fine
double d = l;     // long to double: fine

System.out.println(l); // 100
System.out.println(d); // 100.0
Enter fullscreen mode Exit fullscreen mode

char also widens to int, which gives you its numeric code:

char c = 'A';
int code = c;
System.out.println(code); // 65
Enter fullscreen mode Exit fullscreen mode

One catch: precision can be lost

Widening from int or long to float is allowed, but a float has limited precision, so big numbers can get slightly changed:

int big = 123456789;
float f = big;
System.out.println(f);  // 1.23456792E8
Enter fullscreen mode Exit fullscreen mode

The value is close, but not the same. If you need exact results, use double, or long for whole numbers.

Type casting (narrowing)

Now the other direction. Putting a bigger value into a smaller type is like pouring the jug into a small cup. Some water may spill. Java won't do it silently. You must say it clearly with a cast, by writing the target type in brackets.

double d = 9.99;
int i = (int) d;
System.out.println(i);  // 9
Enter fullscreen mode Exit fullscreen mode

Notice that 9.99 became 9, not 10. Casting a decimal to a whole number cuts off the decimal part. It does not round.

System.out.println((int) 9.99);   // 9
System.out.println((int) -9.99);  // -9
Enter fullscreen mode Exit fullscreen mode

If you want rounding, use Math.round():

System.out.println(Math.round(9.99)); // 10
Enter fullscreen mode Exit fullscreen mode

When the number doesn't fit

When a big whole number is cast into a smaller type, it wraps around. Only the lowest bits are kept.

int big = 130;
byte b = (byte) big;
System.out.println(b);  // -126
Enter fullscreen mode Exit fullscreen mode

Why? A byte can only hold -128 to 127. 130 is 3 past 127, so it wraps around to -126. Another example:

long l = 3_000_000_000L;
int x = (int) l;
System.out.println(x);  // -1294967296
Enter fullscreen mode Exit fullscreen mode

Java doesn't warn you here. It just gives you the wrong number. That's why you should only cast when you are sure the value fits.

Here's a handy hex example. 0xFF is 255, but a byte can't hold 255:

byte b = (byte) 0xFF;
System.out.println(b);  // -1
Enter fullscreen mode Exit fullscreen mode

The bits are 1111 1111, and in a signed byte that means -1.

Type promotion in expressions

This one surprises almost everyone. Look at this code:

byte a = 10;
byte b = 20;
byte c = a + b;  // Error: possible lossy conversion from int to byte
Enter fullscreen mode Exit fullscreen mode

Both a and b are bytes, so why does it fail? Because in math expressions, Java first promotes byte, short and char to int. So a + b is an int, and you can't put an int into a byte without a cast.

The fix:

byte c = (byte) (a + b);  // 30
Enter fullscreen mode Exit fullscreen mode

The general rules for promotion:

  • byte, short and char become int first
  • If one side is long, the result is long
  • If one side is float, the result is float
  • If one side is double, the result is double
int i = 5;
double d = 2.0;
System.out.println(i / d);   // 2.5 (int is promoted to double)
System.out.println(5 / 2);   // 2   (both are int, so decimals are dropped)
System.out.println(5 / 2.0); // 2.5
Enter fullscreen mode Exit fullscreen mode

The += exception

Compound assignment operators like += quietly add the cast for you:

byte b = 10;
b += 5;        // works fine, b is now 15
// b = b + 5;  // Error, needs a cast
Enter fullscreen mode Exit fullscreen mode

Common mistakes to avoid

1. Integer overflow. Numbers wrap around at their limit, with no error:

int max = Integer.MAX_VALUE;   // 2147483647
System.out.println(max + 1);   // -2147483648
Enter fullscreen mode Exit fullscreen mode

2. Decimal surprises. Computers store decimals in binary, so some values are not exact:

System.out.println(0.1 + 0.2);  // 0.30000000000000004
Enter fullscreen mode Exit fullscreen mode

For money, use BigDecimal instead of double.

3. Integer division. Dividing two int values gives an int:

System.out.println(1 / 2);   // 0, not 0.5
Enter fullscreen mode Exit fullscreen mode

4. Casting char arithmetic. Adding to a char gives an int, so cast it back:

char c = 'A';
char next = (char) (c + 1);
System.out.println(next);  // B
Enter fullscreen mode Exit fullscreen mode

5. boolean is special. You can't convert it to or from numbers, with or without a cast. int x = (int) true; will not compile.

Quick recap

  • A variable is a named box with a type: int age = 25;
  • Java has 8 primitive types: byte, short, int, long, float, double, char, boolean.
  • A literal is a value written in code. Use L for long and f for float.
  • Use 0b for binary, 0x for hex, and a leading 0 for octal. Underscores like 1_000_000 make numbers easier to read.
  • Type conversion (widening) is automatic and safe: int to long to double.
  • Type casting (narrowing) is manual and risky: (int) 9.99 gives 9, and (byte) 130 gives -126.
  • In expressions, byte, short and char are promoted to int first.

Once you understand that every type is just a box of a certain size, the rules start to feel logical. Small box into big box is safe. Big box into small box is your responsibility.

If this helped, or if I missed something, let me know in the comments. I'm still learning too. 🙂

Top comments (0)