Java Core Concepts: Data Types, Operators, and Object-Oriented Programming

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 byte
  • short: 2 bytes
  • int: 4 bytes
  • long: 8 bytes
  • char: 2 bytes
  • float: 4 bytes
  • double: 8 bytes
  • boolean: 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:

  • final variable: Must be initialized once; value cannot change.
  • final method: Cannot be overridden by subclasses.
  • final class: 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:

  1. Parent class static initializers.
  2. Child class static initializers.
  3. Parent class instance initializers and field defaults.
  4. Parent class constructor.
  5. Child class instance initializers and field defaults.
  6. Child class constructor.

Thẻ: Java Data Types Operators Object Oriented Programming inheritance

Đăng vào ngày 26 tháng 9 lúc 19:12