5. String str=“i” 与 String str=new String(“i”) 一样吗?
String str = "i"
和 String str = new String("i")
在Java中并不完全相同,尽管它们都用于创建包含单个字符 ‘i’ 的字符串对象。
首先,String str = "i"
使用了字符串字面量语法。这种情况下,Java虚拟机(JVM)会检查字符串常量池(String Constant Pool,SCP)中是否已经有值为 “i” 的字符串对象。如果存在,那么不会创建新的对象,而是将 str
引用指向现有的对象。如果不存在,JVM会在SCP中创建一个新的字符串对象,并将 str
引用指向它。
其次,String str = new String("i")
使用了 new
关键字,这会直接在堆内存(Heap Memory)中创建一个新的字符串对象,无论SCP中是否存在相同的值。然后这个新创建的对象的引用被赋给 str
变量。
以下是两种情况的不同之处:
- 内存分配:
- 字符串字面量存储在SCP中,可以节省内存,因为相同的字符串字面量不会重复创建。
- 使用
new
关键字创建的字符串对象存储在堆上,即使值相同,也会创建新的对象。
- 性能:
- 字符串字面量通常更快,因为SCP中对象的创建和查找通常比堆操作要快。
- 使用
new
的形式可能会稍微慢一些,因为涉及到堆内存的分配。
- 性能(在频繁修改字符串的场景中):
- 字符串字面量创建的字符串是不可变的,如果频繁修改,将产生大量无用的中间对象。
- 使用
new
关键字可能会在堆上创建可变字符串(如果不直接操作SCP中的引用),对于频繁修改的字符串,这可以更高效。
以下是代码示例,展示了两者在SCP中的区别:
String s1 = "i";
String s2 = "i";
String s3 = new String("i");
String s4 = new String("i");System.out.println(s1 == s2); // 输出 true,因为它们引用SCP中的同一个对象
System.out.println(s1 == s3); // 输出 false,因为s3创建了一个新对象在堆上
System.out.println(s3 == s4); // 输出 false,因为s4也创建了一个独立的新对象
总之,尽管 String str = "i"
和 String str = new String("i")
在功能上相似,但它们在内存分配和性能上有所不同。根据使用场景,选择合适的创建方式是很重要的。
6. 如何将字符串反转?
在Java中,字符串反转可以通过几种不同的方式实现。以下是一些常见的方法:
-
使用StringBuilder或StringBuffer的
reverse()
方法:
StringBuilder
和StringBuffer
类提供了reverse()
方法,可以很容易地将字符串反转。public static String reverseWithStringBuilder(String str) {return new StringBuilder(str).reverse().toString(); }
-
使用循环手动反转字符串:
通过从字符串末尾开始遍历,并逐个字符地构建新字符串。public static String reverseWithLoop(String str) {char[] array = str.toCharArray();int left = 0;int right = array.length - 1;while (left < right) {char temp = array[left];array[left] = array[right];array[right] = temp;left++;right--;}return new String(array); }
-
使用递归:
调用自身来反转字符串的一部分。public static String reverseWithRecursion(String str) {if ((null == str) || (str.length() <= 1)) {return str;}return reverseWithRecursion(str.substring(1)) + str.charAt(0); }
-
使用Java 8的
Stream
(虽然这不是最高效的方式):
利用Stream
的collect
方法与joining
收集器。public static String reverseWithStream(String str) {return Arrays.stream(str.split("")).collect(Collectors.collectingAndThen(Collectors.toList(), list -> {Collections.reverse(list);return list;})).stream().collect(Collectors.joining()); }
每种方法都有其适用场景。例如,如果性能是关键考虑因素,通常推荐使用StringBuilder
或手动循环的方式,因为递归和Stream
可能相对较慢,并且会创建更多的临时对象。以下是各种方法的简单性能比较:
StringBuilder
:时间复杂度O(n),空间复杂度O(n)- 循环:时间复杂度O(n),空间复杂度O(1)
- 递归:时间复杂度O(n),空间复杂度O(n)(由于调用栈)
Stream
:时间复杂度O(n),空间复杂度O(n)(取决于实现)
请注意,在实际应用中,应该根据具体情况选择最合适的方法。