In the realm of Java programming, developers encounter a plethora of keywords and concepts that play a crucial role in shaping the structure and behavior of their applications. Among these, the terms 'final', 'finally', and 'finalize' often cause confusion, especially for those new to the language. While they may seem similar at first glance, each has a distinct purpose and functionality within the Java ecosystem.
This article aims to clarify the differences between these three concepts, delving into their individual uses and importance in Java programming. By the end of this article, you will have a solid understanding of 'final', 'finally', and 'finalize', enabling you to apply them effectively in your own Java projects. So, let's embark on this journey to demystify these essential Java components and enhance your programming skills.
Final Keyword in Java
The final keyword in Java is used to declare a variable, method, or class as unchangeable or constant. Once a variable or method is declared as final, its value cannot be changed. Similarly, once a class is declared as final, it cannot be inherited by any other class.
The final keyword is useful in situations where you want to prevent the value of a variable or method from being modified by other parts of the program. It also allows the compiler to optimize the code by knowing that a particular value will remain constant throughout the program's execution.
Final Keyword as a variable
In Java, the 'final' keyword is used to create variables with specific characteristics. There are two types of 'final' variables: constant values and immutable reference variables.
Constant values:
When the 'final' keyword is applied to a primitive data type variable (e.g., int, double, char), it becomes a constant value. Once initialized, the value of a constant variable cannot be changed during the execution of the program. This is particularly useful when you want to define a value that should remain constant throughout the application, such as PI or the speed of light. For example:
final double PI = 3.14159;
Immutable reference variables:
When the 'final' keyword is applied to a reference type variable (e.g., an object or an array), it creates an immutable reference. This means that the reference variable cannot be modified to point to another object or array after its initial assignment. However, it is important to note that the state of the object or array itself may still be mutable. For example:
final List<String> names = new ArrayList<>();
names.add("Alice"); // This is allowed since the object's state can be modified.
names = new LinkedList<>(); // This is not allowed since the reference cannot be changed.
Final Methods in Java
In Java, a final method is a method that cannot be overridden by any subclass. Once a method is declared as final, it cannot be changed by any subclass, making it a great tool for enforcing certain behavior in an object-oriented program.
Here's an example of a final method in Java:
public class Vehicle {
public final void start() {
System.out.println("Starting the vehicle");
}
}
public class Car extends Vehicle {
public void start() {
System.out.println("Starting the car");
}
}
In the above example, the start() method of the Vehicle class is declared as final. This means that it cannot be overridden in the Car class, and if you try to do so, it will generate a compile-time error.
Final Classes in Java
In Java, a final class is a class that cannot be subclassed or extended further. Once a class is marked as final, no other class can extend it, and its functionality cannot be overridden. This is useful when you want to create a class whose implementation should not be altered by other developers.
Here's an example of a final class in Java:
final class Circle {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public double getArea() {
return Math.PI * radius * radius;
}
}
In this example, the Circle class is marked as final. This means that no other class can extend it, and its implementation cannot be altered by other developers. The Circle class has two methods, getRadius
and getArea
, which return the radius of the circle and its area, respectively.
Here's an example of how you can use the Circle class:
public class Main {
public static void main(String[] args) {
Circle circle = new Circle(5.0);
System.out.println("Radius of circle: " + circle.getRadius());
System.out.println("Area of circle: " + circle.getArea());
}
}
Output:
Radius of circle: 5.0
Area of circle: 78.53981633974483
In this example, we create a Circle object with a radius of 5.0. We then use the getRadius
and getArea
methods to get the radius of the circle and its area, respectively. The output shows that the radius of the circle is 5.0 and its area is 78.54, which is calculated using the formula pi * radius * radius
.
Finally Block in Java
In Java, the finally block is used to define a set of statements that will be executed whether or not an exception is thrown within a try block. The finally block is typically used to release resources such as file handles, network connections, or database connections, regardless of whether an exception occurs or not.
Example-1: Using Finally block in exception handling
Here's an example code that demonstrates the use of the finally
block:
import java.io.*;
public class FinallyExample {
public static void main(String[] args) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("file.txt"));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
} finally {
try {
if (br != null) br.close();
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
}
}
}
}
Output:
IOException: file.txt (No such file or directory)
In this example, we create a BufferedReader to read lines from a file called "file.txt. The try block contains the code that reads the lines from the file and prints them to the console. If an IOException
is thrown, we catch it and print an error message. The finally block is used to close the BufferedReader
whether an exception is thrown or not.
Example-2: Using finally keyword
Here's another example that demonstrates the use of finally
block with a try-catch
block:
public class DivideByZeroException {
public static void main(String[] args) {
int numerator = 10;
int denominator = 0;
try {
int result = numerator / denominator;
System.out.println(result);
} catch (ArithmeticException e) {
System.err.println("ArithmeticException: " + e.getMessage());
} finally {
System.out.println("Finally block executed.");
}
}
}
Output:
ArithmeticException: / by zero
Finally block executed.
In this example, we are trying to divide an integer by zero, which will result in an ArithmeticException
. We catch the exception and print an error message. The finally block is used to print a message to the console to indicate that it was executed.
Finalize Method in Java
Java garbage collection overview
responsible for automatically reclaiming memory occupied by objects that are no longer in use, thus preventing memory leaks and optimizing the application's performance. The Java Virtual Machine (JVM) periodically triggers garbage collection to free up memory by identifying and removing objects that are no longer accessible.
The JVM considers an object unreachable when there are no active references to it, and its scope has ended. The garbage collector then identifies such unreachable objects and deallocates the memory occupied by them. This process helps ensure that applications have enough memory resources to function efficiently.
Customizing object cleanup with 'finalize'
The 'finalize' method is a mechanism provided by Java to customize object cleanup before the garbage collector deallocates memory. By default, the 'finalize' method is part of the Object class, and every class in Java implicitly extends the Object class. The 'finalize' method can be overridden in your class to perform additional cleanup tasks, such as closing connections or releasing resources, before an object is garbage collected.
To customize the cleanup process, you need to override the 'finalize' method in your class:
class MyClass {
// class implementation
@Override
protected void finalize() throws Throwable {
// Custom cleanup tasks
// ...
super.finalize(); // Important: Call the superclass's finalize method
}
}
It is crucial to call the superclass's 'finalize' method within your overridden method to ensure proper cleanup. However, relying on the 'finalize' method is generally discouraged due to several issues, such as unpredictable execution timing and potential performance problems. As a result, the 'finalize' method has been deprecated since Java 9, and it is recommended to use alternatives like try-with-resources statements or weak references for resource management and garbage collection.
Example of Finalized method in Java
Here is an example of how to use the finalize()
method in Java:
public class MyClass {
private int[] myArray;
public MyClass(int size) {
myArray = new int[size];
}
protected void finalize() {
// Perform some cleanup operations
myArray = null;
System.out.println("Object cleaned up.");
}
public static void main(String[] args) {
MyClass obj = new MyClass(1000000);
obj = null; // Make the object eligible for garbage collection
System.gc(); // Call the garbage collector
}
}
In the example above, the MyClass
class has a finalize()
method that sets the myArray variable to null and prints a message to the console.
In the main() method, an instance of MyClass is created with an array of size 1,000,000. The obj variable is then set to null, which makes the object eligible for garbage collection. Finally, the System.gc()
method is called to explicitly invoke the garbage collector.
When the garbage collector runs, it will call the finalize()
method of the MyClass
object before releasing it from memory. This will perform the cleanup operations defined in the finalize()
method.
Deprecated status and alternatives
The 'finalize' method has been deprecated since Java 9, primarily due to its unpredictability and the potential for causing performance issues. Instead, modern Java programming recommends alternative approaches to manage resources and garbage collection:
1. Java's try-with-resources statement
The try-with-resources statement is a more reliable and efficient way to manage resources, such as file streams or database connections, that need to be closed after use. It ensures that resources are automatically closed when the try block is exited, either due to successful completion or an exception. This approach eliminates the need for a 'finally' block to close resources and prevents resource leaks. Example:
try (FileInputStream fis = new FileInputStream("input.txt");
FileOutputStream fos = new FileOutputStream("output.txt")) {
// Perform file operations
} catch (IOException e) {
// Handle exceptions
}
2. Weak references and other garbage collection techniques:
In some cases, you may want to have more control over the garbage collection process. Java provides various reference types, such as WeakReference
, SoftReference
, and PhantomReference
, which allow you to influence the garbage collector's behavior when managing objects' lifecycles. By using these reference types, you can create objects that are eligible for garbage collection when memory is scarce or when certain conditions are met, without relying on the deprecated 'finalize' method.
Differences between Final, Finally and Finalize in Java
The following table summarizes the differences between final, finally, and finalize in Java:
Final | Finally | Finalize |
---|---|---|
Keyword used to declare variables, methods, or classes as constant | Block of code that always executes, regardless of whether an exception is thrown or not | Method called by the garbage collector before an object is destroyed |
Once declared as final, the value of a variable or method cannot be changed | Useful in situations where you want to ensure that a particular set of statements are executed, regardless of whether an exception is thrown or not | Useful in situations where you want to perform some cleanup operations before an object is destroyed |
Once declared as final, a class cannot be inherited by any other class | Can be used with try-catch blocks to catch and handle exceptions | The finalize method is called only once by the garbage collector |
Practical examples demonstrating their distinct uses:
'final':
final int MAX_ATTEMPTS = 3; // A constant value
final List<String> names = new ArrayList<>(); // An immutable reference variable
class Parent {
final void showMessage() { // A 'final' method preventing overriding
System.out.println("Hello from Parent class");
}
}
final class Child extends Parent { // A 'final' class restricting further inheritance
// Class implementation
}
'finally':
FileInputStream fis = null;
try {
fis = new FileInputStream("input.txt");
// Perform file operations
} catch (FileNotFoundException e) {
// Handle the exception
} finally {
if (fis != null) {
try {
fis.close(); // Ensuring the file stream is closed
} catch (IOException e) {
// Handle the exception
}
}
}
'finalize' (deprecated):
class MyClass {
// Class implementation
@Override
protected void finalize() throws Throwable {
// Custom cleanup tasks, e.g., closing connections or releasing resources
super.finalize(); // Calling the superclass's finalize method
}
}
Summary
This article explores the differences between 'final', 'finally', and 'finalize' in Java programming, three concepts that, despite their similar names, have distinct purposes and functionalities. The 'final' keyword is used to create constant values and immutable reference variables, ensuring that their values remain unchanged during the program's execution. It can also be applied to methods and classes to prevent method overriding and inheritance, respectively.
The 'finally' block is associated with exception handling, specifically the try-catch-finally structure. It guarantees the execution of cleanup and resource release code, regardless of whether an exception occurs or not. This helps in avoiding resource leaks and ensuring proper application behavior.
The 'finalize' method, now deprecated, was a mechanism to customize object cleanup before garbage collection. Java's garbage collection system automatically reclaims memory occupied by objects no longer in use, preventing memory leaks and optimizing application performance. However, due to its unpredictability and performance issues, it is recommended to use alternatives such as the try-with-resources statement and weak references for resource management and garbage collection.
Further Readings