Java Access Specifiers & Modifiers

Access Specifiers, final, static, abstract, synchronized, native, transient & volatile

Prepared from CJ3 - Access Specifiers & Modifiers

1. Access Specifiers

An access specifier determines how accessible fields, methods and classes are to code in other classes.

Types of Access Specifiers

public

Public classes, methods and fields can be accessed from everywhere.

protected

Protected methods and fields can be accessed within the same class, within subclasses and within classes of the same package.

default

If no access level is specified, the member is accessible from inside the same package, but not from outside the package.

private

Private members are accessible within the same class only.

Default access: The default access specifier in Java is package-private (commonly called default access).

2. Access Modifiers

Access modifiers control what information or data can be accessed by other classes and provide additional definitions to classes, methods, data members or blocks.

Modifiers Covered in the Notes

final static abstract synchronized native transient volatile

Where the Modifiers Can Be Applied

ModifierBlockMember FunctionData MemberClass
final
staticExceptional
abstract
synchronized
native
transient
volatile

Overloading and Overriding

ModifierOverloadOverride
finalYesNo
staticYesNo
abstractYesYes
synchronizedYesYes
nativeYesYes

3. final

The final modifier can be applied to data members, member functions and classes.

  • A final variable cannot be changed during program execution after it has been assigned.
  • A final method cannot be overridden.
  • A final class cannot be inherited.

How do you prevent inheritance in Java?

  1. Place final on the class.
  2. Use a private constructor where appropriate.

Final Example

public final class FinalExample1 {

    final int MAX_COUNT = 10;
    int b;

    FinalExample1() {
    }

    public FinalExample1(int b) {
        this.b = b;
    }

    void display() {
        int a = this.MAX_COUNT + 10;
        System.out.println(a);
        b = b * b;
        System.out.println(b);
    }

    public void display(int c) {
        int b = this.a; // as illustrated in the source notes
        System.out.println(b);
    }

    public static void main(String[] args) {
        FinalExample1 obj = new FinalExample1(14);
        obj.b = 21;
        System.out.println(obj.b);
        obj.display();
        obj.display(5);
    }
}
Remember: final on a variable prevents reassignment, on a method prevents overriding, and on a class prevents inheritance.

4. static

static can be applied to data members, member functions and blocks.

  • Static data members are not duplicated for every object like normal instance data members.
  • A static variable is stored in a common class-level location and is shared by objects of the class.
  • Static data members are also called class variables.
  • Static methods belong to the class and can be called using the class name without creating an object.
  • A static method cannot directly use instance data members or instance methods.
  • Static methods cannot use this or super.
  • Static methods are resolved at compile time and therefore cannot be overridden; a same-signature static method in a subclass is method hiding.
  • Abstract methods cannot be static.
JVM memory note from the source: The notes associate static variables with Metaspace and mention that before Java 8 this area was called PermGen.

Access Rules

Member TypeWhat it can directly access
Instance methodsInstance methods and instance variables.
Static methodsStatic variables and other static methods.
Static methodsCannot directly access instance variables or instance methods; an object reference is required.

5. Static Initialization Blocks

A static block is a set of statements inside { } declared as static. It is executed when the class is loaded.

Static Initialization Block Example

public class Mainclass {
    static int[] values = new int[10];

    static {
        System.out.println("Running initialization block:");
        for (int i = 0; i < values.length; i++) {
            values[i] = (int) (100.0 * Math.random());
        }
    }

    void listValues() {
        for (int value : values) {
            System.out.println(value);
        }
    }

    public static void main(String[] args) {
        Mainclass example = new Mainclass();
        System.out.println("\nFirst object:");
        example.listValues();

        example = new Mainclass();
        System.out.println("\nSecond object:");
        example.listValues();
    }
}

Explicit Static Initialization

public class MyClass {
    MyClass(int marker) {
        System.out.println("Cup(" + marker + ")");
    }

    void f(int marker) {
        System.out.println("f(" + marker + ")");
    }
}

class MyStatic {
    static MyClass c1;
    static MyClass c2;

    static {
        c1 = new MyClass(1);
        c2 = new MyClass(2);
    }

    MyStatic() {
        System.out.println("Cups()");
    }
}

Static Initialization Order

The source includes an example demonstrating the pitfalls of depending on the order of static initializers. Static initialization can trigger initialization of referenced classes and their static members.

public class StaticOrderDemo {
    static {
        Class<?> cl = Values.class;
        System.out.println("Class " + cl.getName() + " Loaded");
    }

    public static final void main(final String[] args) {
    }

    public static class Ranges {
        public static final String[] RANGE_BLUE = {"Sky", "Navy"};
        public static final String[] RANGE_RED = {"Light", "Dark"};

        static {
            System.out.println("static{} method for Ranges");
            System.out.println(Arrays.asList(RANGE_BLUE));
            System.out.println(Values.VALUE_SPECIFIER);
            System.out.println(Arrays.asList(RANGE_RED));
        }
    }

    public static class Values {
        public static final String VALUE = "Blue";
        public static final String VALUE_SPECIFIER;

        static {
            System.out.println("static{} method for Values");
            System.out.println(VALUE);
            System.out.println(Ranges.RANGE_BLUE);
            VALUE_SPECIFIER = Ranges.RANGE_BLUE[1];
        }
    }
}

6. abstract

The abstract modifier can be applied to methods and classes.

  • A function that is declared without a definition is an abstract method.
  • If a class contains at least one abstract method, the class must be declared abstract.
  • An abstract class cannot be instantiated directly.
  • Abstract methods are intended to be implemented by subclasses.

Abstract Example

public abstract class AbstractDemo {

    public AbstractDemo() {
    }

    public abstract void disp();

    public abstract void disp(int a);

    public static void main(String[] args) {
        // AbstractDemo cannot be instantiated directly.
    }
}

Implementation

public class AbstractDemoImpl extends AbstractDemo {

    @Override
    public void disp() {
        System.out.println("This is definition of Abstract method disp");
    }

    @Override
    public void disp(int a) {
        int b = a;
        System.out.println("b value is " + b);
    }

    public static void main(String[] args) {
        AbstractDemoImpl obj = new AbstractDemoImpl();
        obj.disp();
        obj.disp(5);

        AbstractDemo obj1 = new AbstractDemoImpl();
    }
}

7. native

The native keyword is used with methods as a modifier when the implementation is written in another language.

  • A native method allows Java code to interact with native/platform-specific code.
  • When a method is native, the Java program is no longer completely platform-independent.
  • The source notes describe native code being converted into a DLL that can be linked dynamically at runtime.
  • The source states that if a superclass method is native, the overriding method must also be native.
public abstract class AbstractDemo {
    public AbstractDemo() {
    }

    public native void sup();
}
Important: Native methods do not contain a Java method body; the implementation is supplied outside the Java language.

8. transient

transient is a Java modifier used mainly with fields that should not participate in normal Java serialization.

The source notes discuss its use in distributed environments such as EJB, RMI and CORBA, where a variable may be excluded from transport through remote objects.

public class UMSAdobeUsagePart {
    private static final long serialVersionUID = 1L;

    private transient BusinessKey businessKey;

    private UMSAdobeUsagePartPK id;

    @Transient
    private Boolean isDefault;

    @Transient
    private String selectedPeakMonthYear;
}
Key idea: A transient field is not persisted by Java's default serialization mechanism.

9. volatile

volatile is a Java modifier used with variables that may be accessed or changed by multiple threads.

  • A volatile variable may change unexpectedly as different threads access shared state.
  • Without appropriate visibility guarantees, threads may maintain their own copies of a shared value.
  • volatile tells the JVM that reads should observe the current shared value rather than relying on a stale thread-local view.

volatile vs synchronized

  • A primitive variable may be declared volatile, whereas you cannot synchronize on a primitive.
  • Access to a volatile variable does not acquire a lock and therefore does not block in the way a synchronized block can.
  • Volatile is not sufficient for compound read-update-write operations that must be atomic.
  • A volatile object reference may be null; attempting to synchronize on a null object causes NullPointerException.

volatile Example

public class snippet1 implements Runnable {
    volatile int num = 0;

    public void run() {
        Thread t = Thread.currentThread();
        String name = t.getName();

        if (name.equals("Thread1")) {
            num = 10;
        } else {
            System.out.println("value of num is :" + num);
        }
    }

    public static void main(String args[])
            throws InterruptedException {

        Runnable r = new snippet1();

        Thread t1 = new Thread(r);
        t1.setName("Thread1");
        t1.start();

        Thread.sleep(1000);

        Thread t2 = new Thread(r);
        t2.setName("Thread2");
        t2.start();
    }
}

10. synchronized

Synchronization is the capability to control access by multiple threads to a shared resource.

The source notes summarize the goal as preventing two threads from executing the synchronized critical section concurrently.

Multiple Threads
Shared Resource
Controlled Access
Remember: synchronized is used for thread coordination and mutual exclusion, while volatile is primarily about visibility of shared variable updates.

11. Quick Revision

TopicKey Point
publicAccessible from everywhere, subject to Java's class/package rules.
protectedAccessible within the package and through subclasses.
defaultAccessible within the same package when no access modifier is specified.
privateAccessible within the declaring class only.
finalPrevents reassignment, overriding or inheritance depending on where it is applied.
staticBelongs to the class rather than an individual object.
abstractUsed for incomplete method definitions and abstract classes.
synchronizedControls concurrent access to shared resources.
nativeDeclares a method implemented in native code.
transientExcludes a field from normal Java serialization.
volatileProvides visibility semantics for shared variables across threads.