정적 멤버와 static
정적은 '고정된'이란 의미를 가지고 있다. 정적 멤버는 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메소드를 말한다. 정적 멤버는 객체에 소속된 멤버가 아니라 클래스에 소속된 멤버이기 때문에 클래스 멤버라고도 한다.
정적 필드와 정적 메소드는 클래스에 고정된 멤버이므로 클래스 로더가 클래스 ( 바이트 코드 )를 로딩해서 메소드 메모리 영역에 적재할 떄 클래스별로 관리한다. 따라서 클래스의 로딩이 끝나면 바로 사용할 수 있다.
이를 사용해야 하는 경우는 원의 넓이나 둘레를 구할 떄 파이는 Calculator 객체마다 가지고 있을 필요가 없즌 변하지 않는 공용적인 데이터이므로 정적 필드로 선언하는 것이 좋다. 그러나 객체별로 색깔이 다르다면 색깔은 인스턴스 필드로 선언해야 한다.
package com.company.Class;
public class Calculator {
static double pi = 3.141592;
static int plus(int x, int y) {
return x + y;
}
static int minus(int x, int y) {
return x - y;
}
}
또한 정적 필드는 객체 생성 없이도 사용해야 하므로 생성자에서 초기화 작업을 해 줄 수가 없다.ㅇ따라서 정적 필드를 위한 초기화 작업은 정적 블럭을 제공한다. 정적 블록은 클래스가 메모리로 로딩될 때 자동적으로 실행된다. 정적블록은 클래스 내부에 여러 개가 선언되어도 상관없다.
static {
...
}
또한 정적 블록을 선언할 때 주의할 점은 객체가 없어도 실행된다는 특징 때문에, 이들 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없다. 또한 객체 자신의 참조인 this 키워드도 사용이 불가능 하다.
또한 main도 static메서드이니까 바로 인스턴스 필드에 접근할 수 없다 ( 무조건 static 필드 )만 접근할 수 있다.
다음은 적절한 예시다
package com.company.Class;
public class Car {
int speed;
void run() {
System.out.println(speed + "으로 달립니다.");
}
public static void main(String[] args) {
Car myCar = new Car();
myCar.speed = 60;
myCar.run();
}
}
싱글톤(Singleton)
가끔 전체 프로그램에서 단 하나의 객체만 만들도록 보장해야 하는 경우가 있다. 단 하나만 생성된다고 해서 이 객체를 싱글톤이라고 한다. 싱글톤을 만들려면 클래스 외부에서 new 연산자로 생성자를 호출할 수 없도록 막아야 한다. 생성자를 호출한 만큼 객체가 생성되기 때문이다. 생성자를 외부에서 호출할 수 없도록 하려면 생성자 앞에 private접근 제한자를 붙여주면 된다. 접근 제한자는 나중에 자세히 설명하기로 하고, 여기서는 외부에서 생성자 호출을 막기 위해 private를 붙여 준다는 것만 알아 두자.
package com.company.Class;
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
}
static Singleton getInstance() {
return singleton;
}
}
대충 이렇게 만드는데 자신의 타입인 정적 필드를 하나 선언하고 자신의 객체를 생성해 초기화한다. 이렇게 하면 외부에서 객체를 얻는 유일한 방법은 getInstance()메소드를 호출하는 방법이다. getInstance()메소드는 단 하나의 객체만 리턴하기 때문에 아래 코드에서 변수 1과 변수 2는 동일한 객체를 참조한다.
public static void main(String[] args) {
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
if(obj1 == obj2) {
System.out.println("같은 Singleton 객체 입니다.");
} else {
System.out.println("다른 Singleton 객체 입니다.");
}
}
// 같은 Singleton 객체 입니다.
final 필드
final 필드는 바뀌어 지면 안된다. final필드의 초기값을 줄 수 있는 방법은 딱 두가지 밖에 없다. 첫 번째는 필드 선언 시에 주는 방법이고, 두 번째는 생성자에서 주는 방법이다. 생성자는 final필드의 최종 초기화를 마쳐야 하는데, 만약 초기화되지 않은 final필드를 그대로 남겨두면 컴파일 에러가 발생한다.
상수 (static final)
final 필드는 객체마다 저장되고, 생성자의 매개값을 통해서 여러 가지 값을 가질 수 있기 때문에 상수가 될 수 없다. static final필드는 객체마다 저장되지 않고, 클래스에만 포함된다. 그리고 한 번 초기값이 저장되면 변경할 수 없다.
package com.company.Class;
public class Earth {
static final double EARTH_RADIUS = 6400;
static final double EARTH_SURFACE_AREA;
static {
EARTH_SURFACE_AREA = 4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS;
}
public static void main(String[] args) {
System.out.println("지구의 반지름: " + Earth.EARTH_RADIUS + " km");
System.out.println("지구의 표면적: " + Earth.EARTH_SURFACE_AREA + " km^2");
}
}
//지구의 반지름: 6400.0 km
//지구의 표면적: 5.147185403641517E8 km^2
패키지
패키지는 단순히 파일 시스템의 폴더 기능만 하는 것이 아니라 클래스의 일부분이다. 패키지는 클래스를 유일하게 만들어주는 식별자 역할을 한다. 클래스 이름이 동일하더라도 패키지가 다르면 다른 클래스로 인식한다. 클래스의 전체 이름은 "패키지명 + 클래스명"인데 패키지가 상.하위로 구분되어 있다면 도트(.)를 사용해서 다음과 같이 표현한다.
상위패키지.하위패키지.클래스
접근 제한자
< 클래스의 접근 제한자 >
default 접근 제한
클래스를 선언할 때 public을 생략했다면 이는 default 접근 제한을 가진다. default접근 제한을 가지게 되면 같은 패키지에서는 아무런 제한없이 사용할 수 있지만 다른 패키지에서는 사용할 수 없도록 제한된다.
public 접근 제한
클래스가 public접근 제한을 가지게 되면 같은 패키지뿐만 아니라 다른 패키지에서도 아무런 제한 없이 사용할 수 있다. 클래스를 다른 개발자가 사용할 수 있도록 라이브러리 클래스로 개발되어야 한다면, 반드시 public 접근 제한을 갖도록 해야 한다.
< 생성자의 접근 제한 >
클래스에 생성자를 선언하지 않으면 컴파일러에 의해 자동적으로 기본 생성자가 추가된다. 자동으로 생성되는 기본 생성자의 접근 제한자는 클래스의 접근 제한과 동일하다.
public 접근 제한
public 접근 제한은 모든 패키지에서 아무런 제한 없이 생성자를 호출할 수 있도록 한다. 생성자가 public 접근 제한을 가진다면 클래스도 public 접근 제한을 가지는 것이 정상적이다. 클래스가 default 접근 제한을 가진다면 클래스 사용이 같은 패키지로 한정되므로, 비록 생성자가 public 접근제한을 가진다고 하더라도 같은 패키지에서만 생성자를 호출할 수 있다.
protected 접근 제한
protected 접근 제한은 default 접근 제한과 마찬가지로 같은 패키지에 속하는 클래스에서 생성자를 호출할 수 있도록 한다. 차이점은 다른 패키지에 속한 클래스가 해당 클래스의 자식클래스라면 호출할 수 있다.
default 접근 제한
생성자를 선언할 때 public또는 private를 생략했다면 생성자는 default 접근 제한을 가진다. default 접근 제한은 같은 패키지에서는 아무런 제한 없이 생성자를 호출할 수 있으니, 다른 패키지에서는 생성자를 호출할 수 없도록 한다.
private 접근 제한
private 접근 제한은 동일 페키지건 다른 패키지건 상관없이 생성자를 호출하지 못하도록 제한한다. 따라서 클래스 외부에서 new 연산자로 객체를 만들 수 없다. 오로지 클래스 내부에서만 생성자를 호출할 수 있고, 객체를 만들 수 있다. ( Singleton 객체를 만들 떄 사용 )
< 필드와 메소드의 접근 제한 >
public 접근 제한
public 접근 제한은 모든 패키지에서 아무런 제한 없이 필드와 메소드를 사용할 수 있도록 해준다. 필드와 메소드가 public 접근 제한을 가질 경우 클래스도 public접근 제한을 가져야 한다. 클래스가 default접근 제한을 가지게 되면 같은 패키지 안에서만 클래스가 사용되기 때문이다.
protected 접근 제한
protected 접근 제한은 defualt 접근 제한과 마찬가지로 같은 패키지에 속하는 클래스에서 필드와 메소드를 사용할 수 있다. 차이점은 다른 패키지에 속한 클래스가 해당 클래스의 자식 클래스라면 필드와 메소드를 사용할 수 있다.
default 접근 제한
필드와 메소드를 선언할 때 public 또는 private를 생략했다면 default 접근 제한을 가진다. default접근 제한은 같은 패키지에서는 아무런 제한 없이 필드와 메소드를 사용할 수 있으나, 다른 패키지에서는 필드와 메소드를 사용할 수 없도록 한다.
private 접근 제한
Private 접근 제한은 동일 패키지이건 다른 패키지이건 상관없이 필드와 메소드를 사용하지 못하도록 제한한다. 오로지 클래스 내부에서만 사용할 수 있다.
Getter와 Setter
일반적으로 객체 지향 프로그래밍에서 객체의 데이터는 객체 외부에서 직접적으로 접근하는 것을 막는다. 그 이유는 객체의 데이터를 외부에서 마음대로 읽고 변경할 경우 객체의 무결성이 깨어질 수 있기 때문이다. 예를 들어 자동차의 속도는 음수가 될 수 없는데, 외부에서 음수로 변경하면 객체의 무결성이 깨진다.
이를 해결하기 위해 등장 한것이 Getter와 Setter이다. 객체 지향 프로그래밍에서는 메소드를 통해서 데이터를 변경하는 방법을 선호한다. 데이터는 외부에서 접근할 수 없도록 막고 메소드는 공개해서 외부에서 메소드를 통해 데이터에 접근하도록 유도한다. 그 이유는 메소드는 매개값을 검증해서 유효한 값만 데이터로 저장할 수 있기 때문이다.
package com.company.Class;
public class Car {
private int speed;
private boolean stop;
public Car() {}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
if(speed < 0) {
this.speed = 0;
} else {
this.speed = speed;
}
}
public boolean isStop() {
return stop;
}
public void setStop(boolean stop) {
this.stop = stop;
this.speed = 0;
}
public static void main(String[] args) {
Car myCar = new Car();
myCar.setSpeed(-50);
System.out.println("현재 속도: " + myCar.getSpeed());
myCar.setSpeed(60);
System.out.println("현재 속도: " + myCar.getSpeed());
if(!myCar.isStop()) {
myCar.setStop(true);
}
System.out.println("현재 속도: " + myCar.getSpeed());
}
}
// 현재 속도: 0
// 현재 속도: 60
// 현재 속도: 0
어노테이션
어노테이션은 메타데이터라고 볼 수 있다. 메타데이터란 애플리케이션이 처리해야 할 데이터가 아니라. 컴파일 과정과 실행 과정에서 코드를 어떻게 컴파일하고 처리할 것인지를 알려주는 정보이다.
컴일러에게 코드 문법 에러를 체크하도록 정보를 제공하는 대표적인 예는 @Override 어노테이션이다. 메소드가 오버라이드된 것임을 컴파일러에게 알려주어 컴파일러가 오버라이드 검사를 하도록 해준다. 정확히 오버라이드가 되지 않았다면 컴파일러는 에러를 발생시킨다.
'School > Java Programming' 카테고리의 다른 글
Java Programming - 중첩 클래스 & 중첩 인터페이스 (0) | 2022.03.17 |
---|---|
Java Programming - Interface (0) | 2022.03.10 |
Java Programming - Inheritance (0) | 2022.03.09 |
Java Programming - Array (0) | 2022.03.09 |
Java Programming - java의 동작 방식 (0) | 2022.03.03 |