Study/java

[백기선님의 자바 라이브 스터디] 2주차 - 자바 데이터 타입, 변수 그리고 배열

에디개발자 2020. 12. 23. 00:24
반응형

www.youtube.com/watch?v=HLnMuEZpDwU 

나를 닮았다고 한다...

목표

자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법을 익힙니다.

 

학습할 것

  • 프리미티브 타입 종류와 값의 범위 그리고 기본 값
  • 프리미티브 타입과 레퍼런스 타입
  • 리터럴
  • 변수 선언 및 초기화하는 방법
  • 변수의 스코프와 라이프타임
  • 타입 변환, 캐스팅 그리고 타입 프로모션
  • 1차 및 2차 배열 선언하기
  • 타입 추론, var

프리미티브 타입이란?

primitive type

프리미티브란 원시적인 이라는 뜻입니다. 그럼 원시적인 타입이라는 뜻이 되겠네요. Java에서는 메모리에 직접 값을 할당하는 변수를 프리미티브 타입이라고 합니다.

프리미티브 타입의 변수를 할당하면 JVM의 메모리 즉, Runtime Data Areas의 stack 영역에 저장됩니다.

 

프리미티브 타입 종류와 값의 범위 그리고 기본 값

자바에서 프리미티브 타입의 종류는 8가지입니다.

구분 타입 메모리 크기 기본값 범위
논리형 boolean 1 byte false true, false
정수형 byte 1 byte 0 -128 ~ 127
short 2 byte 0 -32,768 ~ 32,767
int 4 byte 0 -2,147,483,648 ~ 2,147,483,647
long 8 byte 0L -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
실수형 float 4 byte 0.0F (3.4 X 10^-38) ~ (3.4 X 10^38) 의 근사값
double 8 byte 0.0 (1.7 X 10^-308) ~ (1.7 X 10^308) 의 근사값
문자형 char 2 byte '/u0000' 0 ~ 65,535

 

프리미티브 타입과 레퍼런스 타입

프리미티브 타입과 레퍼런스 타입의 가장 큰 차이점은 메모리에 어떤 값이 들어있는지입니다.

프리미티브 타입

  • 메모리에 실제 값이 세팅됩니다.
  • JVM의 stack영역에 세팅됩니다.
int i = 1;        // 메모리에 1 값 세팅
double d = 0.1;   // 메모리에 0.1값 세팅

 

 

레퍼런스 타입

  • 메모리에 실제 값이 세팅되있는 주소값이 세팅됩니다.
  • JVM의 Heap영역에 값이 세팅되고 stack영역에 세팅된 heap의 주소값이 들어갑니다.
public class Anmimal {
  int i = 0;
  double d = 0.0;
}

 

예제를 통해 좀 더 자세히 알아보겠습니다.

 

먼저 primitive 타입으로 테스트를 진행해보겠습니다.

진행방법으로는 java의 Call by Value를 이용하여 메서드 내에서 파라미터로 받은 primitiveType 변수들을 수정해보겠습니다.

public void primitive_test() {
    int i = 1;
    double d 1.0;

    primitiveType(i, d);
    
    assertThat(i).isEqualTo(2);    // false
    assertThat(d).isEqualTo(2.0);  // false
}

public void primitiveType(int i, double d) {
    i = 2;
    d = 2.0;
}

결과는 false가 나옵니다. 

Java는 메서드에서 파라미터를 받으면 파라미터는 새로운 메모리에 값을 복사하여 새롭게 변수를 생성하게 됩니다. 그럼 primitive Type 변수들은 값은 동일하지만 새로운 변수로 생성되기 때문에 값을 할당해도 기존의 변수의 값은 변하지 않습니다.

 

다음으로 reference 타입 테스트를 진행해보겠습니다.

진행방법으로는 동일하게 Call By Value를 이용하여 Reference Type 변수들을 넘겨 수정해보겠습니다.

public void reference_test() {
    Animal animal = new Animal();
    animal.i = 1;
    animal.d = 1.0;

    referenceType(animal);
    
    assertThat(animal.i).isEqualTo(2);    // true
    assertThat(animal.d).isEqualTo(2.0);  // true
}

public void referenceType(Animal animal) {
    animal.i = 2;
    animal.d = 2.0;
}

결과는 true가 나옵니다.

메서드에서 넘어온 파라미터를 새로 생성하지만 referenceType 내 변수의 값을 가리키는 주소값을 그대로 복사해왔기 때문에 값을 변경하면 주소값에 존재하는 값을 변경하는 것이기 때문에 변경됩니다.

 

리터럴

리터럴이란? 소스코드의 고정된 값을 대표하는 용어입니다.

리터럴은 변수 초기화에 종종 사용되기도 합니다.

int i = 0;

여기서 리터럴은 0과 같은 값을 나타냅니다.

 

다양한 리터럴 사용방법

변수의 타입에 따라 리터럴도 사용방법이 나뉘어집니다.

int i = 0;
long l = 0L;
float f = 0.0f;
double d = 0.0d;
  • int : 정수
  • long :  리터럴 끝에 L or l 을 붙혀줍니다.
  • float : 리터럴 끝에 F or f 를 붙혀줍니다.
  • double : 리터럴 끝에 D or d 를 붙혀줍니다.

변수 선언 및 초기화하는 방법

변수를 선언하면 JVM에 메모리를 변수 크기만큼 사용하겠다라는 의미입니다.

public void test() {
    int i;
}

위 코드는 JVM Stack 영역에 int 메모리 크기인 "4Byte를 사용하겠다." 라는 의미입니다. 그리고 그 메모리는 i라고 칭합니다.

이렇게 변수를 선언만 해놓으면 메모리는 확보되었으나 내부에 값은 없는 상태입니다. 

변수를 초기화하면 확보된 메모리 영역에 초기화 값을 세팅합니다.

public void test() {
    int i;
    
    i = 0;
}

이 코드는 확보된 4 Byte 메모리 공간에 값을 0으로 세팅할 것이다. 라는 의미입니다. 

위 코드는 한줄로도 요약이 가능합니다.

public void test() {
    int i = 0;
}

변수를 선언하는 동시에 초기화를 하는 코드입니다.

 

변수의 스코프와 라이프타임

스코프란? - 영역

라이프타임이란? - 언제까지 메모리를 유지하는가! 

 

영역이라면 어떤 영역인가? 자바에서는 중괄호 영역을 사용합니다. 간단한 예제로 스코프에 대해서 알아보겠습니다.

public class JavaScope {    
    int a = 0;    // 0)
    public void scope() {
        // 1) 
        int i = 0;
        double d = 0.0;
        scope1();       
    }
    
    public void scope1() {
        // 2)
        int i = 1;
        double d = 0.1;
    }
}    
0) 변수 a 는 JavaScope인 클래스에서 접근이 가능합니다.
1) 변수 i, d는 메서드 scope의 중괄호 안에서 접근이 가능합니다.
2) scope1 메서드 내 같은 명의 변수를 사용하지만 다른 스코프에 있으므로 가능합니다.

 

scope1 메서드 내에서 선언한 프리미티브 타입의 변수들의 스코프는 scope1 메서드 안에서만 메모리를 유지합니다.

위 코드를 보면서 라이프타임을 정리해보겠습니다.

  1. scope() 메서드를 어디선가 호출합니다.
  2. 메서드 내에서 프리미티타입 변수 i, d를 선언하여 메모리를 할당하고 초기화하여 값을 세팅합니다. 그리고 메서드 scope1를 호출합니다.
  3. scope1에서 프리미티타입 변수 i, d를 선언하여 메모리를 할당하고 초기화하여 값을 세팅합니다. 그리고 scope1 메서드 실행이 종료되면서 메모리에 선언된 값과 할당된 메모리를 회수합니다.
  4. scope 메서드가 종료되면서 동일하게 메모리를 회수합니다.

라이프타임에 따른 변수의 종류를 살펴보겠습니다.

Instance Varibles

메서드나 블록에 속해 있지 않고 클래스 블록에 포함되있는 변수

public class JavaScope {
    int i = 0;  // instance varibles    
    public void scope() {
        // scope O
    }
    
    public static void staticScope() {
        // scope X
    }
}
Scope 클래스 전체 ( static 메서드는 제외 )
Life-Time 클래스를 인스턴스화 한 객체가 메모리에서 없어질 때

Class Varibles

Instance Varibles 에서 static이 붙은 변수

public class JavaScope {
    static int i = 0;
    public void scope() {
		// scope O
    }

    public static void staticScope() {
		// scope O
    }
}
Scope 클래스 전체
Life-Time 애플리케이션 종료까지

Local Varibles

인스턴스, 클래스 변수를 제외한 모든 변수

public class JavaScope {
    public void scope() {
        int i = 0;
    }
}
Scope 변수가 선언되있는 블록 내부
Life-Time 변수가 선언되있는 블록 종료까지

 

타입 변환, 캐스팅 그리고 타입 프로모션

타입 변환에는 캐스팅과 프로모션 2가지가 있습니다.

캐스팅

강제로 타입을 변경하는 경우입니다.

자료형의 크기가 큰 것을 작은 것에 강제로 넣는 행위를 캐스팅이라고 합니다.

간단한 코드를 살펴보겠습니다.

public static void main(String[] args) {
    // 1)
    int a = 10;
    byte b = (byte) a;
    System.out.println(b);

    // 2)
    int c = 1010101010;
    byte d = (byte) c;
    System.out.println(d);
}

크기가 큰 int형 값을 크기가 작은 byte형에 강제 캐스팅을 하는 예제입니다.

첫번째는 int형이지만 byte의 크기 내의 값을 할당한 후 캐스팅을 한 경우입니다.

두번재는 byte의 크기 외의 값을 할당한 후 캐스팅을 한 경우입니다.

첫번째는 byte 크기 내의 값을 할당하였기 때문에 값의 변화가 없이 그대로 10이 출력되는 것을 확인할 수 있습니다. 하지만 두번째의 경우에는 byte 크기 외의 값을 할당하였기 때문에 값의 변화가 일어나는 것을 확인할 수 있습니다.

프로모션

묵시적으로 타입이 변경되는 경우입니다.

캐스팅과 반대로 작은 자료형을 큰 자료형에 할당하는 경우입니다. 자바에서는 이런 경우 데이터를 묵시적으로 변경시켜줍니다.

간단한 코드를 살펴보겠습니다.

public static void main(String[] args) {
    byte a = 10;
    int b = a;
    System.out.println(b);
}

int보다 작은 byte에 값을 할당 한 뒤 int 변수에 할당하는 코드입니다.

결과는 데이터가 변형되지 않고 그대로 할당되어 출력하는 것을 확인할 수 있습니다.

 

1차 및 2차 배열 선언하기

배열이란?
같은 타입의 변수를 연속적인 메모리 공간에 적재하는 자료구조입니다.

1차 배열 선언

변수를 선언할 때 []와 함께 사용하여 선언합니다.

int[] intArrays;
int intArrays[];

1차 배열 초기화

배열을 초기화할때 배열의 사이즈를 할당합니다

int[] intArrays = new int[5];

1차 배열 값 할당

배열의 시작은 0부터 시작합니다

intArrays[0] = 0;
intArrays[1] = 1;
intArrays[2] = 5;
intArrays[3] = 3;
intArrays[4] = 1;

1차 배열 선언 초기화 동시에

이 코드는 배열의 크기를 5로 지정하고 값을 할당한 것과 동일합니다.

int[] intArrays = {0, 1, 2, 5, 1}

2차 배열 선언

int[][] intArrays;
int intArrays[][];

2차 배열 초기화

int[][] intArrays = new int[2][2];

2차 배열 값 할당

intArrays[0][0] = 0;
intArrays[0][1] = 2;
intArrays[1][0] = 3;
intArrays[1][1] = 2;

2차 배열 선언 초기화 동시에

int[][] intArrays = {{0, 1}, {1, 4}}

 

타입 추론, var

Java 10부터 지원되는 기능입니다. 

전 아직 Java8만 사용해보았기 때문에 직접적으로는 사용해보지 못하였습니다. 이번 기회에 학습하게 되네요. :) 

타입 추론, var 는 javascript의 var, let, const 를 생각하시면서 보시면 이해가 쉬울 것 입니다. 변수 타입을 정확히 명시하지 않고 var 로 선언하여 주입받은 값의 타입을 추론하여 변수로 사용하는 것입니다. 대표적으로 람다, 제네릭에서 사용되고 있습니다.

제약조건

var는 로컬 변수에서만 사용할 수 있습니다.

public class Var {
    var test;    // error
}

var는 선언과 동시에 초기화를 반드시 해줘야합니다.

public class Var {
    public void test() {
        var test1;            // error
        var test2 = "Var다"   // String test2 = "Var다" 와 동일
    }
}

 

 

 

 

 

 

 

 

반응형