Java의 자동 언박싱 및 오토박싱

이 글은 주로 Java의 자동 언박싱과 자동 박싱에 대한 지식을 소개합니다.

기본 데이터 유형

기본 유형 또는 내장 유형은 클래스(Class)와 다른 Java의 특수 유형입니다. 프로그래밍에서 가장 자주 사용되는 유형입니다.

Java는 강형 언어로서 변수의 첫 번째 선언은 데이터 유형을 나타내야 하며 첫 번째 변수 할당을 변수 초기화라고 합니다.

Java에는 8가지 기본 유형이 있으며 기본 유형은 세 가지 범주로 나눌 수 있습니다.

문자 유형char

부울 유형boolean

숫자형 byte, short, int, long, float, double.

숫자 유형 은 정수 유형 byte, short, 및 부동 소수점 유형 , 로 나눌 수 있습니다 .intlongfloatdouble

Java에는 부호 없는 숫자 유형이 없으며 해당 값의 범위는 고정되어 있으며 시스템 하드웨어 환경이나 운영 체제의 변경 사항에 따라 변경되지 않습니다.

사실 Java에는 void해당 래퍼 클래스가 있는 또 다른 기본 유형이 java.lang.Void있지만 직접 작동할 수는 없습니다.

기본 데이터 유형의 이점은 무엇입니까

우리 모두는 Java 언어에서 new객체가 힙에 저장되고 스택의 참조를 통해 이러한 객체를 사용한다는 것을 알고 있으므로 객체 자체가 더 많은 리소스를 소비합니다.

int 등 자주 사용하는 타입의 경우 이 변수를 사용할 때마다 새로운 자바 객체를 생성해야 한다면 번거로울 것이다. 따라서 Java는 C++와 같이 기본적인 데이터 타입을 제공하는데, 이 데이터의 변수는 new로 생성할 필요가 없으며 힙에 생성하지 않고 바로 스택 메모리에 저장하므로 더욱 효율적이다.

정수 값 범위

자바에서 정수형은 주로 byte, short, 의 4가지 유형이 int있으며 long표현되는 숫자의 범위도 작은 것부터 큰 것까지 다양하며 표현의 범위가 다른 이유는 주로 데이터를 저장할 때 차지하는 바이트 수와 관련이 있다.

단답형 인기 과학인 1바이트 = 8비트(비트)부터 시작하겠습니다. Java의 정수는 부호 있는 숫자입니다.

먼저 계산에서 8비트가 나타낼 수 있는 숫자를 살펴보겠습니다.

最小值:10000000 (-128)(-2^7)
最大值:01111111(127)(2^7-1)

이러한 유형의 정수 중에서,

  • byte: 바이트는 -128(-2^7)에서 127(2^7-1)까지 1바이트로 저장되며, 변수 초기화 시 바이트 타입의 기본값은 0이다.

  • short: short는 -32,768(-2^15)에서 32,767(2^15-1)까지 2바이트로 저장되며, 변수 초기화 시 short 타입의 기본값은 0이다. 변환 이유 자체는 0으로 직접 쓸 수 있습니다.

  • int: int는 -2,147,483,648(-2^31)에서 2,147,483,647(2^31-1)까지 4바이트로 저장되며 변수 초기화 시 int 타입의 기본값은 0이다.

  • long: long은 -9,223,372,036,854,775,808(-2^63)에서 9,223,372,036, 854,775,807(2^63-1)까지 8바이트로 저장되며, 변수 초기화 시 long 타입의 기본값은 0L 또는 0l이다. 0 직접.

범위를 벗어난 경우 수행할 작업

위에서 언급한 바와 같이 정수형은 각 유형마다 일정한 표현 범위가 있지만 프로그램의 일부 계산으로 인해 표현 범위를 초과, 즉 오버플로가 발생합니다. 다음 코드와 같습니다.

    int i = Integer.MAX_VALUE;
    int j = Integer.MAX_VALUE;

    int k = i + j;
    System.out.println("i (" + i + ") + j (" + j + ") = k (" + k + ")");

출력 결과: i(2147483647) + j(2147483647) = k(-2)

이것은 오버플로이며 오버플로가 발생해도 예외가 발생하지 않으며 프롬프트가 표시되지 않습니다. 따라서 프로그램에서 동일한 유형의 데이터를 계산에 사용할 때 데이터 오버플로 문제에 주의해야 합니다.

포장의 종류

자바 언어는 객체지향적 언어이지만 자바에서 기본적인 데이터 타입이 객체지향적이지 않아 실제 사용에 많은 불편함이 있다. , 따라서 기본 데이터 유형에 해당하는 8개의 클래스를 총칭하여 래퍼 클래스(Wrapper Class)라고 합니다.

패키징 클래스는 java.lang패키지에 있으며 패키징 클래스와 기본 데이터 유형 간의 해당 관계는 다음 표에 나와 있습니다.

기본 데이터 유형 포장
바이트 바이트
부울 부울
짧은 짧은
성격
정수 정수
뜨다 뜨다
더블 더블

8개의 클래스 이름 중 Integer, Character 클래스를 제외한 나머지 6개 클래스의 클래스 이름은 클래스 이름의 첫 글자를 대문자로 표기한 점을 제외하면 기본 데이터형과 일치한다.

래퍼가 필요한 이유

많은 사람들은 Java가 효율성을 향상시키기 위해 8가지 기본 데이터 유형을 제공하는데 래퍼 클래스를 제공해야 하는 이유에 대해 의문을 제기합니다.

사실 이 질문은 이전에 이미 답변을 받은 적이 있습니다. 왜냐하면 Java는 객체 지향 언어이고 많은 곳에서 기본 데이터 유형 대신 객체를 사용해야 하기 때문입니다. 예를 들어 컬렉션 클래스에는 int, double 및 기타 유형을 넣을 수 없습니다. 컬렉션의 컨테이너에는 Object 유형의 요소가 필요하기 때문입니다.

기본형도 객체의 특성을 가지게 하기 위해 기본형을 "래핑(wrapping)"하여 객체의 성질을 갖게 하고 여기에 속성과 메소드를 추가하여 기본 유형의 작업.

언박싱 및 포장

글쎄요, 기본 데이터 유형과 래퍼 클래스를 사용하면 때때로 그들 사이에 변환이 있어야 합니다. 예를 들어 기본 데이터 유형의 int를 래핑 유형의 Integer 객체로 변환합니다.

우리는 패키징 클래스가 기본형의 패키징이라고 생각하기 때문에 기본 데이터형을 패키징 클래스로 변환하는 과정이 패키징이다.영어는 boxing에 해당하고, 중국어는 boxing에 해당한다.

반대로 패키징 클래스를 기본 데이터 타입으로 변환하는 과정이 언패킹인데 영어는 언박싱, 중국어는 언박싱에 해당한다.

Java SE5 이전에는 boxing을 수행하기 위해 다음 코드를 전달할 수 있습니다.

    Integer i = new Integer(10);

자동 언박싱 및 오토박싱

자바 SE5에서는 개발자의 수고를 줄이기 위해 자바에서 자동 언박싱과 자동 박싱 기능을 제공한다.

Autoboxing: 기본 데이터 유형을 해당 패키징 클래스로 자동 변환하는 것입니다.

자동 언박싱: 패키징 클래스를 해당 기본 데이터 유형으로 자동 변환하는 것입니다.

    Integer i = 10;  //自动装箱
    int b = i;     //自动拆箱

Integer i=10Integer i = new Integer(10);Java는 개발자가 새로운 Integer 객체를 수동으로 생성할 필요가 없는 오토박싱 기능을 제공하기 때문에 교체할 ​​수 있습니다 .

자동박싱과 자동개봉의 실현원리

이제 Java에서 자동으로 언박싱하는 기능을 제공하므로 원리가 무엇이며 Java에서 자동 언박싱 기능을 구현하는 방법을 살펴보겠습니다.

자동 언박싱을 위한 다음 코드가 있습니다.

    public static  void main(String[]args){
    
    
        Integer integer=1; //装箱
        int i=integer; //拆箱
    }

위의 코드를 디컴파일하면 다음 코드를 얻을 수 있습니다.

    public static  void main(String[]args){
    
    
        Integer integer=Integer.valueOf(1);
        int i=integer.intValue();
    }

Integer.valueOf()위의 디컴파일된 코드에서 메소드를 통해 int의 자동 boxing이 구현되고 integer.intValue메소드를 통해 Integer의 자동 unboxing이 구현됨을 알 수 있습니다 . 독자가 관심이 있는 경우 8가지 유형을 모두 디컴파일하면 다음 규칙을 찾을 수 있습니다.

자동 boxing은 valueOf()패키징 클래스의 메서드를 통해 이루어지며, 자동 unboxing은 xxxValue()패키징 클래스 개체의 메서드를 통해 이루어집니다.

자동으로 압축이 풀리는 장소

원칙을 이해한 후에 Java가 자동으로 개봉되는 상황을 살펴보겠습니다. 위에서 언급한 변수 초기화 및 할당 시나리오는 소개하지 않을 것이며 가장 간단하고 이해하기 쉽습니다.

간과했을 수 있는 시나리오를 주로 살펴보겠습니다.

시나리오 1. 컬렉션 클래스에 기본 데이터 유형 넣기

Java의 컬렉션 클래스는 객체 유형만 수신할 수 있다는 것을 알고 있는데 다음 코드가 오류를 보고하지 않는 이유는 무엇입니까?

    List<Integer> li = new ArrayList<>();
    for (int i = 1; i < 50; i ++){
    
    
        li.add(i);
    }

위의 코드를 디컴파일하여 다음 코드를 얻습니다.

    List<Integer> li = new ArrayList<>();
    for (int i = 1; i < 50; i += 2){
    
    
        li.add(Integer.valueOf(i));
    }

위의 내용에서 기본 데이터 유형을 컬렉션 클래스에 넣으면 자동으로 boxing된다는 결론을 내릴 수 있습니다.

시나리오 2. 포장형과 기본형의 크기 비교

Integer 객체의 크기를 기본 유형과 비교할 때 실제로 무엇을 비교하는지 궁금한 사람이 있습니까? 다음 코드를 살펴보십시오.

    Integer a = 1;
    System.out.println(a == 1 ? "等于" : "不等于");
    Boolean bool = false;
    System.out.println(bool ? "真" : "假");

위의 코드를 디컴파일하여 다음 코드를 얻습니다.

    Integer a = 1;
    System.out.println(a.intValue() == 1 ? "等于" : "不等于");
    Boolean bool = false;
    System.out.println(bool.booleanValue ? "真" : "假");

래퍼 클래스와 기본 데이터 유형 간의 비교 작업은 먼저 래퍼 클래스를 기본 데이터 유형으로 언박싱한 다음 비교하는 것임을 알 수 있습니다.

시나리오 3. 포장형 운영

Integer 개체에 대해 네 가지 산술 연산을 수행할 때 어떻게 작동하는지 궁금한 사람이 있습니까? 다음 코드를 살펴보십시오.

    Integer i = 10;
    Integer j = 20;

    System.out.println(i+j);

디컴파일된 코드는 다음과 같습니다.

    Integer i = Integer.valueOf(10);
    Integer j = Integer.valueOf(20);
    System.out.println(i.intValue() + j.intValue());

우리는 두 가지 패키징 유형 간의 작업이 자동으로 기본 유형으로 언박싱된다는 것을 발견했습니다.

시나리오 4, 삼항 연산자 사용

이것은 많은 사람들이 모르는 장면이며, 저자는 온라인에서 피 묻은 버그가 발생한 후에야 이를 알게 되었습니다. 간단한 삼항 연산자의 코드를 살펴보십시오.

    boolean flag = true;
    Integer i = 0;
    int j = 1;
    int k = flag ? i : j;

많은 사람들이 이 행에서 자동 언박싱이 발생한다는 사실을 모릅니다 int k = flag ? i : j;(JDK1.8 이전, 자세한 내용은 "Alibaba Java Development Manual - Taishan Edition" 언급된 삼항 연산자의 널 포인터 문제는 무엇입니까? ? 참조 ).

디컴파일된 코드는 다음과 같습니다.

    boolean flag = true;
    Integer i = Integer.valueOf(0);
    int j = 1;
    int k = flag ? i.intValue() : j;
    System.out.println(k);

이것은 실제로 삼항 연산자의 문법 사양입니다. 두 번째 및 세 번째 피연산자가 각각 기본 유형 및 개체인 경우 개체는 작업을 위해 기본 유형으로 언박싱됩니다.

예제에서 flag ? i : j;프래그먼트에서 두 번째 단락의 i는 패키징 유형의 객체이고 세 번째 단락의 j는 기본 유형이므로 패키징 클래스는 자동으로 언박싱됩니다. i의 값이 이 시간이면 nullNPE가 발생합니다. ([자동 언박싱으로 인해 널 포인터 예외 발생][1])

시나리오 5. 함수 매개변수 및 반환 값

이것은 코드에서 직접 이해하기 쉽습니다.

    //自动拆箱
    public int getNum1(Integer num) {
    
    
     return num;
    }
    //自动装箱
    public Integer getNum2(int num) {
    
    
     return num;
    }

자동 언박싱 및 캐싱

Java SE의 자동 언박싱도 캐싱과 관련된 기능을 제공하는데 먼저 다음 코드를 보고 출력을 추측해보자.

    public static void main(String... strings) {
    
    

        Integer integer1 = 3;
        Integer integer2 = 3;

        if (integer1 == integer2)
            System.out.println("integer1 == integer2");
        else
            System.out.println("integer1 != integer2");

        Integer integer3 = 300;
        Integer integer4 = 300;

        if (integer3 == integer4)
            System.out.println("integer3 == integer4");
        else
            System.out.println("integer3 != integer4");
    }

우리는 일반적으로 위의 두 가지 판단 결과가 거짓이라고 생각합니다. 비교 값은 같지만 대상이 비교되고 대상의 참조가 다르기 때문에 두 if 판단은 거짓으로 간주됩니다. Java에서는 ==객체 참조가 비교되는 반면 equals값은 비교됩니다. 따라서 이 예에서 서로 다른 개체는 서로 다른 참조를 가지므로 비교를 수행할 때 둘 다 false를 반환합니다. 이상하게도 여기서 두 개의 유사한 if 조건문은 서로 다른 부울 값을 반환합니다.

위 코드의 실제 출력은 다음과 같습니다.

integer1 == integer2
integer3 != integer4

그 이유는 Integer의 캐싱 메커니즘과 관련이 있습니다. Java 5에서는 메모리를 절약하고 성능을 향상시키기 위해 Integer 작업에 새로운 기능이 도입되었습니다. 정수 개체는 동일한 개체 참조를 사용하여 캐싱 및 재사용을 가능하게 합니다.

-128에서 +127 범위의 정수 값으로 작동합니다.

오토박싱에만 적용됩니다. 생성자를 사용하여 개체를 만드는 것은 적용되지 않습니다.

특정 코드 구현에 대해서는 [Java의 Integer Cache Mechanism][2] 문서를 참조하세요. 여기에서는 자세히 설명하지 않습니다.

우리는 autoboxing이 필요할 때 숫자가 -128에서 127 사이이면 개체를 다시 만드는 대신 캐시에 있는 개체를 직접 사용한다는 점만 알면 됩니다.

그 안에 있는 Javadoc은 캐시 지원 -128과 127 사이의 오토박싱 프로세스를 자세히 설명합니다. 127의 최대값은 -XX:AutoBoxCacheMax=size에 의해 수정될 수 있습니다.

실제로 이 기능이 Java 5에 도입되었을 때 범위는 -128에서 +127로 고정되었습니다. 나중에 Java 6에서 java.lang.Integer.IntegerCache.high최대값은 를 통해 설정할 수 있습니다.

이를 통해 애플리케이션의 실제 상황에 따라 유연하게 조정하여 성능을 개선할 수 있습니다. 이 -128에서 127 범위를 선택한 이유는 무엇입니까? 이 숫자 범위가 가장 널리 사용되기 때문입니다. 프로그램에서 Integer를 처음 사용하는 경우 캐시를 초기화하는 데도 약간의 추가 시간이 걸립니다.

Boxing Conversion 섹션의 JLS(Java Language Specification)에는 다음과 같이 명시되어 있습니다.

변수 p의 값이 다음과 같은 경우:

  • -128에서 127 사이의 정수(§3.10.1)
  • true 및 false의 부울 값(§3.10.3)
  • \u0000~ \u007f사이의 문자 (§3.10.4)

범위 내에서 p가 두 객체 a와 b로 래핑되면 a == b를 직접 사용하여 a와 b의 값이 같은지 여부를 판단할 수 있습니다.

자동 언박싱으로 인한 문제

물론 자동 언박싱은 매우 좋은 기능으로 개발자의 에너지를 크게 절약하고 더 이상 박스를 언박싱해야 할 때 신경 쓸 필요가 없습니다. 그러나 그는 또한 몇 가지 문제를 소개합니다.

패키지 개체의 값 비교는 단순히 사용할 수 없으며 ==-128에서 127 사이의 숫자는 괜찮지만 이 범위 밖에서는 여전히 비교가 필요합니다 equals.

앞서 언급한 바와 같이 일부 시나리오에서는 자동 언박싱을 수행하게 되는데, 동시에 자동 언박싱으로 인해 패키징 객체가 null인 경우 자동 언박싱 중에 NPE가 발생할 수 있다고도 말씀드렸습니다.

for 루프에 언박싱 작업이 많으면 많은 리소스가 낭비됩니다.

추천

출처blog.csdn.net/zy_dreamer/article/details/132307188