변수와 이름
선언·초기화·대입·읽기를 실행 순서로 구분하고 가입할 회원 한 명의 정보를 변수로 표현합니다.
지금까지는 실행 환경 정보만 출력했습니다.
이제 회원가입에 필요한 이메일과 나이를 프로그램 안에 보관합니다.
같은 값이 여러 계산과 출력에 쓰일 때 리터럴을 반복하면 어느 값이 같은 의미인지 코드에 남지 않습니다.
변수는 메모리 칸이라는 설명보다 한 실행 동안 같은 의미를 다시 가리키는 이름으로 먼저 이해하는 편이 유용합니다.
변수 하나가 출력과 계산의 공통 원천이 되면 변경 지점도 한 곳으로 모입니다.
- 프로필
profile=email
- 환영 문구
welcome=email
- 감사 로그
audit=email
중복 리터럴의 변경 누락
다음 프로그램은 회원 이메일을 바꾸는 과정에서 환영 문구 한 곳을 놓친 상태입니다.
public final class RepeatedLiteral {
public static void main(String[] args) {
System.out.println("profile=updated@example.com");
System.out.println("welcome=original@example.com");
System.out.println("audit=updated@example.com");
}
}profile=updated@example.com
welcome=original@example.com
audit=updated@example.com출력 세 줄은 모두 정상 문장이라 컴파일러가 모순을 찾지 못합니다.
프로필과 감사 로그에는 새 이메일이 보이지만 환영 문구는 이전 이메일을 사용합니다.
세 문자열이 같은 회원 이메일을 뜻한다는 관계가 소스에 없기 때문입니다.
public final class NamedValue {
public static void main(String[] args) {
String email = "updated@example.com";
System.out.println("profile=" + email);
System.out.println("welcome=" + email);
System.out.println("audit=" + email);
}
}profile=updated@example.com
welcome=updated@example.com
audit=updated@example.com이제 변경 지점은 초기화 식 하나입니다.
변수 이름은 세 사용처가 같은 값을 읽는다는 사실을 드러냅니다.
단순히 글자 수를 줄인 것이 아니라 수정 누락이 생길 구조를 제거했습니다.
선언과 초기화
String email;은 문자열 참조를 담을 지역 변수를 선언합니다.
아직 프로그램이 읽을 수 있는 값은 없습니다.
첫 대입인 email = "member@example.com";이 초기화입니다.
두 문장을 합친 String email = "member@example.com";도 같은 순서로 이해할 수 있습니다.
초기화하지 않은 지역 변수를 읽으면 임의의 숫자가 나오는 것이 아니라 컴파일이 실패합니다.
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);
^이 오류는 JVM이 프로그램을 실행하기 전에 javac가 모든 경로를 검사해 발견합니다.
선언만 한 뒤 조건에 따라 값을 넣는 코드에서는 어떤 경로로 와도 초기화가 끝났다는 사실을 컴파일러가 확인할 수 있어야 합니다.
지역 변수와 달리 객체 필드는 뒤에서 기본값을 받지만, 그 기본값이 업무적으로 유효하다는 뜻은 아닙니다.
지역 변수는 선언·첫 대입·재대입·읽기의 상태 순서를 건너뛸 수 없습니다.
- 1선언
String email; · 읽기 불가
- 2첫 대입
email = original · 읽기 가능
- 3재대입
email = updated · 이전 참조 대체
- 4읽기
println(email) · updated 사용
재대입의 실행 과정
수학의 등호와 Java의 대입 연산자 =는 역할이 다릅니다.
email = "updated@example.com";은 양쪽이 같다는 명제가 아니라 오른쪽 값을 왼쪽 변수의 현재 값으로 저장하라는 문장입니다.
public final class VariableTrace {
public static void main(String[] args) {
String email = "original@example.com";
String copiedEmail = email;
System.out.println("before.current=" + email);
System.out.println("before.copied=" + copiedEmail);
email = "updated@example.com";
System.out.println("after.current=" + email);
System.out.println("after.copied=" + copiedEmail);
}
}before.current=original@example.com
before.copied=original@example.com
after.current=updated@example.com
after.copied=original@example.comString copiedEmail = email;이 실행될 때 오른쪽 변수의 참조값을 새 변수에 복사합니다.
이후 email에 새 문자열을 대입해도 copiedEmail은 이전 문자열을 계속 가리킵니다.
변수를 읽는다고 값이 사라지지 않으며, 대입하지 않은 다른 변수까지 함께 바뀌지도 않습니다.
기존 이메일은 email이라는 이름으로 더는 읽을 수 없지만 copiedEmail로는 읽을 수 있습니다.
값을 변경할 때는 “변수에 회원이 쌓인다”가 아니라 “현재 값이 새 값으로 대체된다”고 표현해야 합니다.
여러 회원을 보존하는 구조는 배열과 객체에서 만듭니다.
기본형 대입은 그 순간의 숫자를 옮길 뿐 두 저장 공간 사이에 지속 연결을 만들지 않습니다.
- email
복사 후 updated@example.com으로 재대입
- copiedEmail
복사 시점의 original@example.com을 계속 참조
회원 정보의 변수 보관
처음에는 입력 처리나 메서드를 섞지 않고 회원 정보 한 건을 지역 변수로만 표현합니다.
public class MemberValues {
public static void main(String[] args) {
String email = "variables@example.com";
int age = 45;
boolean active = true;
System.out.println("email=" + email);
System.out.println("age=" + age);
System.out.println("active=" + active);
}
}email=variables@example.com
age=45
active=trueemail, age, active는 한 회원의 서로 다른 속성을 보관합니다.
아직 객체를 배우기 전이므로 세 변수의 관계를 이름과 가까운 배치로 드러냅니다.
실행 인자를 숫자로 바꾸는 방법, 값이 없을 때의 분기, 여러 기능을 메서드로 나누는 방법은 각각 뒤 문서에서 배웁니다.
변수 첫 수업에서는 이름·타입·현재 값의 관계에만 집중합니다.
지역 변수의 수명
email, age, active는 main의 중괄호 안에서 선언했으므로 그 블록 안에서만 사용할 수 있습니다.
너무 넓은 범위에 변수를 두면 같은 이름이 언제 바뀌는지 추적할 코드가 늘어납니다.
블록과 변수 수명은 ch2-10에서 더 자세히 다룹니다.
한 줄에 여러 변수를 선언할 수도 있지만 서로 다른 의미와 초기화 식을 가진 값은 한 줄씩 두는 편이 수정하기 쉽습니다.
반대로 좌표처럼 항상 함께 읽는 짧은 값이라도 이름이 모호해지면 분리합니다.
문법상 가능한 선언 방식과 사람이 검토하기 좋은 방식은 구분해야 합니다.
사용자가 제공한 원본 값에서 계산된 값이 갈라져 나오는 데이터 계보를 표시합니다.
- email
회원 식별과 연락
- age
입 자격 확인
- active
계정 활성 상태
변수 이름을 읽고 뜻을 알 수 있어야 합니다.
value보다 age, text보다 email이 안전합니다.
아직 명명 규칙 전체를 다루지 않았지만 의미를 이름에 포함하면 서로 다른 회원 값을 섞는 오류를 줄일 수 있습니다.
연습 문제
EmailTrace를 작성하세요.
email을original@example.com으로 초기화합니다.copiedEmail에 현재 이메일을 복사합니다.email만updated@example.com으로 변경합니다.- 두 변수를 출력해 복사한 값이 자동으로 바뀌지 않았음을 확인합니다.
- 현재 이메일로 환영 문구를 만들어 출력합니다.
해설 보기
public final class EmailTrace {
public static void main(String[] args) {
String email = "original@example.com";
String copiedEmail = email;
email = "updated@example.com";
String welcome = "welcome, " + email;
System.out.println("current=" + email);
System.out.println("copied=" + copiedEmail);
System.out.println(welcome);
}
}current=updated@example.com
copied=original@example.com
welcome, updated@example.comcopiedEmail에는 복사 시점의 참조값이 남고 welcome은 재대입 뒤의 현재 이메일을 읽어 만듭니다.
실행 순서를 바꾸어 재대입 전에 환영 문구를 만들면 이전 이메일이 들어가므로 파생 값을 계산하는 시점도 결과의 일부입니다.