오늘은 코드카타 알고리즘과 SQL 2번 문제를 풀고, Java 문법 종합반 강의의 4강을 들었다.
그리고, 계산기 프로젝트의 Lv.2를 완성했다.
오늘 진행한 SQL 문제는 order by desc를 통해 데이터를 역순으로 정렬하는 것이었다.
이 내용은 사전캠프 때 많이 다뤄본 내용이라 큰 어려움 없이 문제를 풀 수 있었다.
그리고, 알고리즘 문제는 어제의 내용과 이어서 지정된 범위 내의 두 수를 입력받았을 때, 두 수의 곱셈을 진행하는 메서드를 만드는 것이었다.
이 문제를 풀면서 새로운 사실을 알게 되었는데, 메서드에 반환자료형을 정해두면 반드시 반환값이 있어야만 한다는 것이다.
처음에 if문을 통해 해당 범위 안에서만 곱셈을 진행하고, return을 해주도록 작성을 했더니 코드가 실행되지 않았다.
그 이유는 else의 경우에서는 return을 하지 않기 때문이었다.
int answer = 0;
if (num1 >= 0 && num1 <= 100 && num2 >= 0 && num2 <= 100) {
answer = num1 * num2;
}
return answer;
그래서 위와 같이 미리 변수를 선언하고, if문 안에서는 계산만 진행한 뒤, 반환값을 설정해주어야 했다.
각각의 문제에 대한 나의 풀이는 깃허브를 통해 업로드해두었다.
GitHub 보러가기
4강에서는 자바에서 활용할 수 있는 여러 기능들을 소개하고 있는데,
오늘은 그 중 예외를 처리하는 방법, null을 다루는 방법, 컬렉션을 사용하는 방법, 그리고 제네릭 클래스와 메서드를 사용하는 방법에 대한 강의를 들었다.
먼저, 프로그램을 실행하다보면 예상하지 못한 상황, 즉 예외가 발생하기도 한다.
예외가 닥치면 프로그램이 비정상적으로 중단되기 때문에 예외를 미리 처리하는 것이 중요하다.
하지만, 한편으로는 미성년자의 접근을 막는 등 특정 조건에서 의도적으로 예외를 발생시키기도 한다.
예외는 크게 2가지 종류로 나눌 수 있는데, 체크 예외와 언체크 예외이다.
체크 예외는 복구 가능성이 있는 예외로, 예외를 처리하는 코드와 함께 작성해야만 하고, 예외 처리를 하지 않을 경우 컴파일 에러가 발생한다.
언체크 예외는 복구 가능성이 없는 예외이기 때문에 예외 처리를 강제하지 않고, 예외를 처리하지 않아도 컴파일 에러는 따로 발생하지 않는다.
예외를 처리하기 위해서는 try - catch문을 사용한다.
try {
callUncheckedException(); // 예외 생성
} catch (RuntimeException e) { // RuntimeException이 발생했을 때 처리하고 싶은 내용
System.out.println("언체크 예외 처리");
} catch (Exception e) { // Exception이 발생했을 때 처리하고 싶은 내용
System.out.println("체크 예외 처리");
}
위와 같이 try 안에 원하는 내용들을 입력하고, catch를 통해 각각의 에러가 일어났을 때 어떻게 처리할 것인지를 정해주면 된다.
체크 예외는 반드시 예외 처리를 해주어야 하는데, throws를 통해 예외를 호출한 곳에게 예외 처리의 책임을 전가해줄 수도 있다.
public void callCheckedException() throws Exception { // throws로 Exception 예외를 상위로 전파
...
throw new Exception();
...
}
위와 같이 작성을 한다면, main 메서드에서 위 메서드를 호출했을 때 예외 처리를 강제하게 된다.
두 번째로 다룬 내용은 Optional 객체를 통해 null을 안전하게 다루는 방법이다.
null을 반환할 때는 NullPointerException이라는 언체크 예외가 생기게 된다.
if문을 통해 null을 직접 처리할 수는 있지만, 모든 가능성을 미리 예측하고 처리하는 것은 현실적으로 어렵기 때문에 Optional 객체를 활용하는 것이다.
ofNullable()을 사용해 null이 반환될 수 있는 객체를 감싸고,
객체 내부에 값이 존재할 경우 true를 반환하는 isPresent() 메서드 혹은 값이 없을 때 기본값을 제공하는 orElseGet() 메서드를 활용할 수 있다.
세 번째로 다룬 내용은 자료 구조들을 쉽게 사용할 수 있도록 하는 컬렉션이다.
언뜻 보기에는 배열과 비슷하지만, 배열은 크기가 고정되어 있어 길이를 변경할 수 없는 반면, 컬렉션은 길이를 동적으로 변경할 수 있다.
컬렉션객체<자료형> 변수이름 = new 컬렉션객체<자료형>();
ArrayList<Integer> arrayList = new ArrayList<Integer>();
위와 같은 형식으로 컬렉션을 선언할 수 있다.
| 인터페이스 | 특징 | 구현체 |
|---|---|---|
| List | 순서 O, 중복 O | ArrayList, LinkedList, Stack 등 |
| Set | 순서 X, 중복 X | HashSet, TreeSet |
| Map | 키-값 구조, 키 중복 X | HashMap, Hashtable, TreeMap |
컬렉션에는 크게 3가지 종류가 있고, 각각의 인터페이스마다 다양한 구현체가 있는데, 구현체마다의 특징이 조금씩 다르다.
필요에 따라 원하는 구현체를 선언해 사용할 수 있다.
add, remove, get와 같이 데이터를 추가하고, 삭제하고, 조회하는 명령을 실행할 수 있는데,
순서를 보장하지 않는 Set에서는 get은 사용할 수 없다.
마지막으로 다른 내용은 클래스, 메서드 등에 매개변수 타입을 미리 지정하지 않고 사용 시점에 유연하게 사용할 수 있도록 하는 제네릭이다.
이를 통해 다양한 타입에서 동일한 코드를 재사용할 수 있고, 잘못된 타입 사용을 컴파일 시점에 방지할 수 있다.
public class GenericBox<T> { // 제네릭 클래스
private T item;
public GenericBox(T item) {
this.item = item;
}
public T getItem() {
return this.item;
}
// 일반 메서드 T item : 클래스의 <T> 를 따라감
public void printItem(T item) {
System.out.println(item);
}
// 제네릭 메서드 : <S>는 <T>와는 별개
public <S> void printBoxItem(S item) {
System.out.println(item);
}
}
위와 같이 클래스 전체에 T 매개변수를 선언해줄 수도 있고,
특정 메서드에서만 작동하는 S 매개변수를 선언해줄 수도 있다.
자세한 설명과 실습 내용은 노션을 통해 정리해두었다.
Notion 확인하기
오늘은 Lv.2 계산기를 만들었는데, Lv.2 계산기는 자바의 객체지향 개념을 적용하여 클래스를 정의하고, 연산을 수행하는 메서드를 만들어서 구현된다.
사실 구체적인 가이드가 제시되어서 가이드에 있는 단계별로 하나씩 만들어가니 큰 어려움은 없었다.
강의 문서를 참고하고, 이것저것 구글링도 많이 해봐야 했지만, 큰 이슈 없이 잘 구현할 수 있었다.
그런데, 어제 Lv.1의 계산기를 구현하면서는 모든 내용이 하나의 클래스 안에 있었기 때문에 내가 생각한 예외 상황들을 if문을 통해 처리할 수 있었다.
하지만, 지금은 일부는 계산 클래스에, 일부는 앱 클래스에 있기 때문에 예외들을 어떻게 처리할 수 있을지를 모르겠다.
try-catch문을 통해 처리를 해보려고 했지만, 계속 오류가 나서 이 부분들에 대해서는 내일 다시 진행을 해보려고 한다.
사실 2단계 계산기를 구현하기 전에는 좀 쫄아있었는데, 막상 해보고 나니까 생각보다 간단하게 해결할 수 있었다.
어제보다 조금 더 똑똑해진 것 같은 느낌이다.
아직은 이건 왜 이렇게 작동하고, 왜 다르게는 작동될 수 없는지 등등 많은 의문점들이 생기고 있지만,
하나씩 해결해가면서 자바를 정복해봐야겠다.