173
class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";
}

public void doIt()
{
    new Son().printMe();
}

函数 doIt 将打印“dad”。有没有办法让它打印“儿子”?

4

17 回答 17

102

简而言之,不,没有办法覆盖类变量。

您不会覆盖 Java 中的类变量,而是隐藏它们。覆盖是实例方法。隐藏不同于覆盖。

在您给出的示例中,通过在 Son 类中声明名为“me”的类变量,您隐藏了它将从其超类 Dad 继承的具有相同名称的类变量“me”。以这种方式隐藏变量不会影响超类 Dad 中类变量“me”的值。

对于你问题的第二部分,如何让它打印“儿子”,我会通过构造函数设置值。尽管下面的代码与您的原始问题有很大的不同,但我会这样写;

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void printName() {
        System.out.println(name);
    }
}

JLS 在第8.3 节 - 字段声明中提供了更多关于隐藏的详细信息

于 2009-11-06T11:10:12.393 回答
73

是的。但就变量而言,它被覆盖(为变量赋予新值。为函数赋予新定义是覆盖)。只是不要声明变量,而是在构造函数或静态块中初始化(更改)。

在父类的块中使用时,该值将得到体现

如果变量是静态的,则在初始化期间使用静态块更改值,

class Son extends Dad {
    static { 
       me = "son"; 
    }
}

否则在构造函数中发生变化。

您也可以稍后在任何块中更改该值。它将反映在超类中

于 2015-02-01T14:33:46.997 回答
31

是的,只需覆盖该printMe()方法:

class Son extends Dad {
        public static final String me = "son";

        @Override
        public void printMe() {
                System.out.println(me);
        }
}
于 2009-03-26T11:09:54.233 回答
17

您可以创建一个 getter,然后覆盖该 getter。如果您要覆盖的变量是其自身的子类,则它特别有用。想象一下你的超类有一个Object成员,但在你的子类中,它现在被更多地定义为一个Integer.

class Dad
{
        private static final String me = "dad";

        protected String getMe() {
            return me;
        }

        public void printMe()
        {
                System.out.println(getMe());
        }
}

class Son extends Dad
{
        private static final String me = "son";

        @Override
        protected String getMe() {
            return me;
        }
}

public void doIt()
{
        new Son().printMe(); //Prints "son"
}
于 2011-07-08T17:08:02.900 回答
12

如果您要覆盖它,我看不出保持此静态的正当理由。我建议使用抽象(参见示例代码)。:

     public interface Person {
        public abstract String getName();
       //this will be different for each person, so no need to make it concrete
        public abstract void setName(String name);
    }

现在我们可以添加爸爸了:

public class Dad implements Person {

    private String name;

    public Dad(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
    return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

儿子:

public class Son implements Person {

    private String name;

    public Son(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
        return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

爸爸遇到了一位漂亮的女士:

public class StepMom implements Person {

    private String name;

    public StepMom(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
        return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

看起来我们有一个家庭,让我们告诉全世界他们的名字:

public class ConsoleGUI {

    public static void main(String[] args) {
        List<Person> family = new ArrayList<Person>();
        family.add(new Son("Tommy"));
        family.add(new StepMom("Nancy"));
        family.add(new Dad("Dad"));
        for (Person person : family) {
            //using the getName vs printName lets the caller, in this case the
            //ConsoleGUI determine versus being forced to output through the console. 
            System.out.print(person.getName() + " ");
            System.err.print(person.getName() + " ");
            JOptionPane.showMessageDialog(null, person.getName());
    }
}

}

System.out 输出:Tommy Nancy Dad
System.err 同上(只是有红色字体)
JOption 输出:
Tommy then
Nancy then
Dad

于 2014-04-14T01:34:32.997 回答
6

这看起来像一个设计缺陷。

删除 static 关键字并设置变量,例如在构造函数中。这样,Son 只是在他的构造函数中将变量设置为不同的值。

于 2009-03-26T11:17:14.993 回答
6

虽然类变量确实可能只隐藏在子类中,而不是被覆盖,但仍然可以在不覆盖printMe ()子类的情况下做你想做的事情,反射是你的朋友。在下面的代码中,为了清楚起见,我省略了异常处理。请注意,在这种情况下,声明meprotected似乎没有多大意义,因为它将隐藏在子类中......

class Dad
  {
    static String me = "dad";

    public void printMe ()
      {
        java.lang.reflect.Field field = this.getClass ().getDeclaredField ("me");
        System.out.println (field.get (null));
      }
  }
于 2013-07-15T03:11:47.333 回答
3
class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String _me = me = "son";
}

public void doIt()
{
    new Son().printMe();
}

...将打印“儿子”。

于 2013-10-18T17:53:51.100 回答
3

它确实打印了“爸爸”,因为该字段没有被覆盖而是隐藏了。有三种方法可以让它打印“儿子”:

方法一:覆盖 printMe

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";

    @override
    public void printMe()
    {
        System.out.println(me);
    }
}

public void doIt()
{
    new Son().printMe();
}

方法二:不隐藏字段,在构造函数中初始化

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    public Son()
    {
        me = "son";
    }
}

public void doIt()
{
    new Son().printMe();
}

方法三:使用静态值在构造函数中初始化一个字段

class Dad
{
    private static String meInit = "Dad";

    protected String me;

    public Dad() 
    {
       me = meInit;
    }

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    private static String meInit = "son";

    public Son()
    {
        me = meInit;
    }

}

public void doIt()
{
    new Son().printMe();
}
于 2015-04-17T03:06:47.087 回答
3

https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html

它被称为隐藏字段

从上面的链接

在一个类中,与超类中的字段同名的字段会隐藏超类的字段,即使它们的类型不同。在子类中,超类中的字段不能通过其简单名称来引用。相反,必须通过 super 访问该字段,这将在下一节中介绍。一般来说,我们不建议隐藏字段,因为它会使代码难以阅读。

于 2017-01-06T12:58:44.020 回答
3

变量不参与覆盖。只有方法可以。方法调用是在运行时解决的,也就是说,调用方法的决定是在运行时做出的,但变量仅在编译时决定。因此,该变量被调用,其引用用于调用而不是运行时对象。

看看下面的片段:

package com.demo;

class Bike {
  int max_speed = 90;
  public void disp_speed() {
    System.out.println("Inside bike");
 }
}

public class Honda_bikes extends Bike {
  int max_speed = 150;
  public void disp_speed() {
    System.out.println("Inside Honda");
}

public static void main(String[] args) {
    Honda_bikes obj1 = new Honda_bikes();
    Bike obj2 = new Honda_bikes();
    Bike obj3 = new Bike();

    obj1.disp_speed();
    obj2.disp_speed();
    obj3.disp_speed();

    System.out.println("Max_Speed = " + obj1.max_speed);
    System.out.println("Max_Speed = " + obj2.max_speed);
    System.out.println("Max_Speed = " + obj3.max_speed);
  }

}

运行代码时,控制台会显示:

Inside Honda
Inside Honda
Inside bike

Max_Speed = 150
Max_Speed = 90
Max_Speed = 90
于 2018-08-14T16:44:08.313 回答
2

仅通过覆盖printMe()

class Son extends Dad 
{
    public void printMe() 
    {
        System.out.println("son");
    }
}

me方法中的引用Dad.printMe隐式指向静态字段Dad.me,因此您正在以一种或另一种方式printMe更改Son...

于 2009-03-26T11:10:49.737 回答
2

您不能覆盖类中的变量。您只能覆盖方法。你应该保持变量私有,否则你会遇到很多问题。

于 2009-03-26T11:19:56.360 回答
1

不。类变量(也适用于实例变量)在 Java 中不表现出覆盖特性,因为类变量是根据调用对象的类型调用的。在层次结构中添加了一个类(人类)以使其更加清晰。所以现在我们有

儿子延伸爸爸延伸人类

在下面的代码中,我们尝试遍历 Human、Dad 和 Son 对象的数组,但它在所有情况下都会打印 Human 类的值,因为调用对象的类型是 Human。

    class Human
{
    static String me = "human";

    public void printMe()
    {
        System.out.println(me);
    }
}
class Dad extends Human
{
    static String me = "dad";

}

class Son extends Dad
{
    static String me = "son";
}


public class ClassVariables {
    public static void main(String[] abc)   {
        Human[] humans = new Human[3];
        humans[0] = new Human();
        humans[1] = new Dad();
        humans[2] = new Son();
        for(Human human: humans)   {
            System.out.println(human.me);        // prints human for all objects
        }
    }
}

将打印

  • 人类
  • 人类
  • 人类

所以没有覆盖类变量。

如果我们想从其父类的引用变量访问实际对象的类变量,我们需要通过将父引用(人类对象)转换为它的类型来显式地告诉编译器。

    System.out.println(((Dad)humans[1]).me);        // prints dad

    System.out.println(((Son)humans[2]).me);        // prints son

将打印

  • 爸爸
  • 儿子

关于这个问题的一部分:-正如已经建议的那样,覆盖 Son 类中的 printMe() 方法,然后调用

Son().printMe();

爸爸的类变量“me”将被隐藏,因为“me”(在 Son 类中)最近的声明(来自 Son 类 printme() 方法)将获得优先权。

于 2018-05-05T16:21:18.623 回答
1

只需在子类构造函数中调用 super.variable

public abstract class Beverage {

int cost;


int getCost() {

    return cost;

}

}`

public class Coffee extends Beverage {


int cost = 10;
Coffee(){
    super.cost = cost;
}


}`

public class Driver {

public static void main(String[] args) {

    Beverage coffee = new Coffee();

    System.out.println(coffee.getCost());

}

}

输出为 10。

于 2019-10-08T02:50:14.070 回答
0

当然使用私有属性,getter 和 setter 将是推荐的做法,但我测试了以下,它的工作原理......见代码中的评论

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";

    /* 
    Adding Method printMe() to this class, outputs son 
    even though Attribute me from class Dad can apparently not be overridden
    */

    public void printMe()
    {
        System.out.println(me);
    }
}

class Tester
{
    public static void main(String[] arg)
    {
        new Son().printMe();
    }
}

Sooo ...我只是重新定义了继承规则还是让 Oracle 陷入了棘手的境地?对我来说,protected static String me 显然被覆盖了,正如你在执行这个程序时看到的那样。此外,为什么属性不应该被覆盖对我来说没有任何意义。

于 2012-07-17T12:24:36.090 回答
0

当您可以轻松地在子类中重新分配变量时,为什么要覆盖变量。

我遵循这种模式来解决语言设计问题。假设您的框架中有一个重要的服务类,需要在多个派生应用程序中以不同的方式使用。在这种情况下,配置超类逻辑的最佳方法是重新分配其“定义”变量。

public interface ExtensibleService{
void init();
}

public class WeightyLogicService implements ExtensibleService{
    private String directoryPath="c:\hello";

    public void doLogic(){
         //never forget to call init() before invocation or build safeguards
         init();
       //some logic goes here
   }

   public void init(){}    

}

public class WeightyLogicService_myAdaptation extends WeightyLogicService {
   @Override
   public void init(){
    directoryPath="c:\my_hello";
   }

}
于 2015-06-10T14:47:00.783 回答