Tags: java
Java is a statically typed language , which means that every variable, method parameter, and expression must have a defined type known at compile time. This helps catch errors early and improves code reliability.A type in Java defines: the kind of value a variable can hold and what operations that can be performed on it.
int count = 42; // int: arithmetic allowed
String name = "Ana"; // String: you can call name.length()
All types in Java fall into two broad categories:
Type Category | Examples | Stored In | Can be null? | |
---|---|---|---|---|
Primitive Types |
int , double , boolean
|
Stack | ❌ No | |
Reference Types |
String , int[] , Runnable
|
Stack (ref) + Heap (value) | ✅ Yes |
Java has 8 primitive types:int, long, double, float, boolean, char, byte, short.
Fast and memory-efficient
Cannot be null — so never throw NullPointerException
Not objects — you cannot call methods on them (x.length() ❌)
Reference types refer to objects stored on the heap.
Examples include:
Classes (String, Scanner, custom types)
Interfaces (Runnable, Function<T,R>)
Arrays (int[], String[])
Lambdas (assigned to functional interfaces)
Enums
String s = "Hello";
Runnable r = () -> System.out.println("Hi");
int[] arr = new int[] {1, 2, 3};
These variables hold references, not actual data. The data lives in the heap, while the reference lives on the stack.
Yes — even arrays and lambdas:
Object o1 = "Hello"; // String is a subclass of Object
Object o2 = new int[] {1, 2}; // Arrays extend Object
Object o3 = (Runnable) () -> {}; // Lambdas are compiled into classes implementing interfaces
All reference types can be upcast to Object
They all have methods like .toString(), .equals(), and .hashCode()
Java allows primitives to be wrapped in object form via boxing:
int x = 5;
Integer boxed = x; // Autoboxing (int → Integer)
int unboxed = boxed; // Auto-unboxing (Integer → int)
public class Box {
private Object value;
public void set(Object value) { this.value = value; }
public Object get() { return value; }
}
Box b = new Box();
b.set("hello");
String s = (String) b.get(); // Manual cast required
No compile-time type safety
Must cast manually (error-prone)
Can easily throw ClassCastException
public class Box<T> {
private T value;
public void set(T value) { this.value = value; }
public T get() { return value; }
}
Box<String> b = new Box<>();
b.set("hello");
String s = b.get(); // no cast needed
Type-safe at compile time
Easier to read and maintain
Prevents runtime errors
Use wildcards when you're writing generic code for flexible input types, but you don’t need to know or change the exact type.
public void printBox(Box<?> box) {
System.out.println(box.get()); // value is safely treated as Object
}
Technique | Type Safe? | Cast Required? | Can Read? | Can Write? | Use When… |
---|---|---|---|---|---|
Object |
❌ No | ✅ Yes | ✅ Yes | ✅ Yes | Avoid unless truly generic or legacy |
<T> |
✅ Yes | ❌ No | ✅ Yes | ✅ Yes | You know the type ahead of time |
<?> |
✅ Yes | ❌ No | ✅ Yes | ❌ No | You don’t know the exact type |