Blame
| bc5a4a | 강세보 | 2026-01-13 10:07:52 | 1 | # Java New Features |
| 2 | ||||
| 3 | Java 9 부터 코딩 스타일 위주로 변경된 내용을 기술한다. Preview 는 제외했고 정식 기능으로 들어간 것 위주라 실질적으로 몇몇 버전이 빠져있다. |
|||
| 4 | ||||
| 5 | 코딩 스타일 외에 가장 큰 변화는 Java 21 의 Virtual Thread 를 들 수 있다. 기존의 Java thread 가 OS 의 thread 와 1:1 매핑이었다면, Virtual thread 는 go 의 coroutine 처럼 software thread 라고 할 수 있다. 많은 경우 그렇지만 virtual thread 도 만능은 아니다. 즉, 기존 thread 에서 세분화해야 하는 경우만 도입이 고려되지 그렇지 않은 경우는 software 로 OS thread 매핑이 한 번 더 되는 꼴이라 딱히 성능 이득을 볼 수 없을 수 있다. |
|||
| 6 | ||||
| 7 | # JDK 10 |
|||
| 8 | ||||
| 9 | ## 로컬 변수 타입 유추 |
|||
| 10 | ||||
| 11 | 아래와 같이 var 로 선언하고 변수에 타입을 유추하는 형태 |
|||
| 12 | ||||
| 13 | ```java |
|||
| 14 | var list = new ArrayList<String>(); // infers ArrayList<String> |
|||
| 15 | var stream = list.stream(); // infers Stream<String> |
|||
| 16 | ``` |
|||
| 17 | ||||
| 18 | # JDK 11 |
|||
| 19 | ||||
| 20 | ## 로컬 변수 Lamba |
|||
| 21 | ||||
| 22 | 암묵적인 타입 선언으로 람다를 구현하는 경우 |
|||
| 23 | ```java |
|||
| 24 | (var x, var y) -> x.process(y) |
|||
| 25 | ``` |
|||
| 26 | ||||
| 27 | ||||
| 28 | 동일 형태 |
|||
| 29 | ```java |
|||
| 30 | (x, y) -> x.process(y) |
|||
| 31 | ``` |
|||
| 32 | ||||
| 33 | 섞어서는 못 쓴다. |
|||
| 34 | ||||
| 35 | ```java |
|||
| 36 | (var x, y) -> x.process(y) // Cannot mix 'var' and 'no var' in implicitly typed lambda expression |
|||
| 37 | (var x, int y) -> x.process(y) // Cannot mix 'var' and manifest types in explicitly typed lambda expression |
|||
| 38 | ``` |
|||
| 39 | ||||
| 40 | # JDK 14 |
|||
| 41 | ||||
| 42 | ## Switch 구문 |
|||
| 43 | ||||
| 44 | 축약해서 사용 가능 |
|||
| 45 | ||||
| 46 | ```java |
|||
| 47 | switch (day) { |
|||
| 48 | case MONDAY, FRIDAY, SUNDAY -> System.out.println(6); |
|||
| 49 | case TUESDAY -> System.out.println(7); |
|||
| 50 | case THURSDAY, SATURDAY -> System.out.println(8); |
|||
| 51 | case WEDNESDAY -> System.out.println(9); |
|||
| 52 | } |
|||
| 53 | ``` |
|||
| 54 | ||||
| 55 | 변수 대입도 가능 |
|||
| 56 | ```java |
|||
| 57 | int numLetters = switch (day) { |
|||
| 58 | case MONDAY, FRIDAY, SUNDAY -> 6; |
|||
| 59 | case TUESDAY -> 7; |
|||
| 60 | case THURSDAY, SATURDAY -> 8; |
|||
| 61 | case WEDNESDAY -> 9; |
|||
| 62 | }; |
|||
| 63 | ``` |
|||
| 64 | ||||
| 65 | # JDK 15 |
|||
| 66 | ||||
| 67 | ## Text Block |
|||
| 68 | ||||
| 69 | """ 로 시작해서 """ 로 끝나는 블록 구문이다. |
|||
| 70 | ||||
| 71 | 기존 구문이 이렇다면 |
|||
| 72 | ```java |
|||
| 73 | String html = "<html>\n" + |
|||
| 74 | " <body>\n" + |
|||
| 75 | " <p>Hello, world</p>\n" + |
|||
| 76 | " </body>\n" + |
|||
| 77 | "</html>\n"; |
|||
| 78 | ``` |
|||
| 79 | ||||
| 80 | 이렇게 가능하다. |
|||
| 81 | ```java |
|||
| 82 | String html = """ |
|||
| 83 | <html> |
|||
| 84 | <body> |
|||
| 85 | <p>Hello, world</p> |
|||
| 86 | </body> |
|||
| 87 | </html> |
|||
| 88 | """; |
|||
| 89 | ``` |
|||
| 90 | ||||
| 91 | # JDK 16 |
|||
| 92 | ||||
| 93 | ## Pattern Matching for instanceof |
|||
| 94 | ||||
| 95 | 기존에 이렇게 쓰던 것을 |
|||
| 96 | ```java |
|||
| 97 | if (obj instanceof String) { |
|||
| 98 | String s = (String) obj; // grr… |
|||
| 99 | … |
|||
| 100 | } |
|||
| 101 | ``` |
|||
| 102 | ||||
| 103 | 이렇게 사용할 수 있다. |
|||
| 104 | ```java |
|||
| 105 | if (obj instanceof String s) { |
|||
| 106 | // Let pattern matching do the work! |
|||
| 107 | … |
|||
| 108 | } |
|||
| 109 | ``` |
|||
| 110 | ||||
| 111 | 근데 이게 Scope 가 묘한게 if 블럭 안에서만 유효하다. 예를 들어 |
|||
| 112 | ```java |
|||
| 113 | class Example1 { |
|||
| 114 | String s; |
|||
| 115 | ||||
| 116 | void test1(Object o) { |
|||
| 117 | if (o instanceof String s) { |
|||
| 118 | System.out.println(s); // Field s is shadowed |
|||
| 119 | s = s + "\n"; // Assignment to pattern variable |
|||
| 120 | … |
|||
| 121 | } |
|||
| 122 | System.out.println(s); // Refers to field s |
|||
| 123 | … |
|||
| 124 | } |
|||
| 125 | } |
|||
| 126 | ``` |
|||
| 127 | ||||
| 128 | 심지어 else 구문에서도 이게 유효하지 않다. |
|||
| 129 | ``` |
|||
| 130 | class Example2 { |
|||
| 131 | Point p; |
|||
| 132 | ||||
| 133 | void test2(Object o) { |
|||
| 134 | if (o instanceof Point p) { |
|||
| 135 | // p refers to the pattern variable |
|||
| 136 | … |
|||
| 137 | } else { |
|||
| 138 | // p refers to the field |
|||
| 139 | … |
|||
| 140 | } |
|||
| 141 | } |
|||
| 142 | } |
|||
| 143 | ``` |
|||
| 144 | ||||
| 145 | ||||
| 146 | ## Records |
|||
| 147 | ||||
| 148 | 데이터 구조체를 간단하게 구현한 것이다. |
|||
| 149 | 예를 들어 전통적인 방식의 데이터 구조체를 class 로 구현한 것이 다음과 같다면 |
|||
| 150 | ```java |
|||
| 151 | class Point { |
|||
| 152 | private final int x; |
|||
| 153 | private final int y; |
|||
| 154 | ||||
| 155 | Point(int x, int y) { |
|||
| 156 | this.x = x; |
|||
| 157 | this.y = y; |
|||
| 158 | } |
|||
| 159 | ||||
| 160 | int x() { return x; } |
|||
| 161 | int y() { return y; } |
|||
| 162 | ||||
| 163 | public boolean equals(Object o) { |
|||
| 164 | if (!(o instanceof Point)) return false; |
|||
| 165 | Point other = (Point) o; |
|||
| 166 | return other.x == x && other.y == y; |
|||
| 167 | } |
|||
| 168 | ||||
| 169 | public int hashCode() { |
|||
| 170 | return Objects.hash(x, y); |
|||
| 171 | } |
|||
| 172 | ||||
| 173 | public String toString() { |
|||
| 174 | return String.format("Point[x=%d, y=%d]", x, y); |
|||
| 175 | } |
|||
| 176 | } |
|||
| 177 | ``` |
|||
| 178 | ||||
| 179 | 이 기능을 다 하는 record 는 다음과 같이 정의한다. |
|||
| 180 | ```java |
|||
| 181 | record Point(int x, int y) { } |
|||
| 182 | ``` |
|||
| 183 | ||||
| 184 | 단, 이렇게 보면 좋아보이지만 이 x, y 는 초기에 값을 대입받으면 더 이상 수정할 수 없다. |
|||
| 185 | ### Constructor |
|||
| 186 | ||||
| 187 | Constructor 에서 추가 작업을 한다면 다음과 같이 사용할 수 있다. |
|||
| 188 | ```java |
|||
| 189 | record Rational(int num, int denom) { |
|||
| 190 | Rational { |
|||
| 191 | int gcd = gcd(num, denom); |
|||
| 192 | num /= gcd; |
|||
| 193 | denom /= gcd; |
|||
| 194 | } |
|||
| 195 | } |
|||
| 196 | ``` |
|||
| 197 | ||||
| 198 | 동일 코드를 명시적으로 작성하면 다음과 같다. |
|||
| 199 | ```java |
|||
| 200 | record Rational(int num, int denom) { |
|||
| 201 | Rational(int num, int demon) { |
|||
| 202 | // Normalization |
|||
| 203 | int gcd = gcd(num, denom); |
|||
| 204 | num /= gcd; |
|||
| 205 | denom /= gcd; |
|||
| 206 | // Initialization |
|||
| 207 | this.num = num; |
|||
| 208 | this.denom = denom; |
|||
| 209 | } |
|||
| 210 | } |
|||
| 211 | ``` |
|||
| 212 | ||||
| 213 | Exception 처리 Case |
|||
| 214 | ``` java |
|||
| 215 | record Range(int lo, int hi) { |
|||
| 216 | Range { |
|||
| 217 | if (lo > hi) // referring here to the implicit constructor parameters |
|||
| 218 | throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi)); |
|||
| 219 | } |
|||
| 220 | } |
|||
| 221 | ``` |
|||
| 222 | ||||
| 223 | Copy 는 다음과 같이 사용한다. 아래와 같은 record 가 있다고 하면 |
|||
| 224 | ```java |
|||
| 225 | record R(T1 c1, ..., Tn cn){ } |
|||
| 226 | ``` |
|||
| 227 | ||||
| 228 | r1 인스턴스가 먼저 있고 이게 null 이 아니면 |
|||
| 229 | ```java |
|||
| 230 | R r2 = new R(r1.c1(), r1.c2(), ..., r1.cn()); |
|||
| 231 | ``` |
|||
| 232 | ||||
| 233 | r2 가 r1의 복제이다. 이 경우 r1.equals(r2) 는 true 이다. |
|||
| 234 | ||||
| 235 | ### Rules for record classes |
|||
| 236 | ||||
| 237 | - 상속 불가. record class 의 부모는 모두 java.lang.Record 이다. sealed class 를 implements 는 가능하다. |
|||
| 238 | ```java |
|||
| 239 | package com.example.expression; |
|||
| 240 | ||||
| 241 | public sealed interface Expr |
|||
| 242 | permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {...} |
|||
| 243 | ||||
| 244 | public record ConstantExpr(int i) implements Expr {...} |
|||
| 245 | public record PlusExpr(Expr a, Expr b) implements Expr {...} |
|||
| 246 | public record TimesExpr(Expr a, Expr b) implements Expr {...} |
|||
| 247 | public record NegExpr(Expr e) implements Expr {...} |
|||
| 248 | ``` |
|||
| 249 | - record class 는 암묵적으로 final 이고 abstract 가 되지 못한다. |
|||
| 250 | - 필드들도 모두 final 이다. |
|||
| 251 | - 인스턴스 필드를 명시적으로 정의할 수 없고 필드를 초기화할 수 없다. |
|||
| 252 | ```java |
|||
| 253 | record Invalid(int x, inty) { |
|||
| 254 | int z; // 인스턴스 필드 명시화 안됨. |
|||
| 255 | int a = 1; // 명시화도 안되고 초기화도 안됨. |
|||
| 256 | } |
|||
| 257 | ``` |
|||
| 258 | ||||
| 259 | # JDK 17 |
|||
| 260 | ||||
| 261 | ## Sealed Classes |
|||
| 262 | ||||
| 263 | Sealed class 나 interface 는 상속 받을 대상을 명시한다. |
|||
| 264 | 패키지 내부인 경우 |
|||
| 265 | ```java |
|||
| 266 | package com.example.geometry; |
|||
| 267 | ||||
| 268 | public abstract sealed class Shape |
|||
| 269 | permits Circle, Rectangle, Square { ... } |
|||
| 270 | ``` |
|||
| 271 | ||||
| 272 | 다른 패키지인 경우 |
|||
| 273 | ```java |
|||
| 274 | package com.example.geometry; |
|||
| 275 | ||||
| 276 | public abstract sealed class Shape |
|||
| 277 | permits com.example.polar.Circle, |
|||
| 278 | com.example.quad.Rectangle, |
|||
| 279 | com.example.quad.simple.Square { ... } |
|||
| 280 | ``` |
|||
| 281 | ||||
| 282 | 패턴 매칭에 사용 가능하다. |
|||
| 283 | ```java |
|||
| 284 | Shape rotate(Shape shape, double angle) { |
|||
| 285 | return switch (shape) { // pattern matching switch |
|||
| 286 | case Circle c -> c; |
|||
| 287 | case Rectangle r -> shape.rotate(angle); |
|||
| 288 | case Square s -> shape.rotate(angle); |
|||
| 289 | // no default needed! |
|||
| 290 | } |
|||
| 291 | } |
|||
| 292 | ``` |
|||
| 293 | ||||
| 294 | # JDK 18 |
|||
| 295 | ||||
| 296 | ## Code Snippets in Java API Documentation |
|||
| 297 | ||||
| 298 | @snippet 태그 사용 |
|||
| 299 | ```java |
|||
| 300 | /** |
|||
| 301 | * The following code shows how to use {@code Optional.isPresent}: |
|||
| 302 | * {@snippet : |
|||
| 303 | * if (v.isPresent()) { |
|||
| 304 | * System.out.println("v: " + v.get()); |
|||
| 305 | * } |
|||
| 306 | * } |
|||
| 307 | */ |
|||
| 308 | ``` |
|||
| 309 | ||||
| 310 | 실제 코드를 당겨오는 것도 가능 |
|||
| 311 | ```java |
|||
| 312 | /** |
|||
| 313 | * The following code shows how to use {@code Optional.isPresent}: |
|||
| 314 | * {@snippet file="ShowOptional.java" region="example"} |
|||
| 315 | */ |
|||
| 316 | ``` |
|||
| 317 | ||||
| 318 | ShowOptional.java 의 내용에 region 을 정의한다. |
|||
| 319 | ```java |
|||
| 320 | public class ShowOptional { |
|||
| 321 | void show(Optional<String> v) { |
|||
| 322 | // @start region="example" |
|||
| 323 | if (v.isPresent()) { |
|||
| 324 | System.out.println("v: " + v.get()); |
|||
| 325 | } |
|||
| 326 | // @end |
|||
| 327 | } |
|||
| 328 | } |
|||
| 329 | ``` |
|||
| 330 | ||||
| 331 | 그 밖에 강조를 하거나 문자열 대치, 링크 등을 사용할 수 있다. |
|||
| 332 | ||||
| 333 | ## Deprecate Finalization for Removal |
|||
| 334 | ||||
| 335 | 다음의 코드 대신에... |
|||
| 336 | ```java |
|||
| 337 | FileInputStream input = null; |
|||
| 338 | FileOutputStream output = null; |
|||
| 339 | ||||
| 340 | try { |
|||
| 341 | input = new FileInputStream(file1); |
|||
| 342 | output = new FileOutputStream(file2); |
|||
| 343 | ... copy bytes from input to output … |
|||
| 344 | output.close(); output = null; |
|||
| 345 | input.close(); input = null; |
|||
| 346 | } finally { |
|||
| 347 | if (output != null) output.close(); |
|||
| 348 | if (input != null) input.close(); |
|||
| 349 | } |
|||
| 350 | ``` |
|||
| 351 | ||||
| 352 | try-resource-statement 와 cleaners 를 사용할 것. |
|||
| 353 | ||||
| 354 | try-resource-statement 는 Java 7 에 도입됨. |
|||
| 355 | ```java |
|||
| 356 | try (FileInputStream input = new FileInputStream(file1); |
|||
| 357 | FileOutputStream output = new FileOutputStream(file2)) { |
|||
| 358 | ... copy bytes from input to output … |
|||
| 359 | } |
|||
| 360 | ``` |
|||
| 361 | ||||
| 362 | cleaner 는 Java 9 에 도입됨. (interface 구하는 방식) |
|||
| 363 | ||||
| 364 | # JDK 21 |
|||
| 365 | ||||
| 366 | ## Sequenced Collections |
|||
| 367 | ||||
| 368 | first 와 last 에 대한 명시적인 method 를 제공한다. |
|||
| 369 | #### Sequenced collections |
|||
| 370 | ```java |
|||
| 371 | interface SequencedCollection<E> extends Collection<E> { |
|||
| 372 | // new method |
|||
| 373 | SequencedCollection<E> reversed(); |
|||
| 374 | // methods promoted from Deque |
|||
| 375 | void addFirst(E); |
|||
| 376 | void addLast(E); |
|||
| 377 | E getFirst(); |
|||
| 378 | E getLast(); |
|||
| 379 | E removeFirst(); |
|||
| 380 | E removeLast(); |
|||
| 381 | } |
|||
| 382 | ``` |
|||
| 383 | ||||
| 384 | ### Sequenced sets |
|||
| 385 | ```java |
|||
| 386 | interface SequencedSet<E> extends Set<E>, SequencedCollection<E> { |
|||
| 387 | SequencedSet<E> reversed(); // covariant override |
|||
| 388 | } |
|||
| 389 | ``` |
|||
| 390 | ||||
| 391 | ### Sequenced maps |
|||
| 392 | ```java |
|||
| 393 | interface SequencedMap<K,V> extends Map<K,V> { |
|||
| 394 | // new methods |
|||
| 395 | SequencedMap<K,V> reversed(); |
|||
| 396 | SequencedSet<K> sequencedKeySet(); |
|||
| 397 | SequencedCollection<V> sequencedValues(); |
|||
| 398 | SequencedSet<Entry<K,V>> sequencedEntrySet(); |
|||
| 399 | V putFirst(K, V); |
|||
| 400 | V putLast(K, V); |
|||
| 401 | // methods promoted from NavigableMap |
|||
| 402 | Entry<K, V> firstEntry(); |
|||
| 403 | Entry<K, V> lastEntry(); |
|||
| 404 | Entry<K, V> pollFirstEntry(); |
|||
| 405 | Entry<K, V> pollLastEntry(); |
|||
| 406 | } |
|||
| 407 | ``` |
|||
| 408 | ||||
| 409 | ### Retrofitting |
|||
| 410 | ||||
| 411 | 그래서 다음과 같이 재조정된다. |
|||
| 412 | ||||
| 413 | - List now has SequencedCollection as its immediate superinterface, |
|||
| 414 | - Deque now has SequencedCollection as its immediate superinterface, |
|||
| 415 | - LinkedHashSet additionally implements SequencedSet, |
|||
| 416 | - SortedSet now has SequencedSet as its immediate superinterface, |
|||
| 417 | - LinkedHashMap additionally implements SequencedMap, and |
|||
| 418 | - SortedMap now has SequencedMap as its immediate superinterface. |
|||
| 419 | ||||
| 420 | ## Record Patterns |
|||
| 421 | ||||
| 422 | JDK 16 의 Pattern Matching for instanceof 에 Record 확장판 |
|||
| 423 | ||||
| 424 | JDK 16 당시는 다음과 같이 사용 |
|||
| 425 | ```java |
|||
| 426 | record Point(int x, int y) {} |
|||
| 427 | ||||
| 428 | static void printSum(Object obj) { |
|||
| 429 | if (obj instanceof Point p) { |
|||
| 430 | int x = p.x(); |
|||
| 431 | int y = p.y(); |
|||
| 432 | System.out.println(x+y); |
|||
| 433 | } |
|||
| 434 | } |
|||
| 435 | ``` |
|||
| 436 | ||||
| 437 | 이걸 JDK 21에서는 다음과 같이 사용 가능하다. |
|||
| 438 | ```java |
|||
| 439 | static void printSum(Object obj) { |
|||
| 440 | if (obj instanceof Point(int x, int y)) { |
|||
| 441 | System.out.println(x+y); |
|||
| 442 | } |
|||
| 443 | } |
|||
| 444 | ``` |
|||
| 445 | ||||
| 446 | 이 때 Point(int x, inty) 를 record pattern 이라고 한다. |
|||
| 447 | ### Nested record patterns |
|||
| 448 | ||||
| 449 | 다단계인 경우 다음과 같이 사용 가능하다. |
|||
| 450 | ```java |
|||
| 451 | record Point(int x, int y) {} |
|||
| 452 | enum Color { RED, GREEN, BLUE } |
|||
| 453 | record ColoredPoint(Point p, Color c) {} |
|||
| 454 | record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {} |
|||
| 455 | ``` |
|||
| 456 | ||||
| 457 | 1단계 |
|||
| 458 | ```java |
|||
| 459 | static void printUpperLeftColoredPoint(Rectangle r) { |
|||
| 460 | if (r instanceof Rectangle(ColoredPoint ul, ColoredPoint lr)) { |
|||
| 461 | System.out.println(ul.c()); |
|||
| 462 | } |
|||
| 463 | } |
|||
| 464 | ``` |
|||
| 465 | ||||
| 466 | 2단계 |
|||
| 467 | ```java |
|||
| 468 | static void printColorOfUpperLeftPoint(Rectangle r) { |
|||
| 469 | if (r instanceof Rectangle(ColoredPoint(Point p, Color c), |
|||
| 470 | ColoredPoint lr)) { |
|||
| 471 | System.out.println(c); |
|||
| 472 | } |
|||
| 473 | } |
|||
| 474 | ``` |
|||
| 475 | ||||
| 476 | var 사용 예제 |
|||
| 477 | ```java |
|||
| 478 | Rectangle r = new Rectangle(new ColoredPoint(new Point(x1, y1), c1), |
|||
| 479 | new ColoredPoint(new Point(x2, y2), c2)); |
|||
| 480 | ||||
| 481 | static void printXCoordOfUpperLeftPointWithPatterns(Rectangle r) { |
|||
| 482 | if (r instanceof Rectangle(ColoredPoint(Point(var x, var y), var c), |
|||
| 483 | var lr)) { |
|||
| 484 | System.out.println("Upper-left corner: " + x); |
|||
| 485 | } |
|||
| 486 | } |
|||
| 487 | ``` |
|||
| 488 | ||||
| 489 | ## Pattern Matching for switch |
|||
| 490 | ||||
| 491 | ```java |
|||
| 492 | static String formatter(Object obj) { |
|||
| 493 | String formatted = "unknown"; |
|||
| 494 | if (obj instanceof Integer i) { |
|||
| 495 | formatted = String.format("int %d", i); |
|||
| 496 | } else if (obj instanceof Long l) { |
|||
| 497 | formatted = String.format("long %d", l); |
|||
| 498 | } else if (obj instanceof Double d) { |
|||
| 499 | formatted = String.format("double %f", d); |
|||
| 500 | } else if (obj instanceof String s) { |
|||
| 501 | formatted = String.format("String %s", s); |
|||
| 502 | } |
|||
| 503 | return formatted; |
|||
| 504 | } |
|||
| 505 | ``` |
|||
| 506 | ||||
| 507 | 이랬던 것을 아래와 같이 사용 가능하다. |
|||
| 508 | ```java |
|||
| 509 | static String formatterPatternSwitch(Object obj) { |
|||
| 510 | return switch (obj) { |
|||
| 511 | case Integer i -> String.format("int %d", i); |
|||
| 512 | case Long l -> String.format("long %d", l); |
|||
| 513 | case Double d -> String.format("double %f", d); |
|||
| 514 | case String s -> String.format("String %s", s); |
|||
| 515 | default -> obj.toString(); |
|||
| 516 | }; |
|||
| 517 | } |
|||
| 518 | ``` |
|||
| 519 | ||||
| 520 | ### switch 에 null |
|||
| 521 | ||||
| 522 | before |
|||
| 523 | ```java |
|||
| 524 | static void testFooBarOld(String s) { |
|||
| 525 | if (s == null) { |
|||
| 526 | System.out.println("Oops!"); |
|||
| 527 | return; |
|||
| 528 | } |
|||
| 529 | switch (s) { |
|||
| 530 | case "Foo", "Bar" -> System.out.println("Great"); |
|||
| 531 | default -> System.out.println("Ok"); |
|||
| 532 | } |
|||
| 533 | } |
|||
| 534 | ``` |
|||
| 535 | ||||
| 536 | after |
|||
| 537 | ```java |
|||
| 538 | static void testFooBarNew(String s) { |
|||
| 539 | switch (s) { |
|||
| 540 | case null -> System.out.println("Oops"); |
|||
| 541 | case "Foo", "Bar" -> System.out.println("Great"); |
|||
| 542 | default -> System.out.println("Ok"); |
|||
| 543 | } |
|||
| 544 | } |
|||
| 545 | ``` |
|||
| 546 | ||||
| 547 | ### Case refinement |
|||
| 548 | ||||
| 549 | 좀 더 나가서 (아래도 JDK 21 부터 가능) |
|||
| 550 | ```java |
|||
| 551 | static void testStringOld(String response) { |
|||
| 552 | switch (response) { |
|||
| 553 | case null -> { } |
|||
| 554 | case String s -> { |
|||
| 555 | if (s.equalsIgnoreCase("YES")) |
|||
| 556 | System.out.println("You got it"); |
|||
| 557 | else if (s.equalsIgnoreCase("NO")) |
|||
| 558 | System.out.println("Shame"); |
|||
| 559 | else |
|||
| 560 | System.out.println("Sorry?"); |
|||
| 561 | } |
|||
| 562 | } |
|||
| 563 | } |
|||
| 564 | ``` |
|||
| 565 | ||||
| 566 | 여러 가지로 가능하다. |
|||
| 567 | ```java |
|||
| 568 | static void testStringNew(String response) { |
|||
| 569 | switch (response) { |
|||
| 570 | case null -> { } |
|||
| 571 | case String s |
|||
| 572 | when s.equalsIgnoreCase("YES") -> { |
|||
| 573 | System.out.println("You got it"); |
|||
| 574 | } |
|||
| 575 | case String s |
|||
| 576 | when s.equalsIgnoreCase("NO") -> { |
|||
| 577 | System.out.println("Shame"); |
|||
| 578 | } |
|||
| 579 | case String s -> { |
|||
| 580 | System.out.println("Sorry?"); |
|||
| 581 | } |
|||
| 582 | } |
|||
| 583 | } |
|||
| 584 | ``` |
|||
| 585 | ||||
| 586 | 좀 더 늘려서... |
|||
| 587 | ```java |
|||
| 588 | static void testStringEnhanced(String response) { |
|||
| 589 | switch (response) { |
|||
| 590 | case null -> { } |
|||
| 591 | case "y", "Y" -> { |
|||
| 592 | System.out.println("You got it"); |
|||
| 593 | } |
|||
| 594 | case "n", "N" -> { |
|||
| 595 | System.out.println("Shame"); |
|||
| 596 | } |
|||
| 597 | case String s |
|||
| 598 | when s.equalsIgnoreCase("YES") -> { |
|||
| 599 | System.out.println("You got it"); |
|||
| 600 | } |
|||
| 601 | case String s |
|||
| 602 | when s.equalsIgnoreCase("NO") -> { |
|||
| 603 | System.out.println("Shame"); |
|||
| 604 | } |
|||
| 605 | case String s -> { |
|||
| 606 | System.out.println("Sorry?"); |
|||
| 607 | } |
|||
| 608 | } |
|||
| 609 | } |
|||
| 610 | ``` |
|||
| 611 | ||||
| 612 | ### enum 쓰는 경우 |
|||
| 613 | ||||
| 614 | before |
|||
| 615 | ```java |
|||
| 616 | public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES } |
|||
| 617 | ||||
| 618 | static void testforHearts(Suit s) { |
|||
| 619 | switch (s) { |
|||
| 620 | case HEARTS -> System.out.println("It's a heart!"); |
|||
| 621 | default -> System.out.println("Some other suit"); |
|||
| 622 | } |
|||
| 623 | } |
|||
| 624 | ``` |
|||
| 625 | ||||
| 626 | after (sealed 도 같이 사용) |
|||
| 627 | ```java |
|||
| 628 | sealed interface CardClassification permits Suit, Tarot {} |
|||
| 629 | public enum Suit implements CardClassification { CLUBS, DIAMONDS, HEARTS, SPADES } |
|||
| 630 | final class Tarot implements CardClassification {} |
|||
| 631 | ||||
| 632 | static void exhaustiveSwitchWithoutEnumSupport(CardClassification c) { |
|||
| 633 | switch (c) { |
|||
| 634 | case Suit s when s == Suit.CLUBS -> { |
|||
| 635 | System.out.println("It's clubs"); |
|||
| 636 | } |
|||
| 637 | case Suit s when s == Suit.DIAMONDS -> { |
|||
| 638 | System.out.println("It's diamonds"); |
|||
| 639 | } |
|||
| 640 | case Suit s when s == Suit.HEARTS -> { |
|||
| 641 | System.out.println("It's hearts"); |
|||
| 642 | } |
|||
| 643 | case Suit s -> { |
|||
| 644 | System.out.println("It's spades"); |
|||
| 645 | } |
|||
| 646 | case Tarot t -> { |
|||
| 647 | System.out.println("It's a tarot"); |
|||
| 648 | } |
|||
| 649 | } |
|||
| 650 | } |
|||
| 651 | ``` |
|||
| 652 | ||||
| 653 | 이렇게도 가능하다. (이게 좀 더 명시적인 듯...) |
|||
| 654 | ```java |
|||
| 655 | static void exhaustiveSwitchWithBetterEnumSupport(CardClassification c) { |
|||
| 656 | switch (c) { |
|||
| 657 | case Suit.CLUBS -> { |
|||
| 658 | System.out.println("It's clubs"); |
|||
| 659 | } |
|||
| 660 | case Suit.DIAMONDS -> { |
|||
| 661 | System.out.println("It's diamonds"); |
|||
| 662 | } |
|||
| 663 | case Suit.HEARTS -> { |
|||
| 664 | System.out.println("It's hearts"); |
|||
| 665 | } |
|||
| 666 | case Suit.SPADES -> { |
|||
| 667 | System.out.println("It's spades"); |
|||
| 668 | } |
|||
| 669 | case Tarot t -> { |
|||
| 670 | System.out.println("It's a tarot"); |
|||
| 671 | } |
|||
| 672 | } |
|||
| 673 | } |
|||
| 674 | ``` |
|||
| 675 | ||||
| 676 | # JDK 22 |
|||
| 677 | ||||
| 678 | ## Foreign Function & Memory API |
|||
| 679 | ||||
| 680 | https://openjdk.org/jeps/454 |
|||
| 681 | ||||
| 682 | 이건 Coding Style 보다는 기능의 대체이다. JNI 를 대체하기 위해 나왔고 조금 복잡해 보이긴 한데, JNI 도 복잡했으니 비슷하지 않을까 싶다. |
|||
| 683 | ||||
| 684 | ### 목적 |
|||
| 685 | Java runtime 바깥의 코드와 데이터에 대한 접근을 위한 것인데 JNI 의 단점을 보완하려고 한다. |
|||
| 686 | * JNI의 native 메소드를 pure-java API 로 대체한다. |
|||
| 687 | * JNI 보다는 오버헤드가 있을 듯 함. |
|||
| 688 | * JVM 이 실행되는 모든 플랫폼의 native library 의 탐색 및 수행 |
|||
| 689 | * 다양한 (native, persistent, managed heap) 메모리 접근에 대한 방법을 제공함 |
|||
| 690 | * thread 간의 메모리 할당과 해지 시에도 쓰지 않는 메모리에 대한 해지 버그에 대해 보장 |
|||
| 691 | * native code 와 data 에 대한 안전하지 않은 (unsafe) 동작에 대해 허용하되 이에 대해 사용자에게 경고하는 기능 |
|||
| 692 | ||||
| 693 | ### Foreign memory (before) |
|||
| 694 | 객체들은 heap memory 에 만들어지고 사용이 끝나면 GC 로 사라진다. heap memory 말고 다른 곳에 만들어지는 off-heap memory 가 필요한 경우를 지원. 몰랐는데 이전에도 off-heap 메모리를 사용했다고... |
|||
| 695 | - ByteBuffer API 에서 시스템의 native I/O 에서 직접 access 할 수 있도록 하기 위해서 allocateDirect 로 할당 시 off-heap memory 로 관리. 주로 Non-block 이나 Async I/O 구현 하는 netty 같은 곳에서 사용. |
|||
| 696 | - sun.misc.Unsafe API 를 사용하는 경우 -> 잘 안 썼던 것 같다. |
|||
| 697 | ||||
| 698 | ### Foreign functions (before) |
|||
| 699 | JNI 로 지원 |
|||
| 700 | ||||
| 701 | ### 그래서 |
|||
| 702 | ||||
| 703 | FFM API 는 다음의 클래스와 인터페이스를 추가함. |
|||
| 704 | - foreign memory 에 대한 allocation 과 deallocation 제어 |
|||
| 705 | - MemorySegment |
|||
| 706 | - Arena |
|||
| 707 | - SegmentAllocator |
|||
| 708 | - 구조화된 foreign memory 접근 및 조작 |
|||
| 709 | - MemoryLayout |
|||
| 710 | - VarHandle |
|||
| 711 | - foreign functions 호출 |
|||
| 712 | - Linker |
|||
| 713 | - SymbolLockup |
|||
| 714 | - FunctionDescriptor |
|||
| 715 | - MethodHandle |
|||
| 716 | ||||
| 717 | ## Unnamed Variables & Patterns |
|||
| 718 | 필요에 의해 정의는 하지만 실제로 쓰이지 않는 경우를 대비해서 만들어진 기능이다. |
|||
| 719 | ||||
| 720 | for loop 를 위해 정의는 하지만 실제로 필요하지 않은 경우 |
|||
| 721 | ||||
| 722 | ```java |
|||
| 723 | static int count(Iterable<Order> orders) { |
|||
| 724 | int total = 0; |
|||
| 725 | for (Order order : orders) // order is unused |
|||
| 726 | total++; |
|||
| 727 | return total; |
|||
| 728 | } |
|||
| 729 | ``` |
|||
| 730 | ||||
| 731 | 다음과 같이 사용할 수 있다. |
|||
| 732 | ||||
| 733 | ```java |
|||
| 734 | static int count(Iterable<Order> orders) { |
|||
| 735 | int total = 0; |
|||
| 736 | for (Order _ : orders) // Unnamed variable |
|||
| 737 | total++; |
|||
| 738 | return total; |
|||
| 739 | } |
|||
| 740 | ``` |
|||
| 741 | ||||
| 742 | 아래의 경우에도 z 대신에 _ 를 사용할 수 있다. |
|||
| 743 | ```java |
|||
| 744 | Queue<Integer> q = ... // x1, y1, z1, x2, y2, z2 .. |
|||
| 745 | while (q.size() >= 3) { |
|||
| 746 | int x = q.remove(); |
|||
| 747 | int y = q.remove(); |
|||
| 748 | int z = q.remove(); // z is unused |
|||
| 749 | ... new Point(x, y) ... |
|||
| 750 | } |
|||
| 751 | ``` |
|||
| 752 | ||||
| 753 | try catch 구문에서 catch 한 Exception 을 만나서 실제로 쓰지 않는 경우에도 Exception 변수로 _ 사용 가능. |
|||
| 754 | ||||
| 755 | 패턴의 경우에도 유사. |
|||
| 756 | ||||
| 757 | ```java |
|||
| 758 | sealed abstract class Ball permits RedBall, BlueBall, GreenBall { } |
|||
| 759 | final class RedBall extends Ball { } |
|||
| 760 | final class BlueBall extends Ball { } |
|||
| 761 | final class GreenBall extends Ball { } |
|||
| 762 | ||||
| 763 | Ball ball = ... |
|||
| 764 | switch (ball) { |
|||
| 765 | case RedBall red -> process(ball); |
|||
| 766 | case BlueBall blue -> process(ball); |
|||
| 767 | case GreenBall green -> stopProcessing(); |
|||
| 768 | } |
|||
| 769 | ``` |
|||
| 770 | ||||
| 771 | 위와 같이 사용하는 경우 red, blue, greean 은 쓰이지 않으므로 다음과 같이 변경할 수 있다. |
|||
| 772 | ||||
| 773 | ```java |
|||
| 774 | switch (ball) { |
|||
| 775 | case RedBall _ -> process(ball); // Unnamed pattern variable |
|||
| 776 | case BlueBall _ -> process(ball); // Unnamed pattern variable |
|||
| 777 | case GreenBall _ -> stopProcessing(); // Unnamed pattern variable |
|||
| 778 | } |
|||
| 779 | ``` |
|||
| 780 | ||||
| 781 | ||||
| 782 | > [!Tip] Java 파일 수행 |
|||
| 783 | > JDK 22 에 Launch Multi-File Source-Code Programs 라는 항목이 있다. 그리고 JDK 11 에서 java Prog.java 형태로 소스코드 파일을 바로 실행할 수 있었다고... 이제는 멀티가 가능하다고 한다. 자세한 내용은 https://openjdk.org/jeps/458 참고. |
|||
| 784 | ||||
| 785 | # JDK 23 |
|||
| 786 | ||||
| 787 | ## Markdown Documentation Comments |
|||
| 788 | 기존에 html 태그를 넣어서 API 문서 작성하던 것을 Markdown 도 쓸 수 있게 함. |
|||
| 789 | ||||
| 790 | ```java |
|||
| 791 | /** |
|||
| 792 | * Returns a hash code value for the object. This method is |
|||
| 793 | * supported for the benefit of hash tables such as those provided by |
|||
| 794 | * {@link java.util.HashMap}. |
|||
| 795 | * <p> |
|||
| 796 | * The general contract of {@code hashCode} is: |
|||
| 797 | * <ul> |
|||
| 798 | * <li>Whenever it is invoked on the same object more than once during |
|||
| 799 | * an execution of a Java application, the {@code hashCode} method |
|||
| 800 | * must consistently return the same integer, provided no information |
|||
| 801 | * used in {@code equals} comparisons on the object is modified. |
|||
| 802 | * This integer need not remain consistent from one execution of an |
|||
| 803 | * application to another execution of the same application. |
|||
| 804 | * <li>If two objects are equal according to the {@link |
|||
| 805 | * #equals(Object) equals} method, then calling the {@code |
|||
| 806 | * hashCode} method on each of the two objects must produce the |
|||
| 807 | * same integer result. |
|||
| 808 | * <li>It is <em>not</em> required that if two objects are unequal |
|||
| 809 | * according to the {@link #equals(Object) equals} method, then |
|||
| 810 | * calling the {@code hashCode} method on each of the two objects |
|||
| 811 | * must produce distinct integer results. However, the programmer |
|||
| 812 | * should be aware that producing distinct integer results for |
|||
| 813 | * unequal objects may improve the performance of hash tables. |
|||
| 814 | * </ul> |
|||
| 815 | * |
|||
| 816 | * @implSpec |
|||
| 817 | * As far as is reasonably practical, the {@code hashCode} method defined |
|||
| 818 | * by class {@code Object} returns distinct integers for distinct objects. |
|||
| 819 | * |
|||
| 820 | * @return a hash code value for this object. |
|||
| 821 | * @see java.lang.Object#equals(java.lang.Object) |
|||
| 822 | * @see java.lang.System#identityHashCode |
|||
| 823 | */ |
|||
| 824 | ``` |
|||
| 825 | ||||
| 826 | Markdown 은 /// 로 구분한다. |
|||
| 827 | ||||
| 828 | ```java |
|||
| 829 | /// Returns a hash code value for the object. This method is |
|||
| 830 | /// supported for the benefit of hash tables such as those provided by |
|||
| 831 | /// [java.util.HashMap]. |
|||
| 832 | /// |
|||
| 833 | /// The general contract of `hashCode` is: |
|||
| 834 | /// |
|||
| 835 | /// - Whenever it is invoked on the same object more than once during |
|||
| 836 | /// an execution of a Java application, the `hashCode` method |
|||
| 837 | /// must consistently return the same integer, provided no information |
|||
| 838 | /// used in `equals` comparisons on the object is modified. |
|||
| 839 | /// This integer need not remain consistent from one execution of an |
|||
| 840 | /// application to another execution of the same application. |
|||
| 841 | /// - If two objects are equal according to the |
|||
| 842 | /// [equals][#equals(Object)] method, then calling the |
|||
| 843 | /// `hashCode` method on each of the two objects must produce the |
|||
| 844 | /// same integer result. |
|||
| 845 | /// - It is _not_ required that if two objects are unequal |
|||
| 846 | /// according to the [equals][#equals(Object)] method, then |
|||
| 847 | /// calling the `hashCode` method on each of the two objects |
|||
| 848 | /// must produce distinct integer results. However, the programmer |
|||
| 849 | /// should be aware that producing distinct integer results for |
|||
| 850 | /// unequal objects may improve the performance of hash tables. |
|||
| 851 | /// |
|||
| 852 | /// @implSpec |
|||
| 853 | /// As far as is reasonably practical, the `hashCode` method defined |
|||
| 854 | /// by class `Object` returns distinct integers for distinct objects. |
|||
| 855 | /// |
|||
| 856 | /// @return a hash code value for this object. |
|||
| 857 | /// @see java.lang.Object#equals(java.lang.Object) |
|||
| 858 | /// @see java.lang.System#identityHashCode |
|||
| 859 | ``` |
|||
| 860 | ||||
| 861 | table 가능하고 JavaDoc 태그는 그냥 써야 하는 듯 하다. 코드 블럭도 가능하다. |
|||
| 862 | ||||
| 863 | # JDK 24 |
|||
| 864 | ||||
| 865 | 대부분 성능 관련 개선이나 API 개선에 대한 내용들이다. |
|||
| 866 | ||||
| 867 | # JDK 25 |
|||
| 868 | ||||
| 869 | ## Scoped Values |
|||
| 870 | Virtual Thread 를 지원하기 위해 Thread 내에서 값을 공유할 수 있다. |
|||
| 871 | ```java |
|||
| 872 | public class Framework { |
|||
| 873 | ||||
| 874 | private final Application application; |
|||
| 875 | ||||
| 876 | public Framework(Application app) { this.application = app; } |
|||
| 877 | ||||
| 878 | private static final ThreadLocal<FrameworkContext> CONTEXT |
|||
| 879 | = new ThreadLocal<>(); // (1) |
|||
| 880 | ||||
| 881 | void serve(Request request, Response response) { |
|||
| 882 | var context = createContext(request); |
|||
| 883 | CONTEXT.set(context); // (2) |
|||
| 884 | Application.handle(request, response); |
|||
| 885 | } |
|||
| 886 | ||||
| 887 | public PersistedObject readKey(String key) { |
|||
| 888 | var context = CONTEXT.get(); // (3) |
|||
| 889 | var db = getDBConnection(context); |
|||
| 890 | db.readKey(key); |
|||
| 891 | } |
|||
| 892 | ||||
| 893 | } |
|||
| 894 | ``` |
|||
| 895 | ||||
| 896 | Thread 간의 value 공유도 가능한 듯 한데, 이 부분은 structured concurrency(현재는 preview) 에서 가능한 듯 하다. |
|||
| 897 | ||||
| 898 | ## Key Derivation Function API |
|||
| 899 | ||||
| 900 | HKDF(RFC 5869)나 Argon2 (RFC 9106) 같은 키 도출 함수 지원. |
|||
| 901 | ||||
| 902 | ```java |
|||
| 903 | // Create a KDF object for the specified algorithm |
|||
| 904 | KDF hkdf = KDF.getInstance("HKDF-SHA256"); |
|||
| 905 | ||||
| 906 | // Create an ExtractExpand parameter specification |
|||
| 907 | AlgorithmParameterSpec params = |
|||
| 908 | HKDFParameterSpec.ofExtract() |
|||
| 909 | .addIKM(initialKeyMaterial) |
|||
| 910 | .addSalt(salt).thenExpand(info, 32); |
|||
| 911 | ||||
| 912 | // Derive a 32-byte AES key |
|||
| 913 | SecretKey key = hkdf.deriveKey("AES", params); |
|||
| 914 | ||||
| 915 | // Additional deriveKey calls can be made with the same KDF object |
|||
| 916 | ``` |
|||
| 917 | ||||
| 918 | ## Module Import Declarations |
|||
| 919 | import module 문으로 다양한 패키지를 모듈 단위로 import 한다. 예를 들어 아래와 같은 예는 |
|||
| 920 | ||||
| 921 | ```java |
|||
| 922 | import javax.xml.*; |
|||
| 923 | import javax.xml.parsers.*; |
|||
| 924 | import javax.xml.stream.*; |
|||
| 925 | ``` |
|||
| 926 | ||||
| 927 | 다음과 같이 바꿀 수 있다. 이는 계층 구조 전체를 가져올 수 있다는 뜻. |
|||
| 928 | ||||
| 929 | ```java |
|||
| 930 | import module java.xml; |
|||
| 931 | ``` |
|||
| 932 | ## Compact Source Files and Instance Main Methods |
|||
| 933 | ||||
| 934 | 이 소스코드를... |
|||
| 935 | ```java |
|||
| 936 | public class HelloWorld { |
|||
| 937 | public static void main(String[] args) { |
|||
| 938 | System.out.println("Hello, World!"); |
|||
| 939 | } |
|||
| 940 | } |
|||
| 941 | ``` |
|||
| 942 | ||||
| 943 | 이렇게 줄일 수 있다고... |
|||
| 944 | ```java |
|||
| 945 | void main() { |
|||
| 946 | IO.println("Hello, World!"); |
|||
| 947 | } |
|||
| 948 | ``` |
|||
| 949 | ||||
| 950 | ```PowerShell |
|||
| 951 | PS C:\Users\bruckner\Workspace\T2X> cat .\HelloWorld.java |
|||
| 952 | void main() { |
|||
| 953 | IO.println("Hello, World!"); |
|||
| 954 | } |
|||
| 955 | PS C:\Users\bruckner\Workspace\T2X> java .\HelloWorld.java |
|||
| 956 | Hello, World! |
|||
| 957 | PS C:\Users\bruckner\Workspace\T2X> java -version |
|||
| 958 | openjdk version "25.0.1" 2025-10-21 LTS |
|||
| 959 | OpenJDK Runtime Environment Temurin-25.0.1+8 (build 25.0.1+8-LTS) |
|||
| 960 | OpenJDK 64-Bit Server VM Temurin-25.0.1+8 (build 25.0.1+8-LTS, mixed mode, sharing) |
|||
| 961 | ``` |
|||
| 962 | ||||
| 963 | 되네요. |
|||
| 964 | ||||
| 965 | ## Flexible Constructor Bodies |
|||
| 966 | ||||
| 967 | JDK 25 중 가장 중요한 변화. 상속 받은 Class 생성 시에 Super Class 먼저 생성하는 방식에 대한 변경이다. |
|||
| 968 | ||||
| 969 | 이전에 이런 방식으로 밖에 짤 수 없었다. |
|||
| 970 | ||||
| 971 | ```java |
|||
| 972 | class Object { |
|||
| 973 | Object() { |
|||
| 974 | // Object constructor body |
|||
| 975 | } |
|||
| 976 | } |
|||
| 977 | ||||
| 978 | class A extends Object { |
|||
| 979 | A() { |
|||
| 980 | super(); |
|||
| 981 | // A constructor body |
|||
| 982 | } |
|||
| 983 | } |
|||
| 984 | ||||
| 985 | class B extends A { |
|||
| 986 | B() { |
|||
| 987 | super(); |
|||
| 988 | // B constructor body |
|||
| 989 | } |
|||
| 990 | } |
|||
| 991 | ||||
| 992 | class C extends B { |
|||
| 993 | C() { |
|||
| 994 | super(); |
|||
| 995 | // C constructor body |
|||
| 996 | } |
|||
| 997 | } |
|||
| 998 | ||||
| 999 | class D extends C { |
|||
| 1000 | D() { |
|||
| 1001 | super(); |
|||
| 1002 | // D constructor body |
|||
| 1003 | } |
|||
| 1004 | } |
|||
| 1005 | ``` |
|||
| 1006 | ||||
| 1007 | 호출하면 다음과 같이 된다. |
|||
| 1008 | ||||
| 1009 | ``` |
|||
| 1010 | D |
|||
| 1011 | --> C |
|||
| 1012 | --> B |
|||
| 1013 | --> A |
|||
| 1014 | --> Object constructor body |
|||
| 1015 | --> A constructor body |
|||
| 1016 | --> B constructor body |
|||
| 1017 | --> C constructor body |
|||
| 1018 | D constructor body |
|||
| 1019 | ``` |
|||
| 1020 | ||||
| 1021 | 변경된 방식은 다음과 같다. |
|||
| 1022 | ||||
| 1023 | ```java |
|||
| 1024 | class Object { |
|||
| 1025 | Object() { |
|||
| 1026 | // Object constructor body |
|||
| 1027 | } |
|||
| 1028 | } |
|||
| 1029 | ||||
| 1030 | class A extends Object { |
|||
| 1031 | A() { |
|||
| 1032 | // A prologue |
|||
| 1033 | super(); |
|||
| 1034 | // A epilogue |
|||
| 1035 | } |
|||
| 1036 | } |
|||
| 1037 | ||||
| 1038 | class B extends A { |
|||
| 1039 | B() { |
|||
| 1040 | // B prologue |
|||
| 1041 | super(); |
|||
| 1042 | // B epilogue |
|||
| 1043 | } |
|||
| 1044 | } |
|||
| 1045 | ||||
| 1046 | class C extends B { |
|||
| 1047 | C() { |
|||
| 1048 | // C prologue |
|||
| 1049 | super(); |
|||
| 1050 | // C epilogue |
|||
| 1051 | } |
|||
| 1052 | } |
|||
| 1053 | ||||
| 1054 | class D extends C { |
|||
| 1055 | D() { |
|||
| 1056 | // D prologue |
|||
| 1057 | super(); |
|||
| 1058 | // D epilogue |
|||
| 1059 | } |
|||
| 1060 | } |
|||
| 1061 | ``` |
|||
| 1062 | ||||
| 1063 | 호출 순서는 다음과 같다. |
|||
| 1064 | ||||
| 1065 | ``` |
|||
| 1066 | D prologue |
|||
| 1067 | --> C prologue |
|||
| 1068 | --> B prologue |
|||
| 1069 | --> A prologue |
|||
| 1070 | --> Object constructor body |
|||
| 1071 | --> A epilogue |
|||
| 1072 | --> B epilogue |
|||
| 1073 | --> C epilogue |
|||
| 1074 | D epilogue |
|||
| 1075 | ``` |
|||
| 1076 | ||||
| 1077 | 생각해보면 쉬운 일 같은데 지금까지 안해준 것도 신기하고... 물론 제약 사항도 있는데 super 를 호출하기 전 까지는 인스턴스화가 완전하게 된 것이 아니어서 다음과 같이 오류가 날 수 있다. |
|||
| 1078 | ||||
| 1079 | ```java |
|||
| 1080 | class X { |
|||
| 1081 | ||||
| 1082 | int i; |
|||
| 1083 | String s = "hello"; |
|||
| 1084 | ||||
| 1085 | X() { |
|||
| 1086 | ||||
| 1087 | System.out.print(this); // Error - explicitly refers to the current instance |
|||
| 1088 | ||||
| 1089 | var x = this.i; // Error - explicitly refers to field of the current instance |
|||
| 1090 | this.hashCode(); // Error - explicitly refers to method of the current instance |
|||
| 1091 | ||||
| 1092 | var y = i; // Error - implicitly refers to field of the current instance |
|||
| 1093 | hashCode(); // Error - implicitly refers to method of the current instance |
|||
| 1094 | ||||
| 1095 | i = 42; // OK - assignment to an uninitialized declared field |
|||
| 1096 | ||||
| 1097 | s = "goodbye"; // Error - assignment to an initialized declared field |
|||
| 1098 | ||||
| 1099 | super(); |
|||
| 1100 | ||||
| 1101 | } |
|||
| 1102 | ||||
| 1103 | } |
|||
| 1104 | ``` |
|||
| 1105 | ||||
| 1106 | # Garbage Collector 중간 정리 |
|||
| 1107 | ||||
| 1108 | | 종류 | Option | 동작 방식 | 장점 | 단점 | |
|||
| 1109 | | ----------------- | -------------------------------------- | ---------------- | -------------------- | ---------------------------------- | |
|||
| 1110 | | Serial | -XX:+UseSerialGC | 싱글 스레드로 동작 | CPU 자원이 적을 때 효과적 | 멀티 스레드에서 쥐약. GC 동작 시 얼음 현상 | |
|||
| 1111 | | Parallel | -XX:+UseParallelGC | 멀티 스레드로 동작 | 가장 많은 Garbage 정리는 가능 | GC 동작 시 얼음 현상 | |
|||
| 1112 | | G1(Garbage First) | -XX:+UseG1GC | 파티션 구조 | 낮은 지연 시간 | 메모리 오버헤드 | |
|||
| 1113 | | Shenandoah | -XX:+UseShenandoahGC | 파티션 구조, 잦은 GC 수행 | Z 대비 처리 시간이 김 | Z 대비 Garbage 처리량이 높음 | |
|||
| 1114 | | Z | +XX:+UseZGC<br>+XX:+UseGenerationalZGC | 동적 파티션 구조 | 셰넌도 대비 처리 시간이 짧음 | 오리지널은 셰넌도 대비 처리량이 낮았으나 Z Gen 에서 극복 | |
|||
| 1115 | 일단 SerialGC 가 먼저 나왔고 ParallelGC 는 JDK 8 에서 default 가 되었다. 이외에 CMS GC 라는 것도 있었는데 JDK 9 이후 deprecated 되고 JDK 14 에서 제거되었다. 이후 G1GC 가 JDK7 에 도입되고 JDK9에 default 가 된다. ZGC 는 JDK 15 에, GenerationalZGC 는 JDK 21 부터 지원한다. Shenandoah GC 는 JDK 17 부터 지원한다. (지원의 의미는 안정화.) Generational Shenandoah 는 experimental 로 JDK25 부터 도입. (안정화는 기다려야 할 듯) |
|||
| 1116 | ||||
| 1117 | G1이 JDK 9 이후 현재 Default 이며 (LTS 로 보면 11, 17, 21, 25) G1 보다 저지연 및 메모리 오버헤드를 줄인 GC 로 만들고 있는 것이 Z 하고 Shenandoah 라고 보면 되는데 전반적으로 Z 가 조금 앞서는 느낌이다. |
