[백기선님과 함께하는 Live-Study] 2주차 - 자바 데이터 타입, 변수 그리고 배열

JoonYoung Maeng·2020년 12월 7일
0
post-thumbnail

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

출처 : https://loustler.io/languages/Java-primitive-type/

Java에서 프리미티브 타입(Primitive Type) 이란 Java에서 제공하는 기본 변수 타입을 말한다.

여기서 타입이란, 컴퓨터가 해당 데이터를 어떻게 식별할지 알려주는 분류기준이라고 생각하면 된다.

프리미티브 타입은 정수 타입을 나타내는 byte, short, int, long실수 타입을 나타내는 float, double, 그리고 문자 타입을 나타내는 char참/거짓을 구분해주는 논리 타입boolean, 8개로 구성되어있다. ****

정수형 타입은 모두 기본값을 0으로 가지고 long 타입의 경우 0L로 가진다.

실수형 타입들인 float 타입은 0.0f, double 타입은 0.0을 기본 값으로 한다.

마지막으로, 문자형 타입인 char 타입은 '\u0000'을 가지고, 논리(참/거짓) 타입 false를 기본값으로 가진다.

각각의 프리미티브 타입은 변수로 선언될 때 가지고 있는 고유 크기를 메모리에 할당받아 초기화한 값을 저장한다. 메소드를 통해서 프리미티브 타입 변수가 매개변수로 보내지면 값에 의한 호출(Call By Value)로 변수를 호출해 메모리에서 변수의 값을 복사해 사용한다.


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

Java는 프리미티브 타입과 래퍼런스 타입(Reference Type)으로 나눌수 있다.

래퍼런스 타입은 new 키워드를 통해 생성된 객체의 주소를 참조하는 타입이다. 배열, Class, 열거형(Enumeration = Enum), Interface가 래퍼런스 타입에 해당된다.

래퍼런스 타입의 데이터 크기는 가변적이고, 동적이기 때문에 1주차에서 배운 동적으로 관리되는 Heap 메모리 영역에 주소값을 저장한다.

또한, 래퍼런스 타입은 빈 객체를 의미하는 Null값을 가지며, 호출이 완료된 후 더 이상 참조하는 값이 없다면 Java가 판단해 Garbage Collection을 진행한다.

프리미티브 타입과 래퍼런스 타입의 가장 큰 차이점은 호출 방식이다.

출처 : https://press.rebus.community/programmingfundamentals/chapter/call-by-value-vs-call-by-reference/

프리미티브 타입의 경우 값에 의한 호출(Call By Value)로 변수를 호출하므로 메모리에서 변수의 값을 복사해서 사용하기 때문에 변수의 값을 바꾸더라도 메모리에 있는 값이 바뀌지 않는다.

하지만, 래퍼런스 타입의 경우 참조에 의한 호출(Call By Reference)로 호출한다.

여기서 참조에 의한 호출(Call By Reference)란 호출되는 변수의 메모리 주소를 복사해서 사용하는 것이다. 따라서, 참조에 의한 호출로 가져온 변수의 값을 바꾼다면 메모리 주소를 참조하여 사용했기 때문에 값이 바뀐다.


리터럴

리터럴의 정의는 소스 코드의 고정된 값이다. 정의만 봐서는 무슨말인지 감이 오질 않는다. 예를 들면, int a = 1; 여기서 a의 값은 1이다. 그렇다면 1이 리터럴인 것이다.

정수형 리터럴은 Long 타입의 경우 L로 끝나고 나머지 정수형 리터럴은 int타입이다. int타입은 10진수, 2진수(0b), 16진수(0x)로 나타낼 수 있다.

int a = 1;      //10진수
int a2 = 0b11;  //2진수
int a3 = 0x12;  //16진수
long a4 = 1L;   //long타입 리터럴

실수형 리터럴은 float 타입의 경우 f로 끝나고 나머지 실수형 리터럴은 double타입이다.

float b = 1.111f;   //float타입
double b1 = 1.111;  //double타입

문자 리터럴은 char타입이고 문자열 리터럴은 String이다. 문자,문자열 리터럴은 유니코드 값을 포함할 수 있다.

char c = 'C';              //문자 리터럴
char c1 = '\u1001';        //문자 리터럴(유니코드)
String c2 = "hello world"; //문자열 리터럴

또한, Java는 문자, 문자열 리터럴에서 \b (backspace), \t (tab), \n (new line), \f (form feed), \r (carriage return), \" (쌍따옴표), \' (따옴표), \ (백슬래시)와 같은 특수 탈출 문자를 지원한다.

논리 리터럴은 boolean타입이고 false, true값 중 하나이다.

boolean d = true;
boolean e = false;

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

class SubMain {
    private int sub;
}

class Main {
    public static void main(String[] args) {
        int a = 10;           //기본 타입 10 초기화 
        int b;                //초기화 x
        int c, d;              //여러개 변수 선언(초기화 x)
        int e = 10, f = 5;    //여러개 변수 선언(초기화 o)
        Sub sub = new Sub();  //래퍼런스 타입 선언
    }
}

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

우선, 스코프는 영어로 범위라는 뜻이다. Java에서 변수의 스코프란 변수에 대한 접근과 변수가 존재할 수 있는 영역을 의미한다. Java는 { } (블록)을 벗어나면 변수가 소멸되는데 이런 변수를 지역 변수(local variable)라 하고 변수가 생서부터 소멸까지를 라이프타임이라고 한다.

class Main {
    public static void main(String[] args) {
        int a = 10;        //지역 변수
        System.out.println(a);
    }
}

또 다른 예를 보면, a가 main메소드 밖에 존재한다. 그렇기 때문에 static int a = 10;은 Main클래스 내에서 모두 접근이 가능하다. 이러한 변수를 전역 변수라고 한다.

class Main {
    static int a = 10;   //전역 변수

    public static void main(String[] args) {
        System.out.println(a);
    }
}

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

타입 변환이란 특정 데이터 타입을 다른 데이터 타입으로 변환 시킬 수 있는 것이다. int타입의 경우 long타입으로 변환이 가능한데 이런 것을 타입 변환이라 한다.

int a = 10;
long b = a;    //int 타입 a를 long타입으로 변환

그렇다면 캐스팅과 프로모션은 무엇일까?

캐스팅과 프로모션은 모두 타입의 변환이라는 점에서는 동일하지만, 타입의 범위에 따라 용어를 구분한다.

먼저 타입 프로모션자동 형 변환이라고도 한다. 크기가 더 작은 타입을 더 큰 타입에 대입할 때 자동으로 작은 타입이 큰 타입으로 변환되는 현상을 말한다.

int a = 10;
float b = a;

int 타입이 나타낼 수 있는 범위보다 float 타입이 나타낼 수 있는 범위가 더 넓으므로 int타입 변수가 float 타입 변수로 대입된다. 이렇게 java는 자동으로 타입을 변환해주데 이런 현상을 타입 프로모션이라 한다

타입 캐스팅의 경우 타입 프로모션과 반대라고 생각하면 된다. 크기가 더 큰 타입을 더 작은 타입에 대입할 때 타입을 명시함으로 써 강제로 집어넣는 현상을 말한다.

float a = 10;
int b = a;

float 타입이 나타낼 수 있는 범위보다 int 타입이 나타낼 수 있는 범위가 더 작으므로 해당 코드는 에러를 발생시킨다. 따라서, 캐스팅을 해줘야하는데 int b = (int) a; 와 같이 명시적으로 타입을 변환해준다. 이를 타입 캐스팅이라 한다.

타입 캐스팅의 경우 큰 타입에서 작은 타입으로 변환 시키는 것이기 때문에 데이터의 손실이 일어날 수 있다는 점을 항상 감안해야한다.

참조 변수 타입의 경우에는 상속 관계가 성립할 때만 타입 변환이 가능하다.

class Car {
    String color;
    int door;
}
class Suv extends Car{
    void info() {
        System.out.println("suv");
    }
}

참조 변수 타입이 캐스팅 되는 경우는 2가지로 나눌 수 있는데 upcast와 downcast이다. upcast의 경우 서브 클래스에서 슈퍼 클래스로 변환 되는 경우이며 타입을 명시해줄 필요가 없다.

Car car = new Car();
Suv suv = new Suv();

car = suv; // upcast

반대로 downcast의 경우 슈퍼 클래스가 서브 클래스로 변환 되는 경우이며 타입을 명시해줄 필요가 있다.

Car car = new Car();
Suv suv = (Suv) car;  //downcast

1차 및 2차 배열 선언하기

배열(Array)란 순차적이고 인덱스 값을 가지고 직접 접근이 가능한 Random Access 특징을 가진다. 배열이 선언되면 데이터는 Heap 메모리에 저장된다.

1차원 배열을 선언하고 어떻게 Heap 메모리에 저장되는지 확인해보자.

class Main {
    public static void main(String[] args) {
        //1차원 배열의 선언
        int[] arr = {1, 2, 3};
        int[] arr2 = new int[3];
    }
}

arr 배열은 int타입으로 1,2,3을 값으로 가지는 3 크기의 배열을 생성한다.

arr2의 경우 배열의 크기로 선언해준다. (이러한 경우 초기화는 0으로 통일된다.)

arr배열과 arr2 배열은 Heap에 아래와 같은 형식으로 저장된다.

이번엔 2차원 배열을 선언하고 어떻게 저장되는지 확인해보자.
{%raw%}

class Main {
    public static void main(String[] args) {
        //2차원 배열의 선언
        int[][] arr = {{1, 2}, {3, 4}};
        int[][] arr2 = new int[2][2];
    }
}

{%endraw%}
2차원 배열은 먼저 인덱스로 해당 인덱스 배열의 주소에 접근한 후 해당 주소를 가지고 데이터에 다시 접근한다. 그림으로 나타내면 이렇게 그려진다.

arr2의 경우에는 동일한 형식으로 데이터가 저장되며 똑같이 0으로 초기화된다.

지금까지 보여준 배열은 프리미티브 타입의 배열이데 참조타입의 배열의 경우에는 어떤식으로 접근이 될까?

class Main {
    public static void main(String[] args) {
        //참조타입 배열 선언
        String[] strArr = {"hello", "world"};

    }
}

참조타입은 new 키워드로 동적으로 할당되기 때문에 해당 데이터의 주소값을 생성시에 가지고 있다. 따라서, 참조타입의 배열은 2차원 배열과 비슷한 느낌으로 참조 타입 생성 객체 주소에 접근할 것이고 생성된 객체가 가지고 있는 주소에 해당 데이터가 존재할 것이다.


타입 추론, var

Java의 타입 추론이란 코드 작성 당시 타입이 정해지지 않았지만, 컴파일러가 그 타입을 유추하는 것을 말한다.

Java 10이하 버전에서 타입추론은 제네릭람다식에서 타입추론 개념이 존재했다.

Java 10 버전 부터는 var 키워드가 추가되었다. var 키워드는 지역 변수이면서 동시에 초기화가 필수적으로 요구된다. 따라서, 멤버 변수 필드 선언, 생성자 형식 변수 또는 다른 종류 변수 선언에는 사용할 수 없다.

var str = "hello";          //String임을 추론 가능
var list = new ArrayList<Integer>();        //ArrayList타입임을 추론 가능

정리

  • 마지막 부분에서 학습한 타입 추론, var키워드는 이번에 처음 알게된 내용이었다. 타입 추론의 경우 불필요한 코드를 줄여주는 장점이 있다곤 하지만, 개발자 입장에서 해당 코드를 읽을 때 가독성이 떨어질 수도 있다는 단점이 있다. 상황에 맞게 사용하는 습관을 들이는게 중요한 역량이 되지 않을까 판단해본다.

Reference

https://loustler.io/languages/Java-primitive-type/

https://re-build.tistory.com/3

https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

https://itmir.tistory.com/296

https://arabiannight.tistory.com/315

https://taesang-jo.blogspot.com/2017/09/java-6-array.html

https://kudl.tistory.com/entry/JAVA-var-키워드

profile
백엔드 개발자 지망생입니다!

0개의 댓글