package org.opentutorials.javatutorials.scope;
public class ScopeDemo {
static void a() {
int i = 0;
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
a();
System.out.println(i);
/*
0
1
2
3
4
*/
}
}
}
package org.opentutorials.javatutorials.scope;
public class ScopeDemo2 {
static int i; //4행 , 선언 , 전역변수
static void a() {
i = 0; // 7행, 할당 , 지역변수
}
public static void main(String[] args) {
for (i = 0; i < 5; i++) { // 11행, 할당 , 지역 변수
a();
System.out.println(i); // 무한 0 출력
}
}
}
만약 위의 코드를 아래와 같이 바꾸면 위의 문제가 사라질 것이다.
우선 메소드만 놓고 봤을 때 메소드 안에서 선언한 변수는 그 메소드가 실행될 때 만들어지고, 그 메소드가 종료되면 삭제된다. 만약 클래스 아래의 변수와 메소드 아래의 변수가 같은 이름을 가지고 있다면 메소드 아래의 변수가 우선하게 된다. 메소드 내의 변수가 존재하지 않을 때 클래스 아래의 변수를 사용하게 되는 것이다.
즉 클래스 아래에서 선언된 변수는 클래스 전역에 영향을 미치지만 메소드 내에서 선언된 변수는 클래스 아래에서 선언된 변수보다 우선순위가 높다고 할 수 있다. 클래스 전역에서 접근 할 수 있는 변수를 전역변수, 메소드 내에서만 접근 할 수 있는 변수를 지역변수라고 한다.
package org.opentutorials.javatutorials.scope;
public class ScopeDemo3 {
static int i;
static void a() {
int i = 0; // 아니면 for문에 int i = 0으로 바꿔도 동일한 결과 출력 (for문의 중괄호 안에서 유효)
}
public static void main(String[] args) {
for (i = 0; i < 5; i++) {
a();
System.out.println(i);
/*
0
1
2
3
4
*/
}
}
}
package org.opentutorials.javatutorials.scope;
public class ScopeDemo4 {
static void a(){
String title = "coding everybody";
}
public static void main(String[] args) {
a();
//System.out.println(title);
}
}
package org.opentutorials.javatutorials.scope;
public class ScopeDemo5 {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
// System.out.println(i);
}
}
결과는 5다. 위의 예제는 메소드 a가 메소드 b를 호출하고 있는데 메소드 b에는 변수 i의 값이 존재하지 않는다. 따라서 메소드 내(b)에서 지역변수가 존재하지 않기 때문에 그 메소드가 소속된 클래스의 전역변수를 사용하게 된다.
이러한 방식을 정적 스코프(static scope) 혹은 렉시컬 스코프(lexical scope)라고도 부른다. 즉 사용되는 시점에서의 유효범위(메소드 a의 i)를 사용하는 것이 아니라 정의된 시점에서의 유효범위(i = 5)를 사용하는 것이다.
동적 스코프라는 것도 있다. 만약 메소드 b의 결과가 10이라면 메소드 b는 메소드 a의 유효범위에 소속된 것이라고 할 수 있다. 하지만 자바는 동적 스코프를 채택하지 않고 있다. 대부분의 현대적인 언어들이 정적 스코프 방식을 선택하고 있다.
package org.opentutorials.javatutorials.scope;
public class ScopeDemo6 {
static int i = 5;
static void a() {
int i = 10;
b();
}
static void b() {
// int i = 30; // 만약 이렇게 넣어주면 i=30출력됨
System.out.println(i);
}
public static void main(String[] args) {
int i = 1;
a();
}
}
package org.opentutorials.javatutorials.scope;
public class ScopeDemo6 {
// static int i = 5; (삭제/0
static void a() {
int i = 10;
b();
}
static void b() {
// int i = 30; (삭제)
System.out.println(i);
}
public static void main(String[] args) {
int i = 1;
a();
}
}
package org.opentutorials.javatutorials.scope;
class C2 {
int v = 10;
void m() {
int v = 20;
System.out.println(v);
}
}
public class ScopeDemo8 {
public static void main(String[] args) {
C2 c1 = new C2();
c1.m();
}
}
package org.opentutorials.javatutorials.scope;
class C3 {
int v = 10;
void m() {
int v = 20;
System.out.println(v); // Result : 20
System.out.println(this.v); // Result : 10
}
}
public class ScopeDemo9 {
public static void main(String[] args) {
C3 c1 = new C3();
c1.m();
}
}
유효범위란 변수를 전역변수, 지역변수 나눠서 좀 더 관리하기 편리하도록 한 것이다. 객체라는 개념이 존재하지 않는 절차지향 프로그래밍에서는 모든 메소드에서 접근이 가능한 변수의 사용을 죄악시하는 경향이 있다. 전역적인 사용의 효용이 분명한 데이터에 한해서 제한적으로 전역변수를 사용하도록 하고 있는 것이다. 객체지향 프로그래밍은 바로 이런 문제를 극복하기 위한 노력이라도고 볼 수 있다. 즉 연관된 변수와 메소드를 그룹핑 할 수 있도록 함으로서 좀 더 마음놓고 객체 안에서 전역변수를 사용할 수 있도록 한 것이다.
어떤 메소드가 전역변수를 사용하고 있다는 것은 그 메소드는 그 전역변수에 의존한다는 의미다. 전역변수에 의존한다는 것은 이 메소드가 다른 완제품의 부품으로서 사용될 수 없다는 의미다. 객체지향 덕분에 좀 더 안심하고 전역변수를 사용하게 되었지만, 객체도 크기가 커지면 관리의 이슈가 생겨난다. 객체지향 프로그래밍에서도 가급적이면 전역변수의 사용을 자제하는 것이 좋고, 동시에 단일 객체가 너무 비대해지지 않도록 적절하게 규모를 쪼개는 것도 중요하다.