210.md 21.7 KB
Newer Older
W
wizardforcel 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
# Java 字符串面试问题与答案

> 原文: [https://howtodoinjava.com/interview-questions/interview-stuff-about-string-class-in-java/](https://howtodoinjava.com/interview-questions/interview-stuff-about-string-class-in-java/)

我们所有人都必须经过与 Java 中的[字符串](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html)类相关的[面试问题](//howtodoinjava.com/java-interview-questions/)。 这些**字符串面试问题**的范围从[不变性](https://en.wikipedia.org/wiki/Immutable_object#Java "immutability in java")到内存泄漏问题。 我将在这篇文章中尝试解决此类问题。

```java
Frequently asked String Interview Questions

1\. Is String keyword in Java?
2\. Why are strings immutable?
3\. What is String constant pool?
4\. Keyword 'intern' usage
5\. Matching Regular expressions?
6\. String comparison with equals() and '=='?
7\. Memory leak issue in String class
8\. How does String work in Java?
9\. What are different ways to create String Object?
10\. How to check if String is Palindrome.
11\. How to remove or replace characters from String.
12\. How to make String upper case or lower case?
13\. How to compare two Strings in java program?
14\. Can we use String in the switch case?
15\. Write a program to print all permutations of String?
16\. Write a java program to reverse each word of a given string??
17\. How to Split String in java?
18\. Why is Char array preferred over String for storing password?
19\. Is String thread-safe in Java
20\. Why String is popular HashMap key in Java
21\. Difference between String, StringBuffer and StringBuilder?
22\. How to concatenate multiple strings.
23\. How many objects will be created with string initialization code?
24\. How do you count the number of occurrences of each character in a string?
25\. Write a java program to reverse a string?
```

## 1\. Java 中的 String 关键字吗?

没有。 `String`不是 Java 保留的关键字。 它是派生类型数据类型,即类。

```java
public class StringExample 
{
    public static void main(String[] args) 
    {
        Integer String = 10;

        System.out.println(String);		//Prints 10
    }
}

```

## 2.为什么字符串是不可变的?

我们都知道 java 中的字符串是[不可变的](https://howtodoinjava.com/java/string/java-interview-question-why-strings-are-immutable/)。 如果您想知道什么是不变性以及如何实现? 按照这篇文章:[如何使 java 类不可变](//howtodoinjava.com/java/related-concepts/how-to-make-a-java-class-immutable/ "How to make a java class immutable")

这里的问题是为什么? 为什么一成不变? 让我们分析一下。

1.  The very first reason i can think of is ***performance increase***. Java language was developed to speed up the application development as it was not that much fast in previous languages. JVM designers must have been smart enough to identify that real-world applications will consist of mostly Strings in form of labels, messages, configuration, output and such numerous ways.

W
wizardforcel 已提交
62
    看到这种过度使用,他们想像到不正确使用字符串会带来多大的危险。 因此,他们提出了字符串池的概念(下一部分)。 字符串池不过是一些大多数唯一字符串的集合。 字符串池背后的最基本思想是重新创建字符串。 这样,如果一个特定的字符串在代码中创建了 20 次,则应用最终将只有一个实例。
W
wizardforcel 已提交
63 64 65 66 67 68 69

2.  第二个原因是 ***安全注意事项*** 。 字符串是 Java 编程各个方面中最常用的参数类型。 无论是加载驱动程序还是打开 URL 连接,您都需要以字符串形式将信息作为参数传递。 如果字符串不是最终的,那么它们就打开了一个安全问题的潘多拉盒。我们所有人都必须通过与 Java 中的 [String](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html) 类相关的[面试问题](//howtodoinjava.com/java-interview-questions/)。 这些问题的范围从[不变性](https://en.wikipedia.org/wiki/Immutable_object#Java "immutability in java")到内存泄漏问题。 我将在这篇文章中尝试解决此类问题。

除了上述两个原因外,我没有找到任何令人信服的答案。 如果您有任何吸引人的内容,请与我分享。

## 3.字符串池概念

W
wizardforcel 已提交
70
字符串池是一个特殊的内存区域,与存储这些字符串常量的常规堆内存分开。 这些对象在应用的生命周期中称为字符串变量。
W
wizardforcel 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

在 Java 中,可以通过多种方式创建 String。 让我们了解它们:

#### 1)字符串分配

```java

String str = "abc";

```

上面的代码使 JVM 验证是否已经有一个字符串“ abc”(相同的字符序列)。 如果存在这样的字符串,则 JVM 仅将现有对象的引用分配给变量`str`,否则,将创建一个新的对象“ abc”,并将其引用分配给变量`str`

#### 2)使用新关键字

```java

String str = new String("abc");

```

此版本最终**在内存**中创建两个对象。 字符串池中的一个对象具有 char 序列“ abc”,第二个对象在堆存储器中由变量`str`引用,并且具有与“ abc”相同的 char 序列。

正如 java docs 所说的: ***除非需要显式的原始副本,否则不需要使用此构造函数,因为字符串是不可变的。***

## 4.关键字“实习生”的用法

[java docs](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#intern--) 最好地描述了这一点:

调用`intern()`方法时,如果池已经包含一个与`equals(Object)`方法确定的等于`String`对象的字符串,则返回池中的字符串。 否则,将此`String`对象添加到池中,并返回对此`String`对象的引用。

```java
String str = new String("abc");

str.intern();

```

W
wizardforcel 已提交
109
因此,对于`s``t`中的任何两个字符串,当且仅当`s.equals(t)``true`时,`s.intern() == t.intern()`才为`true`。 意味着如果 s 和 t 都是不同的字符串对象,并且具有相同的字符序列,则在这两个变量上调用 intern()将导致两个变量引用的单个字符串池文字。
W
wizardforcel 已提交
110 111 112

## 5.匹配正则表达式

W
wizardforcel 已提交
113
如果您还没有探索的话,它并不是那么秘密,但很有用。 您必须已经看到[模式](https://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html "java util  Pattern")和 Matcher 用于正则表达式匹配的用法。 字符串类提供了自己的快捷方式。 直接使用。 此方法还使用函数定义内的 *Pattern.matches()*
W
wizardforcel 已提交
114 115 116 117 118 119 120 121 122

```java

String str = new String("abc");

str.matches("<regex>");

```

W
wizardforcel 已提交
123
## 6.使用 equals()和'=='进行字符串比较
W
wizardforcel 已提交
124 125 126 127

采访中另一个最喜欢的领域。 通常有两种比较对象的方法

*   使用==运算符
W
wizardforcel 已提交
128
*   使用 equals()方法
W
wizardforcel 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347

**`==`运算符比较对象引用**,即内存地址相等性。 因此,如果两个字符串对象引用字符串池中的相同文字或堆中的相同字符串对象,则`s==t`将返回`true`,否则返回`false`

`equals()`方法在 String 类中被覆盖,**它验证字符串对象**持有的 char 序列。 如果它们存储相同的字符序列,则 s.equals(t)将返回 true,否则返回 false。

## 7.内存泄漏问题

到目前为止,我们已经完成了基本的工作。 现在发生了严重的事情。 您是否尝试过从字符串对象创建子字符串? 我敢打赌,是的。 您知道 Java 中子字符串的内部吗? 他们如何造成内存泄漏?

Java 中的子字符串是使用方法`substring(int beginIndex)`和此方法的其他一些重载形式创建的。 所有这些方法都会创建一个新的 String 对象,并更新在本文开头看到的 offset 和 count 变量。

*原始值[]不变。 因此,如果您创建一个包含 10000 个字符的字符串,并创建 100 个每个包含 5-10 个字符的子字符串,则所有 101 个对象将具有大小为 10000 个字符的相同字符数组。 毫无疑问,这是内存浪费。*

让我们使用程序来看看:

```java

import java.lang.reflect.Field;
import java.util.Arrays;

public class SubStringTest {
	public static void main(String[] args) throws Exception
	{
		//Our main String
		String mainString = "i_love_java";
		//Substring holds value 'java'
		String subString = mainString.substring(7);

		System.out.println(mainString);
		System.out.println(subString);

		//Lets see what's inside mainString
		Field innerCharArray = String.class.getDeclaredField("value");
		innerCharArray.setAccessible(true);
		char[] chars = (char[]) innerCharArray.get(mainString);
		System.out.println(Arrays.toString(chars));

		//Now peek inside subString
		chars = (char[]) innerCharArray.get(subString);
		System.out.println(Arrays.toString(chars));
	}
}

Output:

i_love_java
java
[i, _, l, o, v, e, _, j, a, v, a]
[i, _, l, o, v, e, _, j, a, v, a]

```

显然,两个对象都存储有相同的 char 数组,而 subString 只需要四个字符。

让我们使用自己的代码解决此问题:

```java

import java.lang.reflect.Field;
import java.util.Arrays;

public class SubStringTest
{
	public static void main(String[] args) throws Exception
	{
		//Our main String
		String mainString = "i_love_java";
		//Substring holds value 'java'
		String subString = fancySubstring(7, mainString);

		System.out.println(mainString);
		System.out.println(subString);

		//Lets see what's inside mainString
		Field innerCharArray = String.class.getDeclaredField("value");
		innerCharArray.setAccessible(true);
		char[] chars = (char[]) innerCharArray.get(mainString);
		System.out.println(Arrays.toString(chars));

		//Now peek inside subString
		chars = (char[]) innerCharArray.get(subString);
		System.out.println(Arrays.toString(chars));
	}

	//Our new method prevents memory leakage
	public static String fancySubstring(int beginIndex, String original)
	{
		return new String(original.substring(beginIndex));
	}
}

Output:

i_love_java
java
[i, _, l, o, v, e, _, j, a, v, a]
[j, a, v, a]

```

现在,子字符串只有它需要的字符,并且可以无用地收集用于创建正确子字符串的中间字符串,从而不占用任何内存。

## 8\. String 如何在 Java 中工作?

Java 中的字符串类似于任何其他编程语言,都是字符序列。 这更像是用于该 char 序列的实用程序类。 此 char 序列在以下变量中维护:

```java
/** The value is used for character storage. */
private final char value[];

```

要在不同的情况下访问此数组,请使用以下变量:

```java
/** The offset is the first index of the storage that is used. */
private final int offset;

/** The count is the number of characters in the String. */
private final int count;

```

## 10.如何检查回文中的字符串?

如果字符串的值在反转时相同,则称其为[回文](https://en.wikipedia.org/wiki/Palindrome)。 要检查回文,只需反转字符串并检查原始字符串和受尊敬的字符串的内容。

```java
public class StringExample 
{
    public static void main(String[] args) 
    {
        String originalString = "abcdcba";

        StringBuilder strBuilder = new StringBuilder(originalString);
        String reverseString = strBuilder.reverse().toString();

       boolean isPalindrame = originalString.equals(reverseString);

       System.out.println(isPalindrame);	//true
    }
}

```

## 11.如何从 String 中删除或替换字符?

要替换或删除字符,请使用`String.replace()``String.replaceAll()`。 这些方法有两个参数。 第一个参数是要替换的字符,第二个参数是将放置在字符串中的新字符。

如果要删除字符,请在第二个参数中传递空白字符。

```java
String originalString = "howtodoinjava";

//Replace one character
System.out.println( originalString.replace("h", "H") );         //Howtodoinjava

//Replace all matching characters
System.out.println( originalString.replaceAll("o", "O") );      //hOwtOdOinjava

//Remove one character
System.out.println( originalString.replace("h", "") );         //owtodoinjava

//Remove all matching characters
System.out.println( originalString.replace("o", "") );         //hwtdinjava

```

## 12.如何使 String 大写或小写?

使用`String.toLowerCase()``String.toUpperCase()`方法将字符串转换为小写或大写。

```java
String blogName = "HowToDoInJava.com";

System.out.println(blogName.toLowerCase());     //howtodoinjava.com

System.out.println(blogName.toUpperCase());     //HOWTODOINJAVA.COM

```

## 13.如何在 Java 程序中比较两个字符串?

始终使用`equals()`方法来验证字符串是否相等。 切勿使用`"=="`运算符。 双重等于运算符始终检查内存中的对象引用。 `equals()`方法检查字符串内容。

```java
String blogName = "HowToDoInJava.com";

String anotherString = new String("HowToDoInJava.com");

System.out.println(blogName == anotherString);     //false

System.out.println(blogName.equals(anotherString));     //true

```

## 14.我们可以在开关盒中使用 String 吗?

是的,自 Java 7 起,您可以在`switch`语句中使用 [`String`类。在 Java 7 之前,这是不可能的,您必须使用`if-else`语句才能实现类似的行为。](https://howtodoinjava.com/java7/string-class-is-supported-in-switch-statement-in-java-7/)

```java
String number = "1";

switch (number) 
{
case "1":
    System.out.println("One");	//Prints '1'
    break;
case "2":
    System.out.println("Two");
    break;
default:
    System.out.println("Other");
}

```

## 15.编写一个程序以打印 String 的所有排列?

W
wizardforcel 已提交
348
排列是对字符的有序列表的元素进行重新排列,以使每个排列相对于其他排列都是唯一的。 例如 以下是字符串“ ABC”的排列 – ABC ACB BAC BCA CBA CAB。
W
wizardforcel 已提交
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432

> 长度为`N`的字符串具有`N! (N Factorial)`排列。

```java
import java.util.HashSet;
import java.util.Set;

public class StringExample 
{
    public static void main(String[] args) 
    {
        System.out.println(getPermutations("ABC"));	

        //Prints
        //[ACB, BCA, ABC, CBA, BAC, CAB]
    }

    public static Set<String> getPermutations(String string) 
    {
        //All permutations
        Set<String> permutationsSet = new HashSet<String>();

        // invalid strings
        if (string == null || string.length() == 0) 
        {
            permutationsSet.add("");
        } 
        else 
        {
            //First character in String
            char initial = string.charAt(0); 

            //Full string without first character
            String rem = string.substring(1); 

            //Recursive call
            Set<String> wordSet = getPermutations(rem);

            for (String word : wordSet) {
                for (int i = 0; i <= word.length(); i++) {
                    permutationsSet.add(charInsertAt(word, initial, i));
                }
            }
        }
        return permutationsSet;
    }

    public static String charInsertAt(String str, char c, int position) 
    {
        String begin = str.substring(0, position);
        String end = str.substring(position);
        return begin + c + end;
    }
}

```

## 16.编写一个 Java 程序来反转给定字符串的每个单词?

要分别反转每个单词,首先,对字符串进行标记,并在数组中将所有单词分开。 然后对每个单词应用反向单词逻辑,最后连接所有单词。

```java
String blogName = "how to do in java dot com";

//spilt on white space
String[] tokens = blogName.split(" ");

//It will store reversed words 
StringBuffer finalString = new StringBuffer();

//Loop all words and reverse them
for (String token : tokens) {
    String reversed = new StringBuffer(token).reverse().toString();
    finalString.append(reversed);
    finalString.append(" ");
}

//Check final string
System.out.println(finalString.toString());     //woh ot od ni avaj tod moc

```

## 17.如何在 Java 中拆分字符串?

W
wizardforcel 已提交
433
使用[`String.split()`](https://howtodoinjava.com/java/string/4-ways-to-split-tokenize-strings-in-java/)方法可在给定正则表达式的匹配项附近中断给定字符串。 也称为基于定界符的 get **字符串令牌。**
W
wizardforcel 已提交
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453

`split()`方法返回字符串数组。 数组中的每个字符串都是单独的标记。

```java
String numbers = "1,2,3,4,5,6,7";

String[] numArray = numbers.split(",");

System.out.println(Arrays.toString(numArray));	//[1, 2, 3, 4, 5, 6, 7]

```

## 18.为什么用 Char 数组而不是 String 来首选 Char 数组来存储密码?

我们知道字符串存储在 Java 的常量池中。 一旦在字符串池中创建了一个字符串,它将一直保留在该池中,直到收集到垃圾为止。 这时,任何恶意程序都可以访问物理内存位置中的内存位置,也可以访问字符串。

如果我们将密码存储为字符串,那么它也将存储在 spring pool 中,并且在内存中的可用时间比要求的更长,因为垃圾收集周期是不可预测的。 这使得敏感的密码字符串**容易遭到黑客攻击和数据盗窃**

使用后可以将 String 留空吗? 不,我们不可以。 我们知道,一旦创建了字符串,我们将无法对其进行操作,例如 您不能更改其内容。 字符串是最终的且不可变的。

W
wizardforcel 已提交
454
但是 char 数组是可变的,使用后它们的内容可以被覆盖。 因此,您的应用应使用 char []存储密码文本,并在使用密码后,将数组内容替换为空白。
W
wizardforcel 已提交
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583

```java
String password = "123456";     //Do not use it

char[] passwordChars = new char[4];      //Get password from some system such as database

//use password

for(char c : passwordChars) {
    c = ' ';
}

```

## 19\. Java 中的字符串线程安全吗?

是的,字符串是[线程安全的](https://howtodoinjava.com/java/multi-threading/what-is-thread-safety/)。 它们是不可变的,默认情况下,java 中所有不可变的实例都是线程安全的。

## 20.为什么 String 是 Java 中流行的 HashMap 键?

在 Java 中,必须在`Map`中使用的密钥是不可变的,并且应遵循`equals()``hashCode()`方法之间的约定。 `String`类满足这两个条件。

另外,String 类提供了许多有用的方法来比较,排序,标记化或小写。 在`Map`上执行 CRUD 操作时可以使用这些方法。 这使它成为在`Map`中使用而不是创建自己的类的非常有用的类。

## 21\. String,StringBuffer 和 StringBuilder 之间的区别?

*   `String`类表示字符序列,并提供了使用字符的有用方法。 字符串类实例是不可变的。 因此,每次使用字符串类执行字符串连接时,都会使用连接的字符串创建一个新对象。
*   `StringBuilder` class is used to perform string concatenation operations in more memory efficient way. It internally maintains a `char array` and manipulate the content in this array only.

    执行完所有操作后需要获取完整的串联字符串时,它将创建一个具有字符数组内容的新 String。

*   `StringBuffer``StringBuilder`类非常相似。 唯一的区别是它是线程安全的。 所有方法都是`synchronized`

## 22.如何连接多个字符串?

根据您是否需要线程安全来使用`StringBuffer``StringBuilder`类。 在两个类中都使用`append()`方法来连接字符串。

```java
StringBuffer buffer = new StringBuffer();

buffer.append("how")
        .append("to")
        .append("do")
        .append("in")
        .append("java")
        .append(".")
        .append("com");

String blogName = buffer.toString();

System.out.println(blogName); //howtodoinjava.com

```

## 23.使用字符串初始化代码将创建多少个对象?

```java
String s1 = "howtodoinjava.com";
String s2 = "howtodoinjava.com";
String s3 = new String("howtodoinjava.com");

```

1.  上面的代码将创建 **2 个对象**
2.  第一个语句将在字符串池中创建第一个对象。
3.  第二条语句不会创建任何新对象,并且`s2`将引用与`s1`相同的字符串常量。
4.  第三条语句将在堆内存中创建一个新的字符串对象。

24.如何计算字符串中每个字符的出现次数?

为了找到给定字符串中每个字符的出现次数,我们使用了`HashMap`,并将该字符作为键,并将其出现作为值。 每次出现新的字符时,我们将增加该字符的值。

```java
String blogName = "howtodoinjava.com";

HashMap<Character, Integer> occurancesMap = new HashMap<Character, Integer>();

char[] strArray = blogName.toCharArray();

for (char c : strArray)
{
    if(occurancesMap.containsKey(c))
    {
        occurancesMap.put(c, occurancesMap.get(c)+1);
    }
    else
    {
        occurancesMap.put(c, 1);
    }
}

System.out.println(occurancesMap);
//{a=2, c=1, d=1, h=1, i=1, j=1, m=1, n=1, .=1, o=4, t=1, v=1, w=1}

```

25.编写一个 Java 程序来反转没有 StringBuilder 或 StringBuffer 的字符串?

反转字符串的最佳方法肯定是`StringBuffer.reverse()``StringBuilder.reverse()`方法。 不过,面试官可能会要求您编写自己的程序,以检查您的技能水平。

使用下面给出的基于递归的示例来反转字符串。 该程序从字符串中获取第一个字符,并将其放置在字符串中的最后一个位置。 它将替换字符串中的所有字符,直到验证整个字符串为止。

```java
public class StringExample
{
    public static void main(String[] args) 
    {
        String blogName = "howtodoinjava.com";

        String reverseString = recursiveSwap(blogName);

        System.out.println(reverseString);
    }

    static String recursiveSwap(String str)
    {
         if ((null == str) || (str.length() <= 1))
         {
                return str;
         }
         return recursiveSwap(str.substring(1)) + str.charAt(0);
    }
}

```

我可以想到这些**常见的 String 面试问题**将为您的下一次面试提供帮助。 如果您还有其他关于`String`类的问题,请分享。

学习愉快!