final 키워드
Java의 final 키워드는 어디에 적용하느냐에 따라 그 의미가 다릅니다. 변수에 적용되었을 때, 아니면 클래스나 메서드에 적용되었을 때, 그 의미가 각각 다릅니다.
-
변수: final이 적용된 변수의 값은 초기화된 이후에는 변경할 수 없습니다.
-
객체: final로 선언된 객체는 참조를 변경할 수 없습니다. 객체의 참조는 변경할 수 없는 immutable 대상이 되지만, 객체를 구성하는 멤버들은 값이 변경될 수 있습니다.
-
메서드: final로 선언된 메서드는 하위 클래스에서 오버라이드 할 수 없습니다.
-
클래스: final로 선언된 클래스는 계층 대상이 될 수 없습니다(하위 클래스를 만들지 못한다).
finally 키워드
finally 키워드는 try/catch 블록과 함께 사용되며 예외가 던져지더라도 항상 실행될 코드를 지정하기 위해 사용됩니다. finally 블록은 try와 catch 블록이 전부 실행된 후, 그리고 제어 흐름이 원래 지점으로 돌아가기 전에 실행됩니다.
public static String lem() {
System.out.println("lem");
return "return from lem";
}
public static String foo() {
int x = 0;
int y = 5;
try {
System.out.println("start try");
int b = y / x;
System.out.println("end try");
return "returned from try";
}
catch (Exception ex) {
System.out.println("catch");
return lem() + " | returned from catch";
}
finally {
System.out.println("finally");
}
}
public static void bar() {
System.out.println("start bar");
String v = foo();
System.out.println(v);
System.out.println("end bar");
}
public static void main(String[] args) {
bar();
}
이 코드를 실행하면 다음과 같은 문장들이 화면에 출력됩니다.
1 start bar
2 start try
3 catch
4 lem
5 finally
6 return from lem | returned from catch
7 end bar
세 번째 줄부터 다섯 번째 줄까지를 주의 깊게 봐야 합니다. return 문을 포함해서 catch 블록의 모든 부분이 완전히 실행되고 난 다음에 finally 블록이 실행되고, 그런 다음에 함수가 return 됨을 확인할 수 있습니다.
finalize 메서드
Java의 자동화된 가비지 컬렉터 garbage collector 는 객체를 삭제하기 전에 finalize() 메서드를 호출 합니다. 따라서 객체가 삭제되기 직전에 실행되어야 하는 동작이 있다면 Object 클래스에 정의된 finalize() 메서드를 오버라이드 하여 정의할 수 있습니다.
protected void finalize() throws Throwable {
// 열려 있는 파일을 닫거나, 자원을 반환하거나, ...
}
오버로딩 vs. 오버라이딩
오버로딩은 두 메서드가 같은 이름을 갖고 있으나 인자의 수나 자료형이 다른 경우를 지칭합니다.
public double computeArea(Circle c) { ... }
public double computeArea(Square s) { ... }
오버라이딩은 상위 클래스의 메서드와 같은 이름과 시그니처 signature 를 갖는 함수를 하위 클래스에 재정의하는 것입니다.
public abstract class Shape {
public void printMe() {
System.out.println("I am a Shape.");
}
public abstract double computeArea();
}
public class Circle extends Shape {
private double rad = 5;
public void printMe() {
System.out.println("I am a circle.");
}
public double computeArea() {
return rad * rad * 3.15;
}
}
public class Ambiguous extends Shape {
private double area = 10;
public double computeArea() {
return area;
}
}
public class IntroductionOverriding {
public static void main(String[] args) {
Shape[] shapes = new Shape[2];
Circle circle = new Circle();
Ambiguous ambiguous = new Ambiguous();
shapes[0] = circle;
shapes[1] = ambiguous;
for (Shape s : shapes) {
s.printMe();
System.out.println(s.computeArea());
}
}
}
위 코드를 실행한 출력 결과는 다음과 같습니다.
1 I am a circle.
2 78.75
3 I am a shape.
4 10.0
Ambiguous는 printMe()를 그대로 둔 반면, Circle은 재정의하고 있음을 유의해서 보기 바랍니다.
컬렉션 프레임워크 Collection Framework
Java의 컬렉션 프레임워크는 아주 유용합니다. 그 중 가장 유용한 몇 가지를 들어보면 다음과 같습니다.
ArrayList : ArrayList는 동적으로 크기가 조정되는 배열입니다. 새 원소를 삽입하면 크기가 늘어난다.
ArrayList<String> myArr = new ArrayList<String>();
myArr.add("one");
myArr.add("two");
System.out.println(myArr.get(0)); // one 출력
Vector : Vector는 ArrayList와 비슷하지만 다중 쓰레드 안전하도록 동기화 synchronize 된다는 차이가 있습니다. 문법은 거의 동일합니다.
Vector<String> myVect = new Vector<String>();
myVect.add("one");
myVect.add("two");
System.out.println(myVect.get(0));
LinkedList : 이 클래스에 관해 묻는 경우는 별로 없지만, 순환자 iterator 를 어떻게 사용해야 하는지를 잘 보여주므로 알아두면 좋습니다.
LinkedList<String> myLinkedList = new LinkedList<String>();
myLinkedList.add("two");
myLinkedList.addFirst("one");
Iterator<String> iter = myLinkedList.iterator();
while (iter.hasNext()) {
System.out.println(iter.next());
}
HashMap : HashMap 컬렉션은 면접이나 실제 상황 가릴 것 없이 광범위하게 사용됩니다. 사용 문법을 아래에 제시했습니다.
HashMap<String, String> map = new HashMap<String, String>();
map.put("one", "uno");
map.put("two", "dos");
System.out.println(map.get("one"));
연습문제
-
생성자를 private으로 선언하면 계승 관점에서 어떤 영향을 주게 되나요?
-
Java의 객체 리플랙션 reflection 에 대해 설명하고, 유용한 이야기를 밝혀라.