Introduction to Java Programming.

Chapter 1: Introduction  

1.10 Constants in Java: final Variables

In programming, a constant is a value that, once assigned, cannot be changed during the program’s execution. Unlike variables, which are designed to hold data that can vary, constants hold data that remains fixed. In Java, the final keyword is used to declare constants.

1.10.1 What are Constants?

      Definition |: Constants are named values that are immutable, meaning their value cannot be altered once they have been initialized. They provide a way to define fixed values that will be used throughout a program.

      Why They Are Useful |:

      Readability: Using named constants (e.g., PI instead of 3.14159) makes code much easier to understand. A developer reading the code immediately knows the purpose of the value.

      Maintainability: If a constant value needs to be updated (e.g., a tax rate changes), you only need to change it in one place (where the constant is defined), and the change will automatically propagate everywhere the constant is used. Without constants, you would have to find and modify every occurrence of that value, which is error-prone.

      Preventing Errors: By making a value final, the compiler ensures that it cannot be accidentally changed later in the program, preventing potential bugs.

      Configuration Settings: They are commonly used for values that define global parameters or settings within an application, such as MAX_USERS, DEFAULT_TIMEOUT, or DATABASE_NAME.

            Example: The mathematical constant $\pi$ (pi) is a perfect candidate for a constant because its value never changes.

1.10.2 Declaring final Variables

To declare a constant in Java, you use the final keyword before the data type. By convention, constant names are written in uppercase letters, with words separated by underscores (_). This makes them easily distinguishable from regular variables.

      Syntax:
final dataType CONSTANT_NAME = value;

      Examples:

 // Declaring constants using ‘final’

final int MAX_ATTEMPTS = 3;

final double PI = 3.14159;

final String DATABASE_URL = "jdbc:mysql://localhost:3306/mydb";

final boolean DEBUG_MODE = true;

// Example using constants in a class

public class CircleCalculator {

    // Instance constant

    final double PI_VALUE = 3.14159;

    // Static constant (shared by all objects)

    public static final String APP_NAME = "My Awesome App";

    // Method using constants

    public void calculateArea(double radius) {

        // Using the instance constant

        double area = PI_VALUE * radius * radius;

        System.out.println("Area: " + area);

        // Using the static constant

        System.out.println("Application: " + APP_NAME);

    }

    public static void main(String[] args) {

        CircleCalculator calculator = new CircleCalculator();

        calculator.calculateArea(5.0);

        // Static constant can also be accessed using the class name

        System.out.println("Running " + CircleCalculator.APP_NAME);

    }

}

1.10.3 Rules for final Variables

The final keyword imposes strict rules on how a variable can be used:

      Must be Initialized Once |:
A final variable must be initialized exactly once. This can be done either:

    At the time of declaration.

    In a constructor (for instance final variables).

    In a static initializer block (for static final variables).
If a final variable is not initialized, the compiler will generate an error.

 // Example of final variables in Java

 

// 1. Final variable initialized at declaration

final int a = 10; // Correct: initialized at declaration

 

// 2. Final variable without initialization

// final int b;

// COMPILE-TIME ERROR: blank final field ‘b’ may not have been initialized

 

// 3. Final instance variable initialized in the constructor

class MyClass {

    final int value; // Declared, not initialized here

 

    // Constructor initializes the final variable

    public MyClass(int val) {

        this.value = val; // Correct: initialized in constructor

        // this.value = 20; // COMPILE-TIME ERROR: cannot assign a value to final variable ‘value’

    }

}

      Cannot be Reassigned |:
Once a final variable has been initialized, its value cannot be changed or reassigned. Any attempt to modify its value after initialization will result in a compile-time error.

 public class FinalExample {

    public static void main(String[] args) {

        // Final variables cannot be reassigned once initialized

 

        final int count = 5; 

        // count = 10; // COMPILE-TIME ERROR: cannot assign a value to final variable ‘count’

 

        final String GREETING = "Hello"; 

        // GREETING = "Hi"; // COMPILE-TIME ERROR: cannot assign a value to final variable ‘GREETING’

 

        // Printing the values

        System.out.println("Count: " + count);

        System.out.println("Greeting: " + GREETING);

    }

}

      Impact on Objects (Reference is final, Not the Object’s Content) |:
This is a crucial point to understand for final variables that are objects (non-primitive types). When a reference variable is declared final, it means that the variable can only point to one object. The reference itself cannot be changed to point to a different object. However, the state (the data within) of the object that the final reference points to can still be changed, provided the object itself is mutable.

 public class FinalObjectExample {

    public static void main(String[] args) {

        // Final reference to a StringBuilder object

        final StringBuilder sb = new StringBuilder("Java");

 

        System.out.println(sb);  // Output: Java

 

        // Modifying the contents of the object is allowed

        sb.append(" Programming"); 

        System.out.println(sb);  // Output: Java Programming

 

        // Attempting to reassign the final reference to a new object is NOT allowed

        // sb = new StringBuilder("New String");

        // COMPILE-TIME ERROR: cannot assign a value to final variable ‘sb

    }

}

            In this example, sb is a final reference, so it will always point to the same StringBuilder object that was created initially. You cannot reassign sb to a different StringBuilder object. However, because StringBuilder objects are mutable, you can still call methods like append() on that same object to modify its internal content. If you want an object whose content also cannot change, the class of that object must itself be designed to be immutable (like String in Java).

1.10.4 When to Use final

Using final effectively contributes to better code design and makes your programs more robust and easier to understand.

      For True Constants: Use final for values that are genuinely constant throughout your application (e.g., mathematical constants, maximum limits, fixed error codes). These are often declared as public static final to make them globally accessible and to emphasize that they belong to the class rather than an object.

      Achieving Immutability (of References): When you want to ensure that a reference variable (especially an object reference) will always point to the same object after initialization. This helps in preventing unexpected reassignments.

      Method Parameters: Declaring method parameters as final indicates that the method will not modify the value of the parameter. This can make code clearer, especially for primitive types.

      Local Variables: Using final for local variables can sometimes improve readability by indicating that the variable’s value won’t change after its initial assignment, making it easier to reason about the code. It’s often used with anonymous inner classes or Lambda expressions that access local variables.

      Performance: While not its primary purpose, final variables can sometimes allow the Java compiler and JVM to perform minor optimizations, as they know the value won’t change. However, this performance benefit is usually negligible and should not be the main reason for using final. The main benefits are maintainability and readability.

By incorporating final variables into your programming practice, you enforce data integrity, make your code more predictable, and clearly communicate the intent of fixed values to other developers (and your future self).