在我的应用程序中,有许多类包含处理数据的方法,这些方法可以是计算和数据丰富.
我的问题是 - 如果类没有任何类级变量,我可以使类中的所有方法都是静态的吗?
我想线程不会有任何问题.
有什么后果吗?是否有任何性能优势,因为我没有实例化类?
样本类:
Class A{
public Object findTheCar(String id){
Car car= new Car();
//do something
return car;
}
}
Run Code Online (Sandbox Code Playgroud)
我打算将上面的内容改为静态.
shr*_*ari 22
一个经验法则:问问自己"即使没有构建Obj,这个方法也有意义吗?" 如果是这样,那肯定是静态的.
所以在一个类中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中的自定义指令,因为静态方法会同样快,但找不到源 - 因此它可能是完全错误的).是的,它是微优化,可能不需要.而且我们程序员从不做不需要的东西只是因为它们很酷,对吧?
实用性:不是调用新的Util().方法(arg),而是使用静态导入调用Util.method(arg)或方法(arg).更简单,更短.
添加方法:你真的希望类String有一个removeSpecialChars()实例方法,但它不存在(它不应该,因为你的项目的特殊字符可能与其他项目不同),你不能添加它(因为Java是最低限度的),所以你创建了一个实用程序类,并调用removeSpecialChars(s)而不是s.removeSpecialChars().甜.
纯度:采取一些预防措施,你的静态方法将是一个纯函数,也就是说,它唯一依赖的是它的参数.数据输入,数据输出.这更容易阅读和调试,因为您没有担心继承怪癖.您也可以使用实例方法来完成它,但编译器将使用静态方法帮助您(通过不允许引用实例属性,重写方法等).
如果你想制作单身,你还必须创建一个静态方法,但是......不要.我的意思是,三思而后行.
现在,更重要的是,为什么你不想创建静态方法?基本上,多态性不在话下.您将无法覆盖该方法,也无法在界面中声明它.它从您的设计中获得了很大的灵活性.此外,如果你需要状态,如果你不小心,你最终会遇到很多并发错误和/或瓶颈.
因此,仅在以下方案中定义静态方法:
让我们更详细地讨论它:
好处:
静态成员/方法在辅助类中使用,例如Math或常量类.这有助于其他对象利用您不需要创建对象但使用类名调用的字符串或有用函数.示例 - 使用静态函数调用单例对象.
缺点:
静态成员是类的一部分,因此保留在内存中直到应用程序终止并且不能被垃圾收集.使用过多的静态成员有时会预测您无法设计产品并尝试使用静态/过程编程.它表示面向对象的设计受到损害.这可能导致内存溢出.如果你在Java中创建任何静态方法,也存在某些缺点,例如你不能覆盖Java中的任何静态方法,因此它使测试更难以用mock替换该方法.由于静态方法维护全局状态,因此它们可以在并发环境中创建难以检测和修复的细微错误.
要记住的事情:
静态变量将是类定义的一部分而不是堆.但是,当您知道将从多个位置访问对象时,静态变量很有用.访问静态资源不是线程安全的.您可能会在线程环境中获得奇怪/不可预测的结果.但是,如果您只读取静态值,那么使用线程就可以了.
Static Breaks封装的方式:
它们的技术实现是允许在所有类的实例中维护状态.问题是,这本质上不是OOP,因为它忽略了封装.如果某个类的任何实例都可以更改变量,则封装/信息隐藏背后的基本原理将完全丢失:对象不再完全控制其状态.它的状态现在依赖于基本上是全局的变量.我们知道的很糟糕.即使是私有静态变量也会在全局范围内维持状态,但只是限制其访问.对象的任何实例都可以更改静态变量,这会导致歧义,因为对象的各个实例不再能够控制自己的状态.状态变化可以在不知道依赖于有问题的状态的对象的情况下任意发生,因为当发生这种情况时对象可能无法正常工作.正如人们经常说的那样,"继承打破了封装",静态工作以更加严厉的方式做到这一点:不仅暴露内部实现,还暴露内部状态.
在您的示例问题中:
正如您所提到的,该方法将用于多线程环境,请考虑以下问题(修改您的代码):
public Object findTheCar(String id) {
Car car = null; //Line 2
if (id != null) {
car = new Car();
}
//Line 6
// do something
//
//Line 10
return car;
}
Run Code Online (Sandbox Code Playgroud)
在上面:如果这是从两个线程执行,第一个线程在第6行,第二个线程在第2行,那么这仍然是线程安全的.
因为:
局部变量存储在每个线程自己的堆栈中.这意味着线程之间永远不会共享局部变量.这也意味着所有本地原始变量都是线程安全的.
对象的本地引用有点不同.引用本身不共享.但是,引用的对象不存储在每个线程的本地堆栈中.所有对象都存储在共享堆中.如果本地创建的对象永远不会转义它创建的方法,那么它是线程安全的.事实上,只要这些方法或对象都不会使传递的对象可用于其他线程,您也可以将其传递给其他方法和对象.
对象成员与对象一起存储在堆上.因此,如果两个线程在同一对象实例上调用方法,并且此方法更新对象成员,则该方法不是线程安全的.
线程安全检查:如果资源是在同一线程的控制下创建,使用和处置的,并且永远不会逃避对该线程的控制,则该资源的使用是线程安全的.
来自:http://tutorials.jenkov.com/java-concurrency/thread-safety.html
| 归档时间: |
|
| 查看次数: |
4573 次 |
| 最近记录: |