Java Language
객체 클래스 메소드와 생성자
수색…
소개
이 문서 페이지는 자바 클래스 생성자 에 대한 예제 및 새로 생성 된 클래스의 수퍼 클래스 Object
에서 자동으로 상속 된 객체 클래스 메소드 에 대한 세부 정보를 표시하는 데 사용됩니다.
통사론
- 최종 final 클래스 <?> getClass ()
- public 최종 네이티브 무효 notify ()
- public 최종 네이티브 무효 notifyAll ()
- public final native void wait (long timeout) throws InterruptedException
- public final void wait () throws InterruptedException
- public final void wait (long timeout, int nanos) InterruptedException를 슬로우합니다.
- 공개 네이티브 int hashCode ()
- public boolean equals (Object obj)
- public String toString ()
- protected native Object clone () CloneNotSupportedException를 슬로우합니다.
- protected void finalize () Throwable를 슬로우합니다.
toString () 메서드
toString()
메서드는 객체의 내용을 사용하여 객체의 String
표현을 만드는 데 사용됩니다. 이 메서드는 클래스를 작성할 때 무시해야합니다. toString()
은 "hello " + anObject
와 같이 객체가 문자열에 연결될 때 암시 적으로 호출됩니다.
다음을 고려하세요:
public class User {
private String firstName;
private String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return firstName + " " + lastName;
}
public static void main(String[] args) {
User user = new User("John", "Doe");
System.out.println(user.toString()); // Prints "John Doe"
}
}
여기서 Object
클래스의 toString()
은 User
클래스에서 재정 의하여 객체를 인쇄 할 때 객체에 대한 의미있는 데이터를 제공합니다.
println()
을 사용하면 객체의 toString()
메서드가 암시 적으로 호출됩니다. 따라서 이러한 진술은 똑같은 일을합니다.
System.out.println(user); // toString() is implicitly called on `user`
System.out.println(user.toString());
위에서 설명한 User
클래스에서 toString()
이 재정의되지 않으면 System.out.println(user)
은 User@659e0bfd
또는 클래스 이름을 제외한 유용한 정보가 거의없는 유사한 String을 반환 할 수 있습니다. 이는 호출이 User
클래스 구조 나 비즈니스 규칙에 대해 알지 못하는 기본 Java Object
클래스의 toString()
구현을 사용하기 때문입니다. 클래스에서이 기능을 변경하려면 메서드를 재정의하십시오.
equals () 메서드
TL, DR
==
참조 평등에 대한 테스트 ( 동일한 객체 인지 여부)
.equals()
는 값의 동일성을 테스트합니다 ( 논리적으로 "동등" 한지 여부).
equals()
는 두 객체가 equals()
비교하기 위해 사용되는 메소드입니다. Object
클래스의 equals()
메서드의 기본 구현은 두 인스턴스가 동일한 인스턴스를 가리키는 경우에만 true
반환 true
. 따라서 그것은 ==
의한 비교와 동일하게 동작합니다.
public class Foo {
int field1, field2;
String field3;
public Foo(int i, int j, String k) {
field1 = i;
field2 = j;
field3 = k;
}
public static void main(String[] args) {
Foo foo1 = new Foo(0, 0, "bar");
Foo foo2 = new Foo(0, 0, "bar");
System.out.println(foo1.equals(foo2)); // prints false
}
}
foo1
과 foo2
는 같은 필드로 생성 되더라도 메모리에있는 두 개의 다른 객체를 가리키고 있습니다. 따라서 기본 equals()
구현은 false
평가됩니다.
오브젝트의 내용을 동등한지 비교하려면, equals()
를 오버라이드 (override equals()
할 필요가 있습니다.
public class Foo {
int field1, field2;
String field3;
public Foo(int i, int j, String k) {
field1 = i;
field2 = j;
field3 = k;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Foo f = (Foo) obj;
return field1 == f.field1 &&
field2 == f.field2 &&
(field3 == null ? f.field3 == null : field3.equals(f.field3));
}
@Override
public int hashCode() {
int hash = 1;
hash = 31 * hash + this.field1;
hash = 31 * hash + this.field2;
hash = 31 * hash + (field3 == null ? 0 : field3.hashCode());
return hash;
}
public static void main(String[] args) {
Foo foo1 = new Foo(0, 0, "bar");
Foo foo2 = new Foo(0, 0, "bar");
System.out.println(foo1.equals(foo2)); // prints true
}
}
여기서 오버라이드 된 equals()
메서드는 필드가 동일하면 객체가 동일하다고 결정합니다.
hashCode()
메서드도 덮어 썼습니다. 이 메서드에 대한 계약에 따르면 두 개체가 같으면 해시 값도 동일해야합니다. 그래서 거의 항상 hashCode()
와 equals()
함께 오버라이드해야합니다.
equals
메소드의 인수 유형에 특히주의하십시오. Foo obj
아닌 Object obj
Foo obj
입니다. 후자를 메서드에 넣으면 equals
메서드를 재정의하지 않습니다.
자신의 클래스를 작성할 때 equals()
및 hashCode()
재정의 할 때 비슷한 논리를 작성해야합니다. 대부분의 IDE는이를 자동으로 생성 할 수 있습니다.
equals()
구현의 예제는 핵심 Java API의 일부인 String
클래스에서 찾을 수 있습니다. 오히려 포인터를 비교보다는 String
클래스의 내용을 비교 String
.
Java 1.7에서는, 편리한 메소드 인 equals
를 제공하는 java.util.Objects
클래스를 도입 해, null
가능성이있는 2 개의 참조를 비교해, equals
메소드의 구현을 간소화하기 위해서 사용할 수 있습니다.
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Foo f = (Foo) obj;
return field1 == f.field1 && field2 == f.field2 && Objects.equals(field3, f.field3);
}
수업 비교
equals 메소드는 모든 객체에 대해 실행할 수 있기 때문에 메소드가 자주 수행하는 첫 번째 작업 ( null
을 확인한 후 수행) 중 하나는 비교되는 객체의 클래스가 현재 클래스와 일치하는지 확인하는 것입니다.
@Override
public boolean equals(Object obj) {
//...check for null
if (getClass() != obj.getClass()) {
return false;
}
//...compare fields
}
이것은 일반적으로 클래스 객체를 비교하여 위와 같이 수행됩니다. 그러나 이는 몇 가지 특별한 경우에 실패 할 수 있습니다. 예를 들어 일부 프레임 워크는 클래스의 동적 프록시를 생성하며 이러한 동적 프록시는 실제로 다른 클래스입니다. 다음은 JPA를 사용한 예입니다.
Foo detachedInstance = ...
Foo mergedInstance = entityManager.merge(detachedInstance);
if (mergedInstance.equals(detachedInstance)) {
//Can never get here if equality is tested with getClass()
//as mergedInstance is a proxy (subclass) of Foo
}
이 한계를 극복하기위한 하나의 메커니즘은 instanceof
사용하여 클래스를 비교하는 것입니다
@Override
public final boolean equals(Object obj) {
if (!(obj instanceof Foo)) {
return false;
}
//...compare fields
}
그러나 instanceof
사용할 때 피할 수있는 몇 가지 함정이 있습니다. Foo는 잠재적으로 다른 하위 클래스를 가질 수 있으므로 해당 하위 클래스가 equals()
를 재정의 할 수 있으므로 Foo
가 FooSubclass
같지만 FooSubclass
가 Foo
가 아닌 경우가 될 수 있습니다.
Foo foo = new Foo(7);
FooSubclass fooSubclass = new FooSubclass(7, false);
foo.equals(fooSubclass) //true
fooSubclass.equals(foo) //false
이것은 대칭과 전이 속성을 위반하므로 equals()
메서드를 잘못 구현 한 것입니다. 결과적으로 instanceof
사용할 때 위와 같이 equals()
메서드를 final
로 만드는 것이 좋습니다. 이것에 의해, 서브 클래스가 equals()
오버라이드 equals()
override)하는 일이없고, 주요한 가정을 위반하는 일이 없게됩니다.
hashCode () 메서드
Java 클래스가 equals
메소드를 오버라이드 (override)하는 경우, hashCode
메소드도 오버라이드 (override) 할 필요가 있습니다. 메소드의 계약에 정의 된대로 :
- Java 어플리케이션의 실행 중에 같은 오브젝트로 2 회 이상 불려 갈 때마다
hashCode
메소드는, 오브젝트의 equals 비교로 사용 된 정보가 변경되지 않으면, 같은 정수를 일관되게 돌려 줄 필요가 있습니다. 이 정수는 응용 프로그램의 한 실행에서 동일한 응용 프로그램의 다른 실행으로 일관성을 유지할 필요가 없습니다.- 2 개의 객체가
equals(Object)
메소드로equals(Object)
하면, 2 개의 객체의 각각으로hashCode
메소드를 호출하면equals(Object)
, 같은 정수 결과가 생성되지 않으면 안됩니다.equals(Object)
메소드로 2 개의 객체가 동일하지 않은 경우, 2 개의 객체의 각각으로hashCode
메소드를 호출하면equals(Object)
, 다른 정수 결과가 생성 될 필요는 없습니다. 그러나 프로그래머는 부동 한 객체에 대해 고유 한 정수 결과를 생성하면 해시 테이블의 성능이 향상 될 수 있음을 인식해야합니다.
해시 코드는 HashMap
, HashTable
및 HashSet
과 같은 해시 구현에 사용됩니다. hashCode
함수의 결과는 객체가 놓일 양동이를 결정합니다. 이 해시 구현은 제공된 hashCode
구현이 좋은 경우보다 효율적입니다. 좋은 hashCode
구현의 중요한 속성은 hashCode
값의 분포가 일정하다는 것입니다. 즉, 동일한 인스턴스에 많은 인스턴스가 저장 될 확률이 적습니다.
해시 코드 값을 계산하는 알고리즘은 다음과 유사 할 수 있습니다.
public class Foo {
private int field1, field2;
private String field3;
public Foo(int field1, int field2, String field3) {
this.field1 = field1;
this.field2 = field2;
this.field3 = field3;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Foo f = (Foo) obj;
return field1 == f.field1 &&
field2 == f.field2 &&
(field3 == null ? f.field3 == null : field3.equals(f.field3);
}
@Override
public int hashCode() {
int hash = 1;
hash = 31 * hash + field1;
hash = 31 * hash + field2;
hash = 31 * hash + (field3 == null ? 0 : field3.hashCode());
return hash;
}
}
Arrays.hashCode ()를 지름길로 사용하기
Java 1.2 이상에서는, 해시 코드를 계산하기위한 알고리즘을 개발하는 대신에, 필드 치를 포함한 Object 또는 원시적 배열을 제공하는 것으로 java.util.Arrays#hashCode
를 사용해 생성 할 수 있습니다.
@Override
public int hashCode() {
return Arrays.hashCode(new Object[] {field1, field2, field3});
}
Java 1.7에는 제공되는 hash(Object... objects)
의 값을 기반으로 해시 코드를 계산하는 편리한 메소드 인 hash(Object... objects)
를 제공하는 java.util.Objects
클래스가 도입되었습니다. 이 메소드는 java.util.Arrays#hashCode
와 동일하게 작동합니다.
@Override
public int hashCode() {
return Objects.hash(field1, field2, field3);
}
참고 :이 방법은 비효율적이며 사용자 정의 hashCode()
메서드가 호출 될 때마다 가비지 객체를 생성합니다.
- 임시
Object[]
가 생성됩니다.Objects.hash()
버전에서 배열은 "varargs"메커니즘에 의해 생성됩니다. - 필드 중 하나라도 기본 유형 인 경우 상자에 입력해야하며 더 임시 객체가 만들어 질 수 있습니다.
- 배열을 채워야합니다.
- 배열은
Arrays.hashCode
또는Objects.hash
메서드에 의해 반복해야합니다. -
Arrays.hashCode
또는Objects.hash
가 (아마) 작성Objects.hash
Object.hashCode()
대한 호출은 인라인 될 수 없습니다.
해시 코드의 내부 캐싱
객체의 해시 코드를 계산할 때 비용이 많이 소요될 수 있으므로 처음 계산할 때 해시 코드 값을 객체 내에 캐시하는 것이 좋습니다. 예를 들어
public final class ImmutableArray {
private int[] array;
private volatile int hash = 0;
public ImmutableArray(int[] initial) {
array = initial.clone();
}
// Other methods
@Override
public boolean equals(Object obj) {
// ...
}
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
h = Arrays.hashCode(array);
hash = h;
}
return h;
}
}
이 접근법은 해시 코드를 캐시하기 위해 추가 필드의 오버 헤드에 대해 해시 코드를 계산 (반복적으로 계산)하는 데 드는 비용을 절감합니다. 퍼포먼스 최적화에 따라 비용이 지불되는지 여부는 주어진 객체가 얼마나 자주 해쉬 (look up)되는지와 다른 요인들에 달려 있습니다.
또한 ImmutableArray
의 실제 해시 코드가 0 (1 초에 2 32 )이되면 캐시가 비효율적이라는 것을 알 수 있습니다.
마지막으로,이 접근법은 우리가 해싱하는 객체가 변경 가능한 경우 올바르게 구현하는 것이 훨씬 어렵습니다. 그러나 해시 코드가 변경되면 더 큰 걱정거리가 있습니다. 위의 계약을 참조하십시오.
wait () 및 notify () 메소드
wait()
와 notify()
작동합니다. 한 스레드가 객체에서 wait()
를 호출하면 다른 스레드가 notify()
또는 notifyAll()
을 호출 할 때까지 스레드가 차단됩니다.
(참고 : wait () / notify () )
package com.example.examples.object;
import java.util.concurrent.atomic.AtomicBoolean;
public class WaitAndNotify {
public static void main(String[] args) throws InterruptedException {
final Object obj = new Object();
AtomicBoolean aHasFinishedWaiting = new AtomicBoolean(false);
Thread threadA = new Thread("Thread A") {
public void run() {
System.out.println("A1: Could print before or after B1");
System.out.println("A2: Thread A is about to start waiting...");
try {
synchronized (obj) { // wait() must be in a synchronized block
// execution of thread A stops until obj.notify() is called
obj.wait();
}
System.out.println("A3: Thread A has finished waiting. "
+ "Guaranteed to happen after B3");
} catch (InterruptedException e) {
System.out.println("Thread A was interrupted while waiting");
} finally {
aHasFinishedWaiting.set(true);
}
}
};
Thread threadB = new Thread("Thread B") {
public void run() {
System.out.println("B1: Could print before or after A1");
System.out.println("B2: Thread B is about to wait for 10 seconds");
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000); // sleep for 1 second
} catch (InterruptedException e) {
System.err.println("Thread B was interrupted from waiting");
}
}
System.out.println("B3: Will ALWAYS print before A3 since "
+ "A3 can only happen after obj.notify() is called.");
while (!aHasFinishedWaiting.get()) {
synchronized (obj) {
// notify ONE thread which has called obj.wait()
obj.notify();
}
}
}
};
threadA.start();
threadB.start();
threadA.join();
threadB.join();
System.out.println("Finished!");
}
}
몇 가지 예제 출력 :
A1: Could print before or after B1
B1: Could print before or after A1
A2: Thread A is about to start waiting...
B2: Thread B is about to wait for 10 seconds
B3: Will ALWAYS print before A3 since A3 can only happen after obj.notify() is called.
A3: Thread A has finished waiting. Guaranteed to happen after B3
Finished!
B1: Could print before or after A1
B2: Thread B is about to wait for 10 seconds
A1: Could print before or after B1
A2: Thread A is about to start waiting...
B3: Will ALWAYS print before A3 since A3 can only happen after obj.notify() is called.
A3: Thread A has finished waiting. Guaranteed to happen after B3
Finished!
A1: Could print before or after B1
A2: Thread A is about to start waiting...
B1: Could print before or after A1
B2: Thread B is about to wait for 10 seconds
B3: Will ALWAYS print before A3 since A3 can only happen after obj.notify() is called.
A3: Thread A has finished waiting. Guaranteed to happen after B3
Finished!
getClass () 메소드
getClass()
메소드는 객체의 런타임 클래스 유형을 찾는 데 사용할 수 있습니다. 아래 예를 참조하십시오.
public class User {
private long userID;
private String name;
public User(long userID, String name) {
this.userID = userID;
this.name = name;
}
}
public class SpecificUser extends User {
private String specificUserID;
public SpecificUser(String specificUserID, long userID, String name) {
super(userID, name);
this.specificUserID = specificUserID;
}
}
public static void main(String[] args){
User user = new User(879745, "John");
SpecificUser specificUser = new SpecificUser("1AAAA", 877777, "Jim");
User anotherSpecificUser = new SpecificUser("1BBBB", 812345, "Jenny");
System.out.println(user.getClass()); //Prints "class User"
System.out.println(specificUser.getClass()); //Prints "class SpecificUser"
System.out.println(anotherSpecificUser.getClass()); //Prints "class SpecificUser"
}
getClass()
메서드는 가장 구체적인 클래스 형식을 반환하므로 anotherSpecificUser
에서 getClass()
가 호출 될 때 User
보다 상속 트리가 낮기 때문에 반환 값은 class SpecificUser
입니다.
getClass
메소드는 다음과 같이 선언되지만 주목할만한 점은 다음과 같습니다.
public final native Class<?> getClass();
getClass
의 호출에 의해 반환되는 실제의 static 형은 Class<? extends T>
여기에서 T
는 getClass
가 호출되는 객체의 정적 유형입니다.
즉 다음과 같이 컴파일됩니다.
Class<? extends String> cls = "".getClass();
clone () 메서드
clone()
메서드는 객체의 복사본을 만들고 반환하는 데 사용됩니다. 논쟁의 여지가있는이 방법은 문제가되며 복사 생성자 또는 다른 복사 방법을 사용하여 clone()
사용해야하므로 피해야합니다.
메서드를 호출하는 모든 클래스는 Cloneable
인터페이스를 구현해야합니다.
Cloneable
인터페이스 자체는 호출 객체 클래스가 Cloneable
구현하는지 여부를 확인하는 native
clone()
메서드의 동작을 변경하는 데 사용되는 태그 인터페이스입니다. 호출 CloneNotSupportedException
가이 인터페이스를 구현하지 않으면 CloneNotSupportedException
이 발생합니다.
Object
클래스 자체는이 인터페이스를 구현하지 않으므로 호출 객체가 Object
클래스 인 경우 CloneNotSupportedException
이 발생합니다.
복제본이 올바른 복제본은 복제되는 객체와 독립적이어야하므로 반환되기 전에 객체를 수정해야 할 수 있습니다. 이것은 본질적으로 복제중인 개체의 내부 구조를 구성하는 변경 가능한 개체를 복사하여 "전체 복사본"을 만드는 것을 의미합니다. 이것이 올바르게 구현되지 않으면 복제 된 개체가 독립적이지 않고 복제 된 개체와 동일한 변경 가능한 개체에 대한 참조가 있습니다. 이로 인해 하나의 변경 사항이 다른 변경 사항에 영향을 미치기 때문에 일관되지 않은 동작이 발생합니다.
class Foo implements Cloneable {
int w;
String x;
float[] y;
Date z;
public Foo clone() {
try {
Foo result = new Foo();
// copy primitives by value
result.w = this.w;
// immutable objects like String can be copied by reference
result.x = this.x;
// The fields y and z refer to a mutable objects; clone them recursively.
if (this.y != null) {
result.y = this.y.clone();
}
if (this.z != null) {
result.z = this.z.clone();
}
// Done, return the new object
return result;
} catch (CloneNotSupportedException e) {
// in case any of the cloned mutable fields do not implement Cloneable
throw new AssertionError(e);
}
}
}
finalize () 메소드
이것은 Object
클래스의 보호 되고 비 정적 인 메서드입니다. 이 메소드는 객체가 메모리에서 제거되기 전에 객체에 대한 최종 작업 또는 정리 작업을 수행하는 데 사용됩니다.
doc에 따르면, 가비지 콜렉션이 객체에 대한 더 이상의 참조가 없다고 결정할 때,이 메소드는 객체의 가비지 컬렉터에 의해 호출된다.
그러나 오브젝트가 여전히 도달 할 수 있거나 오브젝트가 적합하게 될 때 가비지 콜렉터가 실행되지 않으면 finalize()
메소드가 호출된다는 보장이 없습니다. 그래서이 방법에 의존하지 않는 것이 좋습니다.
Java 핵심 라이브러리에서 FileInputStream.java
와 같은 사용 예제를 찾을 수 있습니다.
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
close();
}
}
이 경우 이전에 리소스가 닫혀 있지 않으면 리소스를 닫을 수있는 마지막 기회입니다.
일반적으로 모든 종류의 응용 프로그램에서 finalize()
메서드를 사용하는 것은 나쁜 습관으로 간주되므로 피해야합니다.
종결자는 파일 해제와 같은 자원 확보를위한 것이 아닙니다 . 가비지 컬렉터는 시스템이 힙 공간이 부족할 때 호출됩니다. 파일 핸들이 부족하거나 다른 이유로 시스템이 실행 중일 때 호출 할 수는 없습니다.
파이널 라이저의 의도 된 유스 케이스는 임박한 운명에 대해 다른 객체에게 알리기 위해 재생하려고하는 객체를위한 것입니다. 이제는 java.lang.ref.WeakReference<T>
클래스를위한 더 나은 메커니즘이 존재합니다. finalize()
메서드를 작성해야한다고 생각되면 WeakReference
를 사용하여 동일한 문제를 해결할 수 있는지 여부를 조사해야합니다. 그래도 문제가 해결되지 않으면 디자인을 다시 생각해야 할 수도 있습니다.
더 읽기 위해 여기 에 대한 항목이다 finalize()
여호수아 블로흐 "효과적인 자바"책에서 방법.
객체 생성자
Java의 모든 생성자는 Object
생성자를 호출해야합니다. 이것은 super()
호출로 수행됩니다. 이것은 생성자의 첫 번째 줄이어야합니다. 그 이유는 추가 초기화가 수행되기 전에 실제로 힙에 오브젝트를 작성할 수 있기 때.입니다.
생성자에서 super()
에 대한 호출을 지정하지 않으면 컴파일러에서 대신 넣습니다.
따라서이 세 가지 예 모두 기능적으로 동일합니다
super()
생성자에 대한 명시 적 호출
public class MyClass {
public MyClass() {
super();
}
}
super()
생성자에 대한 암시 적 호출 사용
public class MyClass {
public MyClass() {
// empty
}
}
암시 적 생성자 사용
public class MyClass {
}
생성자 - 연결은 어떻습니까?
다른 생성자를 생성자의 첫 번째 명령어로 호출 할 수 있습니다. 슈퍼 생성자에 대한 명시 적 호출과 다른 생성자에 대한 호출은 모두 첫 번째 지침이어야하므로 상호 배타적입니다.
public class MyClass {
public MyClass(int size) {
doSomethingWith(size);
}
public MyClass(Collection<?> initialValues) {
this(initialValues.size());
addInitialValues(initialValues);
}
}
새 MyClass(Arrays.asList("a", "b", "c"))
를 호출하면 List 인수를 사용하여 두 번째 생성자를 호출하고 첫 번째 생성자 (즉, super()
) 다음에리스트의 두 번째 크기로 addInitialValues(int size)
를 호출하십시오. 이것은 여러 생성자가 동일한 작업을 수행해야하는 코드 중복을 줄이기 위해 사용됩니다.
특정 생성자를 호출하려면 어떻게해야합니까?
위의 예에서 new MyClass("argument")
또는 new MyClass("argument", 0)
를 호출 할 수 있습니다. 즉, 메소드 오버로드 와 매우 흡사하게, 선택한 생성자에 필요한 매개 변수로 생성자를 호출하기 만하면됩니다.
Object 클래스 생성자는 어떻게 될까요?
기본 빈 생성자가있는 하위 클래스 ( super()
대한 호출을 제외한)에서 발생하는 것 이상은 발생하지 않습니다.
기본 빈 생성자는 명시 적으로 정의 할 수 있지만 그렇지 않은 경우 컴파일러는 이미 다른 생성자가 정의되어 있지 않은 한 사용자를 대신합니다.
그런 다음 Object의 생성자에서 객체가 어떻게 생성됩니까?
객체의 실제 생성은 JVM으로 내려갑니다. Java의 모든 생성자는 인스턴스 초기화를 담당하는 <init>
이라는 특수 메서드로 나타납니다. 이 <init>
메서드는 컴파일러에서 제공하며 <init>
은 Java에서 유효한 식별자가 아니기 때문에 언어에서 직접 사용할 수 없습니다.
JVM은이
<init>
메소드를 어떻게 호출합니까?
JVM은 invokespecial
명령을 사용하여 <init>
메소드를 호출하며 초기화되지 않은 클래스 인스턴스에서만 호출 될 수 있습니다.
자세한 내용은 JVM 사양 및 Java 언어 사양을 참조하십시오.
- 특수 메소드 (JVM) - JVMS - 2.9
- 생성자 - JLS - 8.8