자바 공부
우아한테크코스와 함께 하는 자바 공부
1주차 - 숫자야구게임
요구사항
- 자바 코드 컨벤션 지키기
- indent 2까지만
- 3항 연산자 쓰지 않기
- 함수가 한가지 일만 하도록 최대한 작게 만들기
- System.exit 이용하지 않기
- 비정상적 입력에 대해 IllegalArgumentException 발생
배운 것
static과 final
- static : 클래스의, 공통적인 의미로 인스턴스 변수는 하나의 클래스로부터 만들어졌더라도 다른 값을 갖지만, static 변수는 인스턴스에 관계없이 같은 값을 갖는다. static은 멤버변수, 메서드, 초기화 블럭에 사용 가능하며 사용하는 것이 더 편리하고 속도도 빠르다.
- final : 마지막의, 변경될 수 없는 의미로 거의 모든 대상에 사용될 수 있다. 사용하면 값을 바꿀 수 없는 상수가 되고 메서드는 오버라이딩 불가, 클래스는 자식 클래스 정의 불가하게 된다.
Random 함수의 사용
new Random() 으로 선언하고 random.nextInt(n)을 하면 0~n-1까지의 난수를 출력하므로 1+random.nextInt(9-1+1)를 하면 1부터 9까지가 출력된다. 주어진 RandomUtils 사용하면 UPPER 9, LOWER 1 사용하면 된다.
Integer와 int의 차이
Integer는 Wrapping 객체이다. 따라서 .equals로 비교, int는 자료형으로 ==와 같은 산술 연산 가능 비교
stream의 사용
- 배열의 stream 생성: Stream.of(array)
- List의 stream 생성: list.stream()
- match: anyMatch(i->i==number), noneMatchanyMatch(i->i==number), allMatchanyMatch(i->i==number)와 같이 조건에 따라 true, false로 출력
- filter(): filter(i->i%2==0)과 같이 조건에 맞게 걸러내기
- forEach(): 걸러낸 것들이나 뭐 각각에 forEach(System.out.print(“h”))와 같이 함수 적용하기
- sorted(): 정렬 가능
- distinct(): 중복 제거
- map(): map(s->s.substring(0, 1))과 같이 형의 변환하면서 매핑
- collect(): stream의 요소들을 List나 Set 자료형으로 변환, joining(ex; .stream().map(Card::toStringInfo).collect(Collectors.joining(“,”))), sorting, 평균값 등 리턴
- intStream은 sum(), average(), max(), min() 계산 가능
- intStream.range(0, 3).filter(index -> numbers.get(index).equals(randomnumbers.get(index))).count() 하면은 index에 따른 비교할 수 있다.
메서드의 참조
Player::calculate() 과 같이 메서드 참조 사용 가능
상수 분리
private static final int CONSTANT_NAME = 1; 과 같이 변하지 않는 고정 상수, 문자열 빼서 정의해주기, 얘네들도 올바른 객체에 필요한 위치에 있는지 확인해주기
객체 다루기
컨트롤러는 무상태 객체, 따라서 필드변수는 필요 없다.
생성자를 사용해서 객체 내에 객체를 선언하고 다루면 더 좋다.
컨트롤러에 모든걸 다 넣는게 좋은게 아니다.
static은 올려놓고 가져다 쓰는 것.
정적 클래스는 쓸때마다 호출하는 것.
*객체에 필드변수 갖는 것은 관리해야할게 느는 것. 이 객체가 변수를 가지고 다루는게 맞는지 항상 생각하자*
List, Map, Set
- List : 순서가 있고 중복 허용, Arrays.asList, List.of 로 생성
- add(e), add(index, e), contains(e), get(index), isEmpty(), size(), clear(), remove(index), remove(e)
- Set : 순서가 없고 중복 비허용, Set.of로 생성
- size(), isEmpty(), contains(e), add(e), remove(e)
- Map : key-value의 쌍, Map.of로 생성, linkedHash 하면 순서 불변
- size(), isEmpty(), containsKey(key), containsValue(value), get(key), put(key, value), remove(key), clear(), keySet(), values(), entrySet()
String 다루기
- string.split(“,”)하면 string[]으로, Arrays.asList(string[])하면 List<>으로 나눠짐
- String.join(“”, array)하면 String 하나로 합쳐짐
- String은 불변임, StringBuffer나 StringBuilder가 가변적임. 따라서 문자열에 변화가 빈번하면 가변적인거 사용. StringBuilder는 동기화를 지원하지 않아 멀티쓰레드에서는 부적합하지만 단일쓰레드에서 성능은 더 좋고, StringBuffer는 동기화 되어 멀티쓰레드 환경에서 안전, String도 안전.
Collections
- Collections.shuffle(list) 하면 리스트 셔플
2주차 - 자동차경주게임
요구사항
- 자바 코드 컨벤션 지키기
- indent 2까지만
- 3항 연산자 쓰지 않기
- 함수가 한가지 일만 하도록 최대한 작게 만들기
- System.exit 이용하지 않기
- 비정상적 입력에 대해 IllegalArgumentException 발생, 에러 문구 [ERROR]로 출력
- 함수의 길이 15라인 이하
- else 예약어 쓰지 않기: if 조건절에서 값 return
배운것
MVC 패턴
- model: 모델은 데이터를 가지고 있는 객체로 데이터가 바뀌었을 때 컨트롤러의 데이터를 업데이트하는 로직도 포함한다.
- view: 모델에 포함된 데이터의 시각화
- controller: 컨트롤러는 모델과 뷰에 모두 영향을 미치고 데이터 흐름을 제어하고 뷰를 갱신한다.
- 따라서 데이터와 관련된 작업을 할때는 모델, 뷰는 건드릴 필요 없이 컨트롤러 객체만 건드리면 되도록 만들어야 한다.
factory 패턴
- 팩토리 메서드 패턴: 객체를 생성하기 위한 인터페이스 - carFactory로부터 car 생성하듯, 생성자에 다른게 들어갈 때 책임 위임하는 용도, 웬만하면 지금은 객체 내 of로 사용하자
- 추상 팩토리 패턴: 의존하는 객체 없이 객체 생성, 팩토리 메서드 패턴에 포함된다.
public static Car createCar(final String carName) { return new Car(carName); }
인텔리제이 단축키
- 상수 생성: ctrl+alt+c
- 함수 추출: ctrl+alt+m
split 함수 공백 처리
- .split(“,”, -1)하면 마지막 공백처리까지 할 수 있다. -1은 limit인데 limit의 역할은 배열의 크기를 지정해 split 해주는 것이다. -1이 되면 없는 것도 체크하는 것.
string -> int
- Integer.parseInt(string) 으로 바꾸고, 바꿀 수 없을 경우 NumberFormatException 발생
forEach() vs for()
- 성능, 가독성의 면에서 맵핑이나 필터링과 같이 값을 다루는 것이 아닐 경우에는 for를 사용하는 것이 더 좋다.
클래스와 인스턴스
- 클래스는 붕어빵 틀, 상태가 없다. 메서드는 인스턴스를 생성하지 않은 상태에서도 호출할 수 있다. 여러 인스턴스에서 공유하는 정보가 있는 경우에 필드를 생성해 사용한다.
- 인스턴스는 객체, 상태를 가지고 메서드를 통해 상태가 변경된다. 인스턴스의 필드는 상태 정보를 가지고 있는 변수. 생성자는 객체에만 있다.
객체지향 원칙 9가지
- 한 메서드에 하나의 들여쓰기만 한다. 메서드의 일이 적을수록 재사용성이 높고 디버깅도 쉽기 때문이다.
- else 사용하지 않는다 조건문은 복제의 원인이 되고 가독성도 좋지 않기 때문이다. if에 return을 사용하면 각 분기마다 값을 return하도록 해서 안쓸 수 있다.
- 모든 원시값과 문자열을 포장한다 사소한 값이더라도 정보를 전달할 수 있도록 포장한다.
- 한 줄에 하나의 점을 찍는다.
스트림 등 체이닝을 하는 경우 외에는 점이 둘 이상 나오지 않도록 해야한다.
내가 가진 객체, 내가 만든 객체, 파라미터로 넘겨받은 객체에만 메시지를 보내야한다.
메시지를 받은 객체는 작업을 해야지 내 속을 보여주면 안된다. - 줄여쓰지 않는다. 과도한 축약은 코드 가독성을 저해하므로 삼가한다. 메서드 이름이 길어지면 책임이 너무 많아서일 수 있으니 확인할 것.
- 모든 entity를 작게 유지한다. 50줄 이상 되는 클래스, 10개 파일 이상의 패키지는 안된다.
- 2개 이상의 인스턴스 변수를 가진 클래스는 쓰지 않는다. 기본형 또는 자료구조형 객체들은 두개 이상 인스턴스 변수로 사용하지 않는다. 일급 컬렉션, wrapper 객체는 가능하므로 객체를 또 만들도록 한다.
- 일급 컬렉션을 사용한다. collection을 wrapping하면서 다른 변수가 없는 클래스의 상태이다. (클래스에 컬렉션(list, set) 하나만 넣는다.) 모든 로직이 컬렉션에서 구현되므로 종속적이고, private final로 불변성이 보장되고, 값과 로직이 함께 있어 관리가 쉬워진다.
- getter/setter/property 삼가한다. 객체에 메시지를 던져서 작업하지 값을 다른데서 다루지 말자.
3주차 - 지하철 노선도
배운것
인터페이스
- 아무것도 없이 밑그림만 그려져 있는 기본 설계도로 상속된 곳에서 제 메서드로 구현받아 활동
- 인터페이스끼리는 상속받을 수 있다.
- 보통 클래스에 상속되어 정의 한다. class ABC implements DEF 로 선언한다.
- 인터페이스를 이용해 개발 시간을 단축하고, 표준화, 클래스 간 관계 형성, 독립적인 프로그래밍 가능
- 인터페이스를 사용하는 쪽은 선언부만 알고 호출하면, 인터페이스의 내용이 실행된다. 따라서 알맹이의 변경이 호출자에 영향을 끼치지 않는다.
enum
- 데이터들 간의 연관관계 표현에 쉽다: Staion, Line, Way와 같이 function을 enum으로 표현했듯이!
- 상태와 행위를 한 곳에서 관리: Function<T, R>과 같은 아래의 함수로 Station -> Enroll -> function 매핑했듯이!
- 데이터 그룹관리: 현금의 결제 방법, 카드의 결제 방법 같은 거를 가지고 있도록 하여서 찾기 쉽도록!
- 관리 주체를 DB에서 객체로
- 중복되는 함수를 줄일 수 있다.
- 리팩토링이 쉽다.
- 문맥을 담는다
ITERATOR
- map, arrayList을 순회하면서 remove하고자 할 때 concurrentModificationException 에러가 발생했다. 이는 삭제되었는데 map에서 계속 작아진 사이즈를 반영하지 않고 찾으려고 할 때 발생하는 문제다. 해결법을 찾으니
Iterator<String> iterator = list.iterator(); while(iterator.hasNext()){ String s = iterator.next(); iterator.remove(); }
이처럼 반복자를 사용하는 방법이었다. .next 메서드가 먼저 호출되어야해서 remove에서 에러가 발생할리 없는 코드이다.
이 코드는 IntelliJ에서 아래 코드로 변경하기를 추천받았다.subway.keySet().removeIf(line -> line.isSameNameThan(inputLineName));
removeIf를 이용해 조건에 맞는게 있으면 모두 삭제하는 방식으로 훨씬 직관적이고 이해가 쉽다.
하지만 이거는 해당하는 동일한게 있으면 전부 삭제되므로 remove(index) 로 삭제하는 것이 낫다.
함수적 인터페이스
- Consumer: 매개값 있고, 리턴값 없음 -> .accept(매개값) 으로 사용, andThen(매개값)으로 연속 실행 가능
- Supplier: 매개값 없고, 리턴값 있음 -> 리턴값.get() 으로 사용
- Function: 매개값 있고, 리턴값 있음(매핑 위주) -> apply(매개값)로 적용
- Operator: 매개값 있고, 리턴값 있음(연산 위주)
- Predicate: 매개값 있고, 리턴은 boolean(매개값 조사해서 반환) -> test(매개값)으로 조사
next와 nextLine
- next는 공백기준, nextLine는 한 줄 기준
상속
- 부모는 자식의 메서드에 접근할 수 없고 자식은 부모의 필드와 메서드에 접근할 수 있다.
앞으로
- 좀 더 세분화해서 기능 별로 커밋하는 습관을 가지고, 구현 목록도 세분화해서 작성하자
- 객체의 역할과 책임에 대해 끊임없이 생각하자
- 어떻게 하면 요구사항이 바뀌어도 사용할 수 있는 코드일까 고민하자
- 가독성이 좋은 코드인지 고민하자
- 컨벤션 지키기를 잊지 말자
- 하지 말아야 할 것(객체 내 불필요한 필드 변수의 선언, getter의 남발)을 최대한 줄이려고 고민하자
1주차 피드백
- 이름을 통해 의도 드러내라 : info, data, a, the 사용 X
- 축약하지 말것
- 불필요한 공백, 공백 라인 만들지 말것
- 순서도 주의해라(상수, 클래스 변수, 인스턴스 변수, 생성자, 메서드)
- 반복하지 마라
- space와 tab 하나만 사용하기
- 의미 없는 주석 달지 않기
- 값을 상수로 빼기
- commit 메시지 의미있게 작성하기
- 기능 목록 수시로 업데이트
- 기능 목록 구현 재검토: 예외까지
- readme.md 상세히
2주차 피드백
- java api 적극 활용
- 배열 대신 java collection 사용하기 List, Set, Map 등
- 객체에 메시지를 보내라
- 인스턴스 변수의 수를 줄여라
- 비즈니스 로직과 UI 로직을 분리해서 다른 클래스가 담당하도록 하라
- 함수 라인에 대한 기준
- 커밋에 번호 추가하지 않기
- 예외케이스 고민하기
- 브랜치 확인하기
과거 3주차 피드백
- 시작 단계에서 너무 완벽한 설계를 경계해라: 구현하면서 지식을 쌓고 재설계하고 구현을 반복해라
- 상황에 맞는 설계와 구현 방법을 찾아라: 프로그래밍에 답은 없다. 요구사항에 적합한 최선의 설계와 구현을 하기 위해 노력하자
- 원시 타입과 문자열, collection을 객체로 포장하자 [일급컬렉션]: 상태와 행위를 한번에 관리할 수 있고 메서드에 따라 불변하게 데이터를 관리할 수 있다.
- 적절한 collection(자료구조)를 사용한다. list, map, set같은 자료구조를 적재적소에 사용하고 없으면 나만의 자료구조 만든다. 예를 들어 서로 다른 숫자 이면 set
를 사용해서 구현하도록 한다. 어떤 값에 따른 결과가 바뀐다면 map을 사용하자. - 객체에 메시지를 보내라: get을 사용하지 않고 메시지를 보내서 값을 얻도록 한다
- 반복문 대신 재귀를 사용하도록 하자: try-catch 할 때
피드백
- 필드변수 고려: 이 예시로 inputView(필드변수와 생성자로 만들지 말고 run(inputView)로 넘겨주기)
- 생성자에 다른 변수가 들어가야하면 생성자 말고 of인 팩토리 사용! 생성자에 대한 책임을 갖는 것!
- 컨트롤러한테 최소한의 로직을 주도록!
- 객체입장에서 이름 짓기, 오해만 생기지 않도록!
- get 말고 report, determine 등등 쓰기
- stream 띄우기
- 필요한 곳 아니면 Integer 보다 int
- enum 속 public하지 않고 함수 만들도록
- 객체에 대한 getter가 아닌 경우는 괜찮 출력 이런거
- toString을 출력용으로 작성하진 않도록
- 포맷 맞춰줘야하면 String.format(“[INFO] %s%n”, line.getName()); 로 만들도록 %n은 개행
- System.out.printf(format, line.getName()); 하면 바로 계산 가능
- 패키지 나눠서 보기 쉽게 만들기
- static import 해서 너무 길지 않지만 알아들을 수 있게!
-
필드 변수는 public 진짜 웬만하면 여러번 매개변수 주더라도 사용하지 않도록
- **한 줄에 긴것보다는 뽑아내도록**
- **필드변수 만들기 전에 꼭 필요한가?>?????? 진짜 생각하도록**
- **name 필드변수 가졌으면 유효성 정도는 걔가 체크하도록 하자. 하는 일이 없지 않도록**
시험 유의사항
- 초기화 제때 잘하기
- while문 break 조건 잘 보기
- try catch return 잘 보기
- enum 사용 잘 하기
- 15분 잡고 상수 뽑고 컨벤션 정리하기 - 접근지정자 확인, 함수 순서 확인, 길이 확인
- 5분 잡고 제출하기
- 시작하면 racingcar 띄워놓고 기본 설정 따라가기 (application, input, output, controller)
- 숫자로 이동하는거 나오면 치킨집 보고 enum 적용해서 이동하기
- 시작하면 그림 먼저 그리고 완성되면 시작하기 20분 잡고
- 구현목록 쓰고 바로바로 이름 먼저 쓰고 구현하고 커밋하는게 안 헷갈리고 잘 만들어진다!!
- depth 2 제한 있으면 딱 그만큼 사용해도 되니까 그렇게 하자, try-catch, enum 순회 등
- 명시적인 예외만 먼저하고 암묵적인거는 나중으로
- 무조건 하나하나 동작하게 요구사항 무조건 지키면서!!
- 우선 구현 먼저 하면서 할 일들 적어두기: 예외 처리하는거, 객체 빼는거
- 예외 처리하는거 기본 조건으로 나와있어도 복잡하다 싶으면 미루기
- 숫자 입력 테스트는 view에서 return Integer.parseInt(~~) 하고 NumberFormatException으로 잡기
- 음수인 경우, 숫자 아닌경우(inputView에서 검사), 입력 올바르지 않은 경우 예외처리
- 다하면 상수처리 -> 컨벤션 정리 -> 예외처리 -> 함수빼기(getter 빼기) -> 객체 빼기 순으로 진행
- 코드, 기능구현목록, 출력창 띄워놓고 하면 될듯
- view에는 domain 넘겨주지 않도록 해야함! 자료구조로 전달하면 출력하도록