[ KOSTA 교육 34일차 - Java 시험 중 괜찮았던 문제들 ]

junjun·2024년 6월 12일
0

KOSTA

목록 보기
31/48

스프링을 들어가기 전 마지막으로 자바 시험을 보았습니다.

괜찮다 싶은 문제들이 몇 개 있어 기록용으로 남겨두려합니다.

[1] 다음 중 배열 선언 및 초기화가 잘못된 것은?
1. int[] arr[];
2. int[] arr = {1,2,3,};
3. int[] arr = new int[5];
4. int[] arr = new int[5]{1,2,3,4,5};
5. int arr[5];
6. int[] arr[] = new int[3][];
  • 1번의 경우, 일반적인 배열 선언 방식이 아닙니다.
    만약 제 팀원이 이런식으로 배열을 짰다면... 흠... 싶겠지만, 문법적으로 문제가 없습니다.
    2차원 배열을 선언하는 방법 중 하나입니다.

  • 2번의 경우 또한 일반적인 배열 선언 방식이 아닙니다.
    초기화 부분에 , 이 있는데, 어떻게 잘못되지 않은거냐! 싶을 수 있지만,
    자바 컴파일 시, javac이 해당 부분을 보고 컴파일 시 지워줍니다.

  • 3번의 경우는 올바른 배열 선언 방식입니다.
    크기가 5이고, 각 요소가 int의 디폴트값인 0으로 초기화된 배열이 생성됩니다.

  • 4번의 경우 잘못된 배열 선언 및 초기화입니다.
    int[] arr = new int[]{1,2,3,4,5}; 가 올바른 표현입니다.

  • 5번의 경우 잘못된 배열 선언입니다. 크기를 지정하여 선언할 수 없습니다. int[] arr 으로 선언하면 됩니다.

  • 6번의 경우 2차원 배열 선언 방법 중 하나입니다. 이해하기 쉽게 행렬로 설명하자면, 행의 크기를 3으로 초기화하고 열의 크기는 지정하지 않은 배열 선언 방식입니다.

[2] 다음 코드의 실행 결과는?

public class test {
	public static void main(String[] args) {
		int i = 0;
		for( ; i < 4; i += 2){
			System.out.print(i + " ");
		}
		System.out.println(i);
	}
	
}
  • 답은 0 2 4 입니다.
  • for 문에서 사용하는 변수에서는 보통 지역변수 i를 새로 선언해줍니다.
  • 지역변수를 새로 생성하지 않으면, 기존에 생성된 지역변수를 사용할 수 있습니다.
  • 또한, 그렇게 for 문에서 사용된 지역변수는 for문이 종료된 뒤에도 값의 효력을 가집니다.
    ( 아직 메서드 자체가 호출 스택에 저장되어 있기 떄문입니다. )
[4] 다음 코드의 실행 결과는?
public class test {
	public static void main(String[] args) {
		System.out.println(true+null);
	}
}
  • 정답은 컴파일 에러입니다.
  • 더하기 연산 (+)은
    boolean 기본 자료형과 null 사이에는 정의되지 않기 때문입니다.
    ( The operator + is undefined for the argument type(s) boolean, null )
[5] 다음 코드의 실행 결과는?
public class CallTest{
	static String str;
    public static void main(String[] args){
    	System.out.println(str+'A'+'B'+true);
    }
}
  • strstatic 변수라서, 클래스가 로딩될 때 메모리가 초기화됩니다.

  • 명시적 초기화 및 static 초기화 블럭이 없기에, 참조형의 기본 값인 null로 초기화됩니다.

  • 이러한 String 타입의 str에 'A' 를 더해주면, 비록 값이 null일지라도 타입 자체는 유지되기에, String 타입의 + 연산자가 호출됩니다.

  • String타입의 null에 char을 더하면, "null" 이라는 문자열과 "A"를 더한 "nullA"가 됩니다.

  • 문자 'B'와의 합 또한 마찬가지로 적용되어, "nullAB" 가 됩니다. 이 또한 String 타입입니다.

  • String타입에 boolean 타입의 true 를 더해주면 해당 문자열에 "true" 라는 문자열이 붙습니다. 결과적으로 값이 "nullABtrue" 가 됩니다.

[7] 다음 인터페이스 선언에서 오류가 발생하는 라인은?
interface IfA {
	public int MEM1 = 20;
    public int var2;
    public void display();
    public int value(){return var2;}
}
  • 인터페이스의 모든 멤버 변수에는 묵시적으로 public static final 의 제어자가 붙습니다.

  • 인터페이스의 멤버 메서드에는 public abstract 의 제어자가 묵시적으로 붙습니다.

  • MEM1 과 같은 경우는, 인터페이스 상수로 final 제어자에 따라 초기화되어야 하고, 초기화가 잘 수행되었습니다.

  • var2는 인터페이스 상수이지만 초기화가 되지 않았기에 잘못되었습니다. 오류가 발생합니다.

  • display()는 추상 메서드로 선언이 잘 되었습니다. 구현체가 없고, 이를 상속받아 구현할 자식클래스를 위해 잘 정의된 것입니다.

  • value()는 추상 메서드여야하지만, 구현 부분이 존재합니다. 오류가 발생합니다.
    cf. JDK 8버전 이후에 인터페이스의 메서드에 default 제어자를 붙일 수 있게 되었습니다. ( static 또한 마찬가지입니다 )

[8] 다음 중 초기화에 대한 설명으로 옳지 않은 것은?
1. String 멤버변수는 "" 로 자동 초기화된다.
2. 지역변수는 반드시 초기화해야 한다.
3. 생성자보다 초기화 블럭이 먼저 수행된다.
4. 인스턴스변수보다 클래스변수가 먼저 초기화된다.
  • 1번의 경우, String 타입의 멤버 변수는 명시적 초기화 / 초기화 블럭 / 생성자가 없을 시, null로 초기화됩니다.

  • 지역변수는 메서드 내에 선언한 뒤, 반드시 명시적으로 초기화해주어야 합니다.

  • 3번이 중요한 내용이었는데요. 인스턴스 생성 시 명시적 초기화 / 초기화 블럭 / 생성자의 호출 순서가 중요합니다. 이를 이해함으로써 스프링의 필드 주입, 생성자 주입이 이해하는 데 도움이 된다 생각합니다. 인스턴스 초기화 시, 명시적 초기화 -> 초기화 블럭 -> 생성자 순서로 호출됩니다.

  • 클래스 변수가 인스턴스 변수보다 먼저 초기화됩니다.
    인스턴스 생성 이전 해당 클래스를 static 영역 ( JDK 8 이후 PermGen 영역 )으로 클래스 로더가 올려놓기 때문입니다.

[15] 다음 코드의 실행 결과는?

package com.kosta.lec;

public class ExceptionTest{
	static void test() throws RuntimeException{
    	try {
        		System.out.println("test");
                throw new RuntimeException();
        } catch ( Exception ex ){
        	System.out.println("exception");
        }
   }
   
   public static void main(String[] args){
   		try {
        	test();
        } catch (RuntimeException ex){
        	System.out.println("runtime");
        }
        System.out.println("end");
   }
}
  • test() 가 실행될 때, untimeException 날립니다. 이는, Exception의 자식 예외이므로, 메서드 내의 catch 문에서 핸들링 됩니다. ( "exception" 이 출력됩니다. )

  • 물론, test() 메서드는 명시적으로 RuntimeException을 날리기에 외부에서 호출 시, catch 처리를 해주어야합니다만, 이미 test() 클래스 내부에서 핸들링을 해놨기에 main 문에서의 catch는 실행되지 않습니다.

test
exception
end

순으로 출력되게 됩니다.

[16] 다음 중 아래의 add 메서드의 오버라이딩이 잘못된 것은?
void add(int a, int b) throws InvalidException, NotNumberException {}

class NumException extends Exception {}
class InvalidaException extends NumException {}
class NotNumException extends NumException {}


1. void add(int a, int b) throws InvalidException, NotNumException {}
2. void add(int a, int b) throws InvalidException {}
3. void add(int a, int b) throws NumException {}
4. void add(int a, int b) throws Exception {}
  • 예외처리와 상속에 대해 물어본 문제였습니다.

  • 부모 클래스의 메서드를 오버라이딩 하는 경우, 접근제어자는 같거나 더 넓은 범위로 ( protected로 상속받은 경우 protected 또는 public으로 변환만 가능 ) , 던지는 예외는 같거나 더 좁은 범위로만으로 설정할 수 있습니다.

  • 이 때, 주의해야할 것이 있습니다. 던지는 예외의 범위는 던지는 예외의 개수가 아니라, 포함할 수 있는 예외의 종류 범위를 의미합니다.

  • 즉, 3번의 경우, NumException 하나만을 던지지만, 이는 InvalidaException 과 NotNumException의 부모 예외이기에, 실상 더 넓은 범위의 예외를 던진 것이 됩니다.

  • 따라서 답은 3번과 4번입니다.

package com.kosta.test;

@Getter
public class UserVO{
	private int seq;
	private String userId;
    private String email;
}

package com.kosta.test;

public class CallTest{
	public static void main(String[] args){
    	UserVO uvo = new UserVO();
        System.out.println(uvo.getUserId() + " " + uvo.getEmail() + " " + uvo.getSeq());
    }
}
  • 처음에는 NPE 가 나오는 문제가 아닌가 생각했습니다.

  • 정답은 null null 0 입니다.

  • 멤버변수의 경우, 그 타입이 참조형이던, 기본형이던

    명시적 초기화 / 초기화 블럭 / 생성자를 통해 초기화가 되지 않으면, 해당 타입의 기본 값으로 초기화 됩니다.

  • 이로 인해 ORM을 사용할 때, NPE 문제도 생길 수 있다 하여, 이를 명시적 초기화로 일부러 String의 경우 ""로 명시적 초기화를 할 수도 있다 합니다.

  • 처음 생각했을 때는 막연히 신기했지만, 이렇게 글을 정리하면서 느끼는 것은 class는 곧 사용자 정의 참조형이니 생성자를 통해 초기화될 때, Heap영역에서 기본 값으로 초기화되는 것이 당연하다는 생각이 들었습니다.

0개의 댓글