Хорошим местом для начала является JavaDocs . Они охватывают это:
Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:
- Вызов метода экземпляра нулевого объекта.
- Доступ или изменение поля нулевого объекта.
- Выполнение длины null, как если бы это был массив.
- Доступ или изменение слотов с нулевым значением, как если бы это был массив.
- Бросать нуль, как если бы это было значение Throwable.
Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.
blockquote>Также, если вы попытаетесь использовать нулевую ссылку с
synchronized
, который также выдаст это исключение, за JLS :SynchronizedStatement: synchronized ( Expression ) Block
blockquote>
- В противном случае, если значение выражения равно null,
NullPointerException
.Как это исправить?
Итак, у вас есть
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 = "";
Либо метод
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 ).
Идеальное решение для этого состоит в том, чтобы использовать log4net с консолью appender и файлом appender. Существуют многие другое appenders доступное также. Это также позволяет Вам выключать и включать другой appenders во времени выполнения.
Я не думаю, что существует что-то не так с Вашим подходом.
Если Вы хотели повторно используемый код, считайте реализацию класса названной MultiWriter
или somesuch, который берет в качестве входа два (или N?) TextWriter
потоки и распределяют все предписания, сбросы, и т.д. к тем потокам. Затем можно сделать эту вещь файла/консоли, но столь же легко можно разделить любой поток вывода. Полезный!
Вероятно, не, что Вы хотите, но на всякий случай... По-видимому, PowerShell реализует версию почтенного tee
команда. Который в значительной степени предназначается для точно этой цели. Так... курите их, если Вы получили их.
Полагайте, что рефакторинг Вашего приложения разделяет части взаимодействия с пользователем от бизнес-логики. По моему опыту, такое разделение довольно выгодно для структуры Вашей программы.
Для конкретной проблемы Вы пытаетесь решить здесь, это становится простым для части взаимодействия с пользователем для изменения ее поведения от Console.WriteLine
к файловому вводу-выводу.
Я сказал бы, что подражают диагностика, что сама.NET использует (Трассировка и Отладка).
Создайте "выходной" класс, который может иметь различные классы, которые придерживаются текстового выходного интерфейса. Вы сообщаете выходному классу, он автоматически отправляет вывод, данный классам, которые Вы добавили (ConsoleOutput, TextFileOutput, WhateverOutput).. И так далее.. Это также оставляет Вас открытыми для добавления других "выходных" типов (таких как xml/xslt для получения приятно отформатированного отчета?).
Проверьте Набор Приемников трассировки для наблюдения то, что я имею в виду.
Я работаю над реализованием подобной опции, чтобы получить вывод, отправленный в Консоль и сохранить его к журналу, все еще передавая вывод в режиме реального времени нормальной Консоли, таким образом, это не повреждает приложение (например, если это - консольное приложение!).
Если Вы все еще пытаетесь сделать это в своем собственном коде путем сохранения консольного вывода (в противоположность использованию системы регистрации для сохранения просто информации, Вы действительно заботитесь о), я думаю, что можно избежать сброса после каждой записи, пока Вы также переопределяете Сброс () и удостоверяетесь, что это сбрасывает оригинал stdoutWriter
Вы сохранили, а также Ваш fileWriter
. Вы хотите сделать это в случае, если приложение пытается сбросить частичную строку к консоли для непосредственного дисплея (такого как входная подсказка, индикатор хода выполнения, и т.д.), для переопределения буферизации нормали.
Если тот подход имеет проблемы с Вашим консольным выводом, буферизуемым слишком долго, Вы, возможно, должны были бы удостовериться, что WriteLine () сбрасывает stdoutWriter
(но вероятно не должен сбрасывать fileWriter
кроме тех случаев, когда Ваш Сброс () переопределение называют). Но я думал бы что оригинал Console.Out
(на самом деле идущий в консоль), автоматически сбросил бы ее буфер на новую строку, таким образом, Вам не придется вызвать ее.
Вы могли бы также хотеть переопределить Близко () к (сброс, и) закрывают Ваш fileWriter
(и вероятно stdoutWriter
также), но я не уверен, необходимо ли это действительно или если бы Завершение () в основном TextWriter выпустило бы Сброс () (который Вы уже переопределили бы), и Вы могли бы полагаться на выход приложения для закрытия файла. Необходимо, вероятно, протестировать это, это сбрасывается на выходе, чтобы быть уверенным. И знайте, что аварийный выход (катастрофический отказ), вероятно, не сбросит буферизованный вывод. Если это - проблема, сбрасывая fileWriter
на новой строке может быть желательным, но это - другая хитрая куча проблем для разработки.