NoSuchElementException в цикле фильтра
Пишу тестовое задание, представляющее из себя простой сервис заметок. Это мое первое знакомство с такого рода приложениями. Сначала хоть как-то работало, но после того, как начал разбираться перестало работать при любом заданном фильтре. Кидает ошибку NoSuchElementException . Дебаггер показывает, что это происходит на третьем заходе в цикл.
Iterable entries = entryRepo.findAll(); if (filter != null && !filter.isEmpty()) < Iteratoritr = entries.iterator(); while (itr.hasNext()) < boolean first = !itr.next().getTitle().contains(filter); boolean second = !itr.next().getContent().contains(filter); if (first && second) < itr.remove(); >> >
Как исправить? Или подскажите вариант получше
Отслеживать
10.2k 5 5 золотых знаков 24 24 серебряных знака 68 68 бронзовых знаков
Правильно ли я использую исключения в java?
Добрый День!
Помогите разобраться с основами java, а именно с работой с исключениями.
Есть простая программа, спрашивающая из консоли положительное число.
Если число отрицательное выбрасывается исключение и снова вызывается функция запроса числа.
Собственно пара вопросов:
1. Откуда берется java.util.NoSuchElementException, как это исправить?
2. Насколько оправданно в этом случае использование исключений, вместо старого доброго if-else блока?
3. Где стоит обрабатывать исключение. Непосредственно в функции где оно может выпасть или же в функции вызывающей ее?
4. Вообще используется ли прием reвызова функции из блока catch? Или это бред и обычно используются другие паттерны?
Благодарю) Код программы ниже)
import java.util.Scanner; public class MyProgram < public static void main(String[] args) < int input; try < input = askForInput(); >catch (IllegalArgumentException e) < System.out.println(e.getMessage()); input = askForInput(); >System.out.println(input); > private static int askForInput() < try (Scanner sc = new Scanner(System.in)) < System.out.println("Введите положительное число:"); int result = sc.nextInt(); if (result < 0) < throw new IllegalArgumentException("Вы ввели отрицательное число"); >return result; > > >
- Вопрос задан более года назад
- 153 просмотра
4 комментария
Простой 4 комментария

2. Насколько оправданно в этом случае использование исключений, вместо старого доброго if-else блока?
Не оправдано совершенно. Нужно по максимуму избегать бросания исключений, где можно обойтись обычной проверкой.

Вот посмотри как я делал абсолютно тоже самое в Kotlin (requestUserInputAsInt). В Kotlin примере я бросаю исключение, если программист передал неправильные значения диапазона. Когда он поймает исключение, он исправит свою ошибку и больше она не возникнет. А ввод пользователя ожидаемо может быть неправильным, поэтому мы и обрабатываем это так, как будто это ожидаем.
https://qna.habr.com/q/1166050#answer_2182636
Эти языки знать не обязательно, все языки похожи. Просто читай как псевдокод, всё должно быть понятно.

Если в конструктор класса передали невалидный параметр, то можно бросить исключение, так как в противном случае всё равно нужно будет создать невалидный объект, да ещё и не забыть проверить его после создания, что он невалидный. Ты проверишь, а твой коллега нет. Вот здесь исключение абсолютно оправдано. Есть и другие ситуации. Нужно исходить из логики.
How can I fix java.util.NoSuchElementException
BTW, just removing the lines is good, but doesn’t explain the Exception. The reason is that by calling close() on your Scanner, it will also close its underlying stream (in this case, System.in ), and once closed, you can’t use it again. In your example code, after calling newUser() , your System.in will end up in a closed state, and you’ll never be able to use it again in the rest of your code.
Apr 14, 2019 at 23:42
Possible duplicate of java.util.NoSuchElementException — Scanner reading user input
Apr 15, 2019 at 0:11
1 Answer 1
I found the reason why you are getting this exception.
So in your main method, you initialized your Scanner class object and immediately close it.
Here is the problem. Because when scanner calls the close() method, it will close its input source if the source implements the Closeable interface.
When a Scanner is closed, it will close its input source if the source implements the Closeable interface.
And InputStream class which is the input source in your case implements the Closeable interface.
And further you initialized the Scanner class object into your newUser() method. Here scanner class object initialized successfully but your input source is still close.
So my suggestion is that close scanner class object only once. Please find the updated code of yours.
class Main2 < public static void main(String[] args) < Scanner input = new Scanner(System.in); newUser(input); //input.close() >private static void newUser(Scanner input) < try < System.out.print("Please enter the name for the new user."); String userNameNew = input.nextLine(); System.out.println("Please enter the password for the new user."); String userPassWordNew = input.nextLine(); System.out.println("The new user: " + userNameNew + " has the password: " + userPassWordNew + "." ); PrintWriter out = new PrintWriter("users.txt"); out.print(userNameNew + "\r\n" + userPassWordNew); out.close(); >catch (IOException e) < e.printStackTrace(); >> >
Spring Boot REST API – обработка исключений. Часть 1
Мы рассмотрим простое REST API приложение с одной сущностью Person и с одним контроллером.
@Entity @NoArgsConstructor @AllArgsConstructor @Data public class Person
@RestController @RequestMapping("/persons") public class PersonController < @Autowired private PersonRepository personRepository; @GetMapping public ListlistAllPersons() < Listpersons = personRepository.findAll(); return persons; > @GetMapping(value = "/") public Person getPerson(@PathVariable("personId") long personId) < return personRepository.findById(personId).orElseThrow(() ->new MyEntityNotFoundException(personId)); > @PostMapping public Person createPerson(@RequestBody Person person) < return personRepository.save(person); >@PutMapping("/") public Person updatePerson(@RequestBody Person person, @PathVariable long id) < Person oldPerson = personRepository.getOne(id); oldPerson.setName(person.getName()); return personRepository.save(oldPerson); >>
При старте приложения выполняется скрипт data.sql, который добавляет в базу данных H2 одну строку — Person c То есть Person c в базе отсутствует.
При попытке запросить Person c id=2:
GET localhost:8080/persons/2
метод контроллера getPerson() выбрасывает исключение — в данном случае наше пользовательское MyEntityNotFoundException:
public class MyEntityNotFoundException extends RuntimeException < public MyEntityNotFoundException(Long id) < super("Entity is not found, id EnlighterJSRAW" data-enlighter-language="java">@Controller @RequestMapping(>">) public class BasicErrorController extends AbstractErrorController < . @RequestMapping public ResponseEntity> error(HttpServletRequest request) < HttpStatus status = this.getStatus(request); if (status == HttpStatus.NO_CONTENT) < return new ResponseEntity(status); >else < Mapbody = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL)); return new ResponseEntity(body, status); > > . >
Если поставить в этом методе break point, то будет понятно, из каких атрибутов собирается ответное JSON сообщение.
Проверим ответ по умолчанию, запросив с помощью клиента Postman отсутствующий Person, чтобы выбросилось MyEntityNotFoundException:
GET localhost:8080/persons/2
Причем для того, чтобы поле message было непустым, в application.properties нужно включить свойство:
server.error.include-message=always
В текущей версии Spring сообщение не включается по умолчанию из соображений безопасности.
Обратите внимание, что поле status JSON-тела ответа дублирует реальный http-код ответа. В Postman он виден:

Поле message заполняется полем message выброшенного исключения.
Независимо от того, какое исключение выбросилось: пользовательское или уже существующее, ответ стандартный — в том смысле, что набор полей одинаковый. Меняется только внутренняя часть и, возможно, код ответа (он не обязательно равен 500, некоторые существующие в Spring исключения подразумевают другой код).
Но структура ответа сохраняется.
Не пользовательское исключение
Например, если изменить код, убрав пользовательское MyEntityNotFoundException, то при отсутствии Person исключение будет все равно выбрасываться, но другое:
@GetMapping(value = «/») public Person getPerson(@PathVariable(«personId») long personId)
findById() возвращает тип Optional, а Optional.get() выбрасывает исключение NoSuchElementException с другим сообщением:
java.util.NoSuchElementException: No value present
в итоге при запросе несуществующего Person:
GET localhost:8080/persons/2
ответ сохранит ту же структуру, но поменяется поле message:
Вернем обратно пользовательское исключение MyEntityNotFoundException.
Попробуем поменять ответ, выдаваемый в ответ за запрос. Статус 500 для него явно не подходит.
Рассмотрим способы изменения ответа.
@ResponseStatus
Пока поменяем только статус ответа. Сейчас возвращается 500, а нам нужен 404 — это логичный ответ, если ресурс не найден.
Для этого аннотируем наше исключение:
@ResponseStatus(HttpStatus.NOT_FOUND) public class MyEntityNotFoundException extends RuntimeException < public MyEntityNotFoundException(Long id) < super("Entity is not found, id EnlighterJSRAW" data-enlighter-language="java">< "timestamp": "2021-02-28T15:54:37.070+00:00", "status": 404, "error": "Not Found", "message": "Entity is not found, "path": "/persons/2" >
@ControllerAdvice
Есть еще более мощный способ изменить ответ — @ControllerAdvice, и он имеет больший приоритет, чем @ResponseStatus.
В @ControllerAdvice можно не только изменить код ответа, но и тело. К тому же один обработчик можно назначить сразу для нескольких исключений.
Допустим мы хотим, чтобы ответ на запрос несуществующего Person имел такую структуру:
@Data @AllArgsConstructor @NoArgsConstructor public class ApiError
Для этого создадим обработчик в @ControllerAdvice, который перехватывает наше исключение MyEntityNotFoundException:
@ControllerAdvice public class RestExceptionHandler < @ExceptionHandler() protected ResponseEntity handleEntityNotFoundEx(RuntimeException ex, WebRequest request) < ApiError apiError = new ApiError("entity not found ex", ex.getMessage()); return new ResponseEntity<>(apiError, HttpStatus.NOT_FOUND); > >
Теперь в ответ на запрос
GET localhost:8080/persons/2
мы получаем статус 404 с телом:
< "message": "entity not found ex", "debugMessage": "Entity is not found, >Но помимо MyEntityNotFoundException, наш обработчик поддерживает и javax.persistence.EntityNotFoundException (см. код выше).
Попробуем сделать так, чтобы оно возникло.
Это исключение EntityNotFoundException возникает в методе updatePerson() в контроллера. А именно, когда мы обращаемся с помощью метода PUT к несуществующей сущности в попытке назначить ей имя:
PUT localhost:8080/persons/2
В этом случае мы тоже получим ответ с новой структурой:
Итого, обработчик в @ControllerAdvice позволил изменить не только код ответа, но и тело сообщение. Причем один обработчик мы применили для двух исключений.
Последовательность проверок
Обратите внимание, что MyEntityNotFoundException мы «обработали» дважды — изменили код с помощью @ResponseStatus (1) и прописали в @ContollerAdvice — тут изменили как код, так и тело ответа (2). Эти обработки могли быть противоречивы, но существует приоритет:
- Когда выбрасывается исключение MyEntityNotFoundException, сначала Spring проверяет @ControllerAdvice-класс. А именно, нет ли в нем обработчика, поддерживающего наше исключение. Если обработчик есть, то исключение в нем и обрабатывается. В этом случае код @ResponseStatus значения не имеет, и в BasicErrorController исключение тоже не идет.
- Если исключение не поддерживается в @ControllerAdvice-классе, то оно идет в BasicErrorController. Но перед этим Spring проверяет, не аннотировано ли исключение аннотацией @ResponseStatus. Если да, то код ответа меняется, как указано в @ResponseStatus. Далее формируется ответ в BasicErrorController.
- Если же первые два условия не выполняются, то исключение обрабатывается сразу в BasicErrorController — там формируется стандартный ответ со стандартным кодом (для пользовательских исключений он равен 500).
Но и стандартный ответ можно изменить, для этого нужно расширить класс DefaultErrorAttributes.
Попробуем это сделать.
Изменение DefaultErrorAttributes
Давайте добавим в стандартный ответ еще одно поле. Для этого расширим класс:
@Component public class CustomErrorAttributes extends DefaultErrorAttributes < @Override public MapgetErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) < MaperrorAttributes=super.getErrorAttributes(webRequest, options); errorAttributes.put("newAttribute", "value"); return errorAttributes; > >
В Map errorAttributes перечисляются поля ответа. Мы взяли их из родительского метода и добавили свое поле newAttribute.
Чтобы выполнить проверку, надо убрать @ControllerAdvice, поскольку он самый приоритетный и с ним мы даже не дойдем до BasicErrorController со «стандартными» полями.
Далее запросим ресурс:
localhost:8080/persons/2
В JSON-ответе появилось дополнительное поле.
ResponseStatusException
Рассмотрим еще один вариант, позволяющий сразу протолкнуть код ответа и сообщение стандартные поля, не прописывая обработку пользовательских или встроенных исключений. А вместо этого просто выбросив специально предназначенное исключение ResponseStatusException.
Изменим код метода контроллера getPerson():
@GetMapping(value = "/") public Person getPerson(@PathVariable("personId") long personId) < return personRepository.findById(personId).orElseThrow(() ->new ResponseStatusException( HttpStatus.NOT_FOUND, "Person Not Found")); >
Теперь тут не выбрасывается ни MyEntityNotFoundException, ни java.util.NoSuchElementException. А выбрасывается ResponseStatusException с заданным сообщением и кодом ответа.
Теперь при запросе
GET localhost:8080/persons/2
ответ будет таким:
Как код, так и сообщение появилось в полях стандартного ответа.
ResponseStatusException не вступает в конкуренцию ни со способом @ControllerAdvice, ни с @ResponseStatus — просто потому, что это другое исключение.
Итоги
Код примера доступен на GitHub. В следующей части мы унаследуем RestExceptionHandler от ResponseEntityExceptionHandler. Это класс-заготовка, которая уже обрабатывает ряд исключений.