| 백기선님의 라이브 스터디를 참고하여 작성한 게시물입니다.
참고: 클래스의 선언
기본적으로 클래스는 다음과 같은 형태를 띤다.
접근제어자 class 클래스이름 {
접근제어자 필드타입 필드이름;
...
접근제어자 메소드반환형 메소드이름(파라미터 타입 파라미터 이름)
...
};
자바는 접근제어자를 사용하여 변수나 메소드에 대한 접근 범위를 제한할 수 있다.
모든 클래스
에서 접근 가능
동일한 패키지에 위치한 모든 클래스
, 클래스를 상속한 다른 패키지에 위치한 하위 클래스
에서 접근 가능
어떠한 Access Modifier도 명시하지 않았을 때 기본적으로 적용되는 정책
동일한 패키지에 위치한 모든 클래스에서 접근 가능
변수, 메소드를 포함하는 클래스
에서만 접근 가능
지난 주에 공부했던 것 처럼, 변수는 세 가지로 구분할 수 있다.
Instance, class Variable은 class에서 선언한다.
public class Person {
private String name; // instance variable
protected Integer age; // instance variable
static String staticVariable // class variable
}
메소드는 아래와 같은 형태를 띤다.
Local varaible은 class의 method 내부에서 선언한다.
Local varaible은 사용전 반드시 초기화가 이뤄져야 한다.
public class Person {
...
// Access_Modifier, return_Type, method_name(param_type param_name) { ... }
public void printMessage() {
String message = "hello my name is " + this.name; // local variable
System.out.println(message);
}
}
모든 Class는 최소한 1개의 생성자를 가져야 한다.
생성자를 정의하지 않은 경우, 자바 컴파일러는 기본 생성자를 자동으로 생성한다.
class MyClass {
int i;
boolean b;
char c;
void myMethod() {
...
}
}
위와 같이 생성자가 없는 코드를 자바 컴파일러는 다음과 같이 만들어준다.
class MyClass {
int i;
boolean b;
char c;
// 기본 생성자
MyClass() {
}
void myMethod() {
...
}
}
기본 생성자는 instance variable를 초기화 하지 않는다.
class MyClass {
int i;
boolean b;
char c;
// 명시적으로 생성자를 정의했다.
MyClass(int i) {
this.i = i;
}
void myMethod() {
...
}
}
MyClass obj1 = new MyClass(1);
MyClass obj2 = new MyClass(); // Error, 기본 생성자 없음
위와 같이 개발자가 생성자를 명시적으로 정의한 경우, 기본 생성자는 생성되지 않는다.
참고: Where is definition of Method java/lang/Object."init":()V?
package defaultconstructor;
public class DefaultConstructor {
}
위와 같은 클래스를 컴파일한 바이트 코드를 살펴보자
javap -v -p -s DefaultConstructor.class
코드 영역을 보니, 자바 컴파일러에 의해 default constructor가 추가(?)되었음을 확인할 수 있다.
그런데 코드를 살펴보니, 생성자 내부에서 상위 클래스의 어떤 메소드를 호출한다 (invokespecial). 그리고 그 메소드는 java/lang/Object의 <init>
이다.
<init>
은 생성자, ()V
는 파라미터가 없고 Void를 반환함을 의미한다.
그런데 왜 상위 클래스의 메소드를 호출하는 것일까
생성자는 다음의 규칙에 따라 작동하기 때문이다.
앞에서 살펴봤다.
이는 자바의this()
, super()
를 의미한다.
this(), super()는 생성자 최상단에 위치해야 한다.
위의 생성자 법칙에 따라 정리하자면,
같은 결과를 내놓는 생성자와 메소드가 있다. 둘의 차이점이 있을까
descriptor를 살펴보면 factory메소드는 (I)클래스
인 반면, 생성자는 (I)V
이다.
즉, 생성자는 객체를 생성하여 반환하는 주체가 아니다.
자세한 내용은 뒤에서 다루도록 하겠다.
참고: 자바 소멸자 finalize
C, C++은 객체를 위해 할당한 메모리를 명시적으로 반납해야 하고, 소멸 직전에 소멸자가 호출된다.
자바도 Garbage collector를 통해 자동으로 자원을 반납하기 전 수행할 작업을finalize
를 통해 정의할 수 있다.
// 최상위 클래스 Object
public class Object {
// 기본 생성자
public Object() {}
...중략...
@Deprecated(since="9")
protected void finalize() throws Throwable { }
}
finalize
는 최상위 클래스 Object
에 정의되어 있고, 하위 클래스에서 각각 Override하여 사용한다.
그런데 프로그램을 예측할 수 없게 만든다는 치명적 단점 때문에 Java 9부터는 deprecated되었다.
대신 java.lang.ref
패키지의 Cleaner
클래스가 등장했다.
정의한 클래스로 부터 변수를 생성하는 과정은 세 단계로 구분할 수 있다.
MyClass myObj;
단순히 특정 메모리 주소를 가리키는 변수를 생성한 단계
new MyClass();
instantiation
new
연산자를 통해, 실제 인스턴스를 생성하는 작업Initialization
MyClass myObj = new MyClass();
할당 연산자를 통해 변수에 인스턴스를 할당하는 단계
자바에서 배열은 객체로 취급되므로, new
키워드를 통해 생성한다.
변수와 객체(인스턴스)는 서로 다른 메모리에 할당되므로, 다음의 상황에 주의해야 한다.
사진 출처: https://www.geeksforgeeks.org/new-operator-java/
일반적으로 알고 있는 this에 대한 설명은 생략하겠다.
this is a reference variable that refers to the current object.
다음과 같이 중첩된 클래스가 있다고 하자
class Outer {
class Inner {
public void showOuter() {
System.out.println ("Outer.this = " + Outer.this);
}
public void showInner() {
System.out.println ("this = " + this);
}
}
public static void main (String[] args) {
Outer outer = new Outer();
Inner inner = outer.new Inner();
inner.showOuter();
inner.showInner();
}
}
클래스가 중첩된 상황에서, 변수 이름이 같을 경우에도 this를 사용할 수 있다.