我理解在这段代码中:
class Foo {
public static void method() {
System.out.println("in Foo");
}
}
class Bar extends Foo {
public static void method() {
System.out.println("in Bar");
}
}
.. Bar
中的静态方法隐藏'Foo
中声明的静态方法,而不是在多态意义上覆盖它。
class Test {
public static void main(String[] args) {
Foo.method();
Bar.method();
}
}
...将输出:
在Foo中 在酒吧
在method()
中将final
重新定义为Foo
将禁用Bar
隐藏它的权限,并重新运行main()
将输出:< / p>
在Foo中 在Foo
(修改:将方法标记为final
时编译失败,仅在我删除Bar.method()
时
将静态方法声明为final
,如果它有意或无意地重新定义方法会阻止子类,这被认为是不好的做法吗?
(this很好地解释了使用final
的行为是什么......)
答案 0 :(得分:36)
我不认为将static
方法标记为final
是不好的做法。
正如您所知,final
将阻止该方法被子类隐藏,这是非常好的消息imho 。
我对你的陈述感到非常惊讶:
在Foo中重新定义method()作为final将禁用Bar隐藏它的能力,并重新运行main()将输出:
在Foo中 在Foo
不,在final
中将方法标记为Foo
会阻止Bar
编译。至少在Eclipse中我得到了:
线程中的异常“main”java.lang.Error:未解决的编译问题:无法覆盖Foo的最终方法
另外,我认为人们应该总是调用static
方法,即使在类本身内也要用类名限定它们:
class Foo
{
private static final void foo()
{
System.out.println("hollywood!");
}
public Foo()
{
foo(); // both compile
Foo.foo(); // but I prefer this one
}
}
答案 1 :(得分:31)
静态方法是Java最令人困惑的功能之一。最好的做法是解决这个问题,并使所有静态方法final
成为这些最佳实践之一!
静态方法的问题在于
因此你应该
final
你应该
注意:你的程序的第二个版本应该会出现编译错误。我认为你的IDE隐藏了这个事实!
答案 2 :(得分:6)
如果我有public static
方法,那么它通常已经位于只有static
方法的所谓实用程序类中。自我解释的示例包括StringUtil
,SqlUtil
,IOUtil
等。这些实用程序类本身已经声明为final
并提供了private
构造函数。 E.g。
public final class SomeUtil {
private SomeUtil() {
// Hide c'tor.
}
public static SomeObject doSomething(SomeObject argument1) {
// ...
}
public static SomeObject doSomethingElse(SomeObject argument1) {
// ...
}
}
这样你就无法覆盖它们。
如果您的实体类不是实用类,那么我会质疑public
修饰符的值。不应该是private
吗?不过只是将它移到某个实用程序类。不要使用public static
方法混乱“普通”类。这样您也不需要标记它们final
。
另一种情况是一种抽象工厂类,它通过public static
方法返回self的具体实现。在这种情况下,标记方法final
是完全有意义的,您不希望具体实现能够覆盖该方法。
答案 3 :(得分:4)
通常使用实用程序类 - 只使用静态方法的类 - 不希望使用继承。因此,您可能希望将类定义为final以防止其他类扩展它。这将否定在实用程序类方法上放置最终修饰符。
答案 4 :(得分:3)
代码无法编译:
Test.java:8:Bar中的method()不能 在Foo中覆盖方法();覆盖 方法是静态的最终 public static void method(){
该消息具有误导性,因为根据定义,静态方法永远不会被覆盖。
我在编码时执行以下操作(不是一直100%,但这里没有任何“错误”:
(第一组“规则”是针对大多数事情完成的 - 一些特殊情况在之后被覆盖)
由于界面不能使用静态方法,因此您无法解决问题。如果要在抽象类或具体类中创建静态方法,它们必须是私有的,那么就无法尝试覆盖它们。
特殊情况:
实用程序类(包含所有静态方法的类):
如果要在非私有的具体或抽象类中使用静态方法,则可能需要改为创建实用程序类。
值类(一个非常专门用于保存数据的类,比如java.awt.Point,它几乎包含x和y值):
如果您遵循上述建议,您将获得非常灵活的代码,这些代码也具有相当清晰的职责分离。
示例值类是此Location类:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class Location
implements Comparable<Location>
{
// should really use weak references here to help out with garbage collection
private static final Map<Integer, Map<Integer, Location>> locations;
private final int row;
private final int col;
static
{
locations = new HashMap<Integer, Map<Integer, Location>>();
}
private Location(final int r,
final int c)
{
if(r < 0)
{
throw new IllegalArgumentException("r must be >= 0, was: " + r);
}
if(c < 0)
{
throw new IllegalArgumentException("c must be >= 0, was: " + c);
}
row = r;
col = c;
}
public int getRow()
{
return (row);
}
public int getCol()
{
return (col);
}
// this ensures that only one location is created for each row/col pair... could not
// do that if the constructor was not private.
public static Location fromRowCol(final int row,
final int col)
{
Location location;
Map<Integer, Location> forRow;
if(row < 0)
{
throw new IllegalArgumentException("row must be >= 0, was: " + row);
}
if(col < 0)
{
throw new IllegalArgumentException("col must be >= 0, was: " + col);
}
forRow = locations.get(row);
if(forRow == null)
{
forRow = new HashMap<Integer, Location>(col);
locations.put(row, forRow);
}
location = forRow.get(col);
if(location == null)
{
location = new Location(row, col);
forRow.put(col, location);
}
return (location);
}
private static void ensureCapacity(final List<?> list,
final int size)
{
while(list.size() <= size)
{
list.add(null);
}
}
@Override
public int hashCode()
{
// should think up a better way to do this...
return (row * col);
}
@Override
public boolean equals(final Object obj)
{
final Location other;
if(obj == null)
{
return false;
}
if(getClass() != obj.getClass())
{
return false;
}
other = (Location)obj;
if(row != other.row)
{
return false;
}
if(col != other.col)
{
return false;
}
return true;
}
@Override
public String toString()
{
return ("[" + row + ", " + col + "]");
}
public int compareTo(final Location other)
{
final int val;
if(row == other.row)
{
val = col - other.col;
}
else
{
val = row - other.row;
}
return (val);
}
}
答案 5 :(得分:1)
将静态方法标记为final可能是一件好事,特别是如果您正在开发一个您希望其他人扩展的框架。这样,您的用户不会无意中最终将静态方法隐藏在其类中。但是,如果您正在开发框架,则可能希望避免使用静态方法。
答案 6 :(得分:1)
这个final
问题的大部分可以追溯到VM-s非常愚蠢/保守的时候。那么如果你标记了一个方法final
,它意味着(除其他外),VM可以内联它,避免方法调用。从长期(或长双倍:P)时间开始,情况并非如此:http://java.sun.com/developer/technicalArticles/Networking/HotSpot/inlining.html。
我 猜测 ,Idea / Netbeans检查警告您,因为它认为您要使用final
关键字进行优化,他们认为您是不知道现代虚拟机不需要它。
只是我的两分钱......
答案 7 :(得分:0)
我遇到了使用Spring的AOP和MVC使用final方法的一个不利因素。我试图在AbstractFormController中的一个方法中使用spring的AOP放入安全钩子中,该方法被声明为final。我认为Spring在课堂上使用bcel库进行注入,并且存在一些限制。
答案 8 :(得分:0)
当我创建纯实用程序类时,我使用私有构造函数声明它们,因此无法扩展它们。在创建普通类时,我声明我的方法是静态的,如果它们没有使用任何类实例变量(或者,在某些情况下,即使它们是,我会传递方法中的参数并使其静态,它更容易看到该方法是做什么的)。这些方法被声明为静态,但也是私有的 - 它们只是为了避免代码重复或使代码更容易理解。
话虽如此,我不记得遇到过你的类有公共静态方法且可以/应该扩展的情况。但是,根据这里报道的内容,我会声明其静态方法是最终的。
答案 9 :(得分:0)
因为静态方法是类的属性,所以使用类的名称而不是对象来调用它们。如果我们将父类方法设为final也不会重载,因为最终方法不允许更改其内存位置,但我们可以在同一内存位置更新最终数据成员...