2회차. 자바 데이터 타입, 변수 그리고 배열

KIMA·2023년 1월 10일
0
post-thumbnail

목표

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

학습할 것

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


종류타입기본 값메모리 크기(byte)값의 범위
논리형booleanfalse1true, false
정수형byte01-128 ~ 127
short02-32,768 ~ 32,767
int(default)04-2,147,483,648 ~ 2,147,483,647
long0L8-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
char‘\u0000’20 ~ 65,535
실수형float0.0F4(3.4 10^-38) ~ (3.4 10^38) 의 근사값
double(default)0.08(1.7 10^-308) ~ (1.7 10^308) 의 근사값
  • 메모리 크기에 따라 저장할 수 있는 값의 범위가 정해진다.
    • 1bit란 2진수 한자리를 뜻하며, 0 또는 1로 나타낼 수 있다.
      • 그리고 1byte = 8bit이므로
        • 1byte의 메모리 공간으로 2^8 = 256개의 데이터를 표현할 수 있다.
  • 논리형의 경우 저장할 값이 2개로 1bit면 충분하지만 자ㅏ가 데이터를 다루는 최소 메모리 단위가 1byte이므로, 공간이 남아도 1byte의 공간을 사용한다.
  • 실수형은 정수형과 비교했을 때 메모리 크기는 비슷하지만 값의 범위가 훨씬 넓다.
    • 실수형은 부호, 가수(mantissa), 가수(exponent)로 구성되며, 부동 소수점 방식을 사용하기 때문이다.
      • float 타입 : 부호(1비트), 지수(8비트), 가수(23비트) 총 32비트 = 4바이트
      • double 타입 : 부호(1비트), 지수(11비트), 가수(52비트) 총 64비트 = 8바이트
    • 실수를 0보다 크거나 같고 1보다 작은 값으로 만들어 가수로 표현하고, 원래 수를 표현하기 위해 10을 몇 번 거듭 제곱해야하는지를 지수로 표현한다.
      • 예) 1.234 = 0.1234 * 10^1에서 0.1234는 가수, 1은 지수이다.
    • 실수는 값을 저장할 때 오차가 발생할 수 있어 실수를 사용할 때 정밀도를 반드시 고려하여 타입을 선택해야함
      • float의 가수는 23비트이지만 정규화를 통해 24비트까지 사용 가능
        • 따라서 2^24를 저장할 수 있는데 이를 10진수로 변환하면 약 10^7이므로 정밀도는 7자리가 됨
      • double도 비슷한 방식으로 계산하면 정밀도가 15자리임
  • 음수의 값이 필요 없어 해당 자리를 활용하여 정해진 범위보다 더 큰 범위로 쓰고 싶을 때 unsigned 자료형을 사용하면 된다.
    • 자바 8버전 부터 Integer와 Long의 wrapper 클래스에 unsigned와 관련된 static 메소드가 추가 되었다.
      int unsigned = Integer.parseUnsignedInt("21억과 42억 사이의 값");
      System.out.println(Integer.toUnsignedString(unsigned)); // 21억과 42억 사이의 값
    • 그냥 unsigned말고 BigInteger 쓰자.

프리미티브(primitive) 타입과 레퍼런스(reference) 타입


출처 : https://velog.io/@jaden_94/2주차-항해일지

  • 자바 데이터 타입에는 프리미티브 타입과 레퍼런스 타입 두가지가 존재한다.
  • 프리미티브 타입(=원시 타입, 기본형 타입) : stack 메모리에 을 직접 저장한다.
    • 종류 : 수치 타입, 불리언 타입
      • Number 타입
        • 정수 타입(byte, short, int, long, char)
        • 부동소수점 타입(float, double)
      • Boolean 타입 : true, false
  • 레퍼런스 타입 : stack 메모리에 heap 영역에 할당된 객체의 주소 값을 저장한다.
    • 종류: 클래스 타입, 인터페이스 타입, 배열 타입, 열거 타입

리터럴


  • 리터럴 : 변수나 상수에 저장되는 값 그 자체

  • 예) 10 in int a = 10

  • 종류 : 정수 리터럴, 실수 리터럴, boolean 리터럴, 문자 리터럴, 문자열 리터럴

  • 정수 리터럴

    int decimal = 26; // 10진법
    int binary = 0b10101; // 2진법, 앞에 0b를 붙인다.
    int octal = 076; // 8진법, 앞에 0을 붙인다.
    int hexaDecimal = 0x1a; // 16진법, 앞에 0x를 붙인다.
    • int 타입이 기본이다.
    • long 타입은 숫자뒤에 L울 붙인다.
  • 실수 리터럴

    double a = 0.1;
    double b = 1E-1; // E는 10을 의미한다.
    • double 타입이 기본이다.
    • float 타입은 숫자뒤에 f를 붙인다.
  • booelan 리터럴

    boolean a = true; // 참
    boolean b = false; // 거짓
  • 문자 리터럴

    • 작은따옴표(')안에 문자를 넣어 표현한다.

      char a = 'a';
    • 특수문자를 위한 리터럴도 존재한다.

      특수문자 리터럴의미
      ‘\n’new line
      ‘\r’carriage return
      ‘\t’tab
      ‘\b’backspace
      ‘\f’form feed
      ‘\”’double quote
      ‘\’’single quote
      ‘\’backslash
      ‘\u유니코드’유니코드(16진수 문자)
  • 문자열 리터럴

    • 큰따옴표(")안에 문자열을 넣어 표현한다.
      String a = "abc";

    문자열 리터럴

    출처 : https://velog.io/@jaden_94/String-Class

    • 문자열 리터럴은 다른 리터럴과는 다르게 프리미티브 타입이 아닌 레퍼런스 타입(String)에 저장된다.
      • 따라서, 문자열 리터럴 방식이 아닌 다른 레퍼런스 타입처럼 new연산자를 통해 String 객체를 생성한 다음 객체의 주소를 저장할 수도 있다.
      • 이 두가지 방식(리터럴 방식, 생성자 방식)에는 저장 방식에서 차이점이 존재한다.
      • 먼저, 리터럴 방식은 문자열 리터럴을 String 변수에 전달하면 Heap의 String constant pool 영역에 문자열이 저장되고
        • 다음에 동일한 문자열 리터럴을 String 변수에 또다시 전달하면 다른 레퍼런스 타입 처럼 Heap 영역에 새롭게 생성되는 것이 아닌 String constant pool에 저장했던 문자열을 다시 참조하는 방식으로 작동한다.
      • 생성자 방식은 다른 레퍼런스 타입 처럼 Heap에 새로운 String 객체를 생성하고 해당 객체의 주소를 참조하게 된다.
      • 왜 두 가지 방식으로 나눴을까?
        • 그 이유는 리터럴 방식은 메모리를 재활용하여 메모리를 효과적으로 사용할 수 있다는 장점이 있기 때문이다.

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


  • 변수 선언
    [변수 타입] [변수 이름]; 
    int a; // a라는 이름으로 int타입의 변수를 선언한다.
    • 변수를 선언한다는 것은 특정 타입의 값을 저장할 수 있는 공간을 확보한 다음, 해당 공간에 대한 이름을 지정한 별명으로 지어주는 것이다.
      • 메모리 공간을 메모리 주소값으로 관리하는 것은 어려우므로 별칭을 따로 지정한다.
  • 변수 초기화
    [변수 이름] = [초기화 값];
    a = 10; // a변수를 10으로 초기화한다.
    • 변수의 초기화란 변수를 사용하기 전에 처음으로 값을 저장하는 것이다.
    • 변수를 선언한 이후부터는 변수를 사용할 수 있지만, 메모리는 여러 프로그램이 공유하는 자원이므로 전에 다른 프로그램에 의해 저장된 알 수없는 값(=쓰레기값)이 남아있을 수 있다. 따라서, 사용하기 전에 반드시 변수를 초기화해야 한다.
      • 하지만, 클래스 변수와 인스턴스 변수는 자동으로 초기화해주는 값이 있어 초기화를 생략할 수 있다. 단, 지역 변수는 제외되므로 반드시 초기화 하자.
  • 변수 선언과 초기화를 동시에 수행해도 되고 따로 수행해도 된다.
  • 여러 변수를 동시에 선언/초기화할 수 있다.
    int a, b;
    int a = 0, b = 0;

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


  • 변수의 스코프 : 해당 변수를 사용할 수 있는 범위
  • 변수의 라이프타임 : 해당 변수가 메모리에서 살아있는 시간
  • 변수의 스코프에 따라 나뉘는 변수의 종류
    class ScopeAndLifetime {
      int a;   					//Instance Variables
      static int b;  			//Class Variable
      int add(int c, int d){  	//Local Variables
          return c+d;
      }
    }
    • 인스턴스 변수 : 클래스 안에서 어떤 method나 block안에서 선언되지 않은 변수
      • 스코프 : static method를 제외한 클래스 전체
      • 라이프타임 : 클래스를 인스턴스화한 객체가 메모리에서 사라질 때까지
    • 클래스 변수 : 클래스 안에서 어떤 method나 block안에서 선언되지 않았으며 static 키워드로 선언된 변수
      • 스코프 : 클래스 전체
      • 라이프타임 : 프로그램 종료시까지
    • 로컬 변수 : 인스턴스 변수, 클래스 변수가 아닌 모든 변수 즉, 클래스 안에서 어떤 method나 block안에서 선언된 변수
      • 스코프 : 변수가 선언된 method나 block내부
      • 라이프타임 : method나 block이 종료될 때까지

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


  • 타입 변환 : 하나의 타입을 다른 타입으로 바꾸는 것
    • 타입 변환은 묵시적 타입 변환(타입 프로모션)과 명시적 타입 변환(캐스팅)으로 나뉜다.
  • 지금부터 크기라고 하는 것은 데이터 크기(byte)가 아닌 표현할 수 있는 값의 범위를 뜻한다.
  • Long(8byte)
    👉 float(4byte) ❌
  • float((3.4 10^-38) ~ (3.4 10^38) 의 근사값)
    👉 Long(-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807) ⭕
  • 캐스팅(casting) : 크기가 더 큰 자료형을 작은 자료형에 대입하는 것
    • 방법

      • = (타입)캐스팅 하고자 하는 변수나 리터럴
    • 캐스팅 과정에서 데이터의 손실이나 변형이 올 수 있다.

      // byte는 -256~255까지 표현할 수 있으므로, 캐스팅 했음에도 데이터 변형이나 손실은 오지 않았다.
      int a = 10;     
      byte b = (byte)a;
      System.out.println(b); // 10
      
      // byte의 표현범위를 벗어나는 값을 강제로 캐스팅해 데이터에 변형이 생겼다.
      int a = 10000;     
      byte b = (byte)a;
      System.out.println(b); // 16
  • 타입 프로모션(promotion) : 크기가 더 작은 자료형을 큰 자료형에 대입하는 것
    • 방법
      • = 캐스팅 하고자 하는 변수나 리터럴
        • 데이터 손실이나 변형이 없기 때문에 명시적으로 (타입)을 적지않아도 된다.
    • 데이터 손실이나 변형이 없다.

1차 및 2차 배열 선언하기


  • 배열 선언
    타입[] 배열이름;
    • 배열 선언시, 생성된 배열을 다루기 위한 참조변수를 위한 공간이 생성된다.
  • 배열 생성
    배열이름 = new 타입[길이];
    • 배열 생성시, 특정 타입을 지정한 길이(개수)만큼 연속적으로 저장할 수 있는 공간이 생성된다.
    • 첫번째 공간의 주소가 변수에 저장된다.
  • 선언과 초기화를 동시에 수행할 수 있다.
  • 배열 값 할당
    배열이름[인덱스] =;
    • 인덱스가 배열의 길이를 넘어서지 않도록 주의한다.
  • 배열 선언과 동시에 초기화
    타입[] 배열이름 = {1,2, ...};
  • 2차원 배열
    타입[][] 배열이름; 								// 선언
    배열이름 = new 타입[길이][길이]; 					// 생성
    배열이름[인덱스][인덱스] =; 					// 할당
    타입[][] 배열이름 = {{1,2}, {3,4}}; 		// 선언 및 초기화
    • 1차원 배열과 매우 유사하다.

타입 추론, var


  • 타입 추론 : 데이터 타입을 명시하지 않아도, 컴파일 단계에서 컴파일러가 타입을 유추해 정해준다.
    • 자바 10버전 이전에서의 타입 추론은 1.5버전부터 추가된 Generic이나 8버전에 추가된 lamda에서 사용되어왔다.
    • 자바 10에서는 var라는 Local Variable Type Inference가 추가되었다.
      • 지역 변수를 선언할 때 초기값을 통하여 데이터 타입을 추론한다.
        • 따라서, 선언과 동시에 초기화가 필수적이다.
      • 자바는 하위 버전에 대한 호환성을 지원하기 때문에 10 버전 이전 버전에서 var을 변수명으로 사용한 적이 있었다고 해도 이 기능에 대해 걱정하지 않아도 된다.
        • var는 keyword가 아닌 reserved type name이기 때문에 변수명으로 써도 컴파일에러가 발생하지 않는다.
      var a = 10; // int a = 10;
      var b = "hello, world!" // String b = "hello, world!";
    • 장점 :
      • 코드 양을 줄일 수 있고, 가독성을 높인다.
        // before
        URL url = new URL("https://www.oracle.com/");
        URLConnection conn = url.openConnection();
        Reader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        
        // after
        var url = new URL("https://www.oracle.com/");
        var conn = url.openConnection();
        var reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        출처 : Java Language Changes for Java SE 10
        • =로 할당해주는 과정에서 어떤 타입이 저장될 지 확인할 수 있는데, 변수명 앞에 중복으로 타입을 적어주는 것은 코드량을 늘리는 일이다.
    • 규칙
      • 지역 변수에만 var로 선언이 가능하다.
        • null이 아닌 값으로 초기화는 필수이다.
      • 정수형, 실수형, 문자형은 기본 타입, 문자열은 String, 인스턴스는 해당 데이터의 레퍼런스 타입으로 자동 생성된다.
      • 람다 표현식에서는 명시적인 타입 지정이 필요하다.
        var foo = s -> System.out.println(s); // ❌
        Consumer<String> foo = s -> System.out.println(s); // ⭕
      • 배열 선언시 var 대신 타입 명시가 필요하다.
    • 예제
      • Local variable delarations with initializers

        var list = new ArrayList<String>();
      • Enhanced for-loop indexes

        for(var i : list) {
          System.out.println(i);
        }
      • Index variables declared in traditional for loops

        for(var i = 1; i < 10; i++) {
          System.out.println(i);
        }
      • Try-with-resources variable

        try(var input = new FileInputStream("validation.txt")) {...}
      • 람다 표현식안에서 키워드 앞에서 사용할 수 있는 어노테이션 사용

        // before
        Consumer<String> foo = s -> System.out.println(s);
        
        // after
        Consumer<String> foo = (@Notnull var s) -> System.out.println(s); // ⭕

Reference

profile
안녕하세요.

0개의 댓글