V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
miaoxia
V2EX  ›  Java

Java 中,究竟如何使用静态变量?

  •  
  •   miaoxia · 2016-08-18 11:17:53 +08:00 · 6166 次点击
    这是一个创建于 3020 天前的主题,其中的信息可能已经有所发展或是发生改变。
    • 静态变量的优点
      • 内存中只保留一份,不需要频繁创建和销毁
    • 静态变量的缺点
      • 破坏面向对象类的封装性
      • 生命周期长
      • 可能产生其他问题?...

    曾见过一个大牛的观点:

    1. 代码中的静态变量最好不要超过 10 个。
    2. 而且静态变量要去序列化,当找不到这个变量时再解冻。

    他说的是否有道理?


    现在有这样一个需求:

    • 子类处理具体业务,需要异步。
    • 父类提供一个线程池,子类处理的时候,直接 submit 一个 Runnable 。
    • 多个子类想共享一个父类的线程池。

    如果我把这个 thread pool 定义为 static ,会有什么坏处吗

    33 条回复    2016-08-20 02:07:40 +08:00
    bigapple111
        1
    bigapple111  
       2016-08-18 12:08:44 +08:00
    如果只是说坏处的话,如果涉及到应用场景的话,我相信每个人都有自己的想法,

    这个问题应该是 “如果我把这个 thread pool 定义为 static ,会有什么好处吗?”
    前文提到 “内存中只保留一份,不需要频繁创建和销毁”

    你的应用需要频繁创建 ThreadPool ?
    miaoxia
        2
    miaoxia  
    OP
       2016-08-18 12:30:45 +08:00
    @bigapple111 感谢你的回复
    前文提到的只是静态变量的优点,和场景无关。
    应用肯定不需要频繁创建 threadPool ,而且想如何去避免频繁创建 threadPool 。
    所以才会去假设,将 threadPool 设置成 static 后,会不会实现我预期的目的,
    而且是否可能带来什么问题。
    miaoxia
        3
    miaoxia  
    OP
       2016-08-18 12:32:14 +08:00
    @bigapple111
    如果不设置为 static ,每 new 一个子类的时候
    都会创建一个新的 threadPool 吧
    我就是希望可以避免这个问题,能让所有子类共享父类的 threadPool 。
    tobyxdd
        4
    tobyxdd  
       2016-08-18 13:02:42 +08:00
    没必要为了别人因为他们项目特点总结的条条框框影响自己项目架构 ThreadPool 弄成 Static 再正常不过了
    ghostsf
        5
    ghostsf  
       2016-08-18 13:27:47 +08:00
    根据实际需求来,很明显,这里需要 static
    bigapple111
        6
    bigapple111  
       2016-08-18 13:41:11 +08:00
    @miaoxia

    如果不设置为 static ,每 new 一个子类的时候
    都会创建一个新的 threadPool 吧
    我就是希望可以避免这个问题,能让所有子类共享父类的 threadPool 。

    static 确实能避免上述的问题,大胆的写吧
    bigapple111
        7
    bigapple111  
       2016-08-18 13:43:17 +08:00
    @miaoxia 或者你自己实现一个单例的 ThreadTaskService ,父类里面定义这个 Service,也一样能达到你想要的效果
    SoloCompany
        8
    SoloCompany  
       2016-08-18 13:44:18 +08:00 via iPhone
    1 static 不应该使用一定意义上是正确的
    2 threadpool 应该注入来解决
    shimanooo
        9
    shimanooo  
       2016-08-18 13:53:11 +08:00
    静态常量可以有
    YORYOR
        10
    YORYOR  
       2016-08-18 14:26:22 +08:00
    static 可以,系统启动时 生成一个单例的 pool 也可以
    cloudzhou
        11
    cloudzhou  
       2016-08-18 15:10:34 +08:00
    1 使用单例化
    2 注入
    miaoxia
        12
    miaoxia  
    OP
       2016-08-18 15:26:25 +08:00
    @tobyxdd
    @ghostsf
    @bigapple111
    感谢回复 会小心谨慎的使用 static
    miaoxia
        13
    miaoxia  
    OP
       2016-08-18 15:30:58 +08:00
    @bigapple111
    @YORYOR
    @cloudzhou
    感谢回复 单例是一种解决方式
    不过仍有些疑惑 单例不也是通过 static 方式实现的吗

    是否有以下假设:
    将需要共享的元从宿主中分离出来,做成单例(仍是 static 的)
    就避免 static 可能产生的问题?
    miaoxia
        14
    miaoxia  
    OP
       2016-08-18 15:37:46 +08:00
    @SoloCompany 非常感谢
    开拓了新思路

    再请教下 通过 Constructor 还是 seter 来实现呢?
    在我看来既然已经实现了异步方式, threadPool 的实例是一定要注入的
    既 使用 Constructor 方式来实现

    但在子类的 Constructor 里,还需要传入其他业务相关的参数
    感觉一个业务类的 Constructor 里并列注入一个 threadPool ,看起来有点突兀- -

    由于业务能力尚浅,可能有些比较幼稚的观点,请指教
    miaoxia
        15
    miaoxia  
    OP
       2016-08-18 15:38:03 +08:00
    @shimanooo 感谢回复
    YORYOR
        16
    YORYOR  
       2016-08-18 15:39:44 +08:00
    @miaoxia 单例可以通过 static 实现,也可以你自己在系统启动时仅在系统内 生成一个 final 类型的即可,类似 spring 中的 bean 貌似都是单例的
    slixurd
        17
    slixurd  
       2016-08-18 16:24:17 +08:00
    @YORYOR Spring 的 Bean 是 singleton 这个只是 scope 默认配置而已,你配个 prototype 就不一样了。
    单例什么的和 static field 没有必然关系,只是因为 static field 一定只存在一份,所以经常用来做单例而已。
    什么 double check idiom 之类的
    asj
        18
    asj  
       2016-08-18 17:24:53 +08:00
    只应该拿来定义常量。
    如你问题里描述的需求,应该用 Spring 或者其他框架来管理对象的生命周期,而非依赖语言特性。
    SoloCompany
        19
    SoloCompany  
       2016-08-18 21:02:30 +08:00   ❤️ 1
    @miaoxia

    static 不建议使用主要有两点:容易造成内存泄漏,强耦合,强耦合的问题尤其严重。

    注入的方式有很多种,不依赖框架的话,构造方法或者 setter 方法或者单独的 init 方法都是可取的,用 Spring 之类的依赖注入当然就更简单了,其它比如容器提供 jndi 注册也是一种选择,总而言之有很多种方法可以实现单例, static 是看上去省事但却是最坏的选择
    hinkal
        20
    hinkal  
       2016-08-19 01:06:30 +08:00   ❤️ 1
    这个需求很适合 static ,不过可以考虑下真的是这个需求吗?譬如“多个子类想共享一个父类的线程池”,高耦合,子类想访问线程池中的对象?或许可以抽象出一个线程池控制器,通过代理者进行交互。因此最好从设计上改变,减少不必要 static 的使用。
    georgema1982
        21
    georgema1982  
       2016-08-19 02:17:14 +08:00   ❤️ 1
    一般的原则是 static 和变量可接受的组合是 final static ,即用来定义常量;不要有任何 static 的方法。现代 java 编程里,如果出现非 final 的 static 申明,基本上都是不可接收的
    miaoxia
        22
    miaoxia  
    OP
       2016-08-19 09:00:17 +08:00
    @asj 感谢回复
    你说的有道理,可能我的设计本身存在问题。
    miaoxia
        23
    miaoxia  
    OP
       2016-08-19 09:02:36 +08:00
    @SoloCompany
    感谢您的再次回复
    如果设计不变,我会使用注入的方式来代替 static
    asj
        24
    asj  
       2016-08-19 09:40:53 +08:00
    @miaoxia 说设计什么的可能有点虚,最直接的原因是这样的代码非常难写 unit test 。尝试一下就会有感觉了。
    miaoxia
        25
    miaoxia  
    OP
       2016-08-19 09:42:28 +08:00
    @hinkal 感谢您的回复
    您给出的设计给了我很大启示

    还想和您探讨下 “'多个子类想共享一个父类的线程池'会造成高耦合” 的问题
    您是指 多个子类 与 共同父类 之间的耦合吗
    如果我在父类中将与 threadPool 相关的操作 写成模板
    在子类中去重写实际处理业务的方法 对于子类而言 是否异步是透明的(或者尽量透明)
    这样还会产生耦合的现象吗

    可能我对耦合的定义仍然不是那么明晰 请指教
    miaoxia
        26
    miaoxia  
    OP
       2016-08-19 09:45:42 +08:00
    @georgema1982 感谢回复
    您的观点我完全赞同
    只是实际工作中我在极力避免的过程中会遇到问题

    除本文所指问题外
    例如 Utils 类中,大家的惯例是 private Constructor
    public 方法全部 static
    您觉得这样的情况如何避免?
    miaoxia
        27
    miaoxia  
    OP
       2016-08-19 09:59:38 +08:00
    @hinkal 再补充下
    我的问题中提到"子类去 submit 一个 runnable"
    如果修改我上面说的实现,做出以下的实现:
    1. 父类中的 thread pool 相关操作对于每个子类都是相同的,是一个共同的模板。
    2. 由于是操作相同,当父类的线程机制做调整时,其他全部子类同时受影响。(子类是并列的概念,只是业务有区分)

    如果实现共享父类的 thread pool ,会让全部的并行子类都单向依赖这个 thread pool 。
    会产生高耦合的危害吗?

    换句话说,当这个被依赖的模块,对于依赖它的模块的处理是统一的。
    这样的情况,会产生高耦合的危害吗?
    shyling
        28
    shyling  
       2016-08-19 10:15:51 +08:00 via Android
    那些说注入的仿佛自己没有间接用 static 。。。乖乖用 Executor 不就好咯。。。接口标准库都封装好了。
    hinkal
        29
    hinkal  
       2016-08-19 12:12:52 +08:00
    @miaoxia 如果“父类中的 thread pool 相关操作对于每个子类都是相同的”,那么放父类中做成”模板“,并且把对父类 threadpool 的操作放在父类方法中,这样对于子类,弗雷德 thread pool 是透明的,这样就不会高耦合吧。如果让子类都直接访问父类 static 对象,不符合迪米特法则,就是高耦合。
    hinkal
        30
    hinkal  
       2016-08-19 12:19:13 +08:00
    @miaoxia ”您是指 多个子类 与 共同父类 之间的耦合吗 “不是,我指的是多个子类之间的耦合。本来,子类和父类之间不存在高耦合的问题,因为继承的目的就是公有一些方法和属性,然而你把所有子类产生的对象提交到父类的一个线程集合里,强行让他们互相可以引用,这样就让子类之间耦合起来了。
    jason19659
        31
    jason19659  
       2016-08-19 15:07:13 +08:00
    你也知道静态变量的特点了,在符合这个特点,或者是使用这个最方便的时候可以用
    TakWolf
        32
    TakWolf  
       2016-08-20 00:32:00 +08:00
    凡是单例模式的通常都应该用 static

    基于回收池或者重用池模式的,回收池实例声明周期为逻辑声明周期一致,因此,如果你是对 app 的回收池,通常他也是单例静态的
    georgema1982
        33
    georgema1982  
       2016-08-20 02:07:40 +08:00
    @miaoxia 说明“大家”都还在用老式的设计模式,即用 java 本身的特性来管理一个类的生命周期。现代的设计模式是不要自己实现生命周期,把所有 java 类都写成 pojo 。这种 pojo 不应该有非 public 的 constructor ,不应该存在 static 的方法。你们公司里如果还在用这种老式的设计模式的话,我第一反应就是你们公司不做单元测试
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   972 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 19:17 · PVG 03:17 · LAX 11:17 · JFK 14:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.