gpt4 book ai didi

java - 将所有方法设为静态

转载 作者:行者123 更新时间:2023-12-04 02:36:46 25 4
gpt4 key购买 nike

在我的应用程序中,有许多类包含处理数据的方法,这些方法可以是计算和数据丰富。

我的问题是 - 如果该类没有任何类级变量,我可以将类中的所有方法设为静态吗?

我想线程不会有任何问题。

有什么后果吗?是否有任何性能优势,因为我不必实例化类?

示例类:

Class A{   
public Object findTheCar(String id){
Car car= new Car();
//do something
return car;
}
}

我打算将上述更改为静态。

最佳答案

一个经验法则:问问自己“即使还没有构建 Obj,调用这个方法是否有意义?”如果是这样,它肯定应该是静态的。

所以在一个类(class)Car你可能有一个方法 double convertMpgToKpl(double mpg)这将是静态的,因为人们可能想知道 35mpg 转换成什么,即使没有人制造过汽车。但是void setMileage(double mpg) (设置特定 Car 的效率)不能是静态的,因为在构造任何 Car 之前调用该方法是不可想象的。

(顺便说一句,反过来并不总是正确的:您有时可能有一个涉及两个 Car 对象的方法,并且仍然希望它是静态的。例如 Car theMoreEfficientOf( Car c1, Car c2 ) 。虽然这可以转换为非静态版本,但有些人会争辩说,既然没有“特权”选择哪个 Car 更重要,你不应该强制调用者选择一个 Car 作为你调用方法的对象。这种情况占相当小部分但是,所有静态方法。)

尽管有一些使用静态方法的正当理由:

  • 性能 :如果您想要运行某些代码,并且不想实例化额外的对象来执行此操作,请将其推送到静态方法中。 JVM 也可以优化静态方法很多(我想我曾经读过 James Gosling 声明你不需要在 JVM 中自定义指令,因为静态方法同样快,但找不到源 - 因此这可能是完全错误的)。是的,这是微优化,可能不需要。而且我们程序员从不做不需要的事情,因为他们很酷,对吧?
  • 实用性 : 不是调用 new Util().method(arg),而是调用 Util.method(arg) 或具有静态导入的 method(arg)。更简单,更短。
  • 添加方法 :你真的希望 String 类有一个 removeSpecialChars() 实例方法,但它不存在(它不应该存在,因为你的项目的特殊字符可能与其他项目的不同),并且你不能添加它(因为Java 是最低限度的理智),因此您创建一个实用程序类,并调用 removeSpecialChars(s) 而不是 s.removeSpecialChars()。甜的。
  • 纯度 : 采取一些预防措施,你的静态方法将是一个纯函数,也就是说,它唯一依赖的是它的参数。数据输入,数据输出。这更易于阅读和调试,因为您不必担心继承方面的怪癖。您也可以使用实例方法来做到这一点,但是编译器会使用静态方法为您提供更多帮助(通过不允许引用实例属性、覆盖方法等)。

  • 如果你想创建一个 ,你还必须创建一个静态方法。单例 ,但是……不要。我的意思是,三思而后行。

    现在,更重要的是,为什么您不想创建静态方法?基本上, 多态走出窗外 .您将无法覆盖该方法,也无法在接口(interface)中声明它。您的设计需要很大的灵活性。另外,如果您需要 状态 ,你最终会得到很多 并发错误 和/或瓶颈,如果你不小心。

    So define static methods in the following scenarios only:


  • 如果您正在编写实用程序类并且不应更改它们。
  • 如果该方法未使用任何实例变量。
  • 如果任何操作不依赖于实例创建。
  • 如果有一些代码可以很容易地被所有实例方法共享,则将该代码提取到静态方法中。
  • 如果您确定该方法的定义永远不会被更改或覆盖。作为静态方法不能被覆盖。

  • 让我们也更详细地讨论它:

    优点:

    静态成员/方法在辅助类中使用,例如 Math 或常量类。这有助于其他对象利用字符串或有用的函数,您不需要为其创建对象而是使用类名调用。示例 – 使用静态函数调用单例对象。

    缺点:

    静态成员是类的一部分,因此会一直保留在内存中,直到应用程序终止并且永远不会被垃圾收集。使用过多的静态成员有时预示着您无法设计产品并试图应对静态/过程编程。它表示面向对象的设计受到了损害。这可能会导致内存溢出。如果您在 Java 中使任何方法静态化,也有某些缺点,例如您不能覆盖 Java 中的任何静态方法,因此它使测试变得更加困难,您不能用模拟替换该方法。由于静态方法维护全局状态,因此它们可以在并发环境中创建难以检测和修复的微妙错误。

    需要记住的事情:

    静态变量将是类定义的一部分,而不是在堆上。但是,当您知道将从多个位置访问对象时,静态变量很有用。访问静态资源不是线程安全的。在线程环境中,您可能会得到奇怪/不可预测的结果。但是如果你只读取静态值,那么使用线程就可以了。

    静态如何打破封装:

    它们的技术实现是允许跨类的所有实例维护状态。问题是这本质上不是 OOP,因为它不考虑封装。如果一个变量可以被一个类的任何实例改变,那么封装/信息隐藏背后的基本原则就完全失去了:一个对象不再完全控制它的状态。它的状态现在依赖于本质上是全局的变量。我们知道这是不好的。甚至私有(private)静态变量在全局级别保持状态,但只是限制其访问。对象的任何实例都可以改变静态变量,这会导致歧义,因为对象的各个实例不再控制自己的状态。状态更改可以在不知道依赖于该状态的对象的情况下任意发生,这是有问题的,因为在发生这种情况时对象可能无法正常工作。正如人们常说的那样,“继承破坏封装”静态以一种更为严厉的方式做到了这一点:不仅公开内部实现,还公开内部状态。

    In Your Example Question:



    正如您所提到的,该方法将用于多线程环境,请考虑以下问题(修改您的代码):
    public Object findTheCar(String id) {
    Car car = null; //Line 2
    if (id != null) {
    car = new Car();
    }
    //Line 6
    // do something

    //
    //Line 10
    return car;
    }

    In the above: if this executed from two threads, and 1st thread is at line 6, and second thread is at line 2, still this will be thread safe.



    因为:

    局部变量存储在每个线程自己的堆栈中。这意味着局部变量永远不会在线程之间共享。这也意味着所有本地原始变量都是线程安全的。

    对对象的局部引用有点不同。引用本身不共享。然而,引用的对象并未存储在每个线程的本地堆栈中。所有对象都存储在共享堆中。如果本地创建的对象从不转义其创建方法,则它是线程安全的。事实上,您也可以将它传递给其他方法和对象,只要这些方法或对象都没有使传递的对象可用于其他线程。

    对象成员与对象一起存储在堆上。因此,如果两个线程在同一个对象实例上调用一个方法并且此方法更新对象成员,则该方法不是线程安全的。

    线程安全检查:如果一个资源是在同一个线程的控制下创建、使用和处置的,并且永远不会脱离该线程的控制,则该资源的使用是线程安全的。

    发件人: http://tutorials.jenkov.com/java-concurrency/thread-safety.html

    关于java - 将所有方法设为静态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37717885/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com