Skip to main content

SF-0304 · Concept · Easy

What are access modifiers we use for apex classes?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

Apex has four access modifiersprivate, protected, public, and global — and they work like Java’s, with one Salesforce-specific addition (global) for managed packages. Pick the most restrictive modifier that still lets the caller see what it needs. Wide-open access is the most common cause of failed AppExchange security reviews.

The four levels

ModifierVisibilityUse it for
privateOnly inside the same classInternal helpers, fields you don’t want callers to touch
protectedSame class + subclassesMembers exposed to your inheritance hierarchy but not the world
publicAnywhere in the same namespace (org)Default for application code inside one org
globalAll namespaces, including subscriber orgsManaged package APIs, @RestResource, @InvocableMethod

private — the default for fields

Use private on every field unless callers need to read or write it. Pair it with public getters/setters when controlled exposure is required.

public class OrderService {
    private static final Decimal MAX_DISCOUNT = 0.30;
    private List<OrderItem__c> items;

    public OrderService(List<OrderItem__c> items) {
        this.items = items;
    }
}

Inside the class, private members are accessible. From any caller outside the class, they’re invisible — the compiler rejects the reference.

private is also the access for inner classes that should be hidden inside the outer class:

public class OuterService {
    private class InternalState {
        Integer counter;
        DateTime lastRun;
    }
}

protected — for inheritance chains

protected is the rarely-used middle level. The member is visible to the declaring class and any class that extends it.

public virtual class BaseHandler {
    protected void log(String msg) {
        System.debug(msg);
    }
}

public class AccountHandler extends BaseHandler {
    public void run() {
        log('Running');   // OK — inherited access
    }
}

Outside the inheritance tree, protected behaves like private. Use it only when you’re deliberately designing a virtual/abstract base class with hooks for subclasses.

public — the application default

public is the right answer for most application classes and methods used across an org. The member is visible everywhere in the same namespace.

public with sharing class AccountService {
    public static List<Account> getActiveAccounts() {
        return [SELECT Id, Name FROM Account WHERE Active__c = true];
    }
}

Important subtlety: if your code is in an unmanaged package (or no package at all), the “namespace” is just the org. If it’s in a managed package, public is invisible to subscribers — they cannot call it from their own Apex. For cross-namespace calls, you need global.

global — the managed-package and integration entry point

global exposes the member across namespaces. You need it for:

  • Managed package APIs — methods your AppExchange subscribers call from their own Apex.
  • @RestResource classes — must be global, plus the methods annotated with @HttpGet/@HttpPost/etc.
  • @WebService SOAP endpoints — must be global.
  • @InvocableMethod actions in managed packages so subscriber Flows can call them.
  • Schedulable, Queueable, Batchable classes in managed packages so subscribers can schedule or enqueue them.
@RestResource(urlMapping='/orders/*')
global with sharing class OrderResource {
    @HttpGet
    global static Order__c getOrder() {
        String orderId = RestContext.request.requestURI.substringAfterLast('/');
        return [SELECT Id, Name, Status__c FROM Order__c WHERE Id = :orderId LIMIT 1];
    }
}

Warning: Once you ship global in a managed package, you cannot remove or change its signature. The platform locks it as an API contract to protect subscriber orgs. Use global only when you genuinely need the cross-namespace exposure.

Common pitfalls

  • Defaulting to public on fields. Use private and expose what callers need.
  • Using global on application classes. If the class only needs to be called within your org, public is enough.
  • Forgetting global on REST methods. @RestResource won’t expose public methods.
  • Confusing class access with method access. A public class can have private methods — the class controls whether you can see the class at all; method modifiers control which members.

What interviewers are really looking for

The naming check (private/protected/public/global) is trivial. The signal interviewers want is whether you understand global as the cross-namespace boundary, not just “the widest one.” If you can explain that REST and SOAP endpoints require global, that managed packages need global for any callable surface, and that you should default to private for fields — you’ve shown the encapsulation instinct senior teams hire for.

Verified against: Apex Developer Guide — Access Modifiers. Last reviewed 2026-05-17 for Spring ‘26 release.