Primitive Type 종류와 값의 범위 그리고 기본 값

Primitive Variable 이란?

어떠한 Instance의 참조 값을 가지지 않고, 지정된 Type에 맞는 리터럴을 저장한 변수를 말한다.

객체를 다루지 않기 때문에 Null을 저장할 수 없으며, 같은 타입의 변수들이 동일한 값을 다룰 때에는 Operand Stack이나 Constant Pool에 리터럴을 저장해두었다가 꺼내어 사용한다.

**// 정수 타입
// JDK 7 까지는 int와 long에 대해서 Unsigned 를 지원하지 않았지만,
// JDK 8 부터는 int와 long에 대하여서 지원하기 시작하였다.**

// 1 byte (8 bit)
// -128 (-2^7)  ~  127 (2^7–1).
// 기본 값은 0. (공통적으로 Method, Loop Scope 등에 선언된 Primitive 변수는 초기화 해야한다.)
byte variableB;

// 2 byte (16 bit)
// -32,768(-2^15)  ~  32,767(2^15–1).
// 기본 값은 0.
short variableS;

// 4 byte (32 bit) 
// 2,147,483,648 (-2^31)  ~  2,147,483,647 (2^31-1)\
// int 를 통해 8, 16 진수를 표현 가능하다. 0 시작하면 8진수, 0x 로 시작하면 16진수 이다.
// (JDK 8+) 부호 없는 int를 만들기 위해서는 Integer.parseUnsignedInt 를 사용해야 한다.
// 기본 값은 0. 
int variableI;

// 8 byte (64 bit)
// -9,223,372,036,854,775,808 (-2^63)  ~  9,223,372,036,854,775,807 (2^63–1)
// (JDK 8+) 부호 없는 long를 만들기 위해서는 Long.parseUnsignedLong 를 사용해야 한다.
// 기본 값은 0.
long variableL;


**// 실수 (부동소수점 방식) 타입**

// 4 byte (32 bit)
// 1.40239846 x 10^-45  ~  3.40282347 x 10^38
// 기본 값은 0.0f
float variableF;

// 8 byte (64 bit)
// 4.9406564584124654 x 10-324  ~  1.7976931348623157 x 10308
// 기본 값은 0.0D
double variableD;


**// 논리 타입**

// 표시 용량은 1 bit (내부적으로는 1 byte 내부 7 비트에 값을 채우고 하나의 bit 만 사용한다.) 
// true , false
// 기본 값은 false
boolean bool;


**// 문자 타입**

// 2 Byte (16 bit)
// 유니코드를 표현하는 변수이며, 값의 범위는 0  ~  65,535 까지이다.
// 기본 값은 /u0000 (Unicode Character 'NULL')
char variableC

 

Primitive Type과 Reference Type

Reference Type?

Reference Type 은 Primitive Type을 제외한 모든 타입을 포함한다.

참조 값을 통해 관리하기 때문에 Reference Type이라고 한다. ( Null을 사용할 수 있다. )

Wrapper Class?

Primitive Type을 인스턴스로 다룰 수 있게끔 도와주는 것들을 말한다.

값을 조작하기 위해 작성된 Util Method를 사용하기 위해서 이용하는 경우도 많다.

  • Min, Max_value 상수, valueOf, parsexxx, toString 등..

Boxing과 Unboxing을 통해 Primitive Type을 감싸거나 다시 Primitive로 변환할 수 있다.

//대응 클래스
byte Byte      char Character
short Short    float Float
int Integer    double Double
long Long      boolean Boolean


//Wrapper Class 선언 예시
Integer num = new Integer(10);
            = Integer.valueOf(10);//혹은 primitive 변수



// Integer의 valueOf 메서드는 Boxing 할 값이-127~128 범위 값인 경우 기존에 있는 객체를 
// 재사용할 수 있게끔 작성되어 있다. 
// IntegerCache 클래스에 -127~128 만큼의 범위를 가지는 별도의 배열을 만들고 
// 해당 valueOf 로직에서 검증하여 중복된 경우 기존에 선언된 객체를 반환한다.
public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}

Primitive Type과 Reference Type의 차이점?

  • Reference 변수를 통해서 생성된 객체에 접근하고 조작하고 값을 받아올 수 있다.

  • Null을 다룰 수 있다. (NPE 가 발생될 수 있다.)

    • Null 을 가지는 Wrapper와 Primitive Type 연산 시 NPE 가 발생한다.
  • Generic을 사용할 수 있다.

  • 별도의 식별 값을 가진다. (Hashcode)

  • 성능 상의 차이가 있을 수도 있다.

 

그러면 Wrapper Class 를 사용하지 말라는 걸까?

  • Front 단의 데이터를 검증해야 한다거나, DB Table에서 Null을 다루고 있다면, 해당 데이터를 받아오는 객체에서 검증을 진행해야 하는 것이 맞다.
    • DB Table에서 Null 을 다룰 때 변수를 Primitive로 선언하여 사용한다면 0이 저장될 것이고 의도치 않은 결과가 발생할 수 있다고 생각한다.
  • Collection이나 Generic 은 Primitive를 지원하지 않는다. (결국 Wrapper를 사용해야 한다.)

 

리터럴

리터럴이란? wiki

어떤 변수에 의해 저장되는, 값 자체로써 변화하지 않는 것들을 의미한다.

// 변수를 초기화할 때 사용되는 값들도 모두 리터럴이다.
int numberA = 10;
double numberB = 10.11D;
String str = "문자열 리터럴";

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

흐름

  1. 타입에 맞는 메모리 공간을 확보하고 (byte b;)
  2. 타입에 맞는 리터럴을 저장한다. (byte b = 100;)
// 정수 리터럴의 _ 은 숫자를 구별하기 위해 사용할 수 있게 지원해준다. 
// 100_000 = 100,000
// 맨 앞이나 뒤에 _ 를 작성할 경우 컴파일 에러가 발생한다.

byte variableB; 
variableB = 100;

short variableS;
variableS = 30_000;

// 10진수
int variableI = 100_000;
// 8진수
int variableI = 010; // 8
// 16진수
int variableI = 0xa; // 10
// 2진수
int variableI = 0b11; // 3

long variableL = 100_000_000_000; // 혹은 100_000_000_000l;

float variableF = 3.14f;

double variableD = 3.14; // 3.14D; 기본적으로 Double 로 판단하여 선언해준다.

char variableC = 'c';

boolean bool = true;

 

Variable Scope와 Life Cycle?

Class Scope

public class someClass {
        // 해당 Class 의 Bracket 내부에서만 사용이 가능하다.
        // 외부에서는 해당 Class 의 getter, setter 를 통해서 해당 변수를 접근할 수 있다.
        private int age = 32;
        -------------------------------------------
}

Method Scope

public class someClass {

        public static void main(String[] args) {
        VariableScope variableScope = new VariableScope();
        variableScope.someMethodA();
        System.out.println(variableScope.someMethodB());
    }        

        public void someMethodA() {
                // 해당 Method 의 Bracket 내부에서만 사용이 가능하다.
                int count = 0;
                System.out.println("count = " + count);
        }

        public int someMethodB() {
                //Compile Error 발생
                //cannot find symbol 
                    //symbol:   variable count
                    //location: class noteThree.VariableScope
                count += 1;
                return count;
        }
}

Loop Scope

public class someClass {

        public static void main(String[] args) {
        VariableScope variableScope = new VariableScope();
        variableScope.min(new int[]{10,20,30,40,50});
    }

    public void min(int[] args) {
        int min = 21700000;

        for (int arg : args) {
            // int arg 는 해당 Loop 내부에서만 사용이 가능하다.
            if (min > arg) { min = arg; }
            System.out.println("arg = " + arg);
        }

        //cannot find symbol.. symbol: variable arg...
        System.out.println("arg = " + arg);

        System.out.println("min = " + min);
    }
}

Bracket Scope

public class someClass {

        public static void main(String[] args) {
        VariableScope variableScope = new VariableScope();
        variableScope.someMethod();

    }

        public void someMethod() {
        int numberA = 0;

        {
                        // 해당 변수는 자신을 감싸는 Bracket 영역에서만 사용 가능하다.
            int numberB = 1;
            numberA += numberB;
        }

        //cannot find symbol.. symbol: variable arg...
                System.out.println(numberB);

        System.out.println("numberA = " + numberA); // 1
    }
}

Variable Shadowing

public class someClass {

        public static void main(String[] args) {
        VariableScope variableScope = new VariableScope();
        variableScope.someMethodA();
        variableScope.someMethodB();

    }
        //Class Scope Variable
    private int count = 10;

    public void someMethodA(){
                // Instance Variable 인 count 를 접근하여 사용한다.
        System.out.println("count = " + count);
    }

        // Class Scope > Method Scope
    public void someMethodB(){
                // Method 내부에 Instance Variable 인 count와 동일한 이름을 가진 변수가 존재한다.
                // 동일한 이름의 변수가 하위 Scope에서 존재한다면 하위 Scope의 변수가 상위 Scope의
                // 변수를 가리게(Shadowing) 된다. 
        int count = 1;
        System.out.println("count = " + count);
    }

}

Variable Life Cycle

  • Static Variable (Class Member Variable)

    static 키워드가 붙은 필드 변수를 말하며, 전역적으로 접근이 가능한 녀석이다.

    인스턴스 생성 이전에 미리 로드되어 생성되고 Heap 영역에서 관리된다. (JDK 8+)

    더 이상 해당 Class (Static Obejct)가 참조되지 않는 경우 GC 된다.

    (JDK 7 이전에선 Application 의 Life Cycle이 종료될 때까지 유지된다.)

  • Instance Member

    class scope 에서 작성된 변수를 말한다. 인스턴스의 참조 값을 통해서 접근과 조작이 가능하다.

    인스턴스 생성시에 메모리 공간을 가지고 기본 값으로 초기화되거나 생성자를 통해 주입된다.

    더 이상 해당 Instance가 참조되지 않는 경우 GC 된다.

  • Local Variable

    Method, Loop Scope 등에서 생성되어 사용되는 지역 변수들을 말한다.

    지역 변수가 포함된 Scope가 값을 반환하는 등, 로직이 끝난다면 사라진다.

 

Type Conversions, Casting, Type Promotion

Type Conversions

  • Widening Conversion

    Type 간의 호환성이 존재하고 변환될 타입이 이전에 지니고 있던 타입보다 큰 경우 자동으로 변환되는 것을 말한다.

    (int → long , float → double...)

  • Narrowing Conversion

    대상 Type들이 같은 리터럴을 다루지만, 변환될 타입이 이전에 지니고 있던 타입보다 작은 경우 Casting을 통해 변환할 수 있는 것을 말한다. 내부의 값에 따라 OverFlow가 발생할 수 있다.

    (int → short, double → float)

  • String Conversion

    Wrapper Class의. toString()을 통해 변환되는 것을 말하거나 혹은 parse를 통한 경우를 말한다.

      Integer numA = 100;
    
      String str = num.toString();
    
      int nunB = integer.parseInt(str);
  • Boxing/Unboxing Conversion (JDK 5+)

    Wrapper Class 가 Primitive Type 과의 연산이나 값 저장이 이루어지는 경우에 컴파일러에서 자동적으로 변환해주는 것을 말한다.

      int numA = 10;
      Integer numB = 10; // Integer.valueOf(10) _ 오토박싱
    
      int result = numA + numB; // numB.intValue() - int 10 _ 언박싱
  • Numeric Promotions

    이진 연산 시에 변수들의 크기가 호환되지 않는 경우, 호환성을 위해 지원하는 규칙들을 말한다.

    • 하나의 변수가 Double이면, 다른 변수도 Double로 변경된다.
    • 하나의 변수가 float이면, 다른 변수도 float으로 변경된다.
    • 하나의 변수가 long이면, 다른 변수도 long으로 변경된다.
    • 위의 상황을 만족시키지 않는 경우 Int로 간주한다.

Casting

어떠한 타입을 다른 타입으로 명시적인 변환을 하는 것을 Casting이라고 한다.

  • Primitive Type 간의 Type 변환
  • 구현, 상속 관계의 Class 간의 Type 변환

구현, 상속 관계의 Class 간의 Type 변환

public class Casting {
    public static void main(String[] args) {

        // Up Casting
        // 상위 클래스의 공통 메서드, 상태는 하위 클래스에서 사용 가능하다.
        // 하위 클래스의 재정의, 메서드, 상태는 상위 클래스에서 사용이 불가능하다.
        Parent child = new Child();
        child.name = "name";
        child.getDegree(); // Child의 메서드 접근 - 컴파일 에러

        // Down Casting
        Child childTwo = (Parent) childOne; 
        childTwo.getDegree(); // 사용 가능
    }
}

 

1차 및 2차 배열 선언하기

// 1차원 Int 배열 생성. 
// 정수 값을 넘김으로써 그 길이 만큼의 배열을 생성할 수 있다.

int[] numArrA = new int[**length**];

//혹은

int[] numArrA = {10,20,30,40,50};

int[][] numArrB = new int[length][length];

//혹은

int[][] numArrB = {{10,20}, {30,40}};

// for을 통한 초기화 방식
int[][] numArrB = new int[10][10];
for (int loop = 0; loop < numArrB.length; loop++) {
     for (int innerLoop = 0; innerLoop < numArrB[loop].length; innerLoop++) {
         numArrB[loop][innerLoop] = 10;
     }
}

//혹은

int[][] numArrC = new int[10][10];
for (int loop = 0; loop < numArrB.length; loop++) {
     Arrays.fill(numArrC[loop], 10);
}

// 출력 방식
for (int[] ints : numArrB) {
    System.out.println("ints = " + ints);
    for (int anInt : ints) {
        System.out.println("anInt = " + anInt);
    }
}

// 혹은

System.out.println("numArrB = " + Arrays.deepToString(numArrB));

 

Type Inference (타입 추론) , var

Type Inference 이란 정적인 언어에서 컴파일러가 컴파일 시점에서 변수를 초기화하는 리터럴을 판단하여 해당 타입으로 변환하는 것을 말한다.

자바는 JDK 10부터 Local Scope 내에서 var이라는 새로운 Type을 지원하게 되었다.

사용 시 주의점

  • 초깃값 (리터럴)이 없다면 컴파일 에러가 발생한다. var num;
  • null로 초기화하면 작동하지 않는다. var num = null;
  • local 변수가 아니라면 작동하지 않는다. public num = "String";
  • 람다 표현식에는 var 타입의 변수를 사용할 수 없다. var foo = (String s) → s.length() > 10;
  • 타입 없이 배열 초깃값을 넘겨도 작동하지 않는다. var arr = { 1, 2, 3 };
public void someMethod() {
    var str = "Hello world";
    assertTrue(str instanceof String);

        var num = 10;
    var numB = 10.10D;
    System.out.println(numB+num); // 20.1
}

 

참고한 자료.

Java Primitives versus Objects (https://www.baeldung.com/java-primitives-vs-objects)

Introduction to Java Primitives (https://www.baeldung.com/java-primitives#:~:text=The eight primitives defined in, objects and represent raw values.)

Java Primitive Conversions (https://www.baeldung.com/java-primitive-conversions)

Variable Scope in Java (https://www.baeldung.com/java-variable-scope)

Java 10 LocalVariable Type-Inference (https://www.baeldung.com/java-10-local-variable-type-inference)

자바의 신

+ Recent posts