Skip to content
elephantoo

Variables & data types

Lesson 2 of 43 14 min read

Primitive types, Strings, var, casting and the integer-division trap.


Programs work with data. In Java every piece of data has a type, which tells the compiler what kind of values it can hold and which operations make sense. Java checks types at compile time, so many mistakes are caught before your program ever runs.

Declaring variables#

A variable is a named box that holds a value:

Java
int age = 21;
double price = 19.99;
boolean isStudent = true;
String name = "Ada";
  • int age declares a variable of type int named age.
  • = 21 assigns it the value 21.
  • You can declare first and assign later (int age; age = 21;), but a local variable must be assigned before you read it, otherwise the compiler complains variable age might not have been initialized.

You can change a variable's value later, but never its type:

Java
int score = 10;
score = 25;        // fine
// score = "high"; // compile error: incompatible types

Naming rules and conventions

  • Names may contain letters, digits, _ and $, but cannot start with a digit.
  • They cannot be keywords such as class, int or new.
  • Java is case-sensitive: total and Total are different variables.
  • Convention: variables and methods use camelCase (totalPrice), constants use UPPER_SNAKE_CASE (MAX_USERS), classes use PascalCase (BankAccount).

var: local type inference

From Java 10 you can write var when the type is obvious from the right-hand side:

Java
var greeting = "Hello";   // inferred as String
var count = 3;            // inferred as int
var ratio = 0.75;         // inferred as double

The variable still has a fixed static type; var just saves typing. It only works for local variables that are initialised on the same line. Use it when it improves readability, not to hide important types.

Primitive types#

Java has eight primitive types built into the language:

TypeSizeRange / exampleNotes
byte8-bit-128 to 127Raw binary data
short16-bit-32,768 to 32,767Rare
int32-bitabout ±2.1 billionDefault for whole numbers
long64-bit9_000_000_000LNeeds the L suffix for big literals
float32-bit3.14fNeeds the f suffix
double64-bit3.14159Default for decimals
char16-bit'A'One UTF-16 character, single quotes
booleann/atrue / falseOnly these two values

Underscores in numbers (2_000_000) are only for readability; the compiler ignores them.

Everything that is not a primitive is a reference type: String, arrays, and every class you will write. A reference variable holds a pointer to an object rather than the value itself, and can be null (pointing at nothing).

Strings#

Text uses the String class (capital S, it is not a primitive):

Java
String first = "Elephant";
String second = "oo";
String brand = first + second;           // "Elephantoo"
int length = brand.length();             // 10
String upper = brand.toUpperCase();      // "ELEPHANTOO"
boolean starts = brand.startsWith("El"); // true

Strings are immutable: methods like toUpperCase() return a new string and never change the original. The + operator joins strings, and converts other values to text automatically: "Age: " + 21 gives "Age: 21". There is a whole lesson on strings later.

Casting and conversion#

Java converts automatically when no information can be lost (widening), and demands an explicit cast when it might be (narrowing):

Output
byte -> short -> int -> long -> float -> double     (widening: automatic)
                  char -> int
Conversions.java
public class Conversions {
    public static void main(String[] args) {
        int whole = 5;
        double decimal = whole;           // widening, automatic
        int back = (int) 3.99;            // narrowing, explicit cast, truncates
        long big = 3_000_000_000L;
        int broken = (int) big;           // too big for int: wraps around!

        String text = String.valueOf(42); // number -> String
        int parsed = Integer.parseInt("42");
        double price = Double.parseDouble("19.99");

        char letter = 'A';
        int code = letter;                // chars are numbers underneath
        char next = (char) (letter + 1);

        System.out.println(decimal);
        System.out.println(back);
        System.out.println(broken);
        System.out.println(text + parsed + price);
        System.out.println(code + " " + next);
    }
}
Output
5.0
3
-1294967296
424219.99
65 B

Notice text + parsed + price: because it starts with a String, every + means concatenation, giving "42" + "42" + "19.99".

The integer-division trap#

This surprises almost every beginner:

Division.java
public class Division {
    public static void main(String[] args) {
        System.out.println(5 / 2);          // both int
        System.out.println(5.0 / 2);
        System.out.println(5 / 2.0);
        System.out.println((double) 5 / 2);
        System.out.println((double) (5 / 2)); // too late: division already happened
        System.out.println(7 % 3);          // remainder
    }
}
Output
2
2.5
2.5
2.5
2.0
1

If both operands are integers, Java does integer division and throws the fraction away. Cast at least one side to double before dividing when you want a fractional result.

Overflow and floating-point surprises#

Primitive numbers have limits. Go past them and they silently wrap around:

Limits.java
public class Limits {
    public static void main(String[] args) {
        int max = Integer.MAX_VALUE;
        System.out.println(max);
        System.out.println(max + 1);          // overflow!
        System.out.println(0.1 + 0.2);        // binary floating point
        System.out.println(Math.addExact(2, 3));
    }
}
Output
2147483647
-2147483648
0.30000000000000004
5
  • Use long when values might exceed about two billion (e.g. milliseconds, file sizes, populations).
  • Math.addExact and Math.multiplyExact throw an exception instead of silently overflowing.
  • double cannot store most decimal fractions exactly. Never use double for money; use long cents or java.math.BigDecimal.

Constants with final#

Java
final double PI = 3.14159;
// PI = 3;  // compile error: cannot assign a value to final variable PI

final means "assigned exactly once". Constants shared across a class are usually declared static final (you will meet static later), for example static final int MAX_USERS = 100;.

Putting it together#

Profile.java
public class Profile {
    public static void main(String[] args) {
        final String COURSE = "Java";
        int lessons = 43;
        double minutesEach = 15.5;
        var totalHours = lessons * minutesEach / 60;
        boolean isLong = totalHours > 10;
        char grade = 'A';

        System.out.println("Learning " + COURSE);
        System.out.println("Total hours: " + totalHours);
        System.out.println("Long course? " + isLong);
        System.out.println("Target grade: " + grade);
    }
}
Output
Learning Java
Total hours: 11.108333333333333
Long course? true
Target grade: A

Common mistakes#

  • Forgetting L on large literals: long x = 9000000000; does not compile, because the literal is treated as an int.
  • Writing float f = 1.5;: decimal literals are double, so write 1.5f.
  • Using "A" (a String) where you meant 'A' (a char).
  • Expecting 5 / 2 to be 2.5.
  • Comparing Strings with == instead of .equals(). More on this in the strings lesson.

What's next#

You can now store and convert data. Next we combine values with operators: arithmetic, comparisons, logic and more.

Check your understanding

Quick quiz

0/3 answered
  1. 1.What is the result of 5 / 2 when both numbers are int?

  2. 2.Which keyword declares a variable whose value cannot be reassigned?

  3. 3.Which declaration does NOT compile?

Finished reading?

Mark this lesson complete to track your progress.