Оптимизация Java: объявление переменных класса VS с использованием временных переменных

Прежде всего, извините, если мой английский не идеален, но я не из англоязычной страны (Испания), так что...

Ну вот и вопрос. Creating При создании класса рекомендуется использовать все возможные временные переменные, или лучше объявить свои переменные как переменные класса, просто чтобы все было понятно?

Я приведу пример, используя простой класс SpriteSheet. Это очень короткий и популярный класс, используемый почти в каждой 2D-игре на Java.

Вот код, который изначально был запланирован создателем учебника, который я смотрел:

public class SpriteSheet {

private String path;
private final int SIZE;
public int[] spriteSheetPixels;

public SpriteSheet(String path, int size) {
this.path = path;
SIZE = size;

spriteSheetPixels = new int[SIZE * SIZE];

load();
}

private final void load() {
try {
    BufferedImage image = ImageIO.read(SpriteSheet.class
        .getResource(path));
    int w = image.getWidth();
    int h = image.getHeight();
    image.getRGB(0, 0, w, h, spriteSheetPixels, 0, w);
} catch (IOException e) {
    e.printStackTrace();
}
}

}

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

public final class SpriteSheet {

public final int[] spriteSheetPixels;

public SpriteSheet(final String path, final int width, final int height) {
spriteSheetPixels = new int[width * height];

load(path, width, height);
}

private final void load(final String path, final int width, final int height) {
try {
    BufferedImage image = ImageIO.read(SpriteSheet.class
        .getResource(path));

    final int w = image.getWidth();
    final int h = image.getHeight();
    final byte ZERO = 0;

    image.getRGB(ZERO, ZERO, w, h, spriteSheetPixels, ZERO, w);
} catch (IOException e) {
    e.printStackTrace();
}
}

}

На всякий случай, если вам не хочется уделять слишком много внимания, я постараюсь возобновить то, что я изменил и почему: - Добавил "final" в объявление класса, так как я не думаю, что мне когда-нибудь понадобится чтобы создать его экземпляр. - Удалены все переменные класса, кроме массива, так как это единственное, что я в конечном итоге буду использовать в этом классе. Я чувствую, что оставшиеся переменные, объявленные как переменные класса, это просто пустая трата памяти. Если они временные, если я не ошибаюсь, они будут использованы, и тогда GC позаботится о них рано или поздно, освобождая память. - Помечен массив как окончательный, потому что он останется таким же, как и до конца времени выполнения. - Разделите константу SIZE на ширину и высоту, на всякий случай я решу использовать некоторые не квадратные листы спрайтов. - Объявление w и h на самом деле хорошая идея, так как вызов методов в параметрах обычно плохо влияет на скорость выполнения (или это то, что я читал в некоторых местах). - Поскольку 0 используется несколько раз, я считаю, что объявление его в качестве переменной поможет улучшить скорость выполнения (чуть-чуть, вероятно, в любом случае не будет заметно).

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

Не обращайте внимания на то, что мне не очень важен класс SpriteSheet, мне интереснее качество моих оптимизаций.

Improved Улучшил ли я вещи или сделал их худшими (делая вещи на самом деле медленнее, менее читаемыми, труднее поддерживать в будущем, делая то, что компилятор будет делать в любом случае...)?

Извините, если мой вопрос слишком длинный и слишком расплывчатый, мой первый так легко для меня;)

Заранее спасибо.

РЕДАКТИРОВАТЬ:

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

Вот как я считаю это должно быть:

public final class SpriteSheet {

public final int[] spriteSheetPixels;

public SpriteSheet(final String path, final int width, final int height) {
final byte ZERO = 0;
spriteSheetPixels = new int[width * height];

try {
    BufferedImage image = ImageIO.read(SpriteSheet.class
        .getResource(path));

    image.getRGB(ZERO, ZERO, width, height, spriteSheetPixels, ZERO,
        width);
} catch (IOException e) {
    e.printStackTrace();
}
}

}

Просто понял, что мне не нужен метод на самом деле. Все можно сделать в конструкторе.

3 ответа

Решение

Предполагая, что вы имеете в виду переменные экземпляра в соответствии с вашим примером, а не класс / static переменные. (Если вы действительно имели в виду static переменные, то у вас есть множество других проблем, чтобы иметь дело с...)


Давайте начнем с аспекта "оптимизации" этого вопроса.

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

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


Но важными вопросами здесь являются удобочитаемость, ремонтопригодность и правильность.

Если вы превратите локальные "временные" переменные в переменные экземпляра, тогда появится возможность для разных методов... или разных вызовов одного и того же метода... мешающих друг другу через использование переменных экземпляра. Обратите внимание, что в некоторых случаях использования помехи неизбежны. Например, когда два разных потока вызывают один и тот же метод для одного и того же объекта одновременно или когда метод прямо или косвенно вызывает сам себя; т.е. рекурсия.

А тот факт, что такие вещи могут произойти, делает код труднее для чтения и поддержки. (Если вы действительно не знакомы с кодом и не отслеживаете все изменения, которые кто-либо вносит в него... вы не можете быть уверены, что что-то нарушило ваши предположения... и вы должны проверить.)

Напротив, у вас нет этих проблем с локальными переменными. Они гарантированно не будут видны любому другому вызову метода в текущем или другом потоке.

Короче говоря, объявление переменных как переменных экземпляра "просто для ясности" на самом деле будет иметь обратный эффект. Это сделает вещи значительно менее ясными.


Наконец, на сайте для программистов возник интересный вопрос:

Мой вывод в моем Ответе состоял в том, что это на самом деле не может рассматриваться как "анти-шаблон", потому что это не шаблон проектирования. Но тем не менее это действительно плохо.

Или лучше объявить ваши переменные как переменные класса, просто чтобы все было понятно?

Я думаю, что вы имеете в виду "атрибуты" (переменные экземпляра), а не статические (классовые) переменные. Объявление всего как переменных экземпляра сделает вещи очень неясными.

Краткий ответ: используйте локальные переменные и создавайте атрибуты только в случае крайней необходимости для данных, которые совместно используются различными методами. Кроме того, локальные переменные могут быть немного быстрее, чем атрибуты.

Creating При создании класса рекомендуется использовать все возможные временные переменные или лучше объявить свои переменные как переменные класса, просто чтобы все было понятно?

Если информация относится только к текущему методу, сохраните ее в локальной переменной с минимально возможной областью действия.

Если информация об объекте должна переживать какой-либо один вызов метода и не может быть получена из других источников, сохраните ее в поле.

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

Если вы найдете класс со многими полями, возможно, пришло время разбить его на более мелкие классы. Аналогично, если метод с множеством локальных переменных, то, возможно, пришло время разбить его на более мелкие методы.

Другие вопросы по тегам