有没有一种方法可以覆盖Java中的类变量?


161
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”。有没有办法让它打印“儿子”?


111
如果看到受保护的静态,请运行。
Tom Hawtin-抢险

23
@ TomHawtin-tackline原谅我的无知,但是为什么受保护的静态对象会皱眉呢?我尝试使用Google搜索,但找不到明确的答案。谢谢
陈百强

Answers:


68

是。但是就变量而言,它会被覆盖(将新值赋予变量。将新定义赋予函数是Override)。Just don't declare the variable but initialize (change) in the constructor or static block.

在父类的块中使用时,该值将得到反映

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

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

否则更改构造函数。

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


10
我认为答案应该包含一些代码以提高可读性。只要改变子实施 class Son extends Dad { static { me = 'son' } }
Cléssio门德斯

97

简而言之,没有,没有方法可以覆盖类变量。

您不会在Java中覆盖类变量,而是将其隐藏。覆盖是例如方法。隐藏与覆盖不同。

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

对于您的问题的第二部分,如何使它显示“ son”,我将通过构造函数设置该值。尽管下面的代码与您的原始问题有很大出入,但我会这样写:

public class Person {
    private String name;

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

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

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


1
可以通过在派生类中包含一个静态块来进行覆盖
siddagrl 2013年

1
@siddagrl:您能详细说明吗?
Mr_and_Mrs_D

Person在此示例中,应该替换哪个OP类?
n611x007 2014年

1
@naxa SonDad应该继承自Person,然后调用super("Son or Dad");其构造函数。
Panzercrisis

隐藏Java中的变量是好是坏?
Panzercrisis

31

是的,只需重写该printMe()方法即可:

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

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

如果您正在使用的库没有printMe()方法,或者甚至有该方法是静态方法,该怎么办?
Venkat Sudheer Reddy Aedama 2015年

1
为了使它成为更真实的示例,您可以考虑使用“ printMe”方法是一个非常复杂的函数,并具有不想重复的逻辑。在此示例中,您只想更改人员的姓名,而不是打印人员的逻辑。您应该创建一个名为“ getName”的方法,将其覆盖以返回“ son”,并且printMe方法将在其打印过程中调用getName方法。
2016年

17

您可以创建一个吸气剂,然后覆盖该吸气剂。如果您要覆盖的变量是其自身的子类,则特别有用。想象一下,您的超类有一个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"
}

11

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

     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 然后
Nancy 然后
Dad


1
OP正在考虑更改对静态变量的访问。因此,“我”是通用的“爸爸”或“儿子”-不需要为每个爸爸或儿子创建的东西,因此是静态的。
RichieHH 2014年

是的,您的权利,我没有看到它是静态的。哈哈,如果能完全回答的话,那将使我的答案减少到大约少100行代码。感谢您的抬头
nckbrz

6

这看起来像是设计缺陷。

删除static关键字,然后在构造函数中设置变量。这样,Son会将变量在其构造函数中设置为其他值。


1
这有什么不对?如果“我”的成员变量应该是在你的设计中重写,然后帕特里克是正确的解决方案
CHII

实际上,如果我的价值在每个实例中都相同,则只需删除“ static”就可以了。初始化不必在构造函数中。
内特·帕森斯

是的,尽管从技术上讲(以字节码表示),我认为这几乎是相同的;-)
Patrick Cornelissen,2009年

4

尽管确实可以将类变量仅隐藏在子类中而不被覆盖,但这确实是可以做的而无需覆盖printMe ()子类,而是反射是您的朋友。在下面的代码中,为清晰起见,我省略了异常处理。请注意,在这种情况下声明meas protected似乎没有多大意义,因为它将被隐藏在子类中...

class Dad
  {
    static String me = "dad";

    public void printMe ()
      {
        java.lang.reflect.Field field = this.getClass ().getDeclaredField ("me");
        System.out.println (field.get (null));
      }
  }

非常有趣,
Java.lang.reflect

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();
}

...将打印“儿子”。


这与原始问题不一样吗?
Corley Brigman 2013年

你可以解释为什么吗?(您的回答)
Mr_and_Mrs_D 2013年

3

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

叫做隐藏场

从上面的链接

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


1
始终欢迎潜在解决方案的链接,但是请在该链接周围添加上下文,以便您的其他用户会知道它的含义和存在的原因。如果目标站点无法访问或永久脱机,请始终引用重要链接中最相关的部分。考虑到为什么仅仅是删除一个外部站点的链接以及为什么删除某些答案的可能原因
FelixSFD

2

仅通过覆盖printMe()

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

引用meDad.printMe方法隐含指向静态字段Dad.me,所以拉上你改变什么printMe的呢Son



2

实际上,它会打印“ dad”,因为该字段没有被覆盖而是被隐藏。有三种方法可以使它打印“儿子”:

方法1:覆盖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();
}

方法2:不要隐藏字段并在构造函数中对其进行初始化

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();
}

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

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();
}

2

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

看一下以下代码片段:

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

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();
    }
}

如此...我只是重新定义了继承规则,还是让Oracle陷入了棘手的境地?对我来说,执行该程序时可以清楚地看到,受保护的静态String me被覆盖。而且,对我来说,为什么属性不应被覆盖是没有任何意义的。


1
在这种情况下,您将“隐藏”超类中的变量。这与覆盖不同,与继承无关。其他类将看到一个变量或另一个变量,这取决于它们是引用基类还是子类,并且所看到的变量在编译时是固定的。如果将子类中的名称“ me”更改为其他名称,则将获得相同的效果。当您隐藏这样的变量时,大多数IDE和代码验证工具(findbug等)都会向您发出警告,因为这通常不是您想要执行的操作。
AutomatedMike

您仅从编码的角度看编程。好的,但是在这种情况下需要代码。如果您从团队成员的角度来看编码,那么规则就会变得很明确,例如为何字段不可覆盖等等。我可以就原因向您发表演讲,但是如果您从团队成员的角度看不到它,您只会将我的观点视为无效,并向我抛出无效的论点。
nckbrz 2014年

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";
   }

}

0

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

儿子延伸爸爸延伸人类

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

    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
        }
    }
}

将打印

  • 人的
  • 人的
  • 人的

因此,没有覆盖Class变量。

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

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

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

将打印

  • 儿子

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

Son().printMe();

爸爸的Class变量“ me”将被隐藏,因为“ me”(在Son类中)的最近声明(从Son类printme()方法)。


0

只需在子类构造函数中调用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。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.