Understanding seven non-access modifiers in Java13 min read

In the first part, we have learned about access modifiers in Java, including public, protected, default and private. Indeed, we can see that there are four access modifiers in Java. Recall access modifiers in Java specify the accessibility or scope of a field, method, constructor, or class. We can change the access level of fields, constructors, methods, and classes by applying the access modifier to it.

In this second part, the number of modifiers we will learn will be more, which is up to 7 modifiers, and we group those as non-access modifiers, which are:

  • final
  • abstract
  • static
  • native
  • transient
  • volatile
  • synchronized

What does a non-access modifier mean?

Non-access modifiers in Java are used with classes, methods, variables, constructors, etc, to provide information about their behavior to JVM. Unlike access modifiers, non-access modifiers are typically unrelated to one another, each related to a distinct programming concept; you can combine more than one non-access modifier to your class, variable, method, etc…and the order of non-access modifiers is not important.

final

First, we go with final keyword, which applies to classess, variables and methods. As its name suggests, final means something at the end and may not be changed. Because final keyword works with some different contexts, let’s go to particular ones.

final variables

When final keyword applies to a variable, the value of this variable is treated as constant and cannot be changed, so when declaring a variable with final you should initialize its value and it just can be initialized once. For example, in java.lang.Math class there are some final variables, one of them is PI with type of double. Obviously, pi is not the sort of value that should be changed during the execution of a program. People usually use capitalized letters for final variables.

public class FinalVariable {
    // final variable with type of int and initial value
    final double DISCOUNT = 0.2;
    // final variable with no initial value
    final int AGE;
    // another final variable with type of String
    final String TEXT = "This text should not be changed";
    
    
    DISCOUNT = 0.3; // value of a final variable cannot be changed, it's illegal.
    TEXT = "Text changed"; // also illegal
}

In case final variable stores a reference value which points to an object, this reference must stay the same and cannot be modified to point to another object. But, essentially, the internal states of an object which has a final variable as reference still can be changed. For example, you still can remove an element from a final array.

/** This code will not compile. */
public class FinalVariable2{
    String name;
    FinalVariable2(String n){
        name = n;
    }
}
class Main{
    public static void main(String[] args){
        final FinalVariable2 A = new FinalVariable2("Nam");
        A = new FinalVariable2("Henrick"); // illegal, try to assign final variable to a different object.
        A.name = "Goldenfield"; // changing the internal states is OK!
    }
}

final methods

A method declared with final keyword is called the final method and a final method cannot be overridden. For example, this code fragment below will not compile:

class FinalMethod {
    final void display() {
        System.out.println("Get Schwifty");
    }
}
class AnotherClass extends FinalMethod{
    void display(){ // illegal, compiling error
        System.out.println("Existance is pain!");
    }
}

Because here we have a method display() declared as final, because this method cannot be overridden, whenever we attempt to override this method in a subclass we will get an error like this: error: display() in AnotherClass cannot override display() in FinalMethod

final class

A final class is a class that cannot be subclassed or cannot be extended, which prevents inheritance. For example, the java.lang.Math is a final class in Java, if we try to extend from this class, the code will not compile:

/** This code will not compile */
class SubClass extends java.lang.Math { }

abstract

The abstract non-access modifier may be somehow similar to final modifier which makes people confused. The abstract modifier can be applied to classes and methods only. A class declared as abstract may not be instantiated which means you cannot call an abstract class’s constructor. This modifier is used to achieve abstraction, one of the core concepts of Object-oriented Programming (OOP).

abstract class

An abstract class is a class may not be fully implemented and implementations usually leave to subclass. An abstract class start with an abstract keyword:

abstract class className { 
// body
} 

Abstract classes cannot be instantiated but it still can have a constructor, inside an abstract class, we can define concrete methods (methods with body) or abstract methods (methods without body) at the same time. For example:

abstract class AbstractClassExample {
    int a = 5; // casual variable 
    abstract void draw(); // abstract method, body is empty
    void display() { // concrete method which has a body
        System.out.println("Method with body inside abstract class");
    }
}

We notice that there is an abstract method named draw() which doesn’t have a body, we will learn more about it right after this section. To sum up, there are some characteristics of an abstract method:

  • An abstract class starts with an abstract keyword.
  • An abstract class can have abstract methods, i.g methods declared without implementation and the implementation leaves for the subclass or concrete methods that have their own body.
  • An abstract method needs to be defined in an abstract class, but an abstract class can have both concrete and abstract methods.
  • You can’t create a new object from an abstract class, the details of an abstract class need to be implemented in the subclass and an object can be created in the class that extends an abstract class.
  • An abstract class can have constructors and static methods.
  • A class that extends from an abstract class has to implement all the abstract methods of the abstract class.

abstract method

Abstract methods need to appear in an abstract class and those methods don’t have a body. The implementations of those methods usually leave to subclass. If an abstract class has 5 abstract methods, then a class extends from this abstract class need to override all those methods, otherwise, the code will not compile. For example:

abstract class AbstractMethodExample {
    abstract void display(); 
    abstract int year();
    abstract double gpa();
    abstract String f();
}
class SubClass extends AbstractClassExample {
    @Override
    void display() {
        System.out.println("This method has been overriden.");
    }
    @Override
    int year() {
        return 23;
    }
    @Override
    double gpa() {
        return 1.3;
    }
    @Override
    String f() {
        return "A string";
    }
}

Here we have the abstract class AbstractClassExample with 4 methods inside it, and another subclass named SubClass inherits from this abstract class. We need to provide the implementation for all 4 methods in the subclass, if instead we just provide 3 or less this code will not compile.

static

Next, we examine another non-acess modifier, which is static keyword. Static keyword can be applied to variables, methods, blocks and even classes. You can think static code as a part of a class, rather than being associated with an individual instance of the class. When a member is declared static, it can be accessed before any objects of its class are created, and without reference to any object. 

static variable

When a variable is declared as static, a single copy of this variable will be dispersed to all objects created by this class, all instances of the class share the same static variable, for example:

public class StaticVariableExample {
    static int x = 5;
    public static void main(String[] args){
        System.out.println(StaticVariableExample.x); // access to the static variable through class name
        StaticVariableExample a = new StaticVariableExample(); // create an instance of this class
        StaticVariableExample b = new StaticVariableExample(); // another instance
        System.out.println(a.x); // 5
        System.out.println(b.x); // same as instance a, value of b.x also 5
        a.x = 200; // re-assign variable x to 200 through instance a
        System.out.println(a.x); // a.x is now 200
        System.out.println(b.x); // b.x is also 200 because they share the same static variable.
    }
}

Output:

5
5
5
200
200

In this code fragment above, we have a class with one static variable and assign to it the value of 5. Then we test the static properties by first printing out this variable before we create any instances, then creating two objects and access to the variable x, both of them give us 5. After that, we try to change the value of variable x through the instance variable a, finally we print out the x value of both a and b which gives us the same value of 200.

static method

Pretty much the same as static variable, static method can be called when no objects of this class have been created, the static method is being to a class where it defined rather than the individual objects. A prominent example is a main method. Nevertheless, the static method has some restrictions:

  • A static method may access only the static data of its class; it may not access nonstatic data.
  • A static method may call only the static methods of its class; it may not call nonstatic methods.
  • A static method has no this.
  • A static method may not be overridden to be nonstatic.
public class StaticMethodExample {
    static int x = 5;
    int y = 10;

    static void display() {
        System.out.println("Hello!");
    }

    static void m1() {
        display(); // OK
        System.out.println(y); // illegal, cannot access to non-static field
        System.out.println(this.x); // there is no this in static context, illegal
    }
}

class SubClass extends StaticMethodExample {
    void m1() {
    } // non-static cannot override static method

    static void display() { // perfectly OK 
        System.out.println("Something new!");
    }
}

static block

The static keyword is also applied for a block of code which is a pair of curly braces. The code inside the static block is executed once. At the class-load time, all static initialization is executed in order of appearance within the class definition. For example:

public class StaticBlockExample {
    static int a = 10;
    static int b;

    static { // a static block, will be executed in the class load time
        System.out.println("This string will be printed first!");
        b = a * 4;
    }
    static { // also another static block
        System.out.println("This will be printed second.");
    }

    public static void main(String[] args) {
        System.out.println("Value of a : " + a);
        System.out.println("Value of b : " + b);
    }
}

Output:

This string will be printed first!
This will be printed second.
Value of a : 10
Value of b : 40

static class

The static keyword also can be applied to a class but this class needs to be nested inside another class. There is no static class on the top level. The class contains another class inside it is called Outer class and the class inside this outer class declared with keyword static is called Nested class.

public class OuterClass{
    // body of the OuterClass
    static class StaticClass {
        public static void display() {
            System.out.println("Some text inside a nested class.");
        }
    }
    public static void main(String[] args){
        OuterClass.StaticClass.display(); // access static method through class name
        OuterClass.StaticClass a = new OuterClass.StaticClass();
        a.display(); // or create new instance to access it
    }
}

Output:

Some text inside a nested class.
Some text inside a nested class.

The StaticClass is nested inside the OuterClass, when we want to call a method inside this nested class, because this class is static hence we don’t need a reference of the OuterClass to access the static data inside it. In another case, we still can create an instance to access static fields through this instance normally.

native

The native modifier only works with methods. Like the abstract modifer, the body of native methods can be found elsewhere. The native keyword is applied to a method to indicate that the method is implemented in native code using JNI (Java Native Interface). Unlike abstract methods, the body is in a subclass, with a native method, the body of this method lies entirely outside of Java Virtual Machine, in a library. The native code is usually written in non-Java languages such as C, C++.

public class NativeMethodExample {
    native void doSomething(int a); 
    static {
        System.loadLibrary("Your library name");
    }
    public static void main(String[] args){
        NativeMethodExample a = new NativeMethodExample();
        a.doSomething(5);
    }
}

This code segment above just a sample code, it doesn’t compile until you pass the right library name into your static block. But this procedure is quite complicated, then we leave it for another article when we have a chance.

transient

The keyword transient in Java used to indicate that the variable should not be serialized. The transient modifier is a modifier that is not frequently used by programmers except for a real need for it, transient modifier may only be applied to variables. In order to understand transient, we first need to know what is Serialization and some basic concepts about I/O in Java. Simply put, serialization is a process of making the object’s state is persistent which means the state of the object is converted into a stream of bytes and stored in a file and the de-serialization process is making the object’s state back from the bytes file.

For better visualization, let’s make an example of transient keyword. First, we create a class that holds a transient variable:

import java.io.Serializable;
class Test implements Serializable {
    String name;
    transient int age;
    TransientExample(String name, int age){
        this.name = name;
        this.age = age;
    }
}

Then we create another class to serialize the object from the Test class:

import java.io.*; 
class SerializationExample {
    public static void main(String args[]) throws Exception {
        Test s1 = new Test("Nam", 20);// creating object
        // writing object into file
        FileOutputStream f = new FileOutputStream("f.txt");
        ObjectOutputStream out = new ObjectOutputStream(f);
        out.writeObject(s1); // write object to the output stream
        out.flush(); // forces any buffered output bytes to be written out

        out.close(); // close ObjectOutputStream
        f.close(); // close FileOutputStream
        System.out.println("Compiled successfully!"); // 
    }
}

Output:

Compiled successfully!

Then now we try to deserialize this code above, notice the variable we have declared as transient:

import java.io.*;
class DeserializationExample {
    public static void main(String args[]){
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("f.txt"));
        Test s = (Test) in.readObject();
        System.out.println(s.name + " " + s.age);
        in.close();
    }
}

Output:

Nam 0

volatile

The volatile modifier is rarely used but it has semantic for memory visibility. Only variables may be volatile, when declaring a variable as volatile it indicates that this variable may be modified asynchronously. Using the volatile keyword is one way of making class thread-safe besides the synchronized keyword, which means a method or an instance of this class can be used in many threads simultaneously and this process is called synchronization. The volatile variable is usually of interest in multiprocessor environments.

class Test {
    static long x = 5;
}

In this example above, presume that this class works with two different threads but those two threads run on different processors and each thread has its own copy of this variable x, if you modify the value of x in one thread, it doesn’t guarantee that this value will be updated to another thread and in the main memory. The reason is that even long is primitive data, however, since a long value contains 64 bits, for a read operation, the first 32 bits of this value might be right, it the last 32 bits don’t, hence the simple assignment in the long data type is not considered atomic. This leads to data inconsistency when the same copy of a variable in different threads has a distinct value. To resolve this, we can use the volatile keyword:

class VolatileExample {
     static volatile long x = 5;
}

If we add the volatile keyword to our variable, now this variable will be read and written from and to main memory, and remember static variables are those variables shared among all objects.

Reading and writing other primitive types (32 bits or less) are guaranteed to be atomic.

synchronized

The last keyword we mentioned about non-access modifiers is synchronized. synchronized keyword is also used for synchronization, which is all about different threads reading and writing to the same variables, objects, and resources. synchronized methods enable a simple strategy for preventing thread interference and memory consistency errors: if an object is visible to more than one thread, all reads or writes to that object’s variables are done through synchronized methods.

Because synchronization is not a trivial topic, then we will learn more about this synchronized keyword and synchronization in upcoming articles when we will take a look more closely at this keyword with examples, learn about threads and multithreading.

Previous Article
Next Article
Every support is much appreciated ❤️

Buy Me a Coffee