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 가 조금 앞서는 느낌이다.