[자바의 정석] 변수

Jiwon An·2023년 10월 2일
0

Java

목록 보기
5/9

변수란?

변수란 단 하나의 값을 저장할 수 있는 메모리 공간을 의미합니다.
변수를 선언하면, 메모리의 빈 공간에 '변수타입'에 알맞은 크기의 저장공간이 확보되고, 앞으로 이 저장공간은 '변수이름'을 통해 사용할 수 있게 됩니다.

예를 들어 int age; 선언하면 int 타입에 알맞은 크기인 4byte의 공간이 확보되고, 이 공간은 age라는 이름을 통해 사용할 수 있게 됩니다.

변수의 초기화

변수의 초기화란 변수를 사용하기 전에 처음으로 값을 저장하는 것을 의미합니다.
변수를 선언한 이후부터는 변수를 사용할 수 있으나, 그 전에 반드시 변수를 초기화해야 합니다. 메모리는 여러 프로그램이 공유하는 자원이므로 전에 다른 프로그램에 의해 저장된 알 수 없는 값(쓰레기값, garbage value)이 남아있을 수 있기 때문입니다.
변수의 종류에 따라 변수의 초기화를 생략할 수 있는 경우도 있지만, 변수는 사용되기 전에 적절한 값으로 초기화하는 것이 좋습니다. 지역변수는 사용되기 전에 초기화를 반드시 해야 하지만 클래스변수와 인스턴스변수는 초기화를 생략할 수 있습니다.

변수의 명명규칙

'변수의 이름'처럼 프로그래밍에서 사용하는 모든 이름을 '식별자(identifier)'라고 하며, 식별자는 같은 영역 내에서 서로 구분(식별)될 수 있어야합니다. 그리고 식별자를 만들 때는 다음과 같은 규칙을 지켜야 하빈다.

  1. 대소문자가 구분되며 길이에 제한이 없다.
    • True와 true는 서로 다른 것으로 간주된다.
  2. 예약어를 사용해서는 안 된다.
    • true는 예약어라서 사용할 수 없지만, True는 가능하다.
  3. 숫자로 시작해서는 안 된다.
    • top10은 허용되지만, 7up은 허용되지 않는다.
  4. 특수문자는 '_'와 '$'만을 허용한다.
    • $harp은 허용되지만, S#arp은 허용되지 않는다.

그 외 필수적인 것은 아니지만 자바 프로그래머들에게 권장하는 규칙들은 아래와 같습니다.
반드시 지켜야 하는 것은 아니지만, 코드를 보다 이해하기 쉽게 하기 위한 자바 개발자들 사이의 암묵적인 약속이다.

  1. 클래스 이름의 첫 글자는 항상 대문자로 한다.
    • 변수와 메서드의 이름의 첫 글자는 항상 소문자로 한다.
  2. 여러 단어로 이루어진 이름은 단어의 첫 글자를 대문자로 한다.
    • lastIndexOf, StringBuffer
  3. 상수의 이름은 모두 대문자로 한다. 여러 단어로 이루어진 경우 '_'로 구분한다.
    • PI, MAX_NUMBER

변수의 이름은 짧을수록 좋지만, 약간 길더라도 용도를 알기 쉽게 '의미있는 이름'으로 하는 것이 바람직합니다. 변수의 선언문에 주석으로 변수에 대한 정보를 주는 것도 좋은 생각 입니다.

int curPos = 0; // 현재 위치(current position)

변수의 타입

자료형은 크게 기본형과 참조형 두 가지로 나눌 수 있는데, 기본형 변수는 실제 값(data)를 저장하는 반면, 참조형 변수는 어떤 값이 저장되어 있는 주소(memory address)를 값으로 갖습니다. 자바는 C언어와 달리 참조형 변수 간의 연산을 할 수 없으므로 실제 연산에 사용되는 것은 모두 기본형 변수입니다.

메모리에는 1 byte 단위로 일련번호가 붙어있는데, 이 번호를 메모리 주소(memory address)또는 간단히 주소라고 합니다. 객체의 주소는 객체가 저장된 메모리 주소를 뜻합니다.

기본형 (primitive type)

  • 논리형(boolean), 문자형(char), 정수형(byte, short, int, long), 실수형(float, double)
  • 계산을 위한 실제 값을 저장합니다.

참조형 (reference type)

  • 객체의 주소를 저장합니다.
  • 8개 기본형을 제외한 나머지 타입

참조형 변수(또는 참조변수)를 선언할 때는 변수의 타입으로 클래스의 이름을 사용하므로 클래스의 이름이 참조변수의 타입이 됩니다. 그래서 새로운 클래스를 작성한다는 것은 새로운 참조형을 추가하는 셈입니다.

객체를 생성하는 연산자 new의 결과는 생성된 객체의 주소입니다. 참조형 변수는 null 또는 객체의 주소(4 byte, 0x0 ~ 0xFFFFFFFF)를 값으로 갖습니다. null은 어떤 객체의 주소도 저장되어 있지 않음을 뜻합니다. 단, JVM이 32bit가 아니라 64bit라면 참조형 변수의 크기는 8 byte가 됩니다.

기본형(primitive type)

1. 논리형

  • boolean (1byte)
  • true와 false 중 하나를 값으로 갖으며, 조건식과 논리적 계산에 사용
  • 두가지 값만 표현하면 되므로 1bit만으로도 충분하지만, 자바에서는 데이터를 다루는 최소단위가 byte이기 때문에, boolean의 크기는1byte

2. 문자형

  • char (2byte)
  • 문자를 저장하는데 사용되며, 변수에 하나의 문자만 저장 가능
  • 변수에 '문자'가 저장되는 것 같지만, 사실은 문자가 아닌 '문자의 유니코드(정수)'가 저장된다. 컴퓨터는 숫자밖에 몰기 때문에 모든 데이터를 숫자로 변환하여 저장하는 것이다. 그래서 문자 리터럴 대신 문자의 유니코드를 직접 저장할 수 있습니다.
    '가' == 0xAC00 == '\uAC00'
  • 2 byte이므로, 16자리의 2진수로 표현할 수 있는 정수의 개수인 65536의 코드를 사용할 수 있으며, char형 변수는 이 범위 내의 코드 중 하나를 저장할 수 있습니다.

3. 정수형

  • byte(1byte), short(2byte), int(4byte), long(8byte)
  • byte는 이진 데이터를 다룰 때 사용되며, short은 C언어와의 호환을 위해서 추가됨
  • 정수를 저장하는데 사용되며, 주로 int가 사용됨

4. 실수형

  • float, double
  • 실수를 저장하는데 사용되며, 주로 double이 사용됨

자료형의 범위

정수형

자료형저장 가능한 값의 범위크기
int231-2^{31} ~ 23112^{31} - 1, 약 ±20\pm204byte
long263-2^{63} ~ 26312^{63} - 18byte
float1.4E-45 ~ 3.4E38 (1.410451.4 * 10^{-45} ~ 3.410383.4 * 10^{38})4byte
double4.9E-324 ~ 1.8E308 (4.9103244.9 * 10^{-324} ~ 1.8103081.8 * 10^{308})8byte

각 자료형이 가질 수 있는 값의 범위를 정확히 외울 필요는 없고, 정수형(int, long)의 경우 '2n1-2^{n-1} ~ 2n112^{n-1} - 1' (n은 bit수)이라는 정도만 기억하고 있으면 됩니다. 7~9자리의 수를 계산할 때는 넉넉하게 long타입(약 19자리)으로 변수를 선언하는 것이 좋습니다. 연산중에 저장범위를 넘어서게 되면 원하지 않는 값을 결과로 얻게 될 것이기 때문입니다.

실수형

자료형저장 가능한 값의 범위정밀도크기
float1.4E-45 ~ 3.4E38 (1.410451.4 * 10^{-45} ~ 3.410383.4 * 10^{38})7자리4byte
double4.9E-324 ~ 1.8E308 (4.9103244.9 * 10^{-324} ~ 1.8103081.8 * 10^{308})15자리8byte

실수형은 정수형과 저장형식이 달라서 같은 크기라도 훨씬 큰 값을 표현할 수 있으나 오차가 발생할 수 있다는 단점이 있습니다. 그래서 정밀도(precision)가 중요한데, 정밀도가 높을수록 발생할 수 있는 오차의 범위가 줄어듭니다.

float는 약 ±1038\pm10^{38}과 같이 큰 값을 저장할 수 있지만, 정밀도가 7자리 밖에 되지 않을므로 보다 높은 정밀도가 필요한 경우에는 double을 선택해야합니다. 실수형에서는 저장 가능한 값의 범위뿐만 아니라 정밀도도 타입 선택의 중요한 기준이 됩니다.

float는 접미사나 정밀도 등 신경 쓸 것이 많아서 이런 것들이 귀찮다면 그냥 double을 사용하자.

정수형의 선택기준

변수에 저장하려는 정수값의 범위에 따라 4개의 정수형 중에서 하나를 선택하면 되겠지만, byte나 short보다 int를 사용하도록 하자. byte와 short이 int보다 크기가 작아서 메모리를 조금 더 절약할 수는 있지만, 저장할 수 있는 값의 범위가 작은 편이라서 연산 시에 범위를 넘어서 잘못된 결과를 얻기 쉽습니다. 그리고 JVM의 피연산자 스택(operand stack)이 피연산자를 4 byte단위로 저장하기 때문에 크기가 4 byte보다 작은 자료형(byte, short)의 값을 계산할 때는 4 byte로 변환하여 연산이 수행됩니다. 그래서 오히려 int를 사용하는 것이 더 효율적입니다.
결론적으로 정수형 변수를 선언할 때는 int타입으로 하고, int의 범위(약 ±20\pm20 억)를 넘어서는 수를 다뤄야할 때는 long을 사용하면 됩니다. 그리고 byte나 short은 성능보다 저장공간을 절약하는 것이 더 중요할 때 사용하자.

long타입의 범위를 벗어나는 값을 다룰 때는 실수형 타입이나 BigInteger클래스를 사용하면 됩니다.

정수형의 오버플로우

연산과정에서 해당 타입이 표현할 수 있는 값의 범위를 넘어서는 것을 오버플로우(overflow)라고 합니다. 오버플로구가 발생했다고 해서 에러가 발생하는 것은 아닙니다. 다만 예상했던 결과를 얻지 못할 뿐입니다. 애초부터 오버플로우가 발생하지 않게 충분한 크기의 타입을 선택해서 사용해야 합니다.

실수형의 선택기준

정수형과 달리 실수형은 오차가 발생할 수 있다는 단점이 있습니다. 그래서 실수형에는 표현할 수 있는 값의 범위뿐만 아니라 정밀도(precision)도 중요한 요소입니다. float타입은 정밀도가 7자리인데, 이것은 a10n(1a<10)a * 10^{n} (1\leqq a \lt 10) 의 형태로 표현된 '7자리의 10진수를 오차없이 저장할 수 있다'는 뜻입니다.
만일 7자리 이상의 정밀도가 필요하다면, 변수의 타입을 double로 해야합니다. 실수형 값을 저장할 때 float타입이 아닌 double타입의 변수를 사용하는 경우는 대부분 저장하려는 '값의 범위'때문이 아니라 '보다 높은 정밀도'가 필요해서입니다.
연산속도의 향상이나 메모리를 절약하려면 float를 선택하고, 더 큰 값의 범위라던가 더 높은 정밀도를 필요로 한다면 double을 선택해야 합니다.

실수형의 오버플로우

실수형에서도 변수의 값이 표현범위의 최대값을 벗어나면 오버플로우가 발생하는데, 정수형과 달리 실수형에서는 오버플로우가 발생하면 변수의 값은 무한대가 됩니다. 그리고 정수형에는 없는 언더플로우(underflow)가 있는데, 언더플로우는 실수형으로 표현할 수 없는 아주 작은 값, 즉 양의 최소값보다 작은 값이 되는 경우를 말합니다. 이 때 변수의 값은 0이 됩니다.

형변환

형변환이란 변수 또는 상수의 타입을 다른 타입으로 변환하는 것입니다. boolean을 제외한 나머지 7개의 기본형은 서로 형변환이 가능합니다. 기본형과 참조형은 서로 형변환할 수 없습니다.

기본형 : boolean, char, byte, short, int, long, float, double

형변환 방법

(타입) 피연산자

정수형 간의 형변환

정수형간의 형변환은 큰 타입에서 작은 타입으로 변환하는 경우에 경우에 따라 값 손실이 발생할 수 있습니다. 반대로 작은 타입에서 큰 타입으로의 변환은 저장공간의 부족으로 잘려나가는 일이 없으므로 값 손실이 발생하지 않습니다. 그리고 나머지 빈공간은 양수의 경우에는 0, 음수의 경우에는 1로 채워집니다.

실수형 간의 형변환

실수형에서도 정수형처럼 작은 타입에서 큰 타입으로 변환하는 경우, 빈 공간을 0으로 채웁니다. float타입의 값을 double타입으로 변환하는 경우, 지수(E)는 float의 기저인 127을 뺀 후 double의 기저인 1023을 더해서 변환하고, 가수(M)는 float의 가수 2자리를 채우고 남은 자리를 0으로 채웁니다.

반대로 double타입에서 float타입으로 변환하는 경우, 지수(E)는 double의 기저인 1023을 뺀 후 float의 기저인 127을 더하고 가수(M)는 double의 가수 52자리 중 23자리만 저장되고 나머지는 버려집니다.

한 가지 주의할 점은 형변환할 때 가수의 24번째 자리에서 반올림이 발생할 수 있다는 것입니다. 24번째 자리의 값이 1이면, 반올림이 발생하여 23번째 자리의 값이 증가합니다. 그리고 float타입의 범위를 넘는 값을 float로 형변환하는 경우는 최대값일 경우 '±무한대\pm무한대', 최소값일 경우'±0\pm0'을 결과로 얻습니다.

정수형 <-> 실수형 형변환

정수형을 실수형으로 변환할 때, 실수형은 정수형보다 훨씬 큰 저장범위를 갖기 때문에, 정수형을 실수형으로 변환하는 것은 별 무리가 없습니다. 한 가지 주의할 점은 실수형의 정밀도의 제한으로 인한 오차가 발생할 수 있다는 점입니다. 그래서 10진수로 8자리 이상의 값을 실수형으로 변환할 때는 float이 아닌 double로 형변환해야 오차가 발생하지 않습니다.

실수형을 정수형으로 변환하면, 실수형의 소수점이하 값은 버려집니다. 정수형의 표현 형식으로 소수점 이하의 값은 표현할 수 없기 때문입니다. 만일 실수의 소수점을 버리고 남은 정수가 정수형의 저장범위를 넘는 경우에는 정수의 오버플로우가 발생한 결과를 얻습니다.

자동 형변환

서로 다른 타입의 변수간의 연산은 형변환으로 타입을 일치시키는 것이 원칙입니다. 하지만 경우에 따라 형변환을 생략할 수 있는데, 그러면 컴파일러가 생략된 형변환을 자동적으로 추가합니다.
컴파일러는 기존의 값을 최대한 보존할 수 있는 타입으로 자동 형변환합니다. 표현범위가 좁은 타입에서 넓은 타입으로의 변환은 형변환 연산자를 사용하지 않아도 자동 형변환이 되며, 그 반대 방향으로의 변환은 반드시 형변환 연산자를 써줘야 합니다.

기본형의 자동 형변환이 가능한 방향
byte -> short, char -> int -> long -> float -> double

  1. boolean을 제외한 나머지 7개의 기본형은 서로 형변환이 가능합니다.
  2. 기본형과 참조형은 서로 형변환할 수 없습니다.
  3. 서로 다른 타입의 변수간의 연산은 형변환을 하는 것이 원칙이지만, 값의 범위가 작은 타입에서 큰 타입으로의 형변환은 생략할 수 있습니다.
profile
🚀 백엔드 2년차 개발자입니다 🚀 성장의 즐거움으로 아자자자!🤣

0개의 댓글