介绍
程序员的道路是一个复杂而漫长的过程。在大多数情况下,它以一个在屏幕上显示 Hello World 的程序开始。Java 也不例外(请参阅课程:
“Hello World!”应用程序)。正如我们所看到的,消息是使用以下方式输出的:
System.out.println("Hello World!");
如果您查看 Java API,则
System.out.println方法将
String作为输入参数。我们将讨论此类数据。
字符串作为字符序列
其实
String从英文翻译过来就是字符串。没错,String类型代表的是文本字符串。什么是文本字符串?文本字符串是某种彼此跟随的有序字符序列。符号是字符。顺序——顺序。所以是的,绝对正确,String 是
java.lang.CharSequence
. 如果你查看 String 类本身的内部,那么在它的内部就只有一个字符数组:
private final char value[];
它有
java.lang.CharSequence
一个相当简单的契约:
我们有一个获取元素数量的方法,获取特定元素和获取一组元素+ toString 方法本身,该方法将返回 this)了解 Java 8 中提供给我们的方法更有趣,这是:
chars()
回想
codePoints()
一下 Oracle 教程中的“
原始数据”类型,char 是
single 16-bit Unicode character
。也就是说,本质上 char 只是 int(32 位)大小一半的类型,表示从 0 到 65535 的数字(请参阅十进制值在
ASCII 表中)。也就是说,如果我们愿意,我们可以将 char 表示为 int。Java 8 利用了这一点。从 Java 版本 8 开始,我们有了
IntStream——一个用于处理原始 int 的流。因此,在 charSequence 中可以获得表示 chars 或 codePoints 的 IntStream。在继续讨论之前,我们将通过一个示例来展示这种方法的便利性。让我们使用
Tutorialspoint在线java编译器并执行代码:
public static void main(String []args){
String line = "aaabccdddc";
System.out.println( line.chars().distinct().count() );
}
现在您可以通过这种简单的方式获得许多独特的符号。
代码点
所以,我们看到了字符。现在还不清楚这些是什么类型的代码点。codePoint的概念出现是因为当Java出现时,16位(半个int)足以编码一个字符。因此,java中的char以UTF-16格式(“Unicode 88”规范)表示。后来出现了Unicode 2.0,其概念是将一个字符表示为代理对(2个字符)。这使我们能够将可能值的范围扩展到 int 值。有关更多详细信息,请参阅 stackoverflow:“
将字符与代码点进行比较? ” JavaDoc for
Character中也提到了 UTF-16 。在 JavaDoc 中,据说:
In this representation, supplementary characters are represented as a pair of char values, the first from the high-surrogates range, (\uD800-\uDBFF), the second from the low-surrogates range (\uDC00-\uDFFF).
用标准字母表重现这一点是相当困难的(甚至可能是不可能的)。但符号并非以字母和数字结尾。在日本,他们想出了一种难以编码的东西,即表情符号——表意文字和表情符号的语言。维基百科上有一篇关于此的有趣文章:“
Emoji ”。让我们找一个表情符号的例子,例如:“
Emoji Ghost ”。正如我们所看到的,甚至在那里指示了相同的代码点(值 = U+1F47B)。它以十六进制格式表示。如果我们转换为十进制数,我们会得到 128123。这超过了允许的 16 位(即超过 65535)。我们来复制一下:
不幸的是,JavaRush 平台不支持文本中的此类字符。因此,在下面的示例中,您需要将一个值插入到 String 中。因此,现在我们将了解一个简单的测试:
public static void main(String []args){
String emojiString = "Вставте сюда эмоджи через ctrl+v";
System.out.println(emojiString.codePoints().count());
System.out.println(emojiString.chars().count());
}
正如您所看到的,在这种情况下,1 个代码点对应 2 个字符。这就是魔法。
特点
正如我们上面看到的,Java 中的字符串由 char 组成。原始类型允许您存储值,但是
java.lang.Character
原始类型的包装器允许您使用此符号执行许多有用的操作。例如,我们可以将字符串转换为大写:
public static void main(String[] args) {
String line = "организация объединённых наций";
char[] chars = line.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (i == 0 || chars[i - 1] == ' ') {
chars[i] = Character.toUpperCase(chars[i]);
}
}
System.out.println(new String(chars));
}
嗯,各种有趣的东西:,,,,,,
isAlphabetic()
(
isLetter()
例如,括号。 ' ('有镜像')') 。
isSpaceChar()
isDigit()
isUpperCase()
isMirrored()
字符串池
Java中的字符串是不可变的,即常量。
java.lang.String类本身的 JavaDoc 中也指出了这一点。其次,也是非常重要的,字符串可以指定为文字:
String literalString = "Hello, World!";
String literalString = "Hello, World!";
也就是说,如上所述,任何带引号的字符串实际上都是一个对象。这就引出了一个问题 - 如果我们如此频繁地使用字符串并且它们通常是相同的(例如,文本“Error”或“Successively”),有什么方法可以确保不会每次都创建字符串?顺便说一下,我们还有 Map,其中键可以是字符串。那么我们绝对不能让相同的字符串成为不同的对象,否则我们将无法从Map中获取对象。Java 开发人员想了又想,最后想出了字符串池。这是存储字符串的地方,你可以称之为字符串缓存。并不是所有的行本身都结束在那里,而是只有代码中通过文字指定的行才结束。您可以自己向池中添加一条线,稍后会详细介绍。所以,在内存中我们在某个地方有这个缓存。一个公平的问题:这个游泳池位于哪里?这个问题的答案可以在 stackoverflow 上找到:“
Java 的字符串常量池位于哪里,堆还是栈?” 它位于Heap内存中,一个特殊的运行时常量池区域。
当虚拟机从方法区(Java 虚拟机内的所有线程都可以访问的堆中的一个特殊区域)创建类或接口时,就会分配运行时常量池。字符串池给我们带来了什么?这有几个优点:
- 不会创建相同类型的对象
- 通过引用进行比较比通过 equals 进行逐个字符比较更快
但是如果我们想将创建的对象放入这个缓存中怎么办?然后,我们有一个特殊的方法:
String.intern 这个方法将一个字符串添加到字符串池中。值得注意的是,这不仅仅是某种数组形式的缓存(对于整数)。实习生方法被指定为“native”。这意味着该方法本身是用另一种语言(主要是 C++)实现的。对于基本 Java 方法,可以在 JVM 级别对其应用各种其他优化。一般来说,魔法会在这里发生。阅读以下关于实习生的文章很有趣:
https://habr.com/post/79913/#comment_2345814 这似乎是个好主意。但这将如何影响我们呢?但确实会有影响)
public static void main(String[] args) {
String test = "literal";
String test2 = new String("literal");
System.out.println(test == test2);
}
正如您所看到的,行是相同的,但结果将是错误的。这都是因为 == 不是按值比较,而是按引用比较。这就是它的工作原理:
public static void main(String[] args) {
String test = "literal";
String test2 = new String("literal").intern();
System.out.println(test == test2);
}
请注意,我们仍然会创建新的字符串。也就是说,intern会从缓存中返回给我们一个String,但是我们在缓存中搜索到的原始String会被扔掉进行清理,因为 没有人知道他的事。这显然是不必要的资源消耗 =( 因此,您应该始终使用 equals 来比较字符串,以尽可能避免突然且难以检测的错误。
public static void main(String[] args) {
String test = "literal";
String test2 = new String("literal").intern();
System.out.println(test.equals(test2));
}
Equals 执行逐个字符的字符串比较。
级联
Как мы помним, строки можно складывать. И How мы помним строки у нас неизменяемы. Так How же тогда это работает? Всё верно, создаётся новая строка, которая состоит из символов складываемых an objectов. Существует миллион версий о том, How работает конкатенация через плюс. Кто-то считает что будет каждый раз новый an object, кто-то считает что будет ещё что-то. Но прав может быть кто-то один. И этот кто-то – компилятор javac. Воспользуемся service
онлайн компилятора и выполним:
public class HelloWorld {
public static void main(String[] args) {
String helloMessage = "Hello, ";
String target = "World";
System.out.println(helloMessage + target);
}
}
Теперь сохраним это How zip архив, извлечём в каталог и выполним:
javap –c HelloWorld
И тут мы всё узнаем:
В цикле, конечно, лучше делать конкатенацию через StringBuilder самим. И не потому что Howая-то магия, а чтобы StringBuilder создавался до цикла, а в самом цикле происходил только append. Кстати, тут есть ещё одна интересность. Есть отличная статья: «
Обработка строк в Java. Часть I: String, StringBuffer, StringBuilder». Много полезного в комментариях. Например, указано, что при конкатенации вида
new StringBuilder().append()...toString()
действует intrinsic оптимизация, регулируемая опцией -XX:+OptimizeStringConcat, которая по умолчанию включена. intrinsic - переводится How "внутренний". Такие вещи JVM обрабатывает особенным образом, обрабатывая их How Native, только без дополнительных затрат на JNI. Подробнее: "
Intrinsic Methods in HotSpot VM".
StringBuilder и StringBuffer
Как мы выше видели, StringBuilder очень полезный инструмент. Строки являются immutable, т.е. неизменяемыми. А складывать хочется. Поэтому, нам в помощь даны 2 класса: StringBuilder и StringBuffer. Основное отличие между ними в том, что StringBuffer появился в JDK1.0, в то время How StringBuilder пришёл в java 1.5 How не синхронизированная version StringBuffer, чтобы снять повышенные затраты на ненужную синхронизацию методов. Оба эти классы являются реализацией абстрактного класса AbstractStringBuilder - A mutable sequence of characters. Внутри хранится массив чаров, который расширяется по правилу: value.length * 2 + 2. По умолчанию размер (capacity) у StringBuilder'а equals 16.
Comparable
Строки являются comparable, т.е. реализуют метод compareTo. Выполняется это при помощи посимвольного сравнения. Интересно, что из двух строк выбирается минимальная длинна и по ней выполняется цикл. Поэтому, compareTo вернёт or разницу между int значениями первых несовпавших символов в пределе наименьшей из длинн строк, либо вернёт разницу между длиннами строк, если в пределах минимальной длинны строки все символы совпадают. Такое сравнение называется «лексикографическим».
Работа со строками Java
String имеет множество полезных методов:
На работу со строками сущесвует множество задач. Например, на
Coding Bat. Так же есть курс на coursera: "
Algorithms on Strings".
Заключение
即使是对该类的简短概述也会占用大量空间。这还不是全部。我强烈建议观看 JPoint 2015 的报告:Alexey Shipilev - Catechism java.lang.String
#维亚切斯拉夫
GO TO FULL VERSION