为什么不能覆盖静态方法?
如果可能,请举个例子。
Parent p = new Child()
,然后编写了 p.childOverriddenStaticMethod()
,编译器也会通过查看引用类型将其解析为 Parent.childOverriddenStaticMethod()
。
覆盖取决于拥有一个类的实例。多态性的要点是您可以子类化一个类,并且实现这些子类的对象对于超类中定义的相同方法(并在子类中被覆盖)具有不同的行为。静态方法不与类的任何实例相关联,因此该概念不适用。
推动 Java 设计的有两个考虑因素影响了这一点。一个是对性能的担忧:有很多批评 Smalltalk 太慢(垃圾收集和多态调用是其中的一部分),Java 的创建者决心避免这种情况。另一个决定是 Java 的目标受众是 C++ 开发人员。使静态方法以它们的方式工作有利于 C++ 程序员熟悉并且速度也非常快,因为无需等到运行时才能确定调用哪个方法。
个人认为这是Java设计的一个缺陷。是的,是的,我知道非静态方法附加到实例,而静态方法附加到类等。不过,请考虑以下代码:
public class RegularEmployee {
private BigDecimal salary;
public void setSalary(BigDecimal salary) {
this.salary = salary;
}
public static BigDecimal getBonusMultiplier() {
return new BigDecimal(".02");
}
public BigDecimal calculateBonus() {
return salary.multiply(getBonusMultiplier());
}
/* ... presumably lots of other code ... */
}
public class SpecialEmployee extends RegularEmployee {
public static BigDecimal getBonusMultiplier() {
return new BigDecimal(".03");
}
}
此代码不会像您预期的那样工作。即,SpecialEmployee 与普通员工一样获得 2% 的奖金。但是,如果您删除“静态”,则 SpecialEmployee 将获得 3% 的奖金。
(诚然,这个例子的编码风格很差,在现实生活中,你可能希望奖金乘数在某个数据库中而不是硬编码。但这只是因为我不想让这个例子陷入困境。与该点无关的代码。)
对我来说,您可能希望将 getBonusMultiplier 设为静态似乎很合理。也许您希望能够显示所有员工类别的奖金乘数,而无需在每个类别中都有员工的实例。搜索这样的示例实例有什么意义?如果我们正在创建一个新的员工类别并且还没有任何员工分配给它怎么办?这在逻辑上是一个静态函数。
但它不起作用。
是的,是的,我可以想出很多方法来重写上面的代码以使其工作。我的观点不是它创造了一个无法解决的问题,而是它为粗心的程序员创造了一个陷阱,因为这种语言的行为并不像我认为一个理性的人所期望的那样。
也许如果我尝试为 OOP 语言编写编译器,我很快就会明白为什么实现它以便可以覆盖静态函数是困难的或不可能的。
或者也许有一些很好的理由说明 Java 的这种行为方式。任何人都可以指出这种行为的一个优势,某种类型的问题会因此变得更容易吗?我的意思是,不要只指向 Java 语言规范并说“看,这是记录它的行为方式”。我知道。但是有充分的理由为什么它应该这样做吗? (除了明显的“让它正常工作太难了”......)
更新
@VicKirk:如果你的意思是这是“糟糕的设计”,因为它不适合 Java 处理静态的方式,我的回答是,“嗯,当然。”正如我在原始帖子中所说,它不起作用。但是,如果您的意思是从某种意义上说这是一种糟糕的设计,那么这种工作的语言会存在根本性的问题,即静态可以像虚函数一样被覆盖,那么这会以某种方式引入歧义,或者不可能有效地实施或类似的,我回答,“为什么?这个概念有什么问题?”
我认为我给出的例子是一件很自然的事情。我有一个类,它的函数不依赖于任何实例数据,我可能非常合理地希望独立于实例调用它,并且希望从实例方法中调用它。为什么这不起作用?这些年来,我已经多次遇到这种情况。在实践中,我通过将函数设为虚拟来解决它,然后创建一个静态方法,其生命中的唯一目的是成为一个静态方法,将调用传递给具有虚拟实例的虚拟方法。这似乎是一种非常迂回的到达那里的方式。
someStatic()
并且 B 扩展了 A,那么 B.someMethod()
绑定 到 A 中的方法。如果我随后将 someStatic()
添加到 B,调用代码仍然调用A.someStatic()
直到我重新编译调用代码。同样令我惊讶的是,bInstance.someStatic()
使用了 bInstance 的 declared 类型,而不是运行时类型,因为它在编译时绑定而不是链接,因此 A bInstance; ... bInstance.someStatic()
如果 B.someStatic() 则调用 A.someStatic() ) 存在。
简短的回答是:完全有可能,但 Java 没有这样做。
下面是一些说明 Java 现状的代码:
文件 Base.java
:
package sp.trial;
public class Base {
static void printValue() {
System.out.println(" Called static Base method.");
}
void nonStatPrintValue() {
System.out.println(" Called non-static Base method.");
}
void nonLocalIndirectStatMethod() {
System.out.println(" Non-static calls overridden(?) static:");
System.out.print(" ");
this.printValue();
}
}
文件 Child.java
:
package sp.trial;
public class Child extends Base {
static void printValue() {
System.out.println(" Called static Child method.");
}
void nonStatPrintValue() {
System.out.println(" Called non-static Child method.");
}
void localIndirectStatMethod() {
System.out.println(" Non-static calls own static:");
System.out.print(" ");
printValue();
}
public static void main(String[] args) {
System.out.println("Object: static type Base; runtime type Child:");
Base base = new Child();
base.printValue();
base.nonStatPrintValue();
System.out.println("Object: static type Child; runtime type Child:");
Child child = new Child();
child.printValue();
child.nonStatPrintValue();
System.out.println("Class: Child static call:");
Child.printValue();
System.out.println("Class: Base static call:");
Base.printValue();
System.out.println("Object: static/runtime type Child -- call static from non-static method of Child:");
child.localIndirectStatMethod();
System.out.println("Object: static/runtime type Child -- call static from non-static method of Base:");
child.nonLocalIndirectStatMethod();
}
}
如果你运行它(我是在 Mac 上,从 Eclipse,使用 Java 1.6 运行的)你会得到:
Object: static type Base; runtime type Child.
Called static Base method.
Called non-static Child method.
Object: static type Child; runtime type Child.
Called static Child method.
Called non-static Child method.
Class: Child static call.
Called static Child method.
Class: Base static call.
Called static Base method.
Object: static/runtime type Child -- call static from non-static method of Child.
Non-static calls own static.
Called static Child method.
Object: static/runtime type Child -- call static from non-static method of Base.
Non-static calls overridden(?) static.
Called static Base method.
在这里,唯一可能令人惊讶的案例(以及问题所在)似乎是第一个案例:
“运行时类型不用于确定调用哪些静态方法,即使使用对象实例 (obj.staticMethod()
) 调用也是如此。”
最后一种情况:
“从类的对象方法中调用静态方法时,选择的静态方法是类本身可访问的方法,而不是定义对象运行时类型的类。”
使用对象实例调用
静态调用在编译时解析,而非静态方法调用在运行时解析。请注意,尽管静态方法是(从父级)继承的,但它们不会(被子级)覆盖。如果您有其他预期,这可能是一个惊喜。
从对象方法中调用
对象方法调用使用运行时类型解析,但静态(类)方法调用使用编译时(声明)类型解析。
改变规则
要更改这些规则,以便示例中的最后一个调用称为 Child.printValue()
,静态调用必须在运行时提供类型,而不是编译器在编译时使用声明的类来解析调用对象(或上下文)。然后,静态调用可以使用(动态)类型层次结构来解析调用,就像今天的对象方法调用一样。
这很容易实现(如果我们更改 Java :-O),并且一点也不不合理,但是,它有一些有趣的考虑。
主要考虑的是我们需要决定哪些静态方法调用应该这样做。
目前,Java 在语言中有这种“怪癖”,即 obj.staticMethod()
调用被 ObjectClass.staticMethod()
调用替换(通常带有警告)。 [注意: ObjectClass
是 obj
的编译时类型。] 这些将是以这种方式覆盖的良好候选者,采用 obj
的运行时类型。
如果我们这样做了,它会使方法体更难阅读:父类中的静态调用可能会被动态地“重新路由”。为了避免这种情况,我们必须使用类名调用静态方法——这使得调用更明显地通过编译时类型层次结构解决(就像现在一样)。
调用静态方法的其他方式更加棘手:this.staticMethod()
应该与 obj.staticMethod()
的含义相同,采用 this
的运行时类型。但是,这可能会给现有程序带来一些麻烦,这些程序调用(显然是本地的)静态方法而没有修饰(可以说等同于 this.method()
)。
那么朴素的调用 staticMethod()
呢?我建议他们像今天一样做,并使用本地类上下文来决定做什么。否则会产生很大的混乱。当然,这意味着如果 method
是非静态方法,则 method()
将表示 this.method()
,如果 method
是静态方法,则 ThisClass.method()
。这是另一个混乱的来源。
其他注意事项
如果我们改变这种行为(并且使静态调用可能动态地非本地调用),我们可能想要重新审视 final
、private
和 protected
作为类的 static
方法的限定符的含义。然后,我们都必须习惯 private static
和 public final
方法没有被覆盖,因此可以在编译时安全地解析,并且可以“安全”地作为本地引用读取。
其实我们错了。尽管默认情况下 Java 不允许您覆盖静态方法,但如果您仔细阅读 Java 中的 Class 和 Method 类的文档,您仍然可以通过以下解决方法找到模拟静态方法覆盖的方法:
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
class RegularEmployee {
private BigDecimal salary = BigDecimal.ONE;
public void setSalary(BigDecimal salary) {
this.salary = salary;
}
public static BigDecimal getBonusMultiplier() {
return new BigDecimal(".02");
}
public BigDecimal calculateBonus() {
return salary.multiply(this.getBonusMultiplier());
}
public BigDecimal calculateOverridenBonus() {
try {
// System.out.println(this.getClass().getDeclaredMethod(
// "getBonusMultiplier").toString());
try {
return salary.multiply((BigDecimal) this.getClass()
.getDeclaredMethod("getBonusMultiplier").invoke(this));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return null;
}
// ... presumably lots of other code ...
}
final class SpecialEmployee extends RegularEmployee {
public static BigDecimal getBonusMultiplier() {
return new BigDecimal(".03");
}
}
public class StaticTestCoolMain {
static public void main(String[] args) {
RegularEmployee Alan = new RegularEmployee();
System.out.println(Alan.calculateBonus());
System.out.println(Alan.calculateOverridenBonus());
SpecialEmployee Bob = new SpecialEmployee();
System.out.println(Bob.calculateBonus());
System.out.println(Bob.calculateOverridenBonus());
}
}
结果输出:
0.02
0.02
0.02
0.03
我们想要达到的目标:)
即使我们将第三个变量 Carl 声明为 RegularEmployee 并将其分配给 SpecialEmployee 的实例,我们仍然会在第一种情况下调用 RegularEmployee 方法,在第二种情况下调用 SpecialEmployee 方法
RegularEmployee Carl = new SpecialEmployee();
System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());
看看输出控制台:
0.02
0.03
;)
静态方法被 JVM 视为全局方法,根本没有绑定到对象实例。
如果您可以从类对象调用静态方法(例如在 Smalltalk 之类的语言中),这在概念上是可能的,但在 Java 中并非如此。
编辑
你可以重载静态方法,没关系。但是你不能重写一个静态方法,因为类没有一流的对象。您可以在运行时使用反射来获取对象的类,但您获取的对象与类层次结构不平行。
class MyClass { ... }
class MySubClass extends MyClass { ... }
MyClass obj1 = new MyClass();
MySubClass obj2 = new MySubClass();
ob2 instanceof MyClass --> true
Class clazz1 = obj1.getClass();
Class clazz2 = obj2.getClass();
clazz2 instanceof clazz1 --> false
您可以对课程进行反思,但它止步于此。您不使用 clazz1.staticMethod()
调用静态方法,而是使用 MyClass.staticMethod()
。静态方法不绑定到对象,因此在静态方法中没有 this
或 super
的概念。静态方法是全局函数;因此,也没有多态性的概念,因此,方法覆盖没有意义。
但是,如果 MyClass
是运行时调用方法的对象,就像在 Smalltalk 中那样(或者可能是一条评论建议的 JRuby,但我对 JRuby 一无所知),这可能是可能的。
哦,是的……还有一件事。您可以通过对象 obj1.staticMethod()
调用静态方法,但实际上是 MyClass.staticMethod()
的语法糖,应该避免。它通常会在现代 IDE 中引发警告。我不知道他们为什么允许这条捷径。
clazz2 instanceof clazz1
,您可以改用 class2.isAssignableFrom(clazz1)
,我相信这会在您的示例中返回 true。
dynamic dispatching 使方法覆盖成为可能,这意味着对象的声明类型并不决定其行为,而是决定其运行时类型:
Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"
尽管 lassie
和 kermit
都被声明为 Animal
类型的对象,但它们的行为(方法 .speak()
)会有所不同,因为动态调度只会在运行时将 bind 方法调用 .speak()
调用到实现 -不是在编译时。
现在,static
关键字开始变得有意义了:“静态”一词是“动态”的反义词。所以您不能覆盖静态方法的原因是因为没有静态成员的动态调度 - 因为静态字面意思是“非动态”。如果他们动态调度(因此可以被覆盖),static
关键字就不再有意义了。
是的。实际上Java允许覆盖静态方法,理论上如果你在Java中覆盖静态方法,它会编译和运行顺利,但它会失去Java的基本属性多态性。您将在各处阅读到无法尝试自己编译和运行。你会得到你的答案。例如,如果您有类 Animal 和一个静态方法 eat(),并且您在其子类中重写该静态方法,则将其称为 Dog。然后,当您将 Dog 对象分配给动物引用并根据 Java Dog 的eat() 调用eat() 时,应该调用但在静态覆盖动物的eat() 中将被调用。
class Animal {
public static void eat() {
System.out.println("Animal Eating");
}
}
class Dog extends Animal{
public static void eat() {
System.out.println("Dog Eating");
}
}
class Test {
public static void main(String args[]) {
Animal obj= new Dog();//Dog object in animal
obj.eat(); //should call dog's eat but it didn't
}
}
Output Animal Eating
根据Java的多态原理,输出应该是Dog Eating
。
但是结果不同,因为支持多态Java使用后期绑定,这意味着方法只在运行时被调用,而不是在静态方法的情况下.在静态方法中,编译器在编译时而不是运行时调用方法,所以我们根据引用而不是根据对象获取方法,引用 a 包含这就是为什么你可以说实际上它支持静态覆盖,但理论上,它不不。
在 Java(和许多 OOP 语言,但我不能代表所有人;有些根本没有静态)中,所有方法都有一个固定的签名 - 参数和类型。在虚方法中,第一个参数是隐含的:对对象本身的引用,当从对象内部调用时,编译器会自动添加 this
。
静态方法没有区别——它们仍然有一个固定的签名。但是,通过声明静态方法,您已明确声明编译器不得在该签名的开头包含隐含的对象参数。因此,调用 this 的任何其他代码都不得尝试将对对象的引用放在堆栈上。如果它确实这样做了,那么方法执行将不起作用,因为参数将位于错误的位置 - 移位了 - 在堆栈上。
由于两者之间的这种差异;虚拟方法总是引用上下文对象(即this
),因此可以引用堆内属于该对象实例的任何内容。但是对于静态方法,由于没有传递引用,因此该方法无法访问任何对象变量和方法,因为上下文是未知的。
如果您希望 Java 更改定义以便为每个方法(静态或虚拟)传递对象上下文,那么您本质上将只有虚拟方法。
正如有人在对该操作的评论中所问的那样 - 您想要此功能的原因和目的是什么?
我不太了解Ruby,正如OP提到的那样,我做了一些研究。我看到在 Ruby 中,类确实是一种特殊的对象,可以创建(甚至是动态地)新方法。类是 Ruby 中的完整类对象,它们不在 Java 中。这只是您在使用 Java(或 C#)时必须接受的。这些不是动态语言,尽管 C# 正在添加某些形式的动态语言。实际上,Ruby 没有我能找到的“静态”方法——在这种情况下,这些是单例类对象上的方法。然后,您可以使用新类覆盖此单例,并且前一个类对象中的方法将调用新类中定义的方法(对吗?)。因此,如果您在原始类的上下文中调用一个方法,它仍然只会执行原始静态,但在派生类中调用一个方法,将调用来自父类或子类的方法。有趣,我可以从中看到一些价值。它需要不同的思维模式。
由于您使用的是 Java,因此您需要适应这种做事方式。他们为什么这样做?好吧,可能是为了根据可用的技术和理解来提高当时的性能。计算机语言在不断发展。回到足够远的地方,没有像OOP这样的东西。未来,还会有其他新的想法。
编辑:另一条评论。现在我看到了差异,并且作为我自己的 Java/C# 开发人员,我可以理解为什么如果您来自像 Ruby 这样的语言,您从 Java 开发人员那里得到的答案可能会令人困惑。 Java static
方法与 Ruby class
方法不同。 Java 开发人员将很难理解这一点,相反,那些主要使用 Ruby/Smalltalk 之类的语言工作的人也一样。 Java 也使用“类方法”作为谈论静态方法的另一种方式,但 Ruby 对同一术语的使用不同,我可以看到这也会造成极大的困惑。 Java 没有 Ruby 风格的类方法(抱歉); Ruby 没有 Java 风格的静态方法,它们实际上只是旧的过程风格的函数,就像在 C 中发现的那样。
顺便说一句 - 谢谢你的问题!今天我学到了一些关于类方法(Ruby 风格)的新知识。
嗯……如果你从 Java 中被覆盖的方法应该如何表现的角度来思考,答案是否定的。但是,如果您尝试覆盖静态方法,则不会出现任何编译器错误。这意味着,如果您尝试覆盖,Java 不会阻止您这样做;但你肯定不会得到与非静态方法相同的效果。 Java 中的覆盖仅仅意味着特定方法将根据对象的运行时类型而不是编译时类型来调用(这是覆盖静态方法的情况)。好吧......任何猜测他们为什么表现奇怪的原因?因为它们是类方法,因此在编译时总是只使用编译时类型信息来解决对它们的访问。使用对象引用访问它们只是 Java 设计者给予的额外自由,我们当然不应该只在他们限制它时才考虑停止这种做法:-)
示例:让我们尝试看看如果我们尝试覆盖静态方法会发生什么:-
class SuperClass {
// ......
public static void staticMethod() {
System.out.println("SuperClass: inside staticMethod");
}
// ......
}
public class SubClass extends SuperClass {
// ......
// overriding the static method
public static void staticMethod() {
System.out.println("SubClass: inside staticMethod");
}
// ......
public static void main(String[] args) {
// ......
SuperClass superClassWithSuperCons = new SuperClass();
SuperClass superClassWithSubCons = new SubClass();
SubClass subClassWithSubCons = new SubClass();
superClassWithSuperCons.staticMethod();
superClassWithSubCons.staticMethod();
subClassWithSubCons.staticMethod();
// ...
}
}
输出:-
SuperClass: inside staticMethod
SuperClass: inside staticMethod
SubClass: inside staticMethod
注意输出的第二行。如果 staticMethod 被覆盖,这一行应该与第三行相同,因为我们将运行时类型的对象上的“staticMethod()”调用为“SubClass”而不是“SuperClass”。这证实了静态方法总是只使用它们的编译时类型信息来解析。
我对 Jay 的评论 (https://stackoverflow.com/a/2223803/1517187) 赞不绝口。
我同意这是 Java 的糟糕设计。
正如我们在之前的评论中看到的,许多其他语言都支持覆盖静态方法。我觉得 Jay 也和我一样从 Delphi 来到 Java。
Delphi(Object Pascal)是在 Java 之前实现 OOP 的语言之一,也是最早用于商业应用程序开发的语言之一。
很明显,许多人们有使用这种语言的经验,因为过去它是编写商业 GUI 产品的唯一语言。而且 - 是的,我们可以在 Delphi 中覆盖静态方法。实际上,Delphi 中的静态方法称为“类方法”,而 Delphi 有不同概念的“Delphi 静态方法”,它们是具有早期绑定的方法。要覆盖必须使用后期绑定的方法,请声明“虚拟”指令。所以它非常方便和直观,我希望在 Java 中做到这一点。
一般来说,允许“覆盖”静态方法是没有意义的,因为没有好的方法来确定在运行时调用哪个方法。以 Employee 为例,如果我们调用 RegularEmployee.getBonusMultiplier() - 应该执行哪个方法?
在 Java 的情况下,可以想象一种语言定义,只要通过对象实例调用静态方法,就可以“覆盖”它们。然而,这一切只会重新实现常规的类方法,为语言增加冗余,而不会真正增加任何好处。
为实例成员保留覆盖以支持多态行为。静态类成员不属于特定实例。相反,静态成员属于该类,因此不支持覆盖,因为子类仅继承受保护和公共实例成员,而不是静态成员。您可能希望定义接口和研究工厂和/或策略设计模式来评估替代方法。
通过覆盖,我们可以根据对象类型创建多态性。静态方法与对象无关。所以java不能支持静态方法覆盖。
通过覆盖,您可以实现动态多态性。当您说覆盖静态方法时,您尝试使用的词是矛盾的。
静态说 - 编译时,覆盖用于动态多态性。两者性质相反,因此不能一起使用。
当程序员使用对象并访问实例方法时,就会出现动态多态行为。 JRE 将根据您使用的对象类型映射不同类的不同实例方法。
当你说覆盖静态方法时,我们会通过类名来访问静态方法,类名会在编译时链接,所以没有运行时链接方法与静态方法的概念。所以术语“覆盖”静态方法本身没有任何意义。
注意:即使你使用一个对象访问一个类方法,java 编译器仍然有足够的智能找到它,并且会进行静态链接。
Box.createBox
比 BoxFactory.createBox
更有意义,并且在需要错误检查构造而不抛出异常时是一种不可避免的模式(构造函数不能失败,它们可以只杀死进程/抛出异常),而静态方法可以在失败时返回 null,甚至接受成功/错误回调以编写类似 hastebin.com/codajahati.java 的内容。
Java 中的覆盖仅仅意味着特定方法将基于对象的运行时类型而不是它的编译时类型被调用(这是覆盖静态方法的情况)。由于静态方法是类方法,它们不是实例方法,因此它们与引用指向哪个对象或实例这一事实无关,因为由于静态方法的性质,它属于特定类。您可以在子类中重新声明它,但该子类对父类的静态方法一无所知,因为正如我所说,它仅特定于声明它的那个类。使用对象引用访问它们只是 Java 设计者给予的额外自由,我们当然不应该只在他们限制更多细节和示例 http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html 时才考虑停止这种做法
重写静态方法有什么好处。您不能通过实例调用静态方法。
MyClass.static1()
MySubClass.static1() // If you overrode, you have to call it through MySubClass anyway.
编辑:似乎通过语言设计中的不幸疏忽,您可以通过实例调用静态方法。一般没有人这样做。我的错。
variable.staticMethod()
而不是 Class.staticMethod()
,其中 variable
是声明类型为 Class
的变量。我同意这是糟糕的语言设计。
这个问题的答案很简单,标记为静态的方法或变量只属于类,因此静态方法不能在子类中继承,因为它们只属于超类。
简单的解决方案:使用单例实例。它将允许覆盖和继承。
在我的系统中,我有 SingletonsRegistry 类,它返回传递的类的实例。如果未找到实例,则创建它。
Haxe语言类:
package rflib.common.utils;
import haxe.ds.ObjectMap;
class SingletonsRegistry
{
public static var instances:Map<Class<Dynamic>, Dynamic>;
static function __init__()
{
StaticsInitializer.addCallback(SingletonsRegistry, function()
{
instances = null;
});
}
public static function getInstance(cls:Class<Dynamic>, ?args:Array<Dynamic>)
{
if (instances == null) {
instances = untyped new ObjectMap<Dynamic, Dynamic>();
}
if (!instances.exists(cls))
{
if (args == null) args = [];
instances.set(cls, Type.createInstance(cls, args));
}
return instances.get(cls);
}
public static function validate(inst:Dynamic, cls:Class<Dynamic>)
{
if (instances == null) return;
var inst2 = instances[cls];
if (inst2 != null && inst != inst2) throw "Can\'t create multiple instances of " + Type.getClassName(cls) + " - it's singleton!";
}
}
Singleton.get()
。注册表只是样板开销,它排除了类上的 GC。
静态方法、变量、块或嵌套类属于整个类而不是对象。
Java中的方法用于公开对象/类的行为。在这里,由于方法是静态的(即,静态方法仅用于表示类的行为。)更改/覆盖整个类的行为将违反面向对象编程的基本支柱之一的现象,即高内聚. (请记住,构造函数是 Java 中的一种特殊方法。)
高凝聚力——一个班级应该只有一个角色。例如:一个汽车类应该只产生汽车对象而不是自行车、卡车、飞机等。但是汽车类可能有一些只属于它自己的特征(行为)。
因此,在设计java编程语言时。语言设计者认为允许开发人员仅通过使方法本质上为静态来将类的某些行为保留给自身。
下面的代码尝试覆盖静态方法,但不会遇到任何编译错误。
public class Vehicle {
static int VIN;
public static int getVehileNumber() {
return VIN;
}}
class Car extends Vehicle {
static int carNumber;
public static int getVehileNumber() {
return carNumber;
}}
这是因为,这里我们没有覆盖一个方法,而只是重新声明它。 Java 允许重新声明方法(静态/非静态)。
从 Car 类的 getVehileNumber() 方法中删除 static 关键字将导致编译错误,因为我们试图更改仅属于 Vehicle 类的静态方法的功能。
此外,如果 getVehileNumber() 被声明为 final,那么代码将无法编译,因为 final 关键字限制了程序员重新声明该方法。
public static final int getVehileNumber() {
return VIN; }
总的来说,这取决于软件设计人员在哪里使用静态方法。我个人更喜欢使用静态方法来执行一些操作,而无需创建任何类的实例。其次,向外界隐藏一个类的行为。
这是一个简单的解释。静态方法与类相关联,而实例方法与特定对象相关联。覆盖允许调用与特定对象关联的覆盖方法的不同实现。因此,重写静态方法是违反直觉的,该方法甚至与对象无关,而是首先与类本身相关联。因此,静态方法不能根据调用它的对象而被覆盖,它将始终与创建它的类相关联。
public abstract IBox createBox();
有什么反直觉的? Box 可以实现 IBox 以覆盖 createBox,并让对象的创建导致有效的 IBox,否则返回 null。构造函数不能返回“null”,因此你不得不(1)在任何地方使用异常(我们现在做的),或者(2)创建工厂类来做我之前说的,但对新手和专家都没有意义Java的(我们现在也这样做)。静态未实现的方法解决了这个问题。
现在看到上面的回答大家都知道我们不能覆盖静态方法,但是不要误解了从子类访问静态方法的概念。
如果这个静态方法没有被子类中定义的新静态方法隐藏,我们可以使用子类引用访问超类的静态方法。
例如,请参见下面的代码:-
public class StaticMethodsHiding {
public static void main(String[] args) {
SubClass.hello();
}
}
class SuperClass {
static void hello(){
System.out.println("SuperClass saying Hello");
}
}
class SubClass extends SuperClass {
// static void hello() {
// System.out.println("SubClass Hello");
// }
}
输出:-
SuperClass saying Hello
有关在子类中隐藏静态方法的详细信息,请参阅 Java oracle 文档并搜索您可以在子类中做什么。
谢谢
以下代码表明这是可能的:
class OverridenStaticMeth {
static void printValue() {
System.out.println("Overriden Meth");
}
}
public class OverrideStaticMeth extends OverridenStaticMeth {
static void printValue() {
System.out.println("Overriding Meth");
}
public static void main(String[] args) {
OverridenStaticMeth osm = new OverrideStaticMeth();
osm.printValue();
System.out.println("now, from main");
printValue();
}
}
osm
的静态声明类型是 OverridenStaticMeth
而不是 OverrideStaticMeth
。
objects
)等价物允许方法重载。obj.staticMethod()
) 调用静态方法时会发生这种情况——这是允许的并使用编译时类型。当静态调用在类的非静态方法中时,“当前”对象可能是该类的派生类型——但不考虑在派生类型上定义的静态方法(它们属于运行时类型等级制度)。