Javase.String类的课后作业

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 方法有两种常见的重载形式:

  1. substring(int beginIndex): 返回一个新的字符串,它是此字符串的一个子字符串。子字符串从指定的 beginIndex 处开始,直到此字符串的末尾。
  2. 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();
    }
}

解释:

这段代码的目的是将一个字符串中的所有大写字母转换为小写字母,同时保留非字母字符和小写字母不变。

  1. 方法定义: 定义了一个toLowerCase方法,它接受一个字符串s作为参数,并返回一个字符串。
  2. StringBuilder初始化:为了高效地拼接字符,代码使用了StringBuilder类,并初始化了一个StringBuilder对象。
  3. 遍历字符串: 通过一个for循环,代码遍历了输入字符串s中的每一个字符。
  4. 判断与转换:
  • 使用Character.isLetter(ch)判断当前字符ch是否为字母。
  • 如果是字母,再使用Character.isUpperCase(ch)判断它是否为大写字母。
  • 如果是大写字母,就通过加上32(ASCII码中大写与小写字母之间的差值)来转换为小写字母。
  1. 字符串拼接: 无论字符是否被转换,都使用stringBuilder.append(ch)将其添加到StringBuilder对象中。
  2. 返回结果: 最后,使用stringBuilder.toString()将StringBuilder对象转换为字符串,并返回这个字符串。

简而言之,这段代码遍历输入字符串,将其中的大写字母转换为小写,然后返回转换后的字符串。

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)的数量。

  1. 边界条件检查:首先检查输入字符串s的长度是否为0。如果是,则直接返回0,因为没有内容可以分割。
  2. 初始化计数器:设置一个计数器count为0,用于记录非空段的数量。
    分割字符串:使用split(" ")方法将输入字符串s按照空格分割成一个字符串数组ss。
  3. 遍历并计数:遍历分割后的字符串数组。如果数组中的某个元素(即某个段)的长度不为0,说明它是一个非空段,此时计数器count加1。
  4. 返回结果:最后返回计数器count的值,即非空段的数量。

简而言之,这段代码通过空格分割字符串,并计算其中非空段的数量。

  • 17
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值