一、四大访问权限修饰符(控制可见范围)

Java 一共有 4 个访问修饰符,用来限制类的成员(变量、方法)在不同位置能否被访问,权限范围从大到小排序:

1. public(公共的)

  • 修饰范围:可以修饰类、成员变量、成员方法。
  • 访问权限:任何包、任何类中都能直接访问。
  • 特点:权限最大,无访问限制。
  • 使用场景:对外提供的接口方法、需要全局访问的成员。
// 示例:public 修饰符在不同包/子类中的访问情况
// com.example.package1
package com.example.package1;

public class PublicExample {
    public String publicField = "公共字段";
    public void publicMethod() {
        System.out.println("公共方法");
    }
}

// com.example.package2
package com.example.package2;

import com.example.package1.PublicExample;

public class TestPublic {
    public void test() {
        PublicExample example = new PublicExample();
        System.out.println(example.publicField);  // ✅ 可以访问
        example.publicMethod();                    // ✅ 可以访问
    }
}

// com.example.package2 的子类
package com.example.package2;

import com.example.package1.PublicExample;

public class SubClass extends PublicExample {
    public void test() {
        System.out.println(publicField);  // ✅ 可以访问(继承)
        publicMethod();                    // ✅ 可以访问(继承)
    }
}

2. protected(受保护的)

  • 修饰范围:只能修饰成员变量、成员方法,不能修饰普通类。
  • 访问权限
    1. 本类内部可以访问。
    2. 同一个包下的类可以访问。
    3. 不同包的子类可以继承访问。
  • 使用场景:希望子类继承、但不想对外公开的成员。
// 示例:protected 修饰符在不同包/子类中的访问情况
// com.example.package1
package com.example.package1;

public class ProtectedExample {
    protected String protectedField = "受保护字段";
    protected void protectedMethod() {
        System.out.println("受保护方法");
    }
    
    public void testInSameClass() {
        System.out.println(protectedField);  // ✅ 本类可以访问
        protectedMethod();                    // ✅ 本类可以访问
    }
}

// com.example.package1 的同包类
package com.example.package1;

public class SamePackageClass {
    public void test() {
        ProtectedExample example = new ProtectedExample();
        System.out.println(example.protectedField);  // ✅ 同包类可以访问
        example.protectedMethod();                    // ✅ 同包类可以访问
    }
}

// com.example.package2 的子类
package com.example.package2;

import com.example.package1.ProtectedExample;

public class SubClass extends ProtectedExample {
    public void test() {
        System.out.println(protectedField);  // ✅ 不同包子类可以访问(继承)
        protectedMethod();                    // ✅ 不同包子类可以访问(继承)
    }
}

// com.example.package2 的非子类(无法访问)
package com.example.package2;

import com.example.package1.ProtectedExample;

public class OtherPackageClass {
    public void test() {
        ProtectedExample example = new ProtectedExample();
        // System.out.println(example.protectedField);  // ❌ 编译错误:不同包非子类不能访问
        // example.protectedMethod();                    // ❌ 编译错误:不同包非子类不能访问
    }
}

3. 默认权限(缺省 / 包访问权限)

  • 写法:不写任何修饰符。
  • 访问权限:只有同一个包内的类可以访问,跨包无论是否是子类都无法访问。
  • 特点:介于 protected 和 private 之间,日常开发很少主动使用。
// 示例:默认权限在不同包/子类中的访问情况
// com.example.package1
package com.example.package1;

public class DefaultExample {
    String defaultField = "默认权限字段";  // 没有修饰符,即默认权限
    void defaultMethod() {
        System.out.println("默认权限方法");
    }
    
    public void testInSameClass() {
        System.out.println(defaultField);  // ✅ 本类可以访问
        defaultMethod();                    // ✅ 本类可以访问
    }
}

// com.example.package1 的同包类
package com.example.package1;

public class SamePackageClass {
    public void test() {
        DefaultExample example = new DefaultExample();
        System.out.println(example.defaultField);  // ✅ 同包类可以访问
        example.defaultMethod();                    // ✅ 同包类可以访问
    }
}

// com.example.package2 的子类(无法访问)
package com.example.package2;

import com.example.package1.DefaultExample;

public class SubClass extends DefaultExample {
    public void test() {
        // System.out.println(defaultField);  // ❌ 编译错误:不同包子类不能访问默认权限成员
        // defaultMethod();                    // ❌ 编译错误:不同包子类不能访问默认权限方法
    }
}

// com.example.package2 的非子类(无法访问)
package com.example.package2;

import com.example.package1.DefaultExample;

public class OtherPackageClass {
    public void test() {
        DefaultExample example = new DefaultExample();
        // System.out.println(example.defaultField);  // ❌ 编译错误:不同包类不能访问
        // example.defaultMethod();                    // ❌ 编译错误:不同包类不能访问
    }
}

4. private(私有的)

  • 修饰范围:只能修饰成员变量、成员方法,不能修饰类。
  • 访问权限:仅当前类内部可以访问,子类、同包类、外部类全都不能直接访问。
  • 使用场景:封装成员变量,配合 get/set 方法取值赋值,是 Java 封装思想的核心。
// 示例:private 修饰符在不同包/子类中的访问情况
// com.example.package1
package com.example.package1;

public class PrivateExample {
    private String privateField = "私有字段";
    private void privateMethod() {
        System.out.println("私有方法");
    }
    
    // 提供公共访问方法(封装)
    public String getPrivateField() {
        return privateField;
    }
    
    public void setPrivateField(String value) {
        this.privateField = value;
    }
    
    public void callPrivateMethod() {
        privateMethod();  // ✅ 本类可以访问私有方法
    }
    
    public void testInSameClass() {
        System.out.println(privateField);  // ✅ 本类可以访问
        privateMethod();                    // ✅ 本类可以访问
    }
}

// com.example.package1 的同包类(无法访问)
package com.example.package1;

public class SamePackageClass {
    public void test() {
        PrivateExample example = new PrivateExample();
        // System.out.println(example.privateField);  // ❌ 编译错误:同包类不能访问私有成员
        // example.privateMethod();                    // ❌ 编译错误:同包类不能访问私有方法
        
        // 只能通过公共方法访问
        System.out.println(example.getPrivateField());  // ✅ 通过getter访问
        example.callPrivateMethod();                     // ✅ 通过公共方法间接调用私有方法
    }
}

// com.example.package2 的子类(无法访问)
package com.example.package2;

import com.example.package1.PrivateExample;

public class SubClass extends PrivateExample {
    public void test() {
        // System.out.println(privateField);  // ❌ 编译错误:子类不能访问父类私有成员
        // privateMethod();                    // ❌ 编译错误:子类不能访问父类私有方法
        
        // 只能通过继承的公共方法访问
        System.out.println(getPrivateField());  // ✅ 通过继承的getter访问
        callPrivateMethod();                     // ✅ 通过继承的公共方法间接调用
    }
}

// com.example.package2 的非子类(无法访问)
package com.example.package2;

import com.example.package1.PrivateExample;

public class OtherPackageClass {
    public void test() {
        PrivateExample example = new PrivateExample();
        // System.out.println(example.privateField);  // ❌ 编译错误:外部类不能访问
        // example.privateMethod();                    // ❌ 编译错误:外部类不能访问
        
        // 只能通过公共方法访问
        System.out.println(example.getPrivateField());  // ✅ 通过公共getter访问
        example.callPrivateMethod();                     // ✅ 通过公共方法间接调用
    }
}

访问权限范围对照表

修饰符 本类 同包类 不同包子类 任意外部类
public
protected
默认(缺省)
private

二、非访问修饰符(不控制权限,修改特性)

1. static(静态修饰符)

  • 修饰成员变量
    • 变成静态变量(类变量),不属于对象,属于整个类。
    • 内存只加载一次,所有对象共享同一份数据。
    • 调用方式:类名.变量名(推荐)、对象名.变量名
  • 修饰成员方法
    • 变成静态方法(类方法),可以直接用类名调用。
    • 静态方法不能直接访问非静态成员变量、非静态方法,因为静态优先加载,此时对象还未创建。
    • 静态方法中不能使用 thissuper 关键字。
  • 使用场景:工具方法、全局共享常量、计数变量。

2. final(最终修饰符)

  • 修饰成员变量
    • 变量变成常量,赋值后无法修改。
    • 成员常量必须手动初始化:要么定义时直接赋值,要么在构造方法中赋值。
  • 修饰成员方法
    • 方法不能被子类重写(覆写)。
  • 修饰类
    • 类不能被继承。

3. abstract(抽象修饰符)

  • 只能修饰方法和类,不能修饰变量。
  • 修饰方法:抽象方法,只有方法声明,没有方法体,必须放在抽象类中。
  • 修饰类:抽象类,不能 new 创建对象,用来被子类继承,强制子类实现抽象方法。

⚠️ 注意abstractfinal 不能同时修饰一个方法,抽象方法需要子类重写,final 禁止重写,二者矛盾。

4. transient(瞬态修饰符)

  • 仅修饰成员变量。
  • 作用:Java 序列化时,被 transient 修饰的变量不会被保存到文件中。

5. volatile(易变修饰符)

  • 仅修饰成员变量,多线程并发编程使用。
  • 作用:保证变量在多线程下的可见性,禁止指令重排序。

6. synchronized(同步修饰符)

  • 仅修饰方法。
  • 作用:多线程环境下给方法加锁,保证同一时间只有一个线程执行该方法,解决线程安全问题。

三、修饰符组合使用规则(高频考点)

  1. privateprotectedpublic 三者不能同时出现两个。
  2. abstract 不能和 finalprivatestatic 一起修饰方法。
  3. static 方法不能使用 thissuper
  4. 普通成员方法可以访问静态成员,但静态方法不能访问普通成员。
  5. 类上面只能使用 publicabstractfinal 三个修饰符。

四、总结区分

  1. 访问修饰符public/protected/默认/private):管「能不能访问」。
  2. static:管「属于类还是对象」。
  3. final:管「能不能修改、重写、继承」。
  4. abstract:管「是否需要子类实现」。
  5. transient/volatile/synchronized:多用于 IO、多线程高级场景。

更多推荐