Java Variables

Su Suriya Ravichandran Updated 14 Nov 2026
8 min read ·Lesson 7 of 133

A variable is a named location in memory that holds a value of a specific type. Java requires every variable to be declared with its type before use, and the type never changes for the life of the variable. This is the foundation of Java's static type system.

Declaring and Initializing

int age;              // declaration
age = 30;             // assignment
int year = 2026;      // declaration + initialization in one statement

The form is type name = value;. The type tells the compiler how much memory to reserve and which operations are legal. age = "thirty"; is rejected because a String cannot be stored in an int.

Multiple variables of one type can be declared together:

int x = 1, y = 2, z = 3;
double width, height;

Reassignment

A variable's value can be replaced any number of times, but always with a value of the same type:

int score = 10;
score = 25;
score = score + 5;   // 30
score = 3.5;         // compile error: double cannot be assigned to int

Primitive and Reference Variables

Java has two fundamentally different kinds of variables.

A primitive variable stores the value itself directly: int, long, short, byte, double, float, boolean, char.

A reference variable stores a reference (an address) to an object that lives elsewhere in memory: String, arrays, and every class type.

int a = 5;
int b = a;      // b receives a COPY of the value 5
b = 10;
System.out.println(a);  // 5: a is unaffected

int[] first = {1, 2, 3};
int[] second = first;   // second receives a COPY of the REFERENCE
second[0] = 99;
System.out.println(first[0]);  // 99: both variables point at the same array

Copying a primitive copies the value. Copying a reference copies the address, so both variables name the same object, and a change through one is visible through the other. This distinction explains most confusion about "why did my other variable change" in Java.

A reference variable may hold null, meaning it refers to no object. Using a null reference (calling a method on it, accessing a field) throws NullPointerException. Primitives can never be null.

String name = null;
System.out.println(name.length());   // NullPointerException at runtime

Local Variables, Fields, and Parameters

Where a variable is declared determines its lifetime, default value, and who can see it.

public class Counter {
    private int count = 0;           // field (instance variable)
    private static int instances;    // static field (class variable)

    public Counter() {
        instances++;
    }

    public void add(int amount) {    // parameter
        int previous = count;        // local variable
        count += amount;
        System.out.println(previous + " -> " + count);
    }
}
KindDeclared inLifetimeDefault valueVisibility
Local variableMethod, constructor, or block bodyUntil the block endsNone; must be assigned before useThe enclosing block only
ParameterMethod or constructor headerDuring the callThe argument passedThe method body
Instance fieldClass body, no staticAs long as the object exists0, 0.0, false, '\u0000', or nullDepends on access modifier
Static fieldClass body, with staticAs long as the class is loadedSame defaults as instance fieldsDepends on access modifier

Why locals have no default

Fields are zeroed when an object is created, so reading an uninitialized field yields 0 or null. Local variables are not, and the compiler enforces assignment before any read:

public int compute() {
    int result;
    return result;   // compile error: variable result might not have been initialized
}

This is a deliberate safety rule. An uninitialized local almost always indicates a logic error, and catching it at compile time is cheaper than debugging a mysterious zero at runtime.

Instance versus static fields

Each object has its own copy of every instance field. There is exactly one copy of a static field, shared by all objects of the class.

Counter c1 = new Counter();
Counter c2 = new Counter();
c1.add(5);
c2.add(7);
// c1.count is 5, c2.count is 7, Counter.instances is 2

Static fields are accessed through the class name (Counter.instances). They suit values that belong to the type as a whole: configuration constants, counters, caches.

Scope

A variable's scope is the region of code where its name is valid. A local variable's scope begins at its declaration and ends at the closing brace of the block containing it.

public void demo() {
    int outer = 1;
    if (outer > 0) {
        int inner = 2;
        System.out.println(outer + inner);   // both visible
    }
    System.out.println(inner);   // compile error: inner is out of scope
}

A loop variable declared in a for header is scoped to the loop:

for (int i = 0; i < 3; i++) { }
System.out.println(i);   // compile error

Two local variables with the same name cannot overlap in scope. A local may not redeclare a name already visible from an enclosing block:

int value = 1;
if (true) {
    int value = 2;   // compile error: value is already defined
}

A local may have the same name as a field. The local then shadows the field within its scope, and this.fieldName reaches the field:

public class Point {
    private int x;

    public void setX(int x) {
        this.x = x;     // this.x is the field, x is the parameter
    }
}

Without this., the line would read x = x;, assigning the parameter to itself and leaving the field unchanged. This pattern is universal in setters and constructors.

Constants with final

Declaring a variable final means it can be assigned exactly once.

final int MAX_RETRIES = 3;
MAX_RETRIES = 5;   // compile error: cannot assign a value to final variable

A final local can be declared without a value and assigned later, as long as it is assigned on every path before use and only once. A final field must be assigned either at declaration or in every constructor.

Class-level constants combine static and final:

public class Config {
    public static final int DEFAULT_PORT = 8080;
    public static final String APP_NAME = "Inventory";
}

static gives one shared copy; final prevents modification; the UPPER_SNAKE_CASE name signals to readers that this is a constant. The compiler inlines primitive and String constants of this form directly into code that uses them.

final on a reference variable prevents reassigning the variable, not modifying the object:

final List<String> names = new ArrayList<>();
names.add("Ada");            // allowed: the list object is mutated
names = new ArrayList<>();   // compile error: the variable cannot be reassigned

Type Inference with var

Since Java 10, var lets the compiler infer the type of a local variable from its initializer.

var count = 10;                          // int
var name = "Ada";                        // String
var scores = new HashMap<String, Integer>();   // HashMap<String, Integer>

The variable is still statically typed; var only saves writing the type. The inferred type is fixed at declaration:

var count = 10;
count = "ten";   // compile error: String cannot be converted to int

Restrictions:

  • Only for local variables (including for loop variables and try-with-resources). Not fields, parameters, or return types.
  • An initializer is required: var x; is an error.
  • Cannot be initialized with null alone, since no type can be inferred.
  • Cannot declare arrays with the [] after the name: var arr = new int[5]; works, var arr[] = ... does not.

var is most useful when the type is long and already visible on the right (var reader = new BufferedReader(new FileReader(path));) and least useful when it hides information (var result = service.process(); tells the reader nothing about what result is).

Naming Variables

Variable names follow the identifier rules (start with a letter, _, or $; then letters, digits, _, $; not a keyword) and by convention use camelCase:

int itemCount;
double unitPrice;
boolean isActive;
String customerEmail;

Boolean variables often read as questions (isReady, hasPermission, canRetry) so that conditions read naturally: if (isReady && hasPermission).

Names should state what the value represents, not its type. int n or String str forces every reader to work out the meaning from context; int retryLimit or String recipientAddress does not. Single letters are acceptable only for loop counters (i, j) and short lambda parameters.

Practical Example

public class ShoppingCart {
    private static final double TAX_RATE = 0.18;   // constant shared by all carts
    private static int cartsCreated = 0;           // shared counter

    private double subtotal;                       // per-cart state, defaults to 0.0
    private final String owner;                    // set once in the constructor

    public ShoppingCart(String owner) {
        this.owner = owner;
        cartsCreated++;
    }

    public void addItem(double price, int quantity) {
        double lineTotal = price * quantity;       // local, exists only in this call
        subtotal += lineTotal;
    }

    public double total() {
        var tax = subtotal * TAX_RATE;             // inferred as double
        return subtotal + tax;
    }

    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart("Ada");
        cart.addItem(100.0, 2);
        cart.addItem(50.0, 1);
        System.out.printf("Total for %s: %.2f%n", cart.owner, cart.total());
        System.out.println("Carts created: " + cartsCreated);
    }
}

Output:

Total for Ada: 295.00
Carts created: 1

TAX_RATE never changes and belongs to the class. cartsCreated is shared and increments for every cart. subtotal is per-object and starts at its default 0.0. owner is per-object and final, so it cannot be changed after construction. lineTotal and tax are locals that exist only while their method runs. Each variable kind is chosen by how long the value needs to live and who needs to see it.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Java Variables and get answers drawn from it.

Signed-in readers only.