Study/Java

자바 스터디 - 2주차 자바 데이터 타입, 변수 그리고 배열

pmh 2021. 5. 9. 20:50

🎯 목표

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


☑️ 학습할 것

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

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

프리미티브 타입(원시 타입)의 종류는 다음과 같다.

구분 데이터형 범위 크기(Byte) 초기값
논리형 boolean 1 or 0 (참 혹은 거짓) 1byte false
문자형 char \u0000~\uffff 2byte \u0000
정수형 byte -128 ~ 127 1byte 0
  short -32,768 ~ 32,767 2byte 0
  int -2^31 ~ 2^31 -1 (±21억) 4byte 0
  long -2^63 ~ 2^63 -1 (±920경) 8byte 0L
실수형 float 7자리의 정밀도 8byte 0.0d
  double 16자리의 정밀도 8byte 0.0f

여기까지 작성하면서 의문이었던 점이 하나 있었다.
논리형 타입인 boolean은 참과 거짓, 단 두가지 값만 표현할 수 있으면 되는데 왜 1byte를 사용하는 지 궁금해서 Stack Overflow에서 찾아보았고 내용은 다음 포스트에서 조금 더 자세하게 작성해보자.

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

java에는 프리미티브 타입(원시 타입) 과 레퍼런스 타입이 있다.

프리미티브 타입

  • boolean, char, byte, short, int, long, unsigned int, unsigned long...
  • 초기값이 있지만, null 값을 가질 수 없다.
  • stack 에 값 저장.

레퍼런스 타입

  • Class, Interface, Enum, String, Array 등등...
  • 초기값이 없고, null 값을 가질 수 있다.
  • Heap 에 값이 저장되어있는 곳의 주소를 저장.
  • Wrapper Class는 위의 프리미티브 타입을 레퍼런스 타입으로 정의한 것.
프리미티브 타입 래퍼 클래스
boolean Boolean
char Character
byte Byte
short Short
int Integer
long Long
float Float
double Double

프리미티브 타입 -[박싱]-> 래퍼 클래스
래퍼 클래스 -[언박싱]-> 프리미티브 타입

Integer x = new Integer(90);    // boxing
Integer y = new Integer(90);
int a = num.intValue();         // unboxing
int b = num                     // auto unboxing


// 프리미티브 타입 값과 래퍼 클래스의 값 비교
x == a        // True
x.equals(a)   // True
x.equals(y)   // True
x == y        // False

래퍼 클래스끼리의 비교는 내부의 값을 비교해야 하기 때문에 == 연산자로는 비교가 불가능하다. (래퍼 클래스의 값은 주소를 담고 있기 때문에)

3. 리터럴 (literal)

우리가 알고 있던 상수의 개념과 같다, 그러나 프로그래밍에서는 '값을 한 번 저장하면 변경할 수 없는 저장공간'이라는 뜻으로 상수(constant)를 정의하였기 때문에 리터럴이라고 한다. (그 자체로 값을 의미한다.)

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

수학에서 '변수(變數)'를 '변하는 수'라고 정의하지만 프로그래밍언어에서의 변수(variable)란, 값을 저장할 수 있는 메모리상의 공간을 의미한다.

출처: Java의 정석

int age;
double height = 177.5;

첫 번째 코드는 int 타입의 변수 age를 선언한 것이고
두 번째 코드는 double 타입의 변수 height를 선언하고 177.5로 초기화하는 코드이다.
int 타입의 초기값은 0이기 때문에 age 변수의 값은 0이다.

컴파일 단계에서 에러 발생
error: incompatible types: possible lossy conversion from int to byte

byte a = 3;
byte b = 6;
=> OK
a + 3
a + b
=>ERROR

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

먼저 변수의 스코프란, 변수가 사용될 수 있는 범위를 말하는데 다음의 예시를 보자

public class ScopeTest {
    String classVal = "Class";
    static String staticVal = "Static";

    public void method01() {
        String methodVal = "method";
        System.out.println(methodVal);
    }

    public static void main(String[] args) {
        System.out.println(staticVal); 
        ScopeTest s = new ScopeTest();
        System.out.println(s.classVal);
    }
}

변수는 중괄호 { } 블록 내에서 선언되고 사용된다.
스코프 밖의 변수에는 접근할 수가 없다. -> methodVal은 해당 메소드 안에서만 접근, 사용이 가능하다.
단, static으로 정의된 변수는 인스턴스에 관계없이 같은 값을 갖는다.
static type이 아니면 인스턴스로 정의를 해 주어야 사용할 수 있다. (s.classVal 부분)

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

타입 변환이란 어떠한 타입으로 선언된 변수를 다른 타입으로 바꾸는 것을 말한다.
예를들면

int iVal = 10;
double dVal = iVal;

의 경우 double로 선언된 dVal에 정수값인 10을 컴파일러가 자동으로 10.0이라는 값으로 변환시켜준다.
이를 묵시적 형 변환 또는 타입 프로모션이라고 한다.

다음과 같은 예를 접하는 경우도 있다.

int a = 3;
int b = 5;

// a를 b로 나눈 값을 사용하기 위해 double 변수 div를 선언
double div = a / b;

해당 div변수의 값은 0이 나온다, 하지만 우리가 기대했던 값은 0.6이다.
이러한 현상이 일어나는 이유는 int타입의 변수끼리의 연산을 통해 나온 값 또한 int이기 때문에
소수점 아래는 버려지기 때문이다. 이를 해결하기위해 다음과 같이 작성해준다.

int a = 3;
int b = 5;

// a를 b로 나눈 값을 사용하기 위해 double 변수 div를 선언 + a 변수를 double타입으로 변환
double div = (double)a / b;

위와같이 임의로 변수의 타입을 변환시키는 것을 명시적 형 변환 또는 캐스팅 이라고 한다.
위의 연산에서 a / b 는 3.0 / 5 가 되며
double 타입과 int 타입의 연산의 결과값이 타입 프로모션을 통해 double타입으로 div 변수에 들어가는 것이다.

7. 1차 및 2차 배열 선언하기

1차원 배열

배열에 대해서 알아보자.
배열이란 특정 타입의 변수를 연속으로 작성한 것을 말하는데, 변수들은 메모리상으로도 연속적으로 위치하기 때문에 임의 접근(Random Access)가 가능하다.

int[] members1;
members1 = new int[3];
int[] members2 = {1,2,3,4};

위의 과정의 결과로부터 두가지를 알 수 있다.

  1. int 배열을 선언하면 0으로 초기화가 된다.
  2. 배열을 선언과 동시에 초기화를 할 수 있다.

2차원 배열

2차원 배열 또한 비슷한 방법으로 선언 및 사용가능하다.

int[] members1;
int[] members2 = {{1,2,3},{2,4,6},{3,6,9},{4,8,12}};

8. 타입 추론, var

var 변수는 선언되는 시점에 초기화가 필요하다, 만일 초기화를 해 주지 않는다면 다음의 에러를 발생한다.

public class MyClass {
    public static void main(String args[]) {
        var hello;
    }
}

=========================================

/MyClass.java:3: error: cannot infer type for local variable hello
        var hello;
            ^
  (cannot use 'var' on variable without initializer)
1 error

var 변수는 초기화 없이 사용할 수 없습니다.라고 한다.

또한 초기화 되는 시점에 타입추론을 통해 설정된 타입이 고정되기 때문에
var 변수에 int타입으로 초기화를 한 다음에 해당 변수에 다른 타입을 넣을 때에도 에러가 난다.

public class MyClass {
    public static void main(String args[]) {
        var intVal = 33;
        intVal = 3.14;
    }
}

=========================================

/MyClass.java:4: error: incompatible types: possible lossy conversion from double to int
        intVal = 3.14;
                 ^
1 error