ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

别再用错 final 和 static 了!一文讲透 Java 两大高频关键字(附避坑指南)

别再用错 final 和 static 了!一文讲透 Java 两大高频关键字(附避坑指南) Java 关键字详解final 与 static作用 使用场景final和static是 Java 中使用频率最高、也最容易混淆的两个关键字。本文从「能修饰什么 → 起到什么作用 → 用在什么场景」三个层次把这两个关键字彻底讲清楚。 一句话区分final管「不可改变」static管「类级别共享」。目录一、final 关键字1.1 final 修饰类1.2 final 修饰方法1.3 final 修饰变量1.4 final 修饰引用类型的易错点1.5 final 的三大使用场景二、static 关键字2.1 static 修饰成员变量2.2 static 修饰成员方法2.3 static 修饰代码块2.4 static 修饰内部类2.5 static 的典型使用场景2.6 static 常见误区三、final 与 static 对比总结四、总结一、final 关键字一句话概括final的语义是「最终的、不可改变的」它可以修饰类、方法、变量三种目标但「不可改变」的具体含义随修饰目标而不同。修饰目标作用一句话记忆类该类不能被继承断子绝孙方法该方法不能被重写不容篡改变量该变量只能赋值一次常量一锤定音1.1 final 修饰类当final修饰一个类时该类不能被继承所有成员方法都不能被重写。publicfinalclassString{// String 就是典型的 final 类}// ❌ 编译报错无法从最终类 String 中进行继承classMyStringextendsString{}核心作用防止通过继承来改变类的行为。JDK 中的典型案例String、Integer等包装类都被声明为final——因为如果允许继承并重写方法就可能破坏它们「不可变」的语义。1.2 final 修饰方法当final修饰一个方法时该方法不能被重写但可以正常被子类继承使用。classFather{publicfinalvoidshow(){System.out.println(Father 的方法不允许被改写);}}classSonextendsFather{// ❌ 编译报错Son 中的 show() 无法覆盖 Father 中的 show()// public void show() { }}核心作用① 锁定方法行为防止子类篡改② 在早期 JVM 中还有利于内联优化现代 JVM 已能自动判断这条更多是历史原因。1.3 final 修饰变量final修饰变量时该变量成为常量只能被赋值一次。按变量种类又分三种情况① 修饰局部变量赋值后不可再改。publicstaticvoidmain(String[]args){finalinta10;// a 20; // ❌ 编译报错无法为最终变量 a 分配值}② 修饰成员变量必须显式初始化且只能通过以下两种方式之一赋值声明时直接赋值在构造方法中赋值每个构造方法都要保证赋值一次。classStudent{finalStringnameHanmeimei;// 方式一声明时赋值finalintage;Student(intage){// 方式二构造方法中赋值this.ageage;}}③ 修饰静态变量static final即全局常量通常要求声明时立即赋值命名习惯是全大写。publicclassConstants{publicstaticfinaldoublePI3.1415926;publicstaticfinalintMAX_COUNT100;}1.4 final 修饰引用类型的易错点这是面试与日常编码的高频坑点当final修饰引用类型时它约束的是引用本身指向不能变而不是引用指向的对象内容。publicstaticvoidmain(String[]args){finalint[]array{1,2,3,4,5};array[0]100;// ✅ 编译通过内容可以修改System.out.println(Arrays.toString(array));// [100, 2, 3, 4, 5]// array new int[]{4, 5, 6}; // ❌ 编译报错无法为最终变量 array 分配值}结论final修饰引用类型 →指向不能变内容可以变。这也是「String 不可变不是因为 value 被 final 修饰」的原因——真正原因是 String 类中没有任何修改 value 内容的方法。1.5 final 的三大使用场景场景写法说明定义全局常量public static final int MAX 100;最常用配合static形成类级别常量保护不可变类public final class Xxx禁止被继承防止行为被篡改锁定关键方法逻辑public final void login()安全校验、模板方法等不允许被子类改写二、static 关键字一句话概括static的语义是「静态的、类级别的」——被static修饰的成员属于类而不属于某个具体对象在类加载时就完成初始化全类共享同一份。修饰目标作用一句话记忆成员变量变成类变量全类共享一份大家共用一个成员方法变成类方法可通过类名直接调用不用 new 也能用代码块成为静态代码块类加载时执行一次一次性初始化内部类成为静态内部类不依赖外部类实例独立于外部对象2.1 static 修饰成员变量被static修饰的变量称为静态变量 / 类变量它在方法区中只存一份被该类的所有对象共享。classStudent{publicStringname;// 实例变量每个对象各有一份publicstaticStringschool第一中学;// 静态变量所有对象共享一份publicStudent(Stringname){this.namename;}}publicstaticvoidmain(String[]args){Students1newStudent(张三);Students2newStudent(李四);System.out.println(s1.school);// 第一中学不推荐这样写System.out.println(Student.school);// ✅ 推荐通过类名访问Student.school第二中学;// 修改静态变量System.out.println(s2.school);// 第二中学 —— s2 也受影响}关键区别实例变量随对象的创建而存在静态变量在类加载时就已存在即使没有创建任何对象也能使用。2.2 static 修饰成员方法被static修饰的方法称为静态方法 / 类方法可以直接通过类名调用不需要创建对象。publicclassUtils{publicstaticintadd(inta,intb){returnab;}publicintsub(inta,intb){// 实例方法returna-b;}}publicstaticvoidmain(String[]args){intr1Utils.add(1,2);// ✅ 静态方法类名直接调用// Utils.sub(3, 1); // ❌ 编译报错实例方法必须先 newUtilsunewUtils();intr2u.sub(3,1);// 实例方法通过对象调用}静态方法的两条硬性限制静态方法中不能直接访问非静态实例成员——因为静态方法在对象创建前就可能被调用此时实例成员还不存在静态方法中不能使用this和super—— 它们都依赖当前对象。publicclassDemo{intnum10;// 实例变量publicstaticvoidtest(){// System.out.println(num); // ❌ 编译报错无法从静态上下文中引用非静态变量// System.out.println(this.num); // ❌ 编译报错this 不可用于静态上下文}}反之实例方法可以自由访问静态成员。2.3 static 修饰代码块静态代码块在类加载时自动执行一次且只执行一次常用于初始化静态成员如加载配置文件、初始化连接池。publicclassConfig{publicstaticMapString,Stringmap;static{// 静态代码块类加载时执行一次mapnewHashMap();map.put(url,jdbc:mysql://localhost:3306/db);map.put(driver,com.mysql.cj.jdbc.Driver);System.out.println(静态代码块执行了);}publicConfig(){System.out.println(构造方法执行了);}publicstaticvoidmain(String[]args){newConfig();// 输出顺序静态代码块执行了 → 构造方法执行了newConfig();// 输出构造方法执行了静态代码块不再执行}}执行顺序重要父类静态代码块 → 子类静态代码块 → 父类实例代码块 → 父类构造方法 → 子类实例代码块 → 子类构造方法2.4 static 修饰内部类静态内部类不依赖外部类对象即可创建不能直接访问外部类的非静态成员。publicclassOuter{privatestaticinta1;privateintb2;staticclassInner{// 静态内部类publicvoidshow(){System.out.println(a);// ✅ 可以访问外部类的静态成员// System.out.println(b); // ❌ 不能访问外部类的实例成员}}classInner2{// 普通内部类publicvoidshow(){System.out.println(b);// ✅ 可以访问外部类实例成员}}publicstaticvoidmain(String[]args){Outer.InnerinnernewOuter.Inner();// 静态内部类无需外部类对象inner.show();OuterouternewOuter();Outer.Inner2inner2outer.newInner2();// 普通内部类必须先有外部类对象inner2.show();}}2.5 static 的典型使用场景场景示例说明工具类方法Math.abs()、Arrays.sort()无需创建对象直接调用全局常量public static final int MAX 100;类级别共享常量全局计数器static int count;统计该类创建了多少对象单例模式private static final Xxx instance new Xxx();饿汉式单例静态工厂方法Integer.valueOf(1)隐藏构造细节资源初始化静态代码块加载配置文件类加载时执行一次main方法public static void main(String[] args)JVM 无需创建对象即可调用入口// 场景示例用静态变量统计创建了多少个对象classCounter{publicstaticintcount0;// 所有对象共享publicCounter(){count;// 每创建一个对象count 加 1}}publicstaticvoidmain(String[]args){newCounter();newCounter();newCounter();System.out.println(Counter.count);// 3}2.6 static 常见误区静态方法里不能用this/super也不能直接访问实例变量和实例方法静态方法不能被重写父类静态方法在子类中「同名同参」时是隐藏Hide不是重写且调用取决于编译时类型静态变量是共享的一个对象改了会影响所有对象多线程场景下要格外小心可能需要synchronized或volatilestatic不能修饰局部变量局部变量属于方法栈帧无类级别概念。三、final 与 static 对比总结3.1 final vs static维度finalstatic核心语义最终的、不可改变静态的、类级别共享修饰类类不能被继承只用于静态内部类修饰方法方法不能被重写方法属于类类名可直接调用修饰变量变量只能赋值一次变量属于类全类共享一份生效时机编译期约束引用不可变类加载时初始化常见组合public static final→ 全局常量同左3.2 容易混淆的组合组合含义示例static final静态常量类加载时赋值全类共享public static final int MAX 100;final static与上等价修饰符顺序不影响语义final static int MIN 0;abstractvsfinal互斥抽象要求被继承重写final 禁止二者不能同时修饰同一个类/方法staticvsabstract互斥静态方法不能被重写抽象方法必须被重写不能同时修饰同一个方法四、总结final是「不可变」的保证三种修饰含义不同类不可继承、方法不可重写、变量只能赋值一次注意final修饰引用类型时只锁指向、不锁内容static是「类级别」的标记修饰的成员属于类、全类共享、类加载时初始化静态方法不能访问实例成员、不能用 this/super、不能被重写static final组合是定义全局常量的标准姿势命名习惯全大写如果这篇文章对你有帮助欢迎点赞 收藏 ⭐ 关注后续会继续更新 Java 系列文章
返回列表