Java java.lang - 놓치기 쉬운 개념들

이강현·2025년 4월 11일

놓치기 쉬운 개념들

목록 보기
10/19

hashCode()

Object 클래스의 hashCode()반환 타입int 입니다.

32bit JVM 에서 64bit JVM 으로 넘어가면서 long 타입의 hash code 를 사용하는 것이 맞지만 기존의 int 타입을 유지하는 것을 선택했습니다.

그 이유는 Java 가 backward compativility 를 중요하게 생각하기 때문입니다.

결과적으로 hash collision 이 발생할 확률이 증가했습니다.

hashCode() 를 사용할 때에는 overriding 하여 사용하는 것을 권장합니다.



Covariant Return Type

깊은 복사가 필요할 때 Cloneable 을 사용하면 됩니다.

Cloneable 인터페이스의 clone()Object반환함으로써 모든 사용자 정의 클래스가 사용할 수 있습니다.

Cloneable 을 구현하면 clone()결과down-casting 하여 사용하면 됩니다.

그러나 객체를 복사한다는 의미에서 다른 타입의 객체를 반환할 일이 없습니다.

  • 편의를 위해 Covariant Return Type 을 지원합니다.

  • Overriding 할 때 조상 메서드의 반환타입을 그 자손 타입으로 재정의 할 수 있습니다.

  • Cloneable 에 국한되지 않고 모든 overriding 상황에서 사용할 수 있습니다.



native

Java 의 소스코드를 보다 보면 native 가 붙은 빈 메서드를 찾을 수 있습니다.
이는 시스템 수준의 작업이나 하드웨어 작업을 위해 C/C++ 과 같은 native language 로 작성된 메서드입니다.
native 메서드는 호출을 위해 시그니처를 선언해 둔 것일 뿐 overriding 할 필요가 없습니다.



String Pool

Stringimmutable object(불변 객체) 입니다.
따라서 변경이 불가능하고, 변경이 필요할 때 마다 COW(copy on write)합니다.

  • Java 는 성능 향상을 위해 String Pool 을 사용합니다.
  • 문자열 literal 들을 컴파일 시점에 최적화 하고 String pool 에 저장하고 그 문자열이 사용될 때마다 String Pool 에서 제공합니다.
  • 런타임에 동적으로 생성되는 문자열은 String Pool 에 저장되지 않습니다.
  • 비슷하게 Integer, Long, Short, Byte, Character, Booleancache 를 가지고 있습니다.
	String a = "a";
    String b = "b";
    String aplusb = "a"+"b";
    String ab = "ab";
    if (aplusb == a + b) {
        System.out.println("ab == a + b");
    } else {
        System.out.println("ab != a + b"); // result
    }
    if (ab == aplusb) {
        System.out.println("ab == aplusb"); //result
    } else {
        System.out.println("ab != aplusb");
    }
	String target = "0";

	while (true) {
        String s = String.valueOf((int) (Math.random()*10));
        System.out.println(target + " == " + s);
        if (s == target) { // never be true
            System.out.println("Found!");
            break;
        }
    }



Class Object

  • Java 의 ClassLoaderclass loading 시점에 .class 파일을 읽어서 Class 객체를 생성하고 저장해둡니다.
  • 이 Class 객체에는 설계도처럼 클래스에 대한 정보가 모두 담겨있습니다.

Class 객체를 얻는 방식은 다음과 같습니다.

	Class c = new MyClass.getClass();
	Class c = MyClass.class;
	Class c = Class.forName("MyClass");
  • forName() 을 사용하면 런타임동적으로 클래스 정보를 얻어내는 것이 가능합니다.
  • Reflection API 와 함께 동적으로 여러가지 일 들을 할 수 있습니다.



StringBuffer | StringBuilder equals

StringBuffer 혹은 StringBuilder 는 잦은 변경이 필요한 문자열을 다룰때 사용됩니다.

  • String 과는 다르게 equals() 메서드가 오버라이딩 되어있지 않습니다.
  • StringBuffer 나 StringBuilder 로 작업했던 문자열은 등가 비교를 할 때 반드시 String 으로 변환해서 비교해야 합니다.



StrictMath

Math 클래스의 유틸리티 메서드 들은 최적화를 위해 하드웨어OS 라이브러리를 사용합니다.
따라서 환경에 따라 다른 결과를 얻을 수 있습니다.

  • 플랫폼 독립적인 계산 결과가 필요하다면 StrictMath 클래스를 사용합니다.



BigDecimal

Number 추상 클래스를 상속받은 클래스들 중에 Wrapper 클래스 외에도 BigInteger, BigDecimal 클래스가 있습니다.
배열을 활용해 매우 큰 수를 다루므로 효율적입니다.

  • BigDecimal 을 생성할 때 literal 실수를 사용하면 오차가 생길 수 있습니다.
  • 생성자의 인자는 반드시 문자열을 사용합니다.
BigDecimal fromLiteral = new BigDecimal(0.1);
BigDecimal fromString = new BigDecimal("0.1"); // accurate



Objects

유틸리티 클래스 Objects 에는 유용한 메서드들이 있습니다.

  • equals()
    • Object.equals() 와 달리 null 체크를 해줍니다.
if (a != null && a.equals(b)) {
    /*...*/
}
// can be simplified like this
if (Objets.equals(a, b)) { // no need to check a or b is null
    /*...*/    
}
  • deepEquals()
    • 다차원 배열 등가 비교에 사용합니다.
  • requireNonNull()
    • 유효성 검사를 간단하게 할 수 있습니다.
void setName(String name) {
    if (name == null) {
        throw new NullPointerException("name must not be null");
    }
    this.name = name;
}
// can be simplified like this
void setName(String name) {
    this.name = Objects.requireNonNull(name, "name must not be null");
}
  • compare()
    • Object 에는 없는 대소 비교 기능을 제공합니다.
profile
백엔드 개발자 지망생입니다.

0개의 댓글