Java Study 질문 정리

song yuheon·2023년 8월 20일
0
  1. 운영체제에서 기계어를 읽을 수 있어?

바이트 코드는 JVM이 이해할 수 있는 언어입니다. 운영체제는 기계어를 읽을 수 있지만, 바이트 코드는 직접 읽을 수 없습니다. JVM이 바이트 코드를 JIT 컴파일러를 통해 해당 운영체제에서 실행 가능한 기계어로 변환합니다. 그 이후, 하드웨어는 이 기계어를 실행합니다.

  1. 모든 자바 프로젝트가 Main 메소드부터 시작하면 class가 main이 아닌 경우에도 되나?

    모든 Java 프로그램이 main 메서드를 필요로 하는 것은 아닙니다. 예를 들어, 애플릿(Applet)과 같은 GUI 기반의 애플리케이션은 main 메서드 없이 실행됩니다. 그러나 일반적인 어플리케이션의 실행을 위해선 main 메서드가 필요하며, 그 메서드가 포함된 클래스에서 시작됩니다. 여러 클래스가 있을 경우에도 하나의 main 메서드를 가진 클래스만 프로그램의 시작점으로 존재해야 합니다.

  2. 굳이 double이 아닌 float를 사용하는 이유는?

    floatdouble 둘 다 부동 소수점 숫자를 나타내는 데 사용되지만, float은 4바이트 (32비트)를 차지하고, double은 8바이트 (64비트)를 차지합니다. 그러므로, float를 사용하는 이유는 다음과 같습니다:

    • 메모리 절약: 큰 배열이나 많은 부동 소수점 변수가 있는 경우, float를 사용하면 메모리 사용량을 줄일 수 있습니다.
    • 성능: 특정 하드웨어나 상황에서 float 연산이 double 연산보다 빠를 수 있습니다.
    • API와의 호환성: 일부 라이브러리나 API가 float 형식을 요구하는 경우가 있습니다.

    그러나, doublefloat보다 더 높은 정밀도를 제공하기 때문에, 정밀한 계산이 필요할 때는 double을 사용하는 것이 좋습니다.


Q1. 로직이란?
A. 로직이란, 프로그램의 논리적인 흐름을 의미함. 프로그램이 어떠한 방식으로 동작하고, 어떤 연산이나 판단을 수행해야 하는지에 관한 핵심 부분임.

Q2. 아래 코드에서 ...이 뭐지?

    void carSpeeds(double ... speeds) {
        for (double v : speeds) {
            System.out.println("v = " + v);
        }
    }

A. '...'은 가변 길이의 매개 변수를 의미함. 이는 매개변수의 개수를 동적으로 지정할 수 있게 해주는 기능임. '타입...변수명' 형태로 사용하며, 내부적으로 배열을 생성하여 사용함. 다른 매개변수와 함께 사용할 때는, 가변 인자는 마지막에 선언해야함.

  • 가변 인자란?
    매개변수의 개수를 동적으로 지정해 줄 수 있는 기능의미
    타입...변수명 형태로 사용
    가변인자는 내부적으로 배열을 생성해서 사용
    다른 매개변수가 더 있다면 가변인자는 마지막에 선언
    void sum(String s, String...str) {
        for(String a:str)
            System.out.print(a+s);
가변인자 메소드 오버로딩 주의점
    void sum(String s, String...str) {
        for(String a:str)
            System.out.print(a+s);
    }
    
    void sum(String...str) {
        for(String a:str)
            System.out.print(a);
    }
-> 컴파일러는 어떤 메소드를 사용 해야하는지 구분을 못함 = 가능하면 가변인자를 사용한 메소드는 오버로딩을하지 않는 것이 바람직

https://sleepyeyes.tistory.com/29

Q3. 서로 다른 패키지 2개를 Import 했을때 클래스 명이 같으면?
A. 서로 다른 패키지에서 동일한 클래스명을 가진 클래스를 임포트하면, 하나만 직접 임포트하고 다른 하나는 전체 경로를 사용하여 호출해야함.

Q4. 상속 받은 자식 클래스에서 객체를 생성할 때 왜 부모 클래스로 선언하고 자식클래스로 객체를 생성하는 거지?
A. 이런 방식을 사용하는 이유는 여러 가지 이점이 있음:
1. 다형성을 통해 부모 클래스 타입으로 여러 자식 클래스 타입의 객체를 처리할 수 있음.
2. 코드의 유연성을 높여줌.
3. 확장성을 제공하여 나중에 다른 자식 클래스가 추가되더라도 기존 코드를 크게 수정하지 않고 처리가능함.
4. 부모 클래스를 통해 일관된 메서드 시그니처를 제공함.
5. 캡슐화를 통해 자식 클래스의 내부 구현을 은닉함.

Q5. 인터페이스는 다중상속이 가능한데 추상 클래스나 일반 클래스는 왜 다중 상속을 못하게 했지?
A. 인터페이스는 메서드의 구현을 포함하지 않아 다중 상속의 위험이 없음. 그러나 추상 클래스나 일반 클래스는 메서드의 구현을 포함하므로, 다중 상속 시 중복된 메서드 구현 등의 문제가 발생할 수 있음.

Q6. 디폴트 메서드가 인터페이스에 포함되면 구현이 표함된 메서드를 가지는데 다중 상속에 문제되지 않아?
A. Java에서는 디폴트 메서드와 관련된 충돌 문제를 해결하기 위해 특정 규칙을 제공함:

  • 동일한 디폴트 메서드가 여러 인터페이스에 있을 경우, 해당 클래스는 그 메서드를 명확하게 오버라이드해야함.
  • 디폴트 메서드와 추상 메서드가 충돌할 때는, 추상 메서드가 우선순위를 가짐. 따라서 해당 클래스는 해당 추상 메서드를 구현해야함.

Q1. 연결 예외에서 원인 예외를 등록하는 이유가 뭐야?

  1. 상세한 디버깅 정보 제공
    원인 예외를 포함시키면 문제의 근본 원인을 더욱 명확하게 파악할 수 있음. 상위 레벨의 예외만 보고 문제를 해결하려고 시도하는 것보다, 원인 예외와 함께 모든 관련 정보를 살펴보는 것이 문제의 원인을 더 빠르게 파악하고 수정하는 데 도움이 됨.

  2. 예외의 연쇄적인 흐름 파악
    한 예외가 다른 예외를 일으킬 수 있음. 이러한 연쇄적인 예외의 흐름을 통해 어플리케이션의 동작 중 어떤 부분에서 문제가 발생했는지와 그 이유를 더욱 정확하게 알 수 있음.

  3. 예외 처리의 일관성
    상위 레벨에서 일어난 예외를 포착하고 처리하는 동안, 실제 원인이 되는 예외 정보를 유지하면서 새로운 예외를 던질 수 있음. 이는 로깅이나 사용자에게 에러 메시지를 제공할 때 유용하게 활용될 수 있음.

    결론적으로, 원인 예외를 chained exception에 등록하는 것은 문제의 근본적인 원인을 정확하게 파악하고, 효과적으로 디버깅하며, 사용자나 시스템 관리자에게 유용한 정보를 제공하기 위한 것임.

Q2. 메서드를 스코프로 제네릭을 별도 선언 가능하다는 말의 의미는?

  • 클래스 레벨에서가 아닌 메서드 레벨에서만 사용되는 제네릭 타입 파라미터를 선언할 수 있다는 것을 의미함.

    예를 들어, 자바에서는 클래스 전체에 대한 제네릭 타입을 선언할 수 있지만, 특정 메서드에서만 사용될 제네릭 타입을 별도로 선언할 수도 있음.

public class SomeClass {
    // 클래스 레벨의 제네릭 타입 선언
    public <T> void someMethod(T input) {
        // ...
    }
}
  • 코드 해석
    위의 코드에서 는 메서드 someMethod에 대한 제네릭 타입 파라미터임. 이 T는 SomeClass 전체에 적용되는 것이 아닌 someMethod 내에서만 유효함.

    이렇게 메서드 레벨에서 제네릭 타입을 선언하면, 그 메서드 내부에서만 사용되는 타입 파라미터를 명시적으로 정의하고 사용할 수 있음. 이는 코드의 유연성과 재사용성을 높이는 데 도움을 줌.


Q1. 아래 코드에서 굳이 throws를 사용할 필요가 있어?
어차피 if 문으로 처리할꺼면 throws를 사용하는 이유가 뭐야?

private final boolean just = true;

    // throws : 던지다!! ( = 예외를 던지다 / 발생시키다.)

    public  void thisMethodIsDangerous() throws OurBadException{
        // custom logic

        if(just){
            throw new OurBadException();
        }
    }
    
    
    public OurBadException(){
        super("위험한 행동 예외 처리");
    }    

A. throws를 사용한 함수는 반드시 try-catch-finally 문을 사용해야되 -> 예외 처리 강제화

    public static void main(String[] args) {
        OurClass ourClass = new OurClass();
        try{
            ourClass.thisMethodIsDangerous();    
        }catch (OurBadException e){
            System.out.println(e.getMessage());
        }finally {
            
        }
    }

Q2. 디테일한 예외사항을 왜 사용해야되?
A.
1. 문제 발생시 신속하게 해결 가능 ( 원인 파악 빠름 )
2. 개발자들이 이해하기 쉬워 협업이 좋음
3. 안전한 시스템으로 리스크를 관리해줌

Q3. throws문을 사용해서 메서드가 위험하다고 예측하고 알려야 하는 이유가 뭐야?
A. 개발자 협업 + 안정성 ( 예외처리 강제 수행 ) + 예외 원인 파악이 쉬움

Q4. checked exception을 unchecked exception으로 포장하는 이유는?

A. 코드를 간결하게 해서 복잡도 낮추기 위해 + 예외처리 자율화

Q5. 아래 연결된 예외가 어떻게 여러개의 예외를 뭉쳐서 처리한 다는 거야?

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("이것이 진짜 예외 이유 입니다."));
    }
}

A. 예외를 뭉쳐서 한번에 처리한다=> 동일한 처리 로직이 필요한 여러 예외 유형을 함께 처리한다는 의미
이 코드 => Number... 과 Null... catch블록에서 둘다 처리한다는 의미

Q6. chained Exception이란?

A. 예외의 원인이 딴 예외일 때 이를 연결하는 기능
-> 연쇄 예외는 큰 문제와 그 원인을 연결해주기 위해 사용

Q7. 왜 예외를 다른 예외로 변환하여 던져? 그리고 그렇게 할 때 문제의 원인이 왜곡되거나 잘못 표현되지 않아?

public void someMethod() throws IOException { ... }

public void someResponsibleMethod() throws MoreSpecificException {
		try {
			this.someMethod();
		} catch (IOException e) {
			throw new MoreSpecificException(e.getMessage());
		}
}
  1. 예외를 추상화하여 협업에 도움을 주기 위해
  2. 기존 에러에 설명 추가하고 싶을때?

Q8 제네릭으로 클래스를 만드는 것이 가능한가?
A. 제네릭 클래스가 따로 있는 것이 아닌 클래스 뒤에 <>를 붙이면 제네릭 클래스가 된다.

public class Box<T> {
    private T item;
}

Q9 제네릭 배열 생성과 static이 왜 안되?
배열 = 고정된 타입 + 런타임에 타입 정보
제네릭 = 타입 정보가 컴파일 타임에만 존재 + 런타임에는 타입 정보가 제거됨 (= 타입 소거)
-> 타입 안전성을 보장할 수 없음
static 멤버는 클래스 수준 동작
=> 특정 인스턴스에 대한 제네릭 타입 정보를 알 수 없음

Q10 제네릭 E는 어떨때 사용
A.

T = type
E = Element
K = Key
N = Number
V = Value
R = Result
https://kang-james.tistory.com/entry/JAVA-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0-%EC%A0%9C%EB%84%A4%EB%A6%ADGeneric-%EC%99%84%EB%B2%BD-%EC%A0%95%EB%A6%AC

Q11 아래 코드가 어떻게 동작해?

A

static <T> void sort(List<T> list, Comparator<? super T> c) { ... }
<T>: 'T'는 아직 정해지지 않은 데이터 유형을 나타냄
  나중에 함수를 호출할 때 'T'가 무엇인지 알 수 있음
  예를 들어 숫자, 문자열, 어떤 객체든 가능

List<T> list: 'T' 유형의 아이템을 담은 리스트
  예를 들어, T가 숫자면 숫자 리스트, T가 문자열이면 문자열 리스트

Comparator <? super T> c
이는 'T' 유형의 아이템 두 개를 비교하여 어느 것이 먼저 오고, 어느 것이 나중에 올지를 결정하는 규칙

? super T: 이 부분은 T 또는 T의 부모 유형을 다룰 수 있는 비교 규칙을 사용가능
예를 들어, T가 특정 클래스의 서브클래스라면, 그 서브클래스 뿐만 아니라 부모 클래스를 비교하는 규칙도 사용 가능

<T>는 함수 호출되며 매개변수의 타입이 확정될때 해당 매개변수의 타입으로 바뀐다 
만약 타입들이 일치하지 않으면 컴파일 에러가 난다.

Q12. 아래 코드에서 Parser을 return 할 필요가 있나?
부모객체에 데이터 반영하는건데?

 public Parser parseFirstNum(String firstInput) throws Exception{
        if (!Pattern.matches(NUMBER_REG, firstInput)) {
            throw new BadInputException("정수값");
        }


        this.calculator.setFirstNumber(Integer.parseInt(firstInput));

        return this;
    }

A. 메소드 체이닝을 위해 객체 반환 사용
메소드 체이닝이란?
객체 지향 프로그래밍에서 객체의 메소드를 연속적으로 호출하는 방식

method1().method2().method3()

하지만 코드 보면 각 메소드 독립적 호출
연속적으로 호출하는 것이 아니라서 반환 필요 없음
그대로 코드 사용해도 될것으로 판단

        System.out.println("첫번째 숫자를 입력해주세요!");
        String firstInput = scanner.nextLine();
        parser.parseFirstNum(firstInput);

        System.out.println("연산자를 입력해주세요!");
        String operator = scanner.nextLine();
        parser.parseOperator(operator);

        System.out.println("두번째 숫자를 입력해주세요!");
        String secondInput = scanner.nextLine();
        parser.parseSecondNum(secondInput);

Q13 전역변수, 정적 변수, 배열 등은 Stack에 저장되?

  • 전역변수나 정적 변수는 Heap 영역이 아닌 Method Area(또는 Static 영역)에 저장되는데, 이는 프로그램의 시작부터 종료까지 그 값이 유지되어야 하기 때문임.
    지역 변수는 Stack에 저장되고, 객체는 Heap에 저장됨.
    배열은 참조 타입이기 때문에 Heap 영역에 저장되고, 그 참조 값은 Stack이나 Method Area에 따라 저장될 수 있음.

Q14 쓰레드 구현에 인터페이스 Runnable, 클래스 Thread, 람다 등을 사용하는데 각각의 장단점을 비교해줘

1. 인터페이스 Runnable 구현

  • 장점

    • 다중 상속의 문제를 피할 수 있음. 자바는 다중 상속을 지원하지 않으므로, 다른 클래스를 상속받는 동안에도 Runnable 인터페이스를 구현하여 스레드를 생성할 수 있음.
    • 유연성 증가: Runnable 인터페이스를 구현한 클래스는 다른 목적으로도 재사용이 가능함.
  • 단점

    • Runnable 인터페이스는 run() 메서드 외에 다른 메서드를 제공하지 않음. 따라서 스레드 관련 추가 기능을 원하면 별도로 구현해야 함.

2. 클래스 Thread 상속

  • 장점

    • Thread 클래스는 스레드 생성, 시작, 중단 등의 기능을 제공하는 메서드들이 이미 구현되어 있어 직관적이고 사용하기 쉬움.
    • Thread의 다른 메서드들을 오버라이드해서 확장하기 쉬움.
  • 단점

    • 자바는 다중 상속을 지원하지 않으므로, Thread 클래스를 상속받으면 다른 클래스를 상속받을 수 없게 됨.
    • Thread 클래스를 상속받는 것은 해당 클래스가 Thread로서 동작한다는 강한 연결성을 의미함. 이는 OOP의 원칙에 부합하지 않는 경우가 많음.

3. 람다 (Java 8 이후)

  • 장점

    • 간결한 문법: 짧고 명료한 코드로 스레드를 구현할 수 있음.
    • 익명 함수나 메서드 참조를 사용하여 코드의 중복을 줄일 수 있음.
  • 단점

    • 복잡한 로직이나 다양한 메서드 오버라이드가 필요한 경우에는 적합하지 않을 수 있음.
    • 람다는 주로 단순한 작업이나 임시 작업에 더 적합함.
    new Thread(() -> {
        // 작업 내용
    }).start();

    결론적으로, 프로젝트의 요구사항, 클래스의 설계 구조, 그리고 코드의 간결성과 유연성에 따라 적절한 방법을 선택하면 될 것 같음

Q15. 가비지 컬렉터가 뭐야?

  • 가비지 컬렉터는 자바의 메모리 관리를 담당하는 부분
    프로그램에서 더 이상 참조되지 않는 객체들을 자동으로 감지하고 메모리에서 해제하는 역할을 함
    이를 통해 메모리 누수를 방지하고, 프로그래머는 메모리 관리에 대한 부담 없이 코드를 작성할 수 있게 도와줌
    가비지 컬렉션은 자바의 JVM(Java Virtual Machine)에 내장되어 있어, 프로그래머가 직접 호출할 필요는 없지만, System.gc() 메서드를 통해 가비지 컬렉터를 요청가능 ( 성능에 큰 문제 호출 비권장 )
    https://mangkyu.tistory.com/118

Q1. chars는 정확히 어떤 역활을 하는거야?
chars() 메서드는 주어진 문자열 또는 문자 시퀀스(CharSequence)에 포함된 각 문자를 정수 값으로 표현하는 스트림을 제공한다.

CharSequence의 구현체인 String, StringBuilder, StringBuffer 등에서 chars() 메서드를 호출하면, 해당 문자열의 각 문자에 해당하는 int 값을 포함하는 IntStream이 반환된다.

int 값은 문자의 Unicode 코드 포인트다. 예를 들면, 'A' 문자의 Unicode 코드 포인트는 65이므로, "A" 문자열에 chars() 메서드를 호출하면 해당 스트림은 65라는 값을 포함한다.

예시

String s = "AB";
IntStream stream = s.chars();
stream.forEach(System.out::println);  // 출력: 65 (A의 코드 포인트) 그 다음 줄에 66 (B의 코드 포인트)

chars() 메서드를 사용하면, 문자열의 각 문자에 대한 연산을 스트림을 통해 간편하게 수행할 수 있다.

chars() 메서드는 StringBuffer, StringBuilder, String 등 CharSequence 인터페이스의 구현체들에 존재한다. 이 메서드를 호출하면 해당 객체가 가지고 있는 문자열의 각 문자에 대한 int 값의 시퀀스를 표현하는 IntStream을 반환


Q1. StringTokenizer의 동작 방식은 어떻게 되는가?

A. StringTokenizer 클래스를 이용하면 문자열을 토큰화할 수 있음. StringTokenizer st로 객체를 생성하면 주어진 문자열이 토큰화되어 처리되며, 해당 토큰들은 st 객체 내부에 저장됨. 이때 각 토큰은 직접 접근할 수 없기 때문에 nextToken() 메서드를 통해서만 접근 가능함.

사용 예시:
while(st.hasMoreTokens()) {
	tokens = st.nextToken();
}

Q2. StringBuilder의 동작 방식은?

A. StringBuilder 객체를 생성하면, 내부적으로 문자들을 저장하기 위한 char[] 배열이 함께 생성됨. 문자열에 변화를 주는 연산, 예를 들어 appenddelete를 사용하면, 내부 char[] 배열의 크기가 부족하면 새로운 배열을 생성하거나 기존 배열의 값을 조정해서 동작함.

Q3. Arrays는 원본 데이터에 변화를 주나? 반환값은 무엇인가?

A. Arrays 클래스의 메서드는 원본 데이터에 변화를 주기도 하고, 주지 않기도 함.

  • 변화를 주는 메서드:

    • sort(): 원본 배열을 정렬. 반환값은 void임.
    • fill(): 원본 배열의 요소들을 특정 값으로 채움. 반환값은 void임.
  • 변화를 주지 않는 메서드:

    • binarySearch(): 배열에서 특정 값의 인덱스를 찾음. 찾는 값이 없으면 음수를 반환.
    • equals(): 두 배열이 동일한지 확인. 반환값은 boolean임.
    • toString(): 배열을 문자열로 변환. 문자열 반환.
    • copyOf(), copyOfRange(): 배열의 일부 또는 전체를 복사하여 새 배열 생성. 복사된 새 배열을 반환.
    • asList(): 배열을 리스트로 변환. 변환된 리스트 반환.

Q4. StringTokenizer의 객체에 저장된 토큰화된 char[] 필드 값을 어떻게 가져올 수 있을까?

A. StringTokenizer 내부에는 토큰화된 결과를 직접 저장하는 char[] 필드가 없음. 따라서 while문을 사용하여 토큰을 하나씩 꺼내와야 함.

Q5. char[] arr 문자열 배열 생성 후 new로 따로 객체 생성 안하고 추가 가능한가?

A. char[] 배열은 한 번 크기가 설정되면 크기 변경이 불가능함. 즉, 배열에 새 요소를 추가하려면 새 배열을 만들거나 기존 배열을 복사해야 함.

Q6. new char[3]으로 생성 했다고 했을 때 인덱스 이상으로 확장 가능한가?

A. 아니요, 배열의 크기는 3으로 설정되며 인덱스 3 이상으로 확장 불가능. 이를 넘어서 접근하면 오류 발생함.

Q7. 아래 코드들의 차이는 무엇인가?

A. 두 방식 모두 ArrayList를 생성하는 것에는 차이가 없음. Java 7부터 다이아몬드 연산자를 사용할 수 있게 되어 코드가 간결해졌음.

Q8. ArrayList arr에서 arr의 각 요소를 어떻게 출력해야 되는가?

A.

for (String item : arr) {
    System.out.println(item);
}

Q9. ArrayList를 2차원 배열로 구현하기

A. 주어진 코드는 2차원 배열의 각 원소 값을 더해서 ArrayList의 2차원 형태로 저장하며, 그 결과를 2차원 배열로 반환함.

Q10. Java에서 제곱하는 함수는?

A. Math.pow(밑, 지수)를 사용하여 제곱 값을 구할 수 있음.

Q11. 삼항 연산자에서 피연산자의 타입이 다를 경우 어떻게 되는가?

A. 결과의 타입은 두 피연산자 중 더 큰 데이터 타입으로 결정됨. 예를 들어, double 값과 int 값을 사용할 경우, 결과는 double로 처리됨.

Q12. String에 char를 더해도 문제가 없을까?

A. 문제가 없음. char는 자동으로 String으로 변환되어 연결됨.

Q13. 문자를 대문자로 바꾸려면?

A. Character.toUpperCase(문자)를 사용하면 됨.

Q14. 문자열을 문자 단위로 문자열 배열로 만들려면?

A.

String s = "example";
String[] array = s.split("");

Q15. List.of()는 무엇인가?

A. List.of()는 Java 9부터 사용할 수 있는 메서드로,

변경 불가능한 리스트를 만드는 데 사용됨.

profile
backend_Devloper

0개의 댓글