주노 님의 블로그

20240724 본캠프 8일차 TIL 본문

TIL

20240724 본캠프 8일차 TIL

juno0432 2024. 7. 24. 21:26

본캠프 8일차 내용 간단요약

  • 09:00 ~ 09:20 : 개인과제 발제
  • 09:40 ~ 10:40 : 코드카타
  • 10:40 ~ 12:00 : 강의
    자바문법종합반 3주차 과제
  • 12:00 ~ 13:00 : 과제 준비
  • 13:00 ~ 14:00 : 점심시간
  • 14:00 ~ 15:00 : 강의
    자바문법종합반 3주차 과제
  • 15:00 ~ 16:00 : 알고리즘 특강
  • 16:00 ~ 18:00 : 강의
    자바문법종합반 4주차 강의
  • 18:00 ~ 19:00 : 저녁시간
  • 19:00 ~ 20:00 : 강의
    자바문법종합반 4주차 강의
  • 20:00 ~ 21:00 : TIL작성 

 

오늘 해야할 일✔️ 🔺

4주차 예습 및 강의듣기

✔️ 젯브레인 얼티메이트 가입

❌   개인과제 레벨1

 


코드카타 - SQL

더보기

문제
CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블에서 2022년 10월 16일에 대여 중인 자동차인 경우 '대여중' 이라고 표시하고, 대여 중이지 않은 자동차인 경우 '대여 가능'을 표시하는 컬럼(컬럼명: AVAILABILITY)을 추가하여 자동차 ID와 AVAILABILITY 리스트를 출력하는 SQL문을 작성해주세요. 이때 반납 날짜가 2022년 10월 16일인 경우에도 '대여중'으로 표시해주시고 결과는 자동차 ID를 기준으로 내림차순 정렬해주세요.

 

라는 문제가 있다

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

아무리 풀어봤는데 계속 틀렸다고 뜨는것이다

 

그래서 한번 질문하기를 봤는데

사람들 키워드가

WITH, UNION, MAX, 대여중일경우 1, 대여가능일경우 0

이라는것이다

 

알고보니

CAR_ID가 중복되는것을 알 수 있다

 

세상에 마상에

이미 답을 찾을 키워드는 알아버렸다

 

-- 코드를 입력하세요
SELECT CAR_ID,
CASE WHEN MAX(CASE WHEN start_date <= '2022-10-16' AND END_DATE>='2022-10-16' THEN 1 ELSE 0 END) THEN "대여중" ELSE "대여 가능" END AVAILABILTY
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
GROUP BY CAR_ID
ORDER BY CAR_ID DESC

위 코드인데

STARTDATE와 ENDDATE사이에 22년 10월 16일이 있을경우 1 아니면 0이다

MAX를 사용해서, 그룹화된 CAR_ID중에서 하나라도 1이있을경우 1로 아니면 0으로 작성을했다

그리고 1이면 대여중 0이면 대여 가능으로 설정한다

 

WITH이나 UNION은 주말에 공부하기...


코드카타 - 알고리즘

더보기

문제 설명
어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. 예를 들어 "AB"는 1만큼 밀면 "BC"가 되고, 3만큼 밀면 "DE"가 됩니다. "z"는 1만큼 밀면 "a"가 됩니다. 문자열 s와 거리 n을 입력받아 s를 n만큼 민 암호문을 만드는 함수, solution을 완성해 보세요.

제한 조건
공백은 아무리 밀어도 공백입니다.
s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다.
s의 길이는 8000이하입니다.
n은 1 이상, 25이하인 자연수입니다.
입출력 예
s n result
"AB" 1 "BC"
"z" 1 "a"
"a B z" 4 "e F d"

 

라는 문제가 있다.

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

class Solution {
    public String solution(String s, int n) {
        String answer = "";
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i<s.length(); i++)
        {
           char ch = (char)(s.charAt(i)+n);
            if(Character.isWhitespace(s.charAt(i))){
                sb.append(" ");
            }
            else if (Character.isLowerCase(s.charAt(i)))
            {
                if (ch > 'z')
                {
                    sb.append((char)(ch-26));
                }
                else
                    sb.append(ch);
            }
            else {
                if (ch > 'Z')
                {
                    sb.append((char)(ch-26));
                }
                else
                    sb.append(ch);
            }
        }
        answer=sb.toString();
        return answer;
    }
}

 

이렇게 구현했다

스트링빌더를 사용하여 공백값을 받았을때는

Character클래스의 isWhiteSpacse를 사용하고,

isLowerCase는 소문자인가???에 대한 답을 반환한다

isUpperCase는 대문자인가?? 이다

따로 uppercase를 쓰지않은 이유는 

s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다.라는 제한사항이 있어서 그럼 ㅇㅇ..

 

 

만약 스트링빌더를 사용하지않고 스트링을 사용하였을때는 

class Solution {
    public String solution(String s, int n) {
        String answer = "";
        for(int i = 0; i<s.length(); i++)
        {
           char ch = (char)(s.charAt(i)+n);
            if(Character.isWhitespace(s.charAt(i))){
                answer+=" ";
            }
            else if (Character.isLowerCase(s.charAt(i)))
            {
                if (ch > 'z')
                {
                    answer+=(char)(ch-26);
                }
                else
                    answer+=ch;
            }
            else {
                if (ch > 'Z')
                {
                    answer+=(char)(ch-26);
                }
                else
                    answer+=ch;
            }
        }
        return answer;
    }
}

 

최대 140배까지 차이나는걸 볼 수 있다

 

그 이유는

str = ""; 로 정의하고

str += a; 라고 정의하면 값이 바뀐다고 생각되겠지만

 

String은 불변객체기때문에 더할때마다 새로운 문자열 객체가 생성됨.

 

따라서 위 str은 +를 추가하는 즉시 다른 주소로 객체를 생성하여 만들어준다.

public class Main {
    public static void main(String[] args) {
        String str = "hello";
        System.out.println("Original hash: " + System.identityHashCode(str));

        str += " world";
        System.out.println("New hash: " + System.identityHashCode(str));

        StringBuilder sb = new StringBuilder("hello");
        System.out.println("StringBuilder original hash: " + System.identityHashCode(sb));

        sb.append(" world");
        System.out.println("StringBuilder modified hash: " + System.identityHashCode(sb));
    }
}

위 코드는 str과 stringBuilder를 사용하였을때의 해시코드를 확인하는 코드이다

해시코드는 값이 변경되면 변경되는것 아닌가? 싶었지만, 위 identityHashCode함수는 객체의 식별자를 제공한다,

해시코드가 다르다면 서로 다른 객체라고 생각하면된다..

 따라서 실행결과를 확인하면

3번째의 str과 world를 추가한 str은 서로 해시값이 다른것을 확인할 수있다.

String이 불볍 객체기때문에 world를 추가함으로써 str은 기존의 값을 복사한 내용과 world를 추가한 새로운 객체를 만든것이다

 

StringBuilder는 내부 상태를 수정해도 동일한 객체의 주소를 유지하는것을 볼 수 있다.

 

즉 코테테의 문제에서 문자 크기만큼 str은 객체를 복사, 생성하는 과정이 포함된것이다

 

 stringBuilder은 가변 문자열 객체로 문자열을 더하거나 빼도 기존 배열에서 변경된다

고로 추가하든, 삭제하든 똑같은 주소를 가르키며, 새로운 객체의 생성은 없기때문에 빠르다.


자바 문법 종합반 - 3주차 과제

더보기

스텝 1

위와 같은것을 클래스 다이어그램이라고 한다.

맨위는 클래스 명

중간에 빈칸은 필드명(멤버 변수)

아래는 메서드명이다.

위 내용대로 만들자면 Calculator 클래스를 만드는데

생성자는 String, int, int 세개의 매개변수를 받고, 반환값은 더블인 calculate를 만들라. 라는뜻이다

 

package week3.work1;

public class Calculator {
    public double calculate(String operator, int num1, int num2)
    {
        if(operator.equals("+"))
        {
            return num1 + num2;
        }
        else if (operator.equals("-"))
        {
            return num1-num2;
        }
        else if (operator.equals("*"))
        {
            return num1*num2;
        }
        else if(operator.equals("/"))
        {
            return num1 /num2;
        }
        return 0;
    }
}

 

이렇게 짜면된다 ( 잘못된 string값이나, num2가 0인경우를 제외한 간략한 코드)

 

public class Main {
    public static void main(String[] args) {
        Calculator car = new Calculator();
        System.out.println("num1 : 20 num2 : 5");
        System.out.println("더하기 연산자 : " + car.calculate("+",20,5));
        System.out.println("빼기 연산자 : " + car.calculate("-",20,5));
        System.out.println("나누기 연산자 : " + car.calculate("/",20,5));
        System.out.println("곱하기 연산자 : " + car.calculate("*",20,5));
    }
}

 

그리고 메인문에서 불러올때는 Calculator car = new Calculator로 해서 Calculator 객체를 생성해주고, car객체의 calculate메서드를 불러와 계산한다.

 

스텝 2

나머지 연산자를 추가하기

 

        else if(operator.equals("%"))
        {
            return num1%num2;
        }

이거만 추가하면된다

 

스텝 3

 

갑자기 풍부해진 난이도

사실은 쉽다 객체지향의 SOLID 원칙을 따르라는건데

계산기 클래스 안에

멤버변수로 각각의 객체를 가져오고.

메서드를 놔두라 인것이다.

저 화살표는 포함관계이다 상속 아뉨

 

public class AddOperation {
    public double AddOperation(int num1, int num2)
    {
        return num1 + num2;
    }
}
public class DivideOperation {
    public double DivideOperation(int num1, int num2)
    {
        return num1/num2;
    }
}
public class MultiplyOperation {
    public double MultiplyOperation(int num1, int num2)
    {
        return num1 * num2;
    }
}
public class SubstaractOperation {
    public double SubstaractOperation(int num1, int num2)
    {
        return num1-num2;
    }
}


이렇게 클래스를 따로 뺴서 정의해주고

 

package week3.work3;

public class Calculator {
    AddOperation addOperation = new AddOperation();
    SubstaractOperation substaractOperation = new SubstaractOperation();
    MultiplyOperation multiplyOperation = new MultiplyOperation();
    DivideOperation divideOperation = new DivideOperation();

    public double calculate(String operator, int num1, int num2)
    {
        double answer=0;
        if(operator.equals("+"))
        {
            answer=addOperation.AddOperation(num1,num2);
        }
        else if(operator.equals("-"))
        {
            answer= substaractOperation.SubstaractOperation(num1,num2);
        }
        else if (operator.equals("*"))
        {
            answer= multiplyOperation.MultiplyOperation(num1, num2);
        }
        else if (operator.equals("/"))
        {
            answer= divideOperation.DivideOperation(num1, num2);
        }
        return answer;
    }
}

Calculator 클래스에서 각 객체를 불러와 메서드를 사용하여 적절히 계산해준다

public class Main {
    public static void main(String[] args) {
        Calculator cal = new Calculator();
        System.out.println("더하기 연산자 : " + cal.calculate("+",20,5));
        System.out.println("빼기 연산자 : " + cal.calculate("-",20,5));
        System.out.println("곱하기 연산자 : " + cal.calculate("*",20,5));
        System.out.println("나누기 연산자 : " + cal.calculate("/",20,5));
    }
}

 

이는 객체지향의 원칙 SOLID중 하나인데

단일 책임 원칙을 준수한것으로 볼 수 있다.

 

단일 책임 원칙 (Single Responsibility Principle, SRP)

클래스는 하나의 책임(기능)을 가져야한다

 

개방-폐쇄 원칙 (Open/Closed Principle, OCP)
소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에 열려 있어야 하고, 수정에는 닫혀 있어야 합니다.
기존 코드를 변경하지 않고 기능을 확장할 수 있어야 합니다.

 

리스코프 치환 원칙 (Liskov Substitution Principle, LSP)

서브 타입은 언제나 기반 타입으로 교체할 수 있어야 합니다.
서브 클래스는 기반 클래스가 기대하는 행위를 해야 합니다.

 

인터페이스 분리 원칙 (Interface Segregation Principle, ISP)
클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 됩니다.
인터페이스는 특정 클라이언트를 위한 것이어야 합니다.

 

의존 역전 원칙 (Dependency Inversion Principle, DIP)
고수준 모듈은 저수준 모듈에 의존해서는 안 됩니다. 둘 다 추상화에 의존해야 합니다.
구체적인 구현이 아닌, 추상화된 인터페이스에 의존해야 합니다.

 

위 설명과 같이 STEP 1과 2는 Calculate클래스가 덧셈 뺄셈 나눗셈 곱셈을 수행했는데

기능을 분리하여 Calculate 클래스는 연산작업만 수행하게 되었다

 

 

스텝 4

위 그림을 보면 연산과 계산기 사이의 과정에서 추상 클래스가 추가된것을 알 수 있다.

 추상클래스는 내가 공통된걸 묶을테니, 너네가 맘대로 수정하라 라는것이다

 

public abstract class AbstractOperation {
    abstract double operate(int num1, int num2);
}

 추상클래스이니 abstract로 구현해준다.

 

public class AddOperation extends AbstractOperation{
    @Override
    double operate(int num1, int num2) {
        return num1 + num2;
    }
}

 uml과는 다르게 상속관계니까

extends를 사용해서 AbstractOperation의 기능을 사용해준다

 

 

깜빡할뻔했는데

추상 클래스에서 작성된 추상메서드는 상속받은 클래스에서 모두 구현해줘야한다!

 인텔리제이 내가 맨날 멍청이제이라고 하고다녔는데

괜찮구만..

public class Calculator {
    public AbstractOperation abstractOperation;

    public void setAbstractOperation(AbstractOperation abstractOperation) {
        this.abstractOperation = abstractOperation;
    }
    public double calculate(int num1, int num2) {
        return abstractOperation.operate(num1, num2);
    }
}

 

추상클래스를 받아서, calculate 메서드를 만들어주고

setAbstractOperation은 uml에 없지만, 답지보니까 있더만 ㅇㅇ...

 

public class Main {
    public static void main(String[] args) {
        Calculator cal =new Calculator();

        cal.setAbstractOperation(new AddOperation());
        System.out.println("20+5 = " + cal.calculate(20, 5));
        cal.setAbstractOperation(new SubstaractOperation());
        System.out.println("20-5 = " + cal.calculate(20, 5));
        cal.setAbstractOperation(new MultiplyOperation());
        System.out.println("20*5 = " + cal.calculate(20, 5));
        cal.setAbstractOperation(new DivideOperation());
        System.out.println("20/5 = " + cal.calculate(20, 5));
    }
}

 

메인문은 이렇게 만들면된다

set~operation을 해서 덧셈 뺄셈을 정의해주고
calculate로 계산해준다


자바문법종합반 - 4주차

더보기

예외처리

  • 자바에서 사용자의 입력실수, 스택오버플로우, 메모리부족등의 문제가 생길때
    그 문제를 해결하기위해서는 자바에서는 예외 처리가 있다.

  • 오류
    시스템 레벨에서 환경적인 이유로 일반적으로 회복이 불가능한 오류이다.
    어떠한 에러인지 확인하고 대응을 한다

  • 예외
    코드 레벨에서 처리할수 있는 문제사항이다.

  • 컴파일 에러
    컴파일이란 코드를 컴퓨터가 이해 할 수 있는 언어로 변환되는 과정인데
    프로그래밍 언어의 규칙을 지키지 않았을때 발생하며
    존재하지않은 클래스 호출, 접근이 불가능한 프로퍼티나 메소드에 접근하려고 할때 뜬다.
    해결법 : 문법에 맞게 다시 작성하는것이다

  • 런타임 에러
    runtime : 프로그램이 수행되고 있는 때
    컴파일은 됐지만, 프로그램이 실행중 예외를 만나게되어 중지되는 오류이다.

  • 확인된 예외
    컴파일 시점에서 발생하는 예외로, 반드시 예외처리를 해야만 컴파일이 성공적으로 진행된다.

  • 미확인된 예외
    런타임 시점에서 발생하는 예외로, 예외처리가 반드시 필요하지는 않지만, 프로그램이 정지가 되거나, 넘어가는 에러이다.

예외발생과 예외처리방법

  • 클래스 생성
    예외 발생시 예외를 처리하기 위한 클래스를 생성할 수 있다.

  • throws, throw
    class OurClass {
        private final Boolean just = true;
    		
    		// 신규 문법 throws!
        public void thisMethodIsDangerous() throws OurBadException {
            if (just) {
    						// 신규 문법 throw!
                throw new OurBadException();
            }
        }
    }
    throws와 throw를 사용하는 방법이고, just가 true라서 ourbadexception이 실행됨을 알수있다.

    throws는 메서드의 선언부 뒤에 위치하게 되며, 예외가 발생할때 어떤 예외처리 메서드를 던질지 명시한다.
    throw는 실제 예외를 던질때 사용되며, 특정 조건이 만족된다면 직접 예외객체를 생성한후 던지게된다.

  • try - catch
    예외가 발생할것 같은 코드에 실행후 오류가 발생한다면 예외처리를 해준다.
    public class StudyException {
        public static void main(String[] args) {
            OurClass ourClass = new OurClass();
    
            try {
                // 1. 위험한 메소드의 실행을 "시도" 해 봅니다.
                // "시도" 해보는 코드가 들어가는 블럭입니다.
                ourClass.thisMethodIsDangerous();
            } catch (OurBadException e) {
                // 2. 예외가 발생하면, "잡아서" handling 합니다.
                // 예외가 발생하는경우 "handling" 하는 코드가 들어가는 블럭입니다.
    						// 즉 try 블럭 내의 구문을 실행하다가 예외가 발생하면
    						// 예외가 발생한 줄에서 바로 코드 실행을 멈추고
    						// 여기 있는 catch 블럭 내의 코드가 실행됩니다.
                System.out.println(e.getMessage());
            } finally {
                // 3. 예외의 발생 여부와 상관없이, 실행시켜야 하는 코드가 들어갑니다.
                // 무조건 실행되는 코드가 들어가는 블럭입니다.
                System.out.println("우리는 방금 예외를 handling 했습니다!");
            }
    
        }
    }

    try문에는 원래 실행할 코드가 들어가고
    catch는 예외가 발생하였을때 처리할 방법을 적는다
    finally는 예외의 발생여부상관없이 무조건 실행된다, 즉 예외가있든 없든 실행된다고 보면된다.
    요약하여 예외가 발생하였을때는 try에서 즉시 (다음 코드가 남아있어도 그자리에서 중지하고) catch로 간후 finally를 출력하고
    try에서 예외가 발생하지 않았다면 finally로 가서 수행한다.

    e.getMessage()는 오류 메세지를 출력해준다
    public class MultipleCatchExample {
        public static void main(String[] args) {
            try {
                int[] numbers = {1, 2, 3};
                System.out.println(numbers[5]); // ArrayIndexOutOfBoundsException
                int result = 10 / 0; // ArithmeticException
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("배열의 인덱스가 잘못되었습니다: " + e.getMessage());
            } catch (ArithmeticException e) {
                System.out.println("산술 연산 오류: " + e.getMessage());
            } catch (Exception e) {
                // 모든 다른 예외를 잡기 위한 catch 블록
                System.out.println("예기치 않은 오류가 발생했습니다: " + e.getMessage());
            } finally {
                System.out.println("예외 처리 완료.");
            }
        }
    }
     catch문은 여러개를 둘 수 있는데
    첫번째 캐치문이 그 예외를 처리하지못한다면 다음으로,, 또 다음으로 넘긴다.
    모든 예외는 익셉션클래스에서 파생되므로, 마지막 매개변수는 그외 모든 에러를 처리한다.

    만약 try 두번째줄의 오류가 두번째 catch문에서 예외를 처리했다면(위 코드와 다름) try 첫번째줄 > try 두번째줄 > catch 첫번째문 > catch 두번째문(예외처리) > finally
    이렇게 동작하게 된다.

예외클래스

  • Throwable 클래스
    모든 클래스는 Object 클래스로 부터 파생되었다

    흔히 볼 수 있는 배열 범위 초과, 널 포인트 에러들은 따로 예외 처리를 하지 않더라도 컴파일 시점에서는 문제 없이 통과되지만, 실행 시 예외가 발생하면 프로그램이 중단된다. 
    이 말의 뜻은 이러한 예외들은 코드 레벨에서 복구가 가능하지만, 예외 처리를 하지 않으면 실행 중에 프로그램이 비정상적으로 종료된다는 것이다.



    이미 만들어진 예외를 처리할수 있는 방법이 많다.
    없으면 예외 클래스를 구현하여 직접 만들수 있다.

예외 처리하는법

    • chained Exception
      예외가 서로 연결되어있을때
       a가 예외 b를 발생하였다면 a는 b의 원인이다

      예를들어
      파일을 읽으려고했는데 파일이 없어서 A가 발생했다
      이예외때문에 다른 작업이 실패해서 예외 B가 발생했다.
      이때 예외 B를 던지면서, 예외 A를 기록하여 나중에 끝 예외에갔을때 이 예외가 어디로부터 시작되었는지 알기 쉽게한다.
      public class Main {
          public static void main(String[] args) {
              try {
                  // 예외 생성
                  NumberFormatException ex = new NumberFormatException("가짜 예외이유");
                  // 원인 예외 설정(지정한 예외를 원인 예외로 등록)
                  ex.initCause(new NullPointerException("진짜 예외이유"));
                  // 예외를 직접 던집니다.
                  throw ex;
              } catch (NumberFormatException ex) {
                  // 예외 로그 출력
                  ex.printStackTrace();
                  // 예외 원인 조회 후 출력
                  ex.getCause().printStackTrace();
              }
      
              // checked exception을 감싸서 unchecked exception 안에 넣습니다.
              throw new RuntimeException(new Exception("이것이 진짜 예외 이유입니다."));
          }
      }
      initCause() : 예외를 연결하는 메서드
      getCause() : 예외를 반환하는 메서드

      ex라는 예외를 생성한다, 그 예외는 initCause 메서드를 사용하여 원인 예외를 설정한다

      예외를 직접 던져, number~~~~~~예외가 발생하고 그 원인은 nullpointer~~인것이다.

      try 블록에서 발생한 예외를 catch 블록에서 잡으며
      printStackTrace 메서드를 사용하여 예외의 스택 트레이스(예외 처리과정의 로그)를 출력한다
      getCause 메서드를 사용하여 원인 예외(NullPointerException)의 스택 트레이스도 출력한다..

      위 코드에서 원래 checkexception을 사용하려면 try catch문으로 감싸야겠지만, runtimeException 안에 exception을 넣음으로써 unchecked exception처럼 만든다initCause의 내용은 getCause에서 뜨게된다.이에대한 장점은 예외가 발생한 근본적인 원인을 찾기에 편하다

      numberformatException보다는 nullpointerException이 더 에러를 직관적으로 알 수 있다.

      예외연결의 장점
      여러 예외상황에서 발생한 문제를 특정 예외로 추상화 하여 처리할수 있기 때문이다 (가독성에 좋음)

예외복구방법


  • public String getDataFromAnotherServer(String dataPath) {
    		try {
    				return anotherServerClient.getData(dataPath).toString();
    		} catch (GetDataException e) {
    				return defaultData;
    		}
    }
     오류가 발생하였을때 원래 데이터를 리턴해준다.
    프로그램 종료를 방지하고, 복구를하는 코드이다
    현실적으로 위 방법은 복구가 잘 되지않는다. 자주스이긴 어렵다

  • 예외 처리 회피 방법
    public void someMethod() throws Exception { ... }
    
    public void someIrresponsibleMethod() throws Exception {
    		this.someMethod();
    }
    this.somemethod를 실행하였을때 그때 발생한 오류가 exception으로 흐르게 된다

  • 예외 전환방법
    public void someMethod() throws IOException { ... }
    
    public void someResponsibleMethod() throws MoreSpecificException {
    		try {
    			this.someMethod();
    		} catch (IOException e) {
    			throw new MoreSpecificException(e.getMessage());
    		}
    }

    오류가 발생하면 catch문으로 넘어가게되고, 새로운 예외로 던진다
    이로인해 예외처리는 더더욱 명확하게 처리할수 있게한다.

제네릭

  • 제네릭은 중복되거나 필요없는 코드를 줄여주는것
    예를들어 같은 기능을 하는 함수를 여러번 구현할 필요가없다
    자바는 강타입(변수의 타입을 꼭 지정해줘야함) 언어기때문에 a+b에서 a와 b가 int일때 double일때를 모두 구현해줘야한다.

  • 타입 안정성을 해치지 않는것이다
    public class Generic {
        public Object plusReturnFunction(Object a, Object b) { ... }
    }

    모든 클래스는 Object클래스의 자식.자손이기때문에  다양한 타입의 객체를 받을수있지만, 메서드 내부에서는 모든 타입을 지정해야하기때문에 여러가지 문제가 생길수 있다.

  • 사용법
    // 1.
    public class Generic<T> {
    		// 2.
        private T t;
        // 3.
        public T get() {
            return this.t;
        }
    
        public void set(T t) {
            this.t = t;
        }
    
        public static void main(String[] args) {
    				// 4.
            Generic<String> stringGeneric = new Generic<>();
    				// 5.
            stringGeneric.set("Hello World");
    				
            String tValueTurnOutWithString = stringGeneric.get();
    
            System.out.println(tValueTurnOutWithString);
        }
    }
     클래스나, 메서드에 사용할수 있으며, 이름 뒤에 <>이 입력되며 <>안에 들어가야할 타입 변수를 지정한다.
    T는 다른 개발자들이 암암리에 정해놓은 규약이기때문에 바꿔도된다.

    선언해둔 T는 특정한 타입이 들어갈 자리에 대신 들어갈 수 있다.
    메서드의 리턴타입또한 가능하다.

    아래 main문에서는 String을 입력했으며, 제네릭 클래스의 T부분에는 모두 String이 들어간다.

  • 제네릭 문법
    public class Generic<T> { ... }
    
    Generic<String> stringGeneric = new Generic<>();
    제네릭을 사용한 클래스를 제네릭 클래스라고 하며, 변수명  T는 타입변수라고 한다.

    static 멤버에 사용할 수 없다

    다수의 타입변수를 사용 할 수 있다
    public class Generic<T, U, E> {
        public E multiTypeMethod(T t, U u) { ... }
    }
    
    
    Generic<Long, Integer, String> instance = new Generic();
    instance.multiTypeMethod(longVal, intVal);

    와일드 카드를 통해 제네릭의 제한을 구체적으로 정할 수 있다.
    public class ParkingLot<T extends Car> { ... }
    
    ParkingLot<BMW> bmwParkingLot = new ParkingLot();
    ParkingLot<Iphone> iphoneParkingLot = new ParkingLog(); // error!
    위 제네릭문법은 Car클래스와 그 자손들만 사용가능하다


    <? extends T> : T와 그 자손들만 사용 가능
    <? super T> : T와 그 조상들만 가능
    <?> : 제한 없음

  • 제네릭과 인터페이스를 사용한 List
    • List
      리스트는 추상적 자료구조이다 (리스트 인터페이스는 추상적으로 구현해서 어레이리스트와 링크드리스트가 상속을해서 구체화한것이다)
      순서가있고 중복을 허용해준다

      list는 인터페이스로 이고 제네릭으로 만들어졌다. Collection을 상속받은것을 볼 수 있다.
      제네릭을 사용함으로써 코드의 재사용성을 높이고, 다양한 데이터 타입을 안전하게 처리할 수 있다.
      예를들어
      List<Integer> 이나 List<String>처럼 구체적인 타입을 하나의 인터페이스만으로 구현을 할 수 있다.
      Collection또한 제네릭으로 만든 인터페이스이며 인터페이스끼리 상속을 할시에는 extends를 사용할 수 있다.

      list는 
      //list.java
      int size();
      boolean isEmpty();
      boolean contains(Object o);
      Object[] toArray();
      boolean add(E e);
      boolean remove(Object o);
      
      //ArrayList.java
      public class ArrayList<E> extends AbstractList<E>
              implements List<E>, RandomAccess, Cloneable, java.io.Serializable
              
                      public int size() {
                  checkForComodification();
                  return size;
              }
      public boolean isEmpty() {
              return size == 0;
          }
      public boolean contains(Object o) {
                  return indexOf(o) >= 0;
              }
        public Object[] toArray() {
              return Arrays.copyOf(elementData, size);
          }
              private void add(E e, Object[] elementData, int s) {
              if (s == elementData.length)
                  elementData = grow();
              elementData[s] = e;
              size = s + 1;
          }
              public E remove(int index) {
              Objects.checkIndex(index, size);
              final Object[] es = elementData;
      
              @SuppressWarnings("unchecked") E oldValue = (E) es[index];
              fastRemove(es, index);
      
              return oldValue;
          }
      보는것과 같이 인터페이스로 구현되어 ArrayList는 부모 요소의 내용을 뽑아서 구체화 시킨것을 볼 수 있다. 

래퍼타입

  • prmitive(원시) 타입
    기본적으로 변경이 불가능한 타입이다.
    int, double, byte short등이 있다.
    int a = 10;
    a=20;이라고 했을때 값이 바뀌는것이 아니냐.라고 생각할 수 있지만,
    내부적으로는 10을 20으로 바꾸는 과정이아닌, 10대신 20을 가리키는 것으로 재할당 되는것이다.

    래퍼타입은 
    원시타입이 제공하지 않는 메소드와 기능을 객체에서 사용가능하다.
    parseint나, compare등과같은 유용한 메서드를 제공하여 작업을 편리하게 한다.

    원시값을 객체화 하는것은 박싱이라고하며, 객체를 다시 원시값으로 만들어주는것을 언박싱이라고 하는데
    자동형변환처럼 자동으로 원시타입을 객체로 객체타입을 원시타입으로 변환해주기도 한다
    Integer num = new Integer(17);  // Boxing
    int n = num.intValue(); // UnBoxing
    
    Character ch = 'X'; // AutoBoxing
    char c = ch; // AutoUnBoxing


 


오늘의 회고

원시타입을 공부하면서 제대로 이해하지못한 부분이 많다
그리고 제네릭 강의에서 제네릭 클래스는 원시타입이라는데 이 부분도 되게 이해가 안된다


해야할 일

우선순위
1. 자바 강의 5주차 듣기
2. 개인과제 레벨 1
3. sql with, union 공부하기
4. 객체지향 SOLID
5. 원시타입과 래퍼타입의 차이점과 원시타입을 쓰는 이유


12시간 몰입했는가?

중간에 TIL파일을 반정도 날려서 다시 작성하느라 시간이 꽤 소요된것같다..

'TIL' 카테고리의 다른 글

20240726 본캠프 10일차 TIL  (0) 2024.07.26
20240725 본캠프 9일차 TIL  (0) 2024.07.25
20240723 본캠프 7일차 TIL  (0) 2024.07.23
20240722 본캠프 6일차 TIL  (0) 2024.07.22
20240719 본캠프 5일차 TIL  (0) 2024.07.19