有没有办法在 Java 中覆盖类变量?

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

226600 次浏览

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

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


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

只能通过覆盖 printMe():

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

Dad.printMe方法中对 me的引用隐式地指向静态字段 Dad.me,所以不管怎样,您正在改变 printMeSon中的作用..。

这看起来像是设计缺陷。

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

不能重写类中的变量。只能重写方法。你应该保持变量的私有性,否则你会得到很多问题。

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

在 Java 中不重写类变量,而是隐藏它们。重写是用于实例方法的。隐藏和重写是不同的。

在您给出的例子中,通过在类 Son 中声明名为“ me”的 class 变量,您隐藏了类变量,这个类变量将从它的父类继承来,同名为“ 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-外地声明部分提供了更多关于隐藏的细节

您可以创建一个 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"
}

当然,建议使用私有属性以及 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 置于了一个棘手的境地? 对我来说,受保护的静态 Stringme 显然被覆盖了,正如您在执行此程序时所看到的。而且,为什么属性不应该被覆盖,对我来说也没有任何意义。

虽然类变量可能只隐藏在子类中,而不会被重写,但是仍然可以在不重写子类中的 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));
}
}
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();
}

... 将打印“儿子”。

如果你要覆盖它,我看不到一个有效的理由保持这个静态。我建议使用抽象(参见示例代码)。:

     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 Daddy
Err 与上面的相同(只有红色字体)
JOption 输出: < br > Tommy 那么 < br > Nancy 那么 < br > Daddy

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

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

如果变量是静态的,那么在初始化过程中用静态块更改值,

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

或者在构造函数中改变。

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

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

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

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

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

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


}

Https://docs.oracle.com/javase/tutorial/java/iandi/hidevariables.html

叫做“藏身之地”

从上面的链接

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

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

儿子延伸爸爸延伸人类

在下面的代码中,我们尝试迭代 Human、 Daddy 和 Son 对象的数组,但是它在所有情况下都打印 Human Class 的值,因为调用对象的类型是 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
}
}
}

将打印

  • 人类
  • 人类
  • 人类

所以不重写 Class 变量。

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

    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 ()方法)将获得优先级。

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

请看下面的片段:

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

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