Data Types & Operators in Java
2531 words
13 min read
Visual companion
Python
Type and operator map
Python Week 1: the first filter for runtime behavior
View
Revision summary
What this note is really saying
Short form
# Data Types & Operators in Java ## 🎯 Learning Objectives By the end of this topic, you will be able to: - Distinguish between primitive types (int, double, char, boolean) and reference types - Declare, initialize, and use variables of all primitive types - Perform type casting (widening and narrowing) safely - Use...

Data Types & Operators in Java
🎯 Learning Objectives
By the end of this topic, you will be able to:
- Distinguish between primitive types (int, double, char, boolean) and reference types
- Declare, initialize, and use variables of all primitive types
- Perform type casting (widening and narrowing) safely
- Use arithmetic, relational, logical, assignment, and bitwise operators
- Understand overflow, underflow, and precision issues
📋 Prerequisites
- Java Introduction (week01/01-java-introduction.md): Compilation, JVM, Hello World
- BSCS1002 — Python: You've used variables and operators in Python; Java is more explicit about types
1. Intuition: Why Do We Need Types?
1.1 What Problem Does This Solve?
Imagine a warehouse where boxes of different sizes hold different kinds of items: small boxes for screws (4 bytes), medium boxes for books (8 bytes), and large crates for furniture (any size). If you know the box size, you know exactly how many shelves (memory slots) to allocate.
In Python, you don't declare types — a variable can hold a number, then a string, then a list. The interpreter figures it out at runtime. Java is different: you must declare the type of every variable before using it. This is called static typing.
Why it matters: Static typing catches type errors at compile-time, not runtime. If you try to store "hello" in anintvariable,javacrefuses to compile. This eliminates entire categories of bugs before the program even runs.
1.2 Mental Model: Variables as Labeled Boxes
Every variable in Java is a labeled box of a fixed size and shape:
javaint age = 25; ↓ ┌────────────────┐ │ age │ ← label (variable name) ├────────────────┤ │ 25 │ ← value (stored in memory) └────────────────┘ Size: 4 bytes Shape: can only hold integers
You cannot put a "different-shaped" value (like 3.14 or "hello") into this box — the compiler prevents it.
2. Primitive Types — The Building Blocks
Java has exactly 8 primitive types. They are not objects — they hold raw values directly in memory.
2.1 The Eight Primitive Types
| Type | Size | Range | Default | Example | Use Case |
|---|---|---|---|---|---|
byte | 1 byte | -128 to 127 | 0 | byte b = 100; | Saving memory in large arrays |
short | 2 bytes | -32,768 to 32,767 | 0 | short s = 10000; | Saving memory when range fits |
int | 4 bytes | -2³¹ to 2³¹−1 | 0 | int i = 42; | Default integer type |
long | 8 bytes | -2⁶³ to 2⁶³−1 | 0L | long l = 100L; | Large numbers (timestamps, IDs) |
float | 4 bytes | ±3.4×10⁻³⁸ to ±3.4×10³⁸ | 0.0f | float f = 3.14f; | Scientific calculations (memory-saving) |
double | 8 bytes | ±1.7×10⁻³⁰⁸ to ±1.7×10³⁰⁸ | 0.0d | double d = 3.14159; | Default decimal type |
char | 2 bytes | 0 to 65,535 (Unicode) | '\u0000' | char c = 'A'; | Single Unicode characters |
boolean | JVM-dependent | true or false | false | boolean b = true; | Logical conditions |
2.2 Important Details
Integer Literals:
javaint decimal = 42; // Decimal (base 10) int hex = 0x2A; // Hexadecimal (base 16) — starts with 0x int binary = 0b101010; // Binary (base 2) — starts with 0b int octal = 052; // Octal (base 8) — starts with 0 (avoid confusion!) int big = 1_000_000; // Underscores for readability (Java 7+)
Floating-Point Literals:
javadouble d1 = 3.14; // double by default double d2 = 3.14d; // explicit d suffix float f1 = 3.14f; // f suffix REQUIRED for float double sci = 1.23e-4; // Scientific notation: 0.000123
Common Mistake:float f = 3.14;fails because3.14is adoubleliteral. Always addfsuffix for floats. Character Literals:
javachar letter = 'A'; // Single quotes, NOT double char digit = '9'; // Character 9, not integer 9 char newline = '\n'; // Escape sequence char unicode = '\u0041'; // Unicode escape — same as 'A'
3. Reference Types — Objects That Live on the Heap
3.1 Primitive vs Reference
| Aspect | Primitive | Reference |
|---|---|---|
| Stores | The actual value | A memory address (pointer) to an object |
| Where | Stack (local variables) | Stack → Heap (the object) |
| Default | 0, 0.0, false, '\u0000' | null (no object) |
| Methods | Cannot call methods | Can call object methods |
| Equality | == compares values | == compares references (not content!) |
3.2 Memory Model
javaint x = 10; // x on stack, value=10 String s = "Hello"; // s on stack → points to "Hello" on heap
(Diagram)
3.3 The Special String Type
String is a reference type, but Java treats it specially:javaString s1 = "Hello"; // String literal (stored in string pool) String s2 = "Hello"; // Reuses same object from pool String s3 = new String("Hello"); // Forces new object on heap System.out.println(s1 == s2); // true — same reference System.out.println(s1 == s3); // false — different references System.out.println(s1.equals(s3)); // true — same content
Key Insight: Always use.equals()to compare String content. The==operator only compares references (memory addresses), not the text inside.
4. Type Casting — Converting Between Types
4.1 Widening (Implicit) Casting
Java automatically converts a smaller type to a larger type when no data is lost:
javaint myInt = 100; long myLong = myInt; // OK: int → long (widening) double myDouble = myInt; // OK: int → double (widening) byte b = 50; int i = b; // OK: byte → int (widening)
Widening order:
byte → short → int → long → float → double (The chain also includes char → int)4.2 Narrowing (Explicit) Casting
When converting a larger type to a smaller type, you must explicitly cast — data may be lost:
javadouble pi = 3.14159; int truncated = (int) pi; // Explicit cast: double → int System.out.println(truncated); // 3 (fractional part lost!) long big = 1_000_000_000_000L; int small = (int) big; System.out.println(small); // Unpredictable — overflow!
4.3 Overflow Examples
javaint max = Integer.MAX_VALUE; // 2,147,483,647 int overflow = max + 1; System.out.println(overflow); // -2,147,483,648 (wraps around!) int underflow = Integer.MIN_VALUE - 1; System.out.println(underflow); // 2,147,483,647 (wraps around!)
(Diagram)
5. Operators in Java
5.1 Arithmetic Operators
| Operator | Meaning | Example | Result |
|---|---|---|---|
+ | Addition | 5 + 3 | 8 |
- | Subtraction | 5 - 3 | 2 |
* | Multiplication | 5 * 3 | 15 |
/ | Division | 5 / 2 | 2 (integer!) |
/ | Division (double) | 5.0 / 2 | 2.5 |
% | Modulus (remainder) | 5 % 2 | 1 |
5.2 Integer Division Trap — Common Mistake
javaint a = 5; int b = 2; double result = a / b; // result = 2.0, NOT 2.5! // Because a/b is integer division first (5/2=2), then promoted to double double correct = (double) a / b; // result = 2.5
5.3 Relational Operators
| Operator | Meaning | Example | Result |
|---|---|---|---|
== | Equal to | 5 == 3 | false |
!= | Not equal to | 5 != 3 | true |
> | Greater than | 5 > 3 | true |
< | Less than | 5 < 3 | false |
>= | Greater or equal | 5 >= 3 | true |
<= | Less or equal | 5 <= 3 | false |
5.4 Logical Operators
| Operator | Meaning | Short-circuit? | Example |
|---|---|---|---|
&& | AND | Yes | (x > 0) && (x < 100) |
| ` | ` | OR | |
! | NOT | N/A | !isEmpty |
& | AND (non-short-circuit) | No | (x > 0) & (x < 100) |
| ` | ` | OR (non-short-circuit) | No |
^ | XOR | No | (x > 0) ^ (x < 0) |
Short-circuit means: If the left side of
&& is false, the right side is never evaluated. Similarly for ||: if left is true, right is skipped.javaString s = null; if (s != null && s.length() > 0) { // SAFE: short-circuit prevents null pointer System.out.println("Not empty"); } if (s != null & s.length() > 0) { // UNSAFE: & evaluates both sides — NullPointerException! System.out.println("Not empty"); }
5.5 Assignment Operators
| Operator | Example | Equivalent to |
|---|---|---|
= | x = 5 | x = 5 |
+= | x += 3 | x = x + 3 |
-= | x -= 3 | x = x - 3 |
*= | x *= 3 | x = x * 3 |
/= | x /= 3 | x = x / 3 |
%= | x %= 3 | x = x % 3 |
5.6 Increment/Decrement Operators
| Operator | Name | Example | Effect |
|---|---|---|---|
++x | Pre-increment | int y = ++x; | Increment x, then assign to y |
x++ | Post-increment | int y = x++; | Assign x to y, then increment x |
--x | Pre-decrement | int y = --x; | Decrement x, then assign to y |
x-- | Post-decrement | int y = x--; | Assign x to y, then decrement x |
javaint x = 5; int y = ++x; // x=6, y=6 (increment first) int z = x++; // x=7, z=6 (assign first, increment after)
5.7 Bitwise Operators
| Operator | Meaning | Example |
|---|---|---|
& | Bitwise AND | 5 & 3 → 1 (0101 & 0011 = 0001) |
| ` | ` | Bitwise OR |
^ | Bitwise XOR | 5 ^ 3 → 6 (0101 ^ 0011 = 0110) |
~ | Bitwise complement | ~5 → -6 (inverts all bits) |
<< | Left shift | 5 << 1 → 10 (multiply by 2) |
>> | Right shift (signed) | 5 >> 1 → 2 (divide by 2) |
>>> | Right shift (unsigned) | -5 >>> 1 → 2147483645 |
6. Java vs Python: Types & Operators
| Feature | Java | Python |
|---|---|---|
| Type declaration | Required: int x = 5; | Optional (dynamic): x = 5 |
| Type checking | Static (compile-time) | Dynamic (runtime) |
| Primitive types | 8 fixed-size primitives | Everything is an object (even ints) |
| Type conversion | Explicit narrowing cast required | Implicit widening, but may raise exceptions |
| Increment | x++, ++x, x--, --x | Not available (x += 1 only) |
| Ternary | x > 0 ? "pos" : "neg" | Same syntax "pos" if x > 0 else "neg" |
| String comparison | s1.equals(s2) for content | s1 == s2 compares content (strings are interned) |
7. Common Pitfalls
Pitfall 1: Integer Division
javadouble result = 5 / 2; // result = 2.0, not 2.5!
Why: Both operands are
int, so / performs integer division (truncates). The result 2 is then promoted to 2.0. Fix: Make at least one operand a double: 5.0 / 2 or (double)5 / 2.Pitfall 2: Comparing Strings with ==
javaString s1 = "Hello"; String s2 = new String("Hello"); System.out.println(s1 == s2); // false!
Why:
== compares references (memory addresses), not content. s1 and s2 point to different objects. Fix: Use s1.equals(s2) for content comparison.Pitfall 3: Float Without f Suffix
javafloat f = 3.14; // Compile error!
Why:
3.14 is a double literal. You can't store a double in a float without explicit casting. Fix: float f = 3.14f; or float f = (float) 3.14;Pitfall 4: Overflow Without Warning
javaint population = 9_000_000_000; // Compile error (too large for int)
Fix: Use
long and L suffix: long population = 9_000_000_000L;Pitfall 5: Uninitialized Local Variables
javaint x; System.out.println(x); // Compile error!
Why: Local variables in Java are NOT given default values. The compiler forces you to initialize them. Fix:
int x = 0; before using it.8. Practice Questions
Q1: What is the output of this code?javaint a = 10; double b = 3.5; int c = a + (int) b; System.out.println(c);Answer:13Reasoning:
(int) bcasts 3.5 to 3 (truncation, not rounding)a + 3=10 + 3=13- Result stored in
int c, prints13Q2: What is the output?javaint x = 5; int y = x++ * 2; System.out.println(x + ", " + y);Answer:6, 10Reasoning:
x++is post-increment: uses current value5, then incrementsxto6y = 5 * 2 = 10- After the line,
x = 6,y = 10Q3: Explain the outputjavaSystem.out.println(10 / 3); System.out.println(10.0 / 3); System.out.println(10 / 3.0);Answer:pseudo3 3.3333333333333335 3.3333333333333335
10 / 3: Both operands are int → integer division →310.0 / 3: One operand is double → floating-point division →3.333...10 / 3.0: Same as above →3.333...Q4: What is the value of result?javaboolean result = (10 > 5) && (5 < 2) || (3 == 3);Answer:trueReasoning:
(10 > 5)=true(5 < 2)=falsetrue && false=false(3 == 3)=truefalse || true=trueOperator precedence:&&has higher precedence than||, so(true && false) || true=false || true=true. Q5: Why does this code fail to compile?javapublic class Test { public static void main(String[] args) { int x; if (args.length > 0) { x = Integer.parseInt(args[0]); } System.out.println(x); } }Answer: Compilation error: "variable x might not have been initialized."The compiler sees thatxis only assigned inside theifblock, which may not execute. Java requires all local variables to be definitely assigned before use. Fix by givingxa default value:int x = 0;Q6: What happens if you cast a large long to int?javalong big = 1_000_000_000_000L; int small = (int) big; System.out.println(small);Answer: The output is-727379968(or some unexpected number).Reasoning:longvalue1,000,000,000,000in binary is larger than the 32 bits anintcan hold. The cast truncates to the lower 32 bits, which may result in a completely different value (including negative). There is no exception — Java silently truncates. Q7: What is the output?javaSystem.out.println(5 & 3); System.out.println(5 | 3); System.out.println(5 ^ 3); System.out.println(5 << 1);Answer:pseudo1 7 6 10Reasoning:
5 = 0101,3 = 00110101 & 0011 = 0001 = 10101 | 0011 = 0111 = 70101 ^ 0011 = 0110 = 65 << 1 = 1010 = 10(multiply by 2) Q8: What is the difference between && and & in Java?Answer:
&&is short-circuit AND: if the left operand isfalse, the right operand is NOT evaluated&is non-short-circuit AND: both operands are always evaluatedExample:javaString s = null; if (s != null && s.length() > 0) { } // Safe — short-circuit prevents NPE if (s != null & s.length() > 0) { } // Throws NullPointerException!Q9: What is the range of a byte and why does Java use it?Answer: Abytestores values from -128 to 127 (inclusive), using 1 byte (8 bits) of memory. It's used when:
- Working with raw binary data (file I/O, network protocols)
- Saving memory in large arrays (e.g.,
byte[]vsint[]saves 75% memory)- Interacting with systems that use 8-bit data
Range calculation: 2⁸ = 256 possible values. Since Java uses two's complement for negatives: -128 to 127. Q10: What are the default values for primitives in Java?Answer (applies to fields, NOT local variables):
byte,short,int:0long:0Lfloat:0.0fdouble:0.0dchar:'\u0000'(null character)boolean:false- Any reference type:
nullLocal variables do NOT get default values — they must be explicitly initialized before use.
📐 Key Formulas / Concepts
| Concept | Syntax | Example |
|---|---|---|
| Widening conversion | Automatic | int → long |
| Narrowing conversion | Explicit (type) | (int) 3.14 |
| Integer division | / with two ints | 5/2 = 2 |
| Modulus | % | 5%2 = 1 |
| Post-increment | x++ | Uses then increments |
| Pre-increment | ++x | Increments then uses |
| Short-circuit AND | && | Right side skipped if left false |
| Short-circuit OR | || | Right side skipped if left true |
🔗 Cross-References
- Next: Memory Model — how stack and heap work
- Control Flow: week01/04-control-flow.md
- Python Comparison: BSCS1002 — Python Variables & Data Types
- Reference: Oracle Java Tutorials — Primitive Data Types Join Discord Previous1.1 Java IntroductionNext1.3 Memory Model