Java · Beginner

Variables and Data Types in Java

9 min readUpdated September 24, 2026Every example verified

In short: A Java variable has a fixed type chosen when it is declared, such as int count = 0;, and can only ever hold values of that type. The eight primitive types store numbers, characters and booleans directly; every other type, including String, holds a reference to an object.

Typed variables and why they matter

Java is statically typed: every variable is declared with a type, and the compiler checks every use against it before the program runs. int seats = 42; creates a variable named seats that holds a whole number; a later seats = "many"; is refused at compile time rather than discovered as a crash in production. The type also fixes how much memory the value takes and which operations make sense on it.

Eight types are primitive, meaning the variable holds the value itself. Four hold whole numbers of increasing size: byte, short, int and long. Two hold floating-point numbers: float and double. char holds one UTF-16 character and boolean holds true or false. In everyday code int, long, double, boolean and char cover almost everything; the smaller types earn their place when data must be packed tightly.

Everything else is a reference type: the variable holds a reference to an object stored elsewhere. String is the first one you meet; arrays and every class you write are reference types too. A reference variable can hold null, meaning it points at nothing. A primitive cannot.

Literals carry types. 42 is an int; a whole number above 2,147,483,647 needs the suffix L to become a long, because it does not fit in an int at all. 2.75 is a double and 2.75f is a float. Characters use single quotes and strings double quotes: 'B' is a char, "B" is a String of length one. Underscores may separate digit groups, so 3_250_000_000L reads more easily than 3250000000L.

A declaration may include an initial value or not, but a local variable must be given a value before it is read; the compiler enforces this. Since Java 10, var lets the compiler infer the type from the initialiser: var count = 3; makes an int. The variable is still fixed to that type for its whole life; var saves typing, it does not add flexibility.

Moving a value between types is a conversion. Widening, such as int to long or int to double, happens automatically because nothing is lost. Narrowing needs an explicit cast such as (int) 35.75, which throws the fraction away rather than rounding. Arithmetic on two ints stays int, so 7 / 2 is 3; make one operand a double to get 3.5. The operators lesson covers this in detail.

Whole-number arithmetic that leaves the type's range wraps around silently instead of raising an error: two billion plus two billion in an int is a negative number. Use long for totals that may pass two billion. For money, avoid double: binary floating point cannot represent most decimal fractions exactly, so 0.1 + 0.2 is not quite 0.3. Keep money as whole cents in a long, or use BigDecimal.

Syntax

 Java · syntax
type name;                  // declare; must be assigned before use
type name = value;          // declare and initialise
var name = value;           // Java 10+: type inferred from the value

int seats = 42;
long odometerKm = 3_250_000_000L;
double fare = 2.75;
boolean electric = true;
char route = 'B';
String depot = "North Yard";
int truncated = (int) 35.75;   // explicit cast: 35

Names start with a letter, underscore or dollar sign and are case-sensitive; the convention is lowerCamelCase for variables and UPPER_SNAKE_CASE for constants declared with final.

The primitive types

TypeHoldsRange or precisionLiteral
byte8-bit integer-128 to 127(byte) 7
short16-bit integer-32,768 to 32,767(short) 7
int32-bit integer-2,147,483,648 to 2,147,483,6477
long64-bit integerabout -9.2 to 9.2 quintillion7L
float32-bit floating pointabout 7 significant decimal digits2.5f
double64-bit floating pointabout 15 to 16 significant decimal digits2.5
char16-bit UTF-16 code unit0 to 65,535'B'
booleantruth valuetrue or falsetrue

Declaring the everyday types

A bus record using six different types, printed one per line.

 Java
public class Main {
    public static void main(String[] args) {
        int seats = 42;
        long odometerKm = 3_250_000_000L;
        double fareEuro = 2.75;
        boolean electric = true;
        char route = 'B';
        String depot = "North Yard";

        System.out.println("Route " + route + " from " + depot);
        System.out.println("Seats: " + seats);
        System.out.println("Odometer: " + odometerKm + " km");
        System.out.println("Fare: " + fareEuro);
        System.out.println("Electric: " + electric);
    }
}

Output

Route B from North Yard
Seats: 42
Odometer: 3250000000 km
Fare: 2.75
Electric: true

The odometer value is above the int limit, so it needs long and the L suffix; the underscores in the literal are dropped when it is printed. Joining any value to a String with + converts it to text, so the char, the boolean and both numbers print without any extra work.

Reading typed input, casting and integer division

The program reads a passenger count and a fare. It is run with the input lines 13 and 2.75.

 Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int passengers = in.nextInt();
        double fare = in.nextDouble();
        double total = passengers * fare;
        int roundedDown = (int) total;
        System.out.println("Passengers: " + passengers);
        System.out.println("Total: " + total);
        System.out.println("Rounded down: " + roundedDown);
        int wholeShare = 7 / 2;
        double exactShare = 7 / 2.0;
        System.out.println(wholeShare);
        System.out.println(exactShare);
    }
}

Input given to the program: 132.75

Output

Passengers: 13
Total: 35.75
Rounded down: 35
3
3.5

nextInt() and nextDouble() return values of the named type, so the variables that receive them must match. Multiplying an int by a double widens the int automatically and gives a double. The cast (int) total drops the .75 without rounding. In the last two lines the only difference is 2 versus 2.0: two ints divide to an int, one double makes the whole expression a double.

Overflow, var and floating-point surprises

What happens when an int is pushed past its limit, and two things beginners are surprised by.

 Java
public class Main {
    public static void main(String[] args) {
        int big = 2_000_000_000;
        int wrapped = big + big;
        long safe = (long) big + big;
        System.out.println(wrapped);
        System.out.println(safe);
        System.out.println(Integer.MAX_VALUE);

        var label = "platform";
        var count = 3;
        System.out.println(label + " " + count);
        System.out.println(0.1 + 0.2);
    }
}

Output

-294967296
4000000000
2147483647
platform 3
0.30000000000000004

Four billion does not fit in 32 bits, so the int sum wraps around to a negative number with no warning. Casting one operand to long first makes the addition happen in 64 bits, which is why safe is right. Integer.MAX_VALUE is the largest int. var infers String and int from the initialisers. The last line shows why doubles are wrong for money: 0.1 and 0.2 are not exactly representable in binary, and the tiny errors show up in the sum.

Common mistakes

  • Reading a local variable before assigning it

    Why it goes wrong: int total; System.out.println(total); fails to compile with variable total might not have been initialized. Local variables have no automatic default; only fields and array elements do.

    Fix: Give the variable a starting value when you declare it, or make sure every path assigns it before the first read.

     Java · fix
    int total = 0;
    System.out.println(total);
  • Assigning a double to an int

    Why it goes wrong: int n = 3.7; fails with incompatible types: possible lossy conversion from double to int. Java refuses to throw away the fraction silently.

    Fix: Cast explicitly when truncation is what you want, or declare the variable as double.

     Java · fix
    int n = (int) 3.7;      // 3
    double d = 3.7;         // keeps the fraction
  • Writing a large whole number without the L suffix

    Why it goes wrong: long km = 3250000000; fails with integer number too large. The literal is parsed as an int before it is stored, and it does not fit.

    Fix: Add L: long km = 3250000000L;.

  • Calling nextLine() right after nextInt()

    Why it goes wrong: nextInt() reads the digits but leaves the line break after them in the input. The following nextLine() returns that leftover, an empty string, instead of the next line of text.

    Fix: Call in.nextLine() once to discard the rest of the number's line before reading text, or read everything with nextLine() and convert with Integer.parseInt.

     Java · fix
    int n = in.nextInt();
    in.nextLine();              // consume the rest of that line
    String name = in.nextLine();

Where you use this

Choosing types is the first design decision in any program that stores data. A stock record for a hardware shop might hold the quantity on hand as an int, the unit price in whole cents as a long so that totals stay exact, a boolean for whether the line is discontinued, a char for a single-letter size code and a String for the name. Each choice states what the value can be and stops the compiler from accepting nonsense such as a fractional quantity. A mismatch the compiler finds costs seconds; one a customer finds costs far more.

 Java · in practice
int onHand = 140;
long unitPriceCents = 1299L;
boolean discontinued = false;
char sizeCode = 'M';
String name = "hex bolt M8";
long stockValueCents = onHand * unitPriceCents;

Key points

  • Every variable has one type for life, checked by the compiler.
  • Primitives (int, long, double, boolean, char and the smaller ones) hold values; everything else, including String, holds a reference.
  • A local variable must be assigned before it is read.
  • Whole-number literals are int unless suffixed with L; decimal literals are double unless suffixed with f.
  • Widening conversions are automatic; narrowing needs a cast, and a cast truncates.
  • int arithmetic wraps around silently past 2,147,483,647; use long for large totals.
  • var (Java 10+) infers the type from the initialiser but does not make the variable dynamic.

Try it yourself

After the existing print statement, declare a double named deposit set to 12.5 and a boolean named paid set to false, then print each of them on its own line.

Your program
public class Main {
    public static void main(String[] args) {
        int price = 9;
        int quantity = 4;
        System.out.println("Total: " + price * quantity);
    }
}
Expected output: Total: 36 12.5 false

Practise this

Exercises for this lesson are in the Java practice set.

Open the Java playground

Frequently asked questions

What is the difference between int and Integer in Java?

int is a primitive that holds the number directly and can never be null. Integer is a class that wraps an int in an object, so it can be null and can be stored where an object is required, such as in an ArrayList. Java converts between them automatically (boxing and unboxing), but unboxing a null Integer throws a NullPointerException, so prefer int unless you need an object.

Should I use float or double?

Use double. It is the default type of decimal literals, has about 15 significant digits against float's 7, and modern hardware handles it just as fast. float is worth it only when memory is tight, for example in large graphics or audio buffers. Neither is exact for decimal money; use whole cents in a long or BigDecimal for that.

Progress is stored only in this browser.

How this page was checked. Every program on it was run with OpenJDK 21 at build time by the publishing checks, and the output shown is what it printed. Running Java inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with OpenJDK 21 locally.