为什么java有“String”类型而不是“string”?

包装类很好,它们的用途也很好理解。但是,为什么我们省略了基元类型呢?


答案 1

这取决于你所说的“原始”是什么意思。

Java中的“Primitive”通常意味着“值类型”。但是,C#有一个关键字,其行为与Java的String完全相同,只是编辑器以不同的方式突出显示它。它们是类 或 的别名。String 在任何一种语言中都不是值类型,因此以这种方式它不是基元。stringSystem.Stringjava.lang.String

如果“primitive”的意思是内置于语言中,那么String就是一个原语。它只使用大写字母。字符串文本(引号中的那些内容)会自动转换为 +,并且 + 用于串联。因此,通过这个令牌,它们(和数组)与整数,长头等一样原始。System.String

首先,什么是字符串?

字符串不是包装器。String 是引用类型,而基元类型是值类型。这意味着如果您有:

int x = 5;
int y = x;

x 和 y 的内存都包含“5”。但是:

String x = "a";
String y = x;

x 和 y 的内存都包含指向字符“a”的指针(以及长度、偏移量、ClassInfo 指针和监视器)。字符串的行为就像一个基元,因为它们是不可变的,所以这通常不是问题,但是,如果你,比如说,使用反射来改变字符串的内容(不要这样做!),x和y都会看到变化。事实上,如果您有:

char[] x = "a".toCharArray();
char[] y = x;
x[0] = 'b';
System.out.println(y[0] == 'b'); // prints "true"

所以不要只使用char[](除非这是你想要的行为,或者你真的试图减少内存使用量)。

每个都是一个引用类型 - 这意味着你编写的所有类,框架中的每个类,甚至数组。唯一属于值类型的是简单的数值类型(int、long、short、byte、float、double、char、bool 等)。Object

为什么 String 不像 char[] 那样可变?

这有几个原因,但它主要归结为心理学和实现细节:

  • 想象一下,如果你将一个字符串传递到另一个函数中,并且该函数以某种方式改变了它,你会有多么混乱。或者,如果它将其保存在某个地方并在将来更改它呢?对于大多数引用类型,您接受将其作为类型的一部分,但是Java开发人员决定,至少对于字符串,他们不希望用户担心这一点。
  • 字符串无法以原子方式处理,这意味着多线程/同步将成为一个问题。
  • 字符串文本(您在代码中用引号括起来的内容)在计算机的级别 1 上可能是不可变的(出于安全原因)。这可以通过在程序启动时使用写入时将它们全部复制到内存的另一部分或使用写入时复制来实现,但这很慢

为什么我们没有字符串的值类型版本?

基本上,性能和实现细节,以及拥有2种不同字符串类型的复杂性。其他值类型具有固定的内存占用量。一个int总是32位,一个长总是64位,一个bool总是1位,等等.2除其他外,这意味着它们可以存储在堆栈上,以便函数的所有参数都存在于一个地方。此外,到处制作巨大的琴弦副本会扼杀性能。

另请参阅:在 C# 中,为什么 String 是行为类似于值类型的引用类型?。指的是.NET,但这同样适用于Java。

1 - 在 C/C++ 和其他本机编译的语言中,这是正确的,因为它们位于进程的代码段中,操作系统通常会阻止您编辑该代码段。在Java中,这通常是不真实的,因为JVM将类文件加载到堆上,因此您可以在那里编辑字符串。但是,没有理由不能在本地编译Java程序(有工具可以做到这一点),并且某些体系结构(特别是某些版本的ARM)直接执行Java字节码。

2 - 在实践中,其中一些类型在机器级别上具有不同的尺寸。例如,bools 在堆栈上存储为 WORD 大小(x86 上为 32 位,x64 上为 64 位)。在类/数组中,它们可能会被区别对待。这都是留给JVM的实现细节 - 规范说布尔斯要么是真的,要么是假的,机器可以弄清楚如何做到这一点。


答案 2

的基元类型为 。Stringchar[]

对于许多语言(C,Java,C#,C++等等)都是如此。