[Java] 자바 데이터타입, 변수 그리고 배열
Web/Java

[Java] 자바 데이터타입, 변수 그리고 배열

목차 (클릭시 해당 목차로 이동)


     

     

     

     

    백기선님 자바 스터디 2주차입니다.

    쉬운 주제일 줄 알았으나, 깊게 들어가면 몰랐던 부분들이 계속 나왔습니다.

    그래서 내가 잘 모르는 부분은 좀 더 깊게 들어가며 정리했습니다.

    특히 static이나 리터럴, 정수와 실수간의 형변환등에 대한 부분은 시간을 정말 오래쓰며 정리했습니다.

    자바를 사용하지만 개념이 애매했던 부분들과 원리를 몰랐던 부분들을 자세히 정리할 수 있어서 좋았습니다.

     

     

     

     

     

     

     

    학습할 것


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

     

     

     

     

     

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


     

    자바의 기본형(primitive type)은 모두 8개의 타입이 있습니다.

     

    이것을 크게 4가지로 구분합니다.

     

    4가지 분류


    분류 타입
    논리형 boolean
    true와 false 중 하나를 값으로 가지며, 조건식과 논리적 계산에 사용됩니다.
    문자형 char
    문자를 저장하는데 사용되며, 변수에 하나의 문자만 저장할 수 있습니다.
    정수형 byte, short, int, long
    정수를 저장하는데 사용되며, 주로 int가 사용된다. byte는 이진 데이터를 다룰 때 사용되며, short는 C언어와의 호환을 위해서 추가되었습니다.
    실수형 float, double
    실수를 저장하는데 사용되며, 주로 double이 사용됩니다.

     

     

    특징


    1. char은 정수(유니코드)로 저장하기 때문에 정수형과 많이 다르지 않습니다.
      (정수형, 실수형과 연산또한 가능합니다.)
    2. boolean은 다른 기본형과의 연산이 모두 불가능합니다.

     

     

    유니코드

    전 세계의 모든 문자를 하나의 통일된 2byte 문자집합으로 표현합니다.

    (최근엔 2byte가 부족해 21bit로 늘어났지만 추가된 보충문자는 쓸 일이 거의 없습니다.)

     

     

     

    프리미티브 타입의 범위와 크기 그리고 값


    자료형 저장 가능한 값의 범위 크기 기본 값
    bit byte
    boolean false, true 8 1 false
    char '\u0000' ~ '\uffff' (0~2^16-1, 0~65535) 16 2 '\u0000'
    byte -128 ~ 127 (-2^7 ~ 2^7-1) 8 1 0
    short -32,768 ~ 32,767 (-2^15 ~ 2^15-1) 16 2 0
    int -2,147,483,648 ~ 2,147,483,647 (-2^31~2^31) 32 4 0
    long (-2^63 ~ 2^63-1) 64 8 0L
    float (1.4x10^-45 ~ 3.4x10^38) 32 4 0.0f
    double (4.9x10^324 ~ 1.8x10^308) 64 8 0.0d

     

    • boolean은 2개만 저장하면 되므로 가장 작은 크기인 1byte
    • char은 자바에서 유니코드(2byte 문자체계)를 사용하므로 2byte
    • byte는 크기가 1byte라서 byte
    • int(4 byte)를 기준으로 짧아서 short(2 byte), 길어서 long(8byte)
    • float은 실수값을 부동소수점 (floating-point)방식으로 저장하기 때문에 float
    • double은 float의 2배 크기를 갖기 때문에 double

     

     

    int


    정수형은 4가지를 제공하지만 CPU가 가장 효율적으로 처리할 수 있는 타입인 int를 주로 사용합니다.

     

    초기 언어에서는 cpu가 16bit 였기 때문에 int의 크기는 2byte였습니다.

    그러나 컴퓨터의 성능이 좋아지며 cpu는 32bit를 지원하게 되었고 int는 cpu가 읽기에 가장 효율적인 4byte 크기가 되었습니다.

     

    지금 최신 cpu는 64bit를 지원합니다. 그렇다면 int형은 8byte여야 가장 효율적인 크기가 됩니다.

     

    그렇기 때문에 C언어에서는 CPU에 따라 int의 크기가 달라집니다.

     

     

     

    자바에서도 int의 크기는 달라져야할까요?

     

    아닙니다.

     

    사람이 치는 자바코드는 JVM하고만 상호작용합니다.

    1. JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.

     

    [Java 스터디] 1. JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.

    목차 (클릭시 해당 목차로 이동) 백기선님 Java 스터디를 뒤늦게 시작합니다. https://github.com/whiteship/live-study/issues?q=is%3Aissue+is%3Aclosed whiteship/live-study 온라인 스터디. Contribute to whi..

    ksabs.tistory.com

     

     

    그러므로 자바코드에서 int는 32bit (= 4byte) 크기가 고정입니다.

     

    CPU에 따라 효율적으로 처리하는 연산은 JVM에게 맡기면 됩니다.

     

     

     

     

     

     

     

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


     

    프리미티브 타입은 "값"을 저장하고

    레퍼런스 타입은 "주소값"을 저장합니다.

     

    레퍼런스(참조) 타입은 클래스 타입으로 선언합니다.

     

    (String, Integer 같은 타입도 클래스이므로 레퍼런스 타입입니다.)

    String, Integer 클래스

     

     

    Date() 객체를 생성해서,

    그 주소를 today에 저장하는 코드입니다.

    Date today = new Date();

     

    JVM의 메모리 구조중에 클래스 데이터와 클래스 변수가 저장된 Method Area가 있습니다.

    JVM의 메모리 구조 (출처. anota-com.tistory.com)

     

     

     

    이곳에 클래스를 생성하고 이 주소를 저장한 것이 레퍼런스(참조) 타입입니다.

     

     

     

    쉽게 설명하면,

    • 프리미티브 타입 : 위에서 설명한 8개의 타입
    • 레퍼런스 타입 : 클래스타입으로 선언한 타입

     

     

     

     

     

     

     

    리터럴


     

    리터럴을 설명하기 전에 "상수(constant)"에 대해 먼저 알아야 합니다.

     

     

     

     

     

    상수


    한번 저장하면 다른 값으로 변경할 수 없는 공간 입니다.

     

     

    변수는 하나의 값을 저장하기 위한 공간이라면, 

    상수는 값을 한번만 저장할 수 있는 공간입니다.

     

     

     

    원래 컴퓨터 분야가 아닌 다른 분야에서는 "값"을 상수라고 부르기 때문에 헷갈릴 수 있습니다.

     

    기존에 쓰던 상수의 의미가 자바에서 "리터럴"과 정확히 의미가 같습니다.

     

     

     

    상수 선언 방법

    상수를 선언하는 방법은 변수와 동일하며, 단지 변수의 타입앞에 키워드 'final'을 붙여주기만 하면 됩니다.

     

     

     

     

    리터럴 (literal)


    그 자체로 값을 의미하는 것입니다.

     

    원래 일반적으로 쓰던 상수의 의미와 같습니다.

     

     

     

     

     

    상수가 필요한 이유


    리터럴을 사용하는 대신에 상수를 사용하는 이유가 무엇일까요?

     

    아래 코드에서 만약 20과 10을 다른 수로 바꾸어야 한다면 여러번의 수정이 필요합니다.

    또한, 한눈에 봤을때 어떤 식인지 의미가 잘 들어오지 않습니다.

    int triangleArea = (20 * 10) / 2;
    int rectangleArea = 20 * 10;

     

     

    위 코드를 상수와 리터럴을 이용해 아래 코드로 바꾸면 WIDTH와 HEIGHT의 값만 바꾸면 됩니다.

    또한, WIDTH * HEIGHT 는 사각형의 넓이라는 공식이 눈에 잘 들어옵니다.

    final int WIDTH = 20;
    final int HEIGHT = 10;
    
    int triangleArea = (WIDTH * HEIGHT) / 2;
    int rectangleArea = WIDTH * HEIGHT;

     

     

    이와 같이 상수는 리터럴에 "의미 있는 이름"을 붙여서 코드의 이해와 수정을 돕습니다.

     

     

     

     

     

    리터럴의 타입


    변수에 타입이 있는 것처럼 리터럴에도 타입이 있습니다.

    (사실 변수도 리터럴의 타입에 따라 결정되기 때문에 리터럴의 타입이 없다면 변수의 타입도 없을 것입니다.)

     

     

    여러 타입이 존재하는 정수형, 실수형에서 리터럴의 타입을 표현하는 방법이 있습니다.

     

    정수형

    타입 접미사 특징
    byte 없음 리터럴이 별도로 존재하지 않으며 변수를 저장할 때는 int 타입의 리터럴을 사용합니다.
    short 없음
    int 없음 접미사가 없으면 int타입의 리터럴입니다.
    long l, L long타입에는 l,L의 접미사가 붙습니다.

     

    실수형

    타입 접미사 특징
    float f, F  
    double d, D 접미사가 없으면 double타입의 리터럴입니다
    (접미사 생략 가능)

     

     

    접미사가 붙는 long, float의 접미사만 잘 고려하면 됩니다.

     

     

     

     

     

     

     

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


     

     

    변수


    수학에서 변수는 '변하는 수'라고 정의합니다.

     

    프로그래밍언어에서의 변수는 '값을 저장할 수 있는 메모리 상의 공간'을 의미합니다.

     

    변수란, 단 하나의 값을 저장할 수 있는 메모리 공간

     

     

     

     

     

    변수의 선언


     

    (변수타입) (변수이름);

    으로 선언할 수 있습니다.

    int age; // age 라는 이름의 변수를 선언

     

    이제 age 라는 이름의 값을 저장할 수 있는 메모리 공간이 생겼습니다.

     

    하지만 선언한 직후에는 메모리에 '쓰레기값'이 들어있을 수 있습니다.

    그래서 '초기화'를 해주어야합니다.

     

     

     

     

    변수의 초기화


     

    변수의 초기화란, 변수를 사용하기 전에 처음으로 값을 저장하는 것

     

     

     

     

    변수에 값을 저장할땐 대입연산자 '='를 이용합니다.

    (대입연산자 : 오른쪽의 값을 왼쪽(변수)에 저장하라)

     

     

    int age = 25; // 변수 age를 선언하고 25로 초기화한다.

     

     

     

     

     

     

     

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


     

     

     

     

    변수의 스코프


    프로그램상에서 사용되는 변수들은 각자 사용 가능한 범위를 가집니다.

    그 범위를 변수의 스코프라고 합니다.

     

     

     

     

     

    변수의 종류


    변수의 스코프에 따라 변수를 세가지로 구분할 수 있습니다.

     

    • 지역변수 (local variable)
    • 전역변수 (global variable)
    • 클래스변수 (class variable)

     

     

    변수의 스코프는 보통 자신이 선언된 블럭으로 보면 편합니다.

     

    • 변수 globalScope는 선언된 블락이 class 이므로 해당 클래스 안의 다른 메소드에서도 사용이 가능합니다.
    • 변수 localScope는 자신이 선언된 블락인 scopeTest 메서드 블락안에서만 사용가능합니다.
    • scopeTest 메서드의 매개변수로 선언된 value도 scopeTest 메서드 블락안에서만 사용가능합니다.

     

     

    하지만 여기서 주의해야할 점이 있습니다.

     

     

     

    Static


     

    같은 클래스 내에 있음에도 main 메서드는 globalScope 변수를 사용할 수 없습니다.

     

     

     

    main 메서드는 다른 메서드와 달리 static이라는 키워드로 정의되어 있습니다.

    이런 메서드를 static한 메서드라고 합니다.

     

     

     

    원래 클래스가 사용될 때에는 인스턴스화를 해야합니다.

            VariableScopeExam v1 = new VariableScopeExam();
            VariableScopeExam v2 = new VariableScopeExam();

     

    하지만 static으로 정의된 메서드나 필드들은 클래스가 메모리에 올라갈 때 이미 자동적으로 생성되기 때문에 인스턴스화 되지 않아도 바로 사용이 가능합니다.

     

     

     

    main 메서드는 static으로 정의되어있기 때문에 인스턴스화 하지 않고 바로 실행가능합니다.

     

    그러므로 static으로 정의된 메서드(main)에서는 인스턴스화 되지 않았을 수도 있는 변수는 사용하지 못하는 것입니다.

     

     

     

    그리고 이렇게 클래스 내에서

    static 으로 정의되어있는 변수는 클래스 변수 (static변수)

    static 으로 정의되어있는 메서드는 클래스 메서드 (static메서드)

    라고 합니다.

     

     

     

     

    형 변환 (캐스팅, casting) 이란?


     

     

    모든 변수와 리터럴에는 타입이 있습니다.

     

    하지만 코드를 작성하다보면 서로 다른 타입끼리 연산을 해야하는 경우가 있습니다.

     

    예를들어 int타입의 값과 float타입의 값을 더하는 경우 두 값 다 float타입으로 변환한 후에 계산을 해주어야 합니다.

     

    이럴때 사용하는 것이 타입변환, 캐스팅 입니다.

     

     

    형변환이란, 변수 또는 상수의 타입을 다른 타입으로 변환하는 것

     

     

     

     

     

    형 변환 방법


    타입변환을 하고자 하는 변수나 리터럴의 앞에 변환하고자 하는 타입을 괄호와 함께 붙여주면 됩니다.

     

     

    double d = 85.4;
    int score = (int)d;

     

     

    주의

    타입변환 연산자는 그저 피연산자의 값을 읽어 타입변환 후 값을 반환하는 것이기 때문에,

    피연산자인 변수 d의 값은 타입변환 후에도 아무런 변화가 없습니다.

     

     

     

     

    정수형끼리, 실수형 끼리 형 변환시


     

    큰 타입에서 작은 타입으로의 변환에서는 크기의 차이만큼 잘려나갑니다.

     

    변환 2진수 10진수 값손실
    int
    byte
    00000000 00000000 00000000 00001010
    00001010
    10
    10
    없음
    int
    byte
    00000000 00000000 00000001 00001010
    00001010
    300
    44
    있음

     

     

    반면 작은 타입에서 큰 타입으로의 변환에서는 남은 공간을 0으로 채웁니다.

    그래서 값에 대한 변화가 없습니다.

     

     

     

     

     

    정수형과 실수형 간의 형 변환


     

    정수형과 실수형은 저장형식이 완전히 다르기 때문에 정수형간의 변환처럼 간단히 값을 채우고 자르는 식으로는 할 수 없습니다.

     

     

     

    int : 1 + 31 = 32 (byte)

    S (1) 31 bit

     

    float : 1 + 8 + 23 = 32 (byte)

    S (1) E (8) M (23)

     

     

     

     

    정수 -> 실수로 형 변환

     

    실수형은 정수형보다 더 큰 저장범위를 갖기 때문에 별 무리가 없습니다.

    대신에 실수형의 정밀도의 제한으로 인한 오차가 발생할 수 있습니다.

     

    예를들어 int의 최대값은 약 20억으로 최대 10자리의 정밀도를 요구합니다.

    그러나 float은 10진수로 약 7자리의 정밀도를 제공하므로 8자리 이상의 int형을 float으로 형변환할 때 오차가 발생할 수 있습니다.

     

    그래서 10진수로 8자리 이상의 값을 실수형으로 변환할 때는 float가 아닌 double으로 형변환해야 오차가 발생하지 않습니다.

     

     

    (int형) 91234567 -> (float형) 91234568.0 -> (int 형) 91234568

    (int형) 91234567 -> (double형) 91234567.0 -> (int 형) 91234567

     

     

     

    실수 -> 정수 형 변환

     

    실수형을 정수형으로 변환하면, 실수형의 소수점이하 값은 버려집니다. (반올림 발생 x)

    정수형으로는 소수점의 값은 표현할 수 없기 때문입니다.

     

    9.1234567f -> (int 형변환) -> 9

     

     

    또한, 실수의 소수점 이하의 값을 버리고 남은 정수가 정수형의 저장범위를 넘는 경우에는 정수의 오버플로우가 발생할 수 있습니다.

    double d = 1234567890123.45
    int i = (int) d; // 오버플로우 발생

     

     

     

     

    자동 형 변환 (타입 프로모션)


     

    형 변환에는

     

    • 명시적 형 변환(타입 캐스팅)
    • 자동 형 변환(타입 프로모션)

    이 있습니다.

     

     

    리터럴이나 변수앞에 괄호로 타입을 지정해주어서 하는 것이 명시적 형 변환이고

    명시적 형 변환

    double d = 85.4;
    int score = (int)d;

     

     

    컴파일러가 자동으로 형변환을 추가해주는 것이 자동 형 변환 입니다.

    자동 형 변환

    float f = 1234;

     

     

     

    컴파일러는 표현범위가 좁은 타입에서 넓은 타입으로 형 변환하는 경우에는 값 손실이 없으므로 두 타입중에서 표현범위가 더 넓은 쪽으로 형변환 됩니다.

     

    출처 : https://inor.tistory.com/40

     

    float, double이 int, long보다 오른쪽에 위치한 이유

    보통 자료형의 크기가 큰 것일수록 값의 표현범위가 크기 마련이지만, 실수형은 정수형과 값을 표현하는 방식이 다르기 때문에 같은 크기일지라도 실수형이 정수형보다 훨씬 더 큰 표현범위를 갖기 때문에 float과 double이 같은 크기인 int와 long보다 오른쪽에 위치합니다.

    (물론, 정수형을 실수형으로 변환할때 정밀도의 한계로 인한 오차가 발생합니다.)

     

     

     

     

     

     

     

    배열


     

    같은 타입의 여러 변수를 묶음으로 다루는 것은 '배열' 이라고 합니다.

     

     

    배열은 같은 타입의 여러 변수를 하나의 묶음으로 다루는 것

     

     

     

     

    여기서 중요한 것은 '같은 타입'이어야 한다는 것이며, 서로 다른 타입의 변수들로 구성된 배열은 만들 수 없습니다.

     

     

    배열의 선언과 생성방법


    타입[] 변수이름 = new 타입[길이];
    int[] score = new int[5]; // 길이가 5인 int 배열

     

     

    int[] score : 배열 선언

    int형 배열 score를 선언, 단지 생성된 배열을 다루기 위한 참조변수를 위한 공간을 만든 것 뿐입니다.

     

    new int [5] : 배열 생성

    연산자 'new'에 의해 빈 공간에 5개의 int형 배열을 저장할 수 있는 공간이 마련되고 int의 기본값인 0으로 초기화됩니다.

     

     

     

     

    2차원 배열 선언과 생성


    2차원 배열을 선언하는 방법은 1차원 배열과 같습니다.

    다만 괄호[]가 하나 더 들어갑니다.

    타입[][] 변수이름 = new 타입[행][열];
    int[][] score = new int[4][3]; // 4행 3열의 데이터, 12개의 공간

     

     

    원리

    2차원 배열은 '배열의 배열'로 구성되어있습니다.

    즉, 여러 개의 1차원 배열을 묶어서 또 하나의 배열로 만든 것입니다.

     

     

     

     

     

     

     

    타입 추론, var


     

     

    타입 추론은 말 그대로 개발자가 변수의 타입을 명시적으로 적어주지 않고도, 컴파일러가 알아서 이 변수의 타입을 대입된 리터럴로 추론하는 것입니다.

    (자바10부터 지원하게된 기능입니다.)

     

    지금까지의 자바에서는 지역변수를 선언할때 명시적인 타입을 적고 선언했고, 초기화를 바로 하거나 나중에 값을 넣어도 상관없었습니다.

    String str = "Hello";
    String str1;
    str1 = "Hello1";

     

     

    타입추론 (var)을 사용한다면 String 대신에 var을 적어주고 리터럴을 넣어준다면 컴파일러가 알아서 변수에 타입을 추론해 지정합니다.

    var str2 = "Hello";

     

     

     

     

    타입추론 var 사용시 주의할 점


     

    var은

     

    선언할때 무조건 초기화값이 있어야 합니다.

    var i; // 사용 불가

     

     

    null 값을 넣을 수 없습니다.

    var i = null; // 사용불가

     

     

    멤버변수로 사용이 불가능합니다

     

    private var i = "No" // 멤버 변수에 사용 불가능

     

     

     

    메소드의 파라미터로 사용이 불가능합니다

    void varUse(var a){ // 사용 불가능
    }

     

     

    리턴타입으로 사용이 불가능합니다.

     

     

     

     

    var 특징


     

    1. var은 키워드가 아닙니다.

     

    var은 예약어가 아니기 때문에 변수의 이름으로도 사용이 가능합니다.

    int var = 3; // 가능

     

    컴파일러가 바이트 코드로 변경할 때, var에는 타입이 명시되어 집니다.

     

    var가 int로 바뀌어 버립니다.

    var i = 3; // -> int i = 3;

     

     

     

    2. 런타임 오버헤드가 없습니다.

     

    var 변수를 읽을 때마다 타입을 알아내기 위한 연산을 하는 것이 아니라, 한번 결정되면 중간에 타입이 절대 변경되지 않습니다.

     

    var타입 변수의 값이 3으로 결정되고 컴파일되는 순간, var은 int로 바뀝니다.

    var i = 3;

     

    그래서 int i = "string"; 을 했을때와 같은 결과가 나와 컴파일 에러가 발생합니다.

    var i = 3;
    i = "stirng"; // 컴파일 에러

     

     

     

    'Web > Java' 카테고리의 다른 글

    [Java] GitHub API를 이용한 대시보드 만들기  (2) 2021.07.05
    [Java] JUnit5  (0) 2021.07.05
    [Java] 제어문  (0) 2021.07.05
    [Java] 연산자  (2) 2021.06.25
    [Java] JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.  (2) 2021.06.04