在我的应用程序中,有许多类包含处理数据的方法,这些方法可以是计算和数据丰富。
我的问题是 - 如果类没有任何类级别变量,我可以让类中的所有方法都是静态的吗?
我认为线程不会有任何问题。
有任何后果吗?是否有任何性能优势,因为我没有实例化类?
示例类:
class SMSList {
var $objLogin;
public function __construct()
{
}
public static function ProfileRegistration($objLogin)
{
$objself=new self();
$objself->objLogin=$objLogin;
$obj=new SMSMessage();
if($obj->profile_registration_sms_status==1)
{
$msg=self::Format($obj->profile_registration_sms,$objself-objLogin);
return SMS::sendSMS($objself->objLogin->mobile,$msg);
}
}
public function Format($message,$objLogin)
{
$message=str_replace('#NAME#',$objLogin->contact_person,$message);
$message=str_replace('#COMP_NAME#',$objLogin->companyname,$message );
$message=str_replace('#MOBILE#',$objLogin->mobile,$message);
$message=str_replace('#CITY#',$objLogin->city,$message);
$message=str_replace('#EMAILID#',$objLogin->emailid,$message);
return $message;
}
}
我打算将上面的内容改为静态。
答案 0 :(得分:21)
一个经验法则:问问自己"调用此方法是否有意义,即使尚未构建Obj?"如果是这样,那肯定是静态的。
因此,在类Car
中,您可能有一个静态的方法double convertMpgToKpl(double mpg)
,因为有人可能想知道35mpg转换为什么,即使没有人构建过汽车。但是void setMileage(double mpg)
(设定一个特定汽车的效率)可能是静态的,因为在构建任何汽车之前调用该方法是不可思议的。
(顺便说一下,反过来总是如此:你有时可能会有一个涉及两个Car对象的方法,但仍然希望它是静态的。例如Car theMoreEfficientOf( Car c1, Car c2 )
。虽然这可以转换为非静态版本,有些人会争辩说,由于没有特权"选择哪个汽车更重要,你不应该强迫呼叫者选择一辆汽车作为你的对象&# 39; ll调用方法。但是,这种情况只占所有静态方法的一小部分。)
虽然有一些正当理由可以使用静态方法:
性能:如果您想要运行某些代码,并且不想实例化额外的对象,请将其推送到静态方法中。 JVM还可以很好地优化静态方法(我想我曾经读过James Gosling声明你不需要在JVM中使用自定义指令,因为静态方法会同样快,但是不能。找到源 - 因此它可能是完全错误的)。是的,它是微优化,可能不需要。我们程序员从不做不需要的事情只是因为它们很酷,对吗?
实用性:不是调用新的Util()。方法(arg),而是使用静态导入调用Util.method(arg)或方法(arg)。更简单,更短。
添加方法:你真的希望类String有一个removeSpecialChars()实例方法,但它不存在(并且它不应该,因为你的project的特殊字符可能与其他项目不同,并且您无法添加它(因为Java是最低限度的),因此您创建了一个实用程序类,并调用removeSpecialChars(s)而不是s.removeSpecialChars()。甜。
纯度:采取一些预防措施,你的静态方法将是一个纯函数,也就是说,它唯一依赖的是它的参数。数据输入,数据输出。这更容易阅读和调试,因为您不必担心继承怪癖。您也可以使用实例方法来完成它,但编译器将使用静态方法帮助您(通过不允许引用实例属性,重写方法等)。
如果你想制作一个单身,你还必须创建一个静态方法,但是......不要。我的意思是,三思而后行。
现在,更重要的是,您为什么不想创建静态方法?基本上,多态性不在窗口。您将无法覆盖该方法,也无法在界面中声明该方法。它从您的设计中获得了很大的灵活性。此外,如果您需要州,如果您不小心,最终会出现大量并发错误和/或瓶颈。
因此,仅在以下方案中定义静态方法:
让我们更详细地讨论它:
<强>优点:强>
静态成员/方法用于辅助类,例如Math或常量类。这有助于其他对象利用您不需要创建对象但使用类名调用的字符串或有用函数。示例 - 使用静态函数调用单例对象。
<强>缺点:强>
静态成员是类的一部分,因此保留在内存中直到应用程序终止并且不能被垃圾收集。使用过多的静态成员有时会预测您无法设计产品并尝试使用静态/过程编程。它表示面向对象的设计受到损害。这可能导致内存溢出。如果你在Java中创建任何静态方法,也存在某些缺点,例如你不能覆盖Java中的任何静态方法,因此它使测试更难以用mock替换该方法。由于静态方法维护全局状态,因此它们可以在并发环境中创建难以检测和修复的细微错误。
要记住的事情:
静态变量将是类定义的一部分而不是堆。但是,当您知道将从多个位置访问对象时,静态变量很有用。访问静态资源不是线程安全的。您可能会在线程环境中获得奇怪/不可预测的结果。但如果您只读取静态值,那么使用线程就可以了。
静态打破封装的方式:
它们的技术实现是允许在所有类的实例中维护状态。问题是,这本质上不是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;
}
在上面:如果这是从两个线程执行,第一个线程是在 第6行,第二个线程在第2行,这仍然是线程 安全
<强>因为:强>
局部变量存储在每个线程自己的堆栈中。这意味着线程之间永远不会共享局部变量。这也意味着所有本地原始变量都是线程安全的。
对象的本地引用略有不同。引用本身不共享。但是,引用的对象不存储在每个线程的本地堆栈中。所有对象都存储在共享堆中。如果本地创建的对象永远不会转义它创建的方法,那么它是线程安全的。实际上,只要这些方法或对象都不会使传递的对象可用于其他线程,您也可以将其传递给其他方法和对象。
对象成员与对象一起存储在堆上。因此,如果两个线程在同一个对象实例上调用一个方法,并且此方法更新了对象成员,则该方法不是线程安全的。
线程安全检查:如果资源是在同一个线程的控制下创建,使用和处置的,并且永远不会逃脱该线程的控制,那么该资源的使用是线程安全的。
来自:http://tutorials.jenkov.com/java-concurrency/thread-safety.html
答案 1 :(得分:3)
如果你没有类级变量,那么是的,你可以让你班上的所有方法都是静态的。由于避免了对象实例化,它甚至可能稍微更高效。但是在面向对象的编程中,对象具有&#34; state&#34;和&#34;行为&#34;。您的对象没有状态,只有行为。所以你真的在这里做面向对象编程吗?我认为你不是(这不一定是坏事)。但也许你可能对非OO编程语言感到更舒服。
答案 2 :(得分:3)
我认为有两个问题到目前为止大多数其他答案完全被忽略了。
首先,静态是面向对象编程的异常。如果你看一下它,它就是程序编程&#34;变相。这可能意味着:如果您的代码主要由静态方法组成,这些方法以某种方式按摩某些数据......可能是:您在正确的OO建模/设计方面做得不好。因为,当您创建OO设计时,您会考虑创建抽象;并汇集数据和行为&#34;使用合理的封装;通过提供聪明的信息隐藏更为重要。
然后(在我眼中差不多):使用 static 会对代码的可测试性造成巨大负担。所以,很有可能:你要么没有进行TDD /单元测试;或者您已经使用字节码操作框架(如PowerMock)来测试使用静态方法的代码。而且这两种选择都相当......不太好。
所以,长话短说:来自shridutt的答案为你提供了一个很好的起点,可以评估什么是 static 。但我全心全意地建议你先退后一步,例如:向经验丰富的设计师/编码人员展示你的一些代码,并获得他们的反馈意见&#34; OO和可测试的东西是怎样的东西&#34;。
你的问题听起来似乎可以从思考中获得更多关于代码的好处,而不是在这里或那里添加一些&#34;静态&#34;
答案 3 :(得分:2)
没有属性的类是线程安全的。因此,您可以使用类实例或使用静态方法。两者都可以。这是您的应用程序架构的问题。如果这个简单的Java应用程序,我会在这里使用静态方法。
答案 4 :(得分:1)
你绝对可以使所有方法都是静态的。为什么不?如果语言允许,并且对你有意义,那就去做吧。我很少从做一些对我来说没有意义的事情中受益,因为我之前没有看到过这样做,或者因为它没有遵循一些设计模式(比如“OO” - 这对许多人来说意味着许多事情除了OO之外,人们和Java(尤其是Java 8)实际上支持许多范例。我绝对不会添加不必要的代码行(Obj a = new Obj(); a.do()
vs Obj.do()
)。
就现有技术而言,这类通常被称为“utility classes”。但是没有必要对这样一个类有用。就个人而言,当我看到不需要在类实例中捕获值或引用时,我使用静态方法。有很多次这种情况出现在我身上,我也在其他人的编程中看到了这一点。
答案 5 :(得分:1)
这取决于需要,如果我们想要使方法静态或不。一个好的拇指规则是如果该方法将在整个应用程序中调用(使用)以执行某些标准操作,通常可以考虑将它们设置为静态。 过度使用静态的缺点与处理和释放对象有关,因为静态方法将停留在内存中直到程序停止(重新启动),过度使用静态会对内存消耗产生一些影响。
答案 6 :(得分:1)
如果方法不修改特定实例,也就是说,如果方法没有改变对象的实例变量,那么你可以使方法变为静态。如果你将所有方法都设为静态,那么所有方法之外的变量也应该是静态的。
关于线程,据我所知,区别在于你想要同步你的代码。对于实例方法,你同步或锁定对象,但是对于静态方法,你在类变量上同步。所以在特定的在您的类中,只有一个同步块将被执行。 (例如方法,对于n个实例,可以执行n个同步块)。
表现 在静态方法的情况下,绑定是在编译时,例如绑定是动态的,即在运行时的方法。编译器也隐式地插入这个例如方法。因此静态方法比实例方法快得多。
在继承中,静态方法不能被覆盖,但是你总是可以直接用类调用方法,所以没有问题。
因此,如果您没有实例化该类,那么将这些方法设为静态并将其用作帮助类是一个好主意。