프로그래밍 언어/Java문법 - 객체지향

5. Java 객체지향 문법(5) - 클래스 변수와 클래스 메소드

아사비치즈스틱 2022. 10. 26. 23:48

 

클래스 변수


🤔 클래스 변수란?

  • 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 (참고 자료)


  1. 윤성우의 열혈 자바