Java Variables: Primitive Types, Type Casting, and String

Variables are the building blocks of every piece of Java code. Before you can understand classes, methods, or any other Java feature, you need to know how to store and name values in memory. This tutorial covers the eight primitive types that hold basic values, how Java's type system keeps your code type-safe, and why String behaves differently from all the rest.

The Eight Primitive Types

Java has eight primitive types. Each one stores a different kind of basic value. The "when to use" column answers the most common question: "Which int should I use?"

Type Size (bytes) Range (approx.) When to Use
byte 1 -128 to 127 File I/O streams, network protocol fields, binary data, large byte arrays
short 2 -32,768 to 32,767 File format fields, network protocol fields, memory-constrained environments (embedded systems)
int 4 -2,147,483,647 Counting, indexing, general-purpose integers — the default integer type
long 8 -9,223,372,036,854,775 Large counters, timestamps, database IDs, anything that might exceed 2 billion
float 4 -3.4 × 10^38 Graphics rendering (rarely used in business apps), memory-constrained environments, scientific computing
double 8 -1.7 × 10^308 Prices, measurements, calculations requiring decimal precision — the default decimal type
char 2 0 to 65,535 Single characters, simple status codes, character-level processing
boolean 1 0 or 1 (true/false) Flags and conditions — the only binary type

Don't Overthink the int Choice

If you're unsure whether to use int or long , use int . It's the default, and switching to long prematurely adds complexity with no benefit unless you actually need numbers larger than 2 billion. You can always switch to long later — the assignment is a one-line change.

Declaring and Initializing Variables

Java requires you to declare the type before the name. This is the one syntax rule you must follow:

Syntax: declaration only
int score;
Syntax: declaration + initialization
int score = 0;
Syntax: multiple variables, same type
int id = 1;
String name = "Widget Pro";
double price = 19.99;
Syntax: declaration then initialization
int quantity;
quantity = 5;
Syntax: multiple variables, different types
int id = 1;
String name = "Widget Pro";
double price = 19.99;
int quantity = 5;
Syntax: multiple variables on one line
int id = 1, name = "Widget Pro", price = 19.99, quantity = 5;

Notice the pattern: type name = value . The semicolon is required. The type comes first, then the name, then the value. You can declare multiple variables on one line as long as they share the same type.

Variables that are not initialized contain a default value. Using an uninitialized int score; gives it the default value of 0 .

Type Casting

Java is a statically typed language. Every variable has a type, and the compiler checks that the value you assign matches. When it doesn't match, you get a compile-time error. Type casting is how you tell the compiler "trust me, I know what I'm doing."

Why You Need to Cast

The most common reason is reading a number from a text source (user input, configuration file, API response). Text is a String , numbers are String , and they're incompatible. Casting converts between them:

Reading a double from a String
// This line causes a compile error:
String userInput = "42.5";
double price = Double.parseDouble(inputInput);  // OK

// This also causes a compile error:
String userInput = "42.5";
int count = (int) userInput);  // ERROR

The Narrowing Trap

Casting from a larger type to a smaller type can lose data. Java requires an explicit cast to confirm you accept the risk:

The narrowing trap (DON'T do this)
int bigNumber = 200;
byte smallNumber = bigNumber;          // ERROR: possible lossy conversion
byte smallNumber = (byte) bigNumber;  // OK — explicit cast
Console Output
>
Main.java:3: error: incompatible types: possible lossy conversion from int to byte
Main.java:4: error: incompatible types: possible lossy conversion from int to byte Main.java:6: error: incompatible types: possible lossy conversion from int to byte
Main.java:8: error: incompatible types: possible lossy conversion from int to byte
Main.java:10: error: incompatible types: possible lossy conversion from int to byte Main.java:12: error: incompatible types: possible lossy conversion from int to byte

Use parseLong() for Large Numbers

Integer.parseInt() only handles values that fit in an int range (-2,147,483,647 to 2,147,483,647). For anything larger, use Long.parseLong() . This comes up constantly when reading IDs, timestamps, and file sizes from databases.

The String Type

String is the one type that breaks the "all types are primitive" rule. It's actually a class (a reference type), not a primitive. But Java gives it special syntax to keep it simple to write:

String is a class, not a primitive
String name = "Hello";  // This creates a String object

Strings are immutable — once created, the value cannot be changed. You can't do name = name.replace("Hello", "Hi") . This makes Strings safe to use as map keys and return values from methods (you can't accidentally modify them). The immutability guarantee is why == doesn't work for String comparison — two different String objects that contain the same text are still different objects in memory.

== vs. .equals() for Strings
// WRONG — compares object identity (are these the same object?)
String a = "Hello";
String b = "Hello";
System.out.println(a == b);  // false — they're different objects

// CORRECT — compares the actual content
String a = "Hello";
String b = "Hello";
System.out.println(a.equals(b));  // true

Don't Use + for String Concatenation in Loops

Each + in a loop creates a new String object, which is wasteful. In the logging tutorial, we showed logger.log(Level.INFO, "Order {0} placed by {1}", orderId) with parameterized logging to avoid this problem. For a standalone script, this rarely matters, but in a server handling thousands of log messages per second, it matters a lot.

Variable Scope and the final Keyword

Scope determines where a variable is visible. A variable declared inside a block ({...}) is invisible outside it. A variable declared as a field in a class is visible to all methods in the class. Understanding scope prevents accidental name collisions — two different variables with the same name in different scopes don't interfere with each other.

The Scope Bug: Variable Shadowing

Variable shadowing bug
public class ScoreTracker {

    private int totalScore = 0;

    public void addScore(int points) {
        int total = totalScore + points;  // BUG: creates a LOCAL variable
    }
    public int getTotalScore() {
        return totalScore;  // Still 0 — the local variable
    }
}
Console Output
Main.java:3: error: variable totalScore is already defined

This bug happens because int totalScore = totalScore + points declares a new local variable that hides the field. The assignment targets the local variable, not the field. Fix it by renaming one of them.

The final Keyword

final means the variable can only be assigned once. It's most commonly used for constants — values that should never change during the program's lifetime:
Constants with final
public static final double TAX_RATE = 0.08;
Constants in a class (field)
public class static final double TAX_RATE = 0.08;

public static final String DEFAULT_TAX_RATE = "default";

Don't Use final for Everything

final is for values that truly never change — configuration values, fixed conversion rates, mathematical constants. Don't use it "just in case" — if a value might change, don't make it final. Premature finality makes code harder to modify later.

Summary

  • Variables are named storage locations for values in memory, identified by a type. Java requires the type to be declared before the name.
  • Java has eight primitive types: byte , short , int , long , float , double , char , boolean . Use int by default — it's the default integer type.
  • Type casting converts between types when they don't match. Use (Type) value syntax. Be especially careful with the narrowing trap (large type to smaller type loses data).
  • String is a class (a reference type), not a primitive. Use .equals() for comparison, not == . It's immutable — you can't modify a String object after creation. final marks a variable as constant. Use it for values that should never change. Don't use it "just in case."

Where to Go After Variables

Java Methods

Methods organize code into reusable, named blocks. Variables are the data that methods operate on. The Methods tutorial shows you how to define parameters, return values, and call methods.

Classes and Objects

Classes define the structure — fields and methods as a single unit. Variables are the data inside classes. The Classes tutorial shows you how to design them well.

Exception Handling

When things go wrong, exceptions prevent crashes and give you control over the error handling. The Exception Handling tutorial shows you try/catch/finally, custom exceptions, and try-with-resources.