owned this note
owned this note
Published
Linked with GitHub
# Java 기본 확장
자바는 버전이 올라갈수록 새로운 기능이 추가되었다. 이런 확장된 기능들을 알아보자.
## 자바의 Try-with-Resources에 대하여
자바 프로그래밍 언어에서 예외 처리는 코드를 안정적으로 만드는 중요한 부분이다. `try-catch` 블록을 사용하여 예외를 처리하는 것은 매우 일반적인 방법이다.
하지만, 자원을 사용하는 경우(파일 입출력, 네트워크 소켓 연결 등) 자원을 올바르게 해제하지 않으면 리소스 누수가 발생할 수 있다. 자바 7 이전까지는 이러한 문제를 해결하기 위해 `finally` 블록에서 자원 해제 코드를 명시적으로 작성해야 했다.
자바 7부터 도입된 `Try-with-Resources`는 이러한 문제를 보다 우아하게 해결해 준다.
### Try-with-Resources란?
`Try-with-Resources`는 `try` 문을 사용하여 하나 이상의 리소스를 선언하는 데 사용되는 문법이다.
이 문법을 사용하면, 리소스를 사용한 후 자동으로 해제(자원을 정리)한다는 장점이 있다. 여기서 말하는 리소스란, 자동으로 닫힐 수 있는 어떤 객체를 말하며, 이는 `java.lang.AutoCloseable` 인터페이스 또는 그 자손인 `java.io.Closeable` 인터페이스를 구현해야 한다.
### Try-with-Resources 기본 사용법
기본적인 `Try-with-Resources` 문의 사용법은 다음과 같다.
```java
try (선언할 리소스1; 선언할 리소스2; ...) {
// 리소스와 함께 실행할 코드
} catch (예외타입 변수명) {
// 예외 처리 코드
}
```
이 구조에서 `try` 블록 안에 리소스를 선언한다. 이 리소스들은 **`try` 블록이 종료될 때 자동으로 `close()` 메소드가 호출**되어 자원이 해제된다.
이를 통해 개발자는 리소스 해제를 위한 코드를 직접 작성할 필요가 없게 되며, 코드의 가독성과 안정성이 높아진다.
### 예제: 파일 읽기 (Try-Catch-Finally)
```java
import java.io.*;
public class Main {
public static void main(String[] args) {
FileInputStream fi = null;
try {
fi = new FileInputStream("test.txt");
byte[] bytes = fi.readAllBytes();
System.out.println(new String(bytes));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fi.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
```
### 예제: 파일 읽기 (Try-with-Resources)
```java
import java.io.*;
public class Main {
public static void main(String[] args) {
try (FileInputStream fi = new FileInputStream("test.txt")) {
byte[] bytes = fi.readAllBytes();
System.out.println(new String(bytes));
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
- `FileInputStream`은 `AutoCloseable` 인터페이스를 구현하고 있으므로, `Try-with-Resources`를 사용할 수 있다.
### 장점
- **자원 해제를 위한 코드를 직접 작성할 필요가 없다**: 이는 코드의 양을 줄여주고, 가독성을 향상시킨다.
- **리소스 누수 방지**: 모든 리소스가 올바르게 닫히도록 보장한다.
- **코드의 안정성 향상**: 예외가 발생하더라도 리소스가 안정적으로 닫히므로 안정성이 향상된다.
### 단점
- **Try 범위 제한**: `try` 블록 안에서 선언된 리소스는 `try` 블록이 종료될 때 닫히므로, `try` 블록 밖에서 리소스를 사용할 수 없다.
- **Java 7 이상에서만 사용 가능**: Java 7 이상에서만 사용할 수 있다.
- **리소스를 선언하는 부분이 복잡해질 수 있다**: 여러 리소스를 사용하는 경우, `try` 문 안에서 모두 선언해야 하므로 코드가 복잡해질 수 있다.
- **사용 불가능한 리소스**: `AutoCloseable` 인터페이스를 구현하지 않은 리소스는 `Try-with-Resources`를 사용할 수 없다.
> 단, 위의 단점들을 모두 합쳐도 `Try-with-Resources`의 장점이 더 크다고 할 수 있기 때문에 사용을 권장한다.
### 주의사항
- `Try-with-Resources`를 사용할 때 리소스는 `try` 문 안에서 선언되어야 한다. 따라서, `try` 블록 바깥에서 선언된 리소스는 자동으로 닫히지 않는다.
- 여러 리소스를 사용하는 경우, 리소스는 선언된 반대 순서로 닫힌다. 즉, 마지막에 선언된 리소스부터 첫 번째 리소스까지 역순으로 닫힌다.
## 익명클래스
- 이름이 없는 클래스
- 한번만 사용되며, 클래스를 선언하고 동시에 인스턴스화 할 때 사용된다.
- 주 GUI 구성 요소를 정의하거나, 간단한 인터페이스 구현에 사용되며, 코드를 더욱 간단하게 만들수 있도록 한다
### 익명클래스를 사용하는 이유
- 간결성: 익명 클래스는 주로 한 곳에서만 사용되며, 따라서 별도의 클래스 파일을 작성하는 것에 비해 코드를 간결하게 만들 수 있다.
- 지역적인 사용: 익명 클래스는 메소드 내부에서 정의하고 사용할 수 있어, 그 사용 범위를 한정할 수 있다. 이는 코드의 가독성과 유지 보수성을 향상시킨다.
- 코드의 캡슐화: 익명 클래스는 그것을 사용하는 코드에 가깝게 위치할 수 있어, 특정 동작을 캡슐화하는데 유용하다. 예를 들어, 버튼 클릭 리스너와 같은 GUI 이벤트 처리 코드에서 종종 볼 수 있다.
### 익명클래스 사용 시 주의사항
- 재사용성 부족: 익명 클래스는 이름이 없기 때문에 재사용할 수 없다. 따라서, 같은 코드를 반복해서 사용해야 하는 경우에는 적합하지 않다.
- 복잡성: 비교적 단순한 동작을 가진 클래스를 정의하는 경우에는 익명 클래스가 유용할 수 있지만, 너무 많은 동작을 가진 클래스를 익명 클래스로 만드는 것은 코드를 이해하고 유지 보수하는데 어려움을 초래할 수 있다.
- 구문 제한: 익명 클래스에서는 일부 구문 (예: 생성자, static 멤버 등)을 사용할 수 없다. 또한, 익명 클래스는 자신을 둘러싼 블록의 final 또는 effectively final 변수에만 접근할 수 있다.
- 가독성: 익명 클래스의 사용은 코드의 가독성을 떨어뜨릴 수 있다. 특히, 여러 개의 중첩된 익명 클래스는 코드를 읽고 이해하는데 어려움을 줄 수 있다.
### 익명클래스 구현 방법
- 익명클래스는 클래스 선언과 객체의 생성을 동시에 수행한다.
- 주로 `new` 키워드 뒤에 인터페이스나 클래스 이름을 명시하고, 그 뒤에 중괄호 `{}`를 추가하여 클래스 본문을 작성한다.
- 중괄호 안에는 해당 인터페이스나 클래스를 오버라이딩하는 메서드를 포함시켜 익명클래스를 완성시킨다
### 익명클래스 예시
#### 예시 1
##### 익명 클래스 사용 전
```java
interface HelloWorld {
public void greet();
}
class EnglishGreeting implements HelloWorld {
public void greet() {
System.out.println("Hello world!");
}
}
class Main {
public static void main(String[] args) {
HelloWorld englishGreeting = new EnglishGreeting();
englishGreeting.greet();
}
}
```
##### 익명 클래스 사용 후
```java
interface HelloWorld {
public void greet();
}
class Main {
public static void main(String[] args) {
HelloWorld englishGreeting = new HelloWorld() {
public void greet() {
System.out.println("Hello world!");
}
};
englishGreeting.greet();
}
}
```
#### 예시 2
##### Runnable 인터페이스의 익명 클래스 사용 예
```java
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("익명 클래스의 run 메소드");
}
};
Thread thread = new Thread(runnable);
thread.start();
```
> 위의 코드에서, `new Runnable() { ... }` 부분이 Runnable 인터페이스를 구현하는 익명 클래스이다. Runnable 인터페이스의 run 메소드를 오버라이드하여 구현하고 있다.
##### Thread의 익명 클래스 사용 예
```java
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("익명 클래스의 run 메소드");
}
};
thread.start();
```
> Thread 클래스를 상속받는 익명 클래스. 이 클래스도 마찬가지로 run 메소드를 오버라이드하여 구현하고 있다.
## 가변인자
가변인자는 JDK 1.5부터 추가된 기능이다. 가변인자를 사용하면 메서드의 매개변수를 동적으로 지정할 수 있다.
- 가변인자는 메서드의 매개변수를 동적으로 지정할 수 있게 해준다.
- 가변인자는 메서드의 매개변수에 `타입... 변수명` 형식으로 선언한다.
### 가변인자 사용 전
```java
public void print(String[] args) {
for (String arg : args) {
System.out.println(arg);
}
}
```
### 가변인자 사용 후
```java
public void print(String... args) {
for (String arg : args) {
System.out.println(arg);
}
}
```
### 가변인자 사용 시 유의사항
#### 가변인자를 사용한 메서드를 호출할 때 인자를 전달하지 않아도 된다
```java
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
print();
}
public static void print(String... args) {
System.out.println(Arrays.toString(args));
}
}
```
#### 가변인자를 사용한 메서드를 호출할 때 인자를 배열로 전달할 수도 있다
```java
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
print(new String[] {"Hello", "World"});
}
public static void print(String... args) {
System.out.println(Arrays.toString(args));
}
}
```
#### 모호한 호출을 방지하기 위해 가변인자는 다음과 같은 규칙을 따라야 한다
- 가변인자는 하나만 사용할 수 있다.
- 가변인자는 매개변수 중 가장 마지막에 위치해야 한다.
```java
public void print(String... args, int num) { // 컴파일 에러
//...
}
```
```java
public void print(String... args, int... nums) { // 컴파일 에러
//...
}
```
## 자바의 Enhanced For문
자바는 다양한 반복문을 제공하며, 그 중 하나인 Enhanced For문은 코드의 가독성을 높이고 개발자가 컬렉션 및 배열을 보다 쉽게 순회할 수 있도록 도와준다.
### Enhanced For문의 기본 구조
Enhanced For문, 또는 "for-each"문은 자바 5부터 도입된 반복문이다. 배열이나 컬렉션의 모든 요소를 순회하는 데 사용된다. 기본 구조는 다음과 같다.
```java
for (타입 변수명 : 배열 또는 컬렉션) {
// 실행할 코드
}
```
여기서 타입은 배열 또는 컬렉션에 저장된 요소의 타입을 의미하며, 변수명은 순회하는 동안 각 요소에 접근하기 위해 사용되는 변수의 이름이다. 배열 또는 컬렉션은 순회할 대상이다.
### Enhanced For문의 작동 원리
Enhanced For문은 내부적으로 주어진 배열이나 컬렉션의 `iterator`를 사용하여 순회를 수행한다. 개발자가 명시적으로 `iterator`를 사용하는 코드를 작성할 필요가 없어 코드가 간결해지며, 순회 과정에서 발생할 수 있는 오류를 최소화한다.
반복마다 배열 또는 컬렉션의 현재 요소가 지정된 변수에 할당된다. 이 변수를 통해 반복문 내에서 현재 요소에 접근하고, 필요한 연산을 수행할 수 있다.
### Enhanced For문 사용 예제
#### 배열 사용 예제
```java
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
System.out.println(number);
}
```
위 예제는 정수 배열 `numbers`의 모든 요소를 순회하며 출력한다.
#### 컬렉션 사용 예제
```java
ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");
for (String fruit : fruits) {
System.out.println(fruit);
}
```
이 예제는 `String` 타입의 `ArrayList`인 `fruits`에 저장된 모든 요소를 순회하며 출력한다.
### Enhanced For문의 장점
- **가독성 향상**: 코드가 간결해져 이해하기 쉽다.
- **오류 감소**: 순회 과정에서의 실수를 줄일 수 있다.
- **컬렉션 및 배열 사용 용이**: 배열과 컬렉션 모두에 사용 가능하다.
### Enhanced For문 주의사항
- **수정 제한**: Enhanced For문을 사용하여 순회하는 동안 컬렉션의 요소를 추가, 삭제하는 것은 허용되지 않는다.
- **성능 이슈**: 매우 큰 데이터셋을 순회할 때는 Enhanced For문보다는 iterator를 직접 사용하는 것이 성능상 이점이 있을 수 있다.
## 타입 추론
자바는 전통적으로 강타입 언어이다. 이는 모든 변수, 매개변수, 반환 값 등이 명시적인 타입을 가져야 한다는 것을 의미한다.
그러나, 자바 10 버전부터 `var` 키워드를 도입하여 로컬 변수에 대한 타입 추론(type inference) 기능을 제공하기 시작했다. 이 기능은 개발자로 하여금 코드의 가독성을 향상시키고, 더 빠르게 코드를 작성할 수 있게 한다.
> - 강타입 언어 : 변수의 타입이 컴파일 시점에 결정되며, 한 번 정해지면 변경할 수 없는 언어를 의미. (자바, C, C++ 등)
> - 약타입 언어 : 변수의 타입이 런타임 시점에 결정되며, 동적으로 타입을 변경할 수 있는 언어를 의미. (파이썬, 자바스크립트 등)
### `var` 키워드란?
`var` 키워드는 자바 10 이상에서 로컬 변수를 선언할 때 사용하는 타입 추론 기능이다. 이를 사용하면 컴파일러가 변수의 초기값을 기반으로 해당 변수의 타입을 자동으로 결정한다.
### `var` 기본 사용법
`var`를 사용한 변수 선언은 다음과 같은 형태를 취한다.
```java
var 변수명 = 초기값;
```
예를 들어, 다음과 같이 사용할 수 있다.
```java
var message = "Hello, Java!";
var list = new ArrayList<String>();
var map = new HashMap<String, Integer>();
```
여기서 중요한 점은 `var`로 선언된 변수는 반드시 초기값을 가지고 있어야 한다는 것이다. 초기값이 없는 경우에는 `var`를 사용할 수 없다.
### `var`와 타입 추론의 원리
자바 컴파일러는 `var`로 선언된 변수의 초기값을 분석하여 그 타입을 결정한다. 이 과정에서 컴파일러는 변수의 초기값을 바탕으로 정확한 타입 정보를 유추한다.
```java
var number = 10; // int로 추론
var message = "Hello, World!"; // String으로 추론
```
```java
var list = new ArrayList<String>(); // ArrayList<String>으로 추론
var map = new HashMap<String, Integer>(); // HashMap<String, Integer>으로 추론
```
```java
var stream = list.stream(); // Stream<String>으로 추론
```
### `var` 사용 시 장점
- 코드의 간결성 : `var` 키워드를 사용하면, 특히 타입 이름이 길거나 복잡할 때 코드를 더 간결하게 만들 수 있다.
- 가독성 향상 : 타입 추론을 통해 코드의 주요 목적에 더 집중할 수 있게 하며, 이는 전체적인 코드의 가독성을 향상시킨다.
### `var` 사용 시 주의사항
- 로컬 변수에만 사용 가능 : `var`는 메서드의 매개변수, 반환 타입, 필드(클래스의 멤버 변수)에는 사용할 수 없다. 오직 로컬 변수에만 사용할 수 있다.
- 명확한 타입이 필요한 경우 : `var`를 사용하면 타입이 명시적이지 않기 때문에, 코드를 읽는 사람이 해당 변수의 타입을 직접 추론해야 할 수도 있다. 따라서, 타입이 명확히 드러나는 것이 중요한 경우에는 `var`의 사용을 피해야 할 수 있다.
- 초기값이 없는 경우 사용 불가 : `var`로 선언된 변수는 반드시 초기값을 가져야 한다. 초기값 없이 `var`를 사용하여 변수를 선언하려고 하면 컴파일 오류가 발생한다.
## LocalDate, LocalTime, LocalDateTime
자바에서 현재 시간을 알아오는 방법은 여러 가지가 있다.
전통적으로 자바에서 날짜와 시간을 다루는 방법은 다음과 같다.
- `java.util.Date` 클래스
- `java.util.Calendar` 클래스
- `System.currentTimeMillis()` 메소드
이는 자바 8 이전에 사용되던 방법이며, 이러한 방법은 날짜와 시간을 다루는 데 불편하고 복잡하다는 단점이 있다. 자바 8에서는 이러한 문제를 해결하기 위해 새로운 API를 도입했다. 그 중에서도 `LocalDate`, `LocalTime`, `LocalDateTime` 클래스를 사용하면 날짜와 시간을 쉽게 다룰 수 있다.
이 클래스들은 공통적으로 불변(immutable)하며, 스레드에 안전하기에 자바 8 이후에는 이 클래스들을 사용하는 것이 권장된다.
### LocalDate
`LocalDate`는 날짜 정보를 다루는 클래스이다. 이 클래스는 년, 월, 일을 나타내는데 사용된다. `LocalDate` 객체를 사용하면 날짜를 생성하고 조작하는 다양한 메소드를 사용할 수 있다.
#### LocalDate 생성하기
`LocalDate` 객체는 다양한 방법으로 생성할 수 있다. 가장 일반적인 방법은 현재 날짜를 기준으로 객체를 생성하는 것이다.
```java
LocalDate today = LocalDate.now();
```
특정 날짜로 `LocalDate` 객체를 생성하려면 `of` 메소드를 사용한다.
```java
LocalDate specificDate = LocalDate.of(2077, 12, 25);
```
#### 날짜 정보 얻기
`LocalDate` 객체에서 년, 월, 일과 같은 정보를 얻으려면 다음과 같은 메소드를 사용한다.
```java
LocalDate today = LocalDate.now();
int year = today.getYear();
int month = today.getMonthValue();
int day = today.getDayOfMonth();
```
#### 날짜 조작하기
LocalDate에는 날짜를 조작하는 다양한 메소드가 있다. 다음은 `plusDays`, `plusMonths` 메소드를 사용하여 날짜를 조작하는 예제이다.
```java
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
LocalDate nextMonth = today.plusMonths(1);
```
- 이전 날짜를 구하려면 `minusDays`, `minusMonths` 메소드를 사용한다.
> `LocalDate` 객체는 `String`과 마찬가지로 불변이기 때문에, 날짜를 조작하는 메소드는 기존 객체를 변경하는 대신 새 객체를 반환하는 메소드를 통해 날짜를 조작한다.
#### 날짜 비교하기
두 `LocalDate` 객체를 비교하기 위해 `isBefore`, `isAfter`, `isEqual` 메소드를 사용한다.
```java
LocalDate today = LocalDate.now();
LocalDate specificDate = LocalDate.of(2022, 12, 25);
boolean isBefore = today.isBefore(specificDate);
boolean isAfter = today.isAfter(specificDate);
```
### LocalTime
`LocalTime`은 시간 정보를 다루는 클래스이다. 이 클래스는 시, 분, 초, 밀리초 등의 정보를 나타내는 데 사용된다. `LocalTime` 객체를 사용하면 시간을 생성하고 조작하는 다양한 메소드를 사용할 수 있다.
#### LocalTime 생성하기
`LocalTime` 객체도 `LocalDate`와 유사한 방법으로 생성할 수 있다. 현재 시간을 기준으로 객체를 생성하는 것이 일반적이다.
```java
LocalTime now = LocalTime.now();
```
특정 시간을 지정하여 `LocalTime` 객체를 생성할 때는 `of` 메소드를 사용한다.
```java
LocalTime specificTime = LocalTime.of(14, 30, 0); // 시, 분, 초
```
#### 시간 정보 얻기
`LocalTime` 객체에서 시, 분, 초 등의 정보를 얻으려면 다음과 같은 메소드를 사용한다.
```java
LocalTime now = LocalTime.now();
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
```
#### 시간 조작하기
`LocalTime` 객체의 시간을 조작하려면, 마찬가지로 새 객체를 반환하는 메소드를 사용한다.
```java
LocalTime now = LocalTime.now();
LocalTime inOneHour = now.plusHours(1);
LocalTime in30Minutes = now.plusMinutes(30);
```
#### 시간 비교하기
`LocalTime` 객체 간의 비교도 `LocalDate`와 유사하게 `isBefore`, `isAfter`, `isEqual` 메소드를 통해 수행한다.
```java
LocalTime now = LocalTime.now();
LocalTime specificTime = LocalTime.of(14, 30, 0);
boolean isEarlier = now.isBefore(specificTime);
boolean isLater = now.isAfter(specificTime);
```
### LocalDateTime
때로는 날짜와 시간을 함께 다뤄야 할 때가 있다. 이 경우 `LocalDateTime` 클래스를 사용할 수 있다. `LocalDateTime`은 `LocalDate`와 `LocalTime`의 기능을 모두 포함한다.
#### LocalDateTime 생성하기
`LocalDateTime` 객체는 다음과 같은 방법으로 생성할 수 있다.
```java
LocalDateTime dateTime = LocalDateTime.now();
```
특정 날짜와 시간을 지정하여 `LocalDateTime` 객체를 생성하려면 `of` 메소드를 사용한다.
```java
LocalDateTime specificDateTime = LocalDateTime.of(2022, 12, 25, 14, 30, 0); // 년, 월, 일, 시, 분, 초
```
#### 날짜와 시간 조작하기
`LocalDateTime` 객체에서 날짜와 시간을 조작하는 방법도 `LocalDate`와 `LocalTime`과 유사하다.
```java
LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime tomorrow = dateTime.plusDays(1);
LocalDateTime nextMonth = dateTime.plusMonths(1);
LocalDateTime inOneHour = dateTime.plusHours(1);
LocalDateTime in30Minutes = dateTime.plusMinutes(30);
```
#### 날짜와 시간 비교하기
`LocalDateTime` 객체 간의 비교도 `LocalDate`와 `LocalTime`과 동일한 방법으로 수행한다.
```java
LocalDateTime now = LocalDateTime.now();
LocalDateTime specificDateTime = LocalDateTime.of(2024, 4, 1, 14, 30, 0);
boolean isEarlier = now.isBefore(specificDateTime);
boolean isLater = now.isAfter(specificDateTime);
```