“手撕”String类+练习题

目录

一、什么是String类

二、String类的使用

三、String类的字符串操作 

String对象的比较

字符串的查找

字符串的转换

字符串的替换

字符串的拆分

字符串的截取

大小写和去空格方法

四、字符串的不可变性

五、字符串的修改

六、StringBuilder类和StringBuffer类

七、String的习题


一、什么是String类

简单讲:是一个类!创建字符串和使用字符串方法的类。

用' '圈起来的叫字符,比如:'a','b'....里面只能有一个char类型的字符。

用" "圈起来的叫字符串,比如:"abc"..里面可以连着多个字符一起。

二、String类的使用

 String提供了很多使用方法,常见的三种如下:

//直接赋值
String s1="zhangsan";
//String类是类,可以直接new对象
String s2=new String("zhangsan");
//用字符间接创造字符串
char[] arr={'o','w','s','p','q'};
String s3=new String(arr);

注意:

 String是引用类型,所以它里面存的不是字符串,是地址。让我们看看代码:

并且它源码里面是由char类型的数组组成的,还有个hash值,默认是0。 

public static void main(String[] args) {
// s1和s2引用的是不同对象 s1和s3引用的是同一对象
    String s1 = new String("hello");
    String s2 = new String("world");
    String s3 = s1;
    System.out.println(s1.length()); // 获取字符串长度---输出5
    System.out.println(s1.isEmpty()); // 如果字符串长度为0,返回true,否则返回false
}

 为什么s1和s2引用的是不同对象呢?

答:因为s1和s2分别new对象出来,new的时候,会创造不同的空间存放值。

 

其中:在Java中“”引起来的也是String类型对象。 

// 打印"hello"字符串(String对象)的长度
System.out.println("hello".length());

三、String类的字符串操作 

String对象的比较

1. ==比较引用对象时,注意==比较的是地址!!!若是基本类型,就是比数值。

public static void main(String[] args) {
    int a = 10;
    int b = 20;
    int c = 10;
// 对于基本类型变量,==比较两个变量中存储的值是否相同
    System.out.println(a == b); // false
    System.out.println(a == c); // true
// 对于引用类型变量,==比较两个引用变量引用的是否为同一个对象
    String s1 = new String("hello");
    String s2 = new String("hello");
    String s3 = new String("world");
    String s4 = s1;
    System.out.println(s1 == s2); // false
    System.out.println(s2 == s3); // false
    System.out.println(s1 == s4); // true
}

 2. boolean equals(Object anObject) 方法:按照字典序比较

Object中equals默认按照==比较,String类重写了父类Object中equals方法

public static void main(String[] args) {
    String s1 = new String("hello");
    String s2 = new String("hello");
    String s3 = new String("Hello");
// s1、s2、s3引用的是三个不同对象,因此==比较结果全部为false
    System.out.println(s1 == s2); // false
    System.out.println(s1 == s3); // false
// equals比较:String对象中的逐个字符
// 虽然s1与s2引用的不是同一个对象,但是两个对象中放置的内容相同,因此输出true
// s1与s3引用的不是同一个对象,而且两个对象中内容也不同,因此输出false
    System.out.println(s1.equals(s2)); // true
    System.out.println(s1.equals(s3)); // false
}

 ==比的是地址,而String重写后的equals比的是字符串相同

3.int compareTo(String s) 方法: 按照字典序进行比较 

可能的疑问:不是有equals了吗,为什么还需要compareTo方法?

答:equals比的是相不相同,compareTo比的是谁大谁小  

public static void main(String[] args) {
    String s1 = new String("abc");
    String s2 = new String("ac");
    String s3 = new String("abc");
    String s4 = new String("abcdef");
    System.out.println(s1.compareTo(s2)); // 不同输出字符差值-1
    System.out.println(s1.compareTo(s3)); // 相同输出 0
    System.out.println(s1.compareTo(s4)); // 前k个字符完全相同,输出长度差值 -3
}

具体的比较方法:

1. 先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值

2. 如果前k个字符相等(k为两个字符长度最小值),返回值两个字符串长度差值 

4. int compareToIgnoreCase(String str) 方法:与compareTo方式相同,但是忽略大小写比较 

public static void main(String[] args) {
    String s1 = new String("abc");
    String s2 = new String("ac");
    String s3 = new String("ABc");
    String s4 = new String("abcdef");
    System.out.println(s1.compareToIgnoreCase(s2)); // 不同输出字符差值-1
    System.out.println(s1.compareToIgnoreCase(s3)); // 相同输出 0
    System.out.println(s1.compareToIgnoreCase(s4)); // 前k个字符完全相同,输出长度差值 -3
}

字符串的查找

字符串查找也是字符串中非常常见的操作,String类提供的常用查找的方法:

方法功能
char charAt(int index)返回index位置上字符,如果index为负数或者越界,抛出 IndexOutOfBoundsException异常
int indexOf(int ch)返回ch第一次出现的位置,没有返回-1
int indexOf(int ch, int fromIndex)从fromIndex位置开始找ch第一次出现的位置,没有返回-1
int indexOf(String str)返回str第一次出现的位置,没有返回-1
int indexOf(String str, int fromIndex)从fromIndex位置开始找str第一次出现的位置,没有返回-1
int lastIndexOf(int ch)从后往前找,返回ch第一次出现的位置,没有返回-1
int lastIndexOf(int ch, int fromIndex)从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返 回-1
int lastIndexOf(String str)从后往前找,返回str第一次出现的位置,没有返回-1
int lastIndexOf(String str, int fromIndex)从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返回-1
public static void main(String[] args) {
    String s = "aaabbbcccaaabbbccc";
    System.out.println(s.charAt(3)); // 'b'
    System.out.println(s.indexOf('c')); // 6
    System.out.println(s.indexOf('c', 10)); // 15
    System.out.println(s.indexOf("bbb")); // 3
    System.out.println(s.indexOf("bbb", 10)); // 12
    System.out.println(s.lastIndexOf('c')); // 17
    System.out.println(s.lastIndexOf('c', 10)); // 8
    System.out.println(s.lastIndexOf("bbb")); // 12
    System.out.println(s.lastIndexOf("bbb", 10)); // 3
}

字符串的转换

1.数值和字符串互转 

public static void main(String[] args) {
// 数字转字符串
    String s1 = String.valueOf(1234);
    String s2 = String.valueOf(12.34);
    String s3 = String.valueOf(true);
    String s4 = String.valueOf(new Student("Hanmeimei", 18));
    System.out.println(s1);
    System.out.println(s2);
    System.out.println(s3);
    System.out.println(s4);
    System.out.println("=================================");
// 字符串转数字
// 注意:Integer、Double等是Java中的包装类型,在我之前包装类博客讲过
    int data1 = Integer.parseInt("1234");
    double data2 = Double.parseDouble("12.34");
    System.out.println(data1);
    System.out.println(data2);
}

 2.大小写转化

public static void main(String[] args) {
    String s1 = "hello";
    String s2 = "HELLO";
// 小写转大写
    System.out.println(s1.toUpperCase());
// 大写转小写
    System.out.println(s2.toLowerCase());
}

 3.字符串转数组

public static void main(String[] args) {
    String s = "hello";
// 字符串转数组
    char[] ch = s.toCharArray();
for (int i = 0; i < ch.length; i++) {
    System.out.print(ch[i]);
}
    System.out.println();
// 数组转字符串
    String s2 = new String(ch);
    System.out.println(s2);
}

 4.格式化

public static void main(String[] args) {
    String s = String.format("%d-%d-%d", 2019, 9,14);
    System.out.println(s);
}

字符串的替换

方法功能
String replaceAll(String regex, String replacement)替换所有的指定内容
String replaceFirst(String regex, String replacement)替换收个内容
    String str = "helloworld" ;
    System.out.println(str.replaceAll("l", "_"));
    System.out.println(str.replaceFirst("l", "_"));

注意事项: 由于字符串是不可变对象,替换不修改当前字符串, 而是产生一个新的字符串。下面会解释。

字符串的拆分

方法功能
String[] split(String regex)将字符串全部拆分
String[] split(String regex, int limit)将字符串以指定的格式,拆分为limit组
String str = "hello world hello bit" ;
String[] result1 = str.split(" ") ; // 按照空格拆分,全拆分
String[] result2 = str.split(" ",2) ; // 按照空格拆分,拆两个

for(String s1: result1) {
System.out.println(s1);
}
for(String s2: result2) {
System.out.println(s2);
}
//执行结果
hello
world
hello
bit
hello
world hello bit

 注意:要用数组来接收。

有些特殊字符作为分割符可能无法正确切分, 需要加上转义。

String str = "192.168.1.1" ;
String[] result = str.split("\\.") ;
for(String s: result) {
System.out.println(s);
}

注意事项:

1. 字符"|","*","+"都得加上转义字符,前面加上 "\\" 。

2. 而如果是 "\" ,那么就得写成 "\\\\" 。

3. 如果一个字符串中有多个分隔符,可以用"|"作为连字符。

字符串的截取

方法功能
String substring(int beginIndex)从指定索引截取到结尾
String substring(int beginIndex, int endIndex)截取部分内容
String str = "helloworld" ;
System.out.println(str.substring(5));
System.out.println(str.substring(0, 5));

注意事项:

1. 索引从0开始

2. 注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标 

大小写和去空格方法

方法功能
String trim()去掉字符串中的左右空格,保留中间空格
String toUpperCase()字符串转大写
String toLowerCase()字符串转小写
String str = " hello world " ;
System.out.println("["+str+"]");
System.out.println("["+str.trim()+"]");

String str = " hello%$$%@#$%world 哈哈哈 " ;
System.out.println(str.toUpperCase());
System.out.println(str.toLowerCase());

小提示:trim 会去掉字符串开头和结尾的空白字符(空格, 换行, 制表符等),大小写只转换字母

四、字符串的不可变性

 简单来说:就是字符串这玩意儿的值不能改。让我们通过源码:

提出误区:

我们可以看出value和String都是由final修饰的,但这并不是它不可以被修改的理由!

final修饰是(String类和value指向)不能被继承的意思!!!

比如:下面代码array数组也是final修饰,只是它的地址(指向),它里面的数字却可以修改,也就是说final并不是字符串不可变性的原因!

public static void main(String[] args) {
    final int array[] = {1,2,3,4,5};
    array[0] = 100;
    System.out.println(Arrays.toString(array));
// array = new int[]{4,5,6}; // 编译报错:Error:(19, 9) java: 无法为最终变量array分配值
}

正确理解:

因为就是说我们要是能拿到array的地址(指向)就可以改变这个不可变性,

但是!!!它是由private修饰的,它就是不给你拿到这个值!

为什么 String 要设计成不可变的?(不可变对象的好处是什么?)

1. 方便实现字符串对象池。 如果 String 可变,那么对象池就需要考虑写时拷贝的问题了。

2. 不可变对象是线程安全的。

3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中。

五、字符串的修改

注意:尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低下。 

public static void main(String[] args) {
    String s = "hello";
    s += " world";
    System.out.println(s); // 输出:hello world
//上面的原理(或者说背后实现)的代码是下面
    String s2="hello";
    StringBuilder stringBuilder=new StringBuilder();
    stringBuilder.append(s2);
    stringBuilder.append("world");
    s2=stringBuilder.toString();
    System.out.println(s2);
}

但是这种方式不推荐使用,因为其效率非常低,中间创建了好多临时对象。

public static void main(String[] args) {
    long start = System.currentTimeMillis();
    String s = "";
for(int i = 0; i < 10000; ++i){
    s += i;
}
    long end = System.currentTimeMillis();
    System.out.println(end - start);
    start = System.currentTimeMillis();
    StringBuffer sbf = new StringBuffer("");
for(int i = 0; i < 10000; ++i){
    sbf.append(i);
}
    end = System.currentTimeMillis();
    System.out.println(end - start);
    start = System.currentTimeMillis();
    StringBuilder sbd = new StringBuilder();
for(int i = 0; i < 10000; ++i){
    sbd.append(i);
}
    end = System.currentTimeMillis();
    System.out.println(end - start);
}
//执行结果
144
1
2

可以看待在对String类进行修改时,效率是非常慢的,因此:尽量避免对String的直接需要,如果要修改建议尽量 使用StringBuffer或者StringBuilder。 

StringBuffer s1 = new StringBuffer("");
for(int i = 0; i < 10000; ++i){
    s1.append(i);
}
StringBuilder s2 = new StringBuilder("");
for(int i = 0; i < 10000; ++i){
    s2.append(i);
}

它两不像String一样,每次都要重新new一个对象,也可以说是一劳永逸。

六、StringBuilder类和StringBuffer类

方法功能
StringBuff append(String str)在尾部追加,相当于String的+=,可以追加:boolean、char、char[]、 double、float、int、long、Object、String、StringBuff的变量
char charAt(int index)获取index位置的字符
int length()获取字符串的长度
int capacity()获取底层保存字符串空间总的大小
void ensureCapacity(int mininmumCapacity)扩容
void setCharAt(int index, char ch)将index位置的字符设置为ch
int indexOf(String str)返回str第一次出现的位置
int indexOf(String str, int fromIndex)从fromIndex位置开始查找str第一次出现的位置
int lastIndexOf(String str)返回最后一次出现str的位置
int lastIndexOf(String str, int fromIndex)从fromIndex位置开始找str最后一次出现的位置
StringBuff insert(int offset, String str)在offset位置插入:八种基类类型 & String类型 & Object类型数据
StringBuffer deleteCharAt(int index)删除index位置字符
StringBuffer delete(int start, int end)删除[start, end)区间内的字符
StringBuffer replace(int start, int end, String str)将[start, end)位置的字符替换为str
String substring(int start)从start开始一直到末尾的字符以String的方式返回
String substring(int start,int end)将[start, end)范围内的字符以String的方式返回
StringBuffer reverse()反转字符串
String toString()将所有字符按照String的方式返回
public static void main(String[] args) {
    StringBuilder sb1 = new StringBuilder("hello");
    StringBuilder sb2 = sb1;
// 追加:即尾插-->字符、字符串、整形数字
    sb1.append(' '); // hello
    sb1.append("world"); // hello world
    sb1.append(123); // hello world123
    System.out.println(sb1); // hello world123
    System.out.println(sb1 == sb2); // true
    System.out.println(sb1.charAt(0)); // 获取0号位上的字符 h
    System.out.println(sb1.length()); // 获取字符串的有效长度14
    System.out.println(sb1.capacity()); // 获取底层数组的总大小
    sb1.setCharAt(0, 'H'); // 设置任意位置的字符 Hello world123
    sb1.insert(0, "Hello world!!!"); // Hello world!!!Hello world123
    System.out.println(sb1);
    System.out.println(sb1.indexOf("Hello")); // 获取Hello第一次出现的位置
    System.out.println(sb1.lastIndexOf("hello")); // 获取hello最后一次出现的位置
    sb1.deleteCharAt(0); // 删除首字符
    sb1.delete(0,5); // 删除[0, 5)范围内的字符
    String str = sb1.substring(0, 5); // 截取[0, 5)区间中的字符以String的方式返回
    System.out.println(str);
    sb1.reverse(); // 字符串逆转
    str = sb1.toString(); // 将StringBuffer以String的方式返回
    System.out.println(str);
}

 从上述例子可以看出:String和StringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可以修改。频繁修改字符串的情况考虑使用StringBuilder。

七、String的习题

第一题:

String str = new String("ab"); // 会创建多少个对象
String str = new String("a") + new String("b"); // 会创建多少个对象

第一个会创建2个:

一个是new的对象,一个是“ab”在常量池的对象

第二个会创建6个:

new出来2个对象,常量池“a”和“b”两个,底层调用StringBuilder又一个对象,StringBuilder转字符串toString调用又一个对象,一共六个。

 第二题:

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';
    }
}

分析:

ex有两个变量ch和str。ex指向ch和str(ch和str也是引用对象)存的的地址,指向good和abc。

这时候调用change方法,把str和ch传过去,但请注意:!!它会再开辟两个空间存change的str和ch(相当于String 形参str=实参str,这两个str是不同的)。

由于字符串的不可变性,修改的时候会重新申请一段内存存“test ok”,但是数组里面的值却可以修改(ch地址不能改,但值可以)。

所以change里面的str就改为test ok(是新的空间和内存),但是原本在main方法里面的实参str却没有修改,所以还是good,但是!数组里面却修改成gbc。

答案是good and gbc

第三题

求字符串中第一个唯一的字符

解释题目:

给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。

解题思路:

用一个数组有26个空间,代表每一个a,b,c等每一个字符,

由于数组是从0索引,我们可以把字符串的每个字符拿出来减去字符a,比如‘a’-‘a'==0,刚刚好从0索引,同理其他字符也如此。

遍历字符串,遇到字符就++,再遍历一遍字符串,第一个为1的字符就是第一个唯一的字符。

class Solution {
    public int firstUniqChar(String s) {
        int[] count=new int[26];
        for(int i=0;i<s.length();i++){
           char ch=s.charAt(i);
            count[ch-'a']++;
        }for(int i=0;i<s.length();i++){
            char ch=s.charAt(i);
            if(count[ch-'a']==1){
                return i;
            }
        }
         return -1;
    }
}

注意点:

1.数组要用int类型,因为里面存的是一个字符的次数,是数字整型

2.对于字符串的遍历要用length()这个方法。

3.从字符串取字符用charAt()这个方法

4.要遍历2次,一次是为了计数,第二次才是为了找到第一个唯一的数

4.第四题

字符串最后一个单词的长度

题目解释:

计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于5000。(注:字符串末尾不以空格为结尾) 

输入:

hello nowcoder

输出:

8

说明:

最后一个单词为nowcoder,长度为8   
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s=sc.nextLine();
        String[] str=s.split(" ");
        int count=0;
        for(int i=0;i<str[str.length-1].length();i++){
        count++;
        }
        System.out.print(count);
        }
    }

解题思路:

先输入一个值,用s接收

用split()方法分割,取最后一个

把这个放到for循环中遍历,看看有多少个字符

第五题 

验证是否回文

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 

输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。

废话不多说,代码如下: 

class Solution {
    public boolean isPalindrome(String s) {
        s = s.toLowerCase();//转换为小写
        int left = 0;
        int right = s.length()-1;
        while (left < right) {
            while (left < right && !isCharacterNum(s.charAt(left))) {
                left++;
            }
            while (left < right && !isCharacterNum(s.charAt(right))) {
                right--;
            }
            //left下标 是合法的字符
            //right下标 是合法的字符
            if(s.charAt(left) == s.charAt(right)) {
                left++;
                right--;
            }else {
                return false;
            }
        }
        return true;
    }
    //判断是不是 数字 字符 和  字母字符
    private boolean isCharacterNum(char ch) {
        // if(ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9') {
        //     return true;
        // }
        // return false;

        if(Character.isDigit(ch) || Character.isLetter(ch)) {
            return true;
        }

        return false;
    }  
}

  • 38
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值