Variables and Constants in Java

  • Last Updated: July 4, 2023
  • By: javahandson
  • Series
img

Variables and Constants in Java

Learn how variables and constants in Java work: declaration, initialization, the three variable types, scope, the final keyword, and the naming rules every beginner should know.

1. Introduction

Variables and constants in Java are the very first things you will write in any program. A variable holds a value that can change. A constant holds a value that must stay put.

Think about a scoreboard at a cricket match. The score changes ball by ball, so that is a variable. The number of players in a team stays at eleven, so that is a constant.

Your Java code works the same way. Some values move around while the program runs. Others are fixed the moment you set them, and any attempt to change them should fail loudly.

Getting this distinction right early saves you real pain later. Many bugs come from a value changing when nobody expected it to change.

1.1 What This Article Covers

We will start from the basics and build up slowly. Here is the plan:

  • What a variable really is, and what happens in memory
  • Declaration and initialization, and why they are two different steps
  • The three kinds of variables: local, instance, and static
  • Scope, and how one name can hide another
  • The final keyword at the variable, method, and class level
  • Why final does not always mean unchangeable
  • Naming rules the compiler enforces, and conventions your team expects
  • Common mistakes, plus the interview questions that keep showing up

You do not need any prior knowledge beyond basic data types. If you know what an int is, you are ready.

2. What Is a Variable?

2.1 A Named Box in Memory

A variable is a named box in memory. You put a value inside, and you use the name to find it again.

The computer does not really care about names. It works with raw memory addresses, something ugly like 0x4560XT. Nobody wants to type that.

So Java lets you hand that address a friendly label. You write score, and the compiler quietly maps it to the right spot in memory.

Java calls that label an identifier. Pick a good one, because you will read it far more often than you write it.

2.2 Why the Value Can Change

The word “variable” comes from “vary”. The value inside the box can vary while the program runs.

Say you store 50 in a box named result. Later you add 50 more. Now the same box holds 100, and the name never changed.

int result = 50;
result = result + 50;
System.out.println(result); // Output: 100

Notice what happened on line two. Java read the old value, added 50, then wrote the answer back into the same box.

2.3 The Data Type Decides the Size

Every variable needs a data type. The type tells Java how big the box should be.

  • byte reserves 1 byte of memory
  • int reserves 4 bytes
  • long reserves 8 bytes
  • double reserves 8 bytes
  • boolean needs just a single bit of information

Pick a type that fits the data. Storing someone’s age in a long works, but it wastes seven bytes for no reason.

Pick a type that is too small, though, and the compiler stops you. A byte cannot hold 200, and Java will not let you pretend otherwise.

3. Declaring and Initializing

These two words get mixed up constantly. They are not the same thing, and the difference matters.

3.1 Declaration: Reserving the Space

Declaration is when you tell Java the type and the name. That reserves the memory, but puts nothing in it yet.

int result;      // declared, but empty
String name;     // declared, but empty

The syntax is simple. Write the data type, then a space, then your chosen name.

You must declare a variable before you use it. Skip this step and the compiler throws an error straight away.

3.2 Initialization: Filling the Box

Initialization is when you actually put a value inside. It uses the equals sign.

int result;
result = 50;   // now it holds something

Until that second line runs, the box sits empty. Reading it early is a mistake we will come back to in section 10.

3.3 Doing Both in One Line

Most of the time you declare and initialize together. It is shorter, and it removes any chance of reading an empty box.

int result = 50;
String name = "Rohit";
double price = 19.99;
boolean active = true;

Prefer this style whenever you can. Fewer lines, and fewer ways to get it wrong.

3.4 Updating a Variable Later

Once a variable holds a value, you can overwrite it as often as you like.

int score = 10;
score = 20;        // replaced
score = score + 5; // read, add, write back
System.out.println(score); // Output: 25

Each assignment throws away the old value. There is no history, and no undo.

4. The Three Types of Variables

Java sorts variables by where you declare them. That position changes their lifetime, their memory, and their default value.

4.1 Local Variables

A local variable lives inside a method, a constructor, or a block. It is born when the method starts, and it dies when the method ends.

Here is the rule that trips up every beginner. Local variables get no default value. You must assign one yourself before reading it.

void calculate() {
    int total;              // no default value
    total = 10 + 20;        // assign before use
    System.out.println(total); // Output: 30
}

Try to print total before assigning it, and the code will not compile. Java would rather stop you now than hand you garbage later.

4.2 Instance Variables

An instance variable sits inside the class but outside any method. Every object gets its own private copy.

public class Student {
    int rollNumber;   // instance variable, defaults to 0
    String name;      // instance variable, defaults to null
}

Create two Student objects and you get two separate rollNumber boxes. Change one, and the other never notices.

Unlike local variables, these do get default values:

  • Numbers default to 0 or 0.0
  • boolean defaults to false
  • Objects like String default to null

4.3 Static Variables

Add the static keyword and everything changes. Now the class keeps only one copy, and every object shares it.

public class Student {
    static String schoolName = "Java High"; // shared by all
    int rollNumber;                          // one per object
}

The JVM sets aside memory for a static variable once, right when the class loads. It does not wait for an object.

Change schoolName through one student, and every student sees the new value. That is the whole point, and also the whole danger.

4.4 A Quick Comparison

Aspect Local Instance Static
Declared in A method or block The class body The class body, with static
Default value None Yes (0, false, null) Yes (0, false, null)
How many copies One per method call One per object One per class
Lives until The method ends The object is collected The class unloads

If you remember one row, make it the second. Local variables never get a freebie.

5. Scope and Shadowing

5.1 Where a Variable Lives

Scope means the region of code where a name is visible. Step outside that region, and the name simply does not exist.

A variable declared inside a loop dies at the closing brace. Ask for it afterwards, and the compiler shrugs.

for (int i = 0; i < 3; i++) {
    int doubled = i * 2;   // scope: inside the loop only
}
// System.out.println(doubled); // error: cannot find symbol

Narrow scope is a good thing. Fewer places to touch a variable means fewer places for a bug to hide.

5.2 When Two Names Collide

Java always looks at the narrowest scope first. So a local variable can hide an instance variable that shares its name.

That hiding has a name: shadowing. It is legal, and it is confusing.

public class Shadow {
    int value = 10;   // instance variable

    void show(int value) {   // parameter shadows it
        System.out.println(value);      // Output: 50
        System.out.println(this.value); // Output: 10
    }

    public static void main(String[] args) {
        new Shadow().show(50);
    }
}

5.3 Using this to Break the Tie

The keyword this means “the current object”. Put it in front of a name, and Java skips the local scope entirely.

You will see this constantly in setter methods, where the parameter deliberately matches the field name.

public void setName(String name) {
    this.name = name;  // field = parameter
}

Drop the this there and the line becomes name = name. It compiles, does nothing useful, and the field stays null.

6. Constants and the final Keyword

A constant is a value that must not change once set. Java gives you one keyword for the job: final.

You can apply it in three places, and it means something slightly different in each.

6.1 Why Bother With Constants?

Beginners often ask a fair question. Why not just use a normal variable and promise not to change it?

Because promises break. Constants give you three concrete wins:

  • Safety. The compiler catches an accidental change, so the bug never reaches production
  • Clarity. Anyone reading MAX_SPEED knows the value is fixed, without hunting through the file
  • One source of truth. Change the tax rate in one place, and every calculation updates

That last point matters most in real projects. Scatter the number 0.18 across twenty methods, and one day you will update nineteen of them.

Give it a name once, and the problem disappears for good.

6.2 final at the Variable Level

Mark a variable final, and you get exactly one chance to assign it.

final int RESULT = 50;
// RESULT = RESULT + 10;  // error: cannot assign a value to final variable

You can also declare it first and assign it later. Java simply insists that it happens once.

final int limit;
limit = 100;   // fine, first assignment
// limit = 200;  // error, second assignment

6.3 final at the Method Level

Subclasses can call a final method, but they can never override it. The logic stays exactly as you wrote it.

Use this when the behaviour must never drift. Adding two numbers, for instance, has exactly one correct answer.

public class Addition {
    final int add(int a, int b) {
        return a + b;
    }
}

class Operation extends Addition {
    int add(int a, int b) {   // error
        return a * b;
    }
}
// Output: add(int,int) in Operation cannot override add(int,int) in Addition
//         overridden method is final

6.4 final at the Class Level

Nobody can extend a final class. Inheritance stops right there.

final public class Addition {
    int add(int a, int b) { return a + b; }
}

class Operation extends Addition { }
// Output: cannot inherit from final Addition

Java uses this trick itself. The String class is final, which is part of why strings behave so predictably.

6.5 A Recap of final

  • On a variable, final blocks any second assignment
  • Mark a method final, and no subclass can override it
  • Put it on a class, and inheritance stops completely

One keyword, three jobs. Notice the theme: final always locks something down.

7. final Does Not Mean Immutable

Here is the single biggest misunderstanding about constants in Java. Read this section twice.

7.1 The Reference Is Locked, Not the Object

When a final variable holds an object, final freezes only the reference. The object it points at can still change freely.

final List<String> names = new ArrayList<>();

names.add("Rohit");   // allowed, the list contents changed
names.add("Virat");   // still allowed
System.out.println(names); // Output: [Rohit, Virat]

// names = new ArrayList<>(); // error: cannot assign a value to final variable

Think of it like a house address written in permanent ink. You cannot change the address, but you can still rearrange the furniture inside.

7.2 How to Get Real Immutability

If you want the contents locked too, final is not enough. You need an immutable object.

  • Use List.of(...) or Map.of(...) to build collections that reject changes
  • Use naturally immutable types like String and Integer
  • Make your own class immutable: private final fields, no setters
final List<String> names = List.of("Rohit", "Virat");
names.add("Dhoni");  // throws UnsupportedOperationException at runtime

Now nothing moves. The reference stays put, and the contents refuse every change. That is genuine immutability.

8. Compile-Time Constants

8.1 What Makes a Constant Compile-Time

Not every final variable is equal. Some are special, and the compiler treats them differently.

A compile-time constant must tick three boxes:

  • You declare it final
  • Its type is a primitive or a String
  • Its value comes from a literal the compiler can work out on the spot
final int MAX = 10;              // compile-time constant
final int limit = getLimit();    // NOT one, value known only at runtime

Here is the simple test. Could you replace the name with its value by hand, just by reading the code? If yes, it is a compile-time constant.

8.2 Why switch Cares

Only compile-time constants may appear as case labels. The compiler builds its jump table before your program runs, so it needs the values up front.

final int MAX = 10;
final int limit = getLimit();

switch (x) {
    case MAX:     // fine
        break;
    case limit:   // error: constant expression required
        break;
}

Hit that error one day and you will know exactly why. The value was not knowable at compile time.

9. Naming Rules and Conventions

The compiler enforces some naming rules. Others are just conventions, but breaking them will annoy your teammates.

9.1 The Rules Java Enforces

Break any of these and your code will not compile:

  • Start every name with a letter, an underscore, or a dollar sign
  • Never begin a name with a digit
  • Keywords are off limits, so class and int will not work
  • No spaces, and no special symbols beyond _ and $
  • Case matters, so score and Score are two different variables
int studentResult;   // valid
int _count;          // valid, though unusual
// int 2ndPlace;     // error, starts with a digit
// int class;        // error, class is a keyword
// int total marks;  // error, contains a space

9.2 The Conventions Developers Follow

These are not compiler rules. They are habits, and every Java team expects them.

  • Start variable names with a lowercase letter: result, not Result
  • Use camelCase for multiple words: studentResult
  • Write constants in UPPER_SNAKE_CASE: MAX_SPEED, STUDENT_RESULT
  • Choose meaningful names, so totalMarks beats tm every time
  • Avoid single letters, except for loop counters like i
int studentResult = 150;              // camelCase variable
static final int MAX_SPEED = 120;     // UPPER_SNAKE_CASE constant

Follow the case convention and anyone can tell a constant from a variable at a glance. That is the whole point.

10. var: Letting Java Infer the Type

Java 10 added a small but handy keyword: var. It lets you skip writing the type when the compiler can already work it out.

10.1 How var Works

Look at the right-hand side of the assignment. If the type is obvious there, var saves you from repeating it.

var count = 10;                 // compiler infers int
var name = "Rohit";             // compiler infers String
var prices = new ArrayList<Double>();  // infers ArrayList<Double>

One thing catches people out. var is not a dynamic type, and Java is still strongly typed.

The type gets fixed at compile time and never changes afterwards. Try to reassign a different type and the compiler stops you.

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

10.2 Where var Does Not Work

The keyword only works for local variables. Java needs an initializer to infer from, so several spots are off limits:

  • Instance variables and static variables cannot use var
  • Method parameters cannot use var
  • A declaration with no value, like var x;, will not compile
  • Assigning null gives the compiler nothing to work with

Use it where it genuinely helps readability. Writing var in front of a long generic type is a win, but var x = 1; just hides useful information.

11. A Practical Walkthrough

Let us put the whole article together in one small program. We will price an order, using both variables and a constant.

11.1 Building a Small Bill

The tax rate never changes during the run, so it earns a constant. Everything else moves, so those become plain variables.

public class Bill {

    static final double TAX_RATE = 0.18;  // constant, shared by the class
    private String customer;              // instance variable, one per object

    public Bill(String customer) {
        this.customer = customer;         // this breaks the shadowing tie
    }

    double total(double price, int quantity) {
        double subtotal = price * quantity;   // local variable
        double tax = subtotal * TAX_RATE;     // local variable
        return subtotal + tax;
    }

    public static void main(String[] args) {
        Bill bill = new Bill("Rohit");
        System.out.println(bill.total(100.0, 3)); // Output: 354.0
    }
}

11.2 Reading the Program Back

Walk through it once more, and notice how every idea from this article shows up:

  • TAX_RATE is static and final, so one shared copy that nobody can reassign
  • Its UPPER_SNAKE_CASE name tells you instantly that it is a constant
  • customer is an instance variable, so every Bill object carries its own
  • subtotal and tax are local, so they vanish when the method returns
  • The constructor uses this.customer because the parameter shadows the field

Try changing TAX_RATE inside the total method. The compiler will refuse, and that refusal is exactly the protection you wanted.

12. Common Mistakes and Pitfalls

12.1 Reading a Local Variable Too Early

Local variables have no default value. Read one before assigning it, and the compiler stops you cold.

int total;
System.out.println(total);  // error: variable total might not have been initialized

The fix is easy. Assign a value, even a zero, before the first read.

12.2 Expecting final to Freeze an Object

We covered this in section 7, and it deserves a second mention. A final List can still gain and lose elements.

If the contents must not change, reach for List.of(...). Do not rely on final alone.

12.3 Shadowing by Accident

Naming a local variable the same as a field is legal. It is also a fine way to confuse yourself at 2am.

Either use this.field deliberately, or pick a different local name. Do not leave it ambiguous.

12.4 Forgetting That static Is Shared

Marking a field static by accident is a classic bug. Suddenly every object shares one value.

public class Student {
    static int rollNumber;   // oops, should not be static
}

Student a = new Student();
Student b = new Student();
a.rollNumber = 101;
System.out.println(b.rollNumber); // Output: 101, not 0

Ask yourself one question before adding static. Should every object really share this value?

13. Interview Questions

Q: What is the difference between a variable and a constant in Java?

A: You can reassign a variable as many times as you like while the program runs. A constant uses the final keyword, so you get exactly one assignment. Any later attempt to change it gives you a compile-time error.

Q: Does final make an object immutable?

A: No. final only locks the reference, not the object it points to. A final List can still have elements added or removed. For true immutability, use something like List.of(…) or design your class with private final fields and no setters.

Q: What is the default value of a local variable?

A: Local variables have no default value at all. Instance and static variables get defaults like 0, false, or null. With a local variable, you must assign a value yourself before reading it, or the code will not compile.

Q: What is variable shadowing in Java?

A: Shadowing happens when a local variable or method parameter has the same name as an instance variable. Inside that method, the local name wins. To reach the instance variable, you have to qualify it with this, as in this.value.

Q: What is a compile-time constant?

A: It is a final variable of a primitive or String type, initialized with a literal value the compiler can evaluate immediately. Only compile-time constants can be used as case labels in a switch statement, because the compiler needs those values before the program runs.

Q: Can a final variable be declared without initializing it?

A: Yes. You can declare a final variable and assign it later, as long as you assign it exactly once. Java calls this a blank final. A second assignment gives you a compile-time error.

Q: What is the naming convention for constants in Java?

A: Constants use UPPER_SNAKE_CASE, so all capital letters with underscores between words. MAX_SPEED and STUDENT_RESULT are good examples. The compiler does not enforce this, but every Java team expects it.

Q: How many copies of a static variable exist?

A: Exactly one, no matter how many objects you create. The JVM allocates that memory once when the class loads, and every object shares it. Change it through one object and all the others see the new value.

Q: Can you use var for instance variables in Java?

A: No. The var keyword only works for local variables that have an initializer. Instance variables, static variables, and method parameters must all declare an explicit type. Java needs something on the right-hand side to infer from.

Q: Why does a setter method use this.name = name?

A: The parameter shadows the instance variable, because both share the same name. Java resolves the plain name to the parameter, so this.name tells it to use the field instead. Drop the this and you simply assign the parameter to itself, leaving the field null.

Q: Where does Java store local variables?

A: Local variables live on the stack, inside the frame for that method call. When the method returns, the frame goes away and the variables vanish with it. Instance variables live on the heap instead, alongside the object that owns them.

14. Conclusion

Let us wrap up what we covered. A variable is a named box in memory whose value can change. A constant uses final, takes exactly one assignment, and then locks.

Where you declare a variable decides how it behaves. Local variables get no default and die with the method. Instance variables give every object its own copy. Static variables hand one shared copy to the whole class.

The final keyword works at three levels. It stops you reassigning a variable, stops a subclass overriding a method, and stops anyone extending a class.

Just remember the trap. Final locks the reference, not the object behind it. If the contents must stay fixed, reach for a genuinely immutable type.

Further Reading

Leave a Comment