본문 바로가기

항해 99/Java

페어 프로그래밍 - 코딩 테스트

개인 풀이

코드 리뷰 - ChatGPT

 

1. 짝수와 홀수

class Solution {
    public String solution(int num) {
        String answer = (num % 2 == 0) ? "Even":"Odd";
        return answer;
    }
}

 

코드 리뷰

  • 코드 스타일과 가독성
    • 변수명: answer 변수명은 명확하게 메소드의 결과를 나타냅니다. 이 경우와 같이 간단한 조건문에서는 변수 없이 직접 return하는 것도 고려할 수 있습니다.
    • 조건문: 삼항 연산자를 사용하여 짝수와 홀수를 판별하는 로직은 매우 간결하며 가독성이 좋습니다.
  • 성능
    • 이 경우, 성능 최적화는 크게 고려할 사항이 아닙니다. 주어진 코드는 이미 매우 효율적입니다. 정수의 홀수/짝수 판별은 비트 연산을 사용하여 더 최적화할 수 있지만, 현재 문제의 규모에서는 실질적인 성능 차이가 거의 없습니다.
  • 개선 제안
    • 이 경우, 성능 최적화는 크게 고려할 사항이 아닙니다. 주어진 코드는 이미 매우 효율적입니다. 정수의 홀수/짝수 판별은 비트 연산을 사용하여 더 최적화할 수 있지만, 현재 문제의 규모에서는 실질적인 성능 차이가 거의 없습니다.
class Solution {
    public static String solution(int num) {
        return (num % 2 == 0) ? "Even" : "Odd";
    }
}
  • 이 개선은 소규모 프로그램이나 단일 메소드 테스트에 유용할 수 있습니다. 그러나 현재 코드의 목적에 부합하고, 이미 잘 작성되었기 때문에 이러한 변경은 필수적이지 않습니다.

 

 

 

2. 평균 구하기

class Solution {
    public double solution(int[] arr) {
        double answer = 0;
        for (int num : arr) {
            answer += num;
        }
        answer = answer / arr.length;
        return answer;
    }
}

 

코드 리뷰

  • 코드 스타일과 가독성
    • 변수명: answer 변수명은 계산된 평균을 저장하는 데 사용되며, 메소드의 반환값을 잘 나타냅니다.
    • 루프 사용: 향상된 for 문을 사용하여 배열의 모든 요소를 순회하고 합계를 구하는 방법은 명확하고 효율적입니다.
  • 성능
    • 이 메소드는 배열의 모든 요소를 한 번씩만 순회하기 때문에, 성능 측면에서 이미 최적화되어 있습니다. 배열의 크기에 비례하는 O(n) 시간 복잡도를 가집니다.
  • 개선 제안
    • 가독성 향상: 코드의 가독성을 더욱 향상시키기 위해, 최종 평균을 계산할 때 바로 arr.length로 나누어서 answer에 할당할 수 있습니다. 이는 작은 변경이지만, 코드를 더 간결하게 만들 수 있습니다.
    • 에러 처리: 입력 배열이 비어있는 경우 (즉, arr.length == 0인 경우)에 대한 처리가 없습니다. 이 상황을 처리하지 않으면, 0으로 나누는 오류가 발생할 수 있습니다. 배열이 비어 있는 경우에 대한 적절한 처리를 추가하는 것이 좋습니다.
class Solution {
    public double solution(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0; // 또는 적절한 예외 처리
        }
        
        double sum = 0;
        for (int num : arr) {
            sum += num;
        }
        return sum / arr.length;
    }
}

 

 

 

3. 자릿수 더하기

import java.util.stream.Stream;

public class Solution {
    public int solution(int n) {
        int answer = 0;
        int[] arr = Stream.of(String.valueOf(n).split("")).mapToInt(Integer::parseInt).toArray();
        for (int num : arr) {
            answer += num;
        }
        return answer;
    }
}

 

코드 리뷰

  • 코드 스타일과 가독성
    • 스트림 사용: Stream.of(), mapToInt(), toArray() 메소드를 사용하여 입력된 정수를 개별 자릿수의 배열로 변환하는 방식은 현대적이고 세련된 접근법입니다. 이러한 접근은 코드의 가독성을 높이며, 함수형 프로그래밍의 장점을 활용합니다.
    • 변수명: answer 변수명은 최종 결과를 저장하는 데 적합하며, 의미가 명확합니다.
  • 성능
    • 스트림 API는 간결함과 가독성 측면에서 매우 유용하지만, 반복문에 비해 약간의 성능 오버헤드가 발생할 수 있습니다. 그러나 주어진 문제의 규모에서는 이러한 성능 차이가 눈에 띄게 중요하지 않을 수 있습니다.
    • 자릿수의 합계를 구하는 과정은 O(n) 시간 복잡도를 가지며, 여기서 n은 입력된 정수 n의 자릿수입니다.
  • 개선 제안
    • 코드는 이미 잘 구현되어 있으며, 주어진 작업을 효과적으로 수행합니다. 특별한 성능 개선이 필요하지 않은 경우, 현재 접근 방식을 유지하는 것이 좋습니다.
    • 입력값 n이 음수인 경우에 대한 처리가 명시적으로 포함되어 있지 않습니다. 문제의 요구사항에 따라 음수 입력에 대한 처리를 추가할 수 있습니다. 예를 들어, n이 음수일 경우 절대값을 취하는 로직을 추가할 수 있습니다.
import java.util.stream.Stream;

public class Solution {
    public int solution(int n) {
        int answer = 0;
        // 음수일 경우 절대값 취하기
        n = Math.abs(n);
        int[] arr = Stream.of(String.valueOf(n).split("")).mapToInt(Integer::parseInt).toArray();
        for (int num : arr) {
            answer += num;
        }
        return answer;
    }
}
  • 이 변경은 메소드가 음수 값에 대해서도 올바르게 동작하도록 보장합니다. 다만, 문제의 요구사항에 따라 음수 처리가 필요한지를 확인해야 합니다.

 

 

 

4. 약수의 합

class Solution {
    public int solution(int n) {
        int answer = 0;
        for (int i = 1; i <= n; i++) {
            if (n % i == 0) {
                answer += i;
            }
        }
        return answer;
    }
}

 

코드 리뷰

  • 코드 스타일과 가독성
    • 변수명: answer 변수는 최종적으로 계산된 약수의 합을 저장하며, 이는 반환값으로 적절하게 사용됩니다.
    • 루프 사용: for 반복문을 사용하여 1부터 n까지 모든 숫자를 순회하며, n을 현재 숫자 i로 나누어 나머지가 0인 경우(즉, i가 n의 약수인 경우) answer에 i를 더합니다. 이 로직은 약수를 찾는 전통적인 방법을 잘 구현합니다.
  • 성능
    • 이 알고리즘의 시간 복잡도는 O(n)입니다. n의 크기에 비례하여 성능이 결정됩니다. 큰 n 값에 대해서는 이 코드가 비효율적일 수 있습니다.
    • 약수를 찾는 과정을 최적화할 수 있는 방법 중 하나는 n의 제곱근까지만 반복하고, 각 약수를 찾을 때마다 n을 해당 약수로 나눈 몫도 함께 약수로 추가하는 것입니다. 이 방법은 약수가 짝을 이루는 성질을 이용한 것입니다.
  • 개선 제안
    • 성능을 향상시키기 위해 약수 찾기 과정을 최적화할 수 있습니다.
class Solution {
    public int solution(int n) {
        int answer = 0;
        for (int i = 1; i * i <= n; i++) {
            if (n % i == 0) {
                answer += i;
                if (i != n / i) {
                    answer += n / i;
                }
            }
        }
        return answer;
    }
}

// Stream API 사용
import java.util.stream.IntStream;

class Solution {
    public int solution(int n) {
        return IntStream.rangeClosed(1, n) // 1부터 n까지의 정수 스트림 생성
                        .filter(i -> n % i == 0) // n의 약수인 정수만 필터링
                        .sum(); // 약수들의 합계
    }
}
  • 이 개선된 코드는 i가 n의 제곱근까지만 반복합니다. 만약 n이 i로 나누어 떨어진다면, i는 n의 약수이며, n / i도 n의 약수입니다. 하지만 i와 n / i가 같은 경우(즉, n이 완전제곱수인 경우)를 중복으로 더하지 않도록 주의합니다.
  • 스트림 API를 사용한 이 접근법은 코드의 가독성과 선언적 표현의 강점을 제공하지만, 내부적으로는 여전히 반복 연산을 수행합니다. 따라서 성능 측면에서는 전통적인 반복문을 사용한 접근법과 유사할 수 있으며, 경우에 따라서는 약간의 오버헤드가 발생할 수도 있습니다.

 

 

 

5. 나머지가 1이 되는 수 찾기

class Solution {
    public int solution(int n) {
        int answer = 0;
        for (int i = 1; i <= n; i++) {
            if (n % i == 1) {
                answer = i;
                break;
            }
        }
        return answer;
    }
}

 

코드 리뷰

  • 코드 스타일과 가독성
    • 변수명: answer 변수는 문제의 조건을 만족하는 i 값을 저장하며, 최종적으로 이 값을 반환합니다. 이는 적절하게 명명되었습니다.
    • 루프 사용: for 반복문을 사용하여 1부터 n까지 모든 숫자에 대해 순회합니다. 이는 주어진 문제를 해결하기 위한 표준 접근 방식입니다.
    • 조건문: if 조건문을 통해 n % i == 1을 만족하는지 확인합니다. 이 조건이 만족되는 첫 번째 i 값을 찾으면 즉시 반복문을 중단(break)하고, 해당 값을 반환합니다.
  • 성능
    • 이 알고리즘의 시간 복잡도는 최악의 경우 O(n)입니다. n이 클수록 성능에 미치는 영향이 커집니다. 그러나 주어진 문제의 특성 상, 나머지가 1이 되는 가장 작은 i를 찾기 위해서는 이러한 접근 방식이 필수적입니다.
    • 대부분의 경우, 나머지가 1이 되는 i는 n에 비해 상대적으로 작은 값에서 발견될 수 있으므로 실제 실행 시간은 일반적으로 훨씬 짧을 수 있습니다.
  • 개선 제안
    • 코드는 이미 잘 작성되어 있으며, 주어진 문제의 요구사항을 정확하게 충족합니다. 따라서 성능이나 가독성 측면에서 큰 개선이 필요하지 않습니다.
    • 입력 값 n에 대한 유효성 검사를 추가할 수 있습니다. 예를 들어, n이 음수인 경우 또는 특정 범위를 벗어나는 경우에 대한 처리를 추가할 수 있습니다. 그러나 문제의 맥락에서 이러한 검사의 필요성은 명시되지 않았습니다.
class Solution {
    public int solution(int n) {
        if (n < 1) {
            return -1; // 또는 적절한 예외 처리
        }
        int answer = 0;
        for (int i = 1; i <= n; i++) {
            if (n % i == 1) {
                answer = i;
                break;
            }
        }
        return answer;
    }
}

 

 

 

6. x만큼 간격이 있는 n개의 숫자

class Solution {
    public long[] solution(int x, int n) {
        long[] answer = new long[n];
        long sum = 0;
        for (int i = 0; i < n; i++) {
            sum = sum + x;
            answer[i] = sum;
        }
        return answer;
    }
}

 

코드 리뷰

  • 코드 스타일과 가독성
    • 변수명: answer 배열은 문제의 요구사항을 만족하는 수열을 저장하며, 최종적으로 이 배열을 반환합니다. sum 변수는 각 단계에서의 누적값을 저장하는 데 사용됩니다. 이는 명확하고 적절한 변수명 사용의 좋은 예입니다.
    • 루프 사용: for 반복문을 사용하여 n번 반복하면서, 각 반복마다 x를 누적하여 answer 배열의 각 요소에 할당합니다. 이는 수열을 생성하는 간단하고 효과적인 방법입니다.
  • 성능
    • 이 알고리즘의 시간 복잡도는 O(n)입니다. n의 크기에 비례하여 실행 시간이 증가합니다. 이는 주어진 문제를 해결하는데 필요한 최적의 접근 방식입니다.
    • sum을 사용하여 누적값을 계산하는 방식은 수학적 계산의 오버헤드를 최소화하며, 큰 값에 대해서도 안정적으로 작동합니다.
  • 개선 제안
    • 코드는 이미 잘 작성되어 있으며, 주어진 문제의 요구사항을 정확하게 충족합니다. 따라서 큰 개선이 필요하지 않습니다.
    • sum 변수의 초기값을 x로 설정하고, 반복문을 1부터 시작하여 n-1까지 반복하는 대신, 현재 방식을 유지하는 것이 더 직관적입니다. 현재 방식은 sum의 초기값을 0으로 설정하고, 반복문 내에서 누적하기 전에 x를 더하는 방식으로 명확합니다.
//Stream API 사용
import java.util.stream.LongStream;

class Solution {
    public long[] solution(int x, int n) {
        return LongStream.rangeClosed(1, n) // 1부터 n까지의 숫자 스트림 생성
                         .map(i -> (long) x * i) // 각 요소에 x를 곱하여 수열 생성
                         .toArray(); // 스트림을 배열로 변환
    }
}
  • LongStream.rangeClosed(1, n): 1부터 n까지의 연속된 정수 스트림을 생성합니다. 이는 for 반복문에서의 반복 범위를 대체합니다.
  • .map(i -> (long) x * i): 스트림의 각 요소(1부터 n까지의 각 정수)에 함수를 적용합니다. 여기서는 각 정수 i에 대해, 입력 값 x와 곱셈을 수행하여 x씩 증가하는 수열을 생성합니다. x를 long으로 캐스팅하여 곱셈 결과가 long 타입이 되도록 합니다. 이는 입력 값 x가 큰 경우 오버플로를 방지하기 위함입니다.
  • .toArray(): 최종적으로 스트림을 long[] 배열로 변환합니다.

장점

  • 가독성: 코드가 더 선언적이며, 무엇을 하는지 한눈에 이해하기 쉽습니다.
  • 간결성: 수행하려는 작업을 몇 줄의 코드로 간결하게 표현할 수 있습니다.
  • 함수형 프로그래밍: Stream API를 사용하면 함수형 프로그래밍 패러다임을 활용할 수 있어, 코드의 부수 효과(side-effects)를 줄이고 불변성(immutability)을 증진시킬 수 있습니다.

 

 

 

7. 자연수 뒤집어 배열로 만들기

import java.util.stream.Stream;

class Solution {
    public int[] solution(long n) {
        int[] arr = Stream.of(String.valueOf(n).split("")).mapToInt(Integer::parseInt).toArray();
        int[] answer = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            answer[i] = arr[arr.length - (1 + i)];
        }
        return answer;
    }
}

 

코드 리뷰

  • 코드 스타일과 가독성
    • 변수명: arr와 answer라는 변수명은 각각 변환된 정수 배열과 최종 결과를 담는 배열을 나타냅니다. 이는 명확하고 적절한 이름 선택입니다.
    • 스트림 사용: Stream.of(String.valueOf(n).split(""))를 통해 숫자를 문자열로 변환하고 개별 문자로 분리한 다음, mapToInt(Integer::parseInt)를 사용해 정수 배열로 변환하는 과정은 세련되고 현대적인 접근법을 나타냅니다.
  • 성능
    • 스트림 API는 편리하고 가독성이 높지만, 간단한 배열 변환 작업에서는 기존의 반복문에 비해 성능 오버헤드가 있을 수 있습니다. 그러나 이 경우에는 입력 크기가 크게 문제가 되지 않으므로 실제 영향은 미미할 것입니다.
    • 배열을 뒤집는 과정에서는 전통적인 반복문을 사용하고 있으며, 이는 효율적입니다.
  • 개선 제안
    • 코드는 이미 잘 작성되어 있고, 요구사항을 충족합니다. 그러나, 배열을 뒤집는 과정을 스트림 API를 사용하지 않고 직접적으로 반복문을 통해 구현한 것은 스트림과의 일관성을 다소 떨어뜨립니다. 이 부분은 스트림 API를 사용하여 더 간결하게 만들 수 있지만, 현재 상태에서도 충분히 효율적이고 명확합니다.
    • 배열을 뒤집는 작업을 더 간결하게 하기 위해, Java 8 이상의 IntStream.range와 함께 스트림을 사용하여 인덱스를 역순으로 처리할 수 있습니다. 하지만 이 방법은 기존 방법보다 성능 면에서 이점을 제공하지는 않습니다.
    • 스트림 API를 사용한 배열의 역순 생성 같은 대안은 가독성을 높일 수 있으나, 성능 측면에서는 기존의 접근법과 유사하거나 약간 느릴 수 있습니다.
import java.util.stream.IntStream;

class Solution {
    public int[] solution(long n) {
        String s = String.valueOf(n);
        return IntStream.range(0, s.length()) // 0부터 문자열 길이-1까지의 범위
                        .map(i -> s.charAt(s.length() - i - 1) - '0') // 역순으로 문자 접근 후 정수 변환
                        .toArray(); // 스트림을 배열로 변환
    }
}

 

StringBuilder 사용 개선

import java.util.stream.IntStream;

class Solution {
    public int[] solution(long n) {
        return new StringBuilder().append(n).reverse().chars().map(Character::getNumericValue).toArray();
    }
}
  • 성능 개선
    • 메모리 사용: StringBuilder를 사용하는 방법은 중간에 생성되는 스트림 객체나 람다 표현식 없이 직접 문자열을 조작하기 때문에 메모리 사용량이 적을 수 있습니다.
    • 속도: 문자열 조작과 관련된 작업에서 StringBuilder는 내부적으로 문자 배열을 사용하고, 크기 조정이 필요할 때만 새로운 배열을 할당하기 때문에 매우 빠릅니다. 반면, 스트림 API는 내부적인 반복 및 함수 호출 오버헤드가 있을 수 있습니다.
    • 특히 문자열을 역순으로 만드는 작업과 같이 단순한 조작이 필요한 경우 StringBuilder를 사용하는 것이 Stream API를 사용하는 것보다 성능상의 이점을 가질 수 있습니다.

 

 

 

8. 문자열을 정수로 바꾸기

class Solution {
    public int solution(String s) {
        int answer = Integer.parseInt(s);
        return answer;
    }
}

 

코드 리뷰

  • 정확성
    • Integer.parseInt(s)는 문자열 s가 나타내는 정수 값을 반환합니다. 이 메소드는 문자열이 유효한 정수 형식일 때 올바르게 작동합니다.
    • 만약 s가 유효한 정수 형식이 아니라면, NumberFormatException을 발생시킵니다. 이는 입력 값에 대한 유효성 검사가 없음을 의미합니다.
  • 효율성
    • 이 메소드는 매우 간단하고, 한 가지 작업만 수행하기 때문에 효율적입니다. 추가적인 로직이나 불필요한 연산이 없습니다.
  • 가독성
    • 코드는 깔끔하고 매우 간결합니다. 작업을 수행하는 데 필요한 최소한의 단계만 포함되어 있습니다.
    • answer 변수는 여기서 불필요한 단계를 추가합니다. 입력을 받아 바로 변환하고 반환하는 것이 가능하기 때문에, 이 변수는 중간 단계로서 생략 가능합니다.
  • 개선 사항
    • 가독성을 높이기 위해, answer 변수 없이 바로 결과를 반환하는 것이 좋습니다. 이렇게 하면 코드가 더욱 간결해집니다.
    • 유효성 검사를 추가하여 입력이 유효한 정수 형식인지 확인할 수 있습니다. 하지만, 이 메소드의 목적이 단순히 문자열을 정수로 변환하는 것이라면, 유효성 검사는 메소드를 호출하는 측에서 수행하는 것이 더 적절할 수 있습니다.
class Solution {
    public int solution(String s) {
        return Integer.parseInt(s);
    }
}

 

9. 정수 제곱근 판별

class Solution {
    public long solution(long n) {
        if (Math.pow((int)Math.sqrt(n), 2) == n) {
            return (long) Math.pow(Math.sqrt(n)+1, 2);
        }
        return -1;
    }
}

 

코드 리뷰

  • 문제점
    • 문제의 핵심은 Math.pow((int)Math.sqrt(n), 2) == n 비교 부분에 있습니다. 여기서 Math.sqrt(n)의 결과는 double이고, 이를 int로 캐스팅하면 소수점 이하가 잘립니다. 이로 인해, 원래 n이 완전제곱수라도, 캐스팅으로 인한 정밀도 손실로 인해 조건문이 거짓으로 평가될 수 있습니다.
    • 또한, Math.pow 함수를 사용하여 제곱근을 계산한 후 다시 제곱하는 것은 비효율적이며, 부동 소수점 연산의 복잡성으로 인해 오류를 발생시킬 수 있는 여지를 제공합니다.
  • 효율성
    • 코드는 간단하고 직관적입니다. 하지만, Math.sqrt(n)을 여러 번 호출하고 있어서, 이 부분의 중복을 제거하여 성능을 약간 개선할 수 있습니다.
  • 가독성
    • 코드는 간결하고 이해하기 쉽습니다. 그러나, Math.pow((int)Math.sqrt(n), 2) == n 부분에서 Math.pow와 Math.sqrt를 혼합하여 사용하는 것은 처음 보는 사람에게는 약간 혼란스러울 수 있습니다. 또한, (int)Math.sqrt(n) 부분에서 Math.sqrt(n)의 결과를 정수로 강제 변환하는 것은 정확도 손실을 일으킬 수 있습니다.
  • 개선 제안
    • 제곱근을 한 번만 계산하고 결과를 변수에 저장하여 사용합니다. 이렇게 하면 코드의 실행 효율성을 높일 수 있습니다.
    • 정확도를 유지하기 위해 double 타입을 사용하고, 비교 시에 오차를 고려할 수 있도록 합니다. 하지만, 이 경우 정수 제곱근 판별에는 오차를 고려할 필요가 없으므로, 단순화된 접근 방식이 더 적합할 수 있습니다.
class Solution {
    public long solution(long n) {
        double sqrtN = Math.sqrt(n);
        long intSqrtN = (long) sqrtN;
        if (intSqrtN * intSqrtN == n) {
            return (long) ((sqrtN + 1) * (sqrtN + 1));
        }
        return -1;
    }
}

 

 

 

10. 정수 내림차순으로 배치하기

import java.util.Arrays;

class Solution {
    public long solution(long n) {
        long answer = 0;
        String str = Long.toString(n);
        char[] charArr = str.toCharArray();
        Arrays.sort(charArr);
        String reverseStr = new StringBuilder(new String(charArr)).reverse().toString();
        answer = Long.parseLong(reverseStr);
        return answer;
    }
}

 

코드 리뷰

  • 정확성과 목적
    • 코드는 주어진 목적을 정확하게 충족합니다. 주어진 숫자를 문자열로 변환하고, 문자 배열로 만든 후 정렬하여, 최종적으로 뒤집어서 큰 숫자를 만드는 과정이 잘 구현되어 있습니다.
  • 효율성
    • Arrays.sort() 메소드를 사용하여 문자 배열을 정렬하는 방법은 효율적입니다. 문자 배열로 변환 후 정렬하는 것은 주어진 문제를 해결하기 위한 간단하고 직관적인 접근법입니다.
    • 뒤집기 과정에서 StringBuilder를 사용하는 것도 효율적입니다. StringBuilder의 reverse 메소드는 내부적으로 문자열을 빠르게 뒤집을 수 있게 해 줍니다.
  • 가독성
    • 코드는 매우 깔끔하고 이해하기 쉽습니다. 변수명도 적절히 사용되어 있어 코드의 목적을 이해하기 쉽게 만듭니다.
    • 각 단계(변환, 정렬, 뒤집기, 다시 변환)가 명확하게 구분되어 있어, 코드의 흐름을 따라가기 쉽습니다.
  • 개선 사항
    • 코드는 이미 잘 작성되어 있으며, 주어진 문제를 효율적으로 해결합니다. 따라서 큰 개선 사항은 없습니다.
    • 다만, 코드의 명확성을 더욱 높이기 위해, 최종적으로 문자열을 다시 긴 정수로 변환하는 부분에서 answer 변수에 바로 할당하는 대신, 바로 return 문에서 처리할 수도 있습니다. 이렇게 하면 answer 변수의 선언과 초기화를 생략할 수 있어 코드를 조금 더 간결하게 만들 수 있습니다.
import java.util.Arrays;

class Solution {
    public long solution(long n) {
        String str = Long.toString(n);
        char[] charArr = str.toCharArray();
        Arrays.sort(charArr);
        String reverseStr = new StringBuilder(new String(charArr)).reverse().toString();
        return Long.parseLong(reverseStr);
    }
}

 

 

 

11. 하샤드 수

import java.util.stream.Stream;

class Solution {
    public boolean solution(int x) {
        boolean answer = true;
        int sum = 0;
        int[] arrays = Stream.of(String.valueOf(x).split("")).mapToInt(Integer::parseInt).toArray();
        for (int num : arrays) {
            sum += num;
        }
        if (x % sum != 0) {
            answer = false;
        }    

        return answer;
    }
}

 

코드 리뷰

  • 정확성
    • 이 메소드는 주어진 문제의 요구 사항을 정확하게 충족합니다. 문자열 변환, 자릿수 분할, 각 자릿수의 합 계산, 그리고 나눗셈의 결과에 따른 불리언 반환까지 모든 단계가 포함되어 있습니다.
  • 효율성
    • Stream.of(String.valueOf(x).split("")).mapToInt(Integer::parseInt).toArray();를 사용하여 정수 x를 문자열로 변환하고, 각 자릿수를 분할하여 정수 배열로 변환하는 과정은 Java 8 스트림 API를 활용한 멋진 접근 방식입니다. 하지만, 크기가 작은 배열이나 간단한 연산에 대해서는 스트림 API가 기본 반복문보다 성능적으로 불리할 수 있습니다.
    • for 루프를 사용하여 각 자릿수의 합을 계산하는 부분은 효율적입니다.
  • 가독성
    • 코드는 잘 구조화되어 있고, 각 단계가 명확하게 표현되어 있어 가독성이 좋습니다. 변수 이름도 직관적이어서 코드의 목적을 쉽게 이해할 수 있습니다.
    • answer 변수를 사용하는 대신, 바로 결과를 반환할 수도 있습니다. 이는 코드의 간결성을 높이고, 변수의 사용을 최소화하여 가독성을 더욱 개선할 수 있습니다.
  • 개선사항
    • answer 변수는 불필요하게 초기화되고 있으며, 조건에 따라 바로 true 또는 false를 반환할 수 있습니다. 이렇게 하면 코드의 실행 경로를 간소화하고, 가독성을 높일 수 있습니다.
    • 스트림 API 대신 기본 반복문을 사용하여 성능을 약간 향상시킬 수 있지만, 이는 코드의 명확성과 가독성에 영향을 줄 수 있으므로 선택적인 변경사항입니다.
class Solution {
    public boolean solution(int x) {
        int sum = 0;
        String value = String.valueOf(x);
        for (char c : value.toCharArray()) {
            sum += c - '0';
        }
        return x % sum == 0;
    }
}

class Solution {
    public boolean solution(int x) {
        int sum = String.valueOf(x).chars().map(ch -> ch - '0').sum();
        return x % sum == 0;
    }
}

 

 

 

12. 두 정수 사이의 합

import java.util.stream.LongStream;

class Solution {
    public long solution(int a, int b) {
        long answer = 0;
        if (a == b) {
            answer = (long) a;
        } else if (a > b) {
            answer = LongStream.rangeClosed(b, a).sum();
        } else {
            answer = LongStream.rangeClosed(a, b).sum();
        }
        return answer;
    }
}

 

코드 리뷰

  • 정확성
    • 이 메소드는 주어진 문제를 정확하게 해결합니다. a와 b의 값에 따라 적절히 조건을 분기하여, 두 수가 같은 경우는 그 수를, 다른 경우는 두 수 사이의 모든 정수의 합을 반환합니다.
  • 효율성
    • LongStream.rangeClosed를 사용하여 정수 범위의 합을 계산하는 것은 효율적인 방법입니다. 스트림 API는 내부적으로 범위의 합을 효율적으로 계산할 수 있는 최적화를 제공합니다.
    • a와 b의 상대적인 크기에 따라 조건문을 사용하여 범위를 정하는 것도 효율적입니다. 이는 불필요한 계산을 방지합니다.
  • 가독성
    • 코드는 간결하고, 의도가 명확합니다. 조건문을 사용하여 다양한 경우를 처리하는 방식은 코드를 이해하기 쉽게 만듭니다.
    • answer 변수를 사용하여 최종 결과를 저장하고 반환하는 방식은 코드의 흐름을 명확하게 보여줍니다.
  • 개선 사항
    • 코드는 이미 잘 작성되어 있으며, 주어진 문제를 효과적으로 해결합니다. 따라서, 큰 개선 사항은 없습니다.
    • 하지만, 코드의 간결성을 더욱 높이기 위해 조건문을 단순화하고, answer 변수 없이 결과를 직접 반환할 수 있습니다
class Solution {
    public long solution(int a, int b) {
        return LongStream.rangeClosed(Math.min(a, b), Math.max(a, b)).sum();
    }
}

 

 

 

13. 콜라츠 추측

class Solution {
    public int solution(long num) { // 계산 실행 도중 int 범위 초과 문제 발생 방지
        int answer = 0;
        while (num != 1) {
            if (answer == 500) {
                answer = -1;
                break;
            }
            if (num % 2 == 0) {
                num /= 2;
            } else {
                num = num * 3 + 1;
            }
            answer++;
        }
        return answer;
    }
}

 

코드 리뷰

  • 정확성
    • 주어진 문제의 조건을 정확하게 구현하고 있습니다. num이 1이 될 때까지 주어진 규칙에 따라 연산을 반복하고, 연산 횟수를 세어 answer에 저장합니다.
  • 효율성
    • 이 방법은 간단하고 직관적이며, 주어진 문제를 해결하기에 충분합니다. 반복문을 사용하여 조건에 맞게 처리하고, 500번의 반복 한계를 설정함으로써 무한 루프에 빠지는 것을 방지합니다.
  • 가독성
    • 코드는 매우 명확하고 이해하기 쉽습니다. 변수 이름 answer가 연산을 수행한 횟수를 저장하는 용도로 사용되며, num의 값 변경을 통해 주어진 규칙을 따릅니다.
    • answer를 증가시키는 방식과 종료 조건을 명확하게 설정하여, 코드의 읽기 쉬움을 보장합니다.
  • 개선 사항
    • 이 코드는 이미 효과적으로 작성되었으며, 주어진 문제의 요구 사항을 잘 충족합니다. 따라서, 큰 개선이 필요하지 않습니다.
    • 성능 개선이나 가독성 측면에서 큰 변경을 가할 필요가 없으며, 주어진 문제를 해결하는 데 있어 이미 최적화된 접근 방식을 사용하고 있습니다.

 

 

 

페어프로그래밍

문제 - (수행 역할)

 

1. 짝수와 홀수 - 네비게이터

package pair;

public class Test1 {
    // 1. 조건문을 사용해서 Even하고 Odd를 String 타입으로 반환
    // 2. 나눗셈 연산이 필요
    int num = 3;
    // 정답 코드 부분
    String answer;
    if(num % 2== 0){
        // 3. answer의 값을 Even으로 대입, 대입 연산자 (=) 사용
        answer = "Even";
        //System.out.println("Even");
    } else {
        // 4. answer의 값을 Odd로 대입
        answer = "Odd";
        //System.out.println("Odd");
    }
    // 삼항 연산자 사용하는 방법 -> 네비게이터 추천 코드
    answer = (num % 2 == 0) ? "Even" : "Odd";
}

 

ChatGPT 코드 리뷰 개선 사항

  • 조건문 사용 설명: 조건문을 사용하여 짝수(Even)와 홀수(Odd)를 판별하는 로직에 대한 설명은 명확하게 이루어졌습니다. 하지만, 조건문이 왜 필요한지, 어떤 문제를 해결하기 위해 사용되는지에 대한 배경 설명이 추가되면 더욱 좋을 것입니다.
  • 나눗셈 연산 설명: 나눗셈 연산(%)이 짝수와 홀수를 구분하는 데 어떻게 사용되는지에 대한 설명이 있었으나, % 연산자가 '나머지'를 반환한다는 점을 명시적으로 언급하며, 이 연산 결과가 0이면 짝수, 아니면 홀수라는 점을 좀 더 상세히 설명할 필요가 있습니다.
  • 대입 연산자 사용 설명: 대입 연산자 =를 사용하여 answer 변수에 값을 할당하는 부분의 설명은 있으나, 대입 연산자가 기본적으로 어떻게 작동하는지, 왜 이 방식으로 값을 할당하는지에 대한 기본 원리에 대한 설명이 추가되면 좋을 것 같습니다.
  • 삼항 연산자 설명: 삼항 연산자를 사용한 간결한 코드 제안은 훌륭합니다. 그러나 삼항 연산자의 구조와 작동 방식(조건 ? 참일 때 값 : 거짓일 때 값)에 대한 설명이 부족합니다. 삼항 연산자가 어떻게 조건문을 간결하게 대체할 수 있는지에 대한 설명을 추가하는 것이 좋습니다.

개선 코드

package pair;

public class Test1 {
    // 나머지 연산자(%)를 사용하여 num이 짝수인지 홀수인지 판별한다.
    // % 연산자는 피연산자를 나눈 후 나머지를 반환한다. 여기서 2로 나누었을 때 나머지가 0이면 짝수, 아니면 홀수이다.
    int num = 3;

    String answer;

    // 조건문을 사용하여 num이 짝수인지 홀수인지 확인한다.
    if (num % 2 == 0) {
        // num이 짝수일 경우, answer에 "Even"을 할당한다.
        answer = "Even";
    } else {
        // num이 홀수일 경우, answer에 "Odd"을 할당한다.
        answer = "Odd";
    }

    // 삼항 연산자를 사용하여 위의 조건문을 더 간결하게 표현할 수 있다.
    // 조건 ? 참일 때 값 : 거짓일 때 값의 형식으로 작성한다.
    answer = (num % 2 == 0) ? "Even" : "Odd";
}

 

 

 

2. 평균 구하기 - 드라이버

package pair;

public class Test2 {
    public static void main(String[] args) {
        // 변수 선언
        double answer = 0;
        int[] arr = {1,2,3,4};
        // 배열의 반복과 합
        for (int num : arr) {
            answer += num;
        }
        // 합을 나눠 평균구하기
        answer = answer / arr.length;
        System.out.println(answer);
    }
}

 

ChatGPT 코드 리뷰 개선 사항

 

  • 코드 최적화: 현재 코드는 평균을 구하는 과정에서 기본적인 로직을 잘 따르고 있습니다. 효율성 측면에서는 이미 최적화되어 있으나, 가독성을 더 높이기 위해 Java 8 이상의 스트림 API를 사용할 수 있습니다. 스트림을 사용하면 코드를 더 간결하게 만들 수 있습니다.
  • 타입 선택: 평균을 계산할 때 double 타입을 사용하는 것은 적절합니다. 이는 정확한 소수점 결과를 얻기 위함입니다. 코드에서 이미 answer 변수를 double로 선언하고 있어, 이 부분은 잘 처리되었습니다.

개선 코드

package pair;

import java.util.Arrays;

public class Test2 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4};
        // 스트림을 사용하여 배열의 평균을 계산하고 출력
        double average = Arrays.stream(arr).average().orElse(Double.NaN);
        System.out.println(average);
    }
}

 

 

 

3. 자릿수 더하기 - 네비게이터

package pair;

import java.util.stream.Stream;

public class Test3 {
    public static void main(String[] args) {
        // 자연수 n의 각 자릿수 합을 구해서 반환
        // 나눗셈 연산 및 반복문 사용으로 n을 각 자릿수 별로 나누고 합계 구하기

        // 변수
        // 변수 answer : 합계 값 넣을 변수
        int n = 123;
        int answer = 0;
        // while문 사용 - while ( n 조건식) { 연산 수행 코드 }
        while ( n > 0 ){
            // n의 자릿수 구하기는 나눗셈 연산을 사용하기
            answer += n % 10;
            n = n/10;
        }
        System.out.println(answer);

        // Stream 사용하는 방법
        int[] arr = Stream.of(String.valueOf(n).split("")).mapToInt(Integer::parseInt).toArray();
        for (int num : arr) {
            answer += num;
        }
    }
}

 

ChatGPT 코드 리뷰 개선 사항

 

  • 스트림을 사용한 방법의 위치: n의 값을 수정한 후 스트림을 사용하는 방식으로 배열 arr을 초기화하고 있습니다. 이는 n이 이미 while 루프를 통해 0이 되었기 때문에, 스트림을 사용하는 부분에서 arr은 항상 0만을 포함하는 배열이 됩니다. 스트림을 사용하는 로직은 n의 원래 값에 대해 독립적으로 수행되어야 합니다.
  • 변수 answer의 재사용: 두 번째 방법에서 answer 변수를 다시 사용하면서 첫 번째 방법에서 계산한 값에 추가하고 있습니다. 이는 두 방법의 결과를 별도로 관찰하려는 경우 잘못된 결과를 초래할 수 있습니다.

개선 코드

package pair;

import java.util.stream.Stream;

public class Test3 {
    public static void main(String[] args) {
        int n = 123;

        // 방법 1: 반복문 사용
        // 자연수 n의 각 자릿수 합을 구해서 반환. 나눗셈 연산 및 반복문 사용으로 n을 각 자릿수 별로 나누고 합계 구하기.
        int answer = 0; // 합계를 저장할 변수
        int temp = n; // n의 값은 변경되므로 임시 변수에 저장
        while (temp > 0) {
            answer += temp % 10; // 현재 자릿수를 answer에 더함
            temp = temp / 10; // n을 10으로 나누어 다음 자릿수로 이동
        }
        System.out.println(answer);

        // 방법 2: 스트림 사용
        // 자연수 n을 문자열로 변환 후 스트림으로 각 자리 숫자로 분해하여 합계를 구함.
        // n의 원래 값으로 초기화된 배열 arr을 생성
        int[] arr = Stream.of(String.valueOf(n).split("")).mapToInt(Integer::parseInt).toArray();
        int streamAnswer = 0; // 스트림을 사용하여 계산한 합계를 저장할 새 변수
        for (int num : arr) {
            streamAnswer += num;
        }
        System.out.println(streamAnswer);
    }
}

 

 

 

4. 약수의 합 - 드라이버

package pair;

public class Test4 {
    public static void main(String[] args) {
        // 반복문 사용해서 모든 숫자를 나눈다
        // 나머지가 0이면 약수
        int n = 12;
        int answer = 0;
        for (int i = 1; i <= n; i++) {
            if (n % i == 0) {
                answer += i;
            }
        }
        System.out.println(answer);
    }
}

 

ChatGPT 코드 리뷰 개선 사항

  • 코드 최적화: 현재 코드는 이미 목적에 부합하게 잘 작성되어 있으며, 약수를 찾아 그 합을 계산하는 데 필요한 모든 요소를 포함하고 있습니다. 기본적인 알고리즘 측면에서는 별도의 최적화가 필요하지 않습니다.
  • 가독성과 주석의 개선: 코드의 가독성은 이미 좋으나, 주석에서는 "모든 숫자를 나눈다"라는 표현을 "주어진 숫자 n의 약수를 찾아 그 합을 계산한다"와 같이 더 명확하게 표현할 수 있습니다. 이는 코드의 목적을 좀 더 분명하게 전달하는 데 도움이 됩니다.

개선 코드

package pair;

public class Test4 {
    public static void main(String[] args) {
        // 주어진 숫자 n의 모든 약수를 찾아 그 합을 계산한다.
        // 약수는 나머지가 0이 되는 숫자를 의미한다.
        int n = 12;
        int answer = 0;
        for (int i = 1; i <= n; i++) {
            if (n % i == 0) {
                // i가 n의 약수인 경우, answer에 i를 더한다.
                answer += i;
            }
        }
        System.out.println(answer); // 약수의 합을 출력한다.
    }
}

 

 

 

 

5. 나머지가 1이 되는 수 찾기 - 네비게이터

package pair;

public class Test5 {
    public static void main(String[] args) {
        // x를 나눗셈 연산(%) 사용해서 나머지가 1인 최소 값 구하기
        // 변수
        int n = 10;
        int answer = 0; // 나머지가 1인 최소 값 반환 변수
        // 반복문, 조건문 사용 for (int i = 1; 조건식 ;i++) { 연산 코드 }
        for ( int i = 1; i < n; i++) {
            if(n % i == 1){
                answer = i;
                break;
            }
        }
        System.out.println(answer);
    }
}

 

ChatGPT 코드 리뷰 개선 사항

  • 변수 설명: n과 answer 변수에 대한 설명이 있으나, 각 변수의 역할을 좀 더 상세하게 설명할 수 있습니다.
  • 로직 설명: 반복문과 조건문의 사용 목적을 좀 더 명확히 설명할 필요가 있습니다. 특히, 반복문이 왜 1부터 시작하는지, 그리고 조건문에서 n % i == 1을 사용하는 이유와 그 의미를 상세히 설명할 필요가 있습니다.
  • 코드의 목적: 코드가 해결하고자 하는 문제에 대한 배경이나, 왜 이런 종류의 문제가 중요한지에 대한 설명이 포함되면 이해하기 더 쉬울 것입니다.

개선 코드

package pair;

public class Test5 {
    public static void main(String[] args) {
        // 주어진 자연수 n을 나눌 때 나머지가 1이 되는 가장 작은 양의 정수 i를 찾아 반환합니다.
        // 이는 수학적으로 n을 나누었을 때 나머지가 1이 되도록 하는 최소의 약수를 찾는 문제와 관련이 있습니다.
        int n = 10; // 검사 대상이 되는 자연수
        int answer = 0; // 나머지가 1이 되는 최소의 양의 정수를 저장할 변수

        // 1부터 n 미만까지 모든 정수에 대해 반복하며 조건을 검사합니다.
        // 나머지가 1이 되는 조건을 만족하는 최소의 i 값을 찾기 위해 반복합니다.
        for (int i = 1; i < n; i++) {
            // n을 i로 나누었을 때 나머지가 1이 되는 경우, i가 우리가 찾는 값입니다.
            if (n % i == 1) {
                answer = i; // 조건을 만족하는 i 값을 answer에 저장합니다.
                break; // 최소값을 찾았으므로 반복문을 종료합니다.
            }
        }

        System.out.println(answer); // 찾은 값 출력
    }
}

 

 

 

 

6. x만큼 간격이 있는 n개의 숫자 - 네비게이터

package pair;

public class Test6 {
    public static void main(String[] args) {
       // x를 n만큼 x씩 증가하는 배열을 만들어서 반환
        // x 값을 n 만큼 저장할 배열
        int x = 4;
        int n = 7;
        long[] answer = new long[n];
        long sum = 0;
        // 반복문 사용해서 x를 n만큼 x씩 증가
        for (int i = 0; i < n; i++) {
            sum = sum + x;
            answer[i] = sum;
        }
        for (long num : answer) {
            System.out.println(num);
        }
    }
}

 

ChatGPT 코드 리뷰 개선 사항

  • 변수 설명 개선: x, n, answer, 그리고 sum 변수의 역할과 중요성을 더 상세하게 설명할 필요가 있습니다. 특히, sum 변수가 어떻게 각 반복에서 x만큼 증가하는지에 대한 설명이 추가될 수 있습니다.
  • 로직 설명 추가: 반복문 내에서의 연산 과정(sum = sum + x;)과 이 과정이 어떻게 배열을 채우는지에 대한 상세한 설명이 필요합니다.
  • 로직 설명 추가: 반복문 내에서의 연산 과정(sum = sum + x;)과 이 과정이 어떻게 배열을 채우는지에 대한 상세한 설명이 필요합니다.

개선 코드

package pair;

public class Test6 {
    public static void main(String[] args) {
       // 주어진 시작 값 x를 사용하여 n개의 요소를 갖는 배열을 생성합니다.
       // 배열의 각 요소는 이전 요소에 x만큼 증가한 값입니다. 
       // 이를 통해, x의 배수를 포함하는 배열을 생성할 수 있습니다.
       
        int x = 4; // 시작 값
        int n = 7; // 배열의 요소 개수
        long[] answer = new long[n]; // 결과를 저장할 배열 초기화
        long sum = 0; // 반복문 내에서 x의 누적 합계를 저장할 변수
        
        // x를 시작으로 n만큼 x씩 증가하며 배열을 채웁니다.
        for (int i = 0; i < n; i++) {
            sum += x; // sum에 x를 더하여 누적 합계를 계산합니다.
            answer[i] = sum; // 계산된 누적 합계를 배열에 할당합니다.
        }
        
        // 생성된 배열의 요소를 출력합니다.
        for (long num : answer) {
            System.out.println(num);
        }
    }
}

 

 

 

 

7. 자연수 뒤집어 배열로 만들기 - 네비게이터

package pair;

public class Test7 {
    public static void main(String[] args) {
        // 자연수 n을 뒤집어 각지리 숫자를 배열로 반환
        // 변수
        int n = 12345;
        String num = Integer.toString(n);
        // n을 String 타입으로 변환 후 자릿수 별로 자르기
        int[] answer = new int[num.length()];
        int cnt = 0;
        // String 타입이 된 n을 순서 뒤집기
        while (n > 0) {
            // 자릿수 더하기에서 사용한 공식 활용(마지막 자리부터 배열에 넣을 수 있음)
            // 배열명[인덱스 번호] = 대입할 값;
            answer[cnt] = n % 10;
            cnt++;
            n /= 10;
            // n의 나머지 값을 배열 answer에 넣기, cnt 변수 활용
        }
    }
}

 

ChatGPT 코드 리뷰 개선 사항

  • 불필요한 변환 설명 삭제: 코드 내에서 n을 문자열로 변환하는 과정(String num = Integer.toString(n);)은 실제 배열을 채우는 데 사용되지 않으므로, 이 부분에 대한 설명은 혼동을 줄 수 있습니다. 대신, 직접 n을 처리하는 방식에 초점을 맞추는 것이 좋습니다.
  • 로직의 목적과 작동 방식 명확화: while 루프와 나머지 연산자를 사용하여 자연수 n을 뒤집는 과정을 좀 더 명확하게 설명할 필요가 있습니다. 특히, n % 10이 어떻게 n의 마지막 자리 숫자를 추출하는지, n /= 10이 n을 어떻게 갱신하는지에 대한 설명을 추가하는 것이 좋습니다.
  • 변수의 역할 명확히 하기: cnt 변수가 배열 내에서 현재 저장 위치를 추적하는 역할을 한다는 점을 명확하게 설명할 필요가 있습니다.

개선 코드

package pair;

public class Test7 {
    public static void main(String[] args) {
        // 주어진 자연수 n을 뒤집어 그 자릿수를 배열로 반환합니다.
        int n = 12345;

        // n을 뒤집어 저장할 배열을 초기화합니다. 배열의 크기는 n의 자릿수와 동일합니다.
        int[] answer = new int[String.valueOf(n).length()];
        int cnt = 0; // 배열의 인덱스로 사용할 카운터 변수를 초기화합니다.

        // n이 0보다 클 동안 반복하여 마지막 자리부터 배열에 순서대로 저장합니다.
        while (n > 0) {
            answer[cnt++] = n % 10; // n의 마지막 자리 숫자를 배열에 저장하고, cnt를 1 증가시킵니다.
            n /= 10; // n을 10으로 나누어 다음 자리 숫자로 이동합니다.
        }

        // 배열에 저장된 숫자들을 출력하거나 처리할 수 있습니다.
        // 예: for (int num : answer) { System.out.println(num); }
    }
}

 

 

 

멘토링

시니어 코치님 멘토링 내용

  • 자바의 다형성은 동명이인인데 요구하는 것과 주는 것이 다른 것이라고 이해하면 쉬움, 다형성의 좋은 점은 사용자가 주는 값에 따라 맞는 값을 주는 것(예: 한국어로 인사하면 한국어로 인사가 돌아오고 중국어로 인사하면 중국어로 인사가 돌아옴)
  • 주니어 개발자는 업무를 주고 개발 방향을 제시했을 때 지시대로 잘 수행할 수 있는 능력을 갖추는 것이 중요함
  • 시키는 것을 잘 이해할 수 있는 능력(커뮤니케이션 능력) - 모르는 것은 검색하거나 조언에 따라 개발할 수 있는 능력도 필요함
  • 기본적인 CS 지식은 필요함(업무적인 소통을 위해)
  • 포트폴리오 작성 시 좋은 거 개발한 기능 설명, 개발하는 과정에 대해 작성하는 것이 좋음(개발하기 위해 어떤 접근을 하고 문제 해결을 위해 어떤식으로 했는지) - 프로젝트를 통해 어떤 걸 배우고 문제 해결을 위해 어떤 고민을 했는지 (How보다 Why가 중요 - Why가 틀렸다면 왜 틀렸는지 알면 된다)
  • 주특기 배울 때 사용법을 배우는 것에 초점을 맞추는 게 좋음

기술 매니저님 멘토링 내용

  • 알고리즘 문제에서 가장 중요한 것 2개: 공간 복잡도, 시간 복잡도
    • 공간 복잡도 : 메모리를 얼마나 사용했는지
    • 시간 복잡도 : 처리하는데 걸리는 시간

'항해 99 > Java' 카테고리의 다른 글

페어 프로그래밍 - 코딩 테스트 3  (0) 2024.02.15
페어 프로그래밍 - 코딩 테스트 2  (0) 2024.02.14
WIL - 1  (1) 2024.02.12
역할과 구현, OCP  (0) 2024.02.08
Java 기초 12 - 다형성  (1) 2024.02.08