Free for students · Ad-free · WCAG 2.1 AA Compliant · Accessibility

Scope and Access

Lesson ~10 min read 8 MCQs

In simple terms: In simple terms, scope is about where your variables can be seen and used in your code, just like some notes are for one class and others are for the whole year.

Why this matters

Imagine you're working on a group project for your history class. You have a shared Google Doc where your whole team—Carlos, Maya, and you—keeps the main research and outline. This is your project's "source of truth." But for your specific section on the Boston Tea Party, you jot down some quick notes on a sticky pad right next to your laptop. Those sticky notes are temporary and just for you, right now. You wouldn't expect Carlos, who is working on the Declaration of Independence, to see or use your sticky notes.

In programming, we have a similar concept. Some variables are like the shared Google Doc—available to the whole object. Others are like that sticky note—temporary, private, and only available in the small section of code where you create them. Understanding this difference, called scope, is key to writing clean, bug-free programs.

Comparing the scope of a shared document vs. personal notes.

Concept overview

flowchart TD
    subgraph Class Scope
        A[Instance Variable: private int score]
    end

    B(Method Call: updateScore(100)) --> C{Local Scope for updateScore}
    
    subgraph C
        D[Parameter 'score' (local) is created, value is 100]
        E{this.score = score}
        F[Local 'score' (100) is assigned to Instance 'score']
    end

    C --> G(Method Ends)
    G --> H[Parameter 'score' is destroyed]
    
    A -- persists after method call --> I[Instance Variable 'score' now holds 100]
This flowchart shows the concept of variable scope. It illustrates how an instance variable 'score' persists, while a local parameter also named 'score' is created only for the duration of a method call and is destroyed when the method ends. The 'this' keyword is shown as the bridge to update the instance variable from within the method.

Core explanation

When we write classes, we're creating blueprints for objects. These objects have attributes (data) and behaviors (methods). The data is stored in variables, but not all variables are created equal. The "where" of a variable declaration determines its scope.

Think of scope as a variable's home and the rules for visiting it.

Two Types of Homes: Instance vs. Local

Let's use an analogy. Imagine a Student object is like a student's life at school.

Visualizing `Student` object with instance and local variable analogies.
  1. 1
    Instance Variables
    These are like your school locker. Your locker belongs to you for the entire school year. You can access it between any of your classes. It holds your core stuff: textbooks, your jacket, your student ID. In code, instance variables are declared inside the class but outside of any method. They represent the core state of an object and exist as long as the object exists.
  2. 2
    Local Variables
    These are like a piece of scratch paper you use for a single math problem in algebra class. You write down some numbers, do a calculation, and when you're done with the problem, you might even toss the paper. It's temporary and only relevant to that specific task. In code, local variables are declared inside a method or constructor. They only exist and can only be used within that block of code.

Here's a Student class to illustrate:

public class Student {
    // INSTANCE VARIABLE: Like the school locker.
    // Belongs to the whole Student object.
    private String studentID;

    public Student(String id) {
        // 'id' is a PARAMETER, which is a type of local variable.
        // It only exists inside this constructor.
        studentID = id;
    }

    public void studyForTest(String subject) {
        // 'subject' is a PARAMETER (local).
        // 'hours' is a LOCAL VARIABLE.
        int hours = 2; // Only exists inside studyForTest.

        System.out.println("Studying " + subject + " for " + hours + " hours.");
        System.out.println("My student ID is: " + studentID); // I can access my instance variable here!
    }

    public void anotherMethod() {
        // System.out.println(hours); // ERROR!
        // The 'hours' variable from studyForTest doesn't exist here.



<figure class="lesson-figure"><div class="shr-widget" data-shr-widget="{&quot;code&quot;:[&quot;public class Student {&quot;,&quot;    private String studentID;&quot;,&quot;    public void studyForTest(String subject) {&quot;,&quot;        int hours = 2;&quot;,&quot;        System.out.println(\&quot;Studying \&quot; + subject + \&quot; for \&quot; + hours + \&quot; hours.\&quot;);&quot;,&quot;    }&quot;,&quot;    public void anotherMethod() {&quot;,&quot;        \/\/ ERROR: &#039;hours&#039; is out of scope here!&quot;,&quot;        System.out.println(hours);&quot;,&quot;    }&quot;,&quot;}&quot;],&quot;type&quot;:&quot;for_loop_trace&quot;,&quot;trace_steps&quot;:[{&quot;line&quot;:4,&quot;vars&quot;:{&quot;hours&quot;:2},&quot;explanation&quot;:&quot;Local variable &#039;hours&#039; created in studyForTest.&quot;},{&quot;line&quot;:5,&quot;explanation&quot;:&quot;studyForTest completes, &#039;hours&#039; is destroyed.&quot;},{&quot;line&quot;:9,&quot;error&quot;:&quot;Cannot find symbol: hours&quot;,&quot;explanation&quot;:&quot;anotherMethod tries to access &#039;hours&#039;, but it&#039;s gone (out of scope).&quot;}]}" aria-label="A for loop trace widget showing Java code. The code attempts to print &#039;hours&#039; in &#039;anotherMethod&#039; after it was declared in &#039;studyForTest&#039;. The trace shows an error when &#039;anotherMethod&#039; tries to access &#039;hours&#039;."></div><figcaption class="lesson-figure-caption">Demonstrating local variable scope: `hours` is not accessible outside `studyForTest`.</figcaption></figure>



        // It's out of scope, like trying to find your math scratch paper in history class.
    }
}

Notice a few key things:

  • studentID is an instance variable. It can be accessed in the constructor, in studyForTest, and in anotherMethod. It's available everywhere inside the class.
  • hours is a local variable inside studyForTest. Trying to access it from anotherMethod will cause a compiler error. Its scope is limited to the curly braces {} of the studyForTest method.
  • id and subject are parameters. Parameters are always local variables. Their scope is limited to the constructor or method they belong to. You also can't declare them public or private—it just doesn't make sense for a temporary variable.

The Name Collision Problem: Shadowing

Now, what happens if a local variable has the same name as an instance variable?

Let's say you have a planner in your locker (instance variable) and you also have a small to-do list in your pocket for your next class (local variable). If someone asks, "What's next on your list?" you're going to pull out the list from your pocket, not go all the way to your locker. The local list "hides" or shadows the one in the locker.

This is a very common situation in Java, especially in constructors and setter methods.

public class Player {
    private String teamName;

    public Player(String initialTeam) {
        this.teamName = initialTeam;
    }

    // A method to update the player's team
    public void setTeam(String teamName) {
        // DANGER! Which 'teamName' is which?
        teamName = teamName; // This does NOT work as intended.
    }
}

In the setTeam method, the parameter teamName (the local variable) shadows the instance variable teamName. When you write teamName = teamName;, Java thinks you mean "set the local variable equal to itself." You're not actually changing the object's instance variable at all! The player's team remains unchanged.

The Solution: this

To solve this, we need a way to explicitly say, "I don't want the local variable in my pocket; I want the instance variable from my locker!"

The keyword this is our solution. this is a reference to the current object—the "whole thing." When you use this.variableName, you are unambiguously referring to the instance variable.

Here is the corrected setTeam method:

public void setTeam(String teamName) {
    // Correct!
    // this.teamName refers to the instance variable.
    // teamName (on the right) refers to the local parameter.
    this.teamName = teamName;
}

This line now correctly reads: "Set this object's teamName to the value of the teamName parameter that was just passed into the method."

Using this is not just good practice when shadowing occurs; it makes your code clearer by showing exactly when you're interacting with the object's state versus a temporary local variable.

See it in action

Java Code

      
      
Call Stack
Stack is empty — waiting for first call.
Step 0 / 0

Worked examples

Let's walk through a couple of examples to make sure this is crystal clear.

Example 1

Updating a User's Score

Problem: We have a GameProfile class that tracks a user's score. We need a method updateScore that takes a new score and updates the user's profile. However, the parameter for the new score is also named score.

public class GameProfile {
    private String username;
    private int score;

    public GameProfile(String username, int initialScore) {
        this.username = username;
        this.score = initialScore;
    }

    // How do we correctly implement this method?
    public void updateScore(int score) {
        // ???
    }

    public int getScore() {
        return this.score;
    }
}

Solution Walkthrough:

  1. Identify the variables: Inside the updateScore method, we have two variables named score. One is the private int score instance variable that belongs to the GameProfile object. The other is the int score parameter, which is local to the updateScore method.
  2. Recognize the shadowing: The local parameter score is shadowing the instance variable score. If we just write score = score;, we'll be assigning the local variable to itself, and the object's actual score won't change.
  3. Use this to disambiguate: To specify that we want to modify the instance variable, we must use the this keyword.

Correct Implementation:

public void updateScore(int score) {
    // "Set this object's score to the value of the score parameter."
    this.score = score;
}

Why it works: this.score points directly to the instance variable, bypassing the local variable's shadow. The score on the right side of the = refers to the closest variable with that name, which is the method's parameter.

Example 2

Calculating a Temporary Value

Problem: We have a Ticket class for a concert in Boston. It has an instance variable for the price. We need a method that calculates the final cost after adding a 6.25% Massachusetts sales tax. This final cost should only be calculated and returned, not stored as part of the object's state.

Flow for calculating final ticket cost with a local tax variable.
public class Ticket {
    private String eventName;
    private double price; // e.g., 50.00

    // constructor and other methods...

    public double getFinalCost() {
        // How do we calculate and return the price with tax?
    }
}

Solution Walkthrough:

  1. 1
    Define the goal
    We need a temporary variable to hold the calculated final cost. We don't want to overwrite the base price of the ticket.
  2. 2
    Declare a local variable
    Inside the getFinalCost method is the perfect place for a local variable. Its life will be contained entirely within this one calculation. Let's call it finalCost.
  3. 3
    Perform the calculation
    We can access the instance variable price (or this.price) within the method. We'll calculate price * 1.0625 and store it in our new local variable.
  4. 4
    Return the result
    The method's job is to return this value, so we'll return the local variable.

Correct Implementation:

public double getFinalCost() {
    // 'finalCost' is a local variable. It only exists here.
    double finalCost = this.price * 1.0625;
    return finalCost;
}
Correctly updating an instance variable when a local variable has the same name.

Try it yourself

Time to put this into practice.

  1. The Book Class: Create a Book class with private instance variables for title (String) and currentPage (int).

    • Write a constructor that accepts a title and sets the currentPage to 1.
    • Write a method turnPage() that increments currentPage by 1.
    • Write a method jumpToPage(int pageNumber) that sets the currentPage to the given pageNumber. Be careful! The parameter name pageNumber might be a good candidate for shadowing if you also had an instance variable with a similar name. In this case, you don't, but think about how you would handle it if you did.
  2. The CoffeeOrder Class: Create a CoffeeOrder class with a private double price.

    • Write a method getCostWithTip(double tipRate) that takes a tip rate (e.g., 0.20 for 20%) as a parameter.
    • Inside the method, declare a local variable tipAmount and another local variable totalCost.
    • Calculate the values, and have the method return the totalCost. The original price of the order should not be changed.
Tracing `Book.jumpToPage()` and `turnPage()` to show instance variable updates.