JAVA

변수(Variable)

mukom 2022. 8. 16. 21:14

1. 변수

1-1. 변수의 정의

      메모리 공간(RAM)의 각 공간은 고유한 주소를 부여받아 공간에 있는 값을 사용할 수 있게 한다.

      주소는 숫자로 나열되어 있어 사용자에게 있어 기억하기 힘들다는 단점이 있다.

      이를 위해 기억하기 쉽도록 의미 있는 이름(변수)을 붙여 사용하기 편하게 한다.

 

1-2. 리터럴(Literal)

      변수에 저장하는 변하지 않는 데이터(숫자, 문자, 논리값 등) 그 자체를 의미한다.

      리터럴은 상수풀(constant pool)이라는 곳에 저장되며,

      변수에 값을 부여할 때 같은 값을 가진 메모리가 있는지 1차적으로 확인하는 작업을 거친다.

      이미 상수풀에 존재하는 값이면 해당 메모리의 주소를 넘겨주어 같은 값에 대한 메모리 영역을 활용할 수 있도록 한다.

 

      ✍️ String 메모리 할당

            String은 참조형 타입 변수이기 때문에 기본적으로 new 연산자를 통해 새로운 객체를 생성할 수 있다.

            하지만 대체로 String은 'String str = "value";'의 형태로 처리한다. 

            String은 기본형 타입 변수의 리터럴과 달리 힙 영역의 String constant pool를 이용하는데,

            이로 인하여 String 생성 방식의 차이가 생기게 됐다.

            

            🔹new 연산자

                 new 연산자로 생성된 객체는 String constant pool을 보지 않고,

                 데이터의 중복 여부와 관련 없이 새로운 객체로써 힙 영역에 생성된다.

 

             🔹String literal

                  litertal로 생성된 객체는 String constant pool에 값을 보내며, 

                  이후 같은 방식으로 생성된 객체의 값이 기존의 pool의 값과 같다면 같은 곳을 바라보도록 한다.

      

      ✍️ 상수(Constant)

            변수와 마찬가지로 값을 저장할 수 있는 공간이지만, 초기화를 하면 다른 값으로 변경할 수 없다는 차이가 있다.

            'final' 키워드를 앞에 붙여 선언하며, 상수명을 대문자로 작성한다는 특징이 있다.

 

 


2. 기본형(Primitive type) 타입 변수와 참조형(Reference type) 타입 변수

    

    자바에서는 변수에 저장하는 데이터 타입을 크게 기본형과 참조형으로 제공하고 있다.

 

    2-1. 기본형 타입 변수

  • 기본형 타입은 아래 표와 같이 총 8개가 정의되어 있다.
  • 초기화를 하지 않으면 각 데이터 타입의 기본값이 설정되고, null 값이 존재하지 않는다.
  • 실제 값을 스택(Stack) 영역에 저장한다.
  데이터 타입 할당되는 메모리 크기
(byte / bit)
기본값 표현 범위
논리형 boolean 1 / 8 false true, false
정수형 byte 1 / 8 0 -128 ~ 127
short 2 / 16 0 -32,768 ~ 32,767
int
(기본)
4 / 32 0 -2,147,483,648
~
2,147,483,647
long 8 / 64 0L -9,223,372,036,854,775,808 
~
9,223,372,036,854,775,807
실수형 float 4 / 32 0.0F 1.4E-45
~
3.4028235E38
double
(기본)
8 / 64 0.0 4.9E-324
~
1.7976931348623157E308
문자형 char 2 / 16 '\u0000'  0 ~ 65,535

           🖍 문자형 데이터 타입인 char는 자바에서 사용되는 문자를 유니코드(Unicode)로 처리한다.

                 유니코드에는 음수가 없기 때문에 char의 표현 범위에는 음수가 없으며, 음수 값을 저장할 수 없다.

           

           ✍️ 기본 데이터 타입에 따른 리터럴 접미사 필수 사용

                  JVM의 피연산자 스택이 피연산자를 4byte 단위로 저장하기 때문에, 

                  정수형에서는 연산의 효율을 위하여 int를 기본 데이터 타입으로 사용한다.

                  이로 인하여 int의 표현 범위보다 큰 long의 경우에는 명시적으로 'L'을 통하여 long 타입인 것을 알린다. 

                  🖍 리터럴 접미사는 기본적으로 대소문자를 구분하지 않지만 숫자 1과의 명확한 구분을 위해 대문자를 사용

                  실수형에서는 int와 마찬가지로 4byte 크기의 float가 있지만,

                  double이 float에 비하여 2배 정도 더 정밀한 실수 형태를 표현할 수 있어 기본 데이터 타입으로 사용된다.

                  때문에 float는 double과의 차이를 두기 위하여 명시적으로 'f, F'를 붙여준다.

 

    2-2. 참조형 타입 변수

  • 기본적으로 기본형 타입 변수를 제외한 모두를 참조형 타입 변수라고 한다.
  • 참조형 타입은 선언 시에 객체를 생성하게 되는데 이 객체는 힙(Heap) 영역에, 이 객체의 주소값을 스택(Stack) 영역에 저장되어 참조형 타입을 사용할 때마다 주소값을 참조하여 객체를 사용하는 방식이다.
  • 객체를 생성하기 때문에 기본값으로 null을 사용할 수 있다.
  • 기본형 타입 변수와 달리 메모리 크기가 정해져 있지 않고, 동적으로 할당된다.

    2-3. 기능적 차이

            1️⃣ null을 사용할 수 있는가?

               기본형 타입 변수에서는 null을 사용할 수 없지만,  참조형 타입 변수는 null을 사용할 수 있다.

               

            2️⃣ 제네릭(generic) 타입으로 사용할 수 있는가?

                기본형 타입 변수에서는 제네릭 타입을 사용할 수 없지만, 참조형 타입 변수는 제네릭 타입 변수를 사용할 수 있다.

 

               ✍️ Wrapper class

                      기본형 타입에 해당하는 데이터를 객체로써 표현하기 위하여 객체로 포장(wrapper)한 클래스이다.

                      이러한 경우를 'boxing'이라 하고, 래퍼 클래스에서 다시 기본형 타입으로 전환하는 것을 'unboxing'이라 한다. 

                      JDK 1.5 버전부터는 이러한 전환을 컴파일러가 자동으로 지원하고 있다.

 

                      래퍼 클래스로 기본형 타입 변수를 사용하게 되면 객체로 활용 가능하다.

기본형 타입 변수 래퍼 클래스
boolean Boolean
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character

               ✍️ 기본형 타입 변수 사용 장점

 

                      그럼에도 불구하고 기본형 타입 변수 또한 사용되는 이유는 성능상의 이점이 있기 때문이다.

                      기본형 타입의 경우 리터럴 값이 스택에 존재하고, 참조형 타입의 경우는 실제 객체의 주소값이 존재한다.

                      이로 인하여 기본형은 참조형에 비하여 접근 속도가 상대적으로 빠르게 된다.

                      또한 기본형은 차지하는 메모리 또한 참조형에 비하여 훨씬 적다. 

 

 

 


3. 변수의 선언(Declaration)과 초기화(Initialization)

 

    3-1. 변수의 선언

            변수를 선언한다는 것은 변수의 타입에 맞는 데이터를 저장하기 위한 공간을 확보하는 행위이다.

   

변수타입 변수이름;
 int    age;

            위와 같이 변수를 선언하게 되면 'age'라는 이름의 int 타입을 저장하는 공간이 생긴다.

 

    3-2. 변수의 초기화

 

            데이터를 저장하기 위한 공간이 생겼으면, 이제 데이터를 넣어줄 차례이다.

            변수를 선언만 하게 되면 저장 공간에 알 수 없는 값이 존재하게 되고, 이와 같은 상태에서는 컴파일 에러가 발생할 수 있다.

            이를 막기 위해 저장 공간을 만들고 의미 있는 값을 저장함으로써 초기화한다.

int age;
age = 10;

int age = 10; // 선언과 동시에 초기화를 할 수 있다.

 

 


4. 변수의 스코프(Scope)와 라이프타임(Life Time)

 

    4-1. 변수의 스코프와 라이프타임

            스코프는 변수가 사용될 수 있는 범위를 말한다.

            라이프타임은 변수가 어느 시점까지 사용될 수 있는지를 의미한다. 

            기본적으로 변수는 특징적으로 클래스 변수,  인스턴스 변수, 로컬 변수 세 가지로 나눌 수 있다.

 

      🔹 클래스 변수

  • 클래스 내부에 'static'을 포함하여 선언된 변수를 말한다.
  • 클래스를 통해 생성되는 객체 모두가 공유하는 변수이다.
  • scope : 접근 제한자가 'public'일 경우 같은 프로그램 내 어디서든 접근이 가능하다.
  • life time : 클래스가 로딩되어 생성될 때부터 종료될 때까지 존재한다. (🖍 static Area에 한 번만 생성!)
  • 호출 방법 : 객체를 생성하지 않아도 이미 static Area 메모리에 할당되어 있기 때문에 '클래스.클래스변수명'으로 접근 가능하다.

      🔹 인스턴스 변수

  • 클래스 변수와 마찬가지로 클래스 내부에 선언되었으나 'static'을 포함하지 않는다.
  • 객체마다 다른 값을 저장할 수 있다.
  • scope : static method를 제외한 클래스 내부
  • life time : 객체가 메모리에서 사라질 때까지 사용 가능하다. (🖍 stack 영역에 new 연산자로 매번 생성 가능)
  • 호출 방법 : 객체를 생성하여 '참조형변수명.인스턴스변수명'으로 접근할 수 있다.

      🔹 로컬 변수(지역 변수)

  • 메서드 내부에서 선언되는 변수이다.
  • 클래스 변수와 인스턴스 변수를 제외한 모든 변수를 포함한다. (매개 변수)
  • scope : 선언된 메서드 내에서만 사용할 수 있다.
  • life time : 메서드 사용이 끝날 때까지 사용 가능하다. (🖍 stack 영역에 사용될 때마다 생성)
  • 호출 방법 : 인스턴스 변수와 마찬가지로 객체가 있어야 사용 가능하다. '참조형변수명.메서드명(매개 변수)'

 

public class variables{

	static int classVar;                  // 클래스 변수
    String instanceVar;                   // 인스턴스 변수
    
    public void method(int localVar1) {   // 로컬 변수
    	int localVar2 = localVar1;        // 로컬 변수
    }
}

 

 

 


5. 변수의 형변환(Casting)

    

    서로 다른 타입으로 선언한 변수에 대해 연산을 수행해야 하는 경우 타입을 일치시키는 작업이 필요하다.

    이를 위해 선언했던 타입에서 다른 타입으로 변환하는 것을 형변환이라고 한다.

 

    5-1. 수동 형변환

            기본형 타입에서 boolean 타입을 제외한 나머지 타입 간에는 형변환이 가능하다.

 

 

      🔹 문자 ➡️ 숫자 (🖍 이 때 String에 들어가는 데이터는 숫자로 변환 가능한 값이어야 한다.)

String str = "1";

// string to int
int intNum = Integer.parseInt(str);

//string to double
double doubleNum = Double.valueOf(strNum);

//string to float
float floatNum = Float.valueOf(strNum);

//string to long
long longNum = Long.parseLong(strNum);

//string to short
short shortNum = Short.parseShort(strNum);

 

      🔹 숫자 ➡️ 문자

int intNum = 10;
String str = "";
		
str = String.valueOf(intNum);
str = Integer.toString(intNum);
str = intNum + "";

 

      🔹 실수 ➡️ 정수

double doubleNum = 1.12345;
float floatNum = 1.123f;
int intNum;

//double to int
intNum = (int)doubleNum; // 1

//float to int
intNum = (int)floatNum; // 1

 

      🔹 정수 ➡️ 실수

long longNum = 1234567890;
float floatNum;
double doubleNum;

// long to float
floatNum = (float)longNum; // 1.23456794E9

// long to double
doubleNum = (double)longNum; // 1.23456789E9

            🖍 float와 double은 둘 다 실수를 나타낼 수 있다는 점에서 공통점을 가지지만,

                   메모리 크기에 따른 유효자릿수가 각각 7과 16으로 정밀도에서 큰 차이를 나타낸다.

                   따라서 보다 높은 정밀도가 필요한 경우 double을 사용해야 한다. 

 

 

    5-2. 자동 형변환

            변환하고자 하는 데이터의 값 손실이 발생하지 않는 경우 자동으로 형변환이 이루어지기도 한다.

 

byte(1byte) ➡️ short(2byte) ➡️ int(4byte) ➡️ long(8byte) ➡️ float(4byte) ➡️ double(8byte)

                                                    char(2byte) ↗️