String类的课后作业
- 1.题目1
- 2.题目2
- 3.题目3
- 4.选择题1
- 5.选择题2
- 6.选择题3
- 7.编程题1
- 8.编程题2
1.题目1
指出下列程序运行的结果为:
public class Example {
String str = new String("good");
char[] ch = {'a', 'b', 'c'};
public static void main(String args[]) {
Example ex = new Example();
ex.change(ex.str, ex.ch);
System.out.print(ex.str + " and ");
System.out.print(ex.ch);
}
public void change(String str, char ch[]) {
str = "test ok";
ch[0] = 'g';
}
}
解释:
这段Java代码定义了一个Example类,并在其中声明了一个字符串str和一个字符数组ch。main方法中创建了Example类的一个实例,并调用了change方法来尝试修改这两个成员变量。然后,它打印出这两个变量的值。
关键点: 理解Java中对象引用和原始数据类型(如字符数组中的元素)的传递方式。
- 对于String str:在Java中,字符串是不可变的,且String类型的变量传递的是引用的拷贝。在change方法中,str引用被重新指向了一个新的字符串对象"test ok",但这并不影响原始对象str的引用,因为Java是值传递,这里的“值”是引用的拷贝。所以,str的值在change方法之外保持不变。
- 对于char ch[]:字符数组是引用类型,但在change方法中,我们没有改变ch数组的引用,而是修改了数组的内容(即ch[0]的值被改变为’g’)。这种修改是反映在原始数组上的,因为数组的内容是共享的。
因此,输出将会是原始字符串"good"(因为str的引用没有被改变)和修改后的字符数组(因为数组内容被修改了)。所以,输出结果是:
good and gbc
这里,gbc是字符数组ch的新内容,其中第一个元素被change方法修改为’g’,而其余元素保持不变(‘b’和’c’)。
图文解析:
2.题目2
指出下列程序运行的结果为:
public class SystemUtil {
public static boolean isAdmin(String userId) {
return userId.toLowerCase() == "admin";
}
public static void main(String[] args) {
System.out.println(isAdmin("Admin"));
}
}
解释:
- isAdmin 方法接受一个 userId 字符串参数,并将其转换为小写。
- 然后,它尝试通过 == 运算符来比较转换后的小写字符串是否等于 “admin”。
- 在 Java 中,使用 == 运算符比较字符串对象时,实际上是在比较对象的引用,而不是内容。
- 因此,即使两个字符串的内容相同,如果它们不是同一个对象,== 运算符也会返回 false。
- 在 isAdmin 方法中,“admin” 是一个字符串字面量,它在字符串常量池中创建一个对象。而 userId.toLowerCase() 创建了一个新的字符串对象(即使内容相同),这个新对象与字符串常量池中的 “admin” 对象不是同一个对象。
- 因此,userId.toLowerCase() == “admin” 的结果为 false。
- 在 main 方法中,调用 isAdmin(“Admin”) 会传入字符串 “Admin”,然后转换为小写并与 “admin” 进行比较。
由于上述原因,比较结果为 false,所以,输出结果是:
false
3.题目3
指出下列程序运行的结果为:
public class Test {
public static void main(String[] args) {
String s1 = "abc" + "def";
String s2 = new String(s1);
if (s1.equals(s2))
System.out.println(".equals succeeded");
if (s1 == s2)
System.out.println("==succeeded");
}
}
这段代码会输出:
.equals succeeded
解释:
-
String s1=“abc”+“def”; 这一行会创建一个字符串字面量 “abcdef” 并将其引用赋值给 s1。由于字符串拼接在编译时发生,s1 实际上直接引用了拼接后的字符串 “abcdef”。
-
String s2=new String(s1); 这一行通过 new 关键字创建了一个新的 String 对象,其内容复制自 s1。即使内容相同,s2 引用的是堆上新创建的对象,而不是字符串常量池中的对象。
-
if(s1.equals(s2)) 使用 .equals() 方法比较 s1 和 s2 的内容是否相同。由于两个字符串的内容都是 “abcdef”,所以 .equals() 方法返回 true,因此会执行并输出 “.equals succeeded”。
-
if(s1==s2) 使用 == 操作符比较 s1 和 s2 的引用是否相同。由于 s1 引用的是字符串常量池中的对象,而 s2 引用的是堆上的新对象,两者的引用不同,所以 s1 == s2 的结果为 false,因此不会输出 “==succeeded”。
4.选择题1
在 Java 中,直接赋值存放字符串常量的对象属于(B)类对象。
A.Character
B.String
C.StringBuffer
D.Vector
解释:
A:是char类型的包装类型,可以存储字符
B:可以直接赋值字符串常量。String str = “hello”;
C:StringBuffer不能直接赋值字符串常量。
D:Vector底层是一个动态数组,不可以直接赋值字符串常量。
5.选择题2
以下关于 Java 字符串的说法, 错误的是(D)
A.字符串常量是不可变对象, 不能修改字符串的内容
B.使用 == 比较字符串是比较对象的地址, 而不是字符串的具体内容是否相同
C.使用 contains 方法可以判定字符串中是否包含某个子字符串
D.使用 subString 方法可以截取字符串子串, 传入的参数通过前闭后闭区间的形式表示子串的范围
解释:
A:正确。
观察String源码,我们发现这样的代码:
private final char value[];
public String( String original) {
this.value = original.value;
this.hash = original.hash;
}
可以看到 value 是私有的,类外获取不到,所以无法修改。
B:正确。
在Java中,== 操作符用于比较两个对象的引用是否相同,即它们是否指向内存中的同一个位置。对于字符串,如果你使用 == 来比较两个字符串对象,你实际上是在比较这两个字符串对象的内存地址,而不是它们的内容。因此,即使两个字符串的内容完全相同,如果它们是不同的对象实例,== 也会返回 false。要正确比较字符串的内容是否相同,应该使用 equals() 方法。
C:正确 。
contains就是 字符串中是否包含某个子字符串,包含返回true,不包含返回false。
D:错误。
在Java中,substring 方法用于截取字符串的子串,但是传入的参数并不是通过前闭后闭区间的形式来表示子串的范围。实际上,substring 方法有两种常见的重载形式:
- substring(int beginIndex): 返回一个新的字符串,它是此字符串的一个子字符串。子字符串从指定的 beginIndex 处开始,直到此字符串的末尾。
- substring(int beginIndex, int endIndex): 返回一个新字符串,它是此字符串的一个子字符串。子字符串从 beginIndex 处开始,直到索引 endIndex - 1 的字符;因此,子字符串的长度为 endIndex-beginIndex。
所以,substring 方法的参数表示的是子串的起始位置和结束位置(不包括结束位置的字符),这是一种前闭后开区间 [beginIndex, endIndex),而不是前闭后闭区间。
6.选择题3
以下说法错误的是:(D)
A.针对一个 String 对象频繁调用 += 是比较低效的.
B.可以使用 StringBuilder 中的 append 方法更高效完成字符串拼接
C.StringBuilder 是可变对象.
D.StringBuffer 比 StringBuilder 更高效
解释:
A:频繁的对String,使用加号进行拼接会产生大量的临时对象,如上课所说,字符串本身不可变。所以每次都会产生临时对象。
B:append方法,每次返回的仍然是当前对象,不会频繁创建临时对象。
C:如B所说,StringBuilder是可变的。
D:StringBuffer是线程安全的,相比StringBuilder来说,每次为了保证线程安全,加锁和释放锁都会消耗系统资源,从而导致效率变低。
7.编程题1
题目描述:转换成小写字母
给你一个字符串 s ,将该字符串中的大写字母转换成相同的小写字母,返回新的字符串。
示例 1:
输入:s = "Hello"
输出:"hello"
示例 2:
输入:s = "here"
输出:"here"
示例 3:
输入:s = "LOVELY"
输出:"lovely"
- 方法1: 手动实现toLowerCase()
class Solution {
public String toLowerCase(String s) {
/**
public static boolean isLetter(char ch):判断指定字符是否为字母。
public static boolean isUpperCase(char ch):判断指定字符是否为大写字母。
*/
StringBuilder stringBuilder = new StringBuilder();
for(int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if(Character.isLetter(ch)) {//判断是不是字母
if(Character.isUpperCase(ch)) {//判断是不是大写字母
ch = (char)(ch + 32);
}
}
//字符串拼接:
stringBuilder.append(ch);
}
return stringBuilder.toString();
}
}
解释:
这段代码的目的是将一个字符串中的所有大写字母转换为小写字母,同时保留非字母字符和小写字母不变。
- 方法定义: 定义了一个toLowerCase方法,它接受一个字符串s作为参数,并返回一个字符串。
- StringBuilder初始化:为了高效地拼接字符,代码使用了StringBuilder类,并初始化了一个StringBuilder对象。
- 遍历字符串: 通过一个for循环,代码遍历了输入字符串s中的每一个字符。
- 判断与转换:
- 使用Character.isLetter(ch)判断当前字符ch是否为字母。
- 如果是字母,再使用Character.isUpperCase(ch)判断它是否为大写字母。
- 如果是大写字母,就通过加上32(ASCII码中大写与小写字母之间的差值)来转换为小写字母。
- 字符串拼接: 无论字符是否被转换,都使用stringBuilder.append(ch)将其添加到StringBuilder对象中。
- 返回结果: 最后,使用stringBuilder.toString()将StringBuilder对象转换为字符串,并返回这个字符串。
简而言之,这段代码遍历输入字符串,将其中的大写字母转换为小写,然后返回转换后的字符串。
- 方法2: 利用库方法toLowerCase
class Solution {
public String toLowerCase(String s) {
return s.toLowerCase();
}
}
8.编程题2
题目描述:符串中的单词数
统计字符串中的单词个数,这里的单词指的是连续的不是空格的字符。
请注意,你可以假定字符串里不包括任何不可打印的字符。
示例:
输入: "Hello, my name is John"
输出: 5
解释: 这里的单词是指连续的不是空格的字符,所以 "Hello," 算作 1 个单词。
代码展示:
class Solution {
public int countSegments(String s) {
if(s.length() == 0) {
return 0;
}
int count = 0;
//分割字符串,使之变成字符串数组
String[] ss = s.split(" ");
//假如数组中的元素(字符串)是为0长度的,就不进行计数
for(int i = 0; i < ss.length; i++) {
if(ss[i].length() != 0) {
count++;
}
}
return count;
}
}
解释:
这段代码定义了一个countSegments方法,其目的是计算输入字符串s中由空格分隔的非空段(segments)的数量。
- 边界条件检查:首先检查输入字符串s的长度是否为0。如果是,则直接返回0,因为没有内容可以分割。
- 初始化计数器:设置一个计数器count为0,用于记录非空段的数量。
分割字符串:使用split(" ")方法将输入字符串s按照空格分割成一个字符串数组ss。 - 遍历并计数:遍历分割后的字符串数组。如果数组中的某个元素(即某个段)的长度不为0,说明它是一个非空段,此时计数器count加1。
- 返回结果:最后返回计数器count的值,即非空段的数量。
简而言之,这段代码通过空格分割字符串,并计算其中非空段的数量。