What are some scenarios in which java’s System.out.println would fail to produce any output. I have a call to it inside of a method and sometimes when the method is called I get the println and othertimes I don’t.
Update: I am also using System.out.flush() after the println.
Update: Thank you for the debugging help. It turned out a blocking call to open a dialog made output appear vastly out of the proper order. I thought the method I was trying to print messages for was being called when the dialog closed but the method itself was what was calling the dialog and so after the closing it was already past the printouts which was where i started looking for the test. If someone has the ability to delete this question as the issue was not what was originally asked it’d be appreciated.
asked Jul 21, 2010 at 17:27
lathomas64lathomas64
1,5745 gold badges20 silver badges47 bronze badges
6
System.out.println
on some platforms uses buffered output. Depending on what your code is doing, it is possible that the buffers are not flushed before your program exits. Try putting System.out.flush()
after your println
s and see if that helps.
Edit:
sometimes when the method is called I get the println and othertimes I don’t
How are you verifying that the method is called but the println produces no output? Is it possible your method is throwing an Exception (which is then swallowed) before it gets to the println?
It would, of course, be very helpful to see some actual code.
answered Jul 21, 2010 at 17:30
Jason DayJason Day
8,7691 gold badge40 silver badges46 bronze badges
2
I have never seen this scenario before. In theory, it would only «fail» when the output isn’t there where you expect it is. The output target can namely be changed using System#setOut()
.
answered Jul 21, 2010 at 17:29
BalusCBalusC
1.1m370 gold badges3584 silver badges3536 bronze badges
2
Where are you checking for your output? It’s possible that System.out
has been redirected elsewhere, so maybe you’re looking in the wrong place.
answered Jul 21, 2010 at 17:31
casablancacasablanca
69k7 gold badges133 silver badges149 bronze badges
answer as per @BalusC’s suggestion—
Thank you for the debugging help. It turned out a blocking call to open a dialog made output appear vastly out of the proper order. I thought the method I was trying to print messages for was being called when the dialog closed but the method itself was what was calling the dialog and so after the closing it was already passed the printouts which were where I started looking for the test. If someone has the ability to delete this question as the issue was not what was originally asked it’d be appreciated.
answered Jul 22, 2010 at 15:39
lathomas64lathomas64
1,5745 gold badges20 silver badges47 bronze badges
System.out.println is buffered output, if you do not flush the buffer, it may seem to ‘wait’ until the end of program. Sometimes, the program can die before flushing its buffers. System.out.flush() will force output to be flushed.
answered Jul 21, 2010 at 17:31
Elf KingElf King
1,1795 silver badges7 bronze badges
It’s possible that the file handle has been changed. I.e., stdout
‘s file descriptor is no longer 1
. I’ve seen this done in logging utilities where people don’t want to go and catch any text that might be printed to a file descriptor, so instead they just redirect the stream to a file handle of an open file.
Here’s an example in python:
import sys
h = open(r"C:foo.txt","a+")
sys.stdout = h
sys.stdout.write("hey fellas")
h.close()
Run this at the cmdline, and you’ll not get «hey fellas» printed out as you expect. Instead, it will be redirected to the file C:foo.txt
answered Jul 21, 2010 at 17:38
ChrisChris
1,3913 gold badges17 silver badges31 bronze badges
0 / 0 / 1 Регистрация: 03.06.2015 Сообщений: 37 |
|
1 |
|
12.10.2017, 17:11. Показов 8647. Ответов 3
Я хочу чтобы System.out.println вывело сообщение Hello, world. Лично я знаю что можно вывести и так System.out.println(«Hello, world»); но мне надо иначе. Миниатюры
__________________
0 |
1227 / 844 / 260 Регистрация: 02.04.2009 Сообщений: 3,175 |
|
12.10.2017, 17:50 |
2 |
РешениеИван-1, Добавлено через 2 минуты
1 |
Иван-1 0 / 0 / 1 Регистрация: 03.06.2015 Сообщений: 37 |
||||||||
12.10.2017, 23:32 [ТС] |
3 |
|||||||
Kukstyler,
Вроде ошибок нет но в консоль Hello, world не выводит. А если так:
То без проблем выводит.
0 |
Kukstyler 1227 / 844 / 260 Регистрация: 02.04.2009 Сообщений: 3,175 |
||||
12.10.2017, 23:43 |
4 |
|||
РешениеИван-1, потому, что Вы объявляете класс, но не инстанциируите его. Таким образом, естественно, конструктор не запускается, и сообщение не выводится. Кстати, почитайте про разные типы классов: http://www.quizful.net/post/inner-classes-java Добавлено через 1 минуту
1 |
You have lots going wrong in your code here. If you’re doing anything other than assignments — both variable declarations or methods — the code must be contained in a method for usability.
All of your System.out.println()’s and your loop must be in a method, such as your main()
method. An example:
public static void main(String[] args) {
System.out.println("Welcome to your music libary");
System.out.println("");
System.out.println("1. Add a new song");
System.out.println("2. Edit a song");
System.out.println("3.Remove a song");
System.out.println("4. shuffle your songs")
System.out.println("5. Exit");
}
A do-while loop
always contains a while()
condition.
do {
//do something that's looped
} while(option != 5);
A switch-statement
always has its cases inside its body:
switch (option) {
case 1:
//do something
}
Here you are missing your terminating semi-colon:
System.out.println("4. shuffle your songs")
That should fix the majority of your problems. I would like to suggest following a tutorial on programming with Java.
You have lots going wrong in your code here. If you’re doing anything other than assignments — both variable declarations or methods — the code must be contained in a method for usability.
All of your System.out.println()’s and your loop must be in a method, such as your main()
method. An example:
public static void main(String[] args) {
System.out.println("Welcome to your music libary");
System.out.println("");
System.out.println("1. Add a new song");
System.out.println("2. Edit a song");
System.out.println("3.Remove a song");
System.out.println("4. shuffle your songs")
System.out.println("5. Exit");
}
A do-while loop
always contains a while()
condition.
do {
//do something that's looped
} while(option != 5);
A switch-statement
always has its cases inside its body:
switch (option) {
case 1:
//do something
}
Here you are missing your terminating semi-colon:
System.out.println("4. shuffle your songs")
That should fix the majority of your problems. I would like to suggest following a tutorial on programming with Java.
Каковы сценарии, в которых System.out.println Java не сможет произвести какой-либо вывод? У меня есть вызов внутри метода, и иногда, когда вызывается метод, я получаю println, а иногда — нет.
Обновление: я также использую System.out.flush () после println.
Обновление: Спасибо за помощь в отладке. Оказалось, что блокирующий вызов для открытия диалогового окна приводит к тому, что вывод выглядит не в правильном порядке. Я думал, что метод, для которого я пытался распечатать сообщения, был вызван при закрытии диалогового окна, но сам метод был тем, что вызывал диалоговое окно, и поэтому после закрытия он уже прошел распечатки, с которых я начал искать тест. Если у кого-то есть возможность удалить этот вопрос, так как проблема заключалась не в том, что изначально было задано, мы будем признательны.
6 ответы
System.out.println
на некоторых платформах используется буферизованный вывод. В зависимости от того, что делает ваш код, возможно, что буферы не очищаются до выхода из вашей программы. Попробуйте поставить System.out.flush()
после вашего println
s и посмотрите, поможет ли это.
Редактировать:
иногда при вызове метода я получаю println, а иногда — нет.
Как вы проверяете, что метод вызван, но println не выводит результат? Возможно ли, что ваш метод выдает исключение (которое затем проглатывается) до того, как оно попадает в println?
Конечно, было бы очень полезно увидеть реальный код.
Создан 21 июля ’10, 18:07
Я никогда раньше не видел такого сценария. Теоретически он «потерпит неудачу» только тогда, когда результат будет не там, где вы ожидаете. Целевое значение вывода может быть изменено с помощью System#setOut()
.
Создан 16 июн.
Где вы проверяете свой результат? Возможно, что System.out
был перенаправлен в другое место, поэтому, возможно, вы ищете не в том месте.
Создан 21 июля ’10, 18:07
ответ в соответствии с предложением @BalusC —
Спасибо за помощь в отладке. Оказалось, что блокирующий вызов для открытия диалогового окна приводит к тому, что вывод выглядит не в правильном порядке. Я думал, что метод, для которого я пытался распечатать сообщения, вызывается при закрытии диалогового окна, но сам метод был тем, что вызывал диалоговое окно, и поэтому после закрытия ему уже были переданы распечатки, с которых я начал искать тест. Если у кого-то есть возможность удалить этот вопрос, поскольку проблема заключалась не в том, что изначально было задано, он будет признателен.
Создан 06 июн.
System.out.println — это буферизованный вывод, если вы не очистите буфер, может показаться, что он «ждет» до конца программы. Иногда программа может умереть до очистки своих буферов. System.out.flush () принудительно сбрасывает вывод.
Создан 21 июля ’10, 18:07
Возможно, был изменен дескриптор файла. Т.е., stdout
дескриптор файла больше не 1
. Я видел это в утилитах регистрации, где люди не хотят идти и перехватывать любой текст, который может быть напечатан в дескрипторе файла, поэтому вместо этого они просто перенаправляют поток на дескриптор файла открытого файла.
Вот пример на Python:
import sys
h = open(r"C:foo.txt","a+")
sys.stdout = h
sys.stdout.write("hey fellas")
h.close()
Запустите это в командной строке, и вы не получите распечатку «привет, ребята», как вы ожидали. Вместо этого он будет перенаправлен в файл C: foo.txt.
Создан 21 июля ’10, 18:07
Не тот ответ, который вы ищете? Просмотрите другие вопросы с метками
java
blocking
println
or задайте свой вопрос.
Recovery mode
Из песочницы
С какими трудностями встречается начинающий разработчик java?
Хочу представить вашему вниманию небольшую статью. Статья предназначена для начинающих. Но даже если вы опытный разработчик, не делайте поспешных выводов.
Надеюсь данная публикация будет полезна не только начинающим.
Цель данной публикации:
Показать наиболее часто встречающиеся ошибки начинающих и некоторые приемы их исправления. Понятно, что некоторые ошибки могут быть сложными и происходить по тем или иным причинам. Цель публикации в некоторой степени проанализировать их и помочь выявить на раннем этапе. Надеюсь данная публикация будет полезна начинающим.
Контрольный список ошибок:
- Опечатки. Досадные опечатки, которые не сразу обнаруживаются
- Присвоение в условии вместо сравнения
- Логические ошибки в условии
- Неправильное сравнение строк
- Неправильная инициализация переменных примитивных типов
- Неправильное использование double
- Неправильный тип возвращающего значения в конструкторе.
- Деление на ноль. POSITIVE_INFINITY
- Не учет порядка инициализации класса
- Локальная переменная скрывает переменную класса
- Игнорирование автоматически приведение типов в арифметических выражениях
- Бесконечный цикл с byte, который трудно обнаружить.
- Имя класса отличается от имени файла, в котором он хранится.
- Не проинициализированы объекты, являющиеся элементами массива.
- Помещение в один файл сразу нескольких классов с модификатором public
Подводные камни Java
Все языки программирования имеют свои достоинства и недостатки. Это обусловлено многими причинами. Язык Java не исключение. Я попытался собрать некоторые очевидные и не очевидные трудности, с которыми сталкивается начинающий программист Java. Уверен, что опытные программисты также найдут в моей статье что-то полезное. Практика, внимательность и полученный опыт программирования, помогут избавить вас от многих ошибок. Но некоторые ошибки и трудности лучше рассмотреть заранее. Я приведу несколько примеров с кодом и объяснениями. Многие объяснения вам станут понятны из комментариев к коду. Практика дает очень многое, так как некоторые правила не столь очевидны. Некоторые лежат на поверхности, некоторые скрыты в библиотеках языка или в виртуальной машине java. Помните, что java это не только язык программирования с набором библиотек, это еще и виртуальная машина java.
Для статьи я специально написал работающий код с подробными комментариями. Для написания статьи с примерами кода использовалась java 8. Для тестирования код java помещен в отдельные пакеты.
Пример: «package underwaterRocks.simple;»
С какими трудностями сталкиваются начинающие?
Опечатки
Бывает так, что начинающие программисты делают опечатки, которые трудно обнаружить с первого взгляда.
Пример кода:
Файл: «Simple.java»
/*
учебные пример
; после условия и блок
*/
package underwaterRocks.simple;
/**
*
* @author Ar20L80
*/
public class Simple {
public static void main(String[] args) {
int ival = 10;
if(ival>0);
{
System.out.println("Этот блок не зависит от условия");
}
}
}
Объяснение: «Точка с запятой означает конец оператора. В данном случае ‘;’ — это конец пустого оператора. Это логическая ошибка. Такую ошибку бывает трудно обнаружить.
Компилятор сочтет, что всё правильно. Условие if(ival>0); в данном случае не имеет смысла. Потому как означает: если ival больше ноль, ничего не делать и продолжить. Очевидное решение — убрать точку с запятой.»
Присвоение в условии вместо сравнения
В условии присвоение переменной.
Это не ошибка, но использование такого приема должно быть оправдано.
В большинстве случаев, вы вряд ли будете использовать оператор присвоения, вместо оператора сравнения.
boolean myBool = false;
if(myBool = true) System.out.println(myBool);
В данном коде if(myBool = true) означает: «Присвоить переменной myBool значение true,
если выражение истинно, выполнить условие следующее за скобками.»
В данном коде условие будет всегда истинно. И System.out.println(myBool); будет выполнено всегда, независимо от условия.
== — это сравнение на равенство.
= — это присвоение, вы можете проговорить a = 10; как: «а присвоить значение 10».
Условие в скобках возвращает логическое значение.
Не важно в каком порядке вы запишите. Вы можете сравнить так: (0 == a) или (5 == a)
Если вы забудете один знак равенства, например так (0 = a) или (5 = a), то компилятор сообщит вам об ошибке. Вы присваиваете значение, а не сравниваете.
Вы также можете записать в удобочитаемой форме какой-то интервал.
Например: вам нужно написать: a больше 5 и меньше 10.
Вы пишите так: (a>4 && a<10), но с таким же успехом вы можете написать: (4<a && a<10),
теперь вы видите, что a находится в интервале между 4 и 10, исключая эти значения. Это более наглядно. Сразу видно, что, значение переменной а находится в интервале между 4 и 10 ( ]4,10[ ), исключая эти граничные значения.
Пример в коде(интервал ]3,9[ ):
if(3<a&&a<9) выполнить;
Логическая ошибка
if(условие){} if(условие){} else{} — else относится к ближайшему if.
Часто это бывает причиной ошибок начинающих.
Неправильное сравнение строк
Начинающие довольно часто используют == вместо .equals для сравнения строк.
Инициализация переменных
Рассмотрим инициализацию переменных примитивного типа.
Примитивы (byte, short, int, long, char, float, double, boolean).
Начальные значения.
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char 'u0000'
String (or any object) null
boolean false (зависит от jvm)
Примечание:
Локальные переменные немного отличаются;
Компилятор никогда не присваивает значение по умолчанию неинициализированной локальной переменной.
Если вы не можете инициализировать свою локальную переменную там, где она объявлена,
не забудьте присвоить ей значение, прежде чем будете её использовать.
Доступ к неинициализированной локальной переменной приведет к ошибке времени компиляции.
Подтверждение этого примечания в коде:
Файл: «MyInitLocal.java»
/*
учебные пример
инициализация переменных класса и локальных переменных
*/
package underwaterRocks.myInit;
/**
*
* @author Ar20L80
*/
public class MyInitLocal {
float classes_f;
int classes_gi;
public static void main(String[] args) {
float f;
int i;
MyInitLocal myInit = new MyInitLocal();
/* в этом месте переменные уже инициализированы параметрами по умолчанию.*/
System.out.println("myInit.classes_f = " + myInit.classes_f);
System.out.println("myInit.classes_gi = " + myInit.classes_gi);
// System.out.println("f = " + f); // ошибка. Локальная переменная не инициализирована
// System.out.println("f = " + i); // ошибка. Локальная переменная не инициализирована
}
}
Диапазоны значений:
byte (целые числа, 1 байт, [-128, 127])
short (целые числа, 2 байта, [-32768, 32767])
int (целые числа, 4 байта, [-2147483648, 2147483647])
long (целые числа, 8 байт, [-922372036854775808,922372036854775807])
float (вещественные числа, 4 байта)
double (вещественные числа, 8 байт)
char (символ Unicode, 2 байта, 16 бит, [0, 65535])
boolean (значение истина/ложь, используется int, зависит от JVM)
char: The char data type is a single 16-bit Unicode character. It has a minimum value of ‘u0000’ (or 0) and a maximum value of ‘uffff’ (or 65,535 inclusive).
Документация Oracle >>
Попытаемся инициализировать переменную типа long числом: 922372036854775807.
У нас ничего не выйдет. Потому как, это целочисленный литерал типа int.
Правильная инициализация литералом типа long: 922372036854775807L;
Пример кода:
Файл: «MyInitLocalLong.java»
/*
учебные пример
Инициализация long локально
*/
package underwaterRocks.myInit;
/**
*
* @author Ar20L80
*/
public class MyInitLocalLong {
public static void main(String[] args) {
// long al = 922372036854775807; //ошибка integer number too large
long bl = 922372036854775807L; // так правильно
}
}
На что следует обращать внимание при инициализации переменной.
На диапазон значений переменной данного типа. На то, что переменная инициализируется литералом определенного типа. На явное и неявное приведение типов. На совместимость типов.
При использовании оболочек типа Integer, следует обратить внимание на авто упаковку и авто распаковку данных типов.
Неправильное использование double
Здесь нужно пояснить. Речь идет не о неправильном использовании типа double.
Используем мы правильно. Только результат может удивить начинающего программиста.
Файл: «MinusDouble.java»
/*
учебный пример
*/
package underwaterRocks.tstDouble;
/**
*
* @author vvm
*/
public class MinusDouble {
public static void main(String[] args) {
double a = 4.64;
double b = 2.64;
System.out.println("a-b = "+(a-b));
}
}
/*
Вывод программы
run:
a-b = 1.9999999999999996
*/
Примечание о типе double. Плавающая точка позволяет считать с заданной относительной погрешностью и огромным диапазоном. В научных расчетах часто нужна относительная погрешность.
Неправильное сравнение double
Рассмотрим тип double.
Пример кода:
Файл: «MyDouble.java»
/*
учебные пример
Сравнение double
Осторожно - double.
*/
package underwaterRocks.myDouble;
/**
*
* @author Ar20L80
*/
public class MyDouble {
public static void main(String[] args) {
double dx = 1.4 - 0.1 - 0.1 - 0.1 - 0.1;
System.out.println("dx = " + dx); // dx = 0.9999999999999997
System.out.print("Сравнение (dx == 1.0):");
System.out.println(dx == 1.0); // false, потому что 1.0 не равно 0.9999999999999997
/*как правильно сравнивать double*/
final double EPSILON = 1E-14;
double xx = 1.4 - 0.1 - 0.1 - 0.1 - 0.1;
double xy = 1.0;
/* сравниваем xx c xy */
if (Math.abs(xx - xy) < EPSILON)
System.out.println(xx + " это примерно равно " + xy + " EPSILON = " + EPSILON);
}
}
Тип double удобен там, где не нужна высокая точность. Для финансовых операций этот тип не годится. Хотя некоторые, не очень честные компании, использую тип double, для округления в нужную им сторону. Для финансовых операций в финансовых расчётах используется класс BigDecimal, так как вещественные примитивные типы не годятся для этой цели по причинам потери точности и ошибках результатах округления. Однако, более точные результаты дает использование класса BigInteger.
Конструктор класса
Конструктор класса совпадает с именем класса и ничего не возвращает, даже void.
Пример кода:
Файл: «MyConstructor.java»
/*
учебные пример
Конструктор ничего не возвращает, даже void
То что с void - обычный метод класса
*/
package underwaterRocks.myConstructor;
/**
*
* @author Ar20L80
*/
public class MyConstructor {
public MyConstructor(){
System.out.println("Я конструктор без void");
}
public void MyConstructor(){
System.out.println("Я конструктор c void");
}
public static void main(String[] args) {
MyConstructor myconst = new MyConstructor();
myconst.MyConstructor(); // вызов обычного метода
}
}
Как мы видим в коде два метода с одинаковыми именами: MyConstructor() и MyConstructor(). Один из методов ничего не возвращает. Это и есть конструктор нашего класса. Другой метод с void — это обычный метод класса. В случае, когда вы не создали конструктор или создали, по вашему мнению конструктор класса с void, то компилятор создаст конструктор по умолчанию и вы будете удивлены, почему ваш конструктор не работает.
Деление на ноль
Как вы думаете, какой будет результат выполнения такого кода.
Файл: «DivisionByZero.java»
/*учебный пример*/
package divisionByZero;
import static java.lang.Double.POSITIVE_INFINITY;
/**
*
* @author Ar20L80
*/
public class DivisionByZero {
public static void main(String[] args) {
try{
float f = 12.2f;
double d = 8098098.8790d;
System.out.println(f/0);
System.out.println(d/0);
System.out.println(POSITIVE_INFINITY == f/0);
System.out.println(POSITIVE_INFINITY == d/0);
}
catch (NumberFormatException ex) {
System.out.println("NumberFormatException");
}
catch (ArithmeticException ex) {
System.out.println("ArithmeticException");
}
}
}
Выполнение кода выведет:
Infinity
Infinity
true
true
Деление целого типа на ноль даст ArithmeticException.
В классе java.lang.Double определена константа POSITIVE_INFINITY;
public static final float POSITIVE_INFINITY = 1.0d / 0.0d;
Она преобразуется в строку равную Infinity.
Порядок инициализации
Файл: «InitClass.java»
/*
учебные пример
инициализация класса
*/
package myInitClass;
/**
*
* @author Ar20L80
*/
public class InitClass {
InitClass(){ // конструктор класса
System.out.print("Конструктор");
}
{ // блок инициализации
System.out.print("3 ");
}
public static void main(String[] args) {
System.out.print("2");
new InitClass();
}
static { // статический блок инициализации
System.out.print("1");
}
}
Вначале выполняются все статические блоки, затем блоки инициализации, затем конструктор класса.
Выведется: «123 Конструктор»
Локальная переменная скрывает переменную класса
Хотя современные IDE легко обнаруживают такую ошибку, хотелось бы рассмотреть такую ошибку подробнее. Начнем с классического присвоения переменной в конструкторе. Пример правильный. Тут никакой ошибки нет.
public class MyClass {
private int val = 0;
public MyClass(int val) {
this.val = val;
}
}
Однако, что произойдет, если вы используете такой прием в методе, а не в конструкторе класса? В обычном методе использовать такой прием не рекомендуется. Вопрос относится к правильному проектированию класса.
Простое объяснение: В методе переменная с тем же именем, что и переменная класса, является локальной по отношению к методу. Вы можете обращаться к переменной класса используя this.val. Однако такое обращение из метода, при неправильном проектировании класса только вызовет побочные эффекты и может ухудшить читаемость кода.
Приведение типов в арифметических выражениях выполняется автоматически
Это может стать причиной досадных ошибок.
// byte a = 1;
// byte b = 1;
// byte с = a + b; // ошибка
// byte a = (byte) 1;
// byte b = (byte) 1;
// byte с = a + b; // ошибка
// одно из возможных решений — явное преобразование в арифметических выражениях.
byte a = 1;
byte b = 1;
byte c = (byte) (a + b);
// одно из возможных решений — использование final
// final byte a = 1;
// final byte b = 1;
// byte c = a + b; // автоматического приведения не будет, поскольку a и b final
Одно из возможных решений при работе со строкой:
byte bHundr = Byte.parseByte("100"); // явное приведение строки к типу byte
Еще одна ошибка приведена в следующем коде.
for (byte i = 1; i <= 128; i++) {
System.out.println(i);
}
В данном случае получится бесконечный цикл.
Объяснение. Тип byte [-128, 127]. 128 уже не входит в этот диапазон. Происходит переполнение, и цикл повторяется. Необходимость использования byte в данном случае сомнительная. Хотя имеет место в редких случаях. Рекомендация — использовать int вместо byte. Другая рекомендация — не использовать цикл в вашем алгоритме.
Не проинициализированы объекты, являющиеся элементами массива
int[] cats = new int[10];
for(int i=0; i<cats.length;i++){
System.out.println("cats " + i + " = " + cats[i]);
}
В этом примере у нас массив элементов примитивного типа. И ничего страшного не происходит, когда мы их не проинициализировали. Им будет присвоено значение по умолчанию. В данном случае значение = 0.
Рассмотрим другой пример уже не с примитивами в массиве, а с объектами в массиве.
public class ArrInitObj {
public static void main(String[] args) {
MyObj[] cats = new MyObj[10];
for(int i=0; i<cats.length;i++){
System.out.println("cats " + i + " = " + cats[i]);
System.out.println("cats " + i + ".val = " + cats[i].val);
// мы поймали исключение java.lang.NullPointerException
}
}
}
class MyObj{
public int val;
}
Решение данной проблемы — инициализация всех переменных объекта перед их использованием. Инициализацию можно произвести в конструкторе класса MyObj.
Имя класса отличается от имени файла, в котором он хранится
Современная IDE легко определяет данный вид ошибки. Однако такие ошибки встречаются, хотя и довольно редко. Тут поможет внимательность, учет отличия в именах прописных и строчных букв.
Помещение в один файл сразу нескольких классов с модификатором public
Ошибка довольно редкая. IDE сразу выдаст вам предупреждение.
Имя файла должно совпадать с именем public класса.
Выводы
Многие ошибки не очевидны с первого взгляда. Даже опытные программисты их совершают, но в меньшем количестве. Внимательность, практический опыт, использование отладчика и чтение документации позволят вам избежать многих ошибок.
Надеюсь, статья вам понравилась и оказалась полезной. Буду рад вашим комментариям, замечаниям, предложениям, пожеланиям. Продолжение следует. Вернее дополнение следует.
Ссылки
Рекомендации об оформлении кода на Javа от Oracle >>>
Исключение — ошибка, которая нарушает нормальную работу программы. Java обеспечивает надежный объектно-ориентированный способ обработки исключений. Именно его мы и будем изучать в этом руководстве.
Исключение может возникнуть в разного рода ситуациях: неправильные входные данные, аппаратный сбой, сбоя сетевого соединения, ошибка при работе с базой данных и т.д. Именно поэтому любой Java программист должен уметь правильно обрабатывать исключения, понимать причины их появления и следовать лучшим практикам работы с исключениями даже в небольших проектах.
Java — объектно-ориентированный язык программирования, поэтому всякий раз, когда происходит ошибка при выполнении инструкции, создается объект-исключение, а затем нормальный ход выполнения программы останавливается и JRE пытается найти кого-то, кто может справиться (обработать) это исключение. Объект-исключение содержит много информации об отладке, а именно номер строки, где произошло исключение, тип исключения и т.д.
Что и как происходит, когда появляется ошибка
Когда в методе происходит исключение, то процесс создания объекта-исключения и передачи его в Runtime Environment называется «бросать исключение».
После создания исключения, Java Runtime Environment пытается найти обработчик исключения.
Обработчик исключения — блок кода, который может обрабатывать объект-исключение.
Логика нахождения обработчика исключений проста — прежде всего начинается поиск в методе, где возникла ошибка, если соответствующий обработчик не найден, то происходит переход к тому методу, который вызывает этот метод и так далее.
Пример
У нас есть 3 метода, каждый из которых вызывает друг-друга:
А -> В -> С
(А вызывает В, а В вызывает С). Если исключение появляется в методе C, то поиск соответствующего обработчика будет происходить в обратном порядке:С -> В -> А
(сначала там, где было исключение — в С, если там нет обработчика, то идем в метод В — если тут тоже нет, то идем в А).
Если соответствующий обработчик исключений будет найден, то объект-исключение передаётся обработчику.
Обработать исключение — значит «поймать исключение».
Если обработчик исключений не был найден, то программа завершает работу и печатает информации об исключении.
Обратите внимание, что обработка исключений в Java — это фреймворк, который используется только для обработки ошибок времени выполнения. Ошибки компиляции не обрабатываются рамках обработки исключений.
Основные элементы обработки исключений в Java
Мы используем определенные ключевые слова в для создания блока обработки исключений. Давайте рассмотрим их на примере. Также мы напишем простую программу для обработки исключений.
- Бросить исключение (
throw
) — ключевое слово, которое используется для того, чтобы бросить исключение во время выполнения. Мы знаем, что Java Runtime начинает поиск обработчика исключений как только оно будет брошено, но часто нам самим нужно генерировать исключение в нашем коде, например, в программе авторизации, если какое-то полеnull
. Именно для таких случаем и существует возможность бросить исключение. throws
— когда мы бросаем исключение в методе и не обрабатываем его, то мы должны использовать ключевое словоthrows
в сигнатуре метода для того, чтобы пробросить исключение для обработки в другом методе. Вызывающий метод может обработать это исключение или пробросить его еще дальше с помощьюthrows
в сигнатуре метода. Следует отметить, что пробрасывать можно сразу несколько исключений.- Блок
try-catch
используется для обработки исключений в коде. Словоtry
— это начало блока обработки,catch
— конец блока для обработки исключений. Мы можем использовать сразу несколько блоковcatch
при одномtry
.catch
в качестве параметра принимает тип исключения для обработки. finally
— необязательная завершающая конструкция блокаtry-catch
. Как только исключение остановило процесс исполнения программы, вfinally
мы можем безопасно освободить какие-то открытые ресурсы. Следует отметить, чтоfinally
блок выполняется всегда — не смотря на появление исключительной ситуации.
Давайте посмотрим простую программу обработки исключений в Java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package ua.com.prologistic; import java.io.FileNotFoundException; import java.io.IOException; public class ExceptionHandling { // в методе main() пробрасывается сразу несколько исключений public static void main(String[] args) throws FileNotFoundException, IOException { // в блоке try-catch перехватываются сразу несколько исключений вызовом дополнительного catch(…) try{ testException(—5); testException(—10); }catch(FileNotFoundException e){ e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); }finally{ System.out.println(«Необязательный блок, но раз уже написан, то выполнятся будет не зависимо от того было исключение или нет»); } testException(15); } // тестовый метод создания, обработки и пробрасывания исключения public static void testException(int i) throws FileNotFoundException, IOException{ if(i < 0){ FileNotFoundException myException = new FileNotFoundException(«число меньше 0: « + i); throw myException; }else if(i > 10){ throw new IOException(«Число должно быть в пределах от 0 до 10»); } } } |
А в консоле эта программа напишет такое:
java.io.FileNotFoundException: число меньше 0: —5 at ua.com.prologistic.ExceptionHandling.testException(ExceptionHandling.java:24) at ua.com.prologistic.ExceptionHandling.main(ExceptionHandling.java:10) Необязательный блок, но раз уже написан, то выполнятся будет не зависимо от того было исключение или нет Exception in thread «main» java.io.IOException: Число должно быть в пределах от 0 до 10 at ua.com.prologistic.ExceptionHandling.testException(ExceptionHandling.java:27) at ua.com.prologistic.ExceptionHandling.main(ExceptionHandling.java:19) |
Обратите внимание, что метод testException()
бросает исключение, используя ключевое слово throw
, а в сигнатуре метода используется ключевое слово throws
, чтобы дать понять вызывающему методу тип исключений, которые может бросить testException()
.
Важные моменты в обработке исключений:
- Нельзя использовать блоки
catch
илиfinally
без блокаtry
. - Блок
try
также может быть использован только сcatch
блоком, или только сfinally
блоком, или с тем и другим блоком. - Мы можем использовать несколько блоков
catch
только с однимtry
. try-catch
блоки могут быть вложенными — этим они очень похожи наif-else
конструкции.- Мы можем использовать только один, блок
finally
в одномtry-catch
.
Иерархия исключений в Java
Java исключения являются иерархическими, а наследование используется для категоризации различных типов исключений. Throwable
— родительский класс в иерархии Java исключений. Он имеет два дочерних объекта — Error
и Exception
. Исключения далее разделены на проверяемые исключения и исключения времени выполнения.
- Error — это тип ошибок, которые выходят за рамки вашей программы, их невозможно предвидеть или обработать. Это может быть аппаратный сбой, «поломка» JVM или ошибка памяти. Именно для таких необычных ситуаций есть отдельная иерархия ошибок. Мы должны просто знать, что такие ошибки есть и не можем справиться с такими ситуациями. Примеры
Error
:OutOfMemoryError
иStackOverflowError
. - Проверяемые исключения (Checked Exceptions) — тип исключений, которые мы можем предвидеть в программе и попытаться обработать, например,
FileNotFoundException
. Мы должны поймать это исключение и написать внятное и полезное сообщение пользователю о том, что произошло (также желательно логировать ошибки).Exception
— родительский класс всех проверяемых исключений (Checked Exceptions). Если мы бросили проверяемое исключение, то должны поймать его в том же методе или должны пробросить его с помощью ключевого словаthrows
. - Runtime Exception — это ошибки программиста. Например, пытаясь получить элемент из массива, мы должны проверить длину массива, прежде чем пытаться получить элемент — в противном случае это может быть брошен
ArrayIndexOutOfBoundException
.RuntimeException
— родительский класс для всех Runtime исключений. Если мы сами бросаем Runtime Exception в методе, то не обязательно указывать в сигнатуре метода ключевое словоthrows
.
На рисунке 1 представлена иерархия исключений в Java:
Рисунок 1 — Иерархия исключений в Java
Полезные методы в обработке исключений
Класс Exception
и все его подклассы не содержат какие-либо методы для обработки исключений. Все предоставляемые методы находятся в базовом классе Throwable
. Подклассы класса Exception
созданы для того, чтобы определять различные виды исключений. Именно поэтому при обработке исключений мы можем легко определить причину и обработать исключение в соответствии с его типом.
Полезные методы класса Throwable
:
- public String getMessage() — этот метод возвращает сообщение, которое было создано при создании исключения через конструктор.
- public String getLocalizedMessage() — метод, который переопределяют подклассы для локализации конкретное сообщение об исключении. В реализации
Throwable
класса этот метод просто использует методg
etMessage()
, чтобы вернуть сообщение об исключении (Throwable
на вершине иерархии — ему нечего локализировать, поэтому он вызываетgetMessage())
. - public synchronized Throwable getCause() — этот метод возвращает причину исключения или идентификатор в виде
null
, если причина неизвестна. - public String toString() — этот метод возвращает информацию о
Throwable
в форматеString
. - public void printStackTrace() — этот метод выводит информацию трассировки стека в стандартный поток ошибок, этот метод перегружен и мы можем передать
PrintStream
илиPrintWriter
в качестве аргумента, чтобы написать информацию трассировки стека в файл или поток.
Автоматическое управление ресурсами и улучшения блока перехвата ошибок в Java 7
Если вам нужно перехватывать много исключений в одном блоке try-catch
, то блок перехвата будет выглядеть очень некрасиво и в основном будет состоять из избыточного кода. Именно поэтому в Java 7 это было значительно улучшено и теперь мы можем перехватывать несколько исключений в одном блоке catch
.
Это выглядит следующим образом:
catch(IOException | SQLException | Exception ex){ //что-то сделать с перехваченной ошибкой… } |
Как видим, здесь блок catch
перехватывает сразу несколько исключений — это очень красиво, компактно и удобно.
В большинстве случаев мы используем блок finally
для того, чтобы закрыть открытые потоки, подключения или освободить другие ресурсы. Очень часто мы забываем закрыть и получаем runtime исключения. Такие исключения трудно отлаживать. Поэтому в Java 7 был введен try
с ресурсами, где мы можем открыть ресурс в самом try
и использовать его внутри блока try-catch
. Когда программа заканчивает выполнение блока try-catch
, то среда выполнения автоматически закрывает эти ресурсы. Вот пример try-catch
блока с ресурсами:
// try c ресурсами try (MyResource mr = new MyResource()) { System.out.println(«Красивый и компактный код в try c ресурсами»); } catch (Exception e) { e.printStackTrace(); } |
Создание своих классов исключений
Java предоставляет много классов исключений, но иногда нам может понадобиться создать свои «кастомные» классы исключений. Это может понадобиться для того, чтобы уведомить абонента о конкретном типе исключения с соответствующим сообщением. Например, мы напишем метод для обработки только текстовых файлов, поэтому мы можем написать свой класс исключений и передавать соответствующий код ошибки, когда кто-то передает неподходящий тип файла в качестве входных данных.
Вот пример своего класса исключений и его использование:
package ua.com.prologistic; // наследуемся от класс Exception public class MyException extends Exception { private String errorCode = «Unknown_Exception»; public MyException(String message, String errorCode){ super(message); this.errorCode = errorCode; } public String getErrorCode(){ return this.errorCode; } } |
А теперь проверим в работе наш класс MyException:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package ua.com.prologistic; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; public class CustomExceptionExample { public static void main(String[] args) throws MyException { try { processFile(«file.txt»); } catch (MyException e) { processErrorCodes(e); } } // метод для обработки ошибок private static void processErrorCodes(MyException e) throws MyException { // здесь мы ищем указанный при выбросе исключения код ошибки и сообщаем пользователю что произошло switch(e.getErrorCode()){ case «BAD_FILE_TYPE»: System.out.println(«Неподходящий тип файла»); throw e; case «FILE_NOT_FOUND_EXCEPTION»: System.out.println(«Файл не найден»); throw e; case «FILE_CLOSE_EXCEPTION»: System.out.println(«Ошибка при закрытии файла»); break; default: System.out.println(«Произошла неизвестная ошибка « + e.getMessage()); e.printStackTrace(); } } // метод для работы с файлом, который пробрасывает наш тип исключений private static void processFile(String file) throws MyException { InputStream fis = null; try { fis = new FileInputStream(file); } catch (FileNotFoundException e) { // здесь мы бросаем исключение с указанием кода ошибки throw new MyException(e.getMessage(),«FILE_NOT_FOUND_EXCEPTION»); }finally{ try { if(fis !=null)fis.close(); } catch (IOException e) { // здесь мы бросаем исключение с указанием кода ошибки throw new MyException(e.getMessage(),«FILE_CLOSE_EXCEPTION»); } } } } |
Полезные советы по обработке исключений в Java
- Не используйте для перехвата исключений класс
Exception
. В иерархии исключений есть множество классов на все случаи жизни вашей программы, которые не только эффективно обработают конкретную ошибку, но и предоставят полезную для пользователя и отладки информацию. - Бросайте исключение как можно раньше. Это является хорошей практикой программирования на Java.
- Ловите исключения только тогда, когда сможете эффективно для пользователя и отладки их обработать.
- Освобождайте ресурсы. Перехватывая исключение всегда закрывайте открытые ресурсы. Еще проще и эффективнее это делать с
Java 7
. Используйте try с ресурсами для лаконичного и красивого кода. - Логируйте исключения. Логируйте сообщения, которые предоставляет исключение. В большинстве случаев это даст вам четкое понимание причин и поможет в отладке. Не оставляйте пустым блок
catch
, иначе он будет просто поглощать исключение без каких-либо значимых деталей для отладки. - Один catch для нескольких исключений. Используйте преимущества Java 7 для удобства и красоты вашего кода.
- Используйте свои исключения. Это позволит вам лучше чувствовать свою программу и эффективнее с ней работать.
- Соглашения об именовании. Когда вы создать свои классы исключений, следите за тем, что из самого названия класса будет ясно, что это исключение.
- Используйте исключения с умом. Бросить исключение — достаточно дорогостоящая в Java операция. Возможно, в некоторых случаях будем уместно не бросать исключений, а вернуть, например, логическую переменную, которая обозначала успешное или не успешное выполнение метода.
- Документируйте исключения. Желательно писать javadoc @throws для ваших исключений. Это будет особенно полезно в тех случаях, когда ваша программа предоставляет интерфейс для работы с другими приложениями.
Вот и все, что нужно знать об обработке исключений в Java.
Исключения бывают в жизни у каждого. Каждый из нас сталкивается с ситуациями, когда ошибся и приходится выкручиваться.
Но что, если в программировании можно заранее предотвращать эти неприятные ситуации?! Да-да, можно! Для этого и было в введено понятие Исключений, Ошибок (от англ. — Exception).
Другими словами, в языке программирования Java описаны все возможные варианты исключений, которые может выкинуть Ваша программа! Так как Java — это объектно-ориентированный язык программирования, то каждый Exception это Класс. И эти Классы унаследуют друг друга, раcширяя их возможности и делая их более уникальными для каждого вида ошибок.
А расширяют они друг друга следующим образом:
Как и в жизни, есть ошибки(CHECKED), которые мы можем предотвратить и заранее приготовили для них решение. Например: если Вы едите в путешествие, скорее всего, Вы берете с собой аптечку, чтобы если кому-то станет плохо, у Вас было лекарство под рукой.
А бывают непредвиденные ошибки(UNCHECKED). Например: Вы поехали в жаркую страну и неожиданно циклон пригнал холода и начал падать снег летом.
Так и в языке программирования Java есть:
- CHECKED Ошибки — те, для которых мы обязательно должны прописать какое-то решение, если что-то пойдет не так.
- UNCHECKED Ошибки — те, которые мы не можем предвидеть заранее.
А на логичный вопрос: «А как заранее подготовить решение для ошибки?» Уже заготовлен заранее ответ. Вот так:
Блок try catch — помогает обработать определенный участок кода таким образом, что когда выскочит ошибка, она не приведет к завершению программы, а просто выполнит блок catch.
- В блоке try Вы пишите код, который может в какой-то момент сработать не правильно и создать ошибку.
- В блоке catch Вы ловите эту ошибку, указав какой класс Ошибки будете ловить, после чего описываете как ее обработаете.
Давайте рассмотрим это на примере.
Например: если Вы захотите записать в файл текст, для этого можно использовать класс FileWriter. (Примечание: Если Вы не знакомы с FileWriter, прочитайте вот эту статью).
public class Test { public static void main(String[] args) { FileWriter fileWriter = new FileWriter(«out.txt»); fileWriter.write(«Hello World»); } } |
Как выглядит в среде разработки:
Другими словами, этот Класс подчеркивается красным и среда разрабтки предлагает записать тот же участок кода только в блоке try catch. Попробуем:
Например:
public class Test { public static void main(String[] args) { try { FileWriter fileWriter = new FileWriter(«out.txt»); fileWriter.write(«Hello World»); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } |
В таком случае ошибок не возникает и код отрабатывает успешно:
Так же в блоке catch можно обрабатывать ошибки абсолютно разным образом.
К примеру, если Вы сначала закроете файл, а после попробуете записать в него данные, выскочит Ошибка.
Например:
public class Test { public static void main(String[] args) { try { FileWriter fileWriter = new FileWriter(«out.txt»); fileWriter.close(); fileWriter.write(«Hello World»); } catch (IOException e) { e.printStackTrace(); } } } |
Вывод в консоль:
В блоке catch, написано выводить ошибку в консоль. Было упомянуто, что выполняется любой код распаложеный в блоке catch. Давайте попробуем:
Например:
public class Test { public static void main(String[] args) { try { FileWriter fileWriter = new FileWriter(«out.txt»); fileWriter.close(); fileWriter.write(«Hello World»); } catch (IOException e) { System.out.println(«Ошибка — Нельзя записать в закрытый файл!»); } } } |
Вывод в консоль:
Выполнился только тот код, который написан в блоке catch.
Даже при ошибке программа продолжает успешно работать!
Например:
public class Test { public static void main(String[] args) { try { FileWriter fileWriter = new FileWriter(«out.txt»); fileWriter.close(); fileWriter.write(«Hello World»); } catch (IOException e) { System.out.println(«Ошибка — Нельзя записать в закрытый файл!»); } System.out.println(«Программа работает успешно!»); } } |
Вывод в консоль:
Другими словами программа продолжит выполняться, даже когда выкинулась ошибка. А все потому, что Вы ее правильно обработали.
Но так же существует блок «finally«, который будет выполняться всегда:
- если была вызвана ошибка и выполнился блок catch
- если все исполнилось без ошибок.
Например:
public class Test { public static void main(String[] args) { try { FileWriter fileWriter = new FileWriter(«out.txt»); fileWriter.write(«Hello World»); fileWriter.close(); } catch (IOException e) { System.out.println(«Ошибка — Нельзя записать в закрытый файл!»); }finally { System.out.println(«Выполняюсь всегда!»); } System.out.println(«Программа работает успешно!»); } } |
Вывод в консоль:
И даже так!
Например:
public class Test { public static void main(String[] args) { try { FileWriter fileWriter = new FileWriter(«out.txt»); fileWriter.close(); fileWriter.write(«Hello World»); } catch (IOException e) { System.out.println(«Ошибка — Нельзя записать в закрытый файл!»); }finally { System.out.println(«Выполняюсь всегда!»); } System.out.println(«Программа работает успешно!»); } } |
Вывод в консоль:
В Checked Ошибках можно обойтись и без блока «try catch«, подключив Класс ошибки напрямую к методу зарезервированным словом «throws«:
Например:
public class Test { public static void main(String[] args) throws IOException { FileWriter fileWriter = new FileWriter(«out.txt»); fileWriter.close(); fileWriter.write(«Hello World»); System.out.println(«Программа работает успешно!»); } } |
Вывод в консоль:
В результате в момент когда выскочила ошибка, исполнение программы остановилось. Но таким образом можно избавиться от блоков «try catch«. Если Вы точно уверенны, что код написан правильно.
В таком случае, если поменять местами закрытие и запись в файл — выйдет следующее:
Например:
public class Test { public static void main(String[] args) throws IOException { FileWriter fileWriter = new FileWriter(«out.txt»); fileWriter.write(«Hello World»); fileWriter.close(); System.out.println(«Программа работает успешно!»); } } |
Вывод в консоль:
В UNCHECKED Ошибках все гораздо проще. Так же можно использовать блок «try catch«, если Вы явно видите, что может произойти ошибка.
Например:
public class Test { public static void main(String[] args) { int a = 5; int b = 0; int c = a/b; } } |
Вывод в консоль:
Так как делить на ноль нельзя, в таком случае выскакивает ошибка Класса ArithmeticException. Но если Вы знаете заранее, что может такое произойти при выполнении программы, тогда конечно, чтобы не остановилось выполнение программы можно использовать блок «try catch«.
Например:
public class Test { public static void main(String[] args){ int a = 5; int b = 0; try { int c = a/b; }catch (ArithmeticException e){ System.out.println(«Делить на нуль — нельзя!»); } System.out.println(«Программа работает успешно!»); } } |
Вывод в консоль: