개발저장소
[Java] 클래스 - 2 본문
1. 인스턴스 필드, 메서드와 정적 필드, 메서드
필드와 메서드는 선언 방법에 따라 인스턴스와 정적(static)으로 분류할 수 있다.
인스턴스로 선언되면 객체 생성 후 사용할 수 있고, static으로 선언되면 객체 생성 없이도 사용 가능하다.
※ 필드는 객체가 존재하는 힙 영역에 저장되지만, 메서드는 객체마다 저장되면 중복으로 인해 메모리 효율이 떨어진다. 그러므로 메서드는 메서드 영역에 저장되고, 객체 없이 사용하지 못하도록 제한된다.
this와 super 키워드
this와 this(), super와 super()는 비슷하게 생겼지만 다른 역할을 수행한다.
this()는 생성자가 다른 생성자를 호출할 때 사용된다. this() 호출은 생성자의 첫 번째 문장에서 이루어진다는 특징이 있다.
this는 객체 자신을 뜻한다. 생성자나 메서드의 매개변수가 필드의 변수명과 동일한 경우 인스턴스 필드임을 강조하기 위해 this를 사용한다.
public class Book {
String name;
Book(String name) {
this.name = name;
}
}
super()는 상위 클래스의 생성자를 호출할 때 사용된다. 부모 클래스의 경우에도 생성자를 만들면 super() 키워드가 존재하는데, Object 클래스의 생성자를 호출하는 경우이다. Object 클래스의 생성자는 아무런 동작을 하지 않는다.
super는 상위 클래스를 지칭하는 키워드이다. this와 마찬가지로 상위 클래스의 인스턴스 필드나 메서드를 사용하기 위해 super를 사용한다.
정적 필드와 메서드는 공통적으로 static 키워드가 앞에 온다.
객체마다 가지고 있을 필요가 없는 공통적인 필드는 정적으로 선언하는 것이 좋다. 또, 인스턴스 필드를 굳이 사용하지 않는 메서드도 정적으로 선언하는 것이 좋다.
정적 필드와 메서드는 클래스가 메모리로 로딩되면 바로 사용할 수 있다.
사용법은 간단하다. 클래스 이름에 도트(.) 연산자를 사용해 접근하면 된다.
class A {
int m;
static int n = 5;
}
위와 같은 경우,
A a = new A(); 로 인스턴스가 생성된다면
힙 영역에 A, 그리고 A를 참조하는 a가 스택영역에 생성된다.
인스턴스 A에는 m의 값과, n이 저장된 곳을 참조하는 값이 주어진다.
그리고 n은 클래스 영역에 저장된다. 만약 A.n으로 사용한다면, A.n이 스택 영역에 올라가고 클래스 영역에 있는 n을 참조한다.
정적 메서드는 객체가 없어도 실행되지만, 내부의 인스턴스 필드나 인스턴스 메서드는 사용할 수 없다. 또한 객체 자신을 의미하는 this도 사용할 수 없다.
이러한 특징은 main() 메서드에서도 동일하게 적용된다. 객체 생성 없이 인스턴스 필드와 메서드를 main()에서 바로 사용할 수 없다.
public class Car {
int speed;
void run() {}
public static void main(String[] args) {
speed = 60; // 사용불가
run(); // 사용불가
}
}
final 키워드는 초기값이 저장되면 최종적인 값이 되도록 하고 변경을 불허한다.
상수는 불변의 값인 동시에 여러 값을 가질 필요가 없기 때문에 static과 final 키워드를 동시에 사용한다.
2. 패키지
패키지(package)는 단순히 디렉토리만 의미하지 않는다. 패키지는 클래스의 일부이며, 클래스를 식별하는 용도로 사용된다.
패키지는 클래스를 컴파일하는 과정에서 자동으로 생성된다. package 키워드와 함께 이름을 기술해야 하고, 소스 파일의 최상단에 항상 위치해야 한다.
패키지의 이름은 모두 소문자로 작성하는게 관례이다.
import
같은 패키지에 있는 클래스는 아무 조건 없이 사용 가능하다. 하지만 다른 패키지에 있는 클래스는 import문을 사용해 어떤 패키지인지 명시해야 한다.
만약 서로 다른 패키지에 동일한 클래스 이름이 존재한다면, 정확히 어떤 패키지인지 알려야 한다.
3. 접근 제한자
객체는 외부에서 필드가 변경되거나 메서드를 호출할 수 없도록 막을 필요가 종종 있다.
이때, 접근 제한자(Access Modifier)가 사용된다.
접근 제한자 | 제한 대상 | 제한 범위 |
public | 클래스, 필드, 생성자, 메서드 | 없음 |
protected | 필드, 생성자, 메서드 | 같은 패키지이거나, 자식 객체만 가능 |
default | 클래스, 필드, 생성자, 메서드 | 같은 패키지 |
private | 필드, 생성자, 메서드 | 객체 내부 |
클래스의 접근 제한
클래스는 public과 default 접근 제한을 가진다.
클래스에 public을 생략했다면 같은 패키지에서 제한 없이 사용할 수 있지만, 다른 패키지 사용은 불가능하다.
생성자의 접근 제한
생성자는 public, default, private 접근 제한을 가진다.
default는 같은 패키지에서만, private은 클래스 내부에서만 생성자를 호출할 수 있다.
필드와 메서드의 접근 제한
public, default, private 접근 제한을 가지고, 위와 마찬가지로 동작한다.
4. Getter와 Setter
필드를 외부에서 마음대로 읽고 변경하면 문제가 생길 수 있다. 그렇기 때문에 객체 지향 프로그래밍에서는 메서드를 통해 필드에 접근하는 것을 선호한다. Getter와 Setter는 이러한 역할을 하는 메서드이다.
private type fieldName;
// Getter
public type getFieldName() {
return fieldName;
}
// Setter
public void setFieldName(type fieldName) {
this.fieldName = fieldName;
}
이클립스에서는 [Source] - [Generate Getters and Setters]를 선택해 자동으로 생성할 수 있다.
5. 싱글톤 패턴
애플리케이션 전체에서 단 한 개의 객체만 생성해서 사용하기를 원한다면 싱글톤 패턴(Singleton Pattern)을 사용할 수 있다.
생성자를 private 접근 제한하며 외부에서 new 연산자로 생성자를 호출할 수 없도록 하는 것이다. 사용 방법은 다음과 같다.
- 생성자를 private으로 만들어 class 내부에서만 만들 수 있도록 한다.
- 자기 자신을 참조하는 static 변수를 하나 만든다.
- 오직 한번만 인스턴스를 생성하여 리턴하는 getInstance()를 만든다.
- 외부에서 Class명.getInstance()로 싱글톤 인스턴스를 획득한다.
생성자를 호출할 수 없으니 객체 생성은 불가능해진다. 대신 싱글톤 패턴이 제공하는 정적 메서드를 통해 간접적으로 객체를 얻는다.
public class 클래스 {
// private 접근 제한을 갖는 정적 필드
private static 클래스 singleton = null;
// private 접근 권한을 갖는 생성자 선언
private 클래스() {}
// public 접근 권한을 갖는 정적 메서드 선언
public static 클래스 getInstance() {
if(singleton == null)
sigleton = new 클래스();
return singleton;
}
}
외부에서 객체를 얻는 유일한 방법은 getInstance()를 호출해 미리 선언한 객체를 참조하는 것이다.
대표적인 사용 예: DAO(Database Access Object) 구현 시
'Programming Language > Java' 카테고리의 다른 글
[Java] 추상 클래스와 인터페이스 (0) | 2024.05.08 |
---|---|
[Java] 상속 (0) | 2024.05.03 |
[Java] 클래스 - 1 (0) | 2024.04.26 |
[Java] 제어문 (0) | 2024.04.10 |
[Java] 연산자 (0) | 2024.04.10 |