본문 바로가기

개발 이야기/Android (안드로이드)

Java 에서는 static을 C 와 다르게 사용하자

320x100

C 를 개발하다가, Java 전향 시 객제지향언어 문법을 확인하지 않고 사용하면 생기는 문제 중에 하나가 메모리 누수이다.

뜬금없이 뭔 소리냐 하면 C에서는 DB나 file에 쓰지 않고도 앱 실행 중에 특정 값들이 지속적인 값을 유지하여 참고할 수 있다. static 지시어를 이용해서 변수에다 값을 저장해서 각 함수에서 편하게 flag 로 활용해서 사용하고 있다. 

static flag 와 extern 으로 가지고 노는 것을 플래그질이라고 말하기도 하는 데....

Java 에서는  static 은 클래스에 선언 시 컴파일러의 의해 즉시 메모리에 바로 로드되어진다. 이러면 인스턴스화 과정을 거치지 않고 사용이 가능해진다. 

 

Java 에서는 플래그질에 static을 사용하는 것은 자제하고,  값의 저장소 개념이 아니라 하나의 클래스가 다중 인스턴화가 만들어지는 상황에서 각 인스턴스들이 같은 값을 가지고 있길 바랄 때 사용하는 것이 바람직하다.

 

예를 들자면 장치를 On/Off 시키는 클래스를 설계하고 전체소등 기능을 구현한다고 가정해보자.간단하게 추상화하면 이런 그림이 될 것이다.

클래스 추상화

 

 

Clone 은 장치들을 꺼고 키는 단순한 메소드를 가진 클래스의 선언이다.

Clone.java

public class Clone {
    public static boolean allOff = false;

    public int turnOff() {return 0;}

    public int turnOn() {return  0;}

    public void setAllOff(boolean value) {
        allOff = value;
    }

    public boolean getAllOff() {
        return allOff;
    }
}

 

 

자식 클래스들은 아래와 같이 상속하여 구현한다.

LedConLedControl.java

import android.util.Log;

public class LedControl extends Clone {
    private static final String TAG = "LedControl";
    public boolean bExit = false;

    private void printState() {
        Log.d(TAG, "printState: = " + getAllOff());
        if (getAllOff()) {
            Log.d(TAG, "printState: 모든 LED가 종료됩니다.");
        }
    }

    public LedControl() {
        Thread thread = new Thread(()->{
            Log.d(TAG, "LedControl 시작합니다.");
            while (!bExit) {
                if (getAllOff()) {
                    printState();
                    bExit = true;
                }

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();

    }
}

 

 

LampControl.java

import android.util.Log;

public class LampControl extends Clone {
    private static final String TAG = "LedControl";
    public boolean bExit = false;

    private void printState() {
        Log.d(TAG, "printState: = " + getAllOff());
        if (getAllOff()) {
            Log.d(TAG, "printState: 모든 LEMP 종료됩니다.");
        }
    }

    public LampControl() {
        Thread thread = new Thread(()->{
            Log.d(TAG, "LampControl 시작합니다.");
            while (!bExit) {
                if (getAllOff()) {
                    printState();
                    bExit = true;
                }

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }
}

 

 

 

 

 

 

각각 클래스 인스턴스을 만들어 각 클래스들은 Lamp 와 LED를 각각 제어할 것이다. 

만일 전체장치 Off 기능(일괄소등) 을 만들어야 한다면,  기능을 구현하기 위해 여러분은 어떻게 할 것인가? 부지런하게 각 클래스들을 Array에 일일이 카피해서 turnOff() 메소드를 일일이 호출할 것인가? 그렇다면 여러분은 성실한 개발자이다. 

 

 

 

게으른 나는 그냥 꼼수와 설계로 마무리 할 것이다. 

Clone 부모클래스에서 staic 필드로 선언한 부분을 기억하는가?  

public static boolean allOff = false;

 

일괄소등 버튼을 만든 후에 onClick이벤트 영역에 아래의 코딩 한줄이면 모든 인스턴스들이 종료기능을 수행한다. 간단하지 않는가?  

Clone.allOff = true

 

예제들을 실행하면 결과가 원하는 데로 나오는 것을 확인할 수 있다.

 

 

 

 

클래스 내 static 영역은 해당 클래스의 모든 인스턴스들이 값을 공유한다. 즉 한 인스턴스에서 값이 변경되면 전체 인스턴스의 값이 자동으로 변경되는 것이다. 이런 경우에 이용을 추천하고 static을 생명주기를 가진 객체의 인스턴스 선언에 사용하지 말길 바란다. 구글에서도 경고를 했던 상황이니 꼭 피하길 바람 (생명주기 작동오류 생겨서 동작 안정성 보장못함)

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    Button mBtnStart, mBtnAllClose;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnStart = findViewById(R.id.button1);
        mBtnStart.setOnClickListener(v -> {
            LampControl lampControl = new LampControl();
            LedControl ledControl = new LedControl();
        });
        mBtnAllClose = findViewById(R.id.button2);
        mBtnAllClose.setOnClickListener(v -> Clone.allOff = true);
    }
}

 

언어의 특성을 알고쓰면 코딩량이 줄어드는 개꿀같은 일이 생김을 잊지 말고 심심할때 자료 검색하는 시간 가져보시길 ~~ 

반응형