Java 修饰符

Java 语言提供了很多修饰符,其主要的作用就是为所修饰的代码提供额外的特性, 主要分为以下两类:

  • 访问修饰符
  • 非访问修饰符

访问修饰符

访问修饰符决定了其他类是否可以使用特定字段或调用特定方法. 它有两个级别的访问控制:

  1. 在顶层 - 分为 public(公开) 或 package-private(包私有, 无显式修饰符的情况)
  2. 在成员层 - 分为 public (公开), protected(受保护), private(私有) 或 package-private (包私有, 无显式修饰符的情况)

在顶层(类级别), 如果使用了 public 修饰了一个类, 那么这个类对所有类都是可见的, 如果使用了默认情况(即无修饰符的情况), 那么该类仅能被同一个包下的其他类所访问到.

在成员层(属性, 方法级别), public 无修饰符的可见性与顶层意义相同. 但是额外增加 protected private 两种访问修饰符. 其中,  protected 控制了成员可以被同一个包下的其他类访问(这点与无修饰符类似) 或者被不同包下的子类(subclass)所访问. 而 private 控制了成员只能被类自身所访问.

由此可见访问修饰符可以有四种情况:

  1. 无修饰符的 (默认的, 包私有的)
  2. public (公开的)
  3. protected (受保护的)
  4. private (私有的)

其中 protected private 仅用于类成员的访问控制.

下面一表, 用于快速记忆各个修饰符对于成员访问的限制:

访问级别
修饰符 类自身 同名包 子类 全局
public
protected
无修饰符
private

示例代码:

我们分别创建以下几个类:

//包 modifier
//文件: com/yi21/modifier/A.java
package com.yi21.modifier;

public class A {
    protected static String name = "Class A";
    protected static void hello() {
        System.out.println("Hello, I'm A.");
    }
}

//文件: com/yi21/modifier/B.java
package com.yi21.modifier;

public class B {
    private static String name = "Class B";
    public static void helloA() {
        A.hello();
        System.out.println(A.name);
        System.out.println("Hello, I'm " + name);
    }
}

//新的包 modifier2
//文件: com/yi21/modifier2/C.java
package com.yi21.modifier2;

import com.yi21.modifier.*;

public class C {
    public static void helloB() {
        B.helloA();
        System.out.println(A.class);
        // System.out.println(A.name);
    }
}

//文件: com/yi21/modifier2/A2.java
package com.yi21.modifier2;

import com.yi21.modifier.A;

public class A2 extends A {
    public static void helloPapa() {
        System.out.println("My Father is " + A.name);
    }
}

//入口文件 Yi21Runner.java
import com.yi21.modifier2.*;

public class Yi21Runner {
    public static void main(String[] args) {
        C.helloB();
        System.out.println("------- 21yi.com -------");
        A2.helloPapa();
    }
}

最终我们建立了 5 个文件来展示访问修饰符对成员的访问控制.

讲解如下:

  1. 在包 modifier 下定义了 A 类, A 类为公开否则包 modifier2 下的 A2 无法访问到 A, 也就无法继承 A, A 只提供了两个 proteced 的成员 类变量 name 和静态方法 hello
  2. 在包 modifier 下定义了 B 类, B 类同样为公开, B 定义了 private 成员 类变量 name, 只能在 B 内部访问, 以及 proteced 的成员 静态方法 helloA, 在 helloA 中调用了同包下的 Aprotected 的两个成员: 方法 hello 和类变量 name, 并输出了自己的 private 成员, 类变量 name
  3. 在包 modifier2 下定义了 C 类, C 类调用了 B 类的 helloA 方法, 同时因为 A 类的可访问性, 可以输出 A.class.
  4. 在包 modifier2 下定义了 A2 类, A2 类继承了 A 类, 因此 A2 类可以在自己的方法里调用 A 类的 protected 属性 name.
  5. 最后, 通过默认包下, 输出 C.helloB()A2.HelloPapa() .

执行结果如下:

Hello, I'm A.
Class A
Hello, I'm Class B       
class com.yi21.modifier.A
------- 21yi.com -------
My Father is Class A

非访问修饰符

非访问修饰符, 言下之意就是不进行访问控制, 对其他方面进修修饰的修饰符, 常见的有以下几种:

static

static 的意思是静态的, 所谓静态就是指类不用初始化成一个对象, 即可调用. static 用于修饰类的各种成员, 包括类的内部类(一种定义在类的内部的类).

用法示例如下:

package com.yi21.modifier;

public class StaticDemo {

    static class Duck {
        public void hello() {
            System.out.println("Quak!Quak!Quak!");
        }
    }

    class Fox {
        public void hello() {
            System.out.println("What the fox say?");
        }
    }

    public static String name = "Class StaticDemo";
    public static void hello() {
        System.out.println("Hello I'm " + name);
        Duck duck = new Duck();
        duck.hello();
        StaticDemo demo = new StaticDemo();
        Fox fox = demo.new Fox();
        fox.hello();
    }

}

上述代码, 定义了三个 static 成员, 一个静态内部类 Duck, 一个类变量 name, 一个静态方法 hello, 以及定义了一个非静态成员, 类 Fox .

其中在静态方法 hello 中, 调用静态类 Duck 时就不需要初始化类再调用, 而调用非静态类 Fox 时则需要初始化 StaticDemo, 然后通过初始化的实例来使用类 Fox.

执行结果如下:

Hello I'm Class StaticDemo
Hello I'm Class StaticDemo
Quak!Quak!Quak!
What the fox say?

final

final 的意思是最终的, 最终就意味这不可变化, 不可继承:

  • final 修饰变量时, 变量变成了常量
  • final 修饰了方法, 子类无法重写(override) 父类的方法
  • final 修饰了类, 类无法被继承.

示例代码如下:

package com.yi21.modifier;

public class FinalDemo {
    final static String name = "FinalDemo";
    static class Bird {
        final int footNumber() {
            return 2;
        }
        public void hello() {
            System.out.println("Some bird sounds.");
        }
    }
    final static class Poultry extends Bird {
        /* 无法覆盖父类 final 方法
        int footNumber() {
            return 3;
        }
        */
        public void hello() {
            System.out.println("Some poultry sounds.");
        }
    }
    /* 无法继承 final 类
    static class Duck extends Poultry {

    }
     */
    public static void hello() {
        //无法修改 name 的值
        //name = "OK";
        System.out.println(name);
        Poultry poultry = new Poultry();
        System.out.println(poultry.footNumber());
        poultry.hello();
    }
}

上面代码中, 所有被注释的试图修改, 重写, 继承 final 定义的部分, 都会报错.

更多其他的修饰符

除了上述修饰符外, Java 中还存在其他修饰符, 以下是一些举例:

好了, 以上就是有关 Java 修饰符的全部内容了. 接下来我们讲 Java 中如何声明一个方法