![[Inflearn] 김영한의 실전 자바 - 중급 1편 정리 (B)](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2FcLv7V1%2FbtsI06a5EcZ%2FAAAAAAAAAAAAAAAAAAAAAAovFMfBmwbRv3a4-jXaqZKmT6mTPushFNxA5RdZ-IcM%2Fimg.png%3Fcredential%3DyqXZFxpELC7KVnFOS48ylbz2pIh7yKj8%26expires%3D1753973999%26allow_ip%3D%26allow_referer%3D%26signature%3DqxbjZCVSRSVPAp87SpXOHtv%252B5Vw%253D)
📚 강의 출처
김영한의 실전 자바 - 중급 1편 강의 | 김영한 - 인프런
김영한 | 실무에 필요한 자바의 다양한 중급 기능을 예제 코드로 깊이있게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을
www.inflearn.com
강사님께 항상 감사합니다. 🧑🏻💻
해당 글은 김영한님의 강의와 개인적 지식을 바탕으로 정리한 내용입니다.
모든 자료의 출처는 김영한 강사님임을 미리 밝힙니다.
섹션 6. 날짜와 시간 [ 2회 복습 🔥 ]
현대 개발 환경에서는 애플리케이션 설계과정에서 시간과 날짜를 계산하는 건 매우 복잡하므로, 이를 추상화해 안정적이고 정확한 결과를 반환하는 라이브러리를 제공한다.
Java 날짜와 시간 라이브러리 역사
- JDK 1.0 (java.util.Date) -> JDK 1.1 (java.util.Calendar) -> Joda-Time -> JDK 8(1.8) (java.time 패키지)
LocalDate, LocalTime, LocalDateTime
- LocalDate : 날짜 표현 사용
- LocalTime : 시간 표현 사용
- LocalDateTime : 날짜와 시간을 합한 개념
- Period : 날짜 사이의 년, 월, 일 단위 표현
- Duration : 시간 사이의 시, 분, 초(나노초) 단위 표현
LocalDate (불변)
- now() : 현재 시간 기준 생성
- of(..) : 특정 날짜 기준 생성
- plusXxx() : 특정 Xxx 년, 월, 일 등 추가
LocalTime (불변)
- now() : 현재 시간 기준 생성
- of(..) 특정 시간 기준 생성
- plusXxx() : 특정 시, 분, 초 등 추가
LocalDateTime
- now() : 현재 날짜와 시간 기준 생성
- of(...) : 특정 날짜와 시간 기준 생성
- LocalDate,.. Time 등으로 분리 가능
- LocalDate,.. Time 등으로 합체 가능
- plusXxx() : 특정 추가 가능
- 비교
- isBefore() : 현재 날짜와 시간이 이전이면 true 반환
- isAfter() : 현재 날짜와 시간이 이후이면 true 반환
- isEqual() : 날짜와 시간이 동일인지 비교
- isEqual() : 타임존이 고려해 시간이 동일하면 True
- equals() : 타임존을 고려하지 않고, 온전히 시간이 동일하면 True
ZonedDateTime : LocalDateTime에 ZoneId
- ZoneId
- ZoneId.systemDefault() : 시스템이 사용하는 기본 ZoneId 반환
- ZoneId.of() : 타임존 제공 후 ZoneId 반환
- now() : 현재 날짜와 시간 기준 생성, ZoneId 추가
- of(...) : 특정 날짜와 시간 기준 생성, ZoneId 추가
- withZoneSameInstant(ZoneId) : 타임존 변경
OffsetDateTime : 시간대 고려한 날짜와 시간 표현, 타임존 대신 UTC 시간의 차이 표시
ZonedDateTime, OffsetDateTime는 글로벌 서비스 외 잘 사용 X
Instant : UTC를 기준으로, 시간의 한 지점을 초 데이터로만 제공
- 사용 예
- 전 세계적인 시간 기준 필요시
- 시간대 변환 없이 시간 계산 필요시
- 데이터 저장 및 교환
구분 | Period | Duration |
단위 | 년, 월, 일 | 시간, 분, 초, 나노초 |
사용 대상 | 날짜 | 시간 |
주요 메소드 | getYears(), getMonths(), getDays() | toHours(), toMinutes(), getSeconds(), getNano() |
활용 용도 | 생성, 계산에 사용, 기간 차이 |
TemporalAccessor 인터페이스
- 날짜와 시간을 읽기 위한 기본 인터페이스
Temporal 인터페이스
- 날짜와 시간을 조작(추가, 빼기 등) 하기 위한 기능을 제공
TemporalAmount 인터페이스
- 날짜와 시간 객체에 적용하여 그 객체를 조정하는 기능
시간의 단위 - TemporalUnit, ChronoUnit
- 날짜와 시간을 측정하는 단위를 나타내며, 사용되는 구현체는 ChronoUnit 열거형으로 이루어져 있다.
🧑🏻💻 문제 풀이
Inflearn_Java/실전 자바 - 중급편1/섹션 6. 날짜와 시간 at main · taek2222/Inflearn_Java
🍃 Inflearn 김영한 강사 Java 학습. Contribute to taek2222/Inflearn_Java development by creating an account on GitHub.
github.com
섹션 7. 중첩 클래스, 내부 클래스 1 [ 2회 복습 🔥 ]
class Outer {
...
//중첩 클래스
class Nested {
...
}
}
- 중첩 클래스(Nested Class) : 클래스 안에 클래스를 중첩 정의
- 중첩(Nested) : 어떤 다른 것이 내부에 위치하거나 포함되는 구조적 관계 (회사 물품)
- 내부(Inner) : 나의 내부에 있는 나를 구성하는 요소 (몸과 심장)
중첩 클래스의 선언 위치
- 정적 중첩 클래스 : 정적 변수와 동일한 위치
- 내부 클래스 : 인스턴스 변수와 동일한 위치
- 지역 클래스 : 지역 변수와 동일한 위치
- 정적 중첩 클래스
- Static 키워드 사용 O
- 중첩 클래스이지만, 바깥 클래스의 인스턴스의 소속 X
- 한 클래스에 존재하지만, 전혀 다른 클래스
- 내부 클래스
- Static 키워드 사용 X
- 중첩 클래스 이므로 바깥 클래스의 인스턴스에 소속 O
- 한 클래스에 존재하며, 바깥 클래스를 의존
- 내부 클래스 종류
- 내부 클래스(inner class) : 바깥 클래스의 인스턴스의 멤버에 접근
- 지역 클래스(local class) : 내부 클래스 특징 + 지역 변수에 접근
- 익명 클래스(anonymous class) : 지역 클래스의 특징 + 클래스의 이름이 없는 특별한 클래스
중첩 클래스 사용 목적
- 논리적 그룹화 : 특정 클래스가 하나의 클래스만 의존하는 경우라면 특정 클래스를 해당 클래스 안에 포함시켜 그룹화
- 캡슐화 : 중첩 클래스는 둘로 나뉘어 있는 관계가 아니므로, private 접근 제어자 멤버 변수에도 접근 가능
public class ShadowingMain {
public int value = 1;
class Inner {
public int value = 2;
void go() {
int value = 3;
System.out.println("value = " + value);
System.out.println("this.value = " + this.value);
System.out.println("ShadowingMain.value = " + ShadowingMain.this.value);
}
}
public static void main(String[] args) {
ShadowingMain main = new ShadowingMain();
Inner inner = main.new Inner();
inner.go();
}
}
가장 가까운 변수가 우선순위를 가진다. 위와 같이 this와 클래스 이름. this를 활용해 접근할 수 있다.
섹션 8. 중첩 클래스, 내부 클래스 2 [ 2회 복습 🔥 ]
지역 클래스
- 지역 변수처럼 블록 안에 클래스 선언
- 지역 변수에 접근 가능
- 접근 제어자 사용이 불가능
- 인스턴스 변수, 지역 변수, 매개 변수 접근 모두 가능
- 부모 클래스 상속 및 인터페이스 구현 가능
public class LocalOuterV2 {
private int outInstanceVar = 3;
public void process(int paramVar) {
int localVar = 1;
class LocalPrinter implements Printer {
int value = 0;
@Override
public void print() {
System.out.println("value=" + value);
System.out.println("localVar=" + localVar);
System.out.println("paramVar=" + paramVar);
System.out.println("outInstanceVar=" + outInstanceVar);
}
}
Printer printer = new LocalPrinter();
printer.print();
}
public static void main(String[] args) {
LocalOuterV2 localOuter = new LocalOuterV2();
localOuter.process(2);
}
}
지역 변수 캡처
Printer printer = new LocalPrinter();
//printer.print()를 여기서 실행하지 않고 Printer 인스턴스만 반환한다.
return printer;
}
public static void main(String[] args) {
LocalOuterV3 localOuter = new LocalOuterV3();
Printer printer = localOuter.process(2);
//printer.print()를 나중에 실행한다. process()의 스택 프레임이 사라진 이후에 실행
printer.print();
}
위 Main 코드와 같이 진행된다 했을 때, 객체를 반환하고 메서드를 종료한다. 그때, 메서드 안에는 지역 변수와 매개 변수가 존재할 수 있다. 만약, 이렇게 메서드가 종료되었다면 스택 영역에서 사라질 것이다. 근데 출력에는 이상이 없다. 분명 printer.print()를 출력하기 전 메서드가 종료되고, 지역 변수와 매개 변수가 사라졌을 텐데 정상적으로 구동이 된다. 자바에서는 이러한 현상을 변수 '캡처'라 한다. 변수가 사라지는 건 사실이다. 하지만, 객체는 아직 힙 영역에 살아있기 때문에 값이 없으면 오류가 발생할 것이다. 이를 방지하기 위해 객체가 생성될 때, 접근이 필요한 변수를 사진 찍듯이 값을 캡처해 힙 영역의 객체에 같이 보관한다.
- 캡처한 변수의 생명 주기는 인스턴스 생명 주기와 동일
- 지역 클래스의 객체가 생성되는 시점에 지역 변수 캡처 진행
지역 변수는 사실상 final 키워드를 가진 변수와 마찬가지다. 지역 클래스를 객체로 선언할 때, 변수를 캡처한다. 이미 캡처를 진행했기 때문에 중간에 값을 변경할 수 없다. 막상 "그냥 중간에 값을 변경하면, 새로 캡처를 하면 되지 않나?"라는 생각을 했다. 하지만, 변경을 허용한다면 수많은 문제들이 파생될 수 있다. 캡처 변수 변경 <-> 인스턴스 변수 변경, 예상하지 못한 곳 값 변경 발생, 서로 동기화 과정에서 성능에 나쁜 영향을 줄 수 있기 때문이다.
익명 클래스
- 클래스 이름을 생략하고, 선언과 생성을 한 번에 처리
Printer printer = new Printer(){
//body
}
익명 클래스는 이름 그대로 이름이 없는 클래스이다. 클래스를 생성하고, 객체를 선언하는 과정을 한 번에 처리한다. 하지만, 객체를 할당할 부모클래스와 인터페이스가 꼭 필요하다. 인터페이스나 추상 클래스를 즉석으로 구현할 수 있어 코드의 간결함이 증가한다.
- 좋은 코드 유지의 핵심은 변하는 부분과 변하지 않는 부분을 분리
🧑🏻💻 문제 풀이
Inflearn_Java/실전 자바 - 중급편1/섹션 8. 중첩 클래스, 내부 클래스2 at main · taek2222/Inflearn_Java
🍃 Inflearn 김영한 강사 Java 학습. Contribute to taek2222/Inflearn_Java development by creating an account on GitHub.
github.com
섹션 9. 예외 처리 1 - 이론 [ 2회 복습 🔥 ]
Java의 GC는 JVM 메모리에 있는 인스턴스는 자동으로 해체가 가능하다. 하지만, 실제 애플리케이션에서 클라이언트 및 DB 연결은 GC가 처리하지 않는다. 즉, 직접 연결을 해제해야 한다.
애플리케이션 개발을 진행하면 정말 다양한 오류를 경험한다. 개발자의 실수로 발생한 컴파일 오류부터 클라이언트, DB, 통신 등 정말 다양한 오류를 경험한다. 이러한 에러를 방치한다면 어떻게 될까? 사용자는 분명 어떤 프로세스를 진행하는데 오류가 발생한다면 처음부터 다시 해야 하는 작업에 바로 창을 닫는다. 또한, DB 통신 과정에 오류가 발생했지만 클라이언트에서는 정상 로직 처리가 된다면 아마 사용자는 모르지만 서버에서는 해당 사용자가 주문한 내역을 볼 수 없다. 개발자도 사람인지라 실수로 오류 발생은 충분히 가능성 있다. 하지만, 에러가 발생해도 어떤 이유와 해결 방안을 제시하면 사용자도 용서할 수 있다.
자바 예외 처리
- try, catch, finally, throw, throws 키워드 사용
- Throwable : 예외 최상위
- Error : 메모리 부족 및 심각한 시스템 오류 등 애플리케이션에서 복구가 불가능한 시스템 예외
- Exception : 애플리케이션 로직의 실질적인 최상위 예외, 컴파일러가 체크하는 예외
- RuntimeException : 언체크 예외, 애플리케이션 실행 중 발생하는 런타임 예외
체크, 언체크 예외 키워드
- 체크 예외 : 예외를 잡아서 처리하지 않으면 항상 throws 키워드 사용
- 언체크 예외 : 예외를 잡아서 처리하지 않아도 throws 키워드 생략 가능 (에러 인지를 위해 수동 선언도 가능)
체크 예외 장단점
- 장점 : 개발자의 실수로 예외 누락을 컴파일러로 문제를 잡아 안전
- 단점 : 체크 예외를 반드시 잡거나 던지도록 처리 번거로움 발생
언체크 예외 장단점
- 장점 : throws 예외 선언을 생략 가능해 번거로움 해소
- 단점 : 개발자의 실수로 예외를 누락 가능성
섹션 10. 예외 처리 2 - 실습 [ 2회 복습 🔥 ]
public class NetworkClientExceptionV2 extends Exception { // Exception 상속
private String errorCode;
public NetworkClientExceptionV2(String errorCode, String message) {
super(message); // 에러 객체의 메시지를 저장, 나중에 getMessage()로 출력 가능
this.errorCode = errorCode;
}
public String getErrorCode() { // 코드 조회
return errorCode;
}
}
위와 같이 에러를 새로 정의할 수 있으며, 각종 포함되는 요소를 삽입할 수 있다. 'super(message)'를 통해 에러 메시지를 저장한다.
정상, 예외 흐름 분리
try {
client.connect();
client.send(data);
client.disconnect(); //예외 발생시 무시
} catch (NetworkClientExceptionV2 e) {
System.out.println("[오류] 코드: " + e.getErrorCode() + ", 메시지: " + e.getMessage());
}
- 하나 try 내부에 정상 흐름 삽입
- 예외 부분을 catch로 해결
- 중간에 에러가 발생하면 이후 정상 흐름은 중단하고, 예외 처리로 진행
- ex) 'client.connect()' 에러 발생 -> catch 예외 처리 진행
finally
try {
//정상 흐름
} catch {
//예외 흐름
} finally {
//반드시 호출해야 하는 마무리 흐름
}
- try로 시작한 흐름은 무조건 자동으로 finally 호출
- catch에서 잡을 수 없는 에러도 finally 코드 블록은 호출
- 처리 불가능 에러 발생 -> finally 코드 블록 호출 -> 예외 throws
예외 계층
try {
client.connect();
client.send(data);
} catch (ConnectExceptionV3 e) {
System.out.println("[연결 오류] 주소: " + e.getAddress() + ", 메시지: " + e.getMessage());
} catch (SendExceptionV3 e) {
System.out.println("[전송 오류] 전송 데이터: " + e.getSendData() + ", 메시 지: " + e.getMessage());
} finally {
client.disconnect();
}
예외를 계층화하면 위 예시 코드처럼, 상황에 맞는 에러를 출력할 수 있다.
try {
// 1번 ConnectExceptionV3 발생
// 2번 NetworkClientExceptionV3 발생
// 3번 정의되지 않은 오류, 런타임 등
} catch (ConnectExceptionV3 e) { // 1번 처리
} catch (NetworkClientExceptionV3 e) { // 2번 처리
} catch (Exception e) { // 3번 처리
}
또한, 계층화 구조로 'NetworkClientExceptionV3'에 자식 에러들을 모두 처리할 수 있다.
catch (ConnectExceptionV3 | SendExceptionV3 e)
예외를 위 예시와 같이 한 번에 처리도 가능하다.
체크 예외 처리 방안
예외의 종류는 다양하게 존재한다. 하나의 로직을 실행할 때도 DB, TCP/IP 등 다양한 요소 사용과 함께 오류도 다양하다. 하지만, 이 예외를 모두 처리가 가능할까? 만약 하나의 로직에도 에러 처리가 10개라면 로직을 구성하는 거보다 에러가 훨씬 많다. 또한, 라이브러리에서 발생하는 오류도 있기에 처리가 쉽지 않다. 또한, 예외 처리 로직을 구성한다고 해도 로그를 찍거나 안내 메시지를 하는 해결이 대부분이다. 이러한 문제를 해결하기 위해 부분 에러는 처리하고 나머지는 공통 메시지를 출력해 사용자 전달 및 로그를 남기면 된다.
위 예시와 같이 예외를 결국 throw 하게 되면 복잡한 코드가 만들어진다. 그럼 "Exception으로 처리하면 한번 가능할 텐데?" 맞다. Exception은 에러 객체의 최상위 타입이다. 그러나 최상위 타입으로 지정하면 결국 잡아야 하는 에러까지 전부 다 던지게 되어 버린다. 중요한 에러 로직도 놓치게 된다.
언체크 예외 처리 방안
언체크 예외는 오류를 던질 때, 따로 작성하지 않아도 된다. 그렇기에 중간에 처리할 에러는 중간에 얼마든지 잡을 수 있고, 해결하지 못하는 에러는 공통 예외 처리에서 한 번에 잡아서 처리할 수 있다.
- e.printStackTrace() : 예외 메시지와 스택 트레이스 출력
Try with resources
public class NetworkClientV5 implements AutoCloseable {
...
@Override
public void close() {
System.out.println("NetworkClientV5.close");
disconnect();
}
}
- close 메서드는 AutoCloseable 인터페이스가 제공
- try가 끝나면 자동 호출
- 장점
- 리소스 누수 방지 : finally 블록 안에서 자원 해제 코드를 누락하는 문제 예방
- 코드 간결성 및 가독성 향상 : close 자동 해제로 호출이 따로 필요 X
- 스코프 범위 한정
- 더 빠른 자원 해제 : 다른 사용자가 남은 자원으로 빠르게 접속 가능
'✍️ 정리 > Java' 카테고리의 다른 글
[Inflearn] 김영한의 실전 자바 - 중급 1편 정리 (A) (0) | 2024.07.17 |
---|---|
[Inflearn] 김영한의 실전 자바 - 기본편 정리 (B) (0) | 2024.07.15 |
[Inflearn] 김영한의 실전 자바 - 기본편 정리 (A) (0) | 2024.07.06 |
Backend 개발자를 꿈꾸는 꿈나무💭 기술 블로그
꾸준함을 목표로 하는 꿈나무 개발자 택이✌️입니다. 궁금하신 점이나 잘못된 정보가 있으면 언제든지 연락주세요. 📩 함께 프로젝트 및 스터디도 언제든지 희망합니다! 📖