이번 주는 아마 내 기억이 맞다면 객체지향프로그래밍의 꽃이라고 할 수 있는 다형성부터 시작해서,
기존에 잘 알지 못했던 입출력, 예외처리, 입출력, 제네릭, 컬렉션, Enum, 람다, 스트림, 스레드의 넓은 범위의 내용을 학습했다.수업시간에 들었던 내용을 기반으로 적혀있는 관련 문법을 이해하는 것은 가능해겠지만, 세부적인 내용을 기억하거나, 안보고도 어느정도 해당 문법을 구사할 수 있는 레벨이 되려면 무조건 복습을 해야하는 상황이다..
양이 엄청 많아서 좀 걸리겠지만 차분하게 정리해보자.
[수업 내용]
- 얕은 복사와 깊은 복사

-얕은 복사 : stack에 저장된 해당 레퍼런스 변수가 가리키는 주소값을 그대로 복사해와 저장(즉, 완전히 동일한 heap 영역을 가리킴)
-배열의 경우 단순히 메소드의 매개변수로 배열을 받아오거나, 메소드의 리턴 결과로 배열을 반환하면 얕은 복사가 일어남

-깊은 복사 : heap에 저장되어있는 배열이나 객체의 내부 값을 그대로 갖는 새로운 배열이나 객체를 새로 만들고 이 주소를 레퍼런스 변수가 가리키게 함.
- 깊은 복사를 구현하기 위한 방식 4가지
- for문을 돌면서 일일이 값 복사
- Object 클래스의 clone 메소드
- System 클래스의 arraycopy 메소드
- Arrays의 copyOf메소드(내부적으로 System의 arraycopy 메소드 사용하여 구현됨) - 처음부터 특정 길이만큼을 복사
-String의 경우 레퍼런스 변수이므로 메소드 호출 인자로 넘어가면 얕은 복사가 일어난다.
하지만, 불변 객체라는 특성 때문에 내부적으로 str1 += str2와 같은 연산을 하게 되면 새로운 객체를 할당받게 되므로, 원본 객체가 영향받을 일은 전혀 없다.
-String의 경우 literal을 직접 할당한 경우와, new String()을 통해 생성한 경우 다르게 관리된다. 전자의 경우 Heap영역에서 String Pool이라는 특수한 공간에 의해 캐싱되어 관리되나, 후자의 경우 다른 객체로서 존재하게 된다.
- 객체 지향 프로그래밍의 3대 특성
-캡슐화 : 해당 클래스 내부 요소(필드, 메서드 등)에 대해 적절한 접근 제한자를 설정하여 클래스 외부에서의 직접적인 접근을 막음 -> public, protected등 비교적 널널한 제한으로 설정된 메소드에서만 해당 필드를 접근할 수 있도록 구현되는 것이 보통
-상속 : 부모 클래스나 인터페이스의 내용을 자식 클래스에서 사용할 수 있도록 물려받음(재사용성)
-다형성 : 부모 클래스나 인터페이스의 레퍼런스 변수를 통해 자식 클래스 레벨의 인스턴스에도 접근할 수 있도록 함으로써 동일한 메소드 이름으로 다른 동작을 가능하게 함(동적 바인딩). 유지보수성, 확장성을 확보
- 추상 클래스
-내부적으로 추상메소드를 0개 이상 포함하는 클래스를 의미함
-스스로 생성자를 활용한 인스턴스 생성이 불가능함
-상속받는 자식 클래스 레벨에서 추상메소드에 대한 오버라이딩이 강제화됨
-다중 상속이 불가능하다
- 인터페이스
-추상메소드와 상수 필드만을 가질 수 있는 클래스의 변형체
-모든 메소드는 묵시적으로 public abstract 속성을 가진다.
-모든 추상 메소드는 이를 상속받는 자식이 클래스인 경우 오버라이딩이 강제화된다, 단 인터페이스가 이를 상속받는 경우는 제외한다.
-다중 상속이 가능하다(구체적인 자식 클래스가 한 개의 추상 클래스 + 여러 개의 인터페이스를 상속받는 구조도 가능)
- StringBuilder
-내부 버퍼에서 문자열을 수정하고, 문자열이 변경될 때마다 새로운 객체를 만들지 않으므로, 수정이 많이 일어나는 경우에 메모리를 더 적게 사용한다. (ex. 문자열을 계속해서 + 를 통해 이어나가는 상황)
-append(String str) : 문자열을 끝에 추가
-insert(int index, String str) : 지정된 index에 문자열을 삽입
-delete(int start, int end) : start부터 end - 1 인덱스까지의 문자열을 삭제
-deleteCharAt(int index) : 주어진 인덱스의 문자를 삭제
-replace(int start, int end, String str) : 지정된 범위의 문자를 새로운 문자열로 교체
-reverse() : 문자열을 역순으로 반전시킴
- 예외처리
-코드의 안정성과 신뢰성을 높이고 개발 시 디버깅을 용이하게 하기 위한 목적

-최상위 클래스 Throwable을 기준으로 아래 Error, Exception 하위 클래스가 존재한다
-이 중 Checked Exception에 해당하는 것은 반드시 예외처리를 개발자가 해줘야한다.(ex. IOException)
- 예외 처리하는 3가지 방식
- Try-Catch 구문 작성
- Try {} Catch (Exception e) {} 형식으로 작성
- try 블럭 안에 예외 발생 가능성이 있는 코드들을 작성하고, catch 블럭 안에 예상되는 Exception과 그에 따른 수행 로직을 작성한다.
- 추가적으로
- 메소드 시그니처에 throws 구문 작성
- 메소드를 호출한 사람에게 책임 전가
- 최종 main 메서드 호출 스택에서도 처리되지 않으면 비정상적으로 프로그램이 종료됨
- Try-With-Resource 구문
- Try () {}
Catch (Exception e) {}의 형식으로 작성 - Try ()문 안에 자원을 할당받는 코드를 작성하면 이에 대한 반환을 자동적으로 처리해주는 구문(ex. FileReader, FileWriter)
- Try () {}
- Try-Catch 구문 작성
- 스트림(Stream)
-입출력 장치에서 데이터를 읽고 쓰기 위한 단방향 통로

-크게 바이트 기반의(InputStream, OutputStream), 문자 기반의 (Reader, Writer)로 분류됨
-System.in, out, err는 자바에서 키보드, 콘솔과 같은 표준 입출력 장치로부터 데이터를 입출력하기 위한 스트림임
- 기본적으로 FileInputStream, FileOutputStream을 통해 파일로부터 바이트 단위로 읽어오거나, 파일에 쓸 수 있으며,
FileReader, FileWriter를 통해 문자 단위로 읽고 쓸 수 있다.
- 보조스트림
-기반 스트림에 추가로 적용되어 스트림의 기능을 향상시키거나, 기능을 추가
- 입출력 성능 향상을 위한 버퍼링 적용된 보조 스트림
- BufferedInputStream/BufferedOutputStream, BufferedReader/BufferedWriter
- 형변환 보조 스트림
- InputStreamReader/OutputStreamWriter
- 바이트 스트림 <-> 문자 스트림 간 변환의 역할을 수행한다
- ex)OutputStreamWriter의 경우 아래와 같이 쓰이면, 문자열 스트림을 바이트 기반으로 변환하여 콘솔로 출력한다.
- InputStreamReader/OutputStreamWriter
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
- 기본 자료형 데이터 입출력
- DataInputSteam/DataOutputStream
- 바이트형식으로 파일에 출력하는 FileOutputStream등을 도와 각 자료형 별로 파일에 출력할 수 있게 함.
- writeUTF, writeInt, writeChar의 메소드로 자료형에 맞게 쓰고, readUTF, readInt, readChar를 통해 읽어온다
- 파일에 저장되어 있는 자료형 순서에 맞게 적절히 파싱하여 읽어와야 한다.
- DataInputSteam/DataOutputStream
- 객체 자료형 데이터 입출력
- ObjectInputStream/ObjectOutputStream
- 객체 형태의 스트림을 읽고 쓸 수 있음
- writeObject, readObject 메소드 활용
- ObjectInputStream/ObjectOutputStream
- 직렬화와 역직렬화
-객체를 파일 같은 외부자원의 형태로 펴는 것을 직렬화, 반대로 적혀있는 json 형식의 데이터를 객체로 변환하는 것은 역직렬화라고 할 수 있다
-직렬화가 가능한 것을 명시하기 위해 해당 객체 클래스에 implements Serializable을 추가해주어야 한다.
- 제네릭
-제네릭은 데이터 형식에 의존없이 여러 다른 데이터 타입을 가질 수 있는 문법
- 와일드카드 문법
- 제네릭으로 정의된 클래스가 메소드의 파라미터로 쓰일 경우, 타입을 원하는 범위로 제한하기 위한 문법
- <?>: 모든 타입을 허용하는 와일드 카드
- <? extends T>: T 타입 또는 T의 하위 타입을 허용하는 와일드 카드
- <? super T>: T 타입 또는 T의 상위 타입을 허용하는 와일드 카드
- 컬렉션

-List, ArrayList, LinkedList, Stack(LIFO), Queue(FIFO)
-Set, HashSet(해시테이블 구조, 내부적으로 HashMap을 필드로 두고 관리), LinkedHashSet(해시 테이블 + 저장한 순서 유지), TreeSet(키 기반 정렬)
-Map, HashMap
- 람다(Lambda)
-메소드를 하나의 식으로 표현
-(매개변수부) -> {}의 형태
-함수형 인터페이스는 @FunctionalInterface로 정의할 수 있으며, 이 애노테이션은 해당 인터페이스가 하나의 구현되지 않은 추상 메소드만을 가지는지 컴파일시 체크하도록 한다.
- 람다식을 함수형 인터페이스에 대입하면 그 인터페이스를 구현하는 익명 객체가 생성되고, 해당 함수형 인터페이스의 추상 메소드의 구현부로써 작성한 람다식이 자동적으로 동작하게 할 수 있다.
- 표준 함수적 인터페이스
- 자바 표준 라이브러리(java.util.function 패키지)에 정의된 함수형 인터페이스
- Consumer - 리턴 값이 없는 accept() 메소드, 그저 매개변수로 넘어온 값을 소비하는 역할
- Supplier - 매개변수가 없고 리턴값이 있는 getXXX() 메소드
- Function - 매개변수가 있고 리턴값이 있는 applyXXX() 메소드
- Operator - Function과 유사하게 동작하여 applyXXX() 메소드가 존재함, 매개변수를 이용해 연산한 후 동일한 타입으로 리턴
- Predicate - 매개변수를 받아 Boolean 값을 반환하는 testXXX() 메소드
- 자바 표준 라이브러리(java.util.function 패키지)에 정의된 함수형 인터페이스
- 메소드 참조
- 함수형 인터페이스의 추상메소드에 람다식이 아니라 일반 메소드를 참조시킴
- 함수형 인터페이스의 매개변수 타입/갯수/반환형과 일반 메소드의 매개변수 타입/갯수/반환형이 동일해야 함
- 클래스 이름::메소드 이름, 인스턴스 참조변수 이름::메소드 이름의 형태로 사용
- 생성자 참조
- 클래스 이름::new형태로 사용
- 스트림(Stream)
-컬렉션과 같은 자료형에 대해 각 요소들을 하나씩 순회하면서 처리할 수 있는 기능
-parallelStream()메소드를 통한 병렬 처리도 가능
-원본을 변경하지 않는 읽기 전용이다
-크게 스트림 생성 - 필터링, 매핑 등의 가공 - 최종 결과 의 단계로 이루어진다.
- 스트림 가공 기법들
| 필터링 | filter(), distinct() |
| 변환 | map(), flatMap() |
| 제한 | limit(), skip() |
| 정렬 | sorted() |
| 결과 확인 | peek() |
-map() : Function 타입의 람다식을 인자로 받아(매개변수 -> 리턴, ex. i -> i * 5) 가공하고 새로운 스트림에 담아 반환
-filter() : Predicate 타입의 람다식을 인자로 받아(매개변수 -> Boolean, ex. i -> i % 2 == 0) 해당 조건이 참인 경우만 필터링하여 스트림에 담아 반환
-sorted() : Comparator타입의 인자를 받아 해당 정렬함수대로 스트림을 정렬(int compare 메소드를 오버라이딩, 값이 양수면 swap 일어남)
- 스트림 결과 기법들
-max(), min(), count(), sum()
-reduce : 이전 연산에서 반환된 값이 다음 연산의 첫 번째 인자로 전달되며 연산 진행
-collect : Collector 타입을 받아서 처리, 스트림을 .collect(Collectors.toList())를 통해 불변 리스트로 변경할 수 있음
collect(Collectors.joining())를 통해 특정 문자열로 split 처리할 수 있으며,
collect(Collectors.groupingBy())를 통해 특정 요소에 대해 그룹화 한 결과물을 Map의 형태로 반환할 수 있다.
-match : Predicate 형식의 람다 함수를 받아 true, false를 반환할 수 있다.
[마무리하며]
내용이 많고 복잡하게 느껴지는 부분이 있다. 솔직히 완벽하게 다 외우는 것은 말이 안되고, 그때그때 찾아 활용할 수 있을 정도로 숙지하는 것을 최우선 목표로 두고 복습하였다. 곧 있으면 Spring을 학습하기 시작할 텐데, 받아들여야 하는 정보의 양이 정말 많을 것이라고 예상된다. 미리미리 습관을 들이기 위해 다음 주부터 학습하게 될 자료구조 & 알고리즘에 대해 매일매일 그날의 내용을 복습하는 것을 목표로 다음 주는 달려봐야겠다.
다음 주 토요일 SQLD 시험이 있으니 준비를 슬슬 집중적으로 해야겠다.
현재 유선배 SQLD 과외 책을 한 번 정독한 상태인데, 문제 풀이는 또 다른 영역으로 다가오는 것 같아 연습을 통해 익숙해질 필요가 있어 보인다.
또 최근 Clean Code 스터디를 시작하였다. 매주 목요일 수업 이후 진행하는데, 기본적으로 읽기만 해도 술술 익히면서 그 코드의 목적이 잘 와닿는 코드가 깨끗하며 유지보수하기 좋은 코드라는 것이 핵심인듯 하다. 여태까지는 그때그때 돌아가기만 하는 것을 목적으로 작성했던 코드들이 많은데, 곧 다가올 백엔드 프로젝트에 적용하여 어느정도 책의 내용을 내껏으로 소화하고 싶다.
초심을 잃지 않는 것이 제일 큰 목표였는데, 20퍼센트 가량 지나친 현 시점의 마음가짐은 약간 그렇지 못한 것 같다.
제일 큰 문제는 그날그날의 계획이 없고 그때그때 즉흥적으로 하고 싶은걸 하고, 미루는 등 자꾸 비교적 의미없는 하루가 지속되다보니 생활력이나 동기 부여도 무뎌지는 것 같다.
수업 이후 복습 및 필요한 추가적인 학습을 하는 규칙적인 루틴을 확립하고, 그날그날의 목표와 계획을 잡아두고 생활하는 것이 좋아보인다.
'Beyond SW > 주간 회고' 카테고리의 다른 글
| 한화시스템 Beyond SW캠프 15기 8주차 회고 (0) | 2025.03.17 |
|---|---|
| 한화시스템 Beyond SW캠프 15기 7주차 회고 (7) | 2025.03.09 |
| 한화시스템 Beyond SW캠프 15기 5주차 회고 (0) | 2025.03.02 |
| 한화시스템 Beyond SW캠프 15기 4주차 회고 (1) | 2025.02.17 |
| 한화시스템 Beyond SW캠프 15기 3주차 회고 (2) | 2025.02.09 |