Java Academy

Master Java Development

Menu

Core Java

Servlets

JDBC

Microservices

Build Tools

APIs

Core Java · Lesson 3

Variables and Data Types

Understanding Variables and Data Types

A variable is a named container that stores a value in memory, and every variable has a data type that determines what kind of data it can hold. Java enforces strict type checking, meaning you declare a variable's type once and can only store compatible values in it. This prevents bugs and makes code more predictable — the compiler catches type errors before your program runs, unlike dynamically-typed languages.

Primitive Data Types

Java has eight primitive data types, which are the basic building blocks for storing numbers, characters, and boolean values. Unlike objects, primitives are stored directly in memory and are extremely efficient. Here are the types organized by their purpose:

TypeSizeRangePurpose
byte8 bits-128 to 127Small integer values
short16 bits-32,768 to 32,767Larger small integers
int32 bits-2.1B to 2.1BStandard integer type
long64 bits~-9.2E18 to 9.2E18Very large integers
float32 bits~±1.4E-45 to ±3.4E38Single-precision decimals
double64 bits~±4.9E-324 to ±1.7E308Double-precision decimals (preferred)
char16 bits0 to 65,535Single Unicode character
boolean1 bittrue or falseLogical values
public class PrimitiveTypes {
    public static void main(String[] args) {
        // Integer types
        byte smallNumber = 100;           // fits in 8 bits
        short mediumNumber = 30000;       // fits in 16 bits
        int regularNumber = 1000000;      // most common for integers
        long bigNumber = 9000000000L;     // note the 'L' suffix for long
        
        // Floating-point types
        float approximateValue = 3.14f;   // 'f' suffix for float
        double preciseValue = 3.14159265; // 'double' is the default
        
        // Character and boolean
        char letter = 'A';                // single character in quotes
        boolean isJavaFun = true;         // logical true/false
        
        System.out.println("byte: " + smallNumber);
        System.out.println("long: " + bigNumber);
        System.out.println("double: " + preciseValue);
        System.out.println("Is Java fun? " + isJavaFun);
    }
}

Reference Data Types: Objects and Arrays

Beyond primitives, reference data types store references (memory addresses) to objects on the heap. The most common reference types are classes, interfaces, and arrays. When you create a variable of a reference type like String or ArrayList, the variable holds a pointer to the actual object in memory, not the data itself. This distinction matters: copying a reference doesn't copy the object.

public class ReferenceTypes {
    public static void main(String[] args) {
        // String is a reference type (object)
        String message = "Hello, Java!";
        String copy = message;  // copy contains reference to same object
        
        // Arrays are reference types
        int[] scores = {85, 90, 78, 92};
        String[] names = {"Alice", "Bob", "Charlie"};
        
        // Custom objects
        Person developer = new Person("Eve", 28);
        developer.printInfo();
        
        System.out.println("Message: " + message);
        System.out.println("First score: " + scores[0]);
    }
}

class Person {
    private String name;
    private int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    void printInfo() {
        System.out.println("Name: " + name + ", Age: " + age);
    }
}

Variable Declaration, Initialization, and Scope

Declaration introduces a variable with a type, initialization assigns it a first value, and scope determines where that variable is accessible. Variables declared inside a method are local variables and only exist while that method is running. Variables declared inside a class are instance variables and persist as long as the object exists. Understanding scope prevents errors like "variable not found" and keeps code organized.

public class VariableScope {
    private String globalMessage = "I'm an instance variable";  // scope: entire object
    
    public static void main(String[] args) {
        String localVar = "I'm local to main";  // scope: only inside main()
        
        if (true) {
            int blockVar = 42;                  // scope: only inside this if block
            System.out.println(blockVar);       // OK
        }
        // System.out.println(blockVar);       // ERROR: blockVar doesn't exist here
        
        demonstrateScope();
    }
    
    static void demonstrateScope() {
        String methodVar = "Local to this method";  // scope: only in demonstrateScope()
        System.out.println(methodVar);
    }
}

Type Conversion: Implicit and Explicit Casting

Sometimes you need to convert a value from one type to another. Implicit casting (widening) happens automatically when converting a smaller type to a larger one with no data loss (e.g., int to double). Explicit casting (narrowing) requires you to manually cast when converting a larger type to smaller, potentially losing precision or data — the compiler forces you to acknowledge the risk.

public class TypeConversion {
    public static void main(String[] args) {
        // Implicit casting (widening) — safe, automatic
        int wholeNumber = 100;
        double decimalNumber = wholeNumber;  // int → double automatically
        System.out.println("Implicit: " + decimalNumber);  // 100.0
        
        // Explicit casting (narrowing) — must specify, may lose data
        double pi = 3.14159;
        int truncated = (int) pi;  // double → int (note the cast)
        System.out.println("Explicit: " + truncated);  // 3 (decimal part lost)
        
        // String conversion
        int age = 25;
        String ageText = String.valueOf(age);  // int → String
        int parsedAge = Integer.parseInt(ageText);  // String → int
        System.out.println("Converted: " + parsedAge);
    }
}

Common Mistake: Assuming == compares object contents. For reference types like String, use .equals() instead. string1 == string2 compares if they reference the same object, not if they have the same value. This catches many developers off guard when comparing Strings or other objects.

Best Practices for Variables and Types

  • Choose the right type: use int for most integers, double for decimals, String for text
  • Initialize variables before use: Java requires this; prevents mysterious null errors
  • Use meaningful variable names: totalScore is clearer than ts or x
  • Declare variables close to use: reduces scope and makes code easier to follow
  • Prefer double over float: doubles have more precision and are rarely slower
  • Be explicit with casts: even though Java may allow implicit casts, explicit ones show intent

Real-World Example: Processing Student Grades

A grade management system uses int for scores (0–100), double for GPAs (4.0 scale), String for student names, and boolean for enrollment status. When calculating class averages, the system implicitly casts scores to double for precision. When importing from a CSV file, it parses String values to int using Integer.parseInt(). This mix of types, combined with proper casting and conversion, allows the system to store, calculate, and report student progress accurately.

Quiz Check

Quick Knowledge Test

Loading quiz questions...