ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] 자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법
    Java/Java 기본 및 이론 2021. 1. 3. 19:18

    목표

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

    학습할 것

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

     

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

     

     

    타입

     할당되는 메모리 크기

     기본값

     데이터의 표현 범위

     

     논리형

     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
    -2^{31} ~ 2^{31}-

     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 1038) 의 근사값

     double(기본)

     8 byte

     0.0  (1.7 X 10-308) ~ (1.7 X 10308) 의 근사값

     문자형

     char

     2 byte (유니코드)

     '\u0000' 

     0 ~ 65,535

     

    자바 8부터 

    unsigned int 및

    unsigned long 이라는 개념이 추가됨.

     

    1byte 는 8bit 이다. 1bit 가 추가 될때마다 표현가능한 숫자가 2배가 된다.

    부호가 있는 자료형의 경우 1비트는 부호츨 표현하기 위해 사용하기 떄문에 1byte 의 경우 -128 ~ 127 까지 표현 가능하다.

    양수는 0이 포함되기 떄문에 128이 아니다.

    만약 0 ~ 255 까지 표현하고 싶다면, 부호 비트 자리도 데이터로 취급하려면 unsigned 자료형을 사용하면 된다. 음수는 표현하지 못하는 대신 양수 표현 범위가 두 배 늘어난다.

    2비트로 표현 가능했던 모든 값 : 00, 01, 10, 11

     

    자바에는 unsigned 라는 자료형은 제공하지 않고,

    Integer.toUnsignedString() 메소드 등으로 제공한다. 

     

    실수형의 경우 정수형과 비교했을 떄 메모리 크기는 비슷한데 값의 표현 범위가 훨씬 넓다.

     

    실수는 부호(sign), 가수(Mantissa), 지수(exponent) 로 구성되며 부동 소수점 방식을 사용한다.

    부동 소수점 방식을 사용하여 모든 기수를 0보다 크거나 같고 1보다 작은 값 범위의 값으로 만들고 원래 수를 표현하기 위해 10을 몇번 거듭 제곱해야 하는지 지수로 표현한다.

    sign: 부호를 나타내기 위해 사용하는 비트

    exponent: 지수를 사용하기 위해 사용하는 비트

    Mantissa: 가수를 사용하기 위해 사용하는 비트

    실수 표현 예제

    -118.625를 IEEE 754(32비트 단정밀도)로 표현해 보겠습니다.

    1. 우선 음수이므로 부호비트는 1이 됩니다.
    2. 부호를 뺀 절대값을 이진법으로 나타내면 1110110.101(2)이 됩니다.
    3. 소수점을 왼쪽으로 이동시켜 왼쪽에는 1만 남도록 만듭니다. 1110110.101 = 1.110110101 * 2^6 으로 만듭니다. 이것을 정규화된 부동소수점 수라고 합니다.
    4. 가수부는 소수점의 오른쪽 부분으로, 부족한 비트 수 부분만큼 0으로 채워 23비트로 만듭니다.
    5. 지수는 6이며 Bias를 더해야 합니다. 32비트 IEEE754형식에서 Bias는 127이므로 133이되고 이를 이진법으로 변환하면 10000101이 됩니다.

    java에서 기본적으로 실수리터럴을 사용하면 double 형식입니다.

    그렇기 때문에 float형식의 실수는 마지막에 f를 붙여 float형식의 실수라고 알려주어야 한다. 하지만 float형식은 현재 사용하지 않는 것을 권장한다. 부동 소수점 형식은 정확한 실수값이 아닌 근사값을 저장하는 방식이기 때문에 32비트인 float는 오차가 생길 확률이 크다.

     

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

    Java Data Type
    ㄴ Primitive Type
        ㄴ Boolean Type(boolean)
        ㄴ Numeric Type
            ㄴ Integral Type
                ㄴ Integer Type(short, int, long)
                ㄴ Floating Point Type(float, double)
            ㄴ Character Type(char)
    ㄴ Reference Type
        ㄴ Annotation
        ㄴ Array
        ㄴ String
        ㄴ Class
        ㄴ Enumeration
        ㄴ Interface
    Primitive Type Reference Type
    boolean, char, byte, short, int등 이미 정해진 유형 사용자가 무제한적으로 유형을 선언
    값 저장 주소 저장
    call by value call by reference
    메모리에 저장되는 값이 실제값 저장되어 있는 값은 메모리의 주소
    동일한 유형의 다른 변수에 값이 복사됨 다른 참조유형에 할당되면 동일한 객체를 가리킴
    메소드에 전달되면 사본만 전달됨. 메서드에서는 Primitive Type의 원본에 접근할 수 없음. 메서드는 복사된 값을 변경 가능 객체가 메소드에 전단되면 메소드는 주소값을 전달받게되어 객체의 내용을 변경할 수 있음. 객체의 주소는 변경불가

    위의 값들은 JVM 의 Runtime Data Area 에 저장된다. 

    String name ="james";   // name 변수 와 주소값은 스택에, 실제값은 힙 영역에 생성
    int age = 20; // age 변수 와 값은 스택영역에 생성
    Person person = new Person(name, age);
    stack Heap
    name james  
    age 20  
    person   (주소값)---------------> Person

     

    리터럴

    리터럴은 실제값 그 자체입니다. 소스코드에서 고정된 값을 대표하는 용어이며 리터럴은 변수 초기화에 사용됩니다.

    • Integer Literals
      • long 타입은 L 로 끝나거나 l 로 끝나는 값
      • 그외 나머지 숫자는 int
      • 16진수: 0x 로 시작
      • 2진수: 0b 로 시작
    • Floating-Point Literals
      • float 타입은 F로 끝나거나 f 로 끝나는 값
      • 그 외 나머지는 double (optional, D 혹은 d 로 끝나는 값)
    • Character and String Literals
      • Unicode character
      • special escape character
        • \b : 백스페이스
        • \t : 탭
        • \n : new line
        • \f : form feed
        • \r : 캐리지 리턴
        • \" : 쌍따옴표
        • \' : 따옴표
        • \\ : 역슬래시

     

     

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

    선언 - Declaration?

    변수를 선언한다는 것은, 저장공간을 확보하겠다는 의미로 해석할 수 있다.

    int a;

    위의 코드가 변수를 선언한 것인데, 해석하자면 다음과 같다.
    int 타입의 값을 저장할 수 있는 공간을 확보했고(int 타입은 4byte) 그 공간을 지칭할 이름은 a 이다.

    초기화 - Initialization?

    변수를 초기화 한다는 것은, 저장공간에 원하는 값을 저장하는 것을 의미한다.
    변수를 선언하고 나면, 해당 공간에는 아무런 의미 없는 쓰레기값이 들어가있게 된다. 그리고 그 상태에서 컴파일을 시도하면
    Error:(10, 28) java: variable a might not have been initialized 와 같은 컴파일 에러코드를 볼 수 있을 것이다.
    그러므로 변수는 선언 후에 초기화를 해야하며 위에 선언한 변수에 대한 초기화는 다음과 같이 할 수 있다

    a = 10;

    선언과 초기화

    변수의 선언과 초기화를 한 줄에 하는 것도 가능하다.

    int a = 10;

     

    초기화 블록

    클래스 초기화 블럭 : 클래스 변수의 복잡한 초기화에 사용된다. 클래스가 처음 로딩될 때 한번만 수행된다.

      - class 로딩시에 호출된다.

      - 인스턴스 변수나 인스턴스 메소드에 접근 못함. 

     

    인스턴스 초기화 블럭 : 인스턴스 변수의 복잡한 초기화에 사용된다. 

      - 객체 생성할때마다 호출됨.

      - super 생성자 이후에 실행됨.

      - 생성자 보다 먼저 실행됨.

      - 모든 생성자의 공통 코드를 instance initializer block 에 넣으면 코드 줄일수 있다.

     

    class InitBlock{
        static {
            /* 클래스 초기화 블럭 */
        }
    
        {   /* 인스턴스 초기화 블럭 */ }
    } 

     

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

    클래스 변수는 static을 앞에 붙여서 선언해주어야 한다.

    지역변수는 메소드 안에서 선언을 하면 지역변수로 된다.

    인스턴스 변수는 클래스 내부에 생성된 변수이다.

    public class scope_and_lifetime {
        int num1, num2;   		//Instance Variables
        static int result;  	//Class Variable
        int add(int a, int b){  	//Local Variables
            num1 = a;
            num2 = b;
            return a+b;
        }
    }
    클래스 변수(스태틱 변수) 클래스 영역 클래스가 메모리에 올라갈 때 클래스 전역(클래스 기반으로 생성된 각각의 인스턴스에 공유됨)
    인스턴스 변수 클래스 영역 인스턴스가 생성될 때 각각의 인스턴스
    지역 변수 메소드 영역 변수 선언문이 수행되었을 때 메소드 안

    각각의 변수의 라이프타임은 클래스 변수의 경우 프로그램이 종료할때까지 적용되고 인스턴스 변수의 경우는 인스턴스가 참조 되고 있을때에는 유지되다가 객체를 참조하는 변수가 없을 경우 JVM의 Garbage Collector가 제거하게 된다.

    지역변수의 경우 메소드가 끝나면 소멸되어 사용할 수 없게 됩니다.

    인스턴스변수는 클래스가 로딩될떄 생성되서, 스태틱 함수에서는 인스턴스 변수 사용 불가
    스태틱 변수에도 인스턴스메소드 사용 불가
    인스턴스변수에는 반대로 스태틱 변수 참조가능 왜냐하면 스태틱변수가 먼저 생성되기 때문이다.

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

    타입 변환으로 종류로는 캐스팅(강제 형변환)과 타입 프로모션이 있습니다(자동 형변환).

    타입캐스팅이란 크기가 더 큰 자료형을 크기가 더 작은 자료형에 대입하는 것을 의미.

    데이터가 작은데서 큰 곳으로 옮기는 타입 프로모션의 경우 오류나 문법의 필요없이 형변환이 가능하지만,

    반대로 타입캐스팅은 데이터 표현범위가 작은 곳으로 옮길 경우 표현범위를 넘어나게 되면 전혀 다른 값이 나올수도 있다.

     

    타입 캐스팅

    // 큰 범위에서 작은범위로 캐스팅
    int a = 10;     
    byte b = (byte)a;
    System.out.println(b); //  -> 10
    (byte 는 -256~255까지 표현할 수 있음으로 타입캐스팅을 했음에도 데이터 변형이나 
    손실은 오지 않았다) 
    
    // 작은 범위에서 큰 범위로 캐스팅	
    int a = 10000;     
    byte b = (byte)a;
    System.out.println(b); //  -> 16
    (표현범위를 벗어나는 값을 강제로 타입캐스팅해 데이터에 변형이 생겼다) 
    

     

    타입 프로모션

    byte a = 10;
    int b = a;
    System.out.println(b); //  -> 10

    타입변환은 레퍼런스 타입에서도 가능하다.

     

    부모클래스로의 타입변환은 자동적으로 가능하지만 자식클래스로의 타입변환은 타입캐스팅이 필요하다.

    이유는 자식클래스는 부모클래스의 필드나 메소드를 물려받음으로 자식클래스타입의 객체를 부모클래스타입으로 바꾼다고 해서 데이터의 손실이나 변형이 일어나진 않기 때문이다.

    • Inheritance 관계에서만 가능
    • Upcast : subclass → superclass
      • 모든 subclass 는 superclass 의 컨텐츠를 가지고 있으므로 superclass 로의 casting 이 가능함
    • Downcast: superclass → subclass
      • 모든 superclass 는 subclass 의 컨텐츠를 가지고 있지 않을 수도 있음, 그러므로 오류 발생 가능성 ↑

     

    1차 및 2차 배열 선언하기

    class ArrayExample {
    	public static void main(String[] args) {
            //1차원 배열
            int[] oneDimensionArrayEx1 = {1, 2, 3, 4, 5};
            int[] oneDimensionArrayEx2;
            oneDimensionArrayEx2 = new int[10];
    
            //2차원 배열
            int[][] twoDimensionArrayEx1 = {{1, 2}, {3, 4}};
            int[][] twoDimensionArrayEx2;
            twoDimensionArrayEx2 = new int[10][10];
        }
    }

     

    • oneDimensionArrayEx1 은 Runtime Stack 영역의 힙 영역 주소값을 가짐
    • Heap 영역에 int 타입 크기의 요소 5개를 할당하여 사용됨

     

    • 2차원 배열

    • Runtime Stack 영역의 twoDimensionArrayEx1 은 2개의 요소 크기(2개 요소에 주소값을 가지고 있음)를 가진 힙 영역 주소값을 가짐
    • 힙 영역에는 실제 값이 들어있는 요소들과 주소값이 들어있는 요소들로 존재하게됨

     

    타입 추론, var

    Java 10부터 추가된 특징중 하나인 Local Variable Type Inference은 로컬 변수 선언을 var를 이용하여 기존의 엄격한 타입 선언방식에서 컴파일러에게 타입추론 책임을 위임할 수 있게 되었다.

    var list = new ArrayList<String>(); //infers ArrayList<String> 
    var stream = list.stream();//infers Stream<String>

     

    Local Variable Type Inference 사용 조건

    • 초기화된 로컬 변수 선언시

    • 반복문에서 지역변수 선언 시(enhanced for loop포함)

    var 활용

    1. 지역변수 선언

    var numbers = Arrays.asList(1, 2, 3, 4, 5); 
    for (var i = 0; i < numbers.size(); i++) { 
    	System.out.println("numbers = " + numbers.get(i)); 
    }
    
    

    2. forEach

    var numbers = Arrays.asList(1, 2, 3, 4, 5); 
    for (var number : numbers) { 
    	System.out.println(number); 
    }

     

    ⇒ 기존에는 Object타입으로 받아서 형변환을 하거나 IDE가 아닌 개발자가 직접 타입추론해 명시적 타입선언을 해줬는데 var를 사용하여 훨씬 편하게 타입선언이 가능해진다.

    3. Lambda (Java 11)

    IntBinaryOperator plus10 = (@NonNull var one, @NonNull var two) -> one + two + 10;

     

    ⇒ Java 11부터는 람다 인자에도 var사용이 가능해졌는데, 이를 통해 파라미터 어노테이션 사용까지 가능해졌다.

    반응형

    댓글

Designed by Tistory.