Java更改类的一个实例的属性也会更改另一个实例

时间:2018-08-01 23:25:28

标签: java inheritance

摘要问题:子类的不同实例是否继承相同的父类实例?

我本以为子类的两个实例也具有不同的父类实例,但是也许我对继承没有了解。希望有人可以解释为什么我会看到这种行为。

这是我看到“问题”的班级:

@Entity
@Table(name="inventory.parts_fstnr_capscrews")
public class FastenerCapScrew implements PartInterface {
    ...
    private Dimension length;
    private Dimension threadLength;
    ...
    @ManyToOne
    @JoinColumn(name="fk_lengthid")
    @JsonView(View.CommodityPartPOView.class)
    public Dimension getLength() {
        return length;
    }

    public void setLength(Dimension length) {
        this.length = length;
    }

    @ManyToOne
    @JoinColumn(name="fk_threadlengthid")
    @JsonView(View.CommodityPartPOView.class)
    public Dimension getThreadLength() {
        return threadLength;
    }

    public void setThreadLength(Dimension threadLength) {
        this.threadLength = threadLength;
    }

    @Override
    @Transient
    public List<FiltersInterface> getFilters() {
        List<FiltersInterface> filters = new ArrayList<>();
        LOGGER.debug(filters.toString());
        LOGGER.debug(length.toString());
        LOGGER.debug(threadLength.toString());
        if (length!=null) {
            length.setDbColumnName("FK_LengthID");
            filters.add(length);
        }
        LOGGER.debug(filters.toString());
        LOGGER.debug(length.toString());
        LOGGER.debug(threadLength.toString());
        if (threadLength!=null) {
            threadLength.setDbColumnName("FK_ThreadLengthID");
            filters.add(threadLength);
        }
        LOGGER.debug(filters.toString());
        LOGGER.debug(length.toString());
        LOGGER.debug(threadLength.toString());
        return filters;
    }
}

这是Dimension类:

@Entity
@Table(name="utilities.dimensions")
public class Dimension extends FiltersExtension implements FiltersDimensionInterface {
    ...
}

扩展类:

public class FiltersExtension {
    protected String dbColumnName;

    public String getDbColumnName() {
        return dbColumnName;
    }

    public void setDbColumnName(String dbColumnName) {
        this.dbColumnName = dbColumnName;
    }
}

当我在getFilters()中调用FastenersCapScrew方法时,lengththreadLength的初始输出是预期的,并且都具有dbColumnName=null。然后它运行length.setDbColumnName("FK_LengthID");,但是两者 lengththreadLength都被更改,并且都显示dbColumnName=FK_LengthID。然后运行threadLength.setDbColumnName("FK_ThreadLengthID");,并再次更改两个项目,以使dbColumnName=FK_ThreadLengthID

最初,我认为它必须与Dimension中的hashCode和equals方法有关,因此我将它们更改为包括dbColumnName,如下所示:

@Override
public int hashCode() {
    LOGGER.debug("First compare hashCode with dbColumnName="+this.dbColumnName);
    int hash = 3;
    hash = 37 * hash + this.dimID;
    hash = 37 * hash + Objects.hashCode(this.dbColumnName);
    return hash;
}

@Override
public boolean equals(Object obj) {
    LOGGER.debug("Now compare equals with dbColumnName="+this.dbColumnName);
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final Dimension other = (Dimension) obj;
    if (this.dimID != other.dimID) {
        return false;
    }
    LOGGER.debug("Now compare the column name: "+this.dbColumnName+" vs. "+other.dbColumnName);
    if (!Objects.equals(this.dbColumnName,other.dbColumnName)) {
        return false;
    }
    return true;
}

有人可以向我解释为什么更改一个Dimension实例也会同时更改另一个实例吗?为了解决这个问题,使我确实拥有两个完全独立的实例,该怎么办?谢谢!

对于它的价值,我将Java 8和Spring Boot 2.0.3与Hibernate一起使用,但是我认为这与该问题没有任何关系。

1 个答案:

答案 0 :(得分:2)

肯定有两个子类实例不共享其父字段的内存。也许导致这种现象的原因仅仅是Hibernate的缓存。 Hibernate确实为Dimension类的一个字段创建了FastenerCapScrew的新实例,而不是从缓存中加载它。尝试启用SQL查询日志记录,以调查调用getFilters方法时发生的情况。

编辑 获取基本相同实体的不同实例的最简单方法是在setter中使用防御性复制。只要您不将此技术应用于集合,Hibernate仍应能够执行脏检查,因为它按值比较对象。相比之下,收集是通过身份进行比较的,脏检查对它们不起作用。