스프링을 들어가기 전 마지막으로 자바 시험을 보았습니다.
괜찮다 싶은 문제들이 몇 개 있어 기록용으로 남겨두려합니다.
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으로 초기화하고 열의 크기는 지정하지 않은 배열 선언 방식입니다.
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
입니다.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
)public class CallTest{
static String str;
public static void main(String[] args){
System.out.println(str+'A'+'B'+true);
}
}
str
은 static
변수라서, 클래스가 로딩될 때 메모리가 초기화됩니다.
명시적 초기화 및 static 초기화 블럭이 없기에, 참조형의 기본 값인 null
로 초기화됩니다.
이러한 String 타입의 str에 'A' 를 더해주면, 비록 값이 null일지라도 타입 자체는 유지되기에, String 타입의 + 연산자가 호출됩니다.
String타입의 null에 char을 더하면, "null" 이라는 문자열과 "A"를 더한 "nullA"가 됩니다.
문자 'B'와의 합 또한 마찬가지로 적용되어, "nullAB" 가 됩니다. 이 또한 String 타입입니다.
String타입에 boolean 타입의 true
를 더해주면 해당 문자열에 "true" 라는 문자열이 붙습니다. 결과적으로 값이 "nullABtrue"
가 됩니다.
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
또한 마찬가지입니다 )
1. String 멤버변수는 "" 로 자동 초기화된다.
2. 지역변수는 반드시 초기화해야 한다.
3. 생성자보다 초기화 블럭이 먼저 수행된다.
4. 인스턴스변수보다 클래스변수가 먼저 초기화된다.
1번의 경우, String 타입의 멤버 변수는 명시적 초기화 / 초기화 블럭 / 생성자가 없을 시, null
로 초기화됩니다.
지역변수는 메서드 내에 선언한 뒤, 반드시 명시적으로 초기화해주어야 합니다.
3번이 중요한 내용이었는데요. 인스턴스 생성 시 명시적 초기화 / 초기화 블럭 / 생성자의 호출 순서가 중요합니다. 이를 이해함으로써 스프링의 필드 주입, 생성자 주입이 이해하는 데 도움이 된다 생각합니다. 인스턴스 초기화 시, 명시적 초기화 -> 초기화 블럭 -> 생성자 순서로 호출됩니다.
클래스 변수가 인스턴스 변수보다 먼저 초기화됩니다.
인스턴스 생성 이전 해당 클래스를 static 영역 ( JDK 8 이후 PermGen 영역 )으로 클래스 로더가 올려놓기 때문입니다.
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
순으로 출력되게 됩니다.
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영역에서 기본 값으로 초기화되는 것이 당연하다는 생각이 들었습니다.