Variables, Data Types and Input/Output

Getting Started with Code · 30 min

Data structures & algorithms · Stage 0 foundations

Variables, data types and I/O

A variable is a labelled box in memory. This page fills a box, overwrites one, swaps two numbers through a third — and then shows the three places where the box hands you a wrong answer without saying a word.

Fill the memory table yourself
Before · a 5 · b 3
After · a 3 · b 5

01 The idea

A named box, and what fits inside it

Every value your program works with has to live somewhere. A variable is a name for one place in memory — a box — and everything you do with data comes down to three moves: put a value in a box, copy a value out of a box, or write over what a box already holds. The name is there for you. The machine only cares about the address.

The type is the part students skim, and it is the part that bites. int, double, char and boolean are not decorations. Each one fixes how many bits the box has and how those bits are read back. A four-byte int box cannot hold 3.5 and cannot count past 2,147,483,647, and when you ask it to, you do not get an error. You get a wrong number and a program that keeps running.

A variable is a named box in memory, and its type decides how big the box is and what shape of value fits. Writing to a box throws away whatever was in it. Reading from a box takes a copy — it never ties two names together.
DeclarationNaming a box and giving it a type, which reserves memory of that size. int temp; creates the box and stops there — nothing has been written into it yet.
InitialisationThe first value written into a declared box. int a = 5; declares and initialises on one line, which is why the two words get muddled.
ReassignmentWriting over a box that already holds something. a = b; replaces the contents of a. The old value is gone, and no name remembers it unless you copied it first.

02 Memory trace

Swapping two numbers, box by box

Two variables: a holds 5, b holds 3, and you want them the other way round. Each row below is the state of memory after one line has run. A rose box is the box that line just wrote to. A dot means the box does not exist yet, a question mark means it exists but nothing has been written into it. Follow the 5 — it is the value that nearly gets lost.

memoryabtemp
a = 55··the box for a now exists and holds 5
b = 353·a second box, a second value, no link between them
int temp;53?declared, not initialised — reading it here is a bug
temp = a535a is copied, not moved — the 5 is now in two boxes
a = b335the 5 in a is destroyed here — only temp still has it
b = temp355swapped: a holds 3, b holds 5

Two facts in that trace do all the teaching. First, the temp = a row copies: once it has run the number 5 sits in two independent boxes, and changing one of them does not touch the other. Second, the a = b row destroys: the moment it runs, the 5 that was in a is gone from memory and nothing remembers it. The temp variable exists because those two facts collide — you have to save the value before the write that wipes it. Every "why do I need a third variable" question is that one sentence. Drop temp and the program still runs, still reports no error, and leaves both boxes holding 3.

03 Code and types

The same swap, read from input and printed back

Here is the trace as a whole program, with the two numbers coming from the keyboard instead of being written into the source. The values are the same 5 and 3, so you can lay this against the trace line by line.

Scanner sc = new Scanner(System.in);int a = sc.nextInt();        // you type 5int b = sc.nextInt();        // you type 3int temp;                    // declared, no value yettemp = a;                    // copy the 5 out of aa = b;                       // a is overwritten, the 5 is goneb = temp;                    // the saved copy lands in bSystem.out.printf("a=%d b=%d%n", a, b);   // a=3 b=5

Declaration and initialisation are two separate events. Line 4 makes the box and stops. Java refuses to compile a read of a local variable that was never written to, which turns a whole family of bugs into a compile error. C does not stop you — it hands back whatever bits were already lying at that address, so the same program gives a different answer on a different run.

temp = a copies; it does not create an alias. That is the only reason line 6 is safe. Line 6 destroys the 5 inside a, and the copy sitting in temp is what line 7 hands to b. Reverse lines 5 and 6 and the copy is made after the value is already gone.

nextInt() reads a number, not a line. It consumes the digits and leaves the newline you pressed sitting in the buffer. Call nextLine() straight after it and you get an empty string, because that leftover newline is the first thing it finds. Consume it with one extra nextLine() before you read real text. This is the single most common input bug in a placement test.

%d is a promise about the type. In printf, %d says a whole number goes here and %.2f says a decimal rounded to two places goes here. Formatting changes what is printed and never what is stored — printf("%.2f", 3.14159) shows 3.14 while the variable still holds every digit.

The seven types you will actually use

TypeWhat it holdsSizeWatch out for
intWhole numbers, −2,147,483,648 to 2,147,483,6474 bytes7 / 2 is 3, and one past the top wraps to the bottom
longWhole numbers up to 9,223,372,036,854,775,8078 bytesBig literals need the suffix: 10000000000L
floatDecimals, about 7 significant digits4 bytesNeeds the f suffix, and 7 digits runs out fast
doubleDecimals, about 15 significant digits8 bytes0.1 + 0.2 is not 0.3
charOne character, written 'A'2 bytesSingle quotes, and 'A' + 1 is the int 66
booleanExactly true or false1 bit of informationYou cannot assign 0 or 1 to it in Java
StringText, written "hello"a reference== compares identity, so use .equals

Converting between types

Moving a value into a bigger box is safe, so the compiler does it silently. That is implicit conversion, also called widening: int → long → float → double. Write double d = 7; and d holds 7.0 with nothing lost. Moving a value into a smaller box can lose information, so the compiler refuses until you write the cast yourself. That is explicit conversion, or narrowing, and the cast is you signing for the loss.

A cast toward int chops; it does not round. (int) 3.9 is 3 and (int) -3.9 is −3 — the fractional part is thrown away and the value moves toward zero. If you wanted 4, you wanted Math.round.
Three traps, and all three are silent. 7 / 2 is 3, because both sides are ints. 2147483647 + 1 is -2147483648, because the bits ran out. 0.1 + 0.2 is 0.30000000000000004, because binary cannot write 0.1 exactly. Not one of them raises an error. Step through all three below.

05 Cheat sheet

The lines that get asked about

You writeWhat happensResult and gotcha
int a;Declaration only — a 4-byte box, nothing in itJava will not let you read it; C returns garbage
a = b;Copies the value of b into aThe old a is gone; the two stay independent
7 / 2int ÷ int is done as int division3 — truncates, never rounds
7 % 2Remainder after that division1/ and % together recover the whole answer
(int) 3.9Explicit narrowing, double to int3 — use Math.round if you meant 4
2147483647 + 1int arithmetic with no room left-2147483648 — wraps silently, no error
0.1 + 0.2 == 0.3Exact bit comparison of two doublesfalse — compare with a tolerance
nextInt() then nextLine()Reads the number, then the rest of that line"" — the newline was still waiting
ScopeA variable exists only inside the { } block that declared it. Declare it inside the loop and it is unreachable after the loop, which is usually what you want and occasionally the bug.
LifetimeA local variable is born when its line runs and dies when its block ends. Its box lives on the call stack and gets reused by the next thing that needs that space.
CostThe type is a size decision. A million longs cost 8 MB where a million ints cost 4 MB, and on a hot array that is the difference between fitting in cache and not.

06 Where & why

Which type do you actually pick

In a placement test, int and double cover almost everything you will write, and int is the sensible default. The choice still matters, because picking wrong does not give you an error message. It gives you a plausible wrong answer.

Reach for int when…

The values stay under 2.1 billionArray indexes, counts, ages, marks, loop counters. That range is exactly what four bytes were sized for.
You are counting, not measuringCounts are whole by nature, and whole numbers in an int are exact. A double stores 3 as 3.0 and buys you nothing.
You need / and % togetherQuotient and remainder come out exact, which is how you peel digits off a number, page a list, or split rupees from paise.
The array is largeFour bytes per element instead of eight halves the memory and roughly doubles how much of the array fits in cache.

Use something else when…

SituationBetter choiceWhy
The count can pass two billion — milliseconds since 1970, file sizes, a sum of many large intslongint wraps at 2147483647 without a word. A long runs to about 9.2 quintillion.
Averages, ratios, distances, percentagesdouble7 / 2 in ints is 3. Anything measured needs a fractional part, and double gives about 15 digits.
Moneya long of paise, or BigDecimaldouble cannot store 0.1 exactly, so rupee totals drift by a paisa and reconciliation fails.
A yes-or-no answerbooleanHolding 0 and 1 in an int lets a bug store 7. A boolean has two values, so the compiler catches it.
Checking whether two pieces of text matchString with .equals== asks whether they are the same object, not whether they read the same. Two identical strings from input can fail it.
Nobody will ask you to define the word "variable" in an interview. They will ask what 7 / 2 prints, why 0.1 + 0.2 is not 0.3, and what happens one step past Integer.MAX_VALUE — and every one of those is the same question wearing a different hat: how big is the box, and what shape fits in it?

07 Interview questions

Say these out loud

The three trap questions come up in almost every first-round screen, and the interviewer is listening for why, not just the value.

What is a variable?
A name for one box in memory. The type fixes how big that box is and what kind of value fits in it, and the name is how your code reaches it. Assigning to a variable replaces the contents, so the old value is gone unless you copied it somewhere first.
What is the difference between declaring and initialising a variable?
Declaring reserves the box and gives it a name and a type: int temp;. Initialising is the first value written into it: temp = a;. Java will not compile a read of a local variable that was never initialised. C compiles it happily and hands you whatever bits were already at that address, which is why the same program can print different rubbish on different runs.
Swap two numbers. Why do you need a third variable?
Because the first assignment destroys a value you still need. a = b writes 3 over the 5 in a, and nothing remembers that 5, so a following b = a would set b to 3 as well and both variables would end up holding 3. temp = a makes a second copy before the destructive write, and that copy is what b receives.
Can you swap two ints without a temporary variable?
Yes, but you should not. a = a + b; b = a - b; a = a - b; swaps them, and in Java it survives even when a + b overflows, because int arithmetic wraps consistently and the two subtractions unwind exactly the same wrap — try it with 2000000000 and 2000000000. The XOR version a ^= b; b ^= a; a ^= b; also works, until you swap a variable with itself, which zeroes it. Both are party tricks: the addition version is undefined behaviour in C when it overflows, neither survives being reused on doubles, and neither is as readable as the temp. Use the temp.
What does 7 / 2 give you, and why?
3. Both operands are ints, so the language does integer division: it keeps the whole-number part and discards the rest. It truncates toward zero, it does not round. Write 7 / 2.0 or (double) 7 / 2 to get 3.5, and note that (double)(7 / 2) is still 3.0 because the damage is done inside the brackets before the cast runs.
What is the range of an int, and what happens if you go past it?
32 bits, so −2,147,483,648 to 2,147,483,647. Adding 1 to the maximum wraps around to the minimum, with no exception and no warning — the bits overflow and the sign bit flips. This is the bug behind the classic binary-search midpoint (low + high) / 2, which goes negative on large arrays; the fix is low + (high - low) / 2.
Why is 0.1 + 0.2 not equal to 0.3?
Because a double stores numbers in binary, and 0.1 has no exact binary form, the same way one third has no exact decimal form. The nearest double to 0.1 plus the nearest double to 0.2 comes to 0.30000000000000004, which is a different bit pattern from the nearest double to 0.3. So never compare doubles with == — check that the absolute difference is below a small tolerance like 1e-9.
float or double — which do you use?
double, unless you have a measured reason not to. A float carries about 7 significant digits and a double about 15, and a decimal literal in Java is a double by default, so float f = 1.5; will not even compile without the f suffix. Reach for float only when you are storing millions of values and you have checked that 7 digits is enough.
What is the difference between implicit and explicit type conversion?
Implicit conversion, or widening, happens on its own when the target box is bigger and nothing can be lost: long x = 5; or double d = 7;. Explicit conversion, or narrowing, needs a cast you write yourself, because information can be lost: int n = (int) 3.9; gives 3. The cast is you telling the compiler you accept the loss, and the loss is truncation toward zero, not rounding.
How do you read input, and what goes wrong with Scanner?
Create new Scanner(System.in), then use nextInt() for a number and nextLine() for a whole line. The classic bug is calling nextLine() straight after nextInt(): nextInt() consumes the digits and leaves the newline behind, so the nextLine() returns an empty string. Consume the leftover newline with one extra nextLine() first.
Why does == not work for comparing two Strings?
Because a String variable holds a reference, not the characters, so == asks whether both names point at the same object rather than whether the text matches. Two strings read from input can read identically and still be two different objects, so the test fails. Use .equals for the text, and keep == for the primitives — int, char, boolean and the rest.

08 Practice problems

Write the boxes down first

For each one, write out the variables and their types before you write any code, then predict the output by hand and only then run it. The gap between your prediction and the output is the lesson.

Five results, one pair

Easy
Read two whole numbers a and b and print, one per line, their sum, difference, product, quotient and remainder.
Follow-up
Now run it with a = −7 and b = 2. The quotient prints −3, not −4, and the remainder prints −1, not 1. Write down the single rule that makes both of those true at once.
Show the hint
Java discards the fraction instead of rounding down, so the quotient moves toward zero. Then check your two answers still satisfy a = quotient × b + remainder.

Peel the digits

Easy
Read a three-digit whole number and print its hundreds, tens and units digits separated by spaces, using only / and % — no strings.
Follow-up
Feed the same program 1000 and it prints 10 0 0. Say which one of your three expressions broke, and why the other two were still right.
Show the hint
Every % 10 hands you the last digit; every / 10 throws that digit away. Build each digit out of those two moves and nothing else.

Average of three marks

Medium
Read three whole-number marks and print their average to two decimal places.
Follow-up
(m1 + m2 + m3) / 3 prints 80 for the marks 80, 81 and 81, because the sum and the 3 are both ints — and %.2f on that result only prints 80.00, it does not recover the lost decimals.
Show the hint
Divide by 3.0 so the division itself happens in doubles; formatting can only change what is shown, never what was computed.

Find the wrap by hand

Medium
Start an int at 2147483645, add 1 to it five times, and print the variable after every single addition.
Follow-up
Predict all five values on paper before you run it. The third addition is the one that turns the value negative — and what it lands on is not 2147483648 but −2147483648, the same 32 bits read as a signed number.
Show the hint
An int is 32 bits and the top one is read as the sign. Write your five values down first; the program is only there to prove you right.

Rupees into paise

Medium
Read a price in rupees as a double — 19.99, say — and print it as a whole number of paise. Then read the rupees and the paise as two separate ints and print the paise total that second way as well.
Follow-up
(int)(19.99 * 100) is 1998, not 1999: the nearest double to 19.99 sits a hair below it, and the cast chops instead of rounding. Say which of your two answers a billing system should trust.
Show the hint
Print 19.99 * 100 on its own line before you cast it, and read every digit of what comes out. Then choose between Math.round and never letting the money become a double at all.

Three ways to write one formula

Hard
Read a whole-number Fahrenheit temperature f and print Celsius three times over: once from (f - 32) * 5 / 9, once from (f - 32) * (5 / 9) and once from (f - 32) * 5.0 / 9.
Follow-up
For f = 100 those print 37, then 0, then 37.77777777777778. The middle one prints 0 for every temperature you can type. Name the division that did the damage in each of the first two, and say which one is still usable.
Show the hint
Work out 5 / 9 on its own first — both of its operands are ints. Then remember * and / run left to right unless brackets say otherwise, so in the first version the multiply happens while the value is still exact.