Испытательные шлейфы наверху или нижняя часть? (в то время как по сравнению с делают, в то время как) [закрылся]

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.

blockquote>

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.
blockquote>

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

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

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа перестает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

Трассировка, где эти значения должны быть установлены

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

Если вы попытались отладить проблему и до сих пор не имеете решения, вы можете отправить вопрос для получения дополнительной справки, но не забудьте включить то, что вы пробовали до сих пор. Как минимум, включите stacktrace в вопрос и отметьте важные номера строк в коде. Также попробуйте сначала упростить код (см. SSCCE ).

23
задан 4 revs, 3 users 100% 23 June 2010 в 12:20
поделиться

30 ответов

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

75
ответ дан Brett McCann 29 November 2019 в 00:33
поделиться

В типичном Дискретном классе Структур в информатике это - легкое доказательство, что существует эквивалентность, отображающаяся между двумя.

Стилистически, я предпочитаю, в то время как (легкий-expr) {}, когда легкий-expr известен честный и готовый пойти, и цикл не имеет большого количества повторных издержек/инициализации. Я предпочитаю, делают {} в то время как (somewhat-less-easy-expr); когда там более повторяется наверху, и условие не может быть вполне так просто настроить загодя. Если я пишу бесконечный цикл, я всегда использую в то время как (верный) {}. Я не могу объяснить, почему, но мне просто не нравится писать для (;;) {}.

0
ответ дан plinth 29 November 2019 в 00:33
поделиться

Необходимо сначала думать о тесте как о части кода цикла. Если тест логически принадлежит в начале обработки цикла, то это - тест top-of-the-loop. Если тест логически принадлежит в конце цикла (т.е. это решает, должен ли цикл продолжить работать), то это - вероятно, тест bottom-of-the-loop.

необходимо будет сделать что-то воображение, если тест логически принадлежит их середина.:-)

1
ответ дан staticsan 29 November 2019 в 00:33
поделиться

Это действительно зависит существуют ситуации, когда Вы хотите протестировать наверху, другие, когда Вы хотите протестировать в нижней части, и все еще других, когда Вы хотите протестировать в середине.

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

1
ответ дан Cervo 29 November 2019 в 00:33
поделиться

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

1
ответ дан Chris Bunch 29 November 2019 в 00:33
поделиться

Оба соглашения корректны, если Вы знаете, как записать код правильно:)

Обычно использование второго соглашения ( делают {}, в то время как () ) предназначен для предотвращения, имеют дублированный оператор вне цикла. Считайте следующее (по упрощенному) примером:

a++;
while (a < n) {
  a++;
}

может быть записан более кратко с помощью

do {
  a++;
} while (a < n)

, Конечно, этот конкретный пример может быть записан еще более кратким способом как (принятие синтаксиса C)

while (++a < n) {}

, Но я думаю, что Вы видите точку здесь.

2
ответ дан PolyThinker 29 November 2019 в 00:33
поделиться

Это на самом деле предназначено для разные вещи. В C можно использовать , делают - в то время как конструкция для достижения и сценария (работает, по крайней мере, однажды и выполнения в то время как верный). Но ПАСКАЛЬ имеет повторение - до и , в то время как для каждого сценария, и если я помню правильно, ADA имеет другую конструкцию, которая позволяет Вам выйти в середине, но конечно это не то, что Вы спрашиваете. Мой ответ на Ваш вопрос: Мне нравится мой цикл с тестированием на вершине.

2
ответ дан Salamander2007 29 November 2019 в 00:33
поделиться

Для любого, кто не может думать о причине иметь цикл одного или нескольких раз:

try {
    someOperation();
} catch (Exception e) {
    do {
        if (e instanceof ExceptionIHandleInAWierdWay) {
            HandleWierdException((ExceptionIHandleInAWierdWay)e);
        }
    } while ((e = e.getInnerException())!= null);
}

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

в классе Узел:

public Node findSelfOrParentWithText(string text) {
    Node node = this;
    do {
        if(node.containsText(text)) {
            break;
        }
    } while((node = node.getParent()) != null);
    return node;
}
2
ответ дан Chris Marasti-Georg 29 November 2019 в 00:33
поделиться

Вот перевод:

do { y; } while(x); 

То же как

{ y; } while(x) { y; }

Примечание дополнительный набор фигурных скобок для случая, у Вас есть определения переменной в y. Объем тех должен быть сохранен локальным как в случае-цикла. Так, цикл с условием продолжения просто выполняет свое тело, по крайней мере, однажды. Кроме этого, эти два цикла идентичны. Таким образом, если мы применяем это правило к Вашему коду

do {
    // do something
} while (condition is true);

, соответствующий цикл с условием продолжения для Вашего-цикла похож

{
    // do something
}
while (condition is true) {
    // do something
}

Да, Вы видите соответствие, в то время как для Вашего действительно циклично выполняются, отличается от Вашего в то время как:)

3
ответ дан Johannes Schaub - litb 29 November 2019 в 00:33
поделиться

Различие - то, что действительно циклично выполняются, выполняется, "делают что-то" однажды, и затем проверяет условие видеть, должно ли оно повториться, "делают что-то", в то время как цикл с условием продолжения проверяет условие прежде, чем сделать что-либо

20
ответ дан Andrew G. Johnson 29 November 2019 в 00:33
поделиться

Сначала нельзя выполниться вообще, если условие является ложью. Другой выполнится, по крайней мере, однажды, затем проверить conidition.

12
ответ дан Uri 29 November 2019 в 00:33
поделиться

Ради удобочитаемости кажется разумным протестировать наверху. Факт это - цикл, важен; человек, читающий код, должен знать об условиях цикла прежде, чем попытаться постигать тело цикла.

7
ответ дан mxcl 29 November 2019 в 00:33
поделиться

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

4
ответ дан Eric Sabine 29 November 2019 в 00:33
поделиться

Варианты использования отличаются для двух. Это не вопрос "о лучших практиках".

, Если Вы хотите, чтобы цикл выполнился на основе условия исключительно, чем использование для или , в то время как

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

3
ответ дан None 29 November 2019 в 00:33
поделиться

, в то время как () проверки условие перед каждым выполнением тела цикла и делают... в то время как () проверки условие после каждого выполнения тела цикла.

Таким образом, ** делают..., в то время как () ** s будет всегда выполнять тело цикла, по крайней мере, однажды.

Функционально, некоторое время () эквивалентно

startOfLoop:
    if (!condition)
        goto endOfLoop;

    //loop body goes here

    goto startOfLoop;
endOfLoop:

и..., в то время как () эквивалентно

startOfLoop:

    //loop body

    //goes here
    if (condition)
        goto startOfLoop;

Примечание, что реализация, вероятно, более эффективна, чем это. Однако..., в то время как () действительно включает тот меньше сравнения, чем некоторое время (), таким образом, это немного быстрее. Используйте... в то время как () если:

  • Вы знаете , что условие всегда будет верно в первый раз вокруг, или
  • Вы хотите цикл выполниться однажды, даже если условие является ложью для начала.
3
ответ дан Artelius 29 November 2019 в 00:33
поделиться

Цикл с условием продолжения проверит "условие" сначала; если это будет ложь, это никогда не будет "делать чего-то". Но... цикл с условием продолжения "сделает что-то" сначала, затем проверить "условие".

4
ответ дан JW. 29 November 2019 в 00:33
поделиться

Я бы сказал, что писать циклы if..do.. while - плохая практика по той простой причине, что это увеличивает размер код и вызывает дублирование кода. Дублирование кода подвержено ошибкам, и его следует избегать, так как любое изменение одной части должно выполняться и на дубликате, что не всегда так. Кроме того, чем больше размер кода, тем сложнее будет работать с кешем процессора. Наконец, он обрабатывает нулевые случаи и решает головные боли.

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

0
ответ дан 29 November 2019 в 00:33
поделиться

На самом деле это не ответ, а повторение того, что сказал один из моих лекторов, и это меня тогда заинтересовало.

Два типа цикла while..do и do. На самом деле. while являются экземплярами третьего более общего цикла, в котором тест находится где-то посередине.

begin loop
  <Code block A>
  loop condition
  <Code block B>
end loop

Кодовый блок A выполняется по крайней мере один раз, а B выполняется ноль или более раз, но не выполняется на самой последней (неудачной) итерации. цикл while - это когда кодовый блок a пуст, а do.. while - когда кодовый блок b пуст. Но если вы пишете компилятор, вам может быть интересно обобщить оба случая на такой цикл.

1
ответ дан 29 November 2019 в 00:33
поделиться

Чтобы написать правильный код, в основном нужно провести мысленное, возможно, неформальное доказательство правильности.

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

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

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

1
ответ дан 29 November 2019 в 00:33
поделиться

Я думаю, некоторые люди проводят тестирование внизу, потому что вы можете сэкономить один или несколько машинных циклов, выполнив это 30 лет назад.

1
ответ дан 29 November 2019 в 00:33
поделиться

Исходя из моих ограниченных знаний о генерации кода, я думаю, что может быть хорошей идеей написать нижние тестовые циклы, поскольку они позволяют компилятору лучше выполнять оптимизацию циклов. Для нижних тестовых циклов гарантируется, что цикл выполняется хотя бы один раз. Это означает, что инвариантный код цикла «доминирует» над выходным узлом. Таким образом, его можно безопасно перемещать непосредственно перед началом цикла.

0
ответ дан 29 November 2019 в 00:33
поделиться

Используйте циклы while, если вы хотите проверить условие перед первой итерацией цикла.

Используйте циклы do-while, если вы хотите проверить условие после выполнения первой итерации цикла.

Например, если вы обнаружите, что делаете что-то вроде любого из этих фрагментов:

func();
while (condition) {
   func();
}

//or:

while (true){
    func();
    if (!condition) break;
}

, вам следует переписать его как:

do{
    func();
} while(condition);
56
ответ дан 29 November 2019 в 00:33
поделиться

Как заметил Пиемасонс, разница заключается в том, выполняется ли цикл один раз перед выполнением теста или тест выполняется первым, так что тело цикла может никогда не выполняться.

Ключевой вопрос - что имеет смысл для вашего приложения.

Возьмем два простых примера:

  1. Допустим, вы просматриваете элементы массива. Если в массиве нет элементов, вы не хотите обрабатывать номер один, равный нулю. Поэтому вам следует использовать WHILE.

  2. Вы хотите отобразить сообщение, принять ответ, а если ответ недействителен, спрашивать еще раз, пока не получите действительный ответ. Так что всегда хочется спросить один раз. Вы не можете проверить, действителен ли ответ, пока не получите ответ, поэтому вам нужно пройти через тело цикла один раз, прежде чем вы сможете проверить условие. Вы должны использовать DO / WHILE.

2
ответ дан 29 November 2019 в 00:33
поделиться

Да... это правда... do while будет выполняться по крайней мере один раз. Это единственное различие. Больше не о чем спорить

4
ответ дан 29 November 2019 в 00:33
поделиться

Вот хороший пример из реальной жизни, с которым я недавно столкнулся. Предположим, у вас есть несколько задач обработки (например, обработка элементов в массиве), и вы хотите разделить работу между одним потоком на каждое имеющееся ядро ​​ЦП. Для выполнения текущего кода должно быть хотя бы одно ядро! Так что вы можете использовать do ...while что-то вроде:

do {
    get_tasks_for_core();
    launch_thread();
} while (cores_remaining());

Это почти ничтожно, но, возможно, стоит учесть выигрыш в производительности: его можно также записать как стандартный цикл while , но это всегда будет делать ненужное начальное сравнение который всегда будет оценивать истинно - а на одноядерном процессоре ветвление условия do-while выполняется более предсказуемо (всегда ложно, по сравнению с чередованием истинного / ложного для стандартного , а ).

6
ответ дан 29 November 2019 в 00:33
поделиться

Помогает ли отказ от сделать / , а действительно сделать мой код более читабельным?

Нет.

Если имеет смысл использовать цикл do / while , сделайте это. Если вам нужно выполнить тело цикла один раз перед проверкой условия, то цикл do / while , вероятно, является наиболее простой реализацией.

17
ответ дан 29 November 2019 в 00:33
поделиться

Я сам предпочитаю циклы do-while. Если условие всегда будет истинным в начале цикла, я предпочитаю проверить его в конце. На мой взгляд, весь смысл условий тестирования (кроме утверждений) в том, что результат теста неизвестен. Если я вижу цикл while с проверкой условия вверху, я склоняюсь к рассмотрению случая, когда цикл выполняется ноль раз. Если этого никогда не произойдет, почему бы не написать код, который ясно показывает это?

3
ответ дан 29 November 2019 в 00:33
поделиться

Да, точно так же, как использование for вместо while или foreach вместо for улучшает читаемость. Тем не менее, некоторые обстоятельства требуют выполнения while, и я согласен, что было бы глупо помещать эти ситуации в цикл while.

4
ответ дан 29 November 2019 в 00:33
поделиться

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

4
ответ дан 29 November 2019 в 00:33
поделиться

Как правило, это зависит от того, как вы структурируете свой код. И, как кто-то уже ответил, для некоторых алгоритмов требуется хотя бы одна итерация. Итак, чтобы избежать дополнительного количества итераций или флага хотя бы одного взаимодействия, вы используете do / while.

0
ответ дан 29 November 2019 в 00:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: