14

我知道我可以使用反射来调用私有方法,并获取或设置私有变量的值,但我想覆盖一个方法。

public class SuperClass {

    public void printInt() {
        System.out.println("I am " + getClass() + ". The int is " + getInt());
    }

    private int getInt() {
        return 1;
    }
}

public class SubClass extends SuperClass {

    public static void main(String[] args) {
        (new SubClass()).printInt();
    }

    public int getInt() {
        return 2;
    }
}

我想要 打印出来的main方法,但它打印出来。我听说这可以通过反思来完成,但我不知道怎么做。如果不是反射,有没有人知道另一种方法?(除了进行保护,或将方法复制并粘贴到.)如果实际上覆盖私有方法是不可能的,有没有办法在它上面放置某种触发器,它将在我的子类中调用一个方法之前或私有方法执行后?SubClass21SuperClass.getInt()printInt()SubClass

4

10 回答 10

20

您不能覆盖私有方法,因为没有其他类(包括派生类)可以告诉它存在。它是私人的。

私有方法是隐式的final

在相关的说明中,子类可以声明与超类中的字段或方法同名的private字段或方法,因为从子类的角度来看,这些成员不存在。这些成员之间没有特殊关系。

于 2012-05-16T17:53:48.247 回答
15

私有方法不会被继承,也不能以任何方式被覆盖。谁告诉你你可以通过反思来做到这一点,要么是在撒谎,要么是在谈论其他事情。

getInt但是,您可以访问正在调用的任何子类的私有方法,printInt如下所示:

public void printInt() throws Exception {
    Class<? extends SuperClass> clazz = getClass();
    System.out.println("I am " + clazz + ". The int is " +
                       clazz.getMethod("getInt").invoke(this) );
}

这将具有getInt从超类调用子类的方法的效果printInt。当然,如果子类没有声明 a ,现在这将失败getInt,因此您必须添加一个检查以能够处理不尝试“覆盖”私有方法的“正常”子类:

public void printInt() throws Exception {
    Class<? extends SuperClass> clazz = getClass();

    // Use superclass method by default
    Method theGetInt = SuperClass.class.getDeclaredMethod("getInt");

    // Look for a subclass method
    Class<?> classWithGetInt = clazz;
    OUTER: while( classWithGetInt != SuperClass.class ){

        for( Method method : classWithGetInt.getDeclaredMethods() )
            if( method.getName().equals("getInt") && method.getParameterTypes().length == 0 ){
                theGetInt = method;
                break OUTER;
            }

        // Check superclass if not found
        classWithGetInt = classWithGetInt.getSuperclass();
    }

    System.out.println("I am " + classWithGetInt + ". The int is " + theGetInt.invoke(this) );
}

您仍然必须更改超类代码才能使其工作,并且由于您必须更改超类代码,您应该只将访问修饰符更改为getIntprotected而不是进行反射修改。

于 2012-05-16T18:09:58.647 回答
14

不,你不能。您可以构建一个看起来应该能够做到这一点的程序,使用外部类中的代码可以访问嵌套类的私有成员这一事实。但是,私有方法实际上仍然不能被覆盖。例子:

public class Test {

    public static class Superclass {
        private void foo() {
            System.out.println("Superclass.foo");
        }
    }

    public static class Subclass extends Superclass {
        private void foo() {
            System.out.println("Subclass.foo");
            // Looks like it shouldn't work, but does...
            super.foo();
        }
    }

    public static void main(String[] args) throws Exception {
        Superclass x = new Subclass();
        // Only calls Superclass.foo...
        x.foo();
    }
}

鉴于这将是唯一可以覆盖私有方法的情况,因此不支持它并不是什么大损失。

如果你想改变你的超类的私有成员的行为,你的设计基本上就被破坏了。

于 2012-05-16T18:20:50.427 回答
5

我无法通过反射来做到这一点,但我已经找到了一种通过仪器来做到这一点的方法。正如Dhruba Bandopadhyay 的博客上所建议的那样,我使用了 ASM 。有兴趣的可以看一下我的代码(这里写的太多了)。

于 2012-05-27T17:49:52.293 回答
2

如果您真的想使用反射来做到这一点,您可以:

public class SuperClass {

    private final Method getInt;

    public SuperClass() {
        /** Find the appropriate method to call and cache it. **/
        Method getInt = null;
        try {
            getInt = getClass().getDeclaredMethod("getInt");
        }
        catch (NoSuchMethodException e) {
            try {
                getInt = SuperClass.class.getDeclaredMethod("getInt");
            } catch (NoSuchMethodException e1) {
                throw new RuntimeException(e1);
            }
        }
        getInt.setAccessible(true);
        this.getInt = getInt;
    }

    public void print() {
        int val = 0;
        try {
            val = (Integer) getInt.invoke(this);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        System.out.println(val);
    }

    private int getInt() {
        return 1;
    }
}

public class SubClass extends SuperClass {
    public int getInt() {
        return 2;
    }

    public static void main(String[] args) throws Exception {
        new SubClass().print();
    }
}

我怀疑您想以不同的方式设计程序,而不是采用这种方法。它不是高性能的,它肯定会让任何扩展 SuperClass 的人大吃一惊。

请注意,如果您以这种方式使用反射,则子类的 getInt() 方法可以是私有的。同样,至少出于上述两个原因,采用这种方法几乎绝对是一个坏主意。您有可能以另一种方式实现您的目标。包保护真的不行吗?

于 2012-05-16T18:30:39.923 回答
0

你可以通过引用传递 var -> 使用 java 的类吗?

因为如果可能的话:上面的问题可以重写为:

(请注意,这仅用于规范目的)

VB:

function(ByRef intX as Integer) //值可以显式更改,但方法本身不能:P end function

这么多,我猜...

于 2014-03-05T09:34:19.377 回答
0

您不能覆盖私有方法。

覆盖方法只能存在于被覆盖方法的子类中。如果您要覆盖的方法被标记为private,则无法覆盖它,因为子类不继承任何标记为private的东西,从而切断了该私有方法与子类中的任何东西之间的联系,包括任何子类的方法。

“覆盖”意味着允许 JVM 根据您调用它的实例类型来确定要使用的方法。多态性理论解释了是什么使这成为可能。为了让 JVM 能够说“这些方法是连接的,一个覆盖另一个”,JVM 必须能够从包含覆盖方法的子类中查看被覆盖的方法。

话虽如此,您提供的案例中的类仍然可以编译,但是这两个方法都不会基于 JVM 独立运行基于实例的调用来调用。它们只是完全不同的方法,无法被 JVM 识别为被覆盖或覆盖。

此外,如果您使用的是诸如 NetBeans 之类的 IDE,则使用 @Override 注释将指示编译器您正在覆盖超类中具有相同签名的方法。如果您不这样做,您将收到一条警告,建议您进行注释。但是,如果在以下情况下应用此注解,则会出现编译错误:

  • 超类中没有具有匹配签名的方法

或者

  • 您尝试覆盖的超类中的方法被标记为 私有最终的

有关以下摘录,请参阅 Oracle 官方文档https://docs.oracle.com/javase/tutorial/java/IandI/override.html :

实例方法

...子类覆盖方法的能力允许类从行为“足够接近”的超类继承,然后根据需要修改行为......当覆盖方法时,您可能希望使用 @覆盖注释,指示编译器您打算覆盖超类中的方法。如果由于某种原因,编译器检测到某个超类中不存在该方法,那么它将产生错误。

覆盖可能非常令人困惑,因为它涉及看似独立的 JVM 采取行动。请记住,如果无法访问超类中的方法[s](换句话说,对于所有标记为private的方法),子类就无法调用或覆盖所述方法[s],因为它无法继承它们来这样做.

于 2016-06-10T23:49:32.580 回答
-1

不,我们不能覆盖私有方法,但是在某些情况下我们也可以覆盖私有方法,即内部类

class PrivateTest { 

private String msg = "OuterAndInner";

private void fun() {
     System.out.println("Outer fun()");
}

class Inner extends PrivateTest {
    private void fun()  {
          System.out.println("Accessing Private Member of Outer: " + msg);
    }
}
public static void main(String args[])  {
     PrivateTest o = new PrivateTest();
     Inner  i   = o.new Inner();
     i.fun();
     // o.fun() calls Outer's fun (No run-time polymorphism).
     o = i; 
     o.fun();
}

}

于 2015-05-24T17:42:56.407 回答
-1

您不能覆盖私有方法,但可以在派生类中引入/重新定义一个方法。例如:

class Super
{
   private void fun()
   {
   }
}

class Child extends Super
{
    private void fun()
    {
    }
}

在这种情况下,您没有在 Child 类中覆盖这个 fun(),而是定义了一个具有相同名称的新方法,但是如果您尝试在 Child 类的 fun() 顶部使用 @override,则会出现编译时错误,因为你被迫覆盖这个 fun() 否则你只是在 Child 类中重新定义新的 fun()

于 2016-01-28T21:53:04.150 回答
-5

仔细阅读本文,

PRIVATE 方法不是继承的......因为它们不是继承的,所以它们不能被覆盖

于 2012-05-16T18:01:57.603 回答