네이버클라우드 AIaaS 개발자 양성과정 1기/Java

[Java] 예제 소스 정리 - 변수(원시 변수, 레퍼런스 변수, 형변환)

끈기JK 2022. 12. 29. 20:27

com.eomcs.lang.ex04

 

예제 소스 정리

 

 

# 변수 선언과 값 할당을 동시에 하기

변수 선언 + 값 저장

- 이렇게 변수 선언과 값 저장을 한 번에 하는 것을 '변수 초기화 문장'이라 부른다.

int age = 20;

 

 

# 여러 개의 변수를 한 번에 선언하고 초기화도 함께 수행하기

여러 개의 변수를 선언할 때도 한 개를 선언할 때와 마찬가지로 값을 저장할 수 있다.

int a1 = 100, a2 = 200;

 

 

# 여러 개의 변수를 한 번에 선언하고 초기화도 함께 수행하기 II

모든 변수를 다 초기화시킬 필요는 없다.

int a1, a2 = 200, a3, a4 = 400, a5;

 

 

# 변수 선언 오류

변수 선언 보다 변수 사용이 먼저 올 수 없다.

Javascript의 hoisting을 지원하지 않는다.

count = 50; // 문법 오류!

int count;

//항상 변수가 선언된 다음에 사용해야 한다.
count = 50; // 옳다!

 

 

# 변수의 종류

- 자바 원시 타입의 값을 저장하는 변수와 메모리 주소를 저장하는 변수가 있다.
- 자바 원시 타입 변수(primitive variable)
    정수, 부동소수점, 논리, 문자코드의 값
- 레퍼런스 변수(referece variable)
    자바 원시 타입의 값을 제외한 모든 값

 

 메모리 크기에 따라 저장할 수 있는 값의 범위
 1) primitive data type (원시 데이터 타입)
 - 정수
   - byte   : 1byte 메모리 (-128 ~ 127)
   - short  : 2byte 메모리 (-32768 ~ 32767)
   - int    : 4byte 메모리 (약 -21억 ~ 21억)
   - long   : 8byte 메모리 (약 -922경 ~ 922경)
 - 부동소수점
   - float  : 4byte 메모리 (유효자릿수 7자리)
   - double : 8byte 메모리 (유효자릿수 15자리)
 - 문자
   - char   : 2byte 메모리 (0 ~ 65535). UCS-2 코드 값 저장.
 - 논리값
   - boolean : JVM에서 4 바이트 int 메모리를 사용한다.
               배열일 경우 1 바이트 byte 메모리를 사용한다.

 2) reference(레퍼런스)
    데이터가 저장된 메모리의 주소를 저장하는 메모리.
 - 문자열(주소)
   - String : 문자열이 저장된 메모리의 주소를 저장한다.
              프로그래밍 입문 단계에서는 그냥 문자열을 저장하는 메모리로 생각하라!
 - 날짜(주소)
   - Date : 날짜 값이 저장된 메모리의 주소를 저장한다.
            프로그래밍 입문 단계에서는 그냥 날짜를 저장하는 메모리로 생각하라!
 - 레퍼런스에 대한 자세한 사항은 추후에 자세히 설명할 것이다.

 

 

# 정수 변수 - 변수와 리터럴의 크기

4바이트 정수 리터럴 ==> byte, short 변수
- 4바이트 리터럴인 경우 메모리 크기에 상관없이 저장할 수만 있다면 
byte(1바이트), short(2바이트) 메모리에 값을 저장해도 컴파일 오류가 발생하지 않는다.

byte b;
short s;

b = 127;   // 4바이트 리터럴 ==> 1바이트 메모리
s = 32767; // 4바이트 리터럴 ==> 2바이트 메모리

//- 단, 리터럴을 메모리에 저장할 수 없다면 컴파일 오류가 발생한다.
//    b = 128;   // 1바이트 크기를 초과하는 값
//    s = 32768; // 2바이트 크기를 초과하는 값

//8바이트 정수 리터럴 
//- 8바이트 리터럴인 경우 값이 크고 작음에 상관없이
//  byte(1바이트), short(2바이트), int(4바이트) 메모리에 값을 저장하려하면 컴파일 오류가 발생한다. 
//    b = 127L;
//    s = 32767L;
//    int i = 100L;

 

 

# 정수 변수 - 크기가 다른 변수끼리 값 할당
long l = 100;
int i = 100;
short s = 100;
byte b = 100;
char c = 100;

//변수의 값을 다른 변수에 저장할 때,
//- 값의 크기에 상관없이 같거나 큰 크기의 메모리이어야 한다.

long l2;
int i2;
short s2;
byte b2;
char c2;

// long ===> long 이상
l2 = l;
//i2 = l; // 컴파일 오류
//s2 = l; // 컴파일 오류
//b2 = l; // 컴파일 오류!
//c2 = l; // 컴파일 오류!

// int ===> int 이상
l2 = i;
i2 = i;
//s2 = i; // 컴파일 오류!
//b2 = i; // 컴파일 오류!
//c2 = i;  // 컴파일 오류!

// short ===> short 이상
l2 = s;
i2 = s;
s2 = s;
//b2 = s; // 컴파일 오류!
//c2 = s; // 컴파일 오류! char(0 ~ 65535) | short(-32768 ~ 32767)

// byte ===> byte 이상
l2 = b;
i2 = b;
s2 = b;
b2 = b;
//c2 = b; // 컴파일 오류! char(0 ~ 65535) | byte(-128 ~ 127)

 

 

# 부동소수점 변수 - 변수의 메모리 크기

4바이트 부동소수점 변수
- 유효자릿수 7자리 부동소수점 저장 가능

- 10 진수로 부동소수점을 표현할 경우 소수점을 제외한 숫자가 7 개인 경우 정상적으로 메모리에 저장된다는 의미

float f;

f = 9.876545f; // 소수점을 떼면 숫자의 개수가 7개이다.
System.out.println(f);

f = 987654.5f; // 소수점을 떼면 숫자의 개수가 7개이다.
System.out.println(f);

// 소수점을 뗐을 때 앞에 0만 있을 경우 그 0은 자릿수에서 제외한다.
f = 0.000009876545f; // 소수점을 떼면 숫자가 13개 이지만, 앞의 0을 제외하면 실제 7개이다.
System.out.println(f); // OK

// 주의!
//- 유효자릿수가 7자리를 넘어가면 정상적으로 값을 저장되지 않을 수 있다.
//- 리터럴 문법에서 이미 오류다! 왜? 유효자릿수를 넘어가기 때문이다.
//- 리터럴 값에서 이미 맨 뒤의 값이 반올림 된다.
f = 9.8765456f; 
System.out.println(f);

// 변수에 값을 넣기 전에 이미 리터럴 값에서 맨 뒤의 값이 반올림 된다.
f = 9876545.6f; 
System.out.println(f);

 

 

# 부동소수점 변수 - 변수의 메모리 크기 II

8바이트 부동소수점 변수

- 유효자릿수 15자리 부동소수점 저장 가능

- 10 진수로 부동소수점을 표현할 경우 소수점을 제외한 숫자가 15 개인 경우 정상적으로 메모리에 저장된다는 의미

double d;

d = 9.99999999988888; // 소수점을 떼면 숫자의 개수가 15개이다.
System.out.println(d);

d = 9999999.88888888; // 소수점을 떼면 숫자의 개수가 15개이다.
System.out.println(d);

d = 99999999998888.8; // 소수점을 떼면 숫자의 개수가 15개이다.
System.out.println(d);

// 소수점을 뗐을 때 앞에 0만 있을 경우 그 0은 자릿수에서 제외한다.
d = 0.00000999999999988888; // 소수점을 떼면 숫자가 21개 이지만, 앞의 0을 제외하면 실제 15개이다.
System.out.println(d); // OK

// 주의!
//- 유효자릿수가 15자리를 넘어가면 정상적으로 값을 저장되지 않을 수 있다.
//- 리터럴 값이 이미 유효자릿수를 넘어간다.
//- 변수에 저장하기 전에 리터럴 값이 이미 반올림 되거나 잘린다.
d = 9.999999999999997;
System.out.println(d);

// 변수에 값을 넣기 전에 리터럴에서 맨 뒤의 값이 반올림 되거나 잘린다.
d = 999999999999999.7;
System.out.println(d);

 주의!
'유효자릿수'는 부동소수점의 저장 가능 여부를 간단히 계산하기 위해 나온 개념이다.
실제는 유효자릿수에 해당하는 부동소수점이라도 정규화 과정을 통해 2 진수로 변환되는 과정에서
값이 짤리는 경우가 있음을 잊지 말라!


 단정도(single precision)와 배정도(double precision)
- double 변수는 float 변수에 두 배 정도 더 정밀한 값을 저장할 수 있다.
  그래서 '배정도(double precision)'라 한다.
- float은 double과 비교하여 한 배 정도 정밀한 값을 저장할 수 있다.
  그래서 '단정도(single precision)'이라 한다.

 

 

# 부동소수점 변수 - 변수와 리터럴의 크기 II

 주의!
 - 잘못된 리터럴 값을 변수에 저장해봐야 소용없다!

d = 99999.8888877777f;
System.out.println(d);
// 4바이트 크기를 넘어가는 부동소수점 리터럴은 이미 짤린 값이기 때문에 제대로 저장될 수 없다.


d = 99999.8888877777; // 리터럴 값도 정상. 변수에 저장하는 것도 정상
System.out.println(d);

// 주의!
// 정수의 경우 리터럴을 표현할 때 
// 값이 그 크기를 넘어가면 컴파일 오류가 발생하지만,
// 부동소수점의 경우 리터럴이 메모리의 크기를 넘어갈 때
// 컴파일 오류 대신 단지 값이 짤린다.


// 변수의 크기에 맞춰 부동소수점의 리터럴을 지정하라.
float f = 99999.88f;
System.out.println(f);

d = 99999.8888877777;
System.out.println(d);

// 4바이트 부동소수점을 8바이트 메모리에 저장할 때
// 계산 방식에 의해 소수점 이하의 수가 근사 값으로 바뀐다.
// => 8바이트 메모리에 값을 저장할 때는 8바이트 리터럴을 사용하라.
d = 99999.88f;
System.out.println(d);

 

 

# 부동소수점 변수 - 변수와 리터럴의 크기 III
float f1 = 99988.88f;
float f2 = 11.11111f;

// 각 변수의 값이 개별적으로 옳은 부동소수점이라도,
System.out.println(f1);
System.out.println(f2);

// 주의!
// 연산 결과가 해당 타입의 크기를 벗어 난다면 그 결과 값이 짤린다.
//     99988.88
//   +    11.11111
//  -----------------
//     99999.99111  <== 계산 결과가 4바이트 유효자릿수를 넘어간다.   
//     계산 결과는 유효자릿수 범위 내에서 반올림되거나 잘린다.
float f3 = f1 + f2; 
System.out.println(f3);

// 그래서 부동소수점을 다룰 때는 가능한 float 보다 두 배 더 정밀한 double을 사용하라!
// 문법에서도 double 리터럴을 기본 부동소수점으로 간주한다.
// 즉 부동소수점 뒤에 d나 D 표시를 생략할 수 있다.
double d1 = 99988.88;
double d2 = 11.11111;
double d3 = d1 + d2;
System.out.println(d3);

 부동소수점 사용법
 - 유효자릿수 7자리 이하의 값을 단순이 저장하고 꺼내는 경우 float 타입을 사용하라!
   예) float height; 
       float weight;
 - 부동소수점에 대해 연산을 수행하는 경우 double 타입을 사용하라!
   예) double average;
       double area;

 

 

# 문자 변수 - UCS-2 문자 코드 값 저장
char c1, c2, c3;

// 문자 'A'를 변수에 저장하는 방법?
//- 문자 'A'의 UCS-2 코드 값을 저장하면 된다.
//

c1 = 65;            // 문자 'A'의 코드 값을 10진수로 표현한 것.
c2 = 0x41;          // 문자 'A'의 코드 값을 16진수로 표현한 것.
c3 = 0b0100_0001;   // 문자 'A'의 코드 값을 2진수로 표현한 것.

// println()을 통해 문자를 출력할 수 있다.
//- 변수의 종류가 char 이면 출력할 때 코드 값 대신 문자를 출력한다.
//- 해당 코드의 문자를 폰트 파일에서 찾아 출력한다.
//
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);

 

 

# 문자 변수 - 따옴표('', single quotation) 활용하기

 문자 코드 값을 모른다면 따옴표를 사용하라!
 - 문법
   '문자' : UCS-2에 정의된 코드 값을 리턴한다.

char c = 'A';  // c 변수에 저장되는 것은 문자 'A'의 UCS-2 코드 값이다.
System.out.println(c);

//문자의 UCS-2 코드 값을 확인하기
//- 정수 변수를 사용하면 문자의 코드 값을 받아서 그래도 출력할 수 있다.
//  println()은 int 변수의 값은 10진수로 출력한다.
int i = 'A';
System.out.println(i);

 

 

문자 변수 - UCS-2 코드 확인하기

 자바는 문자를 저장할 때 UCS-2에 정의된 정수 값을 저장한다.
 확인해 보자!

// 영어 대문자의 문자 코드 저장
int v = 0x41;  // 65(10진수)

// 코드 값을 1씩 증가하면서 문자를 출력해 보자!
for (int i = 0; i < 26; i++) {
  // 정수를 그냥 출력하면 10진수를 출력한다.
  // 정수가 문자 코드임을 알려줘야만 println()은 해당 코드의 문자를 출력한다.
  // 문법
  //   (char) 정수
  //System.out.print(v + i);
  System.out.print((char)(v + i));
  System.out.print(' ');

 

 

# 논리값 변수 - 변수의 메모리 크기
// 논리 값을 담을 변수는 JVM 내부에서 4바이트 크기의 int 타입 메모리를 사용한다.  
boolean b1, b2;

b1 = true; // 실제 메모리에는 1을 저장한다.
b2 = false; // 실제 메모리에는 0을 저장한다.

System.out.println(b1);
System.out.println(b2);

// 주의!
// - JVM 내부에서 true, false를 정수 값으로 다룬다고 해서 boolean 변수에 직접 1과 0을 저장할 수 없다.
//b1 = 1; // 컴파일 오류!
//b2 = 0; // 컴파일 오류!

 

 

# 레퍼런스 변수
java.util.Date d1 = new java.util.Date();
java.util.Date d2 = d1;

System.out.printf("%d, %d\n", d1.getDate(), d2.getDate());

d1.setDate(22);

System.out.printf("%d, %d\n", d1.getDate(), d2.getDate());

 d1에 저장된 일자 값을 변경한 후 
 d2에 저장된 일자 값을 출력해 보면 d1과 똑같이 변경되어 있다.
 이유는?
 d1과 d2에 저장되는 것은 값이 아니라 (날짜 정보가 저장되어 있는 메모리의) 주소이다.
 이렇게 값을 저장하지 않고 값이 저장된 위치(주소)를 저장하는 변수를 
 "레퍼런스 (변수)"라 부른다.
 자바 기본 데이터 타입(byte, short, int, long, float, double, boolean, char)을
 제외한 모든 타입의 변수는 레퍼런스이다.

 

 

# 배열 - 배열을 선언하는 방법

 배열?
 - 같은 종류의 메모리를 쉽게 만드는 방법

 - 문법
        메모리종류[] 메모리이름 = new 메모리종류[개수]
        데이터타입[] 변수명 = new 데이터타입[개수];
        ex) int[] arr = new int[5];

 - C언어 스타일 
        데이터타입 변수명[] = new 데이터타입[개수];
        ex) int arr[] = new int[5];

 배열의 개수는 int 타입의 최대 값과 같다.
 즉 2147483647 개 이다.

 

배열의 크기는 int 타입의 최대 값에서 2를 뺀 값이다.
배열의 최대 크기 = Integer.MAX_VALUE - 2

int[] arr3 = new int[2147483647]; // 실행 오류 => VM의 배열 크기 제한을 초과
Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit

int[] arr3 = new int[2147483645]; // OK!
int[] arr3 = new int[Integer.MAX_VALUE - 2]; // OK!

실행 오류 발생!
=> Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
=> JVM이 OS로부터 사용 허가를 받은 메모리 크기를 벗어났기 때문에 발생한 것이다.

해결책?
=> JVM을 실행할 때 최대 힙(heap) 메모리의 크기를 늘리면 된다.
=> JVM 실행 옵션에 다음을 추가하라!
     -Xmx메모리크기
     예) $ java -Xmx20000m ...

 

 

# 배열 - 배열 메모리에 접근 방법

배열 메모리에 접근
 - 문법
          배열변수[인덱스] = 값;
          ex) arr1[0] = 100;

 - 배열의 인덱스는 0부터 시작한다.
 - 인덱스의 범위는 0 ~ (배열개수 - 1) 이다.

 유효하지 않은 인덱스를 지정한다면?
 - 실행 중에 오류가 발생한다.
 - 컴파일 할 때는 인덱스가 유효한지 알 수 없다.

    arr1[5] = 600; // 실행 오류(Runtime Error) 
    arr1[-1] = 700; // 실행 오류(Runtime Error)

 

 

# 배열 - 배열이 필요할 때: 같은 종류의 메모리를 많이 다룰 경우
// 배열 변수에 들어 있는 값의 합계를 구해 보자!
int sum2 = 0;
/*
for (int i = 0; i < arr1.length; i++) {
  sum2 = sum2 + arr1[i];
}
 */
for (int item : arr1) {
  sum2 = sum2 + item;
}

 

 

# 배열 - 배열 레퍼런스와 배열 인스턴스
int[] arr1 = new int[5];

 배열 선언문 분석:
 arr1
   - 배열 메모리의 주소를 담는 변수이다.
   - 이렇게 메모리의 주소를 보관하는 변수를 '레퍼런스'라 부른다.

 new int[5]
   - new 명령은 사용할 메모리를 확보하는 명령이다.
   - 즉 연속된 5 개의 int 타입 메모리를 준비하라는 명령이다.
   - 사용할 메모리를 확보한 후 그 메모리의 찾아 갈 수 있도록 시작 주소를 리턴한다.
   - 이렇게 값을 저장하기 위해 확보된 메모리를 "인스턴스"라 부른다.

 new 명령은 메모리를 확보하는 명령이다.
 - 리턴 값은 확보된 메모리의 시작 주소이다.

 

 [정리]
 레퍼런스(reference)란?
 - 값이 아닌 메모리의 주소를 담는 변수.

 인스턴스(instance)란?
 - 값을 저장하는 메모리.

 

 

# 배열 - 배열 레퍼런스와 null
int[] arr1;
arr1 = new int[5];

// 배열 레퍼런스를 초기화시키고 싶다면 null로 설정하라.
arr1 = null; // 0으로 설정. 즉 레퍼런스가 아무것도 가리키지 않음을 의미.

// 레퍼런스가 배열 인스턴스를 가리키지 않은 상태에서 사용하려 하면?
System.out.println(arr1[0]); // NullPointerException 실행 오류!

 

 

# 배열 - 배열 메모리 초기화
// 배열 선언
int[] arr1 = new int[5]; // OK!

// 배열 메모리를 특정 값으로 초기화 
arr1[0] = 100;
arr1[1] = 90;
arr1[2] = 80;
arr1[3] = 70;
arr1[4] = 60;

// 1) 배열 선언 + 초기화 명령
int[] arr2 = new int[]{100, 90, 80, 70, 60}; // OK!
//int[] arr2 = new int[5]{100, 90, 80, 70, 60}; // 문법오류! 배열 개수 지정하면 안된다. 

// 2) 배열 선언 + 초기화 명령 II
int[] arr3 = {100, 90, 80, 70, 60}; // new int[] 생략 가능

// 그러나 배열 변수를 선언한 후 따로 초기화시킬 때는
// 다음과 같이 new 명령을 생략할 수 없다.
int[] arr4;
//    arr4 = {100, 90, 80, 70, 60}; // 컴파일 오류!

// 배열 초기화 명령을 따로 실행할 때는 new int[] 를 생략할 수 없다.
int[] arr5;
arr5 = new int[]{0, 0, 0, 0, 0};

 정리!
 1) 배열 선언 + 초기화
    데이터타입[] 변수명 = new 데이터타입[]{값, 값, 값};
    - 배열 메모리를 초기화시킬 때는 배열 개수를 지정해서는 안된다.
    - 배열을 초기화시키는 값의 개수 만큼 메모리가 만들어진다.
    - 즉 다음은 값 개수만큼 int 메모리가 3개가 생성된다.
      ex) int[] arr = new int[]{10, 20, 30};
    - 다음과 같이 new 명령을 생략할 수 있다.
      데이터타입[] 변수명 = {값, 값, 값};
      ex) int[] arr = {10, 20, 30};
 
 2) 배열 선언 후 따로 배열 초기화 문장 실행
    데이터타입[] 변수명;
    변수명 = new 데이터타입[]{값, 값, 값};
    ex) 
    int[] arr1;
    arr1 = new int[]{10, 20, 30}; 
    - 변수를 선언한 후 따로 배열을 초기화시킬 때는 
      new 명령을 생략할 수 없다.

 

 

# 배열 - 배열 인스턴스와 가비지
int[] arr1;
arr1 = new int[5];
arr1[0] = 100;

// 레퍼런스에 다른 배열 인스턴스의 주소를 담을 수 있다.
arr1 = new int[] {200, 200, 200};
// 주의!
// - arr1이 보관하고 있던 주소는 잃어버렸기 때문에 이전 배열 인스턴스는 사용할 수 없다.
// - 이렇게 주소를 잃어버려 사용할 수 없는 인스턴스(메모리)를 '가비지(garbage)'라 부른다.

## 가비지(garbage)
- 주소를 잃어 버려 사용할 수 없는 메모리
- 특정 조건이 되면 가비지 수집기(garbage collector)에 의해 메모리 해제된다.
  메모리 해제? 다른 용도로 사용할 수 있도록 표시한다는 의미다.
  
## 가비지 수집 조건 = 가비지 컬렉터가 동작할 때
- 메모리가 부족할 때
  - 운영체제로부터 메모리를 추가로 받기 전에 먼저 기존에 사용하던 메모리에서 
    가비지를 제거한다.
- CPU가 한가할 때
  - 24시간 365일 내내 실행하는 서버 프로그램인 경우, 실행 중간에 CPU가 한가할 때 
    가비지를 제거한다.
- 주의!
  - 프로그램(JVM)을 종료하면 JVM 사용한 메모리를 운영체제가 모두 회수한다.
  
## 가비지를 강제로 지우도록 명령하는 방법?
- 자바는 없다!
- C 언어 => free(메모리주소);
- C++ 언어 => delete 객체주소;
- 요즘 언어의 트랜드는 사용하지 않는 메모리를 개발자가 직접 해제하는 것이 아니라 
  VM이 해제하는 것이다.
  예) JavaScript, C#, Python, PHP, Go, Java 등
- 요즘 언어의 트랜드는 VM으로 실행하는 것이다.
  왜? 직접 기계어로 전환되면 메모리를 관리를 자동으로 수행할 수 없다.
  
## 가비지 컬렉터를 강제로 실행하는 방법?
- 없다!
- 단 원래 계획보다 가능한 빨리 실행하라고 독촉하는 방법은 있다.
  System.gc() 메서드 호출
- 그런데 바로 실행할 지 나중에 실행할 지 그 시점을 보장하지는 않는다. 

 

 

# 상수 - 변수의 값을 고정하는 방법
// 변수
// => 언제든지 값을 변경할 수 있다.
int a; //static type binding
a = 10;
a = 100;
a = -300;

// 상수
// => 값을 오직 한 번만 저장할 수 있다.
// => 변수 앞에 final을 붙여라!
final int b;
b = 100;

// 만약 final로 선언된 변수의 값을 두 번째로 바꾸려 한다면?
//    b = 200; // 컴파일 오류!

// 변수와 상수를 구분하기 위해서
// => 상수인 경우 이름을 보통 모두 대문자로 작성한다.
// => 그리고 변수를 선언하는 문장에서 바로 값을 초기화시킨다.
final float PI = 3.14159f;
final float MILE = 0.621371f;

 

 

# 변수의 종류
public class Exam0710 {

  int a; // 인스턴스 변수

  static int b; // 클래스 변수 == 스태틱 변수


  public static void main(String[] args/*로컬변수=파라미터*/) {

    int c; // 로컬 변수

  }
}

 인스턴스 변수(instance variable)
 - new 명령을 사용하여 인스턴스를 생성할 때 준비되는 변수

 클래스 변수(class variable = static variable)
 - 클래스가 로딩될 때 준비되는 변수

 로컬 변수(local variable)
 - 블록을 실행할 때 준비되는 변수

 파라미터(parameter)
 - 메서드의 아규먼트를 받는 로컬 변수이다.
   예) 위의 코드에서 main()의 args 로컬 변수

 

 

 

# 변수의 종류 - 종류에 따른 변수의 사용 범위
class MyClass {
  int a = 11; // 인스턴스 변수
  static int b = 22; // 클래스 변수

  static void m1(int v1/*로컬변수=파라미터*/) {
    int v2; // 로컬 변수
    v2 = 200;

    System.out.println(v1);
    System.out.println(v2);
  }
}

public class Exam0720 {
  public static void main(String[] args) {

    //1) 인스턴스 변수를 사용하는 방법
    MyClass obj1 = new MyClass(); // new 명령이 실행될 때 변수가 준비된다.
    System.out.println(obj1.a); // 인스턴스를 가리키는 레퍼런스를 통해 변수를 사용할 수 있다.

    //2) 클래스 변수를 사용하는 방법
    System.out.println(MyClass.b); // 클래스를 사용(로딩)하는 시점에 준비된다.


    //3) 로컬 변수를 사용하는 방법
    MyClass.m1(100); // 메서드를 실행할 때 변수가 준비된다. 메서드를 실행하는 동안만 

    // 메서드 호출이 끝나면, 그 메서드의 로컬 변수를 사용할 방법이 없다.
    //    v1 = 300; // 오류!
    // 메서드를 실행하는 동안, 그 메서드 안에서만 사용할 수 있다.
  }
}

 

 

# 변수의 종류 - 같은 블록에서는 중복 선언 불가
public class Exam0810 {

  int a; // 인스턴스 변수

  static int b; // 클래스 변수

  // 클래스 블록 안에 선언된 변수는 종류에 상관없이 중복 선언 불가!
  //  int a; // 컴파일 오류!
  //  static int a; // 컴파일 오류!
  //  int b; // 컴파일 오류!
  //  static int b;  // 컴파일 오류!

  public static void main(String[] args/*로컬변수=파라미터*/) {
    // 그러나 메서스 블록에서는 클래스에 선언된 변수의 이름과 
    // 같은 변수를 선언할 수 있다.
    // 왜? 영역이 다르니까!
    int a;
    int b;

    // 마찬가지로 이 블록 안에서는 같은 이름의 변수를 만들 수 없다.
    //    String a; // 컴파일 오류! 데이터 타입에 상관없이 이름 중복 불가!
  }

  public static void m2() {
    // 여기에 선언된 변수는 main() 블록에 선언된 변수와 다른 변수이다.
    int a;
    int b;
  }

}

 

 

# 로컬 변수와 블록
public static void main(String[] args) {

int a = 100;

{
  //      int a = 200; // 컴파일 오류!
  // 메서드 안에서는 변수 이름 중복 불가!
  // 안쪽 블록에서는 바깥 블록의 변수를 사용할 수 있다.
  a = 200;

  int b = 300;
  // 이 블록의 실행을 끝내는 순간 
  // 이 블록에서 선언된 모든 변수는 제거된다.
  // 그래서 b 변수는 제거된다.
}
System.out.println(a);

// 바깥 블록에서는 안쪽 블록의 변수를 사용할 수 없다.
// 왜?
// 안쪽 블록의 실행이 끝나면, 안쪽 블록에서 선언된 모든 변수들이
// 자동으로 제거된다.
//    System.out.println(b); // 컴파일 오류!

{
  // 이전 블록에서 선언된 변수는 그 블록이 종료되는 순간 
  // 제거되기 때문에, 
  // 다음과 같이 같은 이름의 변수를 선언할 수 있다.
  int b = 400;
}

 

 

# 로컬 변수와 블록 III
int a = 100;

switch (a) {
  case 1:
    //switch 바깥 블록에 같은 이름의 변수가 있다.
    //        int a; //컴파일 오류! 

    // case 문에서 선언한 변수는 switch 문에 소속된다.
    int b;
    {
      // 다음과 명확하게 블록 안에 선언하면
      // 이 블록의 변수가 된다.
      int c; 
    }
    break;
  case 2:
    // case는 다른 블록으로 취급되지 않는다.
    // 위쪽 case에서 선언한 변수는 switch 문에 소속된다.
    // 따라서 다음과 같이 아래쪽 case에서 
    // 같은 이름의 변수를 선언할 수 없다.
    //        int b; // 컴파일 오류!
    int c; // OK!

    break;

  case 3:
    for (int i = 0; i < 100; i++) {
      // for의 바깥 블럭인 switch 문에 이미 b 변수가 있다. 
      //          int b; // 컴파일 오류!
    }
    break;
  default:
}

 

 

# 값 저장과 메모리 크기 - 정수는 부동소수점 메모리에 저장할 수 있다.

주의!
- 부동소수점 메모리의 유효자릿수를 넘어가는 정수를 저장할 경우 값이 짤린다.
- 그럼에도 컴파일 오류가 발생하지 않는다.

byte b = 100; // 1byte
short s = 32767; // 2byte(-32768 ~ 32767)
int i = 98765678; // 4byte(약 -21억 ~ +21억)
long l = 18_2345_3456_4567_5678L; // 8byte(약 -922경 ~ 922경)

float f;
double d;

// 주의!
// - float의 자릿수가 넘어가는 정수를 저장하는 경우 값이 짤릴 수 있다.
// - 그럼에도 불구하고 컴파일 오류가 발생하지 않는다.
// - 그래서 정수 값을 부동소수점 메모리에 저장할 때 특히 주의해야 한다.

f = b; // byte(1) ==> float(4). 값을 그대로 저장.
System.out.println(f);

f = s; // short(2) ==> float(4). 값을 그대로 저장. 
System.out.println(f);

f = i; // int(4) ==> float(4). 
// 유효자릿수를 넘는 정수는 짤린다.
System.out.println(f);

f = l; // long(8) ==> float(4)
// 유효자릿수를 넘는 정수는 짤린다.
System.out.println(f);

d = i; // int(4) ==> double(8)
// 유효자릿수 범위의 정수 값이므로 int(32비트) 값을 그대로 저장할 수 있다.
System.out.println(d);       

d = l; 
// 유효 범위를 넘어가는 정수인 경우 짤린다.
// 주의! 컴파일 오류가 발생하지 않는다.
System.out.println(d);

 정리!
 - 정수 메모리의 값(byte, short, char, int, long)을 
   부동소수점 메모리(float, double)에 저장할 때
   주의해서 사용하라!
 - 유효자릿수를 넘어가는 정수 값인 경우 부동소수점 메모리에 저장될 때 짤릴 수 있다.
   그럼에도 컴파일 오류가 발생하지 않기 때문에
   개발자들이 놓치는 경우가 많다!

 

 

# 형변환 - 명시적 형변환

 부동소수점 메모리의 값은 정수 메모리에 저장할 수 없다.
 그럼에도 저장하려 한다면, 명시적 형변환 문법을 사용해야 한다.
 
 명시적 형변환을 통해 부동소수점의 값을 정수 메모리에 저장할 때
 소수점 이하의 값이 짤린다.

 "명시적 형변환(explicit type conversion = explicit type casting)"?
 - 컴파일러에게 강제로 값을 넣을 것을 명령하는 것.

// float ==> int 
float f = 3.14f;
int i = (int)f;  // 소수점 이하가 제거된다.
System.out.println(i);

// double ==> long
double d = 9876.56789;
long l = (long)d; // 소수점 이하가 제거된다.

 명시적 형변환
 - 큰 메모리의 값을 작은 메모리로 변환 할 때 
 - 부동소수점을 정수로 변환 할 때  
 - 문법
   변수 = (바꾸고자하는타입) 변수 또는 값;

 

 

# 형변환 - 명시적 형변환 : 큰 정수 변수의 값을 작은 정수 변수에 저장
byte b = 100;
short s = 100;
int i = 100;
long l = 100;

// 큰 메모리의 정수 값을 작은 메모리에 저장하는 경우
// => 컴파일 오류가 발생한다.
// => 해결책? 명시적 형변환을 해야 한다.

// 1) 큰 메모리의 값이 작은 메모리에 충분히 들어가는 경우
// short ==> byte
byte b2 = (byte)s; // 명시적 형변환
System.out.println(b2);

// int ==> byte
b2 = (byte)i; // 명시적 형변환
System.out.println(b2);

// long ==> byte
b2 = (byte)l; // 명시적 형변환
System.out.println(b2);

// 2) 큰 메모리의 값이 작은 메모리에 들어 갈 수 없는 경우
// => 앞 쪽 바이트의 값이 짤려서 들어간다.
int i2 = 0b0000_0000_0000_0000_0000_0001_0010_1100; // = 300(10진수)
b2 = (byte) i2; // 
System.out.println(b2); // 0b0010_1100

l = 400_0000_0000L; // 0x00_00_00_09_50_2f_90_00
i = (int)l; // 큰 메모리의 값이 작은 메모리에 들어가지 못하는 경우
System.out.println(l); // 앞 쪽 바이트가 짤린다.
System.out.println(i);
System.out.println(0x502f9000);

 정리!
 - 큰 값을 작은 메모리에 넣는 것은 아무런 의미가 없다.
 - 해서는 안된다.
 - 다만,
   큰 메모리의 값을 바이트 단위로 쪼개고 싶을 때 유용하다. 

 

 

# 형변환 - 명시적 형변환이 불가능한 경우

 1) 정수 메모리끼리 형변환이 가능하다.
 2) 부동소수점을 정수로 형변환이 가능한다.
 3) 형변환 없이 정수는 부동소수점 메모리에 저장할 수 있다.
 4) 숫자를 문자 코드로 형변환 가능한다.
 그 외에는 형변환 불가!

// 예) 정수, 부동소수점 ==/==> boolean
boolean bool;
//    bool = (boolean) 1; // 컴파일 오류!

// 예) 문자열 ==/==> 정수, 부동소수점, 문자, boolean
//    boolean v1 = (boolean) "true"; // 컴파일 오류!
//    char v2 = (char) "가"; // 컴파일 오류!
//    int v3 = (int) "123"; // 컴파일 오류!
//    float v4 = (int) "3.14f"; // 컴파일 오류!

 

 

# 형변환 - 명시적 형변환이 불가능한 경우, 특별한 메서드를 사용하면 가능하다.
byte b = Byte.valueOf("100");
short s = Short.valueOf("32767");
int i1 = Integer.valueOf("2122223333"); // 문자열 ==> int
int i2 = Integer.parseInt("2122223333"); // 문자열 ==> int
long l = Long.valueOf("9221111222233334444"); 
float f1 = Float.valueOf("3.14f");
float f2 = Float.parseFloat("3.14f");
double d = Double.valueOf("9876.54321");
boolean bool1 = Boolean.valueOf("TRUE");   // 문자열 ==> boolean
boolean bool2 = Boolean.parseBoolean("TRUE");   // 문자열 ==> boolean
char c = "가나다".charAt(1); // => 나