Java static 与 final 详解(简单易懂)

一、static(静态关键字)

1. 核心概念

static修饰的成员(变量、方法、代码块、内部类)属于类本身,而不是类的某个实例(对象)。

  • 比喻:班级的公共饮水机(static),属于整个班级,所有同学(对象)都能使用,且只有 1 份;而每个同学的水杯(非 static),属于各自的实例,有多个副本。
  • 内存角度:static 成员在类加载时就被初始化,存放在方法区(而非堆内存),生命周期与类一致,直到 JVM 卸载该类。

2. 常见用法及示例

(1)static 变量(类变量)
  • 所有实例共享同一个 static 变量,修改一个实例的 static 变量,其他实例的该变量也会同步变化。
  • 访问方式:类名.变量名(推荐)或对象名.变量名(不推荐,易混淆)。
  • 命名规范:通常配合final使用(常量),全大写,下划线分隔;普通 static 变量小写开头。

java

运行

复制代码
public class Student {
    // 非static变量(实例变量):每个学生有独立的姓名
    private String name;
    // static变量(类变量):所有学生共享同一个班级名称,只有1份
    public static String className = "高一(1)班";

    public Student(String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        Student s1 = new Student("张三");
        Student s2 = new Student("李四");

        // 访问static变量:推荐用类名访问
        System.out.println(Student.className); // 输出:高一(1)班
        // 修改static变量
        Student.className = "高一(2)班";
        // 所有实例的static变量都被修改
        System.out.println(s1.className); // 输出:高一(2)班
        System.out.println(s2.className); // 输出:高一(2)班
    }
}
(2)static 方法(类方法)
  • 属于类,无需创建对象即可调用(类名.方法名)。
  • 核心限制:不能直接访问非 static 成员 (因为 static 方法没有this引用,无法指向具体实例),但可以访问 static 成员。
  • 典型场景:工具类方法(如Math.random()Arrays.sort())、工具性操作(如对象创建的静态工厂方法)。

java

运行

复制代码
public class Calculator {
    // static方法:无需创建Calculator对象即可调用
    public static int add(int a, int b) {
        // 可以访问static成员,不能访问非static成员
        return a + b;
    }

    // 非static方法:必须创建对象才能调用
    public int subtract(int a, int b) {
        return a - b;
    }

    public static void main(String[] args) {
        // 调用static方法:直接用类名
        int sum = Calculator.add(10, 20);
        System.out.println(sum); // 输出:30

        // 调用非static方法:必须创建对象
        Calculator cal = new Calculator();
        int diff = cal.subtract(20, 10);
        System.out.println(diff); // 输出:10
    }
}
(3)static 代码块
  • 类加载时执行,且只执行一次(无论创建多少个实例)。
  • 用于初始化 static 变量(如加载配置、初始化静态资源)。

java

运行

复制代码
public class Config {
    public static String DB_URL;

    // static代码块:类加载时执行,初始化静态变量
    static {
        System.out.println("static代码块执行");
        DB_URL = "jdbc:mysql://localhost:3306/test";
    }

    public static void main(String[] args) {
        // 首次访问类,触发类加载,执行static代码块
        System.out.println(Config.DB_URL); // 输出:static代码块执行 + jdbc:mysql://localhost:3306/test
        // 再次创建实例,static代码块不会重复执行
        Config c1 = new Config();
        Config c2 = new Config();
    }
}
(4)static 内部类
  • 属于外部类本身,无需依赖外部类的实例即可创建。
  • 不能访问外部类的非 static 成员(只能访问 static 成员)。

java

运行

复制代码
public class OuterClass {
    private static String staticField = "静态字段";
    private String nonStaticField = "非静态字段";

    // static内部类
    public static class StaticInnerClass {
        public void print() {
            // 可以访问外部类的static成员
            System.out.println(staticField);
            // 错误:不能访问外部类的非static成员
            // System.out.println(nonStaticField);
        }
    }

    public static void main(String[] args) {
        // 创建static内部类:无需外部类实例
        OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
        inner.print(); // 输出:静态字段
    }
}

二、final(最终关键字)

1. 核心概念

final表示 "最终的、不可改变的",修饰不同成员时,含义略有差异,但核心都是 "不可修改"。

  • 比喻:刻在石碑上的文字(final 变量)无法修改;密封的箱子(final 方法)无法被打开重写;绝版的书籍(final 类)无法再版(继承)。

2. 常见用法及示例

(1)final 变量(常量)
  • 一旦赋值,不可修改(引用类型变量:引用不可变,但对象内容可变)。
  • 初始化要求:
    • 局部 final 变量:声明时或使用前赋值即可;
    • 成员 final 变量:必须在声明时、构造方法、初始化块中赋值(三者选其一),且只能赋值一次。
  • 命名规范:final 常量(通常配合 static)全大写,下划线分隔(如PIMAX_VALUE)。

java

运行

复制代码
public class FinalDemo {
    // 1. 成员final变量:声明时直接赋值(推荐)
    private final String NAME = "张三";
    // 2. 成员final变量:构造方法中赋值
    private final int AGE;
    // 3. static final常量(类级常量,最常用)
    public static final double PI = 3.1415926;

    public FinalDemo(int age) {
        this.AGE = age; // 构造方法中初始化final变量
    }

    public static void main(String[] args) {
        FinalDemo demo = new FinalDemo(18);
        // 错误:final变量不可修改
        // demo.NAME = "李四";
        // demo.AGE = 20;

        // 引用类型final变量:引用不可变,但对象内容可变
        final StringBuilder sb = new StringBuilder("hello");
        sb.append(" world"); // 合法:修改对象内容
        System.out.println(sb); // 输出:hello world
        // 错误:引用不可变
        // sb = new StringBuilder("new");

        // static final常量:类名访问,不可修改
        System.out.println(FinalDemo.PI); // 输出:3.1415926
    }
}
(2)final 方法
  • 不能被子类重写(override),保证方法的逻辑稳定(如父类的核心业务逻辑不允许子类修改)。

java

运行

复制代码
public class Parent {
    // final方法:子类不能重写
    public final void sayHello() {
        System.out.println("Hello from Parent");
    }
}

public class Child extends Parent {
    // 错误:无法重写final方法
    // @Override
    // public void sayHello() {
    //     System.out.println("Hello from Child");
    // }

    public static void main(String[] args) {
        Child child = new Child();
        child.sayHello(); // 输出:Hello from Parent
    }
}
(3)final 类
  • 不能被继承(extend) ,保证类的完整性(如 Java 核心类StringMathInteger都是 final 类,避免被篡改)。

java

运行

复制代码
// final类:不能被继承
public final class FinalClass {
    public void print() {
        System.out.println("这是final类的方法");
    }
}

// 错误:无法继承final类
// public class SubClass extends FinalClass {
// }

三、static final 组合使用(最常用)

static final修饰的变量是类级常量,具备两大特性:

  1. static:属于类,内存中只有 1 份,类加载时初始化;
  2. final:一旦赋值,不可修改。
  • 典型场景:定义系统常量(如Math.PIInteger.MAX_VALUE)。

java

运行

复制代码
public class Constants {
    // 数据库连接常量
    public static final String DB_URL = "jdbc:mysql://localhost:3306/test";
    public static final String DB_USER = "root";
    public static final String DB_PWD = "123456";

    // 工具类:私有化构造方法,避免创建实例
    private Constants() {}
}

总结

  1. static 核心:属于类而非实例,类加载时初始化,所有实例共享,可通过类名直接访问;常用于工具方法、共享变量、静态初始化。
  2. final 核心:"不可变",修饰变量则值 / 引用不可改,修饰方法则不可重写,修饰类则不可继承;常用于定义常量、保护核心逻辑 / 类结构。
  3. static final:类级常量,内存唯一且不可修改,是 Java 中定义常量的标准方式。
相关推荐
爱学习的阿磊2 小时前
模板编译期排序算法
开发语言·c++·算法
冰暮流星2 小时前
javascript之for-of循环
开发语言·javascript·ecmascript
jiayong232 小时前
MQ性能优化面试题
java·性能优化·kafka·rabbitmq
不绝1912 小时前
Input/屏幕/Camera/光源/碰撞检测/音频相关
开发语言·javascript·ecmascript
明天…ling2 小时前
sql注入笔记总结
java·数据库·sql
云游云记2 小时前
php性能优化总结
开发语言·性能优化·php
独自破碎E2 小时前
【滑动窗口】最小覆盖子串
java·开发语言
fengfuyao9852 小时前
C#实现指纹识别
开发语言·c#