static什么意思|被static修饰的内容归属类而非对象
上次赶项目改Java代码被组长揪着问话,半天搞不懂static什么意思,随手乱改修饰符害得整个测试环境的数据反复错乱,连着加班两个通宵。那天工位旁边的风扇吱呀转着,手边堆了三张打印出来的代码草稿,原本只是想写一个统计在线用户数量的小方法,嫌每次新建对象太繁琐,随手在方法前面添上了static关键字,完全没琢磨过这个改动会牵连全局的数据存储。
同组实习生照着我的写法复制代码,同步踩了一模一样的问题。
原先的业务逻辑里,每创建一个用户管理类的实例,就会生成一份独立的在线计数变量,不同客户端登录增减数据只会改动自身实例里面的值,数据之间互不干扰,在添上static之后,那个计数变量直接变成依附类本身存在的内容,全项目所有实例共用同一块内存空间,测试的时候多开几个测试账号轮番登录退出,数值就毫无规律的暴涨暴跌,一会多出几十条虚假在线记录,一会正常登录的用户被莫名剔除统计列表,反复排查了近一天,从数据库语句查到循环逻辑,在各个断点来回跳转调试,一会怀疑是数据库事务没有提交,一会又觉得前端传参格式出错,来回改了不少无关代码,白白耗费大把工时,中间组长过来巡检,指着关键字让自查,心里发怵不敢多问细节,只能低头挨个注释测试,生怕开口求教被数落基础功底薄弱,只能靠着零散的断点日志一点点捋清数据变化的轨迹。
后来在在午休翻前辈遗留的项目注释,才看见那行备注写着被static修饰的变量存放在静态区,不是跟着对象堆内存走,那时候才恍然,之前分不清实例变量和静态变量的存储位置,总以为所有变量全依附新建的对象,做改动的时候想当然简化代码,贪图少写一行new创建对象的代码,反倒给整体业务埋下隐患,做测试的时候为了省事,直接在main函数里面调用静态方法,不用实例化类,这个便利就是static带来的,但便利背后的资源共用是我完全忽略的关键点,身边做后端的朋友之前也因为静态集合存临时数据,上线后出现内存溢出的故障,项目上线前临时替换存储方案,耽误了版本提测进度,他吐槽的时候我还暗自侥幸,没料到没过几天自己就栽在了同一个知识点上。
试过把工具类所有方法全部加上static,后续需要扩展多配置环境的时候,没办法通过不同实例传入不同参数,只能新增一堆静态变量用来区分配置,代码冗余程度直接翻倍,原先规划好的扩展思路全部作废,只能重新拆分工具类结构,删掉多余的static修饰,拆分出实例化调用的分支,那段时间对着密密麻麻的代码,越改越烦躁,又不想主动请教身边技术过硬的同事,总怕被嘲讽基础知识点掌握不牢,就自己死磕零散的在线文档,东拼西凑零散知识点,浪费掉不少本该用来推进新需求的工作时间,反正那段日子一看见代码里飘着的static就头疼。
静态代码块也是static的一种用法,项目启动优先加载。
盲目全加static是最频繁的失误,没有依据数据是否需要全局共享就随意添加关键字,是大部分初学的人容易踏入的误区,有时候为了调用简便滥用修饰符,短期写代码速度变快,长期维护成本成倍往上走,之前还自作聪明把实体类的成员变量全加上static,导致所有读取到的实体数据互相覆盖,查询不同id的数据,返回值永远是最后一次赋值的内容,线上预发布环境测出这个bug的时候,距离版本上线只剩半天,紧急回滚代码,撤销所有错误添加的static修饰,正确的使用逻辑只需要按需给需要全局共享的数据添加修饰,其余成员保持实例调用即可。那段改bug的间隙还随手查过静态常量,被final和static一同修饰的内容会在类加载时初始化,后续无法修改数值,那个小细节是调试冗余代码时偶然发现,没有特意整理进笔记,转头差点就忘在脑后。
收拾完当天的调试笔记,随手把作废的草稿揉成团丢进桌角垃圾桶。