你好!您已经非常熟悉原始类型,并且经常使用它们。 编程中的原语,特别是 Java 中的原语,有很多优点:它们占用很少的内存,从而提高了程序的效率,并且被明确地划分为值的范围。然而,在学习Java的过程中,我们不止一次地重复着,就像一句口头禅,“在Java中一切皆对象”。但原语是对这些话的直接反驳。它们不是物体。那么“一切皆对象”的原则是错误的吗?并不真地。在 Java 中,每个基本类型都有它的孪生兄弟,即包装类(
包装类对象的创建就像任何其他对象一样:
Wrapper
)。什么是包装纸? 包装器是一个特殊的类,它在其内部存储原语的值。 但由于这是一个类,它可以创建自己的实例。它们将在内部存储必要的原始值,同时是真实的对象。包装类的名称与相应基元的名称非常相似,或者完全一致。因此,记住它们会很容易。
原始数据类型的包装类 | |
---|---|
原始数据类型 | 包装类 |
整数 | 整数 |
短的 | 短的 |
长的 | 长的 |
字节 | 字节 |
漂浮 | 漂浮 |
双倍的 | 双倍的 |
字符 | 特点 |
布尔值 | 布尔值 |
public static void main(String[] args) {
Integer i = new Integer(682);
Double d = new Double(2.33);
Boolean b = new Boolean(false);
}
包装类允许您减轻原始类型的缺点。最明显的一点是原语没有方法。例如,它们没有 method toString()
,因此您无法将数字转换int
为字符串。但有了包装类Integer
就很容易了。
public static void main(String[] args) {
Integer i = new Integer(432);
String s = i.toString();
}
反向转换也会遇到困难。假设我们有一个我们确定包含数字的字符串。然而,在基本类型的情况下,int
我们将无法从字符串中获取该数字并将其实际上转换为数字。但多亏了包装类,我们现在有了这个机会。
public static void main(String[] args) {
String s = "1166628";
Integer i = Integer.parseInt(s);
System.out.println(i);
}
输出: 1166628 我们已成功从字符串中检索到数字并将其分配给引用变量Integer i
。顺便说一下,关于链接。您已经知道参数以不同的方式传递给方法:基元按值传递,对象按引用传递。您可以在创建方法时使用这些知识:例如,如果您的方法适用于小数,但您需要通过引用传递的逻辑,则可以将参数传递给方法Double/Float
而不是double/float
. 另外,除了方法之外,包装类还有静态字段,使用起来非常方便。例如,假设您现在面临一项任务:将最大可能的数字打印到控制台 int
,然后打印最小可能的数字。 这项任务似乎很简单,但如果没有谷歌,你仍然很难完成它。包装类可以轻松地让您解决以下“日常问题”:
public class Main {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
}
}
这些领域可以让你不会从更严肃的任务中分心。更不用说在打印数字2147483647(这正是 MAX_VALUE)的过程中,输入错误也就不足为奇了:) 另外,在之前的一讲中,我们已经提请注意这样一个事实:包装类的对象是不可变的(Immutable)。
public static void main(String[] args) {
Integer a = new Integer(0);
Integer b = new Integer(0);
b = a;
a = 1;
System.out.println(b);
}
输出: 0引用 最初指向的对象а
没有改变其状态,否则值b
也会改变。与 一样String
,不是更改包装器对象的状态,而是在内存中创建一个全新的对象。为什么 Java 的创建者最终决定在该语言中保留原始类型?既然一切都应该是对象,而且我们已经有了可以用来表达原语表达的一切的包装类,为什么不把它们留在语言中并删除原语呢?答案很简单——性能。原始类型之所以被称为原始类型,是因为它们缺乏对象的许多“重”特征。是的,一个对象有很多方便的方法,但你并不总是需要它们。有时您只需要数字 33 或 2.62 或true
/的值false
。在对象的所有好处都不重要并且程序不需要运行的情况下,基元会做得更好。
自动装箱/自动拆箱
Java中原语及其包装类的特性之一是自动装箱/自动拆箱, 让我们来理解这个概念。正如您和我之前已经了解到的,Java 是一种面向对象的语言。这意味着所有用Java编写的程序都是由对象组成的。基元不是对象。但是,可以为包装类变量分配基本类型的值。这个过程称为自动装箱。以同样的方式,可以将原始类型的变量分配给包装类的对象。这个过程称为自动拆箱。例如:public class Main {
public static void main(String[] args) {
int x = 7;
Integer y = 111;
x = y; // auto unpacking
y = x * 123; // autopacking
}
}
在第 5 行,我们将 y 的值赋给基元 x,y 是包装类的对象Integer
。正如您所看到的,不需要执行任何其他操作:编译器知道这一点,int
并且Integer
事实上,是同一件事。这是自动拆包。第 6 行中的自动装箱也发生了同样的事情:对象 y 很容易被分配基元的值 (x*123)。这是自动打包的示例。这就是添加“自动”一词的原因:将原始引用分配给其包装类的对象(反之亦然),您不需要执行任何操作,一切都会自动发生。方便吧?:) 自动打包/自动解包的另一个很大的便利体现在方法的操作上。事实上,方法参数也受到自动打包和自动解包的影响。并且,例如,如果其中一个将两个对象作为输入Integer
,我们可以轻松地向那里传递普通基元int
!
public class Main {
public static void main(String[] args) {
printNumber(7);//regular int, even without a variable
}
public static void printNumber(Integer i) {
System.out.println("You entered a number" + i);
}
}
输出: 您输入了数字 7。 反之亦然:
public class Main {
public static void main(String[] args) {
printNumber(new Integer(632));
}
public static void printNumber(int i) {
System.out.println("You entered a number" + i);
}
}
要记住的重要一点:自动装箱和拆箱不适用于数组!
public class Main {
public static void main(String[] args) {
int[] i = {1,2,3,4,5};
printArray(i);//error, won't compile!
}
public static void printArray(Integer[] arr) {
System.out.println(Arrays.toString(arr));
}
}
尝试将基元数组传递给以对象数组作为输入的方法将导致编译错误。最后,我们再次简单比较一下原语和包装 原语:
- 有性能优势
- 它们让你不违反“一切皆对象”的原则,这样数字、符号和布尔值true/false就不会脱离这个概念
- 通过提供方便的方法和字段来扩展使用这些值的能力
- 当某些方法只能与对象一起使用时是必要的
GO TO FULL VERSION