Today's 알고리즘 코드카타
class Solution {
public int solution(int num1, int num2) {
if (num1 < 0 || num1 > 100 || num2 < 0 || num2 > 100) {
throw new IllegalArgumentException("입력값은 0 이상 100 이하여야 합니다.");
}
int answer = num1 * num2; // 선언과 동시에 계산
return answer;
}
}
class Solution {
public int solution(int num1, int num2) {
int answer = 0; // 변수 선언
if (num1 >= 0 && num1 <= 100 && num2 >= 0 && num2 <= 100) {
answer = num1 * num2; // 따로 조건값 설정
}
return answer;
}
}
위 두가지 방식에 어떤 차이가 있는걸까?
class Solution {
public int solution(int num1, int num2) {
if (num1 < 0 || num1 > 100 || num2 < 0 || num2 > 100) {
throw new IllegalArgumentException("입력값은 0 이상 100 이하이어야 합니다.");
}
if (num2 == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return num1 / num2; // 단순 계산이므로 answer 변수 선언없이 해봤다
}
}
복잡한 작업 중이라면 변수를 선언해두는게 좋을 듯하다.
class Solution {
public int solution(int age) {
int answer = 0;
if (age > 0 && age <= 120) {
answer = 2022 - age + 1; // 단순하고 명확한데 뭔가 더 고급?스러운 방법이 없을까
} else {
throw new IllegalArgumentException("0세 이상 120세 이하의 나이만 계산 가능합니다.");
}
return answer;
}
}
기본 양식을 지키면서 만들어보려 노력
Java 문법 3주차
예외(Exception)과 예외처리(try-catch)
예외 : 프로그램 실행 중 예상하지 못한 상황
예외처리 : 예외 발생 시 프로그램 종료를 방지하고 처리
RuntimeException - UncheckedException (컴파일러가 확인 X)
Exception - CheckedException (컴파일러가 확인 O - 코드 밑 빨간줄 생김)
예외 전파 : 메서드에서 발생한 예외가 해당 메서드 내에서 처리되지 않았을 때 메서드를 호출한 상위 메서드로 전달되는 과정
public class ExceptionPractice {
// 1. 언체크 예외 호출 예시
// public void callUncheckedException() {
// try { // 하위 메서드에서 예외 처리
// if (true) { // 예외 발생 로직
// System.out.println("언체크 예외 발생");
// throw new RuntimeException();
// }
// } catch (RuntimeException e) { // e는 catch 블럭 안에서만 유효하다
// System.out.println("언체크 예외 처리");
// } catch (Exception e) { // catch 여러개 가능
// System.out.println("체크 예외 처리");
// }
// }
// 2. 체크 예외 호출 예시
public void callCheckedException() throws Exception { // 상위 메서드에서 처리할테니 냅둬
if (true) {
System.out.println("체크 예외 발생");
throw new Exception(); // 빨간줄 발생
}
// try {
// if (true) {
// System.out.println("체크 예외 발생");
// throw new Exception();
// }
// } catch (Exception e) {
// System.out.println("체크 예외 처리");
// }
}
}
public class Main {
public static void main(String[] args) {
// 1. 의도하지 않은 예외
// int ret = 10 / 0;
// System.out.println("ret = " + ret);
// System.out.println("프로그램 종료");
// 2. 의도적인 예외 - throw (특정 상황에 예외를 명확하게 정의 및 제어)
// int age = 10;
// if (age < 18) { // 미성년자 접근 > 예외 발생 강제
// throw new IllegalArgumentException("미성년자는 접근할 수 없습니다.");
// }
// System.out.println("프로그램 종료");
// 3. 언체크 예외 호출
// ExceptionPractice exceptionPractice = new ExceptionPractice();
// exceptionPractice.callUncheckedException(); // 하위 메서드로 호출
// System.out.println("프로그램 종료"); // 예외 발생 > 상위 메서드로 전파 > 출력 X
// ExceptionPractice exceptionPractice = new ExceptionPractice();
// try {
// exceptionPractice.callUncheckedException();
// } catch (RuntimeException e) { // 상위 메서드로 전파된 예외 처리
// System.out.println("언체크 예외 처리");
// }
// 4. 체크 예외 호출
ExceptionPractice exceptionPractice = new ExceptionPractice();
try {
exceptionPractice.callCheckedException(); // 여기에 빨간줄 발생
} catch (Exception e) {
System.out.println("체크 예외 처리");
}
System.out.println("프로그램 종료");
}
}
예외처리 실습과제
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true) { // 무조건 반복
try {
System.out.println("아이디: ");
String id = sc.nextLine();
System.out.println("비밀번호: ");
String pw = sc.nextLine();
login(id, pw); // 생소하지만 기능을 호출하는 것이다 | 매개변수가 2개인 login() 메서드를 호출
System.out.println("로그인 성공!");
break; // 로그인 성공 시 종료
} catch (Exception e) {
System.out.println(e.getMessage()); // 예외 객체가 가지고 있는 메시지(설명) 꺼내옴
}
}
}
public static void login(String id, String pw) throws Exception { // 상위(main)에서 처리하이소
if (!id.equals("admin") || !pw.equals("1234")) {
throw new Exception("로그인 실패! 아이디 또는 비밀번호가 잘못되었습니다.\n");
}
}
}
Optional
null (값이 없음 또는 참조하지 않음을 나타냄)을 안전하게 다루게 해주는 객체
-- Student --
public class Student {
private String name; // 학생의 이름을 저장하는 private 변수 (외부에서 직접 접근 불가)
public Student(String name) { // Student 객체 생성 시 이름을 전달받아 초기화하는 생성자
this.name = name; // 전달받은 name 값을 멤버 변수 name에 저장
}
public String getName() { // 외부에서 name 값을 읽을 수 있도록 하는 Getter 메서드
return name; // 저장된 name 값을 반환
}
}
-- CampService --
public class CampService {
private Student student; // CampService 내부에서 관리할 단일 학생 정보를 담는 변수
public Optional<Student> getStudent() { // 학생 정보 조회 메서드 (null일 수 있으므로 Optional로 감싸 반환)
return Optional.ofNullable(student); // student가 null이면 Optional.empty()를 반환함
}
public void setStudent(Student student) { // 외부에서 학생 데이터를 CampService에 주입하는 메서드
this.student = student; // 전달받은 학생 객체를 내부 변수에 저장
}
}
-- Main --
public class Main {
public static void main(String[] args) {
Student spartan = new Student("Spartan");
// “spartan”이라는 이름의 Student 타입의 변수 선언, "Spartan" 이라는 객체를 생성해서 초기값으로 넣었다. = 초기화했다.
CampService campService = new CampService(); // 캠프 서비스 객체 생성
campService.setStudent(spartan); // Spartan 학생을 CampService에 등록
Optional<Student> studentOptional = campService.getStudent(); // CampService에서 등록된 학생 정보 가져오기 (Optional로 감싸짐)
boolean flag = studentOptional.isPresent(); // Optional 내부에 값이 있으면 true, 없으면 false 반환
if (flag) {
Student student = studentOptional.get(); // Optional<Student>에서 꺼낸 객체를 변수 student에 담음
String studentName = student.getName(); // 학생 이름을 가져옴
System.out.println("studentName = " + studentName); // Spartan 출력됨
} else {
System.out.println("학생 데이터가 없습니다."); // 등록이 안 됐거나 null일 경우 출력
}
}
}
주석을 달면서 정리해보니 이해가 잘 된다.
컬렉션
배열과 달리 컬렉션은 길이를 동적으로 변경할 수 있어 유연하다!
ArrayList : 순서 유지, 중복 허용
HashSet : 순서 없음, 중복 불가
HashMap : 키-값 구조, 키 중복 불가
public class Main {
public static void main(String[] args) {
// 배열의 한계 (정적)
// 선언과 동시에 길이를 설정 필요
int[] numbers = new int[2];
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30; // 고정된 길이를 초과 → 에러 발생
// 컬렉션
ArrayList<Integer> arrayList = new ArrayList<Integer>(); // 어레이리스트로 정수를 다룰거야
arrayList.add(10);
arrayList.add(20);
arrayList.add(30); // 길이 제한 없음
// ArrayList 활용
ArrayList<String> names = new ArrayList<>();
names.add("Spartan"); // 데이터 추가
names.add("Steve");
names.add("Spartan"); // 중복 데이터 허용
String name1 = names.get(0); // 데이터 단건 조회 가능
names.remove("Steve"); // 데이터 삭제
// HashSet 활용
HashSet<String> uniqueNames = new HashSet<>();
uniqueNames.add("Spartan"); // 데이터 추가
uniqueNames.add("Steve");
uniqueNames.get(); // get() 활용 X
uniqueNames.add("Spartan"); // 중복 데이터 X
uniqueNames.remove("Spartan"); // 삭제 O
// HashMap 활용
// <키, 값> → 저장
HashMap<String, Integer> memberMap = new HashMap<>();
memberMap.put("Spartan", 15);
memberMap.put("Steve", 24);
// 키 중복 불가 : 값 덮어쓰기 발생
memberMap.put("Steve", 5); // 출력 시 "Steve", 24 → 5 로 덮어씌워짐
// 단건 조회
Integer spartan = memberMap.get("Spartan"); // 키로 조회 > 값 출력
memberMap.remove("Spartan"); // 삭제 O
Set<String> strings = memberMap.keySet(); // 키 확인
Collection<Integer> values = memberMap.values(); // 값 확인
}
}
제네릭(Generic)
제네릭: 타입을 미리 지정하지 않고 사용 시점에 유연하게 결정할 수 있음
타입 소거(Type Erasure): 컴파일 시점에 제네릭 타입정보를 제거하는 과정
제네릭 클래스: 타입 매개변수를 선언해 다양한 타입의 인스턴스를 만들 수 있는 클래스
제네릭 메서드: 메서드 내부에서 타입 매개변수를 사용해 여러 타입에 대응할 수 있도록 정의한 메서드
public class GenericBox<T> {
private T item;
public GenericBox(T item) {
this.item = item;
}
public T getItem() {
return item;
}
// 일반 메서드
public void printItem(T item) {
System.out.println(item);
}
// 제네릭 메서드
public <S> void printBoxItem(S item) { // S가 아니어도 무관
System.out.println(item);
}
}
-- Main --
// 제네릭 활용
// 1. 재사용성 보장 (타입소거: T → Object)
GenericBox<String> strGBox = new GenericBox<String>("ABC");
GenericBox<Integer> intGBox = new GenericBox<Integer>(100);
GenericBox<Double> doubleGBox = new GenericBox<>(0.1);
// 2. 타입 안정성 보장 (타입소거: 자동으로 down casting 삽입)
String item1 = strGBox.getItem();
Integer item2 = intGBox.getItem();
Double item3 = doubleGBox.getItem();
// 일반 메서드
strGBox.printItem("ABC"); // 위에 선언한 String 기준으로 타입 소거 발생
strGBox.printItem(100); // 사용 불가
strGBox.printItem(0.1); // 사용 불가
// 제네릭 메서드 (독립적인 타입 매개변수를 가짐)
strGBox.printBoxItem("ABC");
strGBox.printBoxItem(123); // String과 상관 없음
strGBox.printBoxItem(1.23);
오늘의 회고
오늘은 단순히 AI에게 물어보고 설명을 듣는 방식에서 벗어나, 역으로 질문을 받아 스스로 설명하고 개념을 정리해보는 경험이 새로웠다. 퀴즈를 통해 메서드, 객체, 클래스, 변수의 의미를 다시 정의해보고, 내가 진짜로 이해하고 있는지를 점검했다는 점에서 큰 의미가 있었다. 직접 답하면서 떠오른 비유나 개념들을 정리하는 순간순간들이, 단순 정보 습득을 넘어서 진짜 내 언어가 되어가는 과정이라는 생각이 든다.
그럼에도 아쉬움은 있었다. 처음 접하는 제네릭, 타입 소거 같은 용어들에 익숙하지 않다 보니, 문장을 한 줄 읽는 데도 시간과 에너지가 많이 들어갔다. 배움의 속도가 더뎌진 느낌에 살짝 답답함도 있었지만 그건 내가 제대로 짚고 가고 있기 때문이라는 생각도 해본다. 건너뛰지 않고, 모르는 걸 질문하고, 다시 정리해보며 내 것으로 만들고 있다고 느꼈다.
내일은 3주차 강의를 완강하고, 오늘처럼 하나하나씩 개념들을 쌓아갈 것이다. 그리고 그 배운 것들을 활용해 과제를 2단계까지 마무리해보는 것으로 목표를 정했다. 아마 쉽지 않을 것 같지만 스스로 고민해서 답을 찾으려고 노력할 생각이다.