栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

一篇让你认识String类

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

一篇让你认识String类

认识String类
  • 一、常用方法
    • 1.字符串构造
    • 2.String对象的比较
    • 3.字符串查找
    • 4.转化
    • 5.字符串常量池
      • 5.1 直接赋值和new构造String
      • 5.2 字符串常量池
      • 5.3 String对象创建
    • 6. 字符串的不可变性
    • 7.字符串修改
  • 二、StringBuffer和StringBuilder
    • 1.StringBuffer的介绍
    • 2.尽量避免String的直接修改


一、常用方法 1.字符串构造

String类提供的构造方式非常多,但常用的就以下几种:

public static void main(String[] args) {
        //使用常量串构造
        String s1="hello bit";
        System.out.println(s1);

        //直接newString对象
        String s2=new String("hello bit");
        System.out.println(s2);

        //使用字符数组进行构造
        char[] array={'h','e','l','l','o',' ','b','i','t'};
        String s3=new String(array);
        System.out.println(s3);
    }

如果需要用到其他方法,可以参考Java在线文档。
注意:

  1. String是引用类型,内部并不存储字符串本,字符串具体的内容存储在对象中,而字符串引用类型变量存储的是对象的地址。
public static void main(String[] args) {
        String s1=new String("hello");
        String s2=new String("world");
        String s3=s1;
    }

  1. 在Java中""引起来的也是String类型对象。
//打印"hello"字符串(String对象)的长度
System.out.println("hello".length());
2.String对象的比较

字符串的比较是常见操作之一。比如:字符串排序。Java中总共提供了4种方式:

  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
    }
  1. boolean equals(Object anObject)方法:按照字典序比较

字典序: 字符大小的顺序
String类重写了父类Object中equals方法,Object中equals默认按照==比较,String重写equals方法后,按照如下规则进行比较。比如:s1.equals(s2)

  • 先检测s1和s2是否为同一个对象比较,是返回true,继续下一步;
  • s2是否为String类型的对象,不是返回false,是继续下一步;
  • s1和s2两个字符的长度是否相等,不是返回false,是继续下一步;
  • 循环检测s1和s2引用对象中的每个字符是否相同,不是返回false,是返回true。
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(s2==s3);//false

        System.out.println(s1.equals(s2));//true
        System.out.println(s2.equals(s3));//true
    }

问题:下面哪种写法比较好呢?

 public static void main(String[] args) {
        String str=null;
        // 方式一
        System.out.println(str.equals("Hello"));//会抛出空指针异常
//运行结果
Exception in thread "main" java.lang.NullPointerException
	at day1010.test2.Test.main(Test.java:18)
 public static void main(String[] args) {
        // 方式二
        System.out.println("Hello".equals(str));
    }
}
//运行结果
false

推荐使用方式二,如果str是null,那么方式一就会抛出一个空指针异常。

  1. int compareTo(String s)方法:按照字典序进行比较
    与equals不同的是,equals返回的是boolean类型,而compareTo返回的是int类型。具体比较方式:
  • 先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值。
  • 如果前k个字符相等(k为两个字符长度最小值),返回两个字符串长度差值
public static void main(String[] args) {
        String s1=new String("abc");
        String s2=new String("de");
        String s3=new String("abc");
        String s4=new String("abcdef");
        System.out.println(s1.compareTo(s2));//输出第一个不等的字符差-3
        System.out.println(s1.compareTo(s3));//相同输出0
        System.out.println(s1.compareTo(s4));//前3个字符相同,输出长度差值-3
    }
  1. int compareToIgnoreCase(String str)方法:与compareTo方式相同,但是忽略大小写比较
public static void main(String[] args) {
        String s1=new String("abc");
        String s2=new String("de");
        String s3=new String("ABc");
        String s4=new String("abcdef");
        System.out.println(s1.compareToIgnoreCase(s2));//输出第一个不等的字符差-3
        System.out.println(s1.compareToIgnoreCase(s3));//相同输出0
        System.out.println(s1.compareToIgnoreCase(s4));//前3个字符相同,输出长度差值-3
    }
3.字符串查找

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

方法含义
char charAt(int index)返回index位置上的字符,如果index为负数或者越界,抛出IndexOutOfBoundsExpection异常
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));
        System.out.println(s.indexOf('c'));
        System.out.println(s.indexOf('c',10));
        System.out.println(s.indexOf("bbb"));
        System.out.println(s.indexOf("bbb",10));
        System.out.println(s.lastIndexOf('c'));
        System.out.println(s.lastIndexOf('c',10));
        System.out.println(s.lastIndexOf("bbb"));
        System.out.println(s.lastIndexOf("bbb",10));
    }
//运行结果
b
6
15
3
12
17
8
12
3
4.转化
  1. 数值和字符串转化
public static void main(String[] args) {
        //数字转字符串
        String s1=String.valueOf(1234);
        String s2=String.valueOf(12.34);
        String s3=String.valueOf(true);
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
        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);
    }
  1. 大小写转换
 public static void main(String[] args) {
        String s1="hello";
        String s2="HELLO";
        //小写转大写
        System.out.println(s1.toUpperCase());
        //大写转小写
        System.out.println(s2.toLowerCase());
    }
//运行结果
HELLO
hello    
  1. 字符串数组
public static void main(String[] args) {
        String s="hello";
        //字符串转数组
        char[] ch=s.toCharArray();
        for(int i=0;i 
5.字符串常量池 

先简单说一下几个常量池------->
字节码文件中的常量池:类似一个数组。
运行时常量池: 字节码文件加载到虚拟机时,字节码文件中的常量池也被加载进来了。(注意:每个类都有一份)
字符串常量池:是全局共享的。

5.1 直接赋值和new构造String

下面两种创建String对象的方式相同吗?

public static void main(String[] args) {
        String s1="hello";
        String s2="hello";
        String s3=new String("hello");
        String s4=new String("hello");
        System.out.println(s1==s2);//true
        System.out.println(s1==s3);//false
        System.out.println(s3==s4);//false
    }

上面的代码中,为什么s1和s2引用的是同一个对象,而s3和s4不是呢?
解答: 在Java程序中,类似于:1,2,3,3.14,"hello"等字面类型的常量频繁使用,为了使程序的运行速度更快,更节省内存,Java为8种基本数据类型和String类都提供了常量池
“池"是编程中一种常见的重要的提升效率的方式,之后还会遇到各种"池”。
除了字符串常量池外,Java还有:
Class常量池:每个.Java源文件编译后生成.Class文件,.Class文件中会保存当前类中的字面常量以及符号信息。
运行时常量池:在.Class文件被加载时,.Class文件中的常量池被加载到内存中称为运行时常量池,运行时常量池每个类只保存一份

5.2 字符串常量池

字符串常量池在JVM中是StringTable类,实际是一个固定大小的HashTable(一种高效用来进行查找的数据结构),不同JDK版本下字符串常量池的位置以及默认大小是不同的:

JDK版本字符串常量池位置大小设置
Java6方法区固定大小:1009
Java7可设置,没有大小限制,默认大小:60013
Java8可设置,有范围限制,最小是1009

注意:
1.字符串常量池在JVM中字符串常量池只有一份,是全局共享的。
2.刚开始字符串常量池是空的,随着程序不断运行,字符串常量池中元素会越来越多。
3.字符串常量池中的内容:一部分来自运行时常量池,一部分来自程序动态添加。

5.3 String对象创建

由于不同JDK版本对字符串常量池的处理方式不同,本文在Java8上分析。

  1. 直接使用字符串常量进行赋值
public static void main(String[] args) {
        String s1="hello";
        String s2="hello";
        System.out.println(s1==s2);
    }

接下来,我们通过字节码文件来了解s1和s2的构造过程。

在字节码文件加载时,先将.Class文件中的常量池加载到内存中称为运行时常量池,此时"hello"对象已经创建好了。

  1. 通过new创建String类对象


    总结:只要是new的对象,都是唯一的。
    通过上面的例子可以看出:使用常量串创建String类型对象的效率更高,而且节省空间
  2. intern方法
    intern是一个native方法(native方法:底层使用C++实现,看不到其实现的源代码),该方法的作用是手动创建的String对象添加到常量池中。
 public static void main(String[] args) {
        char[] ch=new char[]{'a','b','c'};
        String s1=new String(ch);//s1对象并不在常量池中
        //s1.intern(); //调用之后,会将s1对象的引用放入常量池中
        String s2="abc";//“abc"在常量池中存在了,s2创建时直接用常量池中"abc"的引用
        System.out.println(s1==s2);
    }
//输出false
//调用s1.intern()之后,就会输出true

问:String类中两种对象实例化的区别?
解答:
1.直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
2.构造方法:会开辟两块堆内存空间,不会自动保存在对象池中,可以使用intern()方法手动添加入池。

6. 字符串的不可变性

字符串是一种不可变对象。它的内容不可改变。

public static void main(String[] args) {
        String str="hello";
        str =str+" world";
        str+="!!!";
        System.out.println(str);
    }
//输出结果
hello world!!!

看到这,大家可能会疑惑,不是说内容不可改变吗?

+=这样的操作,表面上好像是修改了字符串,其实不是。+=之后str打印的结果确实变了,但并不是String对象本身发生改变,而是str引用到了其他的对象,内存变化如下:

字符串不可修改,是因为:

  1. String类在设计时就是不可改变的,String类实现描述中已经说明了。
  2. 所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象。

为什么String要不可变?

  1. 方便实现字符串对象池。如果String可变,那么对象池就需要考虑何时深拷贝字符串的问题了。
  2. 不可变对象是线程安全的。
  3. 不可变对象更方便缓存hash code,作为key时可以更高效的保存到HashMap中。
    那么如果想要修改字符串中的内容,该如何操作?
7.字符串修改

现有字符串str="Hello",想改成str="hello",该怎么做?
1.常见办法:借助原字符串,创建新的字符串

public static void main(String[] args) {
        String str="Hello";
        str="h"+str.substring(1);
        System.out.println(str);
    }
//运行结果
hello   

但是这种方法不推荐使用,因为其效率非常低,中间创建了很多临时对象。
2. 借助StringBuffer和StringBuilder。

二、StringBuffer和StringBuilder 1.StringBuffer的介绍

由于String和不可变性,为了方便字符串的修改,Java中又提供了StringBuffer和StringBuilder类。这两个类的大部分功能是相同的,这里介绍一些StringBuffer常用的一些方法。

方法说明
StringBuffer 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) {
        StringBuffer s1=new StringBuffer("hello");
        StringBuffer s2=s1;
        //追加:即尾插---->字符、字符串、整型数字
        s1.append(' ');
        s1.append("world");
        s1.append(123);
        System.out.println(s1);//hello world123
        System.out.println(s1==s2);//true
        System.out.println(s2);//hello world123
        System.out.println(s1.charAt(0));//获取0号位上的字符 h
        System.out.println(s1.length());//获取字符串的有效长度 14
        System.out.println(s1.capacity());//获取底层数组的总大小 21
        s1.setCharAt(0,'H');//设置任意位置的字符
        s1.insert(0,"Hello world!!!");//在0的位置插入Hello world!!!
        System.out.println(s1);//Hello world!!!Hello world123
        System.out.println(s1.indexOf("Hello"));//获取Hello第一次出现的位置 0
        System.out.println(s1.lastIndexOf("Hello"));//获取Hello最后一次出现的位置 14
        System.out.println(s1.deleteCharAt(0));//删除首字符 ello world!!!Hello world123
        s1.delete(0,5);//删除[0,5)范围内的字符 
        System.out.println(s1);//world!!!Hello world123
        String str=s1.substring(0,5);//截取[0,5)区间中的字符以String的方式返回
        System.out.println(str);//world
        s1.reverse();//字符串逆转 
        str=s1.toString();//将StringBuffer以String的方式返回
        System.out.println(str);//321dlrow olleH!!!dlrow
    }

从上面的代码中可以看出:String和StringBuffer最大的区别在于String的内容无法修改,而StringBuffer的内容可以修改。频繁修改字符串的情况考虑使用StringBuffer。
为了更好地理解String和StringBuffer,我们来看一下两个类的继承结构:

String类StringBuffer类
public final class String implements java.io.Serializable, Comparable, CharSequencepublic final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence

可以发现两个类都是"CharSequence"接口的子类。这个接口描述的是一系列的字符集。所以字符串是字符集的子类,如果后面见到CharSequence,最简单的联想就是字符串。
注意:String和StringBuffer类不能直接转换,如果想互相转换,可以采用如下原则:

  • String变为StringBuffer:利用StringBuffer的构造方法或append()方法。
  • StringBuffer变为String:调用toString()方法。
2.尽量避免String的直接修改
public static void main(String[] args) {
        long start=System.currentTimeMillis();
        String s="";
        for(int i=0;i<100000;++i){
            s+=i;
        }
        long end=System.currentTimeMillis();
        System.out.println(end-start);
        start=System.currentTimeMillis();
        StringBuffer sbf=new StringBuffer("");
        for(int i=0;i<100000;++i){
            sbf.append(i);
        }
        end=System.currentTimeMillis();
        System.out.println(end-start);

        start=System.currentTimeMillis();
        StringBuilder sbd=new StringBuilder();
        for(int i=0;i<100000;++i){
            sbd.append(i);
        }
        end=System.currentTimeMillis();
        System.out.println(end-start);
    }
//运行结果
59409
6
5

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/322094.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号