Почему JVM создает копию объекта при передаче его методу? - PullRequest
0 голосов
/ 05 декабря 2009

Сегодня я столкнулся со странной проблемой: у меня был метод, который использовал два объекта Date в качестве аргументов. Вызывающий метод передал ссылку на тот же объект, что и они оба (рассматриваемый метод был EqualsBuilder.append). Первый параметр прошел нормально, а второй нет. Это был новый объект Date, который отличался от первого в том смысле, что все поля, кроме года и месяца и дня, были установлены на 0. Обратите внимание, что у меня не было никакого кода, который бы копировал объект ... ошибка в JVM?

Код был довольно простым, и я заметил это только потому, что мой модульный тест не удался при сравнении того, что должно было быть тем же объектом (я инициализировал его с некоторой случайной длинной).

Редактировать :

  • Я сам в это не верил ...
  • Я не предполагал ошибки в JVM, я потратил буквально 4 часа, уставившись на этот код и отлаживая его.
  • Я посмотрел в отладчике, чтобы убедиться, что это один и тот же объект (в понедельник также будет проверено с == в вызывающем методе).
  • Я использую 1.6.0_17 в Windows XP
  • Я не могу опубликовать действительный код прямо сейчас, я сделаю это в понедельник.

Редактировать 2 :

  • После перезапуска затмения я не могу воспроизвести ошибку
  • У меня есть 7 очевидцев, которые могут засвидетельствовать, что это произошло:)
  • один из этих свидетелей сказал, что на предыдущем концерте они сталкивались с чем-то в такой степени и что они сталкивались с этой ошибкой (или странным поведением) один раз в 3 года
  • следовательно, я предполагаю, что мои шансы воспроизвести ситуацию довольно малы (я действительно хотел бы, чтобы я делал снимки экрана)

Редактировать 3 :

  • Вот код для рассматриваемого класса:

    import java.util.Date; import java.util.List;

import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder;

публичный класс Foo {

private final long roadId;
private final Date creationDate;
private final Date editDate;
private final List<String> vehicleTypes;
private final boolean continuous;

public Foo(final long roadId, final Date creationDate, final Date editDate, final List<String> vehicleTypes, final boolean continuous) {
    super();
    this.roadId = roadId;
    this.creationDate = creationDate;
    this.editDate = editDate;
    this.vehicleTypes = vehicleTypes;
    this.continuous = continuous;
}

public long getRoadId() {
    return roadId;
}

public Date getCreationDate() {
    return creationDate;
}

public Date getEditDate() {
    return editDate;
}

public List<String> getVehicleTypes() {
    return vehicleTypes;
}

public boolean isContinuous() {
    return this.continuous;
}

@Override
public int hashCode() {
    final HashCodeBuilder builder = new HashCodeBuilder();
    builder.append(this.roadId);
    builder.append(this.creationDate);
    builder.append(this.editDate);
    builder.append(this.vehicleTypes);
    builder.append(this.continuous);        
    return builder.toHashCode();
}

@Override
public boolean equals(final Object obj) {
    if (this == obj) {
        return true;
    }
    if (!(obj instanceof Foo)) {
        return false;
    }
    final Foo other = (Foo)obj;
    EqualsBuilder builder = new EqualsBuilder();
    builder.append(this.roadId, other.roadId);
    builder.append(this.creationDate, other.creationDate);
    builder.append(this.editDate, other.editDate);
    builder.append(this.vehicleTypes, other.vehicleTypes);
    builder.append(this.continuous, other.continuous);
    return builder.isEquals();
}

}

  • И модульный тест, который не прошел: import java.util.Arrays; импорт java.util.Date; import java.util.List;

import static org.junit.Assert. *; импорт org.junit.Before; import org.junit.Test;

публичный класс FooTest {

private static final boolean CONTINUOUS = true;
private static final Date CREATION_DATE = new Date(12345678901L); 
private static final Date EDIT_DATE = new Date(987654321654321L);
private static final long ROAD_ID = 101;
private static final List<String> VEHICLE_TYPES = Arrays.<String> asList("TEST");
private Foo nonEmpty;    
private Foo otherNonEmpty;

@Before
public void setUp() {
    this.nonEmpty = new Foo(FooTest.ROAD_ID, FooTest.CREATION_DATE,
            FooTest.EDIT_DATE, FooTest.VEHICLE_TYPES, true);
    this.otherNonEmpty = new Foo(FooTest.ROAD_ID, FooTest.CREATION_DATE,
            FooTest.EDIT_DATE, FooTest.VEHICLE_TYPES, FooTest.CONTINUOUS);

}

@Test
public void testEquals() {
    assertTrue(this.nonEmpty.equals(this.otherNonEmpty));
}

}

Теперь, если я изменил это:

private static final Date CREATION_DATE = new Date(12345678901L); 
private static final Date EDIT_DATE = new Date(987654321654321L);

на это:

private static final Date CREATION_DATE = new Date(109,1,11); 
private static final Date EDIT_DATE = new Date(110,3,13);

все работало нормально.

Я не думаю, что код неправильный, тем более что после перезапуска JVM все тесты пройдены. В то же время я знаю, что очень маловероятно, что в JVM есть ошибка (хотя это часть программного обеспечения, и ни одно программное обеспечение не содержит ошибок).

Прямо сейчас я проверил код с конструктором, который сначала вызвал ошибки, возможно, мне повезет встретить это снова. Спасибо за отзыв.

Ответы [ 5 ]

3 голосов
/ 05 декабря 2009

Ваше утверждение, что JVM копирует объект, когда он передается в качестве аргумента метода, является экстраординарным и (для меня) невероятным без каких-либо конкретных доказательств. Пожалуйста, предоставьте исходный код вызывающих / вызываемых методов, которые, по вашему мнению, демонстрируют такое поведение, и подробную информацию о вашей версии JVM и платформе оборудования / ОС.

«Чрезвычайные требования требуют чрезвычайных доказательств» .


После перезапуска затмения я не могу воспроизвести ошибку

Это указывает на то, что это проблема "странного затмения". Я ожидаю, что странность Eclipse означала, что вы выполняете код, который не соответствует исходному коду, который вы просматривали. Но мы никогда не узнаем наверняка ...

2 голосов
/ 05 декабря 2009

В Java объекты никогда не копируются при передаче в методы. В Java все переменные передаются по значению, а в случае объектов ссылка на объект передается по значению. никогда копирование не выполнено.

1 голос
/ 23 декабря 2009

Я собираюсь предположить, что java.sql.Date каким-то образом смешивается в этом (основываясь на утверждении, что "все поля, кроме года, месяца и дня были установлены в 0").

1 голос
/ 05 декабря 2009

Если объекты разные, они не могут быть одинаковыми в первую очередь. Я подозреваю, что вы ДУМАЕТЕ, что у вас есть две ссылки на один и тот же объект, но у вас есть два объекта. Как вы определили, что у вас был только один объект?

0 голосов
/ 05 декабря 2009

Этот код:

public void testDates() {
    Date d = new Date();

    runTest(d, d);
}

private void runTest(Date a, Date b) {
    System.out.println(a +" " +b);
}

напечатал этот результат для меня:

Fri Dec 04 22:14:28 GMT 2009 Fri Dec 04 22:14:28 GMT 2009

Это соответствует ситуации, которую вы описываете? (Очевидно, что результат не). Источник EqualsBuilder не выглядит таким уж необычным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...