Java variables primitive types String casting equals guide FP2

Variables in Java — primitive types, String, final and casting from scratch

Variables in Java are more explicit than in Python — every variable has a fixed type declared at creation that never changes. In Python you wrote x = 5 and Python figured the type out. In Java you write int x = 5 and the compiler enforces that x will always be an integer. This article covers every type you’ll use in FP2, how they work in memory, and the differences that cause the most confusion when coming from Python.

Primitive types — the eight basic types

Java has eight primitive types. These are not objects — they’re raw values stored directly in memory:

byte    b = 127;          // 1 byte:  -128 to 127
short   s = 32000;        // 2 bytes: -32,768 to 32,767
int     i = 1000000;      // 4 bytes: -2,147,483,648 to 2,147,483,647
long    l = 9000000000L;  // 8 bytes: very large numbers — note the L suffix
float   f = 3.14f;        // 4 bytes: ~7 decimal digits — note the f suffix
double  d = 3.14159265;   // 8 bytes: ~15 decimal digits
char    c = 'A';          // 2 bytes: single Unicode character — single quotes
boolean b = true;         // 1 bit:   true or false

In FP2 you’ll use four of these constantly: int, double, char and boolean. The others appear occasionally.

The types you use most

int age = 20;                 // whole numbers
double grade = 7.85;          // decimal numbers — always use double, not float
char initial = 'S';           // single character — single quotes mandatory
boolean passing = true;       // true or false — lowercase, not True/False

Python vs Java types — direct comparison

Python      Java            Notes
────────────────────────────────────────────────────
int         int / long      Java int has a maximum (~2 billion)
float       double          Java float exists but double is better
str         String          Java String is a class, not a primitive
bool        boolean         Python True/False → Java true/false
(no equiv)  char            Python has no single-character type

Variable declaration rules

In Java every variable must be declared with its type before use:

// Declaration without value — contains garbage (must assign before reading)
int x;

// Declaration with value
int x = 5;

// Multiple declarations of the same type
int a = 1, b = 2, c = 3;

// Cannot change type
int x = 5;
x = "hello";    // compile error — x is int, not String
x = 3.14;       // compile error — 3.14 is double, not int

The last two lines would be valid Python. In Java they’re compile errors. The type is fixed at declaration time — that’s static typing.

Naming conventions in Java variables

Java uses specific naming conventions that FP2 follows strictly:

// Variables and methods — camelCase (starts lowercase)
int studentAge = 20;
double averageGrade = 7.85;
boolean isPassingYear = true;

// Classes — PascalCase (starts uppercase)
class StudentProfile { }
class BankAccount { }

// Constants — ALL_CAPS with underscores
final double PI = 3.14159;
final int MAX_STUDENTS = 50;

// Packages — all lowercase
package com.sergiolearns.fp2;

Following these conventions is important in FP2 — many exercises are graded partly on style.

String — not a primitive

String is a class in Java, not a primitive type. This has important consequences:

String name = "Sergio";          // declare with capital S
String greeting = "Hello, " + name + "!";    // concatenation with +
String empty = "";                // empty string
String multiline = "Line 1\nLine 2";

String methods you’ll use constantly in FP2

String s = "Hello, World!";

s.length()              // → 13 (number of characters)
s.charAt(0)             // → 'H' (character at index 0)
s.substring(7, 12)      // → "World" (from index 7 to 11)
s.toLowerCase()         // → "hello, world!"
s.toUpperCase()         // → "HELLO, WORLD!"
s.trim()                // → removes leading/trailing spaces
s.contains("World")     // → true
s.startsWith("Hello")   // → true
s.endsWith("!")         // → true
s.replace("World", "Java")  // → "Hello, Java!"
s.indexOf("World")      // → 7 (position of first match)
s.split(", ")           // → ["Hello", "World!"] (String array)
s.isEmpty()             // → false

The == vs .equals() trap — most important difference from Python

In Python == compares values. In Java == on objects compares memory references — whether two variables point to the exact same object in memory, not whether they contain the same text.

String a = "hello";
String b = "hello";
String c = new String("hello");    // explicitly creates a new object

System.out.println(a == b);          // → true (Java reuses literals)
System.out.println(a == c);          // → false (c is a different object)
System.out.println(a.equals(b));     // → true (same content)
System.out.println(a.equals(c));     // → true (same content)

Golden rule: always use .equals() to compare Strings — never ==.

String input = scanner.nextLine();
if (input.equals("quit")) { ... }         // correct
if (input.equalsIgnoreCase("QUIT")) { ... } // case-insensitive — very useful
if (input == "quit") { ... }              // WRONG — may give unexpected results

Why does a == b return true above? Java optimises string literals by reusing the same object for identical values — but this is an implementation detail you can’t rely on. new String("hello") bypasses this optimisation and always creates a new object, which is why a == c is false.

final — constants

final declares a variable whose value cannot change after assignment. Equivalent to Python’s convention of using ALL_CAPS names, but enforced by the compiler:

final double PI = 3.14159265358979;
final int MAX_SIZE = 100;
final String UNIVERSITY = "ULPGC";

PI = 4.0;    // compile error: cannot assign a value to final variable PI

In FP2 use final for values that should not change — mathematical constants, configuration values, maximum sizes.

Type casting

Casting converts a value from one type to another. Java has two kinds:

Widening casting (automatic) — from a smaller type to a larger one. No data is lost so Java does it automatically:

int i = 42;
long l = i;        // int → long — automatic
double d = i;      // int → double — automatic (42 becomes 42.0)
double d2 = l;     // long → double — automatic

Narrowing casting (manual) — from a larger type to a smaller one. Data may be lost so you must explicitly tell Java to do it:

double d = 9.99;
int i = (int) d;      // double → int — explicit cast: truncates to 9 (not rounded)
int i2 = (int) 3.7;   // → 3 (truncated, not rounded)

long l = 1000000000L;
int i3 = (int) l;     // may lose data if l > Integer.MAX_VALUE

The cast operator is the type in parentheses: (int), (double), (char).

Common casting patterns in FP2

// Integer division problem — same as C
int a = 7, b = 2;
double result = a / b;              // → 3.0 (integer division happens first!)
double result2 = (double) a / b;    // → 3.5 (cast before division)
double result3 = a / 2.0;           // → 3.5 (2.0 is already double)

// char to int — ASCII value
char c = 'A';
int ascii = c;         // → 65 (widening — automatic)
System.out.println(ascii);

// int to char
int code = 66;
char letter = (char) code;    // → 'B'
System.out.println(letter);

Stack vs heap — why it matters for FP2

This is the memory model that explains the == vs .equals() behaviour:

Stack — stores primitive values directly. Fast, automatically managed. When you declare int x = 5, the value 5 is stored in the stack frame.

Heap — stores objects. When you create a String, a Scanner, or any object, it lives in the heap. The variable in the stack stores a reference (memory address) pointing to the heap object.

Stack:              Heap:
┌────────────┐      ┌──────────────────────┐
│ int x = 5  │      │ String "Sergio"      │ ← name points here
│ (value: 5) │      │ String "hello"       │ ← both a and b point here
│            │      │ String "hello" (new) │ ← c points here (different object)
└────────────┘      └──────────────────────┘

a == c is false because a and c point to different locations in the heap — even though both locations contain “hello”. .equals() follows the reference and compares the actual content.

A complete program using all variable types

public class VariablesDemo {
    public static void main(String[] args) {
        // Primitive types
        int age = 20;
        double height = 1.78;
        char initial = 'S';
        boolean enrolled = true;

        // String
        String name = "Sergio Medina";
        String degree = "Data Science and Engineering";

        // Constants
        final int CURRENT_YEAR = 2025;
        final double PASS_MARK = 5.0;

        // Calculations with casting
        int birthYear = CURRENT_YEAR - age;
        double heightCm = height * 100;       // 178.0 — widening automatic
        int heightInt = (int) heightCm;        // 178 — narrowing explicit

        // String operations
        String firstName = name.split(" ")[0];  // "Sergio"
        String lastName  = name.split(" ")[1];  // "Medina"
        int nameLength   = name.length();        // 13

        // Output
        System.out.println("=== STUDENT PROFILE ===");
        System.out.printf("Name:      %s%n", name);
        System.out.printf("Initial:   %c%n", initial);
        System.out.printf("Age:       %d (born %d)%n", age, birthYear);
        System.out.printf("Height:    %.2fm (%dcm)%n", height, heightInt);
        System.out.printf("Degree:    %s%n", degree);
        System.out.printf("Enrolled:  %b%n", enrolled);
        System.out.printf("First name: %s (%d chars)%n",
                          firstName, firstName.length());

        // String comparison
        String input = "SERGIO";
        System.out.println("\nName check:");
        System.out.println("  == :          " + (name == input));
        System.out.println("  .equals():    " + (name.equals(input)));
        System.out.println("  .equalsIgnoreCase(): " +
                           (firstName.equalsIgnoreCase(input)));
    }
}

Output:

=== STUDENT PROFILE ===
Name:      Sergio Medina
Initial:   S
Age:       20 (born 2005)
Height:    1.78m (178cm)
Degree:    Data Science and Engineering
Enrolled:  true
First name: Sergio (6 chars)

Name check:
  == :          false
  .equals():    false
  .equalsIgnoreCase(): true

Visualise with Python Tutor

Select Java from the dropdown and paste in pythontutor.com:

public class TypesDemo {
    public static void main(String[] args) {
        int a = 7;
        int b = 2;

        double resultWrong = a / b;           // integer division!
        double resultRight = (double) a / b;  // cast first

        System.out.println("Wrong: " + resultWrong);
        System.out.println("Right: " + resultRight);

        String s1 = "hello";
        String s2 = new String("hello");

        System.out.println(s1 == s2);        // false
        System.out.println(s1.equals(s2));   // true
    }
}

Step through and observe two key moments. When a / b is evaluated, both a and b are int — Java performs integer division giving 3, then stores 3.0 in resultWrong. The cast (double) a converts a to 7.0 before the division, making the whole expression 7.0 / 2 = 3.5. Then when s1 == s2 is evaluated, Python Tutor shows s1 and s2 as arrows pointing to different objects in memory — even though both contain “hello”. The == compares the arrows (are they the same object?), not the content. .equals() follows the arrows, reaches both objects, compares their content and finds them equal.

Quick summary

// PRIMITIVE TYPES
int     i = 42;          // whole numbers — most common
double  d = 3.14;        // decimals — use double not float
char    c = 'A';         // single character — single quotes
boolean b = true;        // true or false — lowercase

// STRING — class, not primitive
String s = "hello";
s.length()      s.charAt(0)     s.substring(0,3)
s.toLowerCase() s.toUpperCase() s.trim()
s.contains("x") s.startsWith("h") s.replace("a","b")
s.split(",")    s.indexOf("x")  s.isEmpty()

// GOLDEN RULE FOR STRINGS
s1.equals(s2)              // compare content — always use this
s1.equalsIgnoreCase(s2)    // case-insensitive comparison
s1 == s2                   // compares references — NEVER for content

// FINAL — constants
final int MAX = 100;       // cannot be reassigned
final double PI = 3.14159;

// CASTING
// Widening (automatic): byte → short → int → long → float → double
int i = 5;
double d = i;              // automatic — no cast needed

// Narrowing (explicit): loses data — truncates, does NOT round
double d = 3.99;
int i = (int) d;           // → 3 (not 4)

// INTEGER DIVISION TRAP
int a = 7, b = 2;
double wrong = a / b;      // → 3.0 (division before widening)
double right = (double)a / b;  // → 3.5 (cast before division)

// NAMING CONVENTIONS
int studentAge = 20;       // variables: camelCase
void printInfo() {}        // methods: camelCase
class Student {}           // classes: PascalCase
final int MAX_SIZE = 100;  // constants: ALL_CAPS

// PYTHON vs JAVA
// x = 5           → int x = 5;
// x = 3.14        → double x = 3.14;
// x = True        → boolean x = true;
// x = 'A'         → char x = 'A';
// x = "hello"     → String x = "hello";
// print(x)        → System.out.println(x);

In the next article we cover operators and input in Java — Scanner, arithmetic, comparison and logical operators with direct comparisons to Python.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *