티스토리 뷰

JVM/JAVA

Java Object내부를 살펴보자! 1편

글을 쓰는 개발자 2021. 12. 3. 09:45
반응형

1. Object 생성자

@IntrinsicCandidate
public Object() {}
/**
Returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class.
The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment:
Number n = 0;  Class<? extends Number> c = n.getClass(); 
Returns:
The Class object that represents the runtime class of this object.
*/

객체를 만들 때 결국 여기까지 도달하게 되는 생성자이다.

기본적으로 다른 객체를 만들 때 다음과 같이 super()를 적어줘야 한다. 하지만 적지 않아도 일반적으로 사용했을 때 에러가 안나는 이유는 자동으로 추가해주기 때문이다.

public class Simple{
	
    public Simple() {
    	// super(); // 쓰지 않아도 컴파일러가 자동으로 추가해준다.
        // super() -> Object의 생성자로 이동
    }

}

위와 같이 기본 생성자 안에 매개변수가 있을 경우에는 다른 클래스가 해당 클래스를 상속 받을 경우 IDE에서 말하는 것과 같이 기본 생성자로는 사용을 못하므로 추천을 해주고 있다.

 

여기서 

@IntrinsicCandidate가 뭐지? 알아보자!

 

2. @IntrinsicCandidate

@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface IntrinsicCandidate {
}

우선 이렇게 생겼다.

The @IntrinsicCandidate annotation is specific to the HotSpot Virtual Machine. It indicates that an annotated method may be (but is not guaranteed to be) intrinsified by the HotSpot VM.

 

The @IntrinsicCandidate annotation is internal to the Java libraries and is therefore not supposed to have any relevance for application code. (쉽게 말하면 우리가 쓸 일은 없다.)


When modifying a method annotated with @IntrinsicCandidate, the corresponding intrinsic code in the HotSpot VM implementation must be updated to match the semantics of the annotated method.

(해당 어노테이션을 수정할 때 HotSpot VM도 또한 같이 업데이트를 해줘야 한다.)
This is because individual Java bytecodes implicitly check for exceptions like NullPointerException and ArrayStoreException. ( 이 어노테이션을 통해 Null체크 와 배열 저장  관련 검사를 한다.)


null checks on references range checks on primitive values used as array indexes
other validity checks on primitive values (e.g., for divide-by-zero conditions)
store checks on reference values stored into arrays array length checks on arrays indexed from within the intrinsic
reference casts (when formal parameters are Object or some other weak type) Note that the receiver value (this) is passed as a extra argument to all non-static methods. 

(나누기 0을 하는지, 배열에 저장된 참조 값에 대한 저장소 검사, 캐스팅에 대한 검사를 진행한다.)

 

    /**
     * Initializes a newly created {@code String} object so that it represents
     * the same sequence of characters as the argument; in other words, the
     * newly created string is a copy of the argument string. Unless an
     * explicit copy of {@code original} is needed, use of this constructor is
     * unnecessary since Strings are immutable.
     *
     * @param  original
     *         A {@code String}
     */
    @IntrinsicCandidate
    public String(String original) {
        this.value = original.value;
        this.coder = original.coder;
        this.hash = original.hash;
    }

String에서도 쓰인다.

1. @Target

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation interface
     * can be applied to.
     * @return an array of the kinds of elements an annotation interface
     * can be applied to
     */
    ElementType[] value();
}

If an @Target meta-annotation is not present on an annotation interface T, then an annotation of type T may be written as a modifier for any declaration except a type parameter declaration.

 

가. ElementType

public enum ElementType {
    /** Class, interface (including annotation interface), enum, or record
     * declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation interface declaration (Formerly known as an annotation type.) */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE,

    /**
     * Module declaration.
     *
     * @since 9
     */
    MODULE,

    /**
     * Record component
     *
     * @jls 8.10.3 Record Members
     * @jls 9.7.4 Where Annotations May Appear
     *
     * @since 16
     */
    RECORD_COMPONENT;
}

해당 enum 변수들은 Target 메타 어노테이션에 사용되는 상수들이다. 각 enum 변수들을 Target에서 쓰이게 되는데 이것은 적용 범위라고 생각하면 된다. 

그럼 Target 어노테이션이 적용할 수 있는 범위는 ANNONTAION_TYPE 만 적용할 수 있다는 뜻이 된다.

그리고 IntrinsicCandidate 어노테이션의 적용 범위는 method, contructor 두 범위 내에서만 적용가능하는 것을 알 수 있다.

참고: http://cris.joongbu.ac.kr/course/java/api/java/lang/annotation/ElementType.html

2. @Retention

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

Indicates how long annotations with the annotated interface are to be retained. If no Retention annotation is present on an annotation interface declaration, the retention policy defaults to RetentionPolicy.CLASS.( 따로 지정하지 않으면 CLASS 정책을 디폴트로 가져간다.)

/**
 * Annotation retention policy.  The constants of this enumerated class
 * describe the various policies for retaining annotations.  They are used
 * in conjunction with the {@link Retention} meta-annotation interface to
 * specify how long annotations are to be retained.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

SOURCE: 컴파일러 의해 어노테이션은 버려진다.

SOURCE를 쓰는 대표적인 어노테이션은 @Override이다. (Lombok 에 있는 @ToString도 또한 SOURCE이다. 쉽게 생각하면 실제로 애플리케이션이 동작하면서 필요하지 않은 녀석들이라고 생각하면 된다.)

/**
 * Indicates that a method declaration is intended to override a
 * method declaration in a supertype. If a method is annotated with
 * this annotation type compilers are required to generate an error
 * message unless at least one of the following conditions hold:
 *
 * <ul><li>
 * The method does override or implement a method declared in a
 * supertype.
 * </li><li>
 * The method has a signature that is override-equivalent to that of
 * any public method declared in {@linkplain Object}.
 * </li></ul>
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

CLASS:  컴파일러까지는 존재하다가 런타임 시점에서 버려진다.(클래스 설정 관련해서 주로 쓰인다.)

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Immutable {
}

The class to which this annotation is applied is immutable. This means that its state cannot be seen to change by callers. Of necessity this means that all public fields are final, and that all public final reference fields refer to other immutable objects, and that methods do not publish references to any internal state which is mutable by implementation even if not by design. Immutable objects may still have internal mutable state for purposes of performance optimization; some state variables may be lazily computed, so long as they are computed from immutable state and that callers cannot tell the difference.
Immutable objects are inherently thread-safe; they may be passed between threads or published without synchronization.

 

RUNTIME: 런타임 시점까지 살아있다. (우리가 흔히 아는 대부분의 어노테이션이 런타임을 쓴다.)

@Documented
@Target(TYPE)
@Retention(RUNTIME)
public @interface Entity {

	/**
	 * (Optional) The entity name. Defaults to the unqualified
	 * name of the entity class. This name is used to refer to the
	 * entity in queries. The name must not be a reserved literal
	 * in the Jakarta Persistence query language.
	 */
	String name() default "";
}

 

2. getClass

 

@IntrinsicCandidate
    public final native Class<?> getClass();

Returns the runtime class of this ObjectThe returned Class object is the object that is locked by static synchronized methods of the represented class.
The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment:
Number n = 0;  Class<? extends Number> c = n.getClass(); 
Returns:
The Class object that represents the runtime class of this object.

 

(Class에 대해서는 나중에 더 조사해서 올려보도록 하겠습니다. )

 

 

3. hashCode

    @IntrinsicCandidate
    public native int hashCode();

Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by java.util.HashMap.
The general contract of hashCode is:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

(쉽게 말하자면 하나의 객체는 고유의 hashcode 값을 가지게 되며 이러한 것은 해시 테이블의 성능을 향상시킨다는 것이다.)

 

JavaEffective Item11

eqauls를 재정의할 때는 hashCode도 반드시 재정의해야 한다. 그렇지 않으면 프로그램이 제대로 동작하지 않을 것이다. 재정의한 hashCode는 Object의 API 문서에 기술된 일반 규약을 따라야 하며, 서로 다른 인스턴스라면 되도록 해시코드도 서로 다르게 구현해야 한다. 

hashCode 설정을 잘못 했을 때

위와 같이 해쉬코드 값을 잘못 설정하면 하나의 값에서 줄줄이 이어져서 시간 복잡도가 O(1)이 아닌 O(N)이 되버리기 때문에 hash 값을 재정의 할 때 잘 고려해야 한다.

 

3. equals

    public boolean equals(Object obj) {
        return (this == obj);
    }

Indicates whether some other object is "equal to" this one.
The equals method implements an equivalence relation on non-null object references:
It is reflexive: for any non-null reference value x, x.equals(x) should return true.
It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
For any non-null reference value x, x.equals(null) should return false.
The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

  • 반사성(reflexivity): null이 아닌 모든 참조 값 x에 대해, x.equals(x)는 true다.
  • 대칭성(symmetry): null이 아닌 모든 참조 값 x,y에 대해 x.equals(y)가 true면 y.equals(x)도 참이다.
  • 추이성(transitivity): null이 아닌 모든 참조 값 x,y,z에 대해 x.equals(y)가 참이고 y.equals(z)가 참이면 x.equals(z)도 참이다.
  • 일관성(consistency):null이 아닌 모든 참조 값 x,y에 대해 x.equals(y)를 반복해서 호출하면 항상 true를 반환하거나 false를 반환한다.
  • non-null: null이 아닌 모든 참조 값 x에 대해 x.equals(null)은 false다.
이러한 것을 검사해주는 AutoValue 프레임워크를 이용하자!

-- Gradle
// https://mvnrepository.com/artifact/com.google.auto.value/auto-value-annotations implementation 'com.google.auto.value:auto-value-annotations:1.8.2'

--Maven
<!-- https://mvnrepository.com/artifact/com.google.auto.value/auto-value-annotations --> <dependency> <groupId>com.google.auto.value</groupId> <artifactId>auto-value-annotations</artifactId> <version>1.8.2</version> </dependency>
재정의해야 할 때는 논리적 동치성을 확인해야할 때!

 

꼭 필요한 경우가 아니면 equals를 재정의하지 말자. 많은 경우에 Object의 equals가 우리가 원하는 비교를 정확히 수행해준다. 재정의해야 할 때는 그 클래스의 핵심 필드 모두를 빠짐 없이, 다섯 가지 규약을 확실히 지켜가며 비교해야 한다.

 

 

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함