Java에는 정수(byte, short, int, long), 부동 소수점(float, double), 부울, 문자 char 등 8가지 기본 데이터 유형이 있습니다. 이에 맞춰 Java는 다음을 제공합니다. 8가지 패키징 클래스: Byte, Short, Integer, Long, Float, Double, Boolean 및 Character. 래퍼 클래스는 다른 클래스와 마찬가지로 객체를 생성합니다.
Integer num = new Integer(0); //创建一个数值为0的Integer对象
위 구성 객체 명령문은 실제로 기본 데이터 유형을 패키징 클래스로 변환한 것입니다. 애플리케이션에서는 기본 유형 데이터와 래퍼 클래스 객체 간에 변환이 필요한 경우가 많습니다.
Integer num1 = new Integer(1); //基本数据类型转为包装类 int num2 = num1.intValue(); //包装类型转为基本数据类型 System.out.println(num1 +" "+ num2);
Java는 사용을 용이하게 할 뿐만 아니라 성능 조정과 같은 다른 목적으로도 사용할 수 있도록 설계된 자동 박싱 및 언박싱 메커니즘을 제공합니다. 이 메커니즘은 기본 유형과 래핑된 유형 간의 변환을 단순화합니다.
//1、包装类中的自动装箱拆箱机制 Integer num1 = 1; //自动装箱 int num2 = num1; //自动拆箱 System.out.println(num1 +" "+ num2);
jad 도구를 사용하여 위 코드를 디컴파일한 결과는 다음과 같습니다.
Integer integer = Integer.valueOf(1); int i = integer.intValue(); System.out.println((new StringBuilder()).append(integer).append("\t").append(i).toString());
Java 컴파일러가 변환 작업을 완료하는 데 도움이 되었다는 것을 알 수 있습니다. 또한 new 키워드를 사용하는 것 외에도 Integer 클래스의 valueOf() 메서드를 사용하여 Integer 객체를 생성할 수도 있음을 알 수 있습니다. 이 두 가지 방법에는 차이점이 있는데, 이에 대해서는 아래에서 설명하겠습니다.
위에서 언급한 패키징 클래스 객체 생성 방법에는 new 키워드 및 valueOf() 메서드 사용이 포함됩니다. 차이점을 느껴보기 위해 코드를 살펴보겠습니다.
//2、包装类中的缓存机制 Integer num3 = 10; Integer num4 = 10; Integer num5 = new Integer(20); Integer num6 = new Integer(20); Integer num7 = 128; Integer num8 = 128; System.out.println((num3==num4) +" "+ num3.equals(num4)); System.out.println((num5==num6) +" "+ num5.equals(num6)); System.out.println((num7==num8) +" "+ num7.equals(num8));
실행 결과는
디컴파일된 코드를 살펴보겠습니다.
Integer integer = Integer.valueOf(10); Integer integer1 = Integer.valueOf(10); Integer integer2 = new Integer(20); Integer integer3 = new Integer(20); Integer integer4 = Integer.valueOf(128); Integer integer5 = Integer.valueOf(128); System.out.println((new StringBuilder()).append(integer == integer1).append("\t").append(integer.equals(integer1)).toString()); System.out.println((new StringBuilder()).append(integer2 == integer3).append("\t").append(integer2.equals(integer3)).toString()); System.out.println((new StringBuilder()).append(integer4 == integer5).append("\t").append(integer4.equals(integer5)).toString());
먼저 Integer의 valueOf() 메소드 소스 코드를 살펴봅니다.
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
그런 다음 Integer 내부의 캐시 배열 멤버를 살펴보겠습니다. IntegerCache 클래스. 낮은 멤버와 높은 멤버
static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; }
를 보면 Integer 클래스를 처음 사용하는 한 Integer의 정적 내부 클래스가 로드될 때 -128부터 127까지의 Integer 개체가 로드됩니다. 생성되고 이를 캐시하기 위해 배열 캐시가 생성됩니다. valueOf() 메서드를 사용하여 객체를 생성하면 캐시된 객체가 직접 반환됩니다. 즉, new 키워드나 valueOf() 메서드를 사용하여 더 작은 값 객체를 생성하면 새 객체가 생성되지 않습니다. -128보다 크고 127보다 크면 새 객체가 생성됩니다.
//2、包装类中的缓存机制 Integer num3 = 10; Integer num4 = 10; Integer num5 = new Integer(20); Integer num6 = new Integer(20); Integer num7 = 128; Integer num8 = 128;
num3과 num4는 모두 127보다 작거나 같으므로 동일한 캐시된 Integer 객체를 가리키므로 ==를 사용한 비교 결과는 true입니다. num5와 num6은 새로운 객체를 사용하기 때문입니다. 키워드.Object, 결과는 false입니다. num7 및 num8은 자동 박싱을 사용하지만 valueOf() 메소드가 실행될 때 i >= IntegerCache.low && i
다음으로 소스 코드에서 Integer의 equals() 메소드 구현을 살펴보겠습니다
public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }
equals() 메소드는 객체가 같은지, 비슷한지를 비교하는 것이 아니라 Integer 객체의 값을 비교하는 것입니다. == 연산자의 비교 방법. 따라서 두 Integer 객체의 값이 같은지 비교해야 할 경우에는 equals() 메서드를 사용하는 것을 기억하세요. 캐싱 메커니즘으로 인해 비교에 ==를 사용하면 혼란스러운 결과가 발생할 수 있습니다.
또한 8가지 패키징 유형 중 캐시 영역이 있는 것은 Character, Byte, Short, Integer, Long이 있으며 구현 방법은 기본적으로 동일하며 캐시 범위는 -128부터 127까지입니다. Boolean은 캐시 영역이 없지만 true와 false 두 가지 값만 있기 때문에 Boolean은 멤버 변수에 해당하는 두 개의 개체를 생성합니다. 캐시 영역이 없는 것은 Float과 Double뿐입니다. 캐시 영역이 없는 이유는 매우 간단합니다. 0~1의 작은 범위에서도 셀 수 없이 많은 부동 소수점이 존재하므로 불가능합니다. 캐시 영역을 사용하여 캐시하세요.
캐시 영역이 있어 자주 사용되는 패키징 클래스 객체를 재사용할 수 있어 성능이 향상됩니다. 새 객체를 생성해야 할 경우 새 객체를 생성하면 유연성이 향상됩니다.
//四则运算、位运算 Integer num9 = 1; Integer num10 = 2; Integer num11 = num9 + num10; Short num12 = 5; Integer num13 = num9 + num12; Long num14 = num9 + 10L; System.out.println(num9 << 1); //位运算 System.out.println(num9 +" "+ num10 +" "+ num11 +" "+ num12 +" "+ num13 +" "+ num14);
역컴파일 결과는 다음과 같습니다
정수 정수 = 정수. valueOf(1);
사용 사용 사용 사용 사용 사용 out out out out ‐ off 's ‐ ‐ ‐ ‐ ‐ ‐ ‐ 정수 정수1 = Integer.valueOf(2)
‐ ‐ ‐ 정수 정수2 = Integer.valueOf(integer.intValue() + 정수1.intValue()); = Integer.valueOf(integer.intValue() + short1.shortValue());
Long long1 = Long.valueOf((long)integer.intValue( ) + 10L);
System.out.println(integer.intValue() < ; System.out.println((new StringBuilder()).append(integer).append("t"). append(integer1).append("t").append(integer2).append ("t").append(short1).append("t").append(integer3).append("t").append( long1).toString());
可以看到Integer num11 = num9 + num10; 这一句被划分为3个步骤:将两个Integer对象分别进行拆箱;将拆箱得到的两个int数值相加求其和;将和值进行装箱,从而将num11指向缓存数组中值为3的Integer对象。
而Short num12 = 5; 这一句则先将5强制转换成short类型,再将其装箱把值为5的Short对象的引用赋给num12。
而Integer num13 = num9 + num12; 这一句除了Integer num11 = num9 + num10;的3个步骤,中间还有short+int=int的类型自动提升的过程。
而Long num14 = num9 + 10L; 这一句Integer num11 = num9 + num10;的3个步骤,中间还有强制类型转换的过程。需要注意的是,如果是Long num14 = num9 + num10; 的话就会出现类型不匹配的错误,因为num9、num10拆箱之后相加的和是int类型,而Long.valueOf(long)需要的形参是long类型,自然会出错。我们也可以看到,当包装类型对象和基本类型数据进行四则运算的时候,对象是会被拆箱的,然后再按基本类型数据的运算规则进行运算。
另外,如果仅仅是打印两个包装类型对象求和的结果,是不会有将和值重新转换成该包装类型的步骤的,如下面所示
System.out.println(num9 + num10);
System.out.println(integer.intValue() + integer1.intValue());
尽管基本类型可以进行自动类型提升/强制类型转换,而包装类则没有类似的使用方式,但是在此还需要注意一点。下面的做法是错的。
Short num3 = 10; Integer num4 = num3; //错误: 不兼容的类型: Short无法转换为Integer Long num5 = (Long)num4; //错误: 不兼容的类型: Integer无法转换为Long Double num6 = num5; //错误: 不兼容的类型: Long无法转换为Double
小结:不同包装类型对象是不能直接转换的,不过有两种途径可以代替:一种是上面讨论的不同包装类对象进行四则运算后赋给某一种类型;另一种就是利用包装类的方法
Integer a = 20; Long b = a.longValue(); Short c = b.shortValue(); System.out.println(a +" "+ b +" "+ c);
Integer num9 = 100; Integer num10 = 200; Short num11 = 50; Long num12 = 50L; System.out.println((num9<num10) +" "+ (num9<200) +" "+ (num9<num11) +" "+ (num9<num12) +" "+ (num9<10L));
反编译结果为
Integer integer = Integer.valueOf(100);
Integer integer1 = Integer.valueOf(200);
Short short1 = Short.valueOf((short)50);
Long long1 = Long.valueOf(50L);
System.out.println((new StringBuilder()).append(integer.intValue()
可以看到,两个同类型的包装类对象进行比较时比较的其实是各自的基本类型数值,如num9
当想比较两个对象是否相等时,注意要使用equals()方法,从前面的讨论也知道,使用==的话比较的其实是引用的对象是否同一个,一般不满足我们的需求。
Integer num13 = new Integer(100); System.out.println(num9.equals(num13) +" "+ num9.equals(50));
反编译结果为
Integer integer2 = new Integer(100);
System.out.println((new StringBuilder()).append(integer.equals(integer2)).append("\t").append(integer.equals(Integer.valueOf(50))).toString());
逻辑运算举例:
System.out.println((num9&1));
反编译结果为
System.out.println(integer.intValue() & 1);
五、包装类作为方法的形参、返回值
//包装类作为方法的形参、返回值 public static Integer intToInteger(int i) { return i; } public static int integerToInt(Integer i) { return i; }
反编译结果为
public static Integer intToInteger(int i) { return Integer.valueOf(i); } public static int integerToInt(Integer integer) { return integer.intValue(); }
六、包装类作为集合的元素
//包装类作为集合元素 List list = new ArrayList(); list.add(1); list.add(new Object()); Iterator it = list.iterator(); while (it.hasNext()) { System.out.println(it.next()); }
反编译结果为
ArrayList arraylist = new ArrayList(); arraylist.add(Integer.valueOf(1)); arraylist.add(new Object()); for(Iterator iterator = arraylist.iterator(); iterator.hasNext(); System.out.println(iterator.next()));
可以发现,虽然集合元素要求是对象,add()方法的形参也是对象(public boolean add(E e)),但由于自动装箱,基本数据类型也可以直接加入集合中。
List<Integer> list = new ArrayList<>(); for (int i=0; i<5; i++) { list.add(i); } Iterator it = list.iterator(); while (it.hasNext()) { System.out.println(it.next()); }
反编译结果为
ArrayList arraylist = new ArrayList(); for(int i = 0; i < 5; i++) arraylist.add(Integer.valueOf(i)); for(Iterator iterator = arraylist.iterator(); iterator.hasNext(); System.out.println(iterator.next()));
七、包装类使用过程中有可能引起的空指针异常
//注意包装类可能产生的空引用异常 Boolean flag1 = false; System.out.println(flag1?"命题为真":"命题为假"); Boolean flag2 = null; System.out.println(flag2?"命题为真":"命题为假"); Boolean flag3 = true;
运行结果为
这里只是简单演示空指针异常。平时使用时需要注意这一点,比如当Boolean的对象作为形参时,在方法执行体的头部需要做下null检测。
上述代码的反编译结果为
Boolean boolean1 = Boolean.valueOf(false);
System.out.println(boolean1.booleanValue() ? "\u547D\u9898\u4E3A\u771F" : "\u547D\u9898\u4E3A\u5047");
Boolean boolean2 = null;
System.out.println(boolean2.booleanValue() ? "\u547D\u9898\u4E3A\u771F" : "\u547D\u9898\u4E3A\u5047");
Boolean boolean3 = Boolean.valueOf(true);
可见三目运算符的条件表达式的位置一定是boolean值,如果你传入的是Boolean对象,则会自动拆箱转换为boolean值。
另外,三目运算符的其他两个表达式位置也是如此,会把包装类对象转换为相应的基本类型对象。
为什么需要包装类?
首先,Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,将每个基本数据类型设计一个对应的类进行代表,这种方式增强了Java面向对象的性质。
其次,如果仅仅有基本数据类型,那么在实际使用时将存在很多的不便,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将int 、double等类型放进去的,因为集合的容器要求元素是Object类型。包装类型的作用在于允许将数值类型作为集合对象的元素,弥补了基本数据类型的不足。
除此以外,包装类还扩展了基本类型的功能,通过添加属性和方法来丰富基本类型的操作。如当我们想知道int取值范围的最小值,我们需要通过运算,如下面所示,但是有了包装类,我们可以直接使用Integer.MAX_VALUE即可。
//求int的最大值 int max = 0; int flag = 1; for (int i=0; i<31; i++) { max += flag; flag = flag << 1; } System.out.println(max +" "+ Integer.MAX_VALUE); //2147483647 2147483647
为什么要保留基本数据类型?
我们都知道在Java语言中,用new关键字创建的对象是存储在堆里的,我们通过栈中的引用来使用这些对象,所以,对象本身来说是比较消耗资源的。对于经常用到的类型,如int等,如果我们每次使用这种变量的时候都需要new一个对象的话,就会比较笨重了。所以,Java提供了基本数据类型,这种数据的变量不需要使用new在堆上创建,而是直接在栈内存中存储,因此会更加高效。
위 내용은 Java 래퍼 클래스를 적용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!