请教下,项目中静态方法过多有什么缺点?

2021-03-29 16:23:34 +08:00
 NULL2020

最近被朋友拉拢一起搞个小项目,我算是目前最后入伙的。

前几天提了个建议,把 redis 的工具类改成静态方法的形式(现在是以 spring bean 的形式交给容器管理),方便调用。结果另一位后端说不要,静态方法太多不好,问他为什么,说叫自己去看 JVM (黑人问号脸)。

我思考几天,也 google 了下,也没见着有特别有说服力的说法,自己勉强想到了一个点,那可能就是会导致 JVM 静态方法区容量过大,最终引起 OOM ?

但是,说实话,一个小项目,也就那点代码量,连一个工具类这点内存都要省了吗?

6474 次点击
所在节点    Java
70 条回复
24bit
2021-03-29 23:06:26 +08:00
感觉无副作用的函数会比较适合写成静态的
24bit
2021-03-29 23:24:59 +08:00
Redis 工具类感觉大概率会依赖外部配置,同时对 Redis 进行修改,写成静态类本质上也就是利用静态全局变量来托管 Redis 对象。

这和用容器来托管感觉没有太多本质的区别,而且使用容器和可以享受 Spring 带来的很多便利。

对于 JVM,静态类感觉其实也就是方法区会多点静态常量?而且静态方法可以通过 invokestatic 调用,这个在性能上应该会比 invokevirtual 好一些。
NULL2020
2021-03-29 23:33:48 +08:00
@huijiewei 大概是我把操作 redis 当作工具使用了吧

@kassadin 单元测试正常使用
gadsavesme
2021-03-30 00:03:17 +08:00
一般上来问题说不清楚,动不动就是自己去看 xxx 的人,大概率自己就是个半吊子。方法区 oom 的我见过,写太多静态方法导致 oom 的我是孤陋寡闻了,几十 m 的内存就够你复制静态方法复制到手抽筋了。
GM
2021-03-30 10:11:47 +08:00
@NULL2020 我是拿一个极端的案例来说明为什么不要用 public static,你这个使用场景明显就属于不适合使用的场景。

什么场景适合使用呢?完全无状态的、不依赖、不修改任何外部状态的函数可以使用 public static 。
GM
2021-03-30 10:12:49 +08:00
@gadsavesme JVM8 以上不存在方法区 OOM 问题。
wolfie
2021-03-30 10:56:17 +08:00
@NULL2020 #18
没必要,且没有优点。什么场景必须由 静态方法 去调用这个工具类。

封装、继承、多态。
新需求,需要加 切面 怎么办、需要不同对象 调用时,注入不同工具子类怎么办。
wolfie
2021-03-30 10:58:04 +08:00
@NULL2020 #17
你这个封装 跟 static 无关。
q149072205
2021-03-30 11:19:12 +08:00
static 速度快啊,不用实例化啊。。
CODEWEA
2021-03-30 11:31:14 +08:00
因为你的提出的意见毫无价值,没有修改的必要性
hantsy
2021-03-30 11:33:09 +08:00
》那可能就是会导致 JVM 静态方法区容量过大,最终引起 OOM ?

这个是存在的。

工具类使用 static,要看情况。spring core 中有一个 org.springframework.utils 都是工具类,没必要实例化,用容器管理。
encro
2021-03-30 13:01:58 +08:00
赞同 @GM

修改内部属性和数据的通常都不用静态。
可能连接池后者单例的都不用静态。


案例一:

比如 redis 操作,没必要静态。

r = new redis();
r->set();

因为这里 redis 可能是连接池,也可能是单例,这时候为了维护容易,不要再去做 redis::set 方法,因为静态方法可能跳过构造函数。

假设有多个 redis 数据库,那么可以是

r1 = new redis(db1);
r2 = new redis(db2);

静态方法可能就成了

redis::set(key,val,db1)

用起来就纠结了


案例二:

mvc 的 model 里面通常除了 create 方法,都不要静态(静态常量可以有)

Class Product{

const STATE_DRAFT =0
const STATE_PUBLISH =1

public static function create(data){}

public function update(data){}

public function remove(){}
}

这时候 update,remove 不静态,是为了减少对外暴露接口,方便代码统一修改。
create 用静态是因为它返回了 product 实例
GM
2021-03-30 13:20:14 +08:00
@encro 要被屎山荼毒过后,才能知道原来依赖注入是很香的,设计模式是真有用的。
no1xsyzy
2021-03-30 13:42:19 +08:00
神说:过早或过度优化是万恶之源

过早或过度抽象也差不了多少。
前期能不封装就不封装,能 Ctrl-C Ctrl-V 决不 Extract function / method
不过,如果你学过 Haskell 的话你会很清楚如何抽象,这个语言是抽象适度程度的训练。

简单地说,面向对象的核心是印欧语系的主谓结构,class 是本体论的映射,interface 是认知论的映射。

@winnerczwx 你需要注意一下一个背景条件:当前已经完成了一个实现。因此从项目创始人角度来说,不改才是对的。

@GM 静态函数完全可以做出类似依赖注入的效果,把需要注入的部分作为参数显式传递。这大约就是 Functor ?
C 时代就是这么写的,Go 的方法书写起来也差不多这个感觉,至于 Python 的话 self 都是显式传递的……
no1xsyzy
2021-03-30 13:53:54 +08:00
@no1xsyzy class 是范畴论的映射,object 是本体论的映射,interface 和 trait 是认知论的两种学说的映射(虽然 Java 的 interface 其实是 trait ?),前者我知道是 “鸭子定律” 的映射,后者不清楚有什么称呼。

提问:你的 redis 操作是范畴论下的操作,还是本体论下的操作?
存在一个相关范畴,你在该范畴下描述一个客观规律?还是说,这是存在一个东西,你在描述一个东西的行为?
HolmLoh
2021-03-30 14:46:27 +08:00
如果我没记错的话
java8 已经用元空间替代掉了原来的永久代
元空间是用的直接内存,只要你的机器够,就不会 OOM

所以因为内存原因而不能用静态方法是没有道理的
1109599636
2021-03-30 14:56:04 +08:00
我一个写 go 和 py 的也想知道为什么,但是 50 多楼下来没有人能给出一个有说服力的答案。。。
GM
2021-03-30 15:16:09 +08:00
@no1xsyzy 那你累不累啊?既然已经是用 spring 框架了,老老实实用 spring 的依赖注入,不香吗?
GM
2021-03-30 15:17:38 +08:00
@1109599636 我在 15 楼已经给出了。
no1xsyzy
2021-03-30 15:29:21 +08:00
@GM 因为用的语言太多了,所以习惯了自己构造抽象(负迁移警告)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/766202

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX