Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[SnowOne 2024] Сергей Луговой: "Java: good practices. Нормально делай — нормально будет"

[SnowOne 2024] Сергей Луговой: "Java: good practices. Нормально делай — нормально будет"

Доклад создан по мотивам проверок вступительных и домашних заданий на курсах Java.

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

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

Посмотрим как применение дженериков и функций помогает упрощать код.
Дадим несколько добрых и полезных рекомендаций, которые помогут создавать «зрелый» код.

jugnsk

May 01, 2024
Tweet

More Decks by jugnsk

Other Decks in Programming

Transcript

  1. Л у говой Сергей. 2024 Java: good practices Норм а

    льно дел а й - норм а льно б у дет
  2. О себе • 35 лет опыта • Начало - С/С++,

    АРМы, БД • Java с первой версии • Telecom: SMSC/USSDC, сервисы • Системы обработки данных • Преподаватель Java в ШИФТ 2
  3. 3 Параметры запуска public static void main(String[] args) { if

    (args[0].equals("-a")) { // sehr gut } else if (args[0].equals("-b")) { // ... } }
  4. 3 Параметры запуска public static void main(String[] args) { if

    (args[0].equals("-a")) { // sehr gut } else if (args[0].equals("-b")) { // ... } } Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0 at org.example.Bad.main(Bad.java:5)
  5. 4 Параметры запуска public static void main(String[] args) { if

    (args.length < 3) { System.out.println(""" Args: [-a] (-b|-c) file1 file2 -a - optional arg -b or -c - required, enables one or another case file1 - some file with data file2 - another file with data""" ); System.exit(2); } throw new RuntimeException("Ooops"); // exit code = 1 }
  6. 4 Параметры запуска public static void main(String[] args) { if

    (args.length < 3) { System.out.println(""" Args: [-a] (-b|-c) file1 file2 -a - optional arg -b or -c - required, enables one or another case file1 - some file with data file2 - another file with data""" ); System.exit(2); } throw new RuntimeException("Ooops"); // exit code = 1 }
  7. if (args[0].equals("-a") || args[0].equals("-d")) { if (args[0].equals("-d")) { order =

    Order.DESC; } if (args[1].equals("-s")) { type = Type.STRING; } if (args[1].equals("-i")) { type = Type.INT; } outFile = args[2]; for (int i=0; i< args.length-3; i++){ files.addLast(args[i+3]); } } if (args[0].equals("-s") || args[0].equals("-i")) { if (args[0].equals("-s")) { type = Type.STRING; } if (args[0].equals("-i")) { type = Type.INT; } outFile = args[1]; for (int i=0; i< args.length-2; i++){ files.addLast(args[i+2]); } } 5 Параметры запуска
  8. 6 Например commons-cli public static CommandLine parse(String[] args) throws ParseException

    { Options options = new Options(); options.addOption(A_OPT, "option A is optional"); var bcGroup = new OptionGroup() .addOption(new Option(B_OPT, "option B required if C not specified")) .addOption(new Option(C_OPT, "option C required if B not specified")); bcGroup.setRequired(true); options.addOptionGroup(bcGroup); try { CommandLine commandLine = new DefaultParser().parse(options, args); if (commandLine.getArgList().size() < 2) throw new ParseException("Not enough files specified"); return commandLine; } catch (ParseException e) { if (args.length > 0 ) System.out.println(e.getMessage()); else new HelpFormatter().printHelp("app <options> file1 file2", options); throw e; } }
  9. 6 Например commons-cli public static CommandLine parse(String[] args) throws ParseException

    { Options options = new Options(); options.addOption(A_OPT, "option A is optional"); var bcGroup = new OptionGroup() .addOption(new Option(B_OPT, "option B required if C not specified")) .addOption(new Option(C_OPT, "option C required if B not specified")); bcGroup.setRequired(true); options.addOptionGroup(bcGroup); try { CommandLine commandLine = new DefaultParser().parse(options, args); if (commandLine.getArgList().size() < 2) throw new ParseException("Not enough files specified"); return commandLine; } catch (ParseException e) { if (args.length > 0 ) System.out.println(e.getMessage()); else new HelpFormatter().printHelp("app <options> file1 file2", options); throw e; } }
  10. 6 Например commons-cli public static CommandLine parse(String[] args) throws ParseException

    { Options options = new Options(); options.addOption(A_OPT, "option A is optional"); var bcGroup = new OptionGroup() .addOption(new Option(B_OPT, "option B required if C not specified")) .addOption(new Option(C_OPT, "option C required if B not specified")); bcGroup.setRequired(true); options.addOptionGroup(bcGroup); try { CommandLine commandLine = new DefaultParser().parse(options, args); if (commandLine.getArgList().size() < 2) throw new ParseException("Not enough files specified"); return commandLine; } catch (ParseException e) { if (args.length > 0 ) System.out.println(e.getMessage()); else new HelpFormatter().printHelp("app <options> file1 file2", options); throw e; } }
  11. 6 Например commons-cli public static CommandLine parse(String[] args) throws ParseException

    { Options options = new Options(); options.addOption(A_OPT, "option A is optional"); var bcGroup = new OptionGroup() .addOption(new Option(B_OPT, "option B required if C not specified")) .addOption(new Option(C_OPT, "option C required if B not specified")); bcGroup.setRequired(true); options.addOptionGroup(bcGroup); try { CommandLine commandLine = new DefaultParser().parse(options, args); if (commandLine.getArgList().size() < 2) throw new ParseException("Not enough files specified"); return commandLine; } catch (ParseException e) { if (args.length > 0 ) System.out.println(e.getMessage()); else new HelpFormatter().printHelp("app <options> file1 file2", options); throw e; } }
  12. 6 Например commons-cli public static CommandLine parse(String[] args) throws ParseException

    { Options options = new Options(); options.addOption(A_OPT, "option A is optional"); var bcGroup = new OptionGroup() .addOption(new Option(B_OPT, "option B required if C not specified")) .addOption(new Option(C_OPT, "option C required if B not specified")); bcGroup.setRequired(true); options.addOptionGroup(bcGroup); try { CommandLine commandLine = new DefaultParser().parse(options, args); if (commandLine.getArgList().size() < 2) throw new ParseException("Not enough files specified"); return commandLine; } catch (ParseException e) { if (args.length > 0 ) System.out.println(e.getMessage()); else new HelpFormatter().printHelp("app <options> file1 file2", options); throw e; } }
  13. 6 Например commons-cli public static CommandLine parse(String[] args) throws ParseException

    { Options options = new Options(); options.addOption(A_OPT, "option A is optional"); var bcGroup = new OptionGroup() .addOption(new Option(B_OPT, "option B required if C not specified")) .addOption(new Option(C_OPT, "option C required if B not specified")); bcGroup.setRequired(true); options.addOptionGroup(bcGroup); try { CommandLine commandLine = new DefaultParser().parse(options, args); if (commandLine.getArgList().size() < 2) throw new ParseException("Not enough files specified"); return commandLine; } catch (ParseException e) { if (args.length > 0 ) System.out.println(e.getMessage()); else new HelpFormatter().printHelp("app <options> file1 file2", options); throw e; } }
  14. 7 Удобное представление параметров public record Params(boolean aEnabled, boolean bEnabled,

    boolean cEnabled, Path file1, Path file2) { } public static Params parseArgs(String[] args) throws ParseException { CommandLine commandLine = parse(args); var fileNames = commandLine.getArgList().iterator(); return new Params( commandLine.hasOption(A_OPT), commandLine.hasOption(B_OPT), commandLine.hasOption(C_OPT), Paths.get(fileNames.next()), Paths.get(fileNames.next()) ); }
  15. 7 Удобное представление параметров public record Params(boolean aEnabled, boolean bEnabled,

    boolean cEnabled, Path file1, Path file2) { } public static Params parseArgs(String[] args) throws ParseException { CommandLine commandLine = parse(args); var fileNames = commandLine.getArgList().iterator(); return new Params( commandLine.hasOption(A_OPT), commandLine.hasOption(B_OPT), commandLine.hasOption(C_OPT), Paths.get(fileNames.next()), Paths.get(fileNames.next()) ); }
  16. 8 Понятный main public static void main(String[] args) { try

    { Params params = null; try { params = ParamsParser.parse(args); } catch (ParseException e) { System.exit(2); } Business.doSomething(params); } catch (Exception e) { System.out.println("Unexpected error occurred: "+e.getMessage()); System.exit(3); } }
  17. 8 Понятный main public static void main(String[] args) { try

    { Params params = null; try { params = ParamsParser.parse(args); } catch (ParseException e) { System.exit(2); } Business.doSomething(params); } catch (Exception e) { System.out.println("Unexpected error occurred: "+e.getMessage()); System.exit(3); } }
  18. 8 Понятный main public static void main(String[] args) { try

    { Params params = null; try { params = ParamsParser.parse(args); } catch (ParseException e) { System.exit(2); } Business.doSomething(params); } catch (Exception e) { System.out.println("Unexpected error occurred: "+e.getMessage()); System.exit(3); } }
  19. 8 Понятный main public static void main(String[] args) { try

    { Params params = null; try { params = ParamsParser.parse(args); } catch (ParseException e) { System.exit(2); } Business.doSomething(params); } catch (Exception e) { System.out.println("Unexpected error occurred: "+e.getMessage()); System.exit(3); } }
  20. 10 Exceptions throw new RuntimeException("Something not available"); throw new NotAvailableException(something

    + " not available"); public void readNextLine() { try { currentLine = reader.readLine(); } catch (IOException e) { e.printStackTrace(); } }
  21. 11 Не стройте логику на исключениях public interface UserRegistry {

    User findUser(String name); User getUser(String name) throws UserNotFoundException; Optional<User> optionalUser(String name); } Это не удобно, сложно контролировать и не эффективно
  22. 11 Не стройте логику на исключениях public interface UserRegistry {

    User findUser(String name); User getUser(String name) throws UserNotFoundException; Optional<User> optionalUser(String name); } User user = usersRegistry.findUser(userName); if (user == null) throw new AccessDeniedException("User "+userName+" not registered"); Это не удобно, сложно контролировать и не эффективно
  23. 11 Не стройте логику на исключениях public interface UserRegistry {

    User findUser(String name); User getUser(String name) throws UserNotFoundException; Optional<User> optionalUser(String name); } User user = usersRegistry.findUser(userName); if (user == null) throw new AccessDeniedException("User "+userName+" not registered"); User user = usersRegistry.findUser(userName); if (user == null) user = User.UNKNOWN; Это не удобно, сложно контролировать и не эффективно
  24. public interface UserRegistry { User findUser(String name); User getUser(String name)

    throws UserNotFoundException; Optional<User> optionalUser(String name); } 12 Не стройте логику на исключениях try { User user = usersRegistry.getUser(userName); // do some business } catch (UserNotFoundException e) { throw new AccessDeniedException(e); } Это не удобно, сложно контролировать и не эффективно
  25. public interface UserRegistry { @Nullable User findUser(String name); User getUser(String

    name) throws UserNotFoundException; Optional<User> optionalUser(String name); } 12 Не стройте логику на исключениях try { User user = usersRegistry.getUser(userName); // do some business } catch (UserNotFoundException e) { throw new AccessDeniedException(e); } Это не удобно, сложно контролировать и не эффективно
  26. public interface UserRegistry { @Nullable User findUser(String name); User getUser(String

    name) throws UserNotFoundException; Optional<User> optionalUser(String name); } 12 Не стройте логику на исключениях try { User user = usersRegistry.getUser(userName); // do some business } catch (UserNotFoundException e) { throw new AccessDeniedException(e); } Это не удобно, сложно контролировать и не эффективно
  27. public interface UserRegistry { @Nullable User findUser(String name); User getUser(String

    name) throws UserNotFoundException; Optional<User> optionalUser(String name); } 12 Не стройте логику на исключениях try { User user = usersRegistry.getUser(userName); // do some business } catch (UserNotFoundException e) { throw new AccessDeniedException(e); } Это не удобно, сложно контролировать и не эффективно
  28. public interface UserRegistry { @Nullable User findUser(String name); User getUser(String

    name) throws UserNotFoundException; Optional<User> optionalUser(String name); } 13 Не стройте логику на исключениях try { User user = usersRegistry.getUser(userName); // do some business // ... // ... // ... // ... // ... // ... // ... // ... // ... Это не удобно, сложно контролировать и не эффективно
  29. public interface UserRegistry { @Nullable User findUser(String name); User getUser(String

    name) throws UserNotFoundException; Optional<User> optionalUser(String name); } 14 Не стройте логику на исключениях User user; try { user = usersRegistry.getUser(userName); } catch (UserNotFoundException e) { user = User.UNKNOWN; } // do some business Это не удобно, сложно контролировать и не эффективно
  30. 15 Не стройте логику на исключениях Это не удобно, сложно

    контролировать и не эффективно User user = usersRegistry.optionalUser(userName) .orElseThrow(()->new AccessDeniedException("User "+userName+" not registered")); User user = usersRegistry.optionalUser(userName).orElse(User.UNKNOWN);
  31. 15 Не стройте логику на исключениях Это не удобно, сложно

    контролировать и не эффективно User user = usersRegistry.optionalUser(userName) .orElseThrow(()->new AccessDeniedException("User "+userName+" not registered")); User user = usersRegistry.optionalUser(userName).orElse(User.UNKNOWN); ifTest 519 869 510 ops/s optionalsTest 481 402 072 ops/s noStackTest 166 561 722 ops/s exceptionTest 3 098 562 ops/s
  32. 15 Не стройте логику на исключениях Это не удобно, сложно

    контролировать и не эффективно User user = usersRegistry.optionalUser(userName) .orElseThrow(()->new AccessDeniedException("User "+userName+" not registered")); User user = usersRegistry.optionalUser(userName).orElse(User.UNKNOWN); ifTest 519 869 510 ops/s optionalsTest 481 402 072 ops/s noStackTest 166 561 722 ops/s exceptionTest 3 098 562 ops/s
  33. 15 Не стройте логику на исключениях Это не удобно, сложно

    контролировать и не эффективно User user = usersRegistry.optionalUser(userName) .orElseThrow(()->new AccessDeniedException("User "+userName+" not registered")); User user = usersRegistry.optionalUser(userName).orElse(User.UNKNOWN); ifTest 519 869 510 ops/s optionalsTest 481 402 072 ops/s noStackTest 166 561 722 ops/s exceptionTest 3 098 562 ops/s 8%
  34. 15 Не стройте логику на исключениях Это не удобно, сложно

    контролировать и не эффективно User user = usersRegistry.optionalUser(userName) .orElseThrow(()->new AccessDeniedException("User "+userName+" not registered")); User user = usersRegistry.optionalUser(userName).orElse(User.UNKNOWN); ifTest 519 869 510 ops/s optionalsTest 481 402 072 ops/s noStackTest 166 561 722 ops/s exceptionTest 3 098 562 ops/s 8% 3x
  35. 15 Не стройте логику на исключениях Это не удобно, сложно

    контролировать и не эффективно User user = usersRegistry.optionalUser(userName) .orElseThrow(()->new AccessDeniedException("User "+userName+" not registered")); User user = usersRegistry.optionalUser(userName).orElse(User.UNKNOWN); ifTest 519 869 510 ops/s optionalsTest 481 402 072 ops/s noStackTest 166 561 722 ops/s exceptionTest 3 098 562 ops/s 8% 3x 170x
  36. 15 Не стройте логику на исключениях Это не удобно, сложно

    контролировать и не эффективно В нормальной программе в нормальной ситуации не должно возникать исключений User user = usersRegistry.optionalUser(userName) .orElseThrow(()->new AccessDeniedException("User "+userName+" not registered")); User user = usersRegistry.optionalUser(userName).orElse(User.UNKNOWN); ifTest 519 869 510 ops/s optionalsTest 481 402 072 ops/s noStackTest 166 561 722 ops/s exceptionTest 3 098 562 ops/s 8% 3x 170x
  37. while (!Thread.currentThread().isInterrupted()) { try { doSomeBusiness(); } catch (AppError e)

    { log.error("Couldn’t do something", e); } } 16 Исключения в долгоживущих потоках
  38. while (!Thread.currentThread().isInterrupted()) { try { doSomeBusiness(); } catch (AppError e)

    { log.error("Couldn’t do something", e); } } 16 Исключения в долгоживущих потоках
  39. while (!Thread.currentThread().isInterrupted()) { try { doSomeBusiness(); } catch (AppError e)

    { log.error("Couldn’t do something", e); } } 16 Исключения в долгоживущих потоках Thread thread = new Thread(()->{/* while !interrupted */}); thread.start();
  40. while (!Thread.currentThread().isInterrupted()) { try { doSomeBusiness(); } catch (AppError e)

    { log.error("Couldn’t do something", e); } } 16 Исключения в долгоживущих потоках Thread thread = new Thread(()->{/* while !interrupted */}); thread.start(); Future<?> f = pool.submit(()->{/* while !interrupted */}); pool.shutdown();
  41. while (!Thread.currentThread().isInterrupted()) { try { doSomeBusiness(); } catch (AppError e)

    { log.error("Couldn’t do something", e); } } 16 Исключения в долгоживущих потоках Thread thread = new Thread(()->{/* while !interrupted */}); thread.start(); CompletableFuture.runAsync(()->{/* while !interrupted */}, pool) .whenComplete((aVoid, err) -> { if (err != null) { log.error("Error occurred", err); } }); Future<?> f = pool.submit(()->{/* while !interrupted */}); pool.shutdown();
  42. 17 Исключения в долгоживущих потоках while (!Thread.currentThread().isInterrupted()) { try {

    doSomeBusiness(); } catch (AppError e) { log.error("Couldn’t do something", e); } catch (Exception e) { log.error("Got unexpected error", e); } }
  43. 18 Исключения в долгоживущих потоках try { while (!Thread.currentThread().isInterrupted()) {

    try { doSomeBusiness(); } catch (AppError e) { log.error("Couldn’t do something", e); } } } catch (Exception e) { log.error("Got unexpected error", e); }
  44. 19 Исключения в долгоживущих потоках log.info("My super job started"); try

    { while (!Thread.currentThread().isInterrupted()) { try { doSomeBusiness(); } catch (AppError e) { log.error("Couldn’t do something", e); } } } catch (Exception e) { log.error("Thread for my super job got unexpected error", e); } finally { log.info("My super job finished"); }
  45. 20 InterruptedException log.info("My super job started"); try { while (!Thread.currentThread().isInterrupted())

    { try { doSomeBusiness(); } catch (AppError e) { log.error("Some task failed", e); } catch (InterruptedException e) { log.info("My super job interrupted"); break; } catch (Exception e) { log.error("Thread for my super job got unexpected error", e); } } } finally { log.info("My super job finished"); } Когд а все понятно
  46. 20 InterruptedException log.info("My super job started"); try { while (!Thread.currentThread().isInterrupted())

    { try { doSomeBusiness(); } catch (AppError e) { log.error("Some task failed", e); } catch (InterruptedException e) { log.info("My super job interrupted"); break; } catch (Exception e) { log.error("Thread for my super job got unexpected error", e); } } } finally { log.info("My super job finished"); } Когд а все понятно
  47. Что дел а ть когд а не зн а ешь

    что дел а ть? 21 InterruptedException
  48. Что дел а ть когд а не зн а ешь

    что дел а ть? 21 InterruptedException <T> T receiveData(DataDeserializer<T> deserializer) { try { return deserializer.deserialize(readLine()); } catch (InterruptedException e) { return null; } }
  49. Что дел а ть когд а не зн а ешь

    что дел а ть? 21 InterruptedException <T> T receiveData(DataDeserializer<T> deserializer) { try { return deserializer.deserialize(readLine()); } catch (InterruptedException e) { return null; } } Добавляем throws <T> T receiveData(DataDeserializer<T> deserializer) throws InterruptedException { return deserializer.deserialize(readLine()); }
  50. Что дел а ть когд а не зн а ешь

    что дел а ть? 21 InterruptedException <T> T receiveData(DataDeserializer<T> deserializer) { try { return deserializer.deserialize(readLine()); } catch (InterruptedException e) { return null; } } Добавляем throws <T> T receiveData(DataDeserializer<T> deserializer) throws InterruptedException { return deserializer.deserialize(readLine()); } <T> T receiveData(DataDeserializer<T> deserializer) { try { return deserializer.deserialize(readLine()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new ReceiveInterrupted(e); } } Бросаем свое специальное исключение
  51. Что дел а ть когд а не зн а ешь

    что дел а ть? 21 InterruptedException <T> T receiveData(DataDeserializer<T> deserializer) { try { return deserializer.deserialize(readLine()); } catch (InterruptedException e) { return null; } } Добавляем throws <T> T receiveData(DataDeserializer<T> deserializer) throws InterruptedException { return deserializer.deserialize(readLine()); } <T> T receiveData(DataDeserializer<T> deserializer) { try { return deserializer.deserialize(readLine()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new ReceiveInterrupted(e); } } Бросаем свое специальное исключение
  52. 23 Logging Уровень логирования очень важен Используем готовые библиотеки TRACE

    Трассировка вызовов функций DEBUG Более подробная информация об операциях INFO Бизнес значимая информация WARN Исправляемые и некритичные ситуации ERROR Не ожидаемые исключения и другие ошибки FATAL Совсем все плохо - рестарт приложения
  53. 24 Logging: exceptions Exception log.error("Unexpected error", e); KnownException log.error("Known error",

    e);
 log.warn(e.getMessage()); Stack trace нужен не всегда
  54. 24 Logging: exceptions Exception log.error("Unexpected error", e); KnownException log.error("Known error",

    e);
 log.warn(e.getMessage()); AppException if (e.traceRequired()) log.error("Oops.", e); else log.error(()->"Couldn’t do something." + " Error: " + e.getMessage()); Stack trace нужен не всегда
  55. 24 Logging: exceptions Exception log.error("Unexpected error", e); KnownException log.error("Known error",

    e);
 log.warn(e.getMessage()); AppException if (e.traceRequired()) log.error("Oops.", e); else log.error(()->"Couldn’t do something." + " Error: " + e.getMessage()); AppWarning extends AppException log.warn(e.getMessage()); Stack trace нужен не всегда
  56. 24 Logging: exceptions Exception log.error("Unexpected error", e); KnownException log.error("Known error",

    e);
 log.warn(e.getMessage()); AppException if (e.traceRequired()) log.error("Oops.", e); else log.error(()->"Couldn’t do something." + " Error: " + e.getMessage()); AppWarning extends AppException log.warn(e.getMessage()); Stack trace нужен не всегда log.warn(e);
  57. 24 Logging: exceptions Exception log.error("Unexpected error", e); KnownException log.error("Known error",

    e);
 log.warn(e.getMessage()); AppException if (e.traceRequired()) log.error("Oops.", e); else log.error(()->"Couldn’t do something." + " Error: " + e.getMessage()); AppWarning extends AppException log.warn(e.getMessage()); Stack trace нужен не всегда Хорошая иерархия исключений упрощает жизнь log.warn(e);
  58. 26 Input/Output try { FileReader fileReader = new FileReader(inFile); BufferedReader

    bufferedReader = new BufferedReader(fileReader); FileWriter fileWriter = new FileWriter(outFile); PrintWriter printWriter = new PrintWriter(fileWriter); while (true) { String line = bufferedReader.readLine(); if (line == null) break; String result = doSomething(line); printWriter.println(result); } printWriter.close(); fileWriter.close(); bufferedReader.close(); fileReader.close(); } catch (IOException e) { e.printStackTrace(); }
  59. 26 Input/Output try { FileReader fileReader = new FileReader(inFile); BufferedReader

    bufferedReader = new BufferedReader(fileReader); FileWriter fileWriter = new FileWriter(outFile); PrintWriter printWriter = new PrintWriter(fileWriter); while (true) { String line = bufferedReader.readLine(); if (line == null) break; String result = doSomething(line); printWriter.println(result); } printWriter.close(); fileWriter.close(); bufferedReader.close(); fileReader.close(); } catch (IOException e) { e.printStackTrace(); }
  60. 26 Input/Output try { FileReader fileReader = new FileReader(inFile); BufferedReader

    bufferedReader = new BufferedReader(fileReader); FileWriter fileWriter = new FileWriter(outFile); PrintWriter printWriter = new PrintWriter(fileWriter); while (true) { String line = bufferedReader.readLine(); if (line == null) break; String result = doSomething(line); printWriter.println(result); } printWriter.close(); fileWriter.close(); bufferedReader.close(); fileReader.close(); } catch (IOException e) { e.printStackTrace(); }
  61. 26 Input/Output try { FileReader fileReader = new FileReader(inFile); BufferedReader

    bufferedReader = new BufferedReader(fileReader); FileWriter fileWriter = new FileWriter(outFile); PrintWriter printWriter = new PrintWriter(fileWriter); while (true) { String line = bufferedReader.readLine(); if (line == null) break; String result = doSomething(line); printWriter.println(result); } printWriter.close(); fileWriter.close(); bufferedReader.close(); fileReader.close(); } catch (IOException e) { e.printStackTrace(); }
  62. 27 I/O: try-with-resources try (var reader = new BufferedReader(new FileReader(inFile));

    var writer = new BufferedWriter(new FileWriter(outFile))) { String line = null; while ((line = reader.readLine()) != null) { String result = doSomething(line); writer.write(result); writer.newLine(); } } catch (IOException e) { log.error(()->"Error processing data from "+inFile+" to "+outFile, e); }
  63. 27 I/O: try-with-resources try (var reader = new BufferedReader(new FileReader(inFile));

    var writer = new BufferedWriter(new FileWriter(outFile))) { String line = null; while ((line = reader.readLine()) != null) { String result = doSomething(line); writer.write(result); writer.newLine(); } } catch (IOException e) { log.error(()->"Error processing data from "+inFile+" to "+outFile, e); }
  64. 27 I/O: try-with-resources try (var reader = new BufferedReader(new FileReader(inFile));

    var writer = new BufferedWriter(new FileWriter(outFile))) { String line = null; while ((line = reader.readLine()) != null) { String result = doSomething(line); writer.write(result); writer.newLine(); } } catch (IOException e) { log.error(()->"Error processing data from "+inFile+" to "+outFile, e); }
  65. 28 public class MySocketReader implements AutoCloseable { private final String

    id; private final Socket sock; private final BufferedReader reader; // ... @Override public void close() { try { reader.close(); } catch (IOException e) { log.warn(()->"Couldn't close reader "+id+". Error: "+e.getMessage()); } try { sock.close(); } catch (IOException e) { log.warn(()->"Couldn't close socket "+id+". Error: "+e.getMessage()); } } } I/O: Autocloseable
  66. 28 public class MySocketReader implements AutoCloseable { private final String

    id; private final Socket sock; private final BufferedReader reader; // ... @Override public void close() { try { reader.close(); } catch (IOException e) { log.warn(()->"Couldn't close reader "+id+". Error: "+e.getMessage()); } try { sock.close(); } catch (IOException e) { log.warn(()->"Couldn't close socket "+id+". Error: "+e.getMessage()); } } } I/O: Autocloseable
  67. 28 public class MySocketReader implements AutoCloseable { private final String

    id; private final Socket sock; private final BufferedReader reader; // ... @Override public void close() { try { reader.close(); } catch (IOException e) { log.warn(()->"Couldn't close reader "+id+". Error: "+e.getMessage()); } try { sock.close(); } catch (IOException e) { log.warn(()->"Couldn't close socket "+id+". Error: "+e.getMessage()); } } } I/O: Autocloseable
  68. 30 I/O: charsets Сharset для Reader/Writer java.nio.* использует UTF-8 по

    умолчанию java.io.* кодировка операционной системы UTF-8 с JDK 18
  69. 33 Именование package readingDataFromFiles; public final class StringFileBuffer implements DataFileStack<String>

    {
 private final String fileName; private final BufferedReader bufferedReader; public String peek() { … } public String pop() { … } }
  70. 33 Именование package readingDataFromFiles; public final class StringFileBuffer implements DataFileStack<String>

    {
 private final String fileName; private final BufferedReader bufferedReader; public String peek() { … } public String pop() { … } }
  71. 33 Именование package readingDataFromFiles; public final class StringFileBuffer implements DataFileStack<String>

    {
 private final String fileName; private final BufferedReader bufferedReader; public String peek() { … } public String pop() { … } }
  72. 33 Именование package readingDataFromFiles; public final class StringFileBuffer implements DataFileStack<String>

    {
 private final String fileName; private final BufferedReader bufferedReader; public String peek() { … } public String pop() { … } }
  73. 34 Именование Объект - существительные и прилагательные Parameter, BaseModel, CommonShape

    https:/ /www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html Try to keep your class names simple and descriptive AbstractInterruptibleBatchPreparedStatementSetter
  74. 34 Именование Объект - существительные и прилагательные Субъект - существительные

    и причастия Parameter, BaseModel, CommonShape Parser, BufferedReader, MergingSorter https:/ /www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html Try to keep your class names simple and descriptive AbstractInterruptibleBatchPreparedStatementSetter
  75. 34 Именование Объект - существительные и прилагательные Субъект - существительные

    и причастия Parameter, BaseModel, CommonShape Parser, BufferedReader, MergingSorter MergeSort MergeSorting FilesMergeSorterApp MergeSortFiles SortedFiles https:/ /www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html Try to keep your class names simple and descriptive AbstractInterruptibleBatchPreparedStatementSetter
  76. 34 Именование Объект - существительные и прилагательные Субъект - существительные

    и причастия Parameter, BaseModel, CommonShape Parser, BufferedReader, MergingSorter Методы - сначала глагол timerStart, startTimer, fileOpen, openFile MergeSort MergeSorting FilesMergeSorterApp MergeSortFiles SortedFiles https:/ /www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html Try to keep your class names simple and descriptive AbstractInterruptibleBatchPreparedStatementSetter
  77. 34 Именование Объект - существительные и прилагательные Субъект - существительные

    и причастия Parameter, BaseModel, CommonShape Parser, BufferedReader, MergingSorter Если метод что-то вычисляет, не используй get Методы - сначала глагол timerStart, startTimer, fileOpen, openFile getTotal(), computeTotal(), getLinesCount(), countLines(), calculateSum() MergeSort MergeSorting FilesMergeSorterApp MergeSortFiles SortedFiles https:/ /www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html Try to keep your class names simple and descriptive AbstractInterruptibleBatchPreparedStatementSetter
  78. 34 Именование Объект - существительные и прилагательные Субъект - существительные

    и причастия Parameter, BaseModel, CommonShape Parser, BufferedReader, MergingSorter Если метод что-то вычисляет, не используй get Методы - сначала глагол timerStart, startTimer, fileOpen, openFile getTotal(), computeTotal(), getLinesCount(), countLines(), calculateSum() MergeSort MergeSorting FilesMergeSorterApp MergeSortFiles SortedFiles https:/ /www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html Коллекции - множественное число или единственное число + тип контейнера Users, ParameterMap, TimerRegistry, MessageQueue Try to keep your class names simple and descriptive AbstractInterruptibleBatchPreparedStatementSetter
  79. Коллекции 36 get add contains next remove ArrayList O(1) O(1)*

    O(n) O(1) O(n) LinkedList* O(n) O(1) O(n) O(1) O(n)* CopyOnWriteArrayList O(1) O(n) O(n) O(1) O(n) Lists add contains next HashSet O(1)* O(1) O(capacity/n) LinkedHashSet O(1)* O(1) O(1) CopyOnWriteArraySet O(n) O(n) O(1) EnumSet O(1) O(1) O(1) TreeSet O(log n) O(log n) O(log n) ConcurrentSkipListSet O(log n) O(log n) O(1) Sets
  80. Коллекции 36 Коллекции: вычислительная сложность get add contains next remove

    ArrayList O(1) O(1)* O(n) O(1) O(n) LinkedList* O(n) O(1) O(n) O(1) O(n)* CopyOnWriteArrayList O(1) O(n) O(n) O(1) O(n) Lists add contains next HashSet O(1)* O(1) O(capacity/n) LinkedHashSet O(1)* O(1) O(1) CopyOnWriteArraySet O(n) O(n) O(1) EnumSet O(1) O(1) O(1) TreeSet O(log n) O(log n) O(log n) ConcurrentSkipListSet O(log n) O(log n) O(1) Sets
  81. Коллекции 36 Коллекции: вычислительная сложность get add contains next remove

    ArrayList O(1) O(1)* O(n) O(1) O(n) LinkedList* O(n) O(1) O(n) O(1) O(n)* CopyOnWriteArrayList O(1) O(n) O(n) O(1) O(n) Lists add contains next HashSet O(1)* O(1) O(capacity/n) LinkedHashSet O(1)* O(1) O(1) CopyOnWriteArraySet O(n) O(n) O(1) EnumSet O(1) O(1) O(1) TreeSet O(log n) O(log n) O(log n) ConcurrentSkipListSet O(log n) O(log n) O(1) Sets
  82. Коллекции 36 Коллекции: вычислительная сложность get add contains next remove

    ArrayList O(1) O(1)* O(n) O(1) O(n) LinkedList* O(n) O(1) O(n) O(1) O(n)* CopyOnWriteArrayList O(1) O(n) O(n) O(1) O(n) Lists add contains next HashSet O(1)* O(1) O(capacity/n) LinkedHashSet O(1)* O(1) O(1) CopyOnWriteArraySet O(n) O(n) O(1) EnumSet O(1) O(1) O(1) TreeSet O(log n) O(log n) O(log n) ConcurrentSkipListSet O(log n) O(log n) O(1) Sets
  83. Коллекции 36 Коллекции: вычислительная сложность get add contains next remove

    ArrayList O(1) O(1)* O(n) O(1) O(n) LinkedList* O(n) O(1) O(n) O(1) O(n)* CopyOnWriteArrayList O(1) O(n) O(n) O(1) O(n) Lists add contains next HashSet O(1)* O(1) O(capacity/n) LinkedHashSet O(1)* O(1) O(1) CopyOnWriteArraySet O(n) O(n) O(1) EnumSet O(1) O(1) O(1) TreeSet O(log n) O(log n) O(log n) ConcurrentSkipListSet O(log n) O(log n) O(1) Sets
  84. Коллекции 36 Коллекции: вычислительная сложность get add contains next remove

    ArrayList O(1) O(1)* O(n) O(1) O(n) LinkedList* O(n) O(1) O(n) O(1) O(n)* CopyOnWriteArrayList O(1) O(n) O(n) O(1) O(n) Lists add contains next HashSet O(1)* O(1) O(capacity/n) LinkedHashSet O(1)* O(1) O(1) CopyOnWriteArraySet O(n) O(n) O(1) EnumSet O(1) O(1) O(1) TreeSet O(log n) O(log n) O(log n) ConcurrentSkipListSet O(log n) O(log n) O(1) Sets hashCode()
  85. 37 Коллекции: вычислительная сложность put/get/rm containsKey next HashMap O(1) O(1)

    O(capacity/n) LinkedHashMap O(1) O(1) O(1) IdentityHashMap O(1) O(1) O(capacity/n) EnumMap O(1) O(1) O(1) TreeMap O(log n) O(log n) O(log n) ConcurrentHashMap O(1) O(1) O(capacity/n) ConcurrentSkipListMap O(log n) O(log n) O(1) Maps
  86. 37 Коллекции: вычислительная сложность put/get/rm containsKey next HashMap O(1) O(1)

    O(capacity/n) LinkedHashMap O(1) O(1) O(1) IdentityHashMap O(1) O(1) O(capacity/n) EnumMap O(1) O(1) O(1) TreeMap O(log n) O(log n) O(log n) ConcurrentHashMap O(1) O(1) O(capacity/n) ConcurrentSkipListMap O(log n) O(log n) O(1) Maps
  87. 37 Коллекции: вычислительная сложность put/get/rm containsKey next HashMap O(1) O(1)

    O(capacity/n) LinkedHashMap O(1) O(1) O(1) IdentityHashMap O(1) O(1) O(capacity/n) EnumMap O(1) O(1) O(1) TreeMap O(log n) O(log n) O(log n) ConcurrentHashMap O(1) O(1) O(capacity/n) ConcurrentSkipListMap O(log n) O(log n) O(1) Maps LRU
  88. 37 Коллекции: вычислительная сложность put/get/rm containsKey next HashMap O(1) O(1)

    O(capacity/n) LinkedHashMap O(1) O(1) O(1) IdentityHashMap O(1) O(1) O(capacity/n) EnumMap O(1) O(1) O(1) TreeMap O(log n) O(log n) O(log n) ConcurrentHashMap O(1) O(1) O(capacity/n) ConcurrentSkipListMap O(log n) O(log n) O(1) Maps LRU new LinkedHashMap<>(maxSize*4/3, 0.75f, true) { protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > maxSize; } };
  89. 38 Коллекции: вычислительная сложность offer peek poll size PriorityQueue O(log

    n) O(1) O(log n) O(1) ConcurrentLinkedQueue O(1) O(1) O(1) O(n) ArrayBlockingQueue O(1) O(1) O(1) O(1) LinkedBlockingQueue O(1) O(1) O(1) O(1) PriorityBlockingQueue O(log n) O(1) O(log n) O(1) DelayQueue O(log n) O(1) O(log n) O(1) LinkedList O(1) O(1) O(1) O(1) ArrayDeque O(1)* O(1) O(1) O(1) LinkedBlockingDeque O(1) O(1) O(1) O(1) Queue
  90. 38 Коллекции: вычислительная сложность offer peek poll size PriorityQueue O(log

    n) O(1) O(log n) O(1) ConcurrentLinkedQueue O(1) O(1) O(1) O(n) ArrayBlockingQueue O(1) O(1) O(1) O(1) LinkedBlockingQueue O(1) O(1) O(1) O(1) PriorityBlockingQueue O(log n) O(1) O(log n) O(1) DelayQueue O(log n) O(1) O(log n) O(1) LinkedList O(1) O(1) O(1) O(1) ArrayDeque O(1)* O(1) O(1) O(1) LinkedBlockingDeque O(1) O(1) O(1) O(1) Queue
  91. 38 Коллекции: вычислительная сложность offer peek poll size PriorityQueue O(log

    n) O(1) O(log n) O(1) ConcurrentLinkedQueue O(1) O(1) O(1) O(n) ArrayBlockingQueue O(1) O(1) O(1) O(1) LinkedBlockingQueue O(1) O(1) O(1) O(1) PriorityBlockingQueue O(log n) O(1) O(log n) O(1) DelayQueue O(log n) O(1) O(log n) O(1) LinkedList O(1) O(1) O(1) O(1) ArrayDeque O(1)* O(1) O(1) O(1) LinkedBlockingDeque O(1) O(1) O(1) O(1) Queue isEmpty()
  92. 38 Коллекции: вычислительная сложность offer peek poll size PriorityQueue O(log

    n) O(1) O(log n) O(1) ConcurrentLinkedQueue O(1) O(1) O(1) O(n) ArrayBlockingQueue O(1) O(1) O(1) O(1) LinkedBlockingQueue O(1) O(1) O(1) O(1) PriorityBlockingQueue O(log n) O(1) O(log n) O(1) DelayQueue O(log n) O(1) O(log n) O(1) LinkedList O(1) O(1) O(1) O(1) ArrayDeque O(1)* O(1) O(1) O(1) LinkedBlockingDeque O(1) O(1) O(1) O(1) Queue Bounded isEmpty()
  93. 38 Коллекции: вычислительная сложность offer peek poll size PriorityQueue O(log

    n) O(1) O(log n) O(1) ConcurrentLinkedQueue O(1) O(1) O(1) O(n) ArrayBlockingQueue O(1) O(1) O(1) O(1) LinkedBlockingQueue O(1) O(1) O(1) O(1) PriorityBlockingQueue O(log n) O(1) O(log n) O(1) DelayQueue O(log n) O(1) O(log n) O(1) LinkedList O(1) O(1) O(1) O(1) ArrayDeque O(1)* O(1) O(1) O(1) LinkedBlockingDeque O(1) O(1) O(1) O(1) Queue Bounded isEmpty()
  94. 40 Stream API Модно и красиво В одну строку простые

    кейсы Удобно делать преобразования/фильтрацию
  95. 40 Stream API Модно и красиво В одну строку простые

    кейсы Удобно делать преобразования/фильтрацию Унифицированное API
  96. 40 Stream API Модно и красиво В одну строку простые

    кейсы Удобно делать преобразования/фильтрацию Унифицированное API Есть вопросы с производительностью
  97. 40 Stream API Модно и красиво В одну строку простые

    кейсы Удобно делать преобразования/фильтрацию Унифицированное API Есть вопросы с производительностью Имеются нюансы с checked исключениями
  98. 41 Stream API: checked exceptions public void generateData(Supplier<String> generator, File

    outFile) throws IOException { try (var writer = new BufferedWriter(new FileWriter(outFile))) { IntStream.range(0, 500000).forEach(x -> { try { writer.append(generator.get()).append(System.lineSeparator()); } catch (IOException e) { throw new RuntimeException(e); } }); } }
  99. 41 Stream API: checked exceptions public void generateData(Supplier<String> generator, File

    outFile) throws IOException { try (var writer = new BufferedWriter(new FileWriter(outFile))) { IntStream.range(0, 500000).forEach(x -> { try { writer.append(generator.get()).append(System.lineSeparator()); } catch (IOException e) { throw new RuntimeException(e); } }); } } UncheckedIOException
  100. 42 Stream API: checked exceptions public void generateData(Supplier<String> generator, File

    outFile) throws IOException { try (var writer = new BufferedWriter(new FileWriter(outFile))) { for (int i = 0; i < 500000; i++) { writer.append(generator.get()).append(System.lineSeparator()); } } }
  101. 43 Stream API: resources public void mergeFiles(Path destination, Collection<Path> sources)

    { var readers = sources.stream() .map(path -> { try { return Files.newBufferedReader(path); } catch (IOException e) { throw new AppError("Couldn't open source file " + path, e); } }) .toList(); // ... }
  102. 43 Stream API: resources public void mergeFiles(Path destination, Collection<Path> sources)

    { var readers = sources.stream() .map(path -> { try { return Files.newBufferedReader(path); } catch (IOException e) { throw new AppError("Couldn't open source file " + path, e); } }) .toList(); // ... }
  103. 43 Stream API: resources public void mergeFiles(Path destination, Collection<Path> sources)

    { var readers = sources.stream() .map(path -> { try { return Files.newBufferedReader(path); } catch (IOException e) { throw new AppError("Couldn't open source file " + path, e); } }) .toList(); // ... }
  104. 44 Stream API: resources public void mergeFiles(Path destination, Collection<Path> sources)

    { var readers = new ArrayList<BufferedReader>(sources.size()); for (Path path : sources) { try { readers.add(Files.newBufferedReader(path)); } catch (IOException e) { for (BufferedReader reader : readers) { try { reader.close(); } catch (IOException ioe) { e.addSuppressed(ioe); } } throw new AppError("Couldn't open source file " + path, e); } } // ... }
  105. 44 Stream API: resources public void mergeFiles(Path destination, Collection<Path> sources)

    { var readers = new ArrayList<BufferedReader>(sources.size()); for (Path path : sources) { try { readers.add(Files.newBufferedReader(path)); } catch (IOException e) { for (BufferedReader reader : readers) { try { reader.close(); } catch (IOException ioe) { e.addSuppressed(ioe); } } throw new AppError("Couldn't open source file " + path, e); } } // ... }
  106. 44 Stream API: resources public void mergeFiles(Path destination, Collection<Path> sources)

    { var readers = new ArrayList<BufferedReader>(sources.size()); for (Path path : sources) { try { readers.add(Files.newBufferedReader(path)); } catch (IOException e) { for (BufferedReader reader : readers) { try { reader.close(); } catch (IOException ioe) { e.addSuppressed(ioe); } } throw new AppError("Couldn't open source file " + path, e); } } // ... }
  107. 44 Stream API: resources public void mergeFiles(Path destination, Collection<Path> sources)

    { var readers = new ArrayList<BufferedReader>(sources.size()); for (Path path : sources) { try { readers.add(Files.newBufferedReader(path)); } catch (IOException e) { for (BufferedReader reader : readers) { try { reader.close(); } catch (IOException ioe) { e.addSuppressed(ioe); } } throw new AppError("Couldn't open source file " + path, e); } } // ... }
  108. 44 Stream API: resources public void mergeFiles(Path destination, Collection<Path> sources)

    { var readers = new ArrayList<BufferedReader>(sources.size()); for (Path path : sources) { try { readers.add(Files.newBufferedReader(path)); } catch (IOException e) { for (BufferedReader reader : readers) { try { reader.close(); } catch (IOException ioe) { e.addSuppressed(ioe); } } throw new AppError("Couldn't open source file " + path, e); } } // ... }
  109. 44 Stream API: resources public void mergeFiles(Path destination, Collection<Path> sources)

    { var readers = new ArrayList<BufferedReader>(sources.size()); for (Path path : sources) { try { readers.add(Files.newBufferedReader(path)); } catch (IOException e) { for (BufferedReader reader : readers) { try { reader.close(); } catch (IOException ioe) { e.addSuppressed(ioe); } } throw new AppError("Couldn't open source file " + path, e); } } // ... }
  110. 46 NPE requireNonNull(T obj) requireNonNull(T obj, String message) requireNonNull(T obj,

    Supplier<String> messageSupplier) Проверка на null requireNonNullElse(T obj, T defaultObj) requireNonNullElseGet(T obj, Supplier<? extends T> supplier) Значение по умолчанию isNull(Object obj) nonNull(Object obj) Используем в предикатах equals(Object a, Object b) deepEquals(Object a, Object b) Сравнения с учетом null hashCode(Object o) hash(Object... values) toString(Object o) toString(Object o, String nullDefault) compare(T a, T b, Comparator<? super T> c) И много другое 
 со всеми проверкaми Удобно и безопасно
  111. 46 NPE requireNonNull(T obj) requireNonNull(T obj, String message) requireNonNull(T obj,

    Supplier<String> messageSupplier) Проверка на null requireNonNullElse(T obj, T defaultObj) requireNonNullElseGet(T obj, Supplier<? extends T> supplier) Значение по умолчанию isNull(Object obj) nonNull(Object obj) Используем в предикатах equals(Object a, Object b) deepEquals(Object a, Object b) Сравнения с учетом null hashCode(Object o) hash(Object... values) toString(Object o) toString(Object o, String nullDefault) compare(T a, T b, Comparator<? super T> c) И много другое 
 со всеми проверкaми : Objects Удобно и безопасно