Static - 2020
The keyword static lets a method run without any instance of the class. In other words, a static method means "the behavior not dependent on an instance variable, so no /instance/object is required. Just the class."
The static modifier has such a profound impact on the behavior of a method or variable that we should fully understand of the concept and usage of the keyword.
Let's thing about the reason for the need of static.
- Suppose if we have a utility class with a method that always runs the same way such as Math.round() or Math.abs(). It wouldn't matter which instance of the class performed the method--it would always behave exactly the same way. The method's behavior does not dependent on the state (values of instance variable) of an object.
So, why do we need an object when the method will never be instance-specific? Why not just ask the class itself to run the method? - Assume we want to keep a running count of all instances instantiated from a class. Where do we keep that variable? It won't work to keep it as an instance variable within the class whose instances we're tracking since the count will just be initialized back to a default value with each new instance.
The answer is the static. Variables and methods marked as static belong to the class rather than to any particular instance. Actually, we can use a static method or variable without having any instances of that class at all. We need only have the class available to be able to invoke a static method or access a static variable. However, if there are instances, a static variable of a class will be shared by all instances of that class. We have only one copy.
public class Egg { static int counter = 0; public Egg() { counter++; } public static void main(String[] args) { new Egg(); System.out.println("Instance counter = " + counter); new Egg(); System.out.println("Instance counter = " + counter); new Egg(); System.out.println("Instance counter = " + counter); } }
Output is:
Instance counter = 1 Instance counter = 2 Instance counter = 3
In the code, the static counter is set to zero when the Egg class is first loaded by the JVM, before any Egg instances are created. Note that static variables get the default value if we don't initialize it. Every time an Egg instance is created, the Egg constructor runs and increments the static
The code in the next section shows what would happen if the count were an instance variable rather than a static variable
public class Egg { int counter = 0; public Egg() { counter++; } public static void main(String[] args) { new Egg(); System.out.println("Instance counter = " + counter); new Egg(); System.out.println("Instance counter = " + counter); new Egg(); System.out.println("Instance counter = " + counter); } }
We get the following compile error:
Exception in thread "main" java.lang.Error: Unresolved compilation problems: Cannot make a static reference to the non-static field counter Cannot make a static reference to the non-static field counter Cannot make a static reference to the non-static field counter
The JVM doesn't know which Egg object's count we're trying to access. The problem is that the main() is itself a static method, and thus isn't running against any particular instance of the class, rather just on the class itself.
A static method can't access instance (non-static) variable, because there is no instance. Even though there are instances of the class on the heap, the static method doesn't know anything about them.
Here is the simplest example of mistake:
public class CommonMistakes { int x = 10; public static void main(String[] args) { System.out.println("x = " + x); } }
The code will never compile, because we can't access a nonstatic (instance) variable from a static method. The compiler says "I have no idea which CommonMistakes' object's x variable you're trying to print! What's running the main() method? It's the class, not an instance of the class.
A static method can't directly invoke a nonstatic method. So, the following code won't compile:
public class Egg { int size = 10; public int getSize() { return size; } public static void main(String[] args) { System.out.println("size = " + getSize()); } }
What do nonstatic methods do?
They usually use instance variable state to affect the behavior of the method. The getSize() method returns the instance variable, size. Whose size?
The object should have been used to invoke the getSize() method.
Static final variables are constants. A variable marked final is saying that it can never change once initialized:
public static final double PI = 3.141592653589793;
- public
Any code can access it. - static
We don't need an instance of the class. - public
PI won't change.
There is no other way to declare a variable as a constant.
How do we initialize the final static variable?
- At the time we are declaring it:
public class Egg { public static final int NO_CHANGE = 100; }
- In a static initializer:
public class Egg { public static final int NO_CHANGE; static { NO_CHANGE = 100; } }
The code runs as soon as the class is loaded, before any static method is called and even before any static variable can be used.
- final primitive variable
We can't change its value. - final reference variable
The reference variable cannot point to any other object on the heap. - final method
We can't override the method. - final class
We can't extend the class to make a new subclass.
Although try and catch provide a mechanism for trapping and handling exceptions, we are left with the problem of how to clean up if an exception occurs.
Because execution transfers out of the try block as soon as an exception is thrown, we can't put our cleanup code at the bottom of the try block and expect it to be executed if an exception occurs.
A finally block encloses code that is always executed at some point after the try block, whether an exception was thrown or not. Even if there is a return statement in the try block, the finally block executes right after the return statement is encountered, and before the return executes!
This is the place to close our files, release our network sockets, and perform any other cleanup.
If the try block executes with no exceptions, the finally block is executed immediately after the try block completes. If there was an exception thrown, the finally block executes immediately after the proper catch block completes.
try { } catch (MyException e) { e.printStackTrace(); } finally { // cleanup }
- There is an optional finally block after the try block or after the catch block.
- The statements inside the finally block will always be executed except if JVM exits from the try block.
- The finally block is used to write the clean up code.
Yes, it will get executed.
The finally block gets executed when the try block exits. However, even when we attempt to exit within the try block (normal exit, return, continue, break or any exception), the finally block will still be executed.
package com.bogotobogo; public class finn { public static void main(String [] args) { System.out.println("start of main()"); System.out.println(WhatsUp()); System.out.println("end of main()"); } public static String foo() { return "foo()"; } public static String WhatsUp() { int denominator = 0; int numerator = 1; try { System.out.println("start of try block"); double result = numerator/denominator; return "return: end of try block"; } catch (Exception e){ System.out.println("start of catch block"); System.out.println("Just got back from " + foo()); return "return: end of catch block"; } finally { System.out.println("finally block"); } } }
Output:
start of main() start of try block start of catch block Just got back from foo() finally block end of catch block end of main()
There are some cases in which the finally block will NOT get executed.
- If the JVM exits in between try/catch block execution.
- If the thread which is executing try/catch block gets killed.
Java provides us a mechanism to run some code just before our object is deleted by the garbage collector. This code is located in a method named finalize() that all classes inherit from class Object. However, any code that we put into our class's overridden finalize() method is not guaranteed to run. The finalize() method for any given object might run, but we can't count on it, so we should not put any critical code into our finalize() method. Actually, overriding the finalize() is discouraged.
And there are following things to remember:
- For any given object, finalize() will be called only once (at most) by the garbage collector.
- Calling finalize() can actually result in saving an object from deletion.
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization