본문 바로가기

변수와 이름

선언·초기화·대입·읽기를 실행 순서로 구분하고 가입할 회원 한 명의 정보를 변수로 표현합니다.

앞 장에서는 문자열을 출력하고 명령의 실행 인자를 읽었습니다.

이제 회원가입에 필요한 이메일과 나이를 프로그램 안에 보관합니다.

같은 값이 여러 계산과 출력에 쓰일 때 리터럴을 반복하면 어느 값이 같은 의미인지 코드에 남지 않습니다.

변수는 메모리 칸이라는 설명보다 한 실행 동안 같은 의미를 다시 가리키는 이름으로 먼저 이해하는 편이 유용합니다.


중복 리터럴의 변경 누락

다음 프로그램은 회원 이메일을 바꾸는 과정에서 환영 문구 한 곳을 놓친 상태입니다.

이번 장의 lab 폴더에는 성공·실패 원인을 따로 관찰할 짧은 실험을, src에는 완성된 프로그램을 저장합니다. 먼저 java-board에서 lab을 만드세요. PowerShell은 New-Item -ItemType Directory -Force lab | Out-Null, macOS·Linux는 mkdir -p lab을 사용합니다.

아래 코드를 lab/RepeatedLiteral.java로 저장한 뒤 다음 두 명령을 순서대로 실행합니다.

javac -encoding UTF-8 -d out lab/RepeatedLiteral.java
java -cp out RepeatedLiteral

다음 파일들도 같은 방식으로 경로와 클래스 이름을 바꾸어 실행합니다. 컴파일 실패를 관찰하는 실험은 오류를 확인한 뒤 멈추며, 이전에 남은 .class를 실행해 성공으로 판단하지 않습니다.

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]");
    }
}

출력 세 줄은 모두 정상 문장이라 컴파일러가 모순을 찾지 못합니다.

프로필과 감사 로그에는 새 이메일이 보이지만 환영 문구는 이전 이메일을 사용합니다.

세 문자열이 같은 회원 이메일을 뜻한다는 관계가 소스에 없기 때문입니다.

lab/NamedValue.java
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);
    }
}

이제 변경 지점은 초기화 식 하나입니다.

변수 이름은 세 사용처가 같은 값을 읽는다는 사실을 드러냅니다.

단순히 글자 수를 줄인 것이 아니라 수정 누락이 생길 구조를 제거했습니다.


선언과 초기화

String email;은 문자열 참조를 담을 지역 변수를 선언합니다.

아직 프로그램이 읽을 수 있는 값은 없습니다.

첫 대입인 email = "[email protected]";이 초기화입니다.

두 문장을 합친 String email = "[email protected]";도 같은 순서로 이해할 수 있습니다.

초기화하지 않은 지역 변수를 읽으면 임의의 숫자가 나오는 것이 아니라 컴파일이 실패합니다.

lab/UninitializedLocal.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);
                           ^

이 오류는 JVM이 프로그램을 실행하기 전에 javac가 모든 경로를 검사해 발견합니다.

선언만 한 뒤 조건에 따라 값을 넣는 코드에서는 어떤 경로로 와도 초기화가 끝났다는 사실을 컴파일러가 확인할 수 있어야 합니다.

지역 변수와 달리 객체 필드는 뒤에서 기본값을 받지만, 그 기본값이 업무적으로 유효하다는 뜻은 아닙니다.


재대입의 실행 과정

수학의 등호와 Java의 대입 연산자 =는 역할이 다릅니다.

email = "[email protected]";은 양쪽이 같다는 명제가 아니라 오른쪽 값을 왼쪽 변수의 현재 값으로 저장하라는 문장입니다.

src/VariableTrace.java
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);
    }
}

String copiedEmail = email;이 실행될 때 오른쪽 변수의 참조값을 새 변수에 복사합니다.

이후 email에 새 문자열을 대입해도 copiedEmail은 이전 문자열을 계속 가리킵니다.

변수를 읽는다고 값이 사라지지 않으며, 대입하지 않은 다른 변수까지 함께 바뀌지도 않습니다.

기존 이메일은 email이라는 이름으로 더는 읽을 수 없지만 copiedEmail로는 읽을 수 있습니다.

값을 변경할 때는 “변수에 회원이 쌓인다”가 아니라 “현재 값이 새 값으로 대체된다”고 표현해야 합니다.

여러 회원을 보존하는 구조는 배열과 객체에서 만듭니다.

이번 장 뒤쪽 예제의 saved=... 출력도 검사를 통과했다는 표시입니다. 아직 회원 목록이나 파일·데이터베이스에 데이터를 저장하는 기능은 없습니다. 변수의 현재 값과 실제 저장 기능을 구분하며 읽으세요.

재대입은 현재 값만 바꾸고 복사한 값은 남긴다

선언→초기화→참조 복사→email 재대입에서 copiedEmail은 옛 참조를 유지합니다. 고정 두 변수의 값이 바뀌는 대표 상태 추적입니다.

email은 선언 뒤 [email protected]으로 초기화되고 copiedEmail이 그 참조값을 복사합니다. 이후 email만 [email protected]으로 재대입되어 두 변수의 현재 값이 달라지는 실행 타임라인입니다.

JAVA · VALUE TIMELINE

재대입은 현재 값만 바꾸고 복사한 값은 남긴다

선언·초기화·복사·재대입을 같은 실행 축에 놓으면 두 변수의 현재 값이 언제 갈라지는지 보입니다.

실행 순서에 따른 두 변수의 현재 참조값
단계emailcopiedEmail
T0 · 선언초기화 전 · 읽기 불가아직 선언되지 않음
T1 · 초기화[email protected]아직 선언되지 않음
T2 · 참조값 복사[email protected][email protected]
T3 · email 재대입[email protected][email protected] 유지
재대입은 현재 값만 바꾸고 복사한 값은 남긴다 T0 선언→T1 초기화→T2 참조값 복사→T3 email만 재대입; copiedEmail은 이전 참조 유지 → 복사 재대입 T0 · 선언 email: 읽기 불가 copiedEmail: 선언 전 T1 · 초기화 email: [email protected] copiedEmail: 선언 전 T2 · 복사 email · copiedEmail 둘 다 같은 문자열 참조 [email protected] T3 · email 재대입 email: updated copiedEmail: original 다른 참조값 화살표는 다음 문장에 따른 상태 변화 updated / original은 본문 이메일의 약칭
재대입은 현재 값만 바꾸고 복사한 값은 남긴다 T0 선언→T1 초기화→T2 참조값 복사→T3 email만 재대입; copiedEmail은 이전 참조 유지. 본문과 바로 아래 설명이 원래 코드·수치를 보완합니다. 초기화 복사 재대입 T0 · 선언 email: 읽기 불가 copiedEmail: 선언 전 T1 · 초기화 email: [email protected] copiedEmail: 선언 전 T2 · 복사 email · copiedEmail 둘 다 같은 문자열 참조 [email protected] T3 · email 재대입 email: updated copiedEmail: original 다른 참조값 updated / original은 본문 이메일 문자열의 약칭
  1. T0 · 선언

    email: 초기화 전이라 읽기 불가

    copiedEmail: 아직 선언되지 않음

  2. T1 · 초기화

    email: [email protected]

    copiedEmail: 아직 선언되지 않음

  3. T2 · 참조값 복사

    email과 copiedEmail 모두 [email protected]을 가리킵니다.

  4. T3 · email만 재대입

    email: [email protected]

    copiedEmail: [email protected] 유지

  • 각 문장이 끝난 뒤의 상태
  • T3에서 email만 새 참조값을 가짐

복사는 그 시점의 참조값을 새 변수에 대입합니다. 이후 한 변수에 새 문자열을 대입해도 다른 변수의 현재 값은 바뀌지 않습니다.

이 예의 조건

  • original/updated는 본문 이메일 문자열의 약칭입니다.
관계도의 보충 설명
원 도표의 전체 관계·라벨·조건
구분내용
23updated
24original은 본문 이메일의 약칭
원 도표의 설명과 주석
구분내용
전체 읽기 정보재대입은 현재 값만 바꾸고 복사한 값은 남긴다 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: original 참조
  • copiedEmail: 같은 original 참조
  • 복사는 이후 재대입을 연동하는 연결을 만들지 않습니다.
email 재대입

email만 updated 문자열 참조로 바꿉니다.

    • 재대입은 선택한 변수의 현재 값만 바꿉니다.

    재대입은 현재 값만 바꾸고 복사한 값은 남긴다

    선언→초기화→참조 복사→email 재대입에서 copiedEmail은 옛 참조를 유지합니다. 고정 두 변수의 값이 바뀌는 대표 상태 추적입니다.

    email은 선언 뒤 [email protected]으로 초기화되고 copiedEmail이 그 참조값을 복사합니다. 이후 email만 [email protected]으로 재대입되어 두 변수의 현재 값이 달라지는 실행 타임라인입니다.

    JAVA · VALUE TIMELINE

    재대입은 현재 값만 바꾸고 복사한 값은 남긴다

    선언·초기화·복사·재대입을 같은 실행 축에 놓으면 두 변수의 현재 값이 언제 갈라지는지 보입니다.

    실행 순서에 따른 두 변수의 현재 참조값
    단계emailcopiedEmail
    T0 · 선언초기화 전 · 읽기 불가아직 선언되지 않음
    T1 · 초기화[email protected]아직 선언되지 않음
    T2 · 참조값 복사[email protected][email protected]
    T3 · email 재대입[email protected][email protected] 유지
    재대입은 현재 값만 바꾸고 복사한 값은 남긴다 T0 선언→T1 초기화→T2 참조값 복사→T3 email만 재대입; copiedEmail은 이전 참조 유지 → 복사 재대입 T0 · 선언 email: 읽기 불가 copiedEmail: 선언 전 T1 · 초기화 email: [email protected] copiedEmail: 선언 전 T2 · 복사 email · copiedEmail 둘 다 같은 문자열 참조 [email protected] T3 · email 재대입 email: updated copiedEmail: original 다른 참조값 화살표는 다음 문장에 따른 상태 변화 updated / original은 본문 이메일의 약칭
    재대입은 현재 값만 바꾸고 복사한 값은 남긴다 T0 선언→T1 초기화→T2 참조값 복사→T3 email만 재대입; copiedEmail은 이전 참조 유지. 본문과 바로 아래 설명이 원래 코드·수치를 보완합니다. 초기화 복사 재대입 T0 · 선언 email: 읽기 불가 copiedEmail: 선언 전 T1 · 초기화 email: [email protected] copiedEmail: 선언 전 T2 · 복사 email · copiedEmail 둘 다 같은 문자열 참조 [email protected] T3 · email 재대입 email: updated copiedEmail: original 다른 참조값 updated / original은 본문 이메일 문자열의 약칭
    1. T0 · 선언

      email: 초기화 전이라 읽기 불가

      copiedEmail: 아직 선언되지 않음

    2. T1 · 초기화

      email: [email protected]

      copiedEmail: 아직 선언되지 않음

    3. T2 · 참조값 복사

      email과 copiedEmail 모두 [email protected]을 가리킵니다.

    4. T3 · email만 재대입

      email: [email protected]

      copiedEmail: [email protected] 유지

    • 각 문장이 끝난 뒤의 상태
    • T3에서 email만 새 참조값을 가짐

    복사는 그 시점의 참조값을 새 변수에 대입합니다. 이후 한 변수에 새 문자열을 대입해도 다른 변수의 현재 값은 바뀌지 않습니다.

    이 예의 조건

    • original/updated는 본문 이메일 문자열의 약칭입니다.

    관계도의 보충 설명

    원 도표의 전체 관계·라벨·조건
    구분내용
    23updated
    24original은 본문 이메일의 약칭
    원 도표의 설명과 주석
    구분내용
    전체 읽기 정보재대입은 현재 값만 바꾸고 복사한 값은 남긴다 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: original 참조
    • copiedEmail: 같은 original 참조
    • 복사는 이후 재대입을 연동하는 연결을 만들지 않습니다.
    email 재대입

    email만 updated 문자열 참조로 바꿉니다.

      • 재대입은 선택한 변수의 현재 값만 바꿉니다.

      회원 정보의 변수 보관

      처음에는 입력 처리나 메서드를 섞지 않고 회원 정보 한 건을 지역 변수로만 표현합니다.

      src/MemberValues.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=true

      email, age, active는 한 회원의 서로 다른 속성을 보관합니다.

      아직 객체를 배우기 전이므로 세 변수의 관계를 이름과 가까운 배치로 드러냅니다.

      실행 인자를 숫자로 바꾸는 방법, 값이 없을 때의 분기, 여러 기능을 메서드로 나누는 방법은 각각 뒤 문서에서 배웁니다.

      변수를 배우는 첫 수업에서는 이름·타입·현재 값의 관계에만 집중합니다.


      지역 변수의 수명

      email, age, active는 main의 중괄호 안에서 선언했으므로 그 블록 안에서만 사용할 수 있습니다.

      너무 넓은 범위에 변수를 두면 같은 이름이 언제 바뀌는지 추적할 코드가 늘어납니다.

      블록과 변수 수명은 ch2-10에서 더 자세히 다룹니다.

      한 줄에 여러 변수를 선언할 수도 있지만 서로 다른 의미와 초기화 식을 가진 값은 한 줄씩 두는 편이 수정하기 쉽습니다.

      반대로 좌표처럼 항상 함께 읽는 짧은 값이라도 이름이 모호해지면 분리합니다.

      문법상 가능한 선언 방식과 사람이 검토하기 좋은 방식은 구분해야 합니다.

      변수 이름을 읽고 뜻을 알 수 있어야 합니다.

      value보다 age, text보다 email이 안전합니다.

      아직 명명 규칙 전체를 다루지 않았지만 의미를 이름에 포함하면 서로 다른 회원 값을 섞는 오류를 줄일 수 있습니다.


      연습 문제

      EmailTrace를 작성하세요.

      1. email을 [email protected]으로 초기화합니다.
      2. copiedEmail에 현재 이메일을 복사합니다.
      3. email만 [email protected]으로 변경합니다.
      4. 두 변수를 출력해 복사한 값이 자동으로 바뀌지 않았음을 확인합니다.
      5. 현재 이메일로 환영 문구를 만들어 출력합니다.
      해설 보기
      src/EmailTrace.java
      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은 재대입 뒤의 현재 이메일을 읽어 만듭니다.

      실행 순서를 바꾸어 재대입 전에 환영 문구를 만들면 이전 이메일이 들어가므로 파생 값을 계산하는 시점도 결과의 일부입니다.