Search Tutorials


1Z0-830 Java SE 21 - Data Types | JavaInUse

1Z0-830 Data Types - Java SE 21 Certification Prep

Primitive Data Types

Java has 8 primitive data types that represent the most basic values. Unlike objects, primitives are stored directly in memory and are not instances of classes. Understanding their characteristics, ranges, and default values is fundamental for the 1Z0-830 exam.
Primitive Types Summary:
  • byte - 8 bits, range: -128 to 127, default: 0
  • short - 16 bits, range: -32,768 to 32,767, default: 0
  • int - 32 bits, range: -2,147,483,648 to 2,147,483,647, default: 0
  • long - 64 bits, range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807, default: 0L
  • float - 32 bits, IEEE 754 floating point, default: 0.0f
  • double - 64 bits, IEEE 754 floating point, default: 0.0d
  • char - 16 bits, Unicode character, range: 0 to 65,535, default: '\u0000'
  • boolean - true or false, default: false
// Integer type declarations and ranges
byte b = 127;           // Maximum byte value
byte b2 = -128;         // Minimum byte value
// byte b3 = 128;       // Compile error - out of range

short s = 32767;        // Maximum short value
short s2 = -32768;      // Minimum short value

int i = 2147483647;     // Maximum int value (Integer.MAX_VALUE)
int i2 = -2147483648;   // Minimum int value (Integer.MIN_VALUE)

long l = 9223372036854775807L;   // Maximum long value - note L suffix
long l2 = -9223372036854775808L; // Minimum long value

// Long literals require L suffix for values outside int range
long large = 3000000000L;  // L suffix required (exceeds int range)
long small = 100;          // L suffix optional (within int range)

// Floating-point declarations
float f = 3.14f;        // f or F suffix required for float literals
float f2 = 3.14F;       // Capital F also valid
// float f3 = 3.14;     // Compile error - double literal needs cast or suffix

double d = 3.14;        // Default for decimal literals (no suffix needed)
double d2 = 3.14d;      // d or D suffix optional
double d3 = 3.14D;

// Scientific notation
double scientific = 1.5e10;   // 1.5 x 10^10 = 15000000000.0
double small = 1.5e-5;        // 1.5 x 10^-5 = 0.000015
float scientificF = 3.2e5f;   // 3.2 x 10^5 = 320000.0

// Integer literal formats
int decimal = 42;           // Decimal (base 10)
int binary = 0b101010;      // Binary (base 2) - prefix 0b or 0B
int binary2 = 0B101010;     // Capital B also valid
int octal = 052;            // Octal (base 8) - prefix 0
int hex = 0x2A;             // Hexadecimal (base 16) - prefix 0x or 0X
int hex2 = 0X2A;            // Capital X also valid
int hexLower = 0x2a;        // Hex digits can be lowercase

// All represent the value 42
System.out.println(decimal);  // 42
System.out.println(binary);   // 42
System.out.println(octal);    // 42
System.out.println(hex);      // 42

// Underscores in numeric literals (Java 7+)
// Improves readability for large numbers
long creditCard = 1234_5678_9012_3456L;
int million = 1_000_000;
int binary = 0b1010_1010_1010_1010;
int hex = 0x1234_ABCD;
double pi = 3.14_15_92;

// Underscores rules - can appear between digits only
int valid1 = 1_000;          // OK
int valid2 = 1__000;         // OK - multiple underscores
int valid3 = 0x1_2_3;        // OK
// int invalid1 = _1000;     // Error - cannot start with underscore
// int invalid2 = 1000_;     // Error - cannot end with underscore
// double invalid3 = 1._5;   // Error - adjacent to decimal point
// double invalid4 = 1_.5;   // Error - adjacent to decimal point
// long invalid5 = 1000_L;   // Error - adjacent to suffix

// Character literals
char letter = 'A';           // Single character in single quotes
char digit = '9';            // Characters include digits
char symbol = '$';

// Unicode escape sequences - \uXXXX format
char unicode = '\u0041';     // Unicode for 'A'
char euro = '\u20AC';        // Euro symbol
char smiley = '\u263A';      // Smiley face

// Escape sequences
char newline = '\n';         // Newline
char tab = '\t';             // Tab
char backslash = '\\';       // Backslash
char singleQuote = '\'';     // Single quote
char doubleQuote = '\"';     // Double quote
char carriageReturn = '\r';  // Carriage return
char backspace = '\b';       // Backspace
char formFeed = '\f';        // Form feed

// Octal escape sequences - \DDD format (not commonly used)
char octalChar = '\101';     // Octal 101 = 'A'

// char is an unsigned 16-bit integer
char minChar = '\u0000';     // Minimum: 0
char maxChar = '\uffff';     // Maximum: 65535

// Can perform arithmetic with char (promotes to int)
char c = 'A';
System.out.println(c + 1);   // 66 (int result, not char)
char next = (char)(c + 1);   // 'B' (needs cast back to char)

// Boolean type
boolean isTrue = true;
boolean isFalse = false;
// boolean wrong = 1;        // Compile error - cannot use 0/1 for boolean
// boolean wrong2 = "true";  // Compile error - must be literal true/false

// Boolean cannot be cast or converted to/from other types
// if (1) {}                 // Compile error in Java (works in C/C++)
if (isTrue) {}               // Correct

// Default values (for instance and class variables, not local variables)
// byte, short, int, long: 0
// float, double: 0.0
// char: '\u0000' (null character, displays as empty)
// boolean: false
// Object references: null

class Defaults {
    byte b;       // 0
    short s;      // 0
    int i;        // 0
    long l;       // 0L
    float f;      // 0.0f
    double d;     // 0.0d
    char c;       // '\u0000'
    boolean bool; // false
    String str;   // null
}

// Local variables have NO default value - must be initialized
void method() {
    int x;
    // System.out.println(x);  // Compile error - x not initialized
    x = 5;
    System.out.println(x);     // OK now
}

// Overflow behavior
byte max = 127;
max++;                         // Overflows to -128 (wraps around)
System.out.println(max);       // -128

int maxInt = Integer.MAX_VALUE;
maxInt++;                      // Overflows to Integer.MIN_VALUE
System.out.println(maxInt);    // -2147483648

// Floating-point special values
double positiveInfinity = 1.0 / 0.0;  // Infinity
double negativeInfinity = -1.0 / 0.0; // -Infinity
double notANumber = 0.0 / 0.0;        // NaN (Not a Number)

System.out.println(Double.isInfinite(positiveInfinity)); // true
System.out.println(Double.isNaN(notANumber));            // true

// NaN comparison behavior
System.out.println(notANumber == notANumber);  // false - NaN != NaN!
System.out.println(notANumber != notANumber);  // true
Exam Tip: Underscores in numeric literals can only appear between digits, not at the start, end, or adjacent to decimal points or type suffixes. _1000, 1000_, 1._5, and 1000_L are all invalid. Remember that char is an unsigned 16-bit integer that can hold Unicode characters from 0 to 65535. Floating-point division by zero produces Infinity (not an exception), and 0.0/0.0 produces NaN.

Wrapper Classes

Each primitive type has a corresponding wrapper class that allows primitives to be treated as objects. This is essential for using primitives with collections, generics, and other object-oriented features.
// Wrapper classes for each primitive
// byte    -> Byte
// short   -> Short
// int     -> Integer
// long    -> Long
// float   -> Float
// double  -> Double
// char    -> Character
// boolean -> Boolean

// Autoboxing - automatic primitive to wrapper conversion
Integer wrapped = 42;            // int -> Integer (autoboxing)
Double d = 3.14;                 // double -> Double
Boolean b = true;                // boolean -> Boolean
Character c = 'A';               // char -> Character

// Unboxing - automatic wrapper to primitive conversion
int primitive = wrapped;         // Integer -> int (unboxing)
double primitiveD = d;           // Double -> double

// Explicit boxing (old style, rarely needed now)
Integer explicitBox = Integer.valueOf(42);
Double explicitBoxD = Double.valueOf(3.14);

// Unboxing with null causes NullPointerException
Integer nullInt = null;
// int x = nullInt;              // NullPointerException at runtime!

// Parsing strings to primitives
int parsed = Integer.parseInt("42");
long parsedLong = Long.parseLong("1000");
double parsedDouble = Double.parseDouble("3.14");
boolean parsedBool = Boolean.parseBoolean("true");

// Parsing with radix (base)
int binary = Integer.parseInt("1010", 2);      // Binary: 10
int hex = Integer.parseInt("2A", 16);          // Hexadecimal: 42
int octal = Integer.parseInt("52", 8);         // Octal: 42

// Parsing errors throw NumberFormatException
try {
    int invalid = Integer.parseInt("abc");     // NumberFormatException
} catch (NumberFormatException e) {
    System.out.println("Invalid number format");
}

// valueOf() methods - parse and return wrapper object
Integer wrappedParsed = Integer.valueOf("42");
Double wrappedDouble = Double.valueOf("3.14");

// Converting primitives/wrappers to String
String s1 = Integer.toString(42);              // "42"
String s2 = String.valueOf(42);                // "42"
String s3 = 42 + "";                           // "42" (concatenation)
String binary = Integer.toBinaryString(42);    // "101010"
String hex = Integer.toHexString(42);          // "2a"
String octal = Integer.toOctalString(42);      // "52"

// Wrapper class constants
int maxInt = Integer.MAX_VALUE;                // 2147483647
int minInt = Integer.MIN_VALUE;                // -2147483648
int intBytes = Integer.BYTES;                  // 4 (number of bytes)
int intSize = Integer.SIZE;                    // 32 (number of bits)

long maxLong = Long.MAX_VALUE;
double maxDouble = Double.MAX_VALUE;
double minDouble = Double.MIN_VALUE;           // Smallest positive value, not most negative!
double minNormal = Double.MIN_NORMAL;          // Smallest normal positive value

// Comparing wrappers - == vs equals()
Integer a = 127;
Integer b = 127;
System.out.println(a == b);                    // true (cached!)

Integer c = 128;
Integer d = 128;
System.out.println(c == d);                    // false (not cached, different objects)
System.out.println(c.equals(d));               // true (compares values)

// Integer caching: -128 to 127 are cached
// Values in this range use the same object from cache
// Values outside create new objects
Integer cached1 = Integer.valueOf(100);        // From cache
Integer cached2 = Integer.valueOf(100);        // Same object from cache
System.out.println(cached1 == cached2);        // true

Integer notCached1 = Integer.valueOf(200);     // New object
Integer notCached2 = Integer.valueOf(200);     // New object
System.out.println(notCached1 == notCached2);  // false

// new Integer() always creates new object (deprecated in Java 9+)
Integer newObj1 = new Integer(100);            // Deprecated - don't use
Integer newObj2 = new Integer(100);
System.out.println(newObj1 == newObj2);        // false (different objects)

// Always use equals() to compare wrapper objects
Integer x = 150;
Integer y = 150;
System.out.println(x.equals(y));               // Correct way - true

// Comparison methods
Integer num1 = 42;
Integer num2 = 100;
int comparison = num1.compareTo(num2);         // Negative (42 < 100)
int comparison2 = Integer.compare(42, 100);    // Static method, same result

// Wrapper class utility methods
System.out.println(Integer.max(10, 20));       // 20
System.out.println(Integer.min(10, 20));       // 10
System.out.println(Integer.sum(10, 20));       // 30
System.out.println(Math.abs(-42));             // 42

// Character wrapper has special utility methods
System.out.println(Character.isDigit('5'));             // true
System.out.println(Character.isLetter('A'));            // true
System.out.println(Character.isLetterOrDigit('5'));     // true
System.out.println(Character.isUpperCase('A'));         // true
System.out.println(Character.isLowerCase('a'));         // true
System.out.println(Character.isWhitespace(' '));        // true
System.out.println(Character.toUpperCase('a'));         // 'A'
System.out.println(Character.toLowerCase('A'));         // 'a'

// Boolean wrapper
Boolean bool1 = Boolean.valueOf("true");       // true
Boolean bool2 = Boolean.valueOf("TRUE");       // true (case-insensitive)
Boolean bool3 = Boolean.valueOf("yes");        // false (only "true" returns true)

// Autoboxing in expressions
Integer result = 5 + 10;                       // int 15 autoboxed to Integer
Integer sum = new Integer(5) + new Integer(10); // Unbox, add, autobox

// Mixing primitives and wrappers
int primitive = 5;
Integer wrapper = 10;
int total = primitive + wrapper;               // Wrapper unboxed to int
Integer total2 = primitive + wrapper;          // Result autoboxed to Integer

// Wrapper immutability - wrappers are immutable like String
Integer num = 100;
Integer original = num;
num = num + 1;                                 // Creates new Integer object
System.out.println(original);                  // Still 100
System.out.println(num);                       // 101 (different object)
Exam Tip: Integer caching is critical for the exam - values from -128 to 127 are cached and reused. Using == on cached values returns true, but values outside this range create new objects, making == return false even with equal values. Always use equals() to compare wrapper objects. Unboxing a null wrapper causes NullPointerException. Remember that Double.MIN_VALUE is the smallest positive value, not the most negative value (unlike Integer.MIN_VALUE).

String Class

Strings are immutable sequences of characters in Java. Every modification creates a new String object. Understanding String behavior, the String pool, and common methods is essential for the exam.
// String creation methods
String s1 = "Hello";                           // String literal - stored in String pool
String s2 = new String("Hello");               // New object on heap, not in pool
String s3 = new String(new char[]{'H','i'});   // From char array

// String pool (interning)
// String literals are automatically interned in a special memory area
String a = "Hello";
String b = "Hello";
System.out.println(a == b);                    // true - same reference from pool

String c = new String("Hello");
System.out.println(a == c);                    // false - c is new heap object
System.out.println(a.equals(c));               // true - same content

// Explicit interning
String d = new String("Hello").intern();       // Returns reference from pool
System.out.println(a == d);                    // true - both reference pool object

// String concatenation creates new String objects
String original = "Hello";
String modified = original + " World";         // New String object created
System.out.println(original);                  // "Hello" - unchanged (immutable)
System.out.println(modified);                  // "Hello World"

// Concatenation at compile time vs runtime
String compile = "Hello" + " " + "World";      // Compiled to single "Hello World" literal
String runtime = "Hello";
runtime = runtime + " " + "World";             // Creates intermediate String objects

// Length and character access
String str = "Hello World";
int length = str.length();                     // 11 (number of characters)
char firstChar = str.charAt(0);                // 'H' (zero-indexed)
char lastChar = str.charAt(str.length() - 1);  // 'd'
// char invalid = str.charAt(11);              // StringIndexOutOfBoundsException

// Searching within strings
String text = "Hello World Hello";
int firstIndex = text.indexOf("o");            // 4 (first occurrence)
int lastIndex = text.lastIndexOf("o");         // 17 (last occurrence)
int notFound = text.indexOf("xyz");            // -1 (not found)

// indexOf with starting position
int secondO = text.indexOf("o", 5);            // 7 (starts search from index 5)

// indexOf with substring
int helloPos = text.indexOf("Hello");          // 0
int secondHello = text.indexOf("Hello", 1);    // 12

// Extracting substrings
String substring1 = str.substring(0, 5);       // "Hello" (from 0 inclusive to 5 exclusive)
String substring2 = str.substring(6);          // "World" (from 6 to end)
String copy = str.substring(0);                // "Hello World" (entire string)

// Empty substring at boundaries
String empty = str.substring(5, 5);            // "" (empty string, not error)

// Case conversion
String lower = str.toLowerCase();              // "hello world"
String upper = str.toUpperCase();              // "HELLO WORLD"
System.out.println(str);                       // "Hello World" - original unchanged

// Trimming whitespace
String spaces = "  Hello World  ";
String trimmed = spaces.trim();                // "Hello World" (removes leading/trailing)
System.out.println(spaces);                    // "  Hello World  " - original unchanged

// strip() - Unicode-aware whitespace removal (Java 11+)
String unicodeSpaces = "\u2000Hello\u2000";
String stripped = unicodeSpaces.strip();       // "Hello" (removes Unicode whitespace)
String stripLeading = unicodeSpaces.stripLeading();   // "Hello\u2000"
String stripTrailing = unicodeSpaces.stripTrailing(); // "\u2000Hello"

// Replacing characters and substrings
String replaced = str.replace("l", "L");       // "HeLLo WorLd" (all occurrences)
String replacedFirst = str.replaceFirst("l", "L"); // "HeLlo World" (first only)
String replacedAll = str.replaceAll("l+", "L");    // "HeLo WorLd" (regex: one or more 'l')

// Checking string properties
boolean starts = str.startsWith("Hello");      // true
boolean ends = str.endsWith("World");          // true
boolean contains = str.contains("llo");        // true
boolean empty = str.isEmpty();                 // false (checks length == 0)
boolean blank = "   ".isBlank();               // true (Java 11+ - all whitespace)

// String equality
String str1 = "hello";
String str2 = "HELLO";
System.out.println(str1.equals(str2));         // false (case-sensitive)
System.out.println(str1.equalsIgnoreCase(str2)); // true (case-insensitive)

// Comparing strings lexicographically
int cmp1 = "apple".compareTo("banana");        // Negative (apple < banana)
int cmp2 = "banana".compareTo("apple");        // Positive (banana > apple)
int cmp3 = "apple".compareTo("apple");         // 0 (equal)
int cmp4 = "Apple".compareToIgnoreCase("apple"); // 0 (case-insensitive)

// Splitting strings
String csv = "apple,banana,cherry";
String[] fruits = csv.split(",");              // ["apple", "banana", "cherry"]

String multiSpace = "a  b   c";
String[] parts = multiSpace.split(" +");       // ["a", "b", "c"] (regex: one or more spaces)

// Split with limit
String data = "a:b:c:d";
String[] limited = data.split(":", 2);         // ["a", "b:c:d"] (limit splits to 2 parts)

// Joining strings
String joined = String.join("-", "a", "b", "c");  // "a-b-c"
String[] words = {"Hello", "World"};
String sentence = String.join(" ", words);        // "Hello World"

// Repeating strings (Java 11+)
String repeated = "ab".repeat(3);              // "ababab"
String noRepeat = "hello".repeat(0);           // "" (empty string)
// String invalid = "x".repeat(-1);            // IllegalArgumentException

// Converting to/from char array
char[] chars = str.toCharArray();              // ['H','e','l','l','o',' ','W','o','r','l','d']
String fromChars = new String(chars);          // "Hello World"
String fromCharRange = new String(chars, 0, 5); // "Hello"

// Getting characters as code points (for Unicode)
int codePoint = str.codePointAt(0);            // Code point of 'H'
int count = str.codePointCount(0, str.length()); // Number of Unicode code points

// String formatting
String formatted = String.format("Name: %s, Age: %d", "Alice", 30);
// "Name: Alice, Age: 30"

// formatted() method (Java 15+)
String formatted2 = "Name: %s, Age: %d".formatted("Bob", 25);

// Checking for specific content
boolean hasNumber = "abc123".matches(".*\\d.*"); // true (contains digit)
boolean onlyLetters = "abc".matches("[a-zA-Z]+"); // true (only letters)

// String immutability implications
String s = "Hello";
s.concat(" World");                            // Creates new String but doesn't assign
System.out.println(s);                         // "Hello" - unchanged!

s = s.concat(" World");                        // Must reassign to see change
System.out.println(s);                         // "Hello World"

// Multiple modifications create multiple objects (inefficient)
String result = "";
for (int i = 0; i < 1000; i++) {
    result += i;  // Creates 1000 intermediate String objects!
}

// Performance: intern() caution
// Don't intern strings in loops - can fill up String pool memory
for (int i = 0; i < 1000000; i++) {
    String s = new String("test" + i).intern(); // BAD - fills String pool
}

// String literals in switch statements (Java 7+)
String command = "start";
switch (command) {
    case "start":
        System.out.println("Starting...");
        break;
    case "stop":
        System.out.println("Stopping...");
        break;
}
Exam Tip: Strings are immutable - all modification methods return a new String. The original is never changed. String literals are stored in the String pool and reused (using == returns true for equal literals). new String() creates a new object on the heap, not in the pool. Always use equals() for content comparison, not ==. Methods like concat(), replace(), substring() return new String objects and must be assigned to see the result. The isBlank() method (Java 11+) returns true for empty or whitespace-only strings, while isEmpty() only checks for zero length.

StringBuilder and StringBuffer

StringBuilder (and StringBuffer) are mutable sequences of characters. Use them when performing many string modifications to avoid creating multiple intermediate String objects.
// Creating StringBuilder
StringBuilder sb = new StringBuilder();                    // Empty, capacity 16
StringBuilder sb2 = new StringBuilder("Hello");            // With initial content
StringBuilder sb3 = new StringBuilder(100);                // With initial capacity

// StringBuilder is mutable - changes the same object
StringBuilder builder = new StringBuilder("Hello");
builder.append(" World");
System.out.println(builder);                               // "Hello World"
// No assignment needed - object is modified in place

// Method chaining - all modification methods return 'this'
StringBuilder result = new StringBuilder()
    .append("Hello")
    .append(" ")
    .append("World")
    .append("!");
System.out.println(result);                                // "Hello World!"

// Common StringBuilder methods

// append() - adds to end
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");                                       // "Hello World"
sb.append(123);                                            // "Hello World123"
sb.append(true);                                           // "Hello World123true"
sb.append('!');                                            // "Hello World123true!"

// insert() - inserts at specified position
sb = new StringBuilder("Hello World");
sb.insert(5, ",");                                         // "Hello, World"
sb.insert(0, ">> ");                                       // ">> Hello, World"
sb.insert(sb.length(), " <<");                             // ">> Hello, World <<"

// delete() - removes characters in range [start, end)
sb = new StringBuilder("Hello World");
sb.delete(5, 11);                                          // "Hello" (removed " World")
sb.delete(0, 2);                                           // "llo" (removed "He")

// deleteCharAt() - removes single character
sb = new StringBuilder("Hello");
sb.deleteCharAt(1);                                        // "Hllo" (removed 'e')

// replace() - replaces range with new string
sb = new StringBuilder("Hello World");
sb.replace(6, 11, "Java");                                 // "Hello Java"
sb.replace(0, 5, "Hi");                                    // "Hi Java"

// reverse() - reverses the sequence
sb = new StringBuilder("Hello");
sb.reverse();                                              // "olleH"

// setCharAt() - replaces single character
sb = new StringBuilder("Hello");
sb.setCharAt(0, 'J');                                      // "Jello"

// setLength() - changes length
sb = new StringBuilder("Hello World");
sb.setLength(5);                                           // "Hello" (truncates)
sb.setLength(10);                                          // "Hello\0\0\0\0\0" (extends with null chars)

// Capacity management
sb = new StringBuilder();                                  // Initial capacity 16
int capacity = sb.capacity();                              // 16
sb.append("This is a longer string");
int newCapacity = sb.capacity();                           // Automatically increased (34)

sb.ensureCapacity(100);                                    // Ensures minimum capacity
sb.trimToSize();                                           // Reduces capacity to match length

// Getting information
int length = sb.length();                                  // Current length
char ch = sb.charAt(0);                                    // Character at index
String sub = sb.substring(0, 5);                           // Substring (returns String, not StringBuilder)

// Converting to String
String result = sb.toString();                             // Creates new String object

// StringBuilder vs String performance
// String - creates many intermediate objects
String s = "";
for (int i = 0; i < 1000; i++) {
    s = s + i;  // Creates 1000+ String objects
}

// StringBuilder - modifies single object
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);  // Modifies same StringBuilder object
}
String result = sb.toString();  // Single String created at end

// StringBuilder vs StringBuffer
// StringBuilder: NOT thread-safe, faster (use in single-threaded code)
// StringBuffer: Thread-safe (synchronized), slower

// StringBuffer usage (same API as StringBuilder)
StringBuffer buffer = new StringBuffer();
buffer.append("Thread-safe");
buffer.append(" operations");

// In practice, almost always use StringBuilder
// Only use StringBuffer if multiple threads access same buffer

// Common mistakes

// Mistake 1: Not assigning substring result
sb = new StringBuilder("Hello World");
sb.substring(0, 5);  // Returns String "Hello" but doesn't modify sb
System.out.println(sb);  // Still "Hello World"!

// Correct:
String result = sb.substring(0, 5);  // Capture returned String
// Or to modify sb:
sb.delete(5, sb.length());  // Actually removes characters

// Mistake 2: Trying to use + with StringBuilder
StringBuilder sb1 = new StringBuilder("Hello");
StringBuilder sb2 = new StringBuilder("World");
// StringBuilder result = sb1 + sb2;  // Compile error!
// Correct:
sb1.append(sb2);  // Or: sb1.append(sb2.toString());

// Comparing StringBuilder objects
StringBuilder a = new StringBuilder("Hello");
StringBuilder b = new StringBuilder("Hello");
System.out.println(a == b);  // false (different objects)
System.out.println(a.equals(b));  // false (equals() not overridden!)
System.out.println(a.toString().equals(b.toString()));  // true (compare as Strings)

// StringBuilder doesn't override equals() or hashCode()
// Cannot use StringBuilder as HashMap key effectively

// Efficient StringBuilder usage patterns
// Estimate capacity if you know approximate final size
int estimatedSize = 1000;
StringBuilder efficient = new StringBuilder(estimatedSize);
for (int i = 0; i < 100; i++) {
    efficient.append("some text");
}

// Reusing StringBuilder
StringBuilder reusable = new StringBuilder();
for (int i = 0; i < 10; i++) {
    reusable.setLength(0);  // Clear contents
    reusable.append("Iteration ").append(i);
    System.out.println(reusable);
}

// StringBuilder in method signatures
// Return String, not StringBuilder (StringBuilder is implementation detail)
public String buildMessage(String name) {
    StringBuilder sb = new StringBuilder();
    sb.append("Hello, ").append(name).append("!");
    return sb.toString();  // Return String
}
Exam Tip: StringBuilder is mutable - methods like append(), insert(), delete() modify the same object and return 'this' for chaining. Use StringBuilder instead of String concatenation in loops for better performance. substring() returns a String (not StringBuilder) and doesn't modify the original. StringBuilder doesn't override equals() - to compare contents, convert to String first. StringBuilder is not thread-safe; StringBuffer is the synchronized (thread-safe) version. Remember that setLength() can truncate or extend the sequence.

Local Variable Type Inference (var)

The var keyword (introduced in Java 10) allows the compiler to infer the type of local variables from their initializer expressions. This reduces verbosity while maintaining type safety.
// Basic var usage - compiler infers type from right side
var message = "Hello";                           // Inferred as String
var number = 42;                                 // Inferred as int
var decimal = 3.14;                              // Inferred as double
var flag = true;                                 // Inferred as boolean

// With object creation
var list = new ArrayList();              // Inferred as ArrayList
var map = new HashMap();        // Inferred as HashMap
var file = new File("data.txt");                 // Inferred as File

// Type is determined at compile time, not runtime
var obj = "Hello";
// obj = 42;                                     // Compile error - obj is String, not Object

// var with generics
var names = new ArrayList();             // ArrayList
names.add("Alice");
// names.add(123);                               // Compile error - expects String

var numbers = List.of(1, 2, 3);                  // List (immutable)
var mutableList = new ArrayList<>(numbers);      // ArrayList

// var with diamond operator
var list2 = new ArrayList<>();                   // ArrayList - be careful!
var list3 = new ArrayList();             // ArrayList - explicit type needed

// var in for loops
var fruits = List.of("apple", "banana", "cherry");
for (var fruit : fruits) {                       // fruit is String
    System.out.println(fruit.toUpperCase());
}

// Traditional for loop with var
for (var i = 0; i < 10; i++) {                   // i is int
    System.out.println(i);
}

// Multiple variables in for loop
for (var i = 0, j = 10; i < j; i++, j--) {      // Both are int
    System.out.println(i + " " + j);
}

// var with arrays
var array = new int[]{1, 2, 3, 4, 5};           // int[]
var matrix = new int[3][3];                      // int[][]

// var with method return types
var stream = list.stream();                      // Stream
var result = stream.filter(s -> s.length() > 3)
                   .collect(Collectors.toList()); // List

// var is NOT a keyword - it's a reserved type name
// You can still use "var" as identifier in other contexts
int var = 5;                                     // Valid - var as variable name
var var = 10;                                    // Valid - var keyword with var as name!

class MyClass {
    void var() {}                                // Valid - var as method name
}

// Limitations of var

// 1. Cannot use without initializer
// var x;                                        // Compile error - no initializer
var x = 10;                                      // OK

// 2. Cannot initialize with null (without cast)
// var nullValue = null;                         // Compile error - cannot infer type
var nullValue = (String) null;                   // OK - explicit type
var nullable = Optional.empty();                 // OK - type is Optional

// 3. Cannot use for method parameters
// void method(var param) {}                     // Compile error

// 4. Cannot use for constructor parameters
// public MyClass(var param) {}                  // Compile error

// 5. Cannot use for instance or class variables
class Example {
    // var field = 10;                           // Compile error
    static final String CONSTANT = "value";      // Must use explicit type
}

// 6. Cannot use for method return types
// var getData() { return "data"; }              // Compile error

// 7. Cannot use with array initializer (without new)
// var arr = {1, 2, 3};                          // Compile error
var arr = new int[]{1, 2, 3};                    // OK

// 8. Cannot use with lambda expressions (ambiguous)
// var lambda = () -> System.out.println("Hi");  // Compile error
Runnable lambda = () -> System.out.println("Hi"); // Need explicit functional interface type

// 9. Cannot use with method references
// var printer = System.out::println;            // Compile error

// 10. Cannot reassign to different type
var str = "Hello";
// str = 42;                                     // Compile error - str is String

// var with ternary operator - inferred as common supertype
var value = flag ? "text" : "other";             // String (both branches are String)
var mixed = flag ? "text" : 42;                  // Object (common supertype of String and Integer)
// mixed.length();                               // Compile error - Object doesn't have length()

// var loses specific type information in some cases
var list4 = new ArrayList();             // Type is ArrayList
List list5 = new ArrayList<>();          // Type is List
// list4 has ArrayList methods, list5 has only List methods

// Using var with explicit types when needed
var specificList = (List) new ArrayList();  // Cast to interface type

// var with compound assignments
var count = 0;
count += 5;                                      // OK - count remains int
count = count + 5;                               // OK

// var with numeric operations - inferred type from literal
var byteVal = (byte) 10;                         // byte (needs cast)
var shortVal = (short) 100;                      // short (needs cast)
var intVal = 1000;                               // int (default for integer literals)
var longVal = 1000L;                             // long (L suffix)
var floatVal = 3.14f;                            // float (f suffix)
var doubleVal = 3.14;                            // double (default for decimal literals)

// Best practices for var

// Good uses of var - type is obvious from right side
var name = "Alice";                              // Obviously String
var count = 10;                                  // Obviously int
var user = new User("Bob", 30);                  // Obviously User
var data = fetchDataFromDatabase();              // Method name makes type clear

// Avoid when type is not obvious
var result = process();                          // What type is this? Need to check method
var value = calculate(a, b);                     // Unclear what type is returned

// Use var to reduce verbosity with long generic types
Map<String, List<Integer>> map = new HashMap<>();  // Verbose
var map2 = new HashMap<String, List<Integer>>();   // Cleaner with var

// Don't use var just to save typing if it reduces clarity
// User user = new User();                       // Clear
// var user = new User();                        // Also acceptable
// var u = new User();                           // Avoid short names with var

// var with try-with-resources
try (var reader = new BufferedReader(new FileReader("file.txt"))) {
    String line = reader.readLine();
}

// var in catch blocks
try {
    // code
} catch (var e) {                                // var inferred as Exception type
    e.printStackTrace();
}


Exam Tip: var is NOT a keyword but a reserved type name - you can use "var" as a variable name. var can only be used with local variables that have an initializer. It cannot be used for method parameters, return types, fields, or variables without initializers. var cannot be initialized with null without an explicit cast. The type is inferred at compile time and is fixed - you cannot reassign to a different type. var is not allowed with array initializers like {1,2,3} but works with new int[]{1,2,3}. When using var with diamond operator (<>), be careful as the type might be inferred as Object if not specified.

Text Blocks (Java 15+)

Text blocks provide a cleaner way to write multi-line string literals without excessive escape sequences and concatenation. They preserve formatting and improve code readability.
// Traditional multi-line string (messy with escape sequences)
String json = "{\n" +
              "    \"name\": \"Alice\",\n" +
              "    \"age\": 30\n" +
              "}";

// Text block - much cleaner
String jsonBlock = """
    {
        "name": "Alice",
        "age": 30
    }
    """;

// Text block syntax rules
// 1. Opening delimiter (""") must be followed by line terminator
// String invalid = """Hello""";                 // Compile error - no newline after """
String valid = """
    Hello
    """;

// 2. Content starts on line after opening delimiter
String text = """
    Line 1
    Line 2
    """;
// Produces: "Line 1\nLine 2\n"

// 3. Closing delimiter position determines indentation
String leftAligned = """
Line 1
Line 2
""";  // No indentation preserved

String indented = """
    Line 1
    Line 2
    """;  // 4 spaces of indentation preserved

// Closing delimiter determines baseline for incidental whitespace removal
String example1 = """
        Hello
        World
        """;  // "Hello\nWorld\n" - indentation removed

String example2 = """
        Hello
        World
    """;  // "    Hello\n    World\n" - less indentation, more preserved

// Intentional vs incidental whitespace
String poem = """
        Roses are red,
            Violets are blue,
        Sugar is sweet,
            And so are you.
        """;
// Preserves relative indentation

// Escape sequences work in text blocks
String html = """
    
        
            

Title

Line 1\nLine 2 """; // Special escape sequences for text blocks // \ at end of line - prevents line break (line continuation) String singleLine = """ Hello \ World\ """; // Produces: "Hello World" (no line breaks) String withBreak = """ Hello World """; // Produces: "Hello\nWorld\n" (with line breaks) // \s - explicit space (preserves trailing whitespace) // Normally trailing spaces on a line are removed String spaces = """ line1 line2 """; // Trailing spaces removed: "line1\nline2\n" String preservedSpaces = """ line1 \s line2 \s """; // Trailing spaces preserved: "line1 \nline2 \n" // \ - escape the newline String noNewlines = """ First\ Second\ Third """; // Produces: "FirstSecondThird\n" // Combining escapes String formatted = """ Name: %s\ Age: %d """.formatted("Alice", 30); // Produces: "Name: AliceAge: 30\n" // Mixed quotes in text blocks - no escaping needed String quotes = """ He said, "Hello!" She replied, 'Hi there!' """; // Triple quotes inside text block need escaping String tripleQuotes = """ This is a text block with \"""triple quotes\""" inside """; // Empty lines and trailing newlines String withEmptyLines = """ Line 1 Line 3 """; // Produces: "Line 1\n\nLine 3\n" // No trailing newline if closing delimiter on same line as content String noTrailing = """ Line 1 Line 2"""; // Produces: "Line 1\nLine 2" (no trailing newline) // String methods work on text blocks String block = """ Hello World """; int length = block.length(); // Includes newlines String upper = block.toUpperCase(); String[] lines = block.split("\n"); // ["Hello", "World", ""] // Formatted text blocks (Java 15+) String formatted = """ Name: %s Age: %d """.formatted("Bob", 25); // Using text blocks for SQL String sql = """ SELECT id, name, email FROM users WHERE age > 18 AND status = 'active' ORDER BY name """; // Using text blocks for JSON String jsonData = """ { "users": [ {"name": "Alice", "age": 30}, {"name": "Bob", "age": 25} ] } """; // Using text blocks for HTML String webpage = """ My Page

Welcome

"""; // Common mistakes with text blocks // Mistake 1: Expecting different indentation behavior String wrong = """ Indented Less indented """; // Closing delimiter determines baseline // Result: " Indented\nLess indented\n" // Mistake 2: Not considering closing delimiter position String unexpected = """ Line 1 Line 2 """; // Closing delimiter all the way left // Result: " Line 1\n Line 2\n" (preserves all indentation) // Mistake 3: Forgetting text blocks are still String objects String block = """ Hello """; String regular = "Hello\n"; System.out.println(block.equals(regular)); // true - same content // Text blocks and String pool String pooled1 = """ Hello """; String pooled2 = """ Hello """; System.out.println(pooled1 == pooled2); // true - interned // Concatenation with text blocks String combined = """ Part 1 """ + """ Part 2 """; // Performance - text blocks are compiled to regular strings // No runtime overhead compared to traditional strings
Exam Tip: Text blocks must have the opening triple quotes followed immediately by a line terminator - cannot start content on same line. The position of the closing delimiter determines how much indentation is removed from all lines. Use backslash (\) at end of line to prevent line break. Use \s to explicitly preserve trailing whitespace. Text blocks are still String objects and are interned in the String pool like regular string literals.

Type Casting and Conversion

Type casting converts a value from one type to another. Java supports both implicit (widening) and explicit (narrowing) conversions for primitive types and reference types.
// Primitive type casting

// Widening (implicit) conversions - smaller to larger type
// No data loss, happens automatically
// Order: byte -> short -> int -> long -> float -> double
//               char -> int

byte b = 10;
short s = b;           // byte -> short (automatic)
int i = s;             // short -> int (automatic)
long l = i;            // int -> long (automatic)
float f = l;           // long -> float (automatic, may lose precision)
double d = f;          // float -> double (automatic)

// char widens to int
char c = 'A';          // 65
int charAsInt = c;     // 65 (automatic widening)

// Narrowing (explicit) conversions - larger to smaller type
// May lose data, requires explicit cast
double d = 3.99;
int i = (int) d;       // 3 (truncates decimal, doesn't round!)
System.out.println(i); // 3, not 4

long big = 1000L;
int smaller = (int) big;        // OK if value fits
byte smallest = (byte) big;     // May overflow if value too large

// Truncation, not rounding
System.out.println((int) 3.1);  // 3
System.out.println((int) 3.5);  // 3
System.out.println((int) 3.9);  // 3
System.out.println((int) -3.9); // -3

// To round, use Math.round()
double value = 3.7;
int rounded = (int) Math.round(value);  // 4
long roundedLong = Math.round(value);   // Returns long, not int

// Overflow in narrowing conversions
int large = 130;
byte b = (byte) large;  // -126 (overflow, wraps around)

int max = Integer.MAX_VALUE;
byte b2 = (byte) max;   // -1 (overflow)

// Casting between char and int
char letter = 'A';
int code = (int) letter;      // 65 (explicit cast, but widening)
int code2 = letter;           // 65 (automatic widening)

int num = 66;
char charB = (char) num;      // 'B' (narrowing, needs explicit cast)

// boolean cannot be cast to/from any other type
boolean flag = true;
// int x = (int) flag;        // Compile error
// boolean b = (boolean) 1;   // Compile error

// Compound assignment performs implicit cast
byte b = 10;
b = b + 1;                    // Compile error - b + 1 is int
b = (byte)(b + 1);            // OK - explicit cast
b += 1;                       // OK - compound operator includes implicit cast

short s = 5;
s = s * 2;                    // Compile error - s * 2 is int
s *= 2;                       // OK - implicit cast

// Promotion in expressions
byte b1 = 10;
byte b2 = 20;
// byte sum = b1 + b2;        // Compile error - result is int
int sum = b1 + b2;            // OK
byte sum2 = (byte)(b1 + b2);  // OK with cast

// All integer types smaller than int are promoted to int in expressions
byte b = 1;
short s = 2;
char c = 'A';
int result = b + s + c;       // All promoted to int

// long if any operand is long
int i = 10;
long l = 20L;
long result = i + l;          // Result is long

// float if any operand is float (and no double)
int i = 10;
float f = 5.5f;
float result = i + f;         // Result is float

// double if any operand is double
float f = 5.5f;
double d = 3.14;
double result = f + d;        // Result is double

// Reference type casting

// Upcasting (implicit) - subclass to superclass
String str = "Hello";
Object obj = str;             // String -> Object (automatic)

// Downcasting (explicit) - superclass to subclass
Object obj = "Hello";
String str = (String) obj;    // OK - obj actually contains String

// ClassCastException at runtime if types incompatible
Object obj = Integer.valueOf(42);
// String str = (String) obj; // ClassCastException at runtime!

// Safe downcasting with instanceof
Object obj = "Hello";
if (obj instanceof String) {
    String str = (String) obj;  // Safe - checked first
    System.out.println(str.length());
}

// Pattern matching with instanceof (Java 16+)
Object obj = "Hello";
if (obj instanceof String str) {  // str is pattern variable
    System.out.println(str.length());  // Can use str directly
}

// Pattern variable scope
if (obj instanceof String str) {
    System.out.println(str);      // str in scope
}
// System.out.println(str);       // Compile error - str not in scope

// Negative instanceof
if (!(obj instanceof String str)) {
    // str NOT in scope here
} else {
    System.out.println(str);      // str in scope in else block
}

// instanceof with null
Object obj = null;
boolean result = obj instanceof String;  // false (null is not instance of anything)

// Casting with arrays
Object[] objArray = new String[5];       // OK - String[] is subtype of Object[]
// Integer[] intArray = (Integer[]) objArray;  // ClassCastException at runtime

// Primitive arrays cannot be cast to Object arrays
int[] intArray = {1, 2, 3};
// Object[] objArray = intArray;         // Compile error - incompatible types

// But can cast to Object (single object, not array of Objects)
Object obj = intArray;                   // OK - array is an object

// Casting and method overloading
class Example {
    void process(int x) {
        System.out.println("int: " + x);
    }
    
    void process(long x) {
        System.out.println("long: " + x);
    }
}

Example ex = new Example();
byte b = 5;
ex.process(b);                           // Calls process(int) - widening
ex.process((long) b);                    // Calls process(long) - explicit cast

// Loss of precision warning
long bigNum = 9876543210L;
float f = bigNum;                        // Allowed but may lose precision
System.out.println((long) f);            // May not equal original value

// Integer division before casting
double result = 5 / 2;                   // 2.0 (integer division, then widened)
double correct = 5.0 / 2;                // 2.5 (double division)
double correct2 = (double) 5 / 2;        // 2.5 (cast before division)
double wrong = (double) (5 / 2);         // 2.0 (division happens first)
Exam Tip: Casting from floating-point to integer truncates (doesn't round) - (int)3.9 equals 3. Narrowing conversions may cause overflow without warning. Compound operators (+=, *=, etc.) include an implicit cast. All integer types smaller than int are promoted to int in expressions. boolean cannot be cast to or from any other type. Always use instanceof to check before downcasting reference types. Pattern matching with instanceof (Java 16+) creates a pattern variable that's only in scope where the instanceof is true. Remember that null instanceof Type always returns false.

1Z0-830 Java SE 21 Certification - Table of Contents

Master all exam topics with comprehensive study guides and practice examples.


Popular Posts