5. Java 객체지향 문법(5) - 클래스 변수와 클래스 메소드
클래스 변수
🤔 클래스 변수란?
- static으로 선언된 변수로써, 선언된 클래스의 모든 인스턴스(객체)가 공유하는 변수를 의미합니다.
클래스 변수 선언
- 클래스 내에 선언된 변수 앞에 static 선언을 붙이면 인스턴스 변수가 아닌 클래스 변수가 됩니다.
static 클래스변수명
다음의 예를 통해 자세히 이해 해봅시다.
💻 예제 코드
class InstCnt{
static int instNum = 0;
//생성자
InstCnt() {
instNum++; //static으로 선언된 변수의 값을 증가
System.out.println("인스턴스 생성: " + instNum);
}
}
public class ClassVar {
public static void main(String[]args){
//객체 생성
InsCnt incnt1 = new InsCnt(); //instName = 1
InsCnt incnt2 = new InsCnt(); //instName = 2
InsCnt incnt3 = new InsCnt(); //instName = 3
}
}
📸 출력 결과
인스턴스 생성: 1
인스턴스 생성: 2
인스턴스 생성: 3
👩🏻🏫 설명
- 위의 코드를 살펴보면 다음과 같은 사실을 알 수 있습니다.
- 생성자에서 객체를 생성할 때마다 instNum의 값을 하나씩 증가시킨 다음 결과를 출력하도록 했습니다.
- 코드와 출력 결과를 살펴보면 객체를 생성할 때마다 instNum이 하나씩 증가함을 알 수 있습니다.
→ 이로써 클래스 변수 instNum은 클래스 변수가 포함된 객체를 선언할 때 값을 공유하는 변수임을 알 수 있습니다.
→ 왜냐하면 클래스 변수는 객체 내에 존재하는 변수가 아닌 ‘어떠한 객체에 속하지 않은 채 메모리 공간에 존재하는 변수’ 이기 때문입니다.
- 클래스 변수가 포함된 클래스의 객체들은 클래스 변수에 접근할 수 있는 권한이 있어 접근할 수 있습니다.
- 또한 클래스 변수는 변수임으로 ‘접근 수준 지시자’를 적용받습니다.
클래스 변수의 접근 방법
- 클래스 변수에 접근하는 방법은 다음과 같이 크게 2가지 나누어집니다.
1. 클래스 내부에서 접근 = 클래스 변수의 이름을 통해 직접 접근이 가능합니다.
2. 클래스 외부에서의 접근 = 클래스명 또는 객체 선언 후 이름을 통해 접근이 가능합니다.
- 이와 관련하여 다음의 예를 통해 살펴보겠습니다.
💻 예제 코드
class AccessWay{
static int num = 0;
//생성자
AccessWay(){
incrCnt();
}
//메소드
void incrCnt(); {
num ++; //1. 클래스 내부에서 변수의 이름을 통해 접근
}
}
public class ClassVarAccess{
public static void main(String[] args) {
AccessWay way = new AccessWay();
way.num++; //2. 객체 생성 후 인스턴스의 이름을 통해 접근
AccessWay.num++; //2.클래스 이름을 통해 접근
System.out.pritnln("num = " + AccessWay.num);
}
}
📸 출력 결과
num = 3
- 위의 코드에서와 같이 클래스 내부에서는 클래스 변수의 이름을 통해 접근하며 클래스 외부에서는 클래스명 또는 객체 생성 후 인스턴스의 이름을 통해서 접근이 가능합니다.
클래스 변수의 초기화되는 시점
- 앞선 설명에서 클래스 변수는 인스턴스의 생성과 상관이 없다고 설명하였습니다.
❓ 그렇다면 클래스 변수는 언제 초기화되는가?
🔥 클래스 변수는 해당 클래스 변수가 포함된 클래스의 정보를 가상머신이 읽는 순간 메모리 공간에 할당되고 초기화됩니다.
- 아래의 예를 통해 살펴봅시다.
💻 예제 코드
class InstCnt{
static int instNum = 100;
//생성자
InstCnt() {
instNum++;
System.out.println("인스턴스 생성: " + instNum);
}
}
public class OnlyClassNoInstance{
public static void main(String[] args){
InstCnt.instNum -= 15; //인스턴스 생성 없이 클래스 변수 instNum에 접근
System.out.println(InstCnt.instNum);
}
}
📸 출력 결과
85
- 이를 통해 클래스 변수는 객체가 생성 되기 이전에 메모리 공간에 존재함을 알 수 있습니다.
❓ 그렇다면 클래스 변수는 언제 사용하는 것이 좋을까?
🔥 선언되는 객체간에 공통으로 가져야하는 값을 클래스 변수를 선언합니다.
- 다음의 예를 살펴보겠습니다.
💻 예제 코드
class Circle{
static final double PI = 3.1415; //클래스 상수 선언
private double radius;
//생성자
Circle(double rad) {
radious = rad;
}
void showPerimeter(){
double peri = (radius* 2)*PI;
System.out.println("둘레: " + peri);
}
void showArea(){
double area = (radius * radius) * PI;
System.out.println("넓이: " +area);
}
}
public class CircleConstPI {
public static void main(String[]args){
Circle c1 = new Circle(1.2);
Circle c2 = new Circle(1.5);
c1.showPerimeter();
c1.showArea();
c2.showPerimeter();
c2.showArea();
}
}
📸 출력 결과
둘레: 7.5396
넓이: 4.52376
둘레: 9.4245
넓이: 7.0683750000000005
- 위의 예를 통해 원의 원주율을 클래스 변수로 선언하여 c1,과 c2의 원의 둘레와 넓이를 구할 수 있었습니다.
- 이처럼 선언되는 객체 간 공통으로 가져야 하는 값에 대하여 클래스 변수로 선언함으로써 사용합니다.
클래스 메소드
🤔 클래스 메소드란?
- static으로 선언된 메소드로써 선언된 클래스의 모든 인스턴스(객체)가 공유하는 메소드를 의미합니다.
- 클래스 메소드 또한 클래스 변수와 성격이 유사합니다.
- 접근 방법도 동일하며 객체 생성 이전부터 호출이 가능하고 또한 어느 객체에도 속하지 않는 메소드입니다.
클래스 메소드의 선언
- 클래스 내에 선언된 메소드 앞에 static 선언을 붙이면 클래스 메소드가 됩니다.
static 반환형 클래스메소드명 (매개변수1, 매개변수2)
- 다음의 코드를 통해 자세히 살펴봅시다.
💻 예제 코드
class NumberPrinter{
private int myNum = 0;
static void showInt(int n){ //클래스 메소드 (static 메소드)
System.out.println(n);
}
static void showDouble(double n){ //클래스 메소드(static 메소드)
System.out.println(n);
}
void setMyNumber(int n){ //인스턴스 메소드
myNum = n;
}
void showMyNumber(){ //인스턴스 메소드
showInt(myNum);
}
}
public class ClassMethod {
public static void main(String[]args){
NumberPrinter.showInt(20); //클래스 이름을 통한 클래스 메소드 호출(추천)
NumberPrinter np = new NumberPrinter(); //객체 생성(비추천)
np.showDouble(3.15); //객체 이름을 통한 메소드 호출
np.setMyNumber(75);
np.showMyNumber();
}
}
📸 출력 결과
20
3.15
75
👩🏻🏫 설명
- 코드에서 다음 부분을 자세히 살펴봅시다.
NumberPrinter.showInt(20);
- 해당 부분에서 객체 생성 없이 클래스 메소드를 사용할 수 있습니다.
이처럼 클래스 메소드는 객체를 선언하지 않아도 사용할 수 있습니다.
(클래스 메소드는 어떠한 객체에 속하지 않아 사용할 수 있습니다.)
❓ 그렇다면 클래스 변수는 언제 사용하는 것이 좋을까?
- 아래의 코드를 살펴보겠습니다.
💻 예제 코드
class SimpleCalculator{
static final double PI = 3.1415;
double add(double n1, double n2){
return n1 + n2;
}
double min(double n1, double n2){
return n1 - n2;
}
double calCircleArea(double r){
return PI * r * r;
}
double calCirclePeri(double r){
return PI * (r*2);
}
}
public class UseCalculator {
public static void main(String []args){
SimpleCalculator sc = new SimpleCalculator();
System.out.println(" 3 + 4 = " + sc.add(3,4));
System.out.println("반지름 2.2, 원의 넓이: " + sc.calCircleArea(2.2)+ " \n");
System.out.println("15 - 7 = " + sc.min(15,7));
System.out.println("반지름 5,0, 원의 넓이: " + sc.calCirclePeri(5.0));
}
}
📸 출력 결과
3 + 4 = 7.0
반지름 2.2, 원의 넓이: 15.204860000000002
15 - 7 = 8.0
반지름 5,0, 원의 넓이: 31.415000000000003
👩🏻🏫 설정
- 위의 코드에서 SimpleCalculator에 정의된 메소드가 갖는 특성 두가지가 있습니다.
1. 모두 클래스 외부의 기능을 제공하기 위한 메소드이다.
2. 모두 인스턴스 변수의 값을 참조하거나 수정하지 않는다. (매개변수로 받는 값들을 수정하지 않는다.)
- 따라서 SimpleCalculator에 정의된 메소드들은 생성된 객체에 속할 필요가 없습니다.
-> 이에 클래스 메소드를 사용하여 수정하는 것이 더 낫습니다.
🔥 클래스 메소드를 사용하여 코드를 변경한 예
💻 예제 코드
class SC{
static final double PI = 3.1415;
//모두 클래스 메소드 사용
static double add(double n1, double n2){
return n1 + n2;
}
static double min(double n1, double n2){
return n1 - n2;
}
static double calCircleArea(double r){
return PI * r * r;
}
static double calCirclePeri(double r){
return PI * (r*2);
}
}
public class UseCalculaotrCMVer {
public static void main(String[]args){
System.out.println("3 + 4 = " + SC.add(3,4));
System.out.println("반지름 2,2,원의 넓이: " + SC.calCircleArea(2.2) + "\n");
System.out.println("15 -7 = " + SC.min(15,7));
System.out.println("반지름 5,0, 원의 둘레: " + SC.calCirclePeri(5.0));
}
}
📸 출력 결과
3 + 4 = 7.0
반지름 2.2, 원의 넓이: 15.204860000000002
15 - 7 = 8.0
반지름 5,0, 원의 넓이: 31.415000000000003
- 위처럼 클래스 메소드를 생성하면 불필요한 객체의 생성 없이 메소드를 사용할 수 있습니다.
❓ 클래스 메소드에서는 같은 클래스에 선언된 인스턴스 변수에 접근이 가능할까?
- 클래스 메소드에서는 같은 클래스에 선언된 인스턴스 변수에 접근이 불가합니다.
- 아래의 코드를 살펴 봅시다.
💻 예제 코드
class AAA{
int numer = 0;
static void addNumber(int n {
number += n; //클래스 메소드에서 같은 클래스 안에 있는 인스턴스 변수에 접근이 가능할까?
}
}
- 위의 코드에서 인스턴스 변수 numer를 클래스 변수 addNumber가 접근하려고 합니다.
→ 그러나 클래스 변수는 어느 인스턴스에 속하지 않으므로 인스턴스 변수에 대한 접근이 불가합니다.
→ 또한 클래스 메소드는 인스턴스 메소드의 호출도 불가합니다
👩🏻🏫 설명
- 인스턴스 변수는 인스턴스에 속합니다. 이는 인스턴스가 생성되어야지 메모리 공간에 존재함을 의미합니다.
-> 반면 클래스 메소드는 인스턴스(객체) 생성 이전 부터 호출이 가능하기 때문입니다.
💡 따라서 클래스 메소드는 같은 클래스에 정의되어 있는 다른 클래스 메소드나 성격이 비슷한 클래스 변수에는 접근합니다.
올바른 방식으로 수정한 결과
class AAA{
static numer = 0;
static void showNum(){
System.out.print(number); //클래스 변수에 접근이 가능
}
static add Number(int n) {
number += n; //클래스 변수로 접근 가능
showNum(); //클래스 메소드를 호출 가능하다.
}
}
또 다른 용도의 Static 선언
- Static 선언은 클래스 변수와 클래스 메소드의 선언 이외의 용도로 Static 초기화 블록으로 사용할 수 있습니다.
💻 예제 코드
import java.time.LocalDate;
public class DateOfExecution {
static String data; //프로그램의 실행 날짜를 저장하기 위한 변수
//클래스 로딩 시 단 한 번 실행되는 영역
static {
LocalDate nDate = LocalDate.now();
data = nDate.toString();
}
public static void main(String[]args){
System.out.println(data);
}
}
📸 출력 결과
2022 - 10 - 28
👩🏻🏫 설명
- 변수 date가 인스턴스 변수라면, 위의 두 문장을 생성자에 넣으면 되지만, 클래스 변수이므로 생성자는 적절하지 않습니다.
-> 이러한 상황을 위해 자바는 static 초기화 블록이라는 것을 제공합니다.
- static 초기화 블록은 클래스 변수와 마찬가지로, 가상머신이 클래스의 정보를 읽어들일 때 실행됩니다.
-> 따라서 static 초기화 블록을 사용하면, 클래스 변수를 선언과 동시에 초기화할 수 있습니다.
📒 Reference (참고 자료)
- 윤성우의 열혈 자바