Data Types and Operators in Java
Java is a strongly typed language, meaning every variable and expression has a type determined at compile time. All variables must be explicitly declared with their types.
Identifiers and Basic Data Types
Identifiers are names used for classes, variables, and methods. They cannot contain spaces and may only include letters, digits, underscores (_), and dollar signs ($). Java keywords are always lowercase.
The eight primitive data types and their memory sizes are:
byte: 1 byteshort: 2 bytesint: 4 byteslong: 8 byteschar: 2 bytesfloat: 4 bytesdouble: 8 bytesboolean: 1 bit (implementation dependent)
Note that large integer literals exceeding the range of int are not automatically treated as long; an 'L' suffix is required. Similarly, floating-point literals default to double, requiring an 'F' suffix for float.
Character Encoding and Literals
Computers store characters using binary codes defined by character sets. ASCII was the early standard, but Unicode now supports global languages using 16-bit codes (up to 65,536 characters). In Java, char values can be represented as single characters, escape sequences (e.g., \n), or Unicode escapes (e.g., \u0041).
// Direct character assignment
char c = 'A';
// Escape sequence for newline
char newline = '\n';
// Using Unicode value
char unicodeChar = '\u0061'; // Represents 'a'
public class CharDemo {
public static void main(String[] args) {
int asciiValue = 97;
char converted = (char) asciiValue;
System.out.println(converted); // Output: a
}
}
Common escape sequences include \b (backspace), \t (tab), \n (newline), and \r (carriage return). The tab character aligns text to multiples of 8 spaces.
Variable Declaration and Type Inference
Since Java 10, the var keyword allows local variable type inference. However, initialization is mandatory at declaration so the compiler can deduce the type.
var number = 100; // Inferred as int
var decimal = 3.14; // Inferred as double
var flag = true; // Inferred as boolean
// var error; // Compilation error: cannot infer type without initialization
Type Conversion and Expressions
Strings cannot be directly cast to primitives. Use wrapper classes like Integer.parseInt() for conversion. When mixing types in arithmetic expressions, automatic promotion occurs: byte, short, and char promote to int. If a string is involved in a + operation, it acts as concatenation rather than addition.
String str = "Result: " + (10 + 5); // Concatenates "Result: 15"
System.out.println(str);
// Implicit conversion from int to char if within range
char ch = 65;
System.out.println(ch); // Output: A
Literals and Constant Pool
Literals are constant values written directly in code. String literals are cached in the JVM's string constant pool. Reusing the same literal references the same object in memory, whereas new String("literal") creates a new object on the heap.
Operators
Arithmetic Operators
Division (/) with floating-point numbers handles division by zero gracefully, returning Infinity or -Infinity. However, integer division by zero throws an ArithmeticException.
public class DivisionTest {
public static void main(String[] args) {
double d1 = 10.0 / 0.0; // Infinity
double d2 = -10.0 / 0.0; // -Infinity
// Modulo with floats
double mod = 5.2 % 3.1; // ~2.1
// Integer division by zero causes runtime exception
try {
int i = 5 / 0;
} catch (ArithmeticException e) {
System.out.println("Cannot divide integer by zero");
}
}
}
Increment/Decrement
Post-increment (a++) uses the current value in the expression before incrementing, while pre-increment (++a) increments before use.
Bitwise and Logical Operators
Bitwise operators (&, |, ^, ~) operate on individual bits. The unsigned right shift (>>>) fills left bits with zeros. Logical operators (&&, ||) support short-circuit evaluation.
Comparison
The == operator compares values for primitives and references for objects. For object equality based on content, override the equals() method.
Arrays
Declaration and Initialization
An array is an object storing multiple values of the same type. The reference variable is stored in stack memory, while the actual array object resides in heap memory.
- Static Initialization: Values are provided explicitly. Length is inferred.
- Dynamic Initialization: Only length is specified. Elements receive default values (0 for numeric, false for boolean, null for objects).
// Static
int[] scores = {90, 85, 78};
// Dynamic
String[] names = new String[3];
// names[0], names[1], names[2] are initially null
Accessing Arrays
Array indices start at 0. Accessing an index outside the bounds throws an ArrayIndexOutOfBoundsException. Multi-dimensional arrays in Java are actually arrays of arrays, allowing for jagged structures where sub-arrays can have different lengths.
The Arrays Utility Class
The java.util.Arrays class provides static methods for sorting, searching (binary search requires sorted input), and comparing arrays. Note that equals() checks element-wise equality, not reference identity.
Object-Oriented Programming Fundamentals
Classes and Objects
A class defines the blueprint for objects, containing fields (variables), methods, constructors, and initialization blocks. Objects are instances created via constructors using the new keyword.
Constructors
Constructors initialize objects. They share the class name and have no return type. If no constructor is defined, Java provides a default no-arg constructor. Constructors can overload each other and call one another using this().
This and Super Keywords
this: Refers to the current instance. Used to distinguish between instance variables and parameters with the same name.super: Refers to the parent class instance. Used to call parent constructors (super()) or access overridden methods/fields.
Encapsulation and Access Modifiers
Encapsulation hides internal state and exposes behavior through public methods. Java provides four access levels:
private: Accessible only within the class.default(package-private): Accessible within the same package.protected: Accessible within the package and by subclasses.public: Accessible everywhere.
Inheritance
Java supports single inheritance using the extends keyword. Subclasses inherit non-private members from superclasses. Method overriding requires matching signatures and compatible return types (covariant returns allowed since JDK 5). The @Override annotation helps verify correct overriding.
Polymorphism
Polymorphism allows a subclass object to be referenced by its superclass type. At runtime, the JVM invokes the method implementation of the actual object type, not the reference type. This enables dynamic method dispatch.
class Animal {
public void speak() {
System.out.println("Generic animal sound");
}
}
class Dog extends Animal {
@Override
public void speak() {
System.out.println("Bark");
}
public void fetch() {
System.out.println("Fetching ball");
}
}
public class PolymorphismDemo {
public static void main(String[] args) {
Animal myPet = new Dog(); // Upcasting
myPet.speak(); // Output: Bark (Dynamic binding)
// myPet.fetch(); // Compile error: Animal does not have fetch()
// Downcasting to access specific methods
if (myPet instanceof Dog) {
Dog dog = (Dog) myPet;
dog.fetch();
}
}
}
Abstract Classes and Interfaces
Abstract classes can contain both abstract methods (without body) and concrete methods. They cannot be instantiated. Interfaces define contracts for classes to implement, supporting multiple inheritance of type. Since Java 8, interfaces can have default and static methods.
Final Modifier
The final keyword prevents modification:
finalvariable: Must be initialized once; value cannot change.finalmethod: Cannot be overridden by subclasses.finalclass: Cannot be inherited.
Note that final applied to a reference variable ensures the reference points to the same object, but the object's internal state may still be mutable unless the object itself is immutable.
Static Members
Members marked with static belong to the class rather than any instance. They are shared across all objects and can be accessed without creating an instance. Static context cannot directly access non-static members.
Initialization Blocks
Java executes initialization in this order:
- Parent class static initializers.
- Child class static initializers.
- Parent class instance initializers and field defaults.
- Parent class constructor.
- Child class instance initializers and field defaults.
- Child class constructor.