Sonacube는 코드내에서 같은 문자열을 매번 선언해서 사용하는 것을 지양하라고 말합니다.
문자열이 코드내에서 반복될 경우 상수로 선언해서 그 변수를 사용하라고 가이드 합니다.
1. 왜 그래야 하는가?
답?
1. static final 로 선언하지 않으면 다른 사람이 만든 코드가 변수를 수정할 수 있기 때문
2. 상수가 한 번만 사용되어야 가시성이 좋아짐
2. 문자열을 직접 선언하는 것과 상수 변수를 사용하는 것은 어떤 점이 다를까?
String 은 클래스 선언에 따라 메모리에 저장되는 방식이 다르다.
String str1 = new String("abc"); // 인스턴스로 생성된다.
String str2 = "abc"; // 상수풀에 있는 문자열을 가르킨다.
JVM 에서는 아래와 같은 구로조 데이터를 저장한다.
String 은 선언 방식에 따라 아래와 같이 힙메모리 또는 상수 풀 (Runtime Constant Pool) 에 저장된다.
즉, 상수 풀에 저장된 데이터는 같은 값을 선언하면 같은 메모리를 공유하지만, 힙메모리에 생성된 데이터는 같은 값을 선언하더라도 서로 다른 인스턴스에 저장된다.
예시
public class StringTest {
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2); // 결과 : false
String str3 = "abc";
String str4 = "abc";
System.out.println(str3 == str4); // 결과 : true
}
}
3. 상수 변수에 사용하는 static, final 키워드는 무엇을 의미할까?
3-1. static
java에서 static은 클래스(정적) 멤버를 설정하는 키워드이다.
java 프로그램은 JVM에 의해 실행될 때 클래스 로딩을 하는데, 이때 클래스의 모든 멤버와 정보가 JVM의 class영역에 올라가게 되므로 static을 사용한 모든 멤버는 별도의 객체(인스턴스)의 생성없이 사용 가능하다.
하지만 가비지 컬렉터의 관리 밖에 있어서 항상 메모리에 상주해 있기 때문에, 프로젝트가 커지고 시스템이 오랜 시간동안 돌아가게 되면 시스템 운영 속도가 점차 느려질 수 있다.
또한, static 메서드가 아닌 곳에서는 static 요소들에 언제든 객체 생성 없이도 접근할 수 있지만, static 내부에서는 static 이 아닌 외부에 접근할 수 없다.
즉, static 메서드 안에서 외부에 접근하려는 요소 (field, method) 들은 모두 static 이어야 한다.
이러한 속성을 고려하여, static 은 여러번 참조해야 하는 상수나 유틸리티 같은 경우에만 선언하는 것이 좋다.
static 키워드를 코드로 사용하는 예시
public class Main {
static int testVal = 1; // static 변수
int val = 3; // 인스턴스 변수
public static void testStatic() { // static 메서드
System.out.println("test");
}
public static void main(String[] args) {
// System.out.pringln(val); // static 메서드 안에서는 외부의 static 멤버에만 접근 가능
System.out.println(testVal);
System.out.println(Test.a);
Test.print();
testStatic();
Test test1 = new Test();
System.out.println(test1.b);
Test test2 = new Test();
System.out.println(test2.b);
Test test3 = new Test();
System.out.println(test3.a);
System.out.println(test3.b);
}
}
class Test {
static int a = 0;
int b = 0;
Test() {
a++;
b++;
}
static void print() {
System.out.println("static 메소드 입니다.");
}
}
위 예를 보면 Test 클래스에 있는 변수 a 와 print 메서드를 static 키워드를 붙여 선언하였다.
따라서 Main 클래스에서 Test 클래스의 변수 a 와 print 메서드는 객체 생성 없이도 접근이 가능하다.
하지만 static 메소드인 main 함수 안에서는 밖에 선언되어 있는 val 변수를 사용할 수 없다.
static 메서드 안에서는 static 이 아닌 외부에 접근이 불가능 하기 때문이다.
반면, 그 아래의 Main 클래스의 static 변수인 testVal static 메서드인 testStatic, Test 클래스의 static 변수인 a, static 메서드인 print 는 오류없이 정상적으로 접근가능 하다.
또한 Test 객체를 3개를 생성하면서 생성자로 static 변수 a 와 인스턴스 변수 b 를 1씩 증가를 시켜주고 있다.
결과를 확인해보면 Test 객체 생성 후 각각 출력한 b 의 값은 모두 1 이고 a 는 3 인 것을 확인할 수 있다.
그 이유는 static 변수 a 는 객체를 생성하기 전부터 0을 가지고 있다가 객체 생성자에 의해 1 씩 3 번 증가 되었고, 인스턴스 변수인 b 는 각 인스턴스가 생성될때 0 으로 초기화 됐다가 생성자에 의해 1 씩 증가하였기 때문이다.
한 클래스의 static 변수는 클래스 영역에 저장되어 있어 어떤 인스턴스든 모두 똑같은 값을 가진다는 것을 꼭 기억하자!
[출처] [Java]static|작성자 flzl2008
3-2. final
상수, 메서드, 클래스 3가지 경우에 같이 사용할 수 있다.
1) 상수 정의에 사용
용도 : 상수에 언제든 값을 한 번 저장하고, 다음에 다시 바꾸지 않을 때 사용
final int a;
// final int a = 1; // 상수 선언과 함께 값을 정의해도 된다.
Scanner s = new Scanner(System.in);
a = s.nextInt();
// a = 10; // 이 경우 오류 발생 (값을 변경할 수 없다)
System.out.println(a);
위 예제의 결과는 사용자가 입력한 값이다.
final int a 를 선언만 한 뒤, 사용자입력을 받아 초기화 할 수도 있고, 아래 주석처럼 선언과 함께 값을 초기화해도 된다.
상수 정의 후 만약 주석처리한 'a = 10' 을 주석 해제하여 실행하면 상수를 변경하려 하므로 컴파일 에러가 난다.
2) 메서드에 사용
용도 : 오버라이딩 (재정의) 을 못하게 만든다.
class Test {
public final void test2() {
// 내용 정의
}
}
public class Main extends Test {
// public test2(){} compile error
// final method 는 오버라이딩 못함
}
위 예제에서 보는 바와 같이 메서드에 final 키워드를 사용하여 오버라이딩이 불가하다.
3) 클래스에 사용
용도 : 상속을 못하게 만든다.
final class Test {
int test;
}
// class Main extends Child{} // final 클래스는 상속할 수 없다
클래스에 final 키워드는 상속을 못하게 할 때 사용된다.
따라서 final 키워드를 사용한 Test 클래스는 객체를 생성할 수 없다.
3-3. static final
static 은 클래스 변수이다.
그러므로 static final 은 객체 (인스턴스) 가 아닌, 클래스에 존재하는 단 하나의 상수이다.
즉, 객체마다 값이 바뀌는 것이 아닌, 클래스에 존재하는 상수이므로, 선언과 동시에 초기화를 해 주어야 하는 클래스 상수이다.
public class test {
static final int a = 1; // 선언과 동시에 초기화
public static void main (String args[]) {
System.out.println(a);
}
}
4. java 에서 변수는 위치에 따라서 이름이 다르다. 위치에 따른 변수의 이름과 그 차이점은?
변수는 선언 위치에 따라 전역변수와 지역변수로 나뉜다.
전역변수 | 전체에서 어디서든 호출하면 사용할 수 있는 변수 1. 객체변수 (인스턴스 변수) 클래스 영역에서 선언되며, 클래스의 객체를 생성할 때 만들어짐 즉, 객체화를 시켜서 호출해야지만 사용 가능 → 객체화시킬 때마다 서로 다른 저장공간을 가진다 2. 클래스 변수 (static 변수) 객체화를 시키지 않고도 사용이 가능 → 여러번 객체화시켜도 공통적인 저장공간을 가진다 |
지역변수 | 특정한 구역 ({ }) 안에서 생성되어 그 지역에만 사용할 수 있는 변수 |
예시
class Exampla01 {
int global_int; // 전역변수 (객체변수) : 같은 클래스에서 호출 가능
static int gloval_static_int; // 전역변수 (클래스변수) : 다른 클래스에서도 호출이 가능
void method() {
int local_int = 0; // 지역변수 : { } 안에서 생성, { } 를 벗어나면 바로 삭제
}
}