변수 선언과 대입
변수를 선언하고 첫 값을 넣고 다른 값으로 바꾸는 과정을 실행 순서대로 따라가며 회원 한 명의 정보를 변수로 표현합니다.
1장에서는 문자열을 그대로 출력하고, 명령 뒤에 적은 값을 args로 받아 화면에 보여 주었습니다. 값을 한 번 쓰고 끝낼 때는 그것으로 충분합니다. 하지만 회원가입 프로그램은 같은 이메일을 프로필에도, 환영 문구에도, 기록에도 씁니다. 같은 값을 여러 곳에서 다시 쓰려면 그 값에 이름을 붙여 두어야 합니다.
이렇게 값에 붙인 이름을 변수(variable)라고 합니다. 이 글에서는 변수를 만들고(선언), 처음 값을 넣고(초기화), 다른 값으로 바꾸는(재대입) 과정을 한 줄씩 따라갑니다. 마지막에는 가입할 회원 한 명의 이메일, 나이, 활성 여부를 변수로 표현합니다.
같은 값을 여러 번 적으면 생기는 일
이번 장부터는 작업 폴더에 lab 폴더를 하나 더 둡니다. lab에는 오류를 일부러 내 보거나 한 가지 동작만 확인하는 짧은 실험 파일을 두고, src에는 완성된 프로그램을 둡니다. java-board 폴더에서 PowerShell이라면 New-Item -ItemType Directory -Force lab | Out-Null, macOS·Linux라면 mkdir -p lab으로 폴더를 만드세요.
첫 실험은 회원의 이메일을 바꾸다가 한 곳을 빠뜨린 프로그램입니다. 아래 코드를 lab/RepeatedLiteral.java로 저장합니다.
public final class RepeatedLiteral {
public static void main(String[] args) {
System.out.println("[email protected]");
System.out.println("[email protected]");
System.out.println("[email protected]");
}
}그다음 1장에서처럼 컴파일하고 실행합니다. 이 장의 다른 파일도 경로와 클래스 이름만 바꾸어 같은 방식으로 실행하면 됩니다.
javac -d out lab/RepeatedLiteral.java
java -cp out RepeatedLiteral출력 세 줄은 모두 문법적으로 올바른 문장이라서 컴파일러는 아무 문제도 알려 주지 않습니다. 그런데 프로필과 감사 기록(audit)에는 새 이메일이 나오고, 환영 문구에만 옛 이메일이 남았습니다. 사람은 세 문자열이 모두 '이 회원의 이메일'이라는 것을 알지만, 소스 코드에는 그 관계가 어디에도 적혀 있지 않기 때문입니다. 이메일을 바꿀 때 세 곳을 빠짐없이 찾아 고치는 일은 전적으로 사람의 기억에 달려 있습니다.
같은 값에 이름을 붙이면 이 관계를 코드에 적을 수 있습니다. 다음 프로그램은 이메일을 email이라는 변수에 한 번만 넣고, 세 출력문이 모두 그 변수를 읽게 합니다.
public final class NamedValue {
public static void main(String[] args) {
String email = "[email protected]";
System.out.println("profile=" + email);
System.out.println("welcome=" + email);
System.out.println("audit=" + email);
}
}"profile=" + email은 문자열 profile= 뒤에 email에 들어 있는 값을 이어 붙입니다. 이제 이메일을 바꾸려면 String email = ... 한 줄만 고치면 되고, 세 출력문이 같은 이름을 읽으므로 한 곳만 바뀌고 나머지가 남는 일이 생기지 않습니다. 즉 변수는 글자 수를 줄이는 장치이기 전에, '이 값들은 같은 것'이라는 사실을 코드에 적어 두는 장치입니다.
선언과 초기화
NamedValue의 String email = "[email protected]";에는 사실 두 가지 일이 함께 들어 있습니다. 둘을 나누어 쓰면 다음과 같습니다.
String email; // 선언: String 값을 담을 변수 email을 만든다
email = "[email protected]"; // 초기화: 첫 값을 넣는다첫 줄은 변수를 선언(declaration)합니다. 앞의 String은 이 변수에 담을 값의 종류, 즉 타입(type)이고 뒤의 email이 변수 이름입니다. 선언만 한 변수는 이름은 있지만 아직 읽을 수 있는 값이 없습니다. 둘째 줄처럼 변수에 처음으로 값을 넣는 것을 초기화(initialization)라고 하며, String email = "[email protected]";은 이 두 단계를 한 문장으로 쓴 것입니다. 타입에는 String 말고도 정수를 담는 int, 참과 거짓을 담는 boolean 등이 있고, 다음 글에서 하나씩 살펴봅니다.
그렇다면 초기화하지 않은 변수를 읽으면 어떻게 될까요? Java에서는 엉뚱한 값이 출력되는 것이 아니라, 프로그램이 아예 컴파일되지 않습니다.
public final class UninitializedLocal {
public static void main(String[] args) {
String email;
System.out.println(email);
}
}lab\UninitializedLocal.java:4: error: variable email might not have been initialized
System.out.println(email);
^
1 error메시지는 '4번째 줄에서 읽는 email이 초기화되지 않았을 수 있다'는 뜻입니다. macOS·Linux에서는 경로가 lab/UninitializedLocal.java처럼 /로 표시됩니다. 이렇게 컴파일 오류를 확인하는 실험은 오류 메시지를 읽는 것으로 끝냅니다. 컴파일에 실패하면 새 .class 파일이 만들어지지 않으므로, 이어서 java를 실행했는데 무언가 출력된다면 out에 남아 있던 예전 .class가 실행된 것입니다.
이 검사는 프로그램을 실행하기 전에 javac가 합니다. javac는 변수를 읽는 곳마다 그곳에 이르는 모든 실행 경로를 살펴, 어느 경로로 오든 값이 먼저 들어갔는지 확인합니다. 덕분에 초기화를 잊은 실수는 프로그램을 실행해 보기도 전에 드러납니다.
'모든 경로'가 무슨 뜻인지는 조건에 따라 값을 넣는 코드에서 드러납니다. 1장에서 본 if는 괄호 안의 조건이 참일 때만 중괄호 안을 실행합니다. 아래 코드에서 args.length > 0은 '실행 인자가 하나 이상 있다'는 조건입니다.
String email;
if (args.length > 0) {
email = args[0];
}
System.out.println(email); // error: variable email might not have been initialized인자를 주고 실행하면 email에 값이 들어가겠지만, 인자 없이 실행하면 if 안을 건너뛰고 곧바로 email을 읽게 됩니다. 컴파일러는 실행할 때 인자가 몇 개 들어올지 알 수 없으므로, 값 없이 지나가는 경로가 하나라도 있으면 컴파일을 거부합니다. 조건이 거짓일 때 실행할 else를 붙여 두 경로 모두에서 값을 넣으면 컴파일됩니다. else는 if와 else로 조건 분기하기에서 자세히 배웁니다.
String email;
if (args.length > 0) {
email = args[0];
} else {
email = "[email protected]";
}
System.out.println(email);지금까지 본 변수는 모두 main 안에서 선언한 지역 변수(local variable)입니다. 뒤에서 배울 객체의 필드는 따로 값을 넣지 않아도 0, false, null 같은 기본값을 받는다는 점이 다릅니다. 하지만 기본값이 들어 있다고 해서 그 값이 올바른 값이라는 뜻은 아닙니다. 예를 들어 나이가 0으로 들어 있는 회원은 대개 '나이를 아직 모르는' 회원일 뿐입니다.
더 깊이: 컴파일러는 변수의 값을 따지지 않는다
초기화 검사가 지나치게 엄격하게 느껴질 때도 있습니다. 아래 코드는 사람이 보기에는 안전합니다. 두 if의 조건이 같으므로, 두 번째 if 안에서 email을 읽을 때는 언제나 값이 들어 있습니다.
boolean hasArgument = args.length > 0;
String email;
if (hasArgument) {
email = args[0];
}
if (hasArgument) {
System.out.println(email); // error: variable email might not have been initialized
}그래도 javac는 이 코드를 거부합니다. Java 언어 명세의 확정 대입(definite assignment) 규칙은 문장과 연산자의 구조만 보고 판단할 뿐, hasArgument 같은 변수에 실행 중 어떤 값이 들어 있을지는 따지지 않기 때문입니다. 예외는 if (true)처럼 컴파일할 때 이미 값이 정해지는 상수식(constant expression)뿐입니다. 실행 중의 값까지 따져서 모든 경우를 판단하는 것은 일반적으로 불가능하므로, 명세는 구조만 보는 단순한 규칙을 정해 두었습니다. 덕분에 어떤 컴파일러를 쓰든 같은 코드에는 같은 판정이 나옵니다. 실무에서는 선언과 함께 초기화하거나 if와 else 양쪽에서 값을 넣어, 이런 판정에 기대지 않는 코드를 씁니다. 정확한 규칙은 Java 언어 명세 16장에 있습니다.
대입은 값을 바꿔 넣는 일
=는 수학의 등호와 모양이 같지만 하는 일이 다릅니다. 수학에서 age = age + 1은 성립할 수 없는 식이지만, Java에서는 아무 문제 없는 문장입니다. Java의 =는 대입 연산자(assignment operator)로, 오른쪽을 계산한 값을 왼쪽 변수에 넣으라는 명령이기 때문입니다. age가 45일 때 age = age + 1;을 실행하면 먼저 오른쪽의 age + 1이 현재 값 45를 읽어 46을 만들고, 그다음 46이 age에 들어갑니다.
이미 값이 있는 변수에 다시 대입하면 이전 값은 새 값으로 바뀝니다. 다음 프로그램은 한 변수의 값을 다른 변수에 복사한 뒤 원래 변수만 바꾸면 어떻게 되는지 보여 줍니다.
public final class VariableTrace {
public static void main(String[] args) {
String email = "[email protected]";
String copiedEmail = email;
System.out.println("before.current=" + email);
System.out.println("before.copied=" + copiedEmail);
email = "[email protected]";
System.out.println("after.current=" + email);
System.out.println("after.copied=" + copiedEmail);
}
}문장이 하나씩 실행될 때마다 두 변수의 값이 어떻게 바뀌는지 표로 따라가 보겠습니다. 표의 original과 updated는 두 이메일 문자열을 줄여 쓴 것입니다.
| 실행한 문장 | email | copiedEmail |
|---|---|---|
String email = "[email protected]"; | original | 아직 선언되지 않음 |
String copiedEmail = email; | original | original |
email = "[email protected]"; | updated | original |
String copiedEmail = email;은 이 문장이 실행되는 순간 email에 들어 있던 값을 copiedEmail에 복사합니다. 이것으로 두 변수가 이후에도 함께 움직이도록 연결되는 것은 아닙니다. 그래서 email에 새 문자열을 대입해도 copiedEmail은 복사해 둔 원래 이메일을 그대로 가지고 있고, 처음 두 줄과 달리 마지막 두 줄의 출력은 서로 다릅니다. 변수를 읽는다고 값이 사라지지도 않고, 대입하지 않은 변수가 덩달아 바뀌지도 않습니다.
아래 그림은 같은 과정을 단계별로 보여 줍니다. 그림에 나오는 '참조'는 String 변수에 들어 있는 값을 부르는 말인데, 정확한 뜻은 이 절 끝의 '더 깊이'에서 설명합니다. 지금은 '변수에 들어 있는 값'이라고 읽어도 충분합니다.
재대입은 현재 값만 바꾸고 복사한 값은 남긴다
선언→초기화→참조 복사→email 재대입에서 copiedEmail은 옛 참조를 유지합니다. 고정 두 변수의 값이 바뀌는 대표 상태 추적입니다.
JAVA · VALUE TIMELINE
재대입은 현재 값만 바꾸고 복사한 값은 남긴다
선언·초기화·복사·재대입을 같은 실행 축에 놓으면 두 변수의 현재 값이 언제 갈라지는지 보입니다.
| 단계 | copiedEmail | |
|---|---|---|
| T0 · 선언 | 초기화 전 · 읽기 불가 | 아직 선언되지 않음 |
| T1 · 초기화 | [email protected] | 아직 선언되지 않음 |
| T2 · 참조값 복사 | [email protected] | [email protected] |
| T3 · email 재대입 | [email protected] | [email protected] 유지 |
- T0 · 선언
email: 초기화 전이라 읽기 불가copiedEmail: 아직 선언되지 않음 - T1 · 초기화
email:[email protected]copiedEmail: 아직 선언되지 않음 - T2 · 참조값 복사
email과copiedEmail모두[email protected]을 가리킵니다. - T3 · email만 재대입
email:[email protected]copiedEmail:[email protected]유지
- 각 문장이 끝난 뒤의 상태
- T3에서 email만 새 참조값을 가짐
복사는 그 시점의 참조값을 새 변수에 대입합니다. 이후 한 변수에 새 문자열을 대입해도 다른 변수의 현재 값은 바뀌지 않습니다.
이 예의 조건
- original/updated는 본문 이메일 문자열의 약칭입니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 23 | updated |
| 24 | original은 본문 이메일의 약칭 |
| 구분 | 내용 |
|---|---|
| 전체 읽기 정보 | 재대입은 현재 값만 바꾸고 복사한 값은 남긴다 email은 선언 뒤 [email protected]으로 초기화되고 copiedEmail이 그 참조값을 복사합니다. 이후 email만 [email protected]으로 재대입되어 두 변수의 현재 값이 달라지는 실행 타임라인입니다. JAVA · VALUE TIMELINE 재대입은 현재 값만 바꾸고 복사한 값은 남긴다 선언·초기화·복사·재대입을 같은 실행 축에 놓으면 두 변수의 현재 값이 언제 갈라지는지 보입니다. 실행 순서에 따른 두 변수의 현재 참조값 단계 email copiedEmail T0 · 선언 초기화 전 · 읽기 불가 아직 선언되지 않음 T1 · 초기화 [email protected] 아직 선언되지 않음 T2 · 참조값 복사 [email protected] [email protected] T3 · email 재대입 [email protected] [email protected] 유지 T0 · 선언 email : 초기화 전이라 읽기 불가 copiedEmail : 아직 선언되지 않음 T1 · 초기화 email : [email protected] copiedEmail : 아직 선언되지 않음 T2 · 참조값 복사 email 과 copiedEmail 모두 [email protected] 을 가리킵니다. T3 · email만 재대입 email : [email protected] copiedEmail : [email protected] 유지 각 문장이 끝난 뒤의 상태 T3에서 email만 새 참조값을 가짐 복사는 그 시점의 참조값을 새 변수에 대입합니다. 이후 한 변수에 새 문자열을 대입해도 다른 변수의 현재 값은 바뀌지 않습니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 선언과 초기화 email 선언 뒤 original 문자열 참조로 초기화합니다. |
|
|
| 참조값 복사 copiedEmail에 email의 현재 참조값을 복사합니다. |
|
|
| email 재대입 email만 updated 문자열 참조로 바꿉니다. |
|
재대입은 현재 값만 바꾸고 복사한 값은 남긴다
선언→초기화→참조 복사→email 재대입에서 copiedEmail은 옛 참조를 유지합니다. 고정 두 변수의 값이 바뀌는 대표 상태 추적입니다.
JAVA · VALUE TIMELINE
재대입은 현재 값만 바꾸고 복사한 값은 남긴다
선언·초기화·복사·재대입을 같은 실행 축에 놓으면 두 변수의 현재 값이 언제 갈라지는지 보입니다.
| 단계 | copiedEmail | |
|---|---|---|
| T0 · 선언 | 초기화 전 · 읽기 불가 | 아직 선언되지 않음 |
| T1 · 초기화 | [email protected] | 아직 선언되지 않음 |
| T2 · 참조값 복사 | [email protected] | [email protected] |
| T3 · email 재대입 | [email protected] | [email protected] 유지 |
- T0 · 선언
email: 초기화 전이라 읽기 불가copiedEmail: 아직 선언되지 않음 - T1 · 초기화
email:[email protected]copiedEmail: 아직 선언되지 않음 - T2 · 참조값 복사
email과copiedEmail모두[email protected]을 가리킵니다. - T3 · email만 재대입
email:[email protected]copiedEmail:[email protected]유지
- 각 문장이 끝난 뒤의 상태
- T3에서 email만 새 참조값을 가짐
복사는 그 시점의 참조값을 새 변수에 대입합니다. 이후 한 변수에 새 문자열을 대입해도 다른 변수의 현재 값은 바뀌지 않습니다.
이 예의 조건
- original/updated는 본문 이메일 문자열의 약칭입니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 23 | updated |
| 24 | original은 본문 이메일의 약칭 |
| 구분 | 내용 |
|---|---|
| 전체 읽기 정보 | 재대입은 현재 값만 바꾸고 복사한 값은 남긴다 email은 선언 뒤 [email protected]으로 초기화되고 copiedEmail이 그 참조값을 복사합니다. 이후 email만 [email protected]으로 재대입되어 두 변수의 현재 값이 달라지는 실행 타임라인입니다. JAVA · VALUE TIMELINE 재대입은 현재 값만 바꾸고 복사한 값은 남긴다 선언·초기화·복사·재대입을 같은 실행 축에 놓으면 두 변수의 현재 값이 언제 갈라지는지 보입니다. 실행 순서에 따른 두 변수의 현재 참조값 단계 email copiedEmail T0 · 선언 초기화 전 · 읽기 불가 아직 선언되지 않음 T1 · 초기화 [email protected] 아직 선언되지 않음 T2 · 참조값 복사 [email protected] [email protected] T3 · email 재대입 [email protected] [email protected] 유지 T0 · 선언 email : 초기화 전이라 읽기 불가 copiedEmail : 아직 선언되지 않음 T1 · 초기화 email : [email protected] copiedEmail : 아직 선언되지 않음 T2 · 참조값 복사 email 과 copiedEmail 모두 [email protected] 을 가리킵니다. T3 · email만 재대입 email : [email protected] copiedEmail : [email protected] 유지 각 문장이 끝난 뒤의 상태 T3에서 email만 새 참조값을 가짐 복사는 그 시점의 참조값을 새 변수에 대입합니다. 이후 한 변수에 새 문자열을 대입해도 다른 변수의 현재 값은 바뀌지 않습니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 선언과 초기화 email 선언 뒤 original 문자열 참조로 초기화합니다. |
|
|
| 참조값 복사 copiedEmail에 email의 현재 참조값을 복사합니다. |
|
|
| email 재대입 email만 updated 문자열 참조로 바꿉니다. |
|
변수 하나는 한 번에 값 하나만 가집니다. 재대입이 끝나면 원래 이메일은 email로는 더 이상 읽을 수 없고, 미리 복사해 둔 copiedEmail로만 읽을 수 있습니다. 그러니 대입은 '변수에 값이 쌓인다'가 아니라 '현재 값이 새 값으로 바뀐다'로 이해해야 합니다. 여러 회원을 한꺼번에 보관하는 방법은 3장의 배열과 4장의 객체에서 배웁니다. 이번 장 뒤쪽 예제에 나오는 saved=... 출력도 입력이 검사를 통과했다는 표시일 뿐이고, 회원 목록이나 파일·데이터베이스에 실제로 저장하는 기능은 아직 없습니다.
값을 바꿀 일이 없는 변수라면 선언 앞에 final을 붙여 재대입 자체를 막을 수도 있습니다. final은 final 변수·필드·상수에서 다룹니다.
더 깊이: String 변수에 실제로 들어 있는 것
변수를 흔히 '이름표가 붙은 상자'에 비유합니다. int age = 45;라면 꽤 정확한 비유입니다. age라는 상자에 45가 그대로 들어 있고, int copy = age;는 45를 복사해 다른 상자에 넣습니다. 하지만 String 변수에서는 이 비유가 어긋납니다. String 변수에는 문자열 자체가 아니라, 메모리 어딘가에 있는 문자열 객체를 가리키는 값인 참조(reference)가 들어 있기 때문입니다.
그래서 String copiedEmail = email;이 복사하는 것도 문자열이 아니라 참조입니다. 복사 직후에는 두 변수가 같은 문자열 객체를 가리키며, 위 그림의 '참조값 복사' 단계가 이 상태입니다. 이어서 email = "[email protected]";을 실행하면 email만 다른 문자열 객체를 가리키게 되고, copiedEmail은 여전히 원래 객체를 가리킵니다. 게다가 Java의 문자열은 한 번 만들면 내용을 바꿀 수 없으므로, 한 변수를 통해 공유된 문자열의 내용이 바뀌는 일도 없습니다. 기본형 변수와 참조형 변수의 복사가 어떻게 다른지는 기본형과 참조형 복사에서 자세히 다룹니다.
회원 한 명의 정보를 변수로 표현하기
이제 가입할 회원 한 명의 정보를 변수로 적어 보겠습니다. 입력 처리나 메서드는 섞지 않고, 이메일·나이·활성 여부를 지역 변수 세 개로만 표현합니다.
public class MemberValues {
public static void main(String[] args) {
String email = "[email protected]";
int age = 45;
boolean active = true;
System.out.println("email=" + email);
System.out.println("age=" + age);
System.out.println("active=" + active);
}
}[email protected]
age=45
active=trueemail, age, active는 한 회원의 서로 다른 속성입니다. 이메일은 글자들이므로 String, 나이는 정수이므로 int, 활성 여부는 참과 거짓 중 하나이므로 boolean에 담았습니다. 출력의 age=45와 active=true는 문자열 뒤에 각 변수의 현재 값이 이어 붙어 만들어진 것입니다.
아직 객체를 배우지 않았으므로, 세 변수가 한 회원의 정보라는 사실은 이름과 가까운 배치로만 드러낼 수 있습니다. 여러 값을 하나의 회원으로 묶는 방법은 클래스로 관련 데이터 묶기에서 배웁니다. 실행 인자로 받은 문자열을 숫자로 바꾸는 Integer.parseInt는 다음 두 글의 예제에서 먼저 써 보고, 값이 없을 때 다르게 처리하는 분기는 if와 else로 조건 분기하기에서 다룹니다. 지금은 변수마다 이름, 타입, 현재 값이 있다는 관계에만 집중하면 됩니다.
변수를 쓸 수 있는 범위와 이름
email, age, active는 main의 중괄호 안에서 선언했으므로 그 중괄호 안에서만 이름으로 쓸 수 있습니다. 이처럼 변수를 쓸 수 있는 코드의 범위를 스코프(scope)라고 합니다. 변수를 필요한 곳보다 넓은 범위에 두면 그 값이 언제 어디서 바뀌는지 확인해야 할 코드도 함께 늘어나므로, 변수는 쓰는 곳 가까이에 선언하는 것이 좋습니다. 블록과 스코프는 변수 스코프와 숫자 형변환에서 자세히 다룹니다.
한 줄에 변수 여러 개를 선언할 수도 있지만, 의미와 초깃값이 서로 다른 변수는 한 줄에 하나씩 두는 편이 안전합니다. 예를 들어 다음 선언은 width와 height를 모두 0으로 초기화하는 것처럼 보이지만, 실제로 초기화되는 것은 height뿐입니다.
int width, height = 0;
System.out.println(width); // error: variable width might not have been initialized= 0은 바로 앞의 height에만 붙기 때문입니다. int width = 0;과 int height = 0;처럼 한 줄에 하나씩 쓰면 이런 오해가 생기지 않고, 나중에 한 변수만 고치기도 쉽습니다.
이름도 중요합니다. int a = 45;와 int b = 18;만 보고는 어느 쪽이 회원 나이이고 어느 쪽이 최소 가입 나이인지 알 수 없습니다. 둘 다 int라서 서로 바꿔 써도 컴파일러는 알려 주지 않지만, age와 minimumAge라는 이름이라면 사람이 바로 알아챌 수 있습니다. 어떤 이름이 문법적으로 허용되는지, Java 개발자들이 따르는 이름 관례는 무엇인지는 다음 글에서 정리합니다.
정리
- 변수는 값에 붙인 이름입니다. 같은 값을 여러 곳에서 쓸 때 변수로 묶으면 고칠 곳이 한 곳으로 줄어듭니다.
String email;은 선언이고, 변수에 처음 값을 넣는 것이 초기화입니다. 초기화하지 않은 지역 변수를 읽으면 컴파일 오류가 나며,javac는 그 변수에 이르는 모든 실행 경로를 검사합니다.=는 오른쪽 값을 계산해 왼쪽 변수에 넣는 대입입니다. 변수는 한 번에 값 하나만 가지며, 재대입하면 이전 값은 새 값으로 바뀝니다.copiedEmail = email은 그 순간의 값을 복사할 뿐, 두 변수를 연결하지 않습니다.- 지역 변수는 선언한 중괄호 안에서만 쓸 수 있습니다. 필요한 범위에서, 한 줄에 하나씩, 뜻이 드러나는 이름으로 선언합니다.
연습 문제
1. 이메일 변경 추적하기
src/EmailTrace.java를 작성하세요.
email을[email protected]으로 초기화합니다.copiedEmail에 현재 이메일을 복사합니다.email만[email protected]으로 바꿉니다.- 두 변수를 출력해 복사한 값이 저절로 바뀌지 않았음을 확인합니다.
- 현재 이메일로 환영 문구를 만들어 출력합니다.
해설 보기
public final class EmailTrace {
public static void main(String[] args) {
String email = "[email protected]";
String copiedEmail = email;
email = "[email protected]";
String welcome = "welcome, " + email;
System.out.println("current=" + email);
System.out.println("copied=" + copiedEmail);
System.out.println(welcome);
}
}copiedEmail에는 복사한 순간의 값이 남고, welcome은 재대입이 끝난 뒤의 email을 읽어 만들어집니다. String welcome = ... 줄을 재대입보다 위로 옮기면 마지막 줄은 welcome, [email protected]이 됩니다. welcome도 email과 연결된 것이 아니라 만들어지는 순간의 값으로 계산한 새 문자열이기 때문입니다. 그래서 다른 값을 바탕으로 계산하는 변수는 언제 계산하느냐에 따라 결과가 달라집니다.
2. 두 변수의 값 맞바꾸기
다음 코드는 first와 second의 값을 서로 바꾸려는 의도로 작성했습니다. 실행 결과를 먼저 예측하고, 의도대로 동작하도록 고치세요.
int first = 1;
int second = 2;
first = second;
second = first;
System.out.println("first=" + first);
System.out.println("second=" + second);해설 보기
실행 결과는 다음과 같습니다.
first=2
second=2first = second;가 실행되는 순간 first의 원래 값 1은 2로 바뀌어 사라집니다. 그다음 second = first;는 이미 2가 된 first를 읽으므로 second도 2가 됩니다. 1을 잃기 전에 다른 변수에 복사해 두어야 합니다.
int first = 1;
int second = 2;
int temp = first; // temp=1, first=1, second=2
first = second; // temp=1, first=2, second=2
second = temp; // temp=1, first=2, second=1
System.out.println("first=" + first);
System.out.println("second=" + second);first=2
second=1주석은 각 문장이 실행된 뒤의 세 변수 값입니다. temp가 원래 값 1을 보관해 두었기 때문에 마지막 대입에서 second가 1을 받을 수 있습니다.
다음 글 기본 타입과 리터럴에서는 이 글에서 쓴 int, boolean, String 외에 어떤 타입이 있는지, 소스에 적은 45나 0.75 같은 값의 타입이 어떻게 정해지는지 살펴봅니다.