Java static 关键字从浅入深
文章目录前言一、static 的基本概念1.1 static 修饰什么1.2 static 的一句话理解二、static 变量2.1 类变量与实例变量2.2 使用场景三、static 方法3.1 静态方法的特点3.2 使用场景四、static 代码块4.1 static 代码块什么时候执行4.2 初始化顺序五、static 内部类5.1 静态内部类5.2 使用场景六、static 与内存模型6.1 static 存在哪里6.2 类加载与初始化6.3 线程安全思考七、项目中如何使用 static7.1 推荐使用7.2 谨慎使用八、面试如何考察 static8.1 高频问题8.2 回答思路总结前言static是 Java 里非常常见的关键字。它的核心意思是这个成员属于类而不是属于某一个对象。如果把类比作一张“设计图”对象就是按设计图造出来的一台台机器。普通字段是每台机器自己的零件static字段则像挂在工厂墙上的公共看板所有机器看到的是同一份。一、static 的基本概念1.1 static 修饰什么static可以修饰变量、方法、代码块、内部类。常见形式publicclassDemo{staticintcount;staticvoidhello(){System.out.println(hello);}static{System.out.println(class init);}staticclassHelper{}}不能直接修饰普通外部类也不能修饰局部变量。1.2 static 的一句话理解普通成员依赖对象static成员依赖类。所以普通成员通常用对象.成员访问静态成员通常用类名.成员访问。二、static 变量2.1 类变量与实例变量被static修饰的变量叫类变量。它在类加载后只有一份被这个类的所有对象共享。实例变量属于对象每 new 一次就有一份。classUser{staticinttotal;Stringname;User(Stringname){this.namename;total;}}这里name是每个用户自己的名字total是所有用户共同维护的总人数。2.2 使用场景static变量适合存放“全班共享”的数据。常见场景publicclassAppConfig{publicstaticfinalStringAPP_NAMEdemo;}publicclassCounter{privatestaticinttotal;publicCounter(){total;}}适合放常量、计数器、共享配置、缓存引用。不适合放用户登录状态、请求参数、订单数据等会因对象或线程变化而变化的数据。三、static 方法3.1 静态方法的特点静态方法属于类可以不创建对象直接调用。Math.max(1,2);Integer.parseInt(123);静态方法中不能直接访问非静态字段因为它执行时不一定有对象。classDemo{Stringname;staticvoidtest(){// System.out.println(name); // 编译错误}}原因很简单你站在工厂办公室里能看公共看板但不能知道某一台机器里私有零件的状态除非你拿到那台机器的引用。3.2 使用场景静态方法适合无状态工具方法。publicclassStringUtils{publicstaticbooleanisBlank(Stringvalue){returnvaluenull||value.trim().isEmpty();}}如果方法强依赖对象状态就不要写成static。四、static 代码块4.1 static 代码块什么时候执行静态代码块在类初始化时执行并且一个类只执行一次。classDemo{static{System.out.println(load Demo);}}常见触发时机包括创建对象、访问静态字段、调用静态方法、反射加载类。4.2 初始化顺序大致顺序是类加载 - 静态字段赋值和静态代码块 - 实例字段和实例代码块 - 构造方法静态内容先于对象存在。它像工厂开门前先装好的公共设备对象只是后面生产出来的产品。五、static 内部类5.1 静态内部类静态内部类不依赖外部类对象。classOuter{staticclassHelper{}}Outer.HelperhelpernewOuter.Helper();普通内部类依赖外部类对象。classOuter{classInner{}}OuterouternewOuter();Outer.Innerinnerouter.newInner();5.2 使用场景静态内部类常用于把辅助类收在外部类内部表达“它只服务于这个外部类”的关系。经典用法是 Holder 单例publicclassSingleton{privateSingleton(){}privatestaticclassHolder{privatestaticfinalSingletonINSTANCEnewSingleton();}publicstaticSingletongetInstance(){returnHolder.INSTANCE;}}它利用类初始化的线程安全特性实现懒加载和安全发布。六、static 与内存模型6.1 static 存在哪里很多旧资料会说static变量在方法区。更准确地说类的元数据在方法区的实现中HotSpot 从 JDK 8 开始使用元空间保存类元数据而静态字段的实际存储与类对象相关底层实现细节和 JVM 版本有关。学习时可以先记住static成员和类生命周期绑定不和某个实例对象绑定。6.2 类加载与初始化Java 类从被 JVM 认识到可使用会经历加载、验证、准备、解析、初始化等阶段。对static来说重点是准备阶段会给静态变量分配默认值。初始化阶段会执行显式赋值和static代码块。staticintcount10;准备阶段count 0。初始化阶段count 10。6.3 线程安全思考static变量是共享的共享就可能有并发问题。privatestaticintcount;publicstaticvoidadd(){count;}count不是原子操作多线程下可能丢失更新。项目中可用AtomicInteger、锁、不可变对象或者避免共享可变静态状态。七、项目中如何使用 static7.1 推荐使用推荐用于常量publicstaticfinalintMAX_RETRY3;推荐用于无状态工具方法publicstaticStringnormalize(Stringvalue){returnvaluenull?:value.trim().toLowerCase();}推荐用于 Holder 单例privatestaticclassHolder{privatestaticfinalServiceINSTANCEnewService();}7.2 谨慎使用谨慎使用可变静态变量。publicstaticListStringusersnewArrayList();它容易带来全局状态污染、测试互相影响、线程安全问题和难以追踪的数据变化。项目经验里static最大的问题不是不会用而是用得太方便。八、面试如何考察 static8.1 高频问题常见问题static变量和实例变量区别是什么static方法能不能访问普通成员static代码块什么时候执行类加载初始化顺序是什么static final和普通static有什么区别静态内部类为什么能实现单例静态变量线程安全吗8.2 回答思路先讲归属static属于类普通成员属于对象。再讲生命周期static随类加载初始化普通成员随对象创建初始化。再讲访问限制静态上下文没有this不能直接访问实例成员。最后讲项目风险共享可变状态要注意并发和测试污染。总结static的本质是“属于类而不是属于对象”。用得好它可以表达常量、工具方法、类级共享资源和单例结构用得不好它会变成全局变量制造并发问题和测试污染。记住一句话能不共享就不共享必须共享就尽量不可变如果共享可变状态就要认真考虑线程安全和生命周期。