Java - 클래스 맴버와 인스턴스 맴버

Yuri Lee·2020년 9월 28일
1

Java

목록 보기
10/23

클래스가 설계도라고 하면 인스턴스는 그 설계도에 따라 만들어진 구체적인 제품이다. 설계도는 하나만 있어도 되고, 그 설계도에 대한 제품들은 여러개일 수 있다. 인스턴스와 인스턴스를 구분하는 핵심적인 기준은 그 인스턴스 안에 들어가 있는 변수의 값에 따라서 각각의 인스턴스가 가지고 있는 상태가 다르다. 그리고 그 상태에 따라 어떤 행위를 하면 (=메소드를 실행하면) 다른 결과들을 돌려준다. 마치 함수가 있을 때 (메소드가 있을 때 ) 메소드에 전달한 인자가 무엇이느냐에 따라서 리턴값이 달라지는 것처럼 ..

멤버

멤버라는 개념을 통해 클래스와 인스턴스에 대한 의미와 존재에 대해서 자세히 알아보자. 맴버(member)는 영어로 구성원이라는 뜻이다. 객체도 구성원이 있는데 아래와 같다.

  • 변수
  • 메소드

객체를 만들기 위해서 우선 클래스를 정의하고, 클래스에 대한 인스턴스를 만들었다. 복습을 해보자. 이전 CalculatorDemo.java에서 left와 right 변수는 누구의 맴버일까? 인스턴스의 맴버다. 인스턴스를 만들어야 사용할 수 있었고, 인스턴스마다 서로 다른 값을 가지고 있기 때문이다. 그렇다면 클래스도 맴버를 가질 수 있다는 것일까? 아래 그림과 같이 클래스도 맴버를 가질 수 있다! 😲

클래스의 소유인 변수는 , 클래스의 멤버 변수는 모든 인스턴스에서 똑같은 값을 갖는다. 인스턴스를 생성하지 않아도 클래스에 직접 접근해서 (클래스에 소속되어있는 변수에 접근) 그 값을 사용할 수 있다는 것을 이해해야 한다.

클래스 변수

package com.yuri.javatutorials.classninstance;

class Calculator {
    static double PI = 3.14;
    int left, right;
 
    public void setOprands(int left, int right) {
        this.left = left;
        this.right = right;
    }
 
    public void sum() {
        System.out.println(this.left + this.right);
    }
 
    public void avg() {
        System.out.println((this.left + this.right) / 2);
    }
}
 
public class CalculatorDemo1 {
 
    public static void main(String[] args) {
 
        Calculator c1 = new Calculator();
        System.out.println(c1.PI);
 
        Calculator c2 = new Calculator();
        System.out.println(c2.PI);
 
        System.out.println(Calculator.PI);
 
    }
 
}

sum() 과 avg()는 우리가 만든 객체에 따라서 다른 결과를 갖게 된다. 객체마다 다른 변수값(상태값)을 갖기 때문이다. 하지만 지금 추가하려는 원주울 값은 객체마다 다른 값을 가질 필요가 없다.

모든 인스턴스가 공유하는 변수를 만들면 편리할 것이다. 그 역할을 하는 것이 클래스 변수이다. 인스턴스 변수는 인스턴스 마다 다른 값을 가지게 되는 변수이다. 하지만 클래스 변수는 클래스의 변수이기 때문에 그 클래스에 따라 만들어진 모든 인스턴스들은 그 클래스 변수가 가진 값을 자연스럽게 갖게 된다.

static double PI = 3.14;

변수 PI의 앞에 static이 붙었다. static을 맴버(변수,메소드) 앞에 붙이면 클래스의 맴버가 된다. 클래스 소속의 변수를 만드는 법을 알았으니까 이번에는 이것을 사용하는 법을 알아보자. 아래는 클래스 변수에 접근하는 방법 두가지를 보여준다.

// 인스턴스를 통해서 PI에 접근
System.out.println(c1.PI);
// 클래스를 통해서 PI에 접근
System.out.println(Calculator.PI);

두번째 방법은 객체 Calculator.java의 다른 기능(sum, avg)은 필요없고, 원주율만 필요할 때 클래스에 직접 접근하기 때문에 인스턴스를 생성할 필요가 없어진다.

클래스 변수는 변수의 변경사항을 모든 인스턴스에서 공유해야 할 때도 사용한다. 만약 계산을 할 때 특별한 값을 포함시켜야 한다면 어떻게 해야 할까? 아래 예제는 sum과 avg를 실행할 때마다 특정한 값을 연산에 포함시키고 싶을 때 시도해볼 수 있는 방법이다.

package com.yuri.javatutorials.classninstance;

class Calculator2 {
    static double PI = 3.14;
    // 클래스 변수인 base가 추가되었다.
    static int base = 0;
    int left, right;
 
    public void setOprands(int left, int right) {
        this.left = left;
        this.right = right;
    }
 
    public void sum() {
        // 더하기에 base의 값을 포함시킨다.
        System.out.println(this.left + this.right + base);
    }
 
    public void avg() {
        // 평균치에 base의 값을 포함시킨다.
        System.out.println((this.left + this.right + base) / 2);
    }
}
 
public class CalculatorDemo2 {
 
    public static void main(String[] args) {
 
        Calculator2 c1 = new Calculator2();
        c1.setOprands(10, 20);
        // 30 출력
        c1.sum();
 
        Calculator2 c2 = new Calculator2();
        c2.setOprands(20, 40);
        // 60 출력
        c2.sum();
 
        // 클래스 변수 base의 값을 10으로 지정했다.
        Calculator2.base = 10;
 
        // 40 출력
        c1.sum();
 
        // 70 출력
        c2.sum();
 
    }
 
}

30
60
40
70

결과는 다음과 같다. 클래스 변수 base의 값을 변경한 결과 모든 인스턴스의 base 값이 일제히 변경되었다.

클래스 변수의 용도를 정리해보면 아래와 같다.

  • 인스턴스에 따라서 변하지 않는 값이 필요한 경우 (위의 예에서는 PI)
    (이런 경우 final을 이용해서 상수로 선언하는 것이 바람직 하지만 final을 아직 배우지 않았기 때문에 언급하지 않았다)
  • 인스턴스를 생성할 필요가 없는 값을 클래스에 저장하고 싶은 경우
    (예를 들어 cal~.PI 라고 하게 되면 3.14를 얻을 수 있다. 만약 이 3.14라는 원주율만을 얻고 싶다면 굳이 new와 같은 이런 과정을 거쳐서, 메모리를 사용하면서 굳이 클래스를 인스턴스로 만들어서 할 값을 가져올 필요가 없다. c.PI이런 식으로.. 필요 없다~!🤭🤭 우리가 인스턴스를 만드는 이유는 c.sum, c.avg 라고 하는 형태의 메소드를 이용해서 인스턴스마다 다르게 설정되어있는 상태, 다시 말해서 인스턴스 별로 다르게 세팅되어있는 변수의 값에 따른 다른 처리 결과를 얻고 싶을 때 인스턴스를 만드는 것이다.)
  • 값의 변경 사항을 모든 인스턴스가 공유해야 하는 경우
    (base 예..)

클래스 메소드

클래스와 인스턴스는 멤버를 가져올 수 있다. 그리고 그 멤버는 변수와 메소드이다. 그럼 이번엔 메소드에 대해 알아보자.

package com.yuri.javatutorials.classninstance;

class Calculator3{
	  
    public static void sum(int left, int right){
        System.out.println(left+right);
    }
     
    public static void avg(int left, int right){
        System.out.println((left+right)/2);
    }
}
 
public class CalculatorDemo3 {
     
    public static void main(String[] args) {
        Calculator3.sum(10, 20);
        Calculator3.avg(10, 20);
         
        Calculator3.sum(20, 40);
        Calculator3.avg(20, 40);
    }
 
}

이번에는 인스턴스가 없고, 그냥 클래스에 직접 접근해서 메소드에 접근하고 있다. 만약 메소드가 인스턴스 변수를 참조하지 않는다면 클래스 메소드를 사용해서 불필요한 인스턴스의 생성을 막을 수 있다.

🌟🌟🌟 중요 🌟🌟🌟
1. 인스턴스 메소드(static 키워드를 갖고 있지 않는 메소드)는 클래스 맴버에 접근 할 수 있다.
2. 클래스 메소드는 인스턴스 맴버에 접근 할 수 없다.

인스턴스 변수는 인스턴스가 만들어지면서 생성되는데, 클래스 메소드는 인스턴스가 생성되기 전에 만들어지기 때문에 클래스 메소드가 인스턴스 맴버에 접근하는 것은 존재하지 않는 인스턴스 변수에 접근하는 것과 같다.

클래스라는 것은 언제나 메소드보다 먼저 존재하게 된다. 클래스라는 설계도를 만들고, 그의 구체적인 제품인 인스턴스를 나중에 만들기 때문이다. 클래스 메소드는 아직 생성되지 않는 인스턴스에 접근한다고 이해하면 쉽다. 인스턴스가 생성되었다는 것은 이미 클래스가 존재한다는 전제가 있기 때문에 접근할 수 있다.

package com.yuri.javatutorials.classninstance;

class C1{
    static int static_variable = 1;
    int instance_variable = 2;
    static void static_static(){
        System.out.println(static_variable);
    }
    static void static_instance(){
        // 클래스 메소드에서는 인스턴스 변수에 접근 할 수 없다. 
        //System.out.println(instance_variable);
    }
    void instance_static(){
        // 인스턴스 메소드에서는 클래스 변수에 접근 할 수 있다.
        System.out.println(static_variable);
    }
    void instance_instance(){        
        System.out.println(instance_variable);
    }
}
public class ClassMemberDemo {  
    public static void main(String[] args) {
        C1 c = new C1();
        // 인스턴스를 이용해서 정적 메소드에 접근 -> 성공
        // 인스턴스 메소드가 정적 변수에 접근 -> 성공
        c.static_static();
        // 인스턴스를 이용해서 정적 메소드에 접근 -> 성공
        // 정적 메소드가 인스턴스 변수에 접근 -> 실패
        c.static_instance();
        // 인스턴스를 이용해서 인스턴스 메소드에 접근 -> 성공
        // 인스턴스 메소드가 클래스 변수에 접근 -> 성공
        c.instance_static();
        // 인스턴스를 이용해서 인스턴스 메소드에 접근 -> 성공 
        // 인스턴스 메소드가 인스턴스 변수에 접근 -> 성공
        c.instance_instance();
        // 클래스를 이용해서 클래스 메소드에 접근 -> 성공
        // 클래스 메소드가 클래스 변수에 접근 -> 성공
        C1.static_static();
        // 클래스를 이용해서 클래스 메소드에 접근 -> 성공
        // 클래스 메소드가 인스턴스 변수에 접근 -> 실패
        C1.static_instance();
        // 클래스를 이용해서 인스턴스 메소드에 접근 -> 실패
        //C1.instance_static();
        // 클래스를 이용해서 인스턴스 메소드에 접근 -> 실패
        //C1.instance_instance();
    }
 
}

static int static_variable = 1;

static이 앞에 붙어있기 때문에 클래스 변수이다.

int instance_variable = 2;

static이 없다. 즉 인스턴스 변수라는 뜻이다.

각각의 인스턴스 메소드와 클래스 메소드에서 이것들을 호출하는 과정에서 문제가 생기는 것과 생기지 않는 것을 봐보자.

static void static_static(){
    System.out.println(static_variable);
}

이것은 클래스 메소드가 클래스 변수를 호출하고 있을 때의 이름이 static_static 이다. 클래스 메소드가 클래스 변수에 접근할 때 어떻게 되는가를 살펴본다.

static void static_instance(){
// 클래스 메소드에서는 인스턴스 변수에 접근 할 수 없다.
//System.out.println(instance_variable);
}

클래스 메소드에서 인스턴스 변수에 접근 할때 어떤 일이 생기는지..

void instance_static(){
// 인스턴스 메소드에서는 클래스 변수에 접근 할 수 있다.
System.out.println(static_variable);
}

인스턴스 메소드에서 클래스 변수에 접근할 때 가능한지...

용어

인스턴스 변수와 클래스 변수는 아래와 같이 부르기도 한다.

  • 인스턴스 변수 -> Non-Static Field
  • 클래스 변수 -> Static Field
    필드(field)라는 것은 클래스 전역에서 접근 할 수 있는 변수를 의미하는데 이에 대한 자세한 내용은 유효범위 수업에서 알아보겠다.

이 글은 생활코딩의 자바 강좌를 바탕으로 정리한 내용입니다.

profile
Step by step goes a long way ✨

0개의 댓글