💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 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. 我能想到的第一个原因是***性能提升***。 开发 Java 语言是为了加快应用程序开发,因为以前的语言并没有那么快。 JVM 设计人员必须足够聪明,才能识别出现实世界中的应用程序将主要由标签,消息,配置,输出等多种形式的字符串组成。 看到这种过度使用,他们想像到不正确使用字符串会带来多大的危险。 因此,他们提出了字符串池的概念(下一部分)。 字符串池不过是一些大多数唯一字符串的集合。 字符串池背后的最基本思想是重新创建字符串。 这样,如果一个特定的字符串在代码中创建了 20 次,则应用最终将只有一个实例。 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.字符串池概念 字符串池是一个特殊的内存区域,与存储这些字符串常量的常规堆内存分开。 这些对象在应用的生命周期中称为字符串变量。 在 Java 中,可以通过多种方式创建`String`。 让我们了解它们: #### 1)字符串赋值 ```java String str = "abc"; ``` 上面的代码使 JVM 验证是否已经有一个字符串`"abc"`(相同的字符序列)。 如果存在这样的字符串,则 JVM 仅将现有对象的引用分配给变量`str`,否则,将创建一个新的对象`"abc"`,并将其引用分配给变量`str`。 #### 2)使用`new`新关键字 ```java String str = new String("abc"); ``` 此版本最终**在内存**中创建两个对象。 字符串池中的一个对象具有`char`序列`"abc"`,第二个对象在堆存储器中由变量`str`引用,并且具有与`"abc"`相同的`char`序列。 正如 java 文档所说的: ***除非需要显式的原始副本,否则不需要使用此构造器,因为字符串是不可变的。*** ## 4.关键字“实习生”的用法 [java 文档](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(); ``` 因此,对于`s`和`t`中的任何两个字符串,当且仅当`s.equals(t)`为`true`时,`s.intern() == t.intern()`才为`true`。 意味着如果`s`和`t`都是不同的字符串对象,并且具有相同的字符序列,则在这两个变量上调用`intern()`将导致两个变量引用的单个字符串池文本。 ## 5.匹配正则表达式 如果您还没有探索的话,它并不是那么秘密,但很有用。 您必须已经看到[`Pattern`](https://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html "java util Pattern")和`Matcher`用于正则表达式匹配的用法。 字符串类提供了自己的快捷方式。 直接使用。 此方法还使用函数定义内的`Pattern.matches()`。 ```java String str = new String("abc"); str.matches("<regex>"); ``` ## 6.使用`equals()`和`==`进行字符串比较 面试中另一个最喜欢的领域。 通常有两种比较对象的方法 * 使用`==`运算符 * 使用`equals()`方法 **`==`运算符比较对象引用**,即内存地址相等性。 因此,如果两个字符串对象引用字符串池中的相同文本或堆中的相同字符串对象,则`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 吗? 是的,自 Java7 起,您可以在`switch`语句中使用[`String`类](https://howtodoinjava.com/java7/string-class-is-supported-in-switch-statement-in-java-7/)。在 Java7 之前,这是不可能的,您必须使用`if-else`语句才能实现类似的行为。 ```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`的所有排列? 排列是对字符的有序列表的元素进行重新排列,以使每个排列相对于其他排列都是唯一的。 例如,以下是字符串“`ABC`”的排列 – `ABC ACB BAC BCA CBA CAB`。 > 长度为`N`的字符串具有`N!`(`N`的阶乘)个排列。 ```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 中拆分字符串? 使用[`String.split()`](https://howtodoinjava.com/java/string/4-ways-to-split-tokenize-strings-in-java/)方法可在给定正则表达式的匹配项附近中断给定字符串。 也称为基于定界符的获取**字符串令牌**。 `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 池中,并且在内存中的可用时间比要求的更长,因为垃圾收集周期是不可预测的。 这使得敏感的密码字符串**容易遭到黑客攻击和数据盗窃**。 使用后可以将`String`留空吗? 不,我们不可以。 我们知道,一旦创建了字符串,我们将无法对其进行操作,例如,您不能更改其内容。 字符串是最终的且不可变的。 但是`char`数组是可变的,使用后它们的内容可以被覆盖。 因此,您的应用应使用`char[]`存储密码文本,并在使用密码后,将数组内容替换为空白。 ```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`类用于以更节省内存的方式执行字符串连接操作。 它在内部维护一个`char`数组并仅操作该数组中的内容。 执行完所有操作后需要获取完整的连接字符串时,它将创建一个具有字符数组内容的新`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`类的问题,请分享。 学习愉快!