Table of Contents

Data types in Java with examples

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

Data types in Java with examples

Every value you write in a Java program has a type, and picking that type is the very first decision you make. That is what data types in Java are about: telling the compiler what kind of value a variable holds, and how much memory to hand it. Get this right and everything else clicks into place. Get it wrong and your number silently wraps around, your decimal loses precision, or your code refuses to compile at all.

1. Introduction

1.1 Why Data Types Exist at All

Think about a set of storage boxes in your kitchen. A tiny box holds a pinch of salt. A big box holds five kilos of rice. You would never buy a huge crate just to store one spoon of sugar.

Memory works the same way. A variable needs a box, and the type decides how big that box will be. Ask for a byte and Java hands you a tiny 1-byte box. Ask for a long and you get a roomy 8-byte one.

There is a second job too. The type tells the compiler what you can legally do with the value. You may divide two numbers. You may not divide two booleans. Java catches that nonsense before your program ever runs.

So a type gives you three things at once: a size, a range of allowed values, and a set of allowed operations. That is the whole idea in one sentence.

int result = 50;      // 4 bytes, whole number
char grade = 'A';     // 2 bytes, one character
boolean pass = true;  // logical flag

Notice how each line reads almost like English. The type comes first, then the name, then the value. Java never guesses for you, and that strictness is a feature, not a chore.

1.2 What This Article Covers

We will start simple and build up. By the end you should be able to look at any variable and know exactly what it costs and what it can hold.

  • All eight primitive types, grouped by category, with size, range and default
  • The range formula, worked out step by step (a lot of tutorials get this wrong)
  • Why char needs 2 bytes while C gets away with 1
  • Default values, and the trap that catches every beginner with local variables
  • Non-primitive types: String, arrays, classes and interfaces
  • Type casting, overflow, wrapper classes and var

No prior Java needed. If you can read a System.out.println, you are ready.

2. Primitive vs Non-Primitive Data Types

2.1 The Two Big Families

Java splits every type into two camps. Primitives come baked into the language. Non-primitives you build, or the library builds for you.

Primitives are the atoms. Java ships exactly eight of them, and that list has never grown. You cannot invent a ninth one.

Non-primitives are the molecules. String, arrays, classes, interfaces and enums all live here. Each one sits on top of primitives, or on top of other non-primitives.

// primitive - the value lives right in the variable
int age = 25;

// non-primitive - the variable holds a reference to an object
String name = "Riya";
int[] marks = {90, 85, 78};

2.2 How They Really Differ

The split is not just naming. It changes how your program behaves at runtime.

  • A primitive stores the actual value. A non-primitive stores an address that points to an object.
  • Primitives can never hold null. Any non-primitive can.
  • Every primitive has a fixed size. An object’s size depends on what sits inside it.
  • Primitives carry no methods. Call .length() on a String and it answers; try that on an int and the compiler laughs.
  • Primitive names start lowercase. Class names start uppercase, which is a handy tell.

That last point saves real time when you read unfamiliar code. Spot int and you know the value is right there. Spot Integer and you know an object is involved.

3. The Integer Family: byte, short, int, long

Four primitives hold whole numbers. They differ only in how many bytes they take, and therefore how far they can count. All four are signed, so each one covers negatives as well.

3.1 byte

The smallest of them all. A byte takes 1 byte, which is 8 bits, and its range runs from -128 to 127. Its default value is 0.

Watch that range carefully. Plenty of tutorials print “-127 to 128” and it is simply wrong. The low end is -128 and the high end is 127.

byte temperature = -40;
byte volume = 100;
System.out.println(temperature + " " + volume); // Output: -40 100

// byte tooBig = 200;  // compile error: range is -128 to 127

When would you actually reach for it? Raw file bytes, image pixels, network packets. Anywhere you handle a big pile of small numbers and memory really matters.

3.2 short

Double the space, far more room. A short takes 2 bytes and covers -32,768 to 32,767. Its default value is 0.

short year = 2026;
short population = 30000;
System.out.println(year + " " + population); // Output: 2026 30000

Honestly, short is rare in modern code. Most developers jump straight to int because the memory saving rarely pays for the extra thinking.

3.3 int

Here is your workhorse. An int takes 4 bytes and spans -2,147,483,648 to 2,147,483,647. Its default value is 0.

Roughly two billion either way. That covers ages, counts, loop counters, IDs and almost everything else you meet day to day.

Java also treats int as the default type for any whole-number literal. Write 5 in your code and the compiler reads it as an int, nothing else.

int salary = 850000;
int loss = -2000;
int max = 2147483647;
System.out.println(max); // Output: 2147483647

3.4 long and the L Suffix

When two billion is not enough, go long. A long takes 8 bytes and runs from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. Its default value is 0L.

That is about 9.2 quintillion in each direction. Timestamps in milliseconds, view counts on a viral video, national population totals: all comfortable here.

Now the rule that trips everyone. Any big literal still counts as an int first, so you must add an L to mark it as a long.

// long views = 3000000000;   // compile error! literal is read as int
long views = 3000000000L;     // correct - the L saves you

long small = 100;             // fine, 100 fits in an int and widens quietly
System.out.println(views);    // Output: 3000000000

Use a capital L, never lowercase. A lowercase l looks exactly like the digit 1 in most fonts, and that has burned many a night shift.

4. How the Range Formula Works

4.1 The Correct Formula

Those ranges are not random numbers someone picked. Each one falls out of a simple formula. For any signed type with n bits, the range looks like this:

Range = -2^(n-1)  to  2^(n-1) - 1

where n = total number of bits in the type

Read the exponent carefully: it is n minus 1, not n. One bit goes to the sign, so only n-1 bits remain for the magnitude. Many older tutorials write it as “-2^n to 2^n – 1”, and that formula is flat-out wrong.

4.2 Working It Out for byte

Let us plug real numbers in. A byte holds 8 bits, so n equals 8.

byte : n = 8 bits
  low  = -2^(8-1) = -2^7  = -128
  high =  2^(8-1) - 1 = 2^7 - 1 = 128 - 1 = 127
  Range: -128 to 127

int : n = 32 bits
  low  = -2^31 = -2,147,483,648
  high =  2^31 - 1 = 2,147,483,647
  Range: -2,147,483,648 to 2,147,483,647

long : n = 64 bits
  low  = -2^63 = -9,223,372,036,854,775,808
  high =  2^63 - 1 = 9,223,372,036,854,775,807

Try the same trick on short. Sixteen bits, so 2^15 gives 32,768, and the range lands on -32,768 to 32,767. The pattern never breaks.

4.3 Why the Negative Side Gets One Extra

Look at those ranges again. The negative end always reaches one step further than the positive end. Why the imbalance?

Blame zero. An 8-bit box holds 256 distinct patterns, and one of them must represent zero. Zero sits on the positive team, so the positives get 0 through 127, which is 128 slots. That leaves 128 slots for the negatives, giving -128 through -1.

Add them up: 128 plus 128 equals 256. Every pattern has a job, and nothing is wasted. Neat, once you see it.

5. The Floating-Point Family: float and double

Whole numbers only take you so far. Prices, temperatures, averages and measurements all need a decimal point. Java gives you two types for that job.

5.1 float and the f Suffix

A float takes 4 bytes. Its range stretches roughly from -3.4e38 to +3.4e38, and it keeps about 7 decimal digits of precision. Its default value is 0.0f.

Here comes the suffix rule. Java reads every decimal literal as a double by default, so a bare 2.5 will not fit into a float slot. You must append an f.

// float price = 2.5;   // compile error: 2.5 is a double
float price = 2.5f;     // correct
float pi = 3.14159f;

System.out.println(price + " " + pi); // Output: 2.5 3.14159

Seven digits sounds like plenty until it isn’t. Push past that and float starts rounding on you, quietly.

5.2 double, the Default Decimal

A double takes 8 bytes. Its range runs roughly from -1.7e308 to +1.7e308, with around 15 decimal digits of precision. Its default value is 0.0d.

Because a decimal literal already counts as a double, the d suffix is optional. Both lines below compile happily.

double distance = 384400.5;    // no suffix needed
double same = 384400.5d;       // d is optional

double tiny = 1.0 / 3;
System.out.println(tiny);      // Output: 0.3333333333333333

Reach for double unless you have a strong reason not to. It doubles your precision for a cost most programs never notice.

5.3 Never Use Them for Money

Both types store binary fractions, and some ordinary decimals have no exact binary form. So 0.1 plus 0.2 does not land on 0.3.

System.out.println(0.1 + 0.2);  // Output: 0.30000000000000004

Shocking the first time you see it, right? Yet nothing is broken here. Binary simply cannot express 0.1 perfectly, exactly as decimal cannot express one third.

For prices, invoices and interest, switch to java.math.BigDecimal. Your accountant will thank you.

6. char: The Character Type

6.1 char Basics

A char holds exactly one character. It takes 2 bytes and its range runs from 0 to 65,535, covering the Unicode character set. Its default value is '\u0000', the null character, which prints as blank space.

Single quotes matter here. One character in single quotes gives a char; text in double quotes gives a String.

char grade = 'A';
char symbol = '#';
char rupee = '₹';   // Unicode escape

System.out.println(grade + " " + symbol + " " + rupee); // Output: A # ₹

6.2 Why char Takes 2 Bytes

In C, a char takes 1 byte. Java doubles that. Why?

One byte gives you 2^8, which is 256 slots. ASCII fits in there comfortably: English letters, digits and punctuation. That was fine in 1972.

Java, though, aimed at the whole planet from day one. Hindi, Chinese, Arabic, Greek, Japanese and dozens more all need their own characters. Cram them into 256 slots and you run out almost immediately.

So Java picked Unicode and gave char 2 bytes. Two bytes give 2^16, which is 65,536 slots. Suddenly every major script on Earth has room to breathe.

  • 1 byte (ASCII): 2^8 = 256 characters, English only
  • 2 bytes (Unicode): 2^16 = 65,536 characters, most world scripts

6.3 char Is Secretly a Number

Under the hood a char is just an unsigned 16-bit number. The letter ‘A’ is really the number 65. Print it as an int and Java shows you the code.

char letter = 'A';
int code = letter;             // widening: char to int
System.out.println(code);      // Output: 65

char next = (char) (letter + 1);
System.out.println(next);      // Output: B

That trick powers a lot of small utilities. Shifting letters, building ciphers, checking whether a character is a digit: all of it leans on char arithmetic.

7. boolean: True or False

7.1 boolean Basics

The simplest primitive of the eight. A boolean holds only true or false, and its default value is false.

Every if, every while, every comparison in your code produces one of these. It is the switch that steers your whole program.

boolean isActive = true;
boolean hasPassed = (marks >= 40);

if (isActive) {
    System.out.println("User is live"); // Output: User is live
}

One warning for anyone arriving from C or Python. Java refuses to treat 1 as true or 0 as false. Write if (1) and the compiler stops you cold.

7.2 How Big Is a boolean?

Great interview question, and the honest answer surprises people. The Java spec never fixes a size for boolean.

Logically it needs a single bit. In practice, though, a JVM usually spends a whole byte on it, because CPUs address bytes and not bits. Inside an array, some JVMs pack them tighter.

So the correct answer is: JVM-dependent. Say “1 bit of information, but the actual footprint depends on the JVM” and you will sound like someone who has read the spec.

8. All 8 Primitives: The Cheat Sheet

Here is the whole family in one place. Bookmark this table; you will come back to it more often than you expect.

Type Size Range Default Example
byte 1 byte -128 to 127 0 byte b = 100;
short 2 bytes -32,768 to 32,767 0 short s = 30000;
int 4 bytes -2,147,483,648 to 2,147,483,647 0 int i = 100000;
long 8 bytes -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 0L long l = 3000000000L;
float 4 bytes approx. -3.4e38 to 3.4e38 (7 decimal digits) 0.0f float f = 2.5f;
double 8 bytes approx. -1.7e308 to 1.7e308 (15 decimal digits) 0.0d double d = 2.5;
char 2 bytes 0 to 65,535 (Unicode) ‘\u0000’ char c = 'A';
boolean JVM-dependent true or false false boolean ok = true;

Four integer types, two floating-point types, one character type, one logical type. Eight in total, and that number never changes.

9. Default Values and the Local Variable Trap

9.1 Fields Get a Free Default

Declare a field inside a class and skip the value. Java does not complain. Instead, the JVM quietly fills it with a default.

Numbers start at zero. Booleans start at false. Characters start at the null character. Objects, including String, start at null.

9.2 The DefaultValues Program

Do not take my word for it. Run this and watch the JVM fill every slot for you.

package com.javahandson;

public class DefaultValues {

    byte b;
    short s;
    int i;
    long l;

    float f;
    double d;

    char ch;
    boolean bool;

    String name;

    public static void main(String[] args) {

        DefaultValues dv = new DefaultValues();

        System.out.println("byte    : " + dv.b);
        System.out.println("short   : " + dv.s);
        System.out.println("int     : " + dv.i);
        System.out.println("long    : " + dv.l);
        System.out.println("float   : " + dv.f);
        System.out.println("double  : " + dv.d);
        System.out.println("char    : [" + dv.ch + "]");
        System.out.println("boolean : " + dv.bool);
        System.out.println("String  : " + dv.name);
    }
}

/* Output:
byte    : 0
short   : 0
int     : 0
long    : 0
float   : 0.0
double  : 0.0
char    : [ ]
boolean : false
String  : null
*/

See the char line? Square brackets wrap it so the blank becomes visible. That blank is '\u0000', not a space you typed.

Also notice String printing null, not an empty string. Non-primitives default to null every single time, and mixing that up causes real bugs.

9.3 Local Variables Get Nothing

Now the point most tutorials skip, and interviewers absolutely love. Local variables get no default value at all.

A local variable lives inside a method. The JVM does not initialise it, so you must assign a value before you read it. Skip that step and the compiler refuses to build.

public void demo() {
    int count;
    // System.out.println(count);
    // compile error: variable count might not have been initialized

    count = 10;
    System.out.println(count);  // Output: 10
}

Why the difference? Fields belong to an object, and the JVM zeroes that whole block of memory when it creates the object. Locals live on the stack, where nobody sweeps up for you.

Honestly, this rule is a gift. It turns a whole class of “uninitialised value” bugs into a compile error you fix in ten seconds.

10. Non-Primitive Data Types

Primitives hold one value each. Real programs need more than that, so Java gives you non-primitive types to group and model things.

10.1 String

A String holds a sequence of characters. It behaves so naturally that beginners often assume it must be a primitive. It isn’t.

String city = "Pune";
System.out.println(city.length());       // Output: 4
System.out.println(city.toUpperCase());  // Output: PUNE
System.out.println(city.charAt(0));      // Output: P

Those dots give the game away. Primitives have no methods, yet String is packed with them.

10.2 Why String Is Non-Primitive

String is a class in java.lang. It extends Object, exactly like every other class you write. Only a class can do that.

  • It carries methods such as length(), concat() and contains().
  • It inherits from Object, which primitives can never do.
  • Its default value is null, not some empty character.
  • A String variable stores a reference, not the characters themselves.

Java does grant it two special favours, and that is where the confusion starts. You may create one with a literal, "Pune", and you may join two with +. No other class gets that treatment. Underneath, though, it remains an ordinary object.

10.3 Arrays: The Derived Type

An array groups many values of one type under a single name. Older textbooks call this a derived type, because it derives directly from a primitive or a class.

Arrays are homogeneous. Every slot must share the same type, no exceptions.

int[] marks = {20, 30, 70, 80};        // valid - all ints
System.out.println(marks[2]);          // Output: 70
System.out.println(marks.length);      // Output: 4

// int[] mixed = {20, "Java", true};   // invalid - mixed types

C and C++ also offer structs, unions and pointers as derived types. Java dropped all three on purpose, so arrays are the only derived type left standing.

10.4 Class: Your Own Data Type

A class lets you invent a type Java never shipped. Need a Student? Build one. Need an Invoice or a Booking? Same story.

Unlike an array, a class can mix types freely. Bundle an int, a String and a boolean into one tidy package.

package com.javahandson.oops;

public class Student {

    int studentId;
    String name;
    String standard;

    public int getStudentId() {
        return studentId;
    }

    public void setStudentId(int studentId) {
        this.studentId = studentId;
    }

    public String getName() {
        return name;
    }

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

    public String getStandard() {
        return standard;
    }

    public void setStandard(String standard) {
        this.standard = standard;
    }
}

Now Student works like any other type. Declare it, pass it to methods, drop it into a list. You just extended the language.

Student s = new Student();
s.setStudentId(101);
s.setName("Riya");
System.out.println(s.getName()); // Output: Riya

10.5 Interface and Enum

Two more non-primitives round out the set.

An interface names a contract without any code behind it. Say Runnable and everyone knows a run() method exists. It is still a type, so you can declare variables of it.

An enum pins a variable to a fixed list of constants. Days of the week, order status, payment mode: all perfect fits.

enum Status { ACTIVE, INACTIVE, BLOCKED }

Status current = Status.ACTIVE;
System.out.println(current);          // Output: ACTIVE

11. Type Casting: Moving Between Types

Sooner or later you need a value to hop from one type to another. That hop is casting, and it comes in two flavours.

11.1 Widening: The Implicit Cast

Pouring a small glass into a big jug is safe. Nothing spills. Java feels the same way about moving a small type into a bigger one, so it does the job for you.

byte -> short -> int -> long -> float -> double
        char -> int -> long -> float -> double
int i = 100;
long l = i;        // int widens to long, no cast written
double d = l;      // long widens to double

System.out.println(l);  // Output: 100
System.out.println(d);  // Output: 100.0

Widening is automatic and lossless for whole numbers. You write no extra syntax, and Java promises the value survives intact.

11.2 Narrowing: The Explicit Cast

The other direction is risky. Pour a jug into a glass and it overflows. Java therefore refuses to do it silently, and demands a cast in brackets.

double price = 99.99;
int rounded = (int) price;      // explicit cast
System.out.println(rounded);    // Output: 99  (decimal chopped off)

long big = 130L;
byte b = (byte) big;
System.out.println(b);          // Output: -126  (wrapped around!)

Read that cast as a signed note: “I know this may lose data, and I accept it.” Java trusts you and moves on.

11.3 What a Narrowing Cast Really Does

Two very different things can go wrong, so keep them straight.

  • Truncation: casting double to int chops off the decimals. It does not round. So 99.99 lands on 99, and -2.7 lands on -2.
  • Bit dropping: casting long to byte keeps only the lowest 8 bits and throws the rest away. That is why 130 came back as -126.

Where did -126 come from? The value 130 in 8 bits is 10000010. Byte reads the leading 1 as a negative sign, and the pattern means -126. Precision does not shrink here; the number itself changes meaning.

12. Overflow: Crossing the Range Line

12.1 Compile Time vs Runtime

Here is the distinction that separates a beginner from someone who has debugged production code.

Write a literal outside the range and the compiler catches it. Nothing runs; you fix the line and move on.

// byte b = 200;
// compile error: incompatible types, possible lossy conversion from int to byte

Compute your way past the range at runtime, though, and Java says nothing. No exception, no warning, no crash. The value simply wraps around and your program carries on with garbage.

12.2 The Wrap-Around

Picture the range as a clock face. Step one past the top and you land at the bottom, exactly like 12 rolling over to 1.

byte b = 127;
b++;
System.out.println(b);       // Output: -128   (wrapped, no error)

int max = Integer.MAX_VALUE; // 2,147,483,647
System.out.println(max + 1); // Output: -2147483648

int a = 100000;
int product = a * a;         // 10,000,000,000 does not fit in an int
System.out.println(product); // Output: 1410065408  (silent garbage)

That last one is genuinely dangerous. Both operands are ints, so Java multiplies them as ints and overflows before anything is copied anywhere. Assigning to a long afterwards cannot rescue it.

12.3 How to Stay Safe

  • Pick a wider type up front. When a count may pass two billion, start with long.
  • Force the maths into long by casting one operand: long product = (long) a * a;
  • Call Math.addExact() or Math.multiplyExact(), which throw an exception instead of wrapping.
  • Lean on BigInteger when even a long runs out of room.
int a = 100000;
long safe = (long) a * a;
System.out.println(safe);              // Output: 10000000000

// Math.multiplyExact(a, a);           // throws ArithmeticException: integer overflow

13. Wrapper Classes and Autoboxing

13.1 int vs Integer

Every primitive has an object twin, called a wrapper class. Think of the wrapper as a gift box: same value inside, but now it counts as an object.

Primitive Wrapper
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

Why bother? Collections refuse primitives. You cannot write List<int>, so you write List<Integer> instead.

Wrappers also accept null, which primitives cannot. A missing age reads naturally as a null Integer, while an int would have to fake it with 0 or -1.

13.2 Autoboxing and Unboxing

Since Java 5 the compiler swaps between the two forms on its own. Boxing wraps a primitive into an object; unboxing unwraps it again.

List<Integer> scores = new ArrayList<>();
scores.add(90);                 // autoboxing: int 90 becomes Integer

int first = scores.get(0);      // unboxing: Integer becomes int
System.out.println(first);      // Output: 90

Integer boxed = 5;              // autoboxing
int plain = boxed + 1;          // unboxing, then add
System.out.println(plain);      // Output: 6

Convenient, yes. Free, no. Each box allocates an object, so a tight loop doing millions of boxings will crawl. Keep primitives in hot code.

Unboxing a null wrapper also explodes with a NullPointerException, which is a classic 2 a.m. bug.

13.3 The Integer Cache Trap

Now a favourite interview puzzle. Compare two Integers with == and the answer flips depending on the value.

Integer a = 100, b = 100;
System.out.println(a == b);     // Output: true

Integer x = 200, y = 200;
System.out.println(x == y);     // Output: false  (!)

System.out.println(x.equals(y)); // Output: true

Java caches Integer objects from -128 to 127, so 100 reuses one shared object. The value 200 sits outside the cache, so each variable points at a fresh object, and == compares addresses.

The lesson is short. Use == for primitives, and .equals() for objects. Always.

14. Choosing the Right Type

14.1 Quick Rules That Work

Beginners agonise over this choice. In practice a handful of rules cover almost every case.

  • Whole numbers? Default to int and stop thinking about it.
  • Might the value pass two billion? Switch to long, especially for timestamps and IDs.
  • Decimals? Reach for double, not float.
  • Money or anything audited? Use BigDecimal, never a floating-point type.
  • Yes-or-no flag? That is a boolean.
  • One character? Pick char. Many characters? Pick String.
  • Huge arrays of tiny numbers, or raw binary data? Only then does byte earn its keep.

Notice how rarely short and float appear. Both exist for good historical reasons, yet modern code hardly touches them.

14.2 var in Java 10 and Later

Java 10 added var, and people immediately misread it. No, Java did not turn into JavaScript.

The type is still fixed and still checked. You simply let the compiler infer it from the right-hand side.

var count = 10;              // inferred as int
var name = "Riya";           // inferred as String
var list = new ArrayList<String>();  // inferred as ArrayList<String>

// count = "hello";          // compile error - count is locked to int

Three limits are worth remembering. var works on local variables only, never on fields or method parameters. It needs an initialiser on the same line. And var x = null; will not compile, because nothing can be inferred from null.

Use it when the type is obvious from the right side. Skip it when hiding the type would make a reader guess.

15. Common Mistakes

15.1 A float Without the f

Every decimal literal starts life as a double. Drop it into a float without the suffix and the compiler stops you.

// float rate = 7.5;   // error: possible lossy conversion from double to float
float rate = 7.5f;     // fixed

15.2 Quiet byte Overflow

A literal out of range fails at compile time, which is friendly. Arithmetic that drifts out of range fails silently, which is not.

byte b = 127;
b += 1;                  // compound assignment hides an implicit cast
System.out.println(b);   // Output: -128, and not a single warning

Sneaky detail: b += 1 compiles even though b = b + 1 would not. The compound operator slips in a hidden cast on your behalf.

15.3 Comparing Strings With ==

For primitives, == compares values and behaves exactly as you expect. For objects it compares references instead.

String a = "java";
String b = "java";
String c = new String("java");

System.out.println(a == b);        // Output: true   (same pooled literal)
System.out.println(a == c);        // Output: false  (different object)
System.out.println(a.equals(c));   // Output: true   (same characters)

The first line lulls you into a false sense of safety, because string literals share a pool. Feed the same code a value from a file or a form, and == starts returning false. Just use .equals() everywhere.

15.4 Integer Division Truncation

Divide an int by an int and you get an int. The decimal part vanishes before any assignment happens.

int total = 7, count = 2;

double wrong = total / count;
System.out.println(wrong);   // Output: 3.0   (not 3.5!)

double right = (double) total / count;
System.out.println(right);   // Output: 3.5

Look closely at the first case. Java divides 7 by 2 as ints, gets 3, and only then widens it to 3.0. Casting one operand first fixes everything.

Averages, percentages and progress bars all fall into this pit. Cast early and you never see it again.

16. Interview Questions

Q: How many primitive data types does Java have, and what are they?

A: Java has exactly eight primitives, in four groups. Integers: byte, short, int, long. Floating-point: float, double. Character: char. Logical: boolean. That list is fixed and has not changed since Java 1.0.

Q: What is the range of a byte in Java, and how do you calculate it?

A: A byte spans -128 to 127. The formula for an n-bit signed type is -2^(n-1) to 2^(n-1) – 1. A byte holds 8 bits, so the range works out to -2^7 to 2^7 – 1, which gives -128 to 127. Beware of tutorials printing “-127 to 128”; that is simply wrong.

Q: Why does the negative range go one further than the positive range?

A: Zero takes one slot on the positive side. An 8-bit type has 256 patterns in total, so the positives cover 0 to 127 (128 slots) and the negatives cover -128 to -1 (another 128 slots). Nothing goes to waste.

Q: Why does char take 2 bytes in Java when C uses only 1?

A: C relies on ASCII, and 1 byte gives 2^8 = 256 characters, which covers English. Java targets international text, so it adopted Unicode. Two bytes give 2^16 = 65,536 characters, enough room for Hindi, Chinese, Arabic, Greek and many more scripts.

Q: What are the default values of the primitive data types?

A: byte, short and int default to 0. A long defaults to 0L, a float to 0.0f and a double to 0.0d. A char defaults to ‘\u0000’, the null character, and a boolean defaults to false. Any non-primitive, including String, defaults to null.

Q: Do local variables get default values?

A: No, and this trips up nearly everyone. Only fields receive defaults, because the JVM zeroes an object’s memory on creation. Local variables sit on the stack and stay uninitialised, so reading one before you assign it causes a compile error.

Q: Why is String a non-primitive data type?

A: String is a class in java.lang that extends Object and carries methods such as length(), concat() and contains(). Only a class can extend another class or hold methods. A String variable also stores a reference and defaults to null, so it is predefined but definitely non-primitive.

Q: What is the difference between widening and narrowing casting?

A: Widening moves a smaller type into a larger one, such as int to long. Java performs it automatically and no data is lost. Narrowing moves a larger type into a smaller one, such as double to int, and it needs an explicit cast because the value may lose precision or wrap around.

Q: What happens when an int overflows at runtime?

A: Nothing dramatic, and that is the danger. The value wraps around silently, so Integer.MAX_VALUE + 1 becomes -2147483648. Java throws no exception. Use a long, cast one operand before the maths, or call Math.addExact() to make the overflow throw.

Q: What is autoboxing, and why can int not go into a List?

A: Generics work only with objects, so List<int> does not compile and you write List<Integer> instead. Autoboxing lets the compiler convert an int to an Integer automatically, and unboxing converts it back. Both cost an allocation, so avoid them inside hot loops.

Q: Why does 0.1 + 0.2 not equal 0.3 in Java?

A: float and double store binary fractions, and 0.1 has no exact binary form, just as one third has no exact decimal form. The tiny rounding error surfaces as 0.30000000000000004. For money and any audited figure, use java.math.BigDecimal.

double a = 0.1;
double b = 0.2;
System.out.println(a + b); // 0.30000000000000004
System.out.println((a + b) == 0.3); // false

Q: Can you tell the size of a boolean in Java?

A: The specification never fixes one. Logically a boolean carries a single bit of information, yet a typical JVM spends a whole byte on it because hardware addresses bytes, not bits. So the honest answer is JVM-dependent.

17. Conclusion

Let us pull the threads together. A type fixes three things for a variable: its size in memory, the range of values it accepts, and the operations you may perform on it.

Java gives you eight primitives. Four hold whole numbers, two hold decimals, one holds a character, and one holds a truth value. Everything else, including String and arrays, sits in the non-primitive camp and stores a reference instead of a value.

Keep the numbers straight and you are ahead of most beginners. A byte covers -128 to 127, an int covers roughly plus or minus two billion, and the formula behind both is -2^(n-1) to 2^(n-1) – 1.

Watch the small traps too. Add f to float literals and L to long literals. Remember that fields get defaults while locals get none. Compare objects with .equals(), and cast before you divide.

Master this chapter and the rest of Java gets much easier. Variables, methods, arrays and classes all build straight on top of it.

Further Reading

Leave a Comment