포스트

(Day 29) 인스턴스, 오토박싱, 언박싱, 예외처리

23/12/22

복습

인스턴스를 복사하는 방법

  1. Object 의 clone() 메서드를 사용하여 인스턴스 복제 가능.
  2. clone() 메서드를 사용하려면 Cloneable 인터페이스를 구현한다고 JVM에 알려줘야 함.

COPY: Shallow VS Deep

인스턴스를 복제할 때, 깊은 복사와 얕은 복사는 의존 객체는 복제하지 않음. 인스턴스의 레퍼런스 변수가 가리키고 있는 다른 인스턴스가 있다면, shallow copy한 경우 그 인스턴스를 똑같이 가리키고 있음. 의존객체는 똑같이 하나라는 것. deep copy한 경우는 의존 객체들도 복제하여 별개의 인스턴스들을 가짐.

String 클래스의 인스턴스 생성 방법 2가지

  1. 리터럴을 대입연산자를 이용해서 만드는 경우
  2. new 연산자를 사용하여 생성자로 리터러를 주는 경우
1
2
String s1 = "Hello"; //상수풀에 문자열별로 하나만 생성
String s2 = new String("Hello"); //힙 영역에 인스턴스 생성

equals() 메서드

equlas()는 최상위 클래스 Object에서는 ==와 같이 정의됨. 나머지 클래스들은 이 Object 클래스의 equals() 메서드를 상속받아서 그대로 쓰거나, 오버라이딩(덮어쓰기) 함. String 클래스에서는 덮어써서 재정의함. 인스턴스 주소 비교가 아니라 내용물 비교로.

Immutable, Mutable

내용이 변경될 수 있는 인스턴스와 그렇지 않은 인스턴스. String vs StringBuffer

StringBuffer VS StringBuilder

한 문장으로 요약하면, Buffer는 Thread-Safe 하지만 Builder는 아니다. 스레드 세이프를 구현하려면 스레드락을 걸고 풀고 하는 부분이 자원을 소모하기 때문에, 스레드 세이프한 것이 무조건 좋은 것은 아니다. 한 스레드만 사용하는 것이 보장된다면 StringBuilder를 쓰는 것이 좋다. 이후 스레드 세이프에 대해 이야기를 한다면, 임계영역 용어를 사용하여 설명하도록 하자.

Thread-Safe

여러 스레드가 사용하려고 해도 문제가 없는 것을 말한다. Thread-Safe에 논하려면 우선 중요한 키워드들이 있다. 여길 참고했다.

  1. 공유자원
  2. 임계구역
  3. 생산자-소비자 문제
  4. 임계구역

학습

인스턴스의 부가정보

각각의 인스턴스는 클래스 정보를 가지고 있다. 이것은 링크를 갖고 있다고도 말할 수 있다. 이것이 의미하는 것이 무엇인가? 바디가 아예 비어있는, 그냥 껍데기뿐인 클래스라고 할지라도, 그것을 인스턴스화(instantiate) 하면 크기를 갖는 인스턴스가 생긴다는 말이다. 크기가 0인 인스턴스는 없다.

래퍼클래스가 없다면 불편해지는 일 원시값들을 객체로 만들 수가 없는 것이 불편하다.

오토박싱 / 오토언박싱

Auto-boxing / Auto-unboxing int 값을 가지고 Integer 객체를 생성하려면, Integer obj = Integer.valueOf(100); Integer 객체로 int 값을 뽑아내려면, int value = obj.intValue();

원래는! 위와 같이 해야 하는게 객체를 다루는 통일적인 방법이다. 근데 이런 사용이 너무 많다보니까, Integer obj = 100; 이라는 문장이 있으면 컴파일 할 때 자동으로 Integer obj = Inteber.valueOf(100);으로 자동으로 바꿔준다. 이것이 Auto-boxing 이다. Auto-boxing은 컴파일러가 해 주는 것이다. 컴파일러가 컴파일 과정에서 변환한 것이지, JVM이 처리하는 것이 아니다. 박싱? 리터럴 값을 인스턴스로 만들기 위해 박스에 포장해주는 것처럼 보이니까.

언박싱은 뭔가? 객체를 원시값을 위한 타입의 변수에 그냥 넣을 수 없다. 근데 그걸 그냥 코드를 자동으로 바꿔준다. 박스(인스턴스)를 자동으로 언박싱해주는 것이다. int i = obj; 라고 하는 건 원래는 말이 안되는데 그걸 컴파일러가 자동으로 바꿔준다. JVM은 컴파일러가 변환한 바이트코드를 그대로 수행한다.

이런 오토박싱, 오토 언박싱은 아규먼트로 주고, 파라미터로 받을 때도 적용된다.

  • String str = “Hello”; 이 문장도 마찬가지다. 근데 auto-boxing은 통일적인 방법과 그 결과가 완전히 동일 하지 않다. 메모리 영역을 상수영역을 쓰기 때문이다.

세부적인 내용: 정수 캐싱

충분히 작은 정수(-128~127)는 상수 영역에 캐시로 두고 사용한다. 그래서 그 값을 오토박싱하는 경우, 인스턴스의 주소는 모두 같다. 반대로 상수 영역에 캐시되는 작은 수가 아닌 경우에, 오토박싱한 인스턴스들은 값이 같아도 인스턴스의 주소가 모두 다르다.

equals() 오버라이딩

래퍼클래스의 equlas()는 오버라이딩 되어 있다. 오브젝트 클래와 같이 인스턴스 주소를 비교하는 것이 아니라, 그 값을 비교하도록 재정의가 되어 있다.

예외 처리

프로그램에 에러가 발생한다고 하는데.. 대부분은 예외가 발생한 것이다.

예외가 발생한다면? 어떤 코드에서 예외가 발생하면, Exception (예외 객체)를 받아줄 호출자를 찾는다. 뭔가가 고장나거나 진행불가능한 문제가 발생한 것이 아니다.

이를 위해 본인을 호출한 메서드를 호출하고 거기도 받아줄(catch) 수 없다면 자신을 호출한 메서드로 올라간다. 계속 타고 올라가고 올라가면, 결국 JVM 으로 그 예외가 도달하게 된다. 그러면 JVM은 프로그램 실행을 멈춰버린다. 그리고 콘솔에 예외 발생한 걸 섬뜩하게(?) 늘어놓는다.

이건 마치 보고 체계같이 생겼다. 윗선의 귀에 문제가 들어가면 일이 커지는 (?) 그런 상황이다 ㅋㅋㅋ…

예외를 보고받았을 때 처리하지 않으면 상위 호출자에게 예외를 보고한다.

예외 처리의 위치는 개발자가 직접 결정한다. 상위 메서드의 상위 메서드의 상위 메서드의 … 상위 메서드에서 예외를 처리한다면, 예외가 발생했던 메서드를 다시 호출하려는 경우 다시 해야 한다.

try {

} catch (Exception e) {

}

모든 클래스의 조상은 Object

함수형 프로그래밍이 가장 좋은(?) 것인가? 아니. 함수형 프로그래밍은 OOP가 소 잡는 칼처럼 너무 클 때 사용되는 것이다.

트라이캐치 다 붙여야돼?

컴포지트 패턴 적용한 메뉴에서 핸들러 사용해서 작업할 때 예외 처리를 하려고 한다. 모든 핸들러마다 다 예외처리 코드를 작성해줘야 하나? 그러면! 코드도 중복되고!

this를 썼다. this 아주 아름답다 this를 썼기에 호출하고 호출하고 호출하고 호출하고 호출하고 …. 그러면 이 호출의 계층에서 예외처리의 계층을 만들 수가 있다. 예외처리의 설계들은 패턴하고 같이 봐야 한다. 그리고, 패턴을 모르면 적절한 예외처리의 설계를 하기가 힘들어진다!

이런 거 할 일이 얼마나 많을까~

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.