常用类和基础api(一)

tips: 本文基于JDK8.0但不仅限于JDK8

目录

前言

一、String

1. 类的声明

2. 内部声明的属性

3.字符串常量的存储位置

4.String的不可变性

5.String实例化的两种方式

二、常用api

1.String

2.StringBuffer和StringBuilder

总结



前言

今天秋秋学习了关于java中String家族的三兄弟的一些api以及底层原理,想通过这篇文章从多角度解析一下关于java中这三者的异同之处,有不明白的小伙伴可以通过这篇文章加强理解,也欢迎各位小伙伴们多多指正.


提示:以下是本篇文章正文内容,下面案例可供参考

一、String

首先我们来谈谈最常用的String类,我将分为以下几点阐述:类的声明,内部声明的属性,字符串常量的存储位置,String不可变性的理解以及实例化的两种方式来介绍

tips:以防有小伙伴不了解常量池,在开始之前我们先介绍一下常量池的概念.

String a = "abc";
String b = new String("abc");
System.out.println(a == b);

------------------------------------
结果:false

 这里就是第一个要理解的地方,为什么a和b的地址不同,a的地址指向哪里,b的地址又指向哪里呢?

首先对象存在堆空间内,这是毋庸置疑的,而a作为字面量一开始就已经存储在了class文件中,之后运行时自然也就到了方法区,这两者的地址自然也就不同了.

下面我们通过几个字符串的拼接更好的理解一下常量池的概念

 @Test
    public void test5(){
        String s1 = "hello";
        String s2 = "world";
        String s3  = "helloworld";
        String s4 = "hello" + "world";
        String s5 = s1 + "world";//通过查看字节码文件,调用了StringBuilder中的toString方法,创建了一个新对象
        String s6 = "hello" + s2;
        String s7 = s1 + s2 ;
        System.out.println(s3 == s4);//true
        System.out.println(s3 == s5);//false
        System.out.println(s3 == s6);//false
        System.out.println(s3 == s7);//false
        System.out.println(s5 == s6);//false
        System.out.println(s5 == s7);//false

        String s8  = s5.intern();
        System.out.println(s8 == s3);//true,返回的是字面量的地址
    }

上述拼接操作我们分为以下几种情况

String的连接操作: 
    //情况1: 常量 + 常量 结果仍然存储在字符串常量池中

    //情况2: 常量 + 变量 或者 变量+变量  会使用StringBuilder创建一个新String对象,返回堆空间地址

    //情况3:调用字符串的intern()方法  返回的是字面量的地址
    

    //特殊情况 :如果在变量前面加上final,就相当于常量加常量, == 和常量池中一样


    //情况4:concat()拼接字符串,都需要去new,所以和常量池比都是false

    //不管参数是常量还是变量,总之,调用完返回一个新new 的对象
    //
   

很多小伙伴对intern()方法不理解,这里我们提前讲解,intern()方法分为两种情况,

声明为public native String intern();

1.常量池中有,就直接返回常量池中该字符串的地址(引用)

2.常量池中没有,就在常量池中复制一份,再返回常量池中该字符串的引用

1. 类的声明

public final class String
    implements java.io.Serializable, Comparable, CharSequence,
               Constable, ConstantDesc {}

    >final:String是不可继承的
    >Serializable :可序列化的接口,凡是实现这个接口的对象可以通过网络或本地流进行数据的传输
    >Comparable:实现这个接口的对象可以比较大小

2. 内部声明的属性
 

    JDK8
    private final char value[];//存储字符串数据的容器

    >final:指明value数组一旦初始化,地址不可变,这也间接体现了String的不可变性

    JDK9开始底层的结构就变了

    private final byte[] value;

    为了节省内存空间,做了优化,拉丁字母一个字节,其他的还是按照两个字节存 

3.字符串常量的存储位置

3.1 字符串常量都存在字符串常量池中,不允许存在两个相同的字符串

3.2 字符串常量池在不同的jdk版本中存放位置不同

     JDK7之前存在方法区
     JDK7之后存在堆空间
    改变位置的原因:方法区是类的加载区,所以gc很少去, (gc:垃圾清理机制)

    为了节省空间,就给到堆空间了,方法区改名元空间,因为后来允许方法区使用物理内存

 

4.String的不可变性

   4.1 当对字符串变量重写赋值时,需要重新指定一个字符串常量进行修改,不能在原有位置进行修改

   4.2 当对现有的字符串进行拼接操作时,需要重新开辟空间,不能在原有位置修改

   4.3 修改完了就在堆里面了,相当于new了一个对象出来了

   4.4 当调用replace方法时也是新创建一个变量在堆空间里

5.String实例化的两种方式

    String s1 = "hello"

    String s1 = new String("hello")

    String()构造器在内存中创建了几个对象?
    两个,new的是同一个char数组,数组放的是hello

    指向的是同一个字符串常量池里面的hello字符串

    创建了两个对象,一个是在堆空间new的对象,另一个是在字符串常量池生成的字面量

二、常用api

1.String

    boolean isEmpty():判断字符串是否为空
    int length():判断字符串的长度
    String concat():字符串的拼接
    boolean equals(Object obj):比较字符串是否相等,区分大小写
    boolean equalsIgnoreCase(Object obj):比较字符串是否相等,不区分大小写
    int CompareTo(String other):比较字符串大小,区分大小写
    int CompareToIgnoreCase(String other):比较字符串大小,不区分大小写
    String toLowerCase():大写转小写
    String toUpperCase():小写转大写
    String trim():去掉字符串前后空白符
    public String intern():结果在常量池共享




  查找相关方法

    boolean contains(xx):是否包含xx

    int indexOf(xx):从前往后查找当前字符串中xx,如果有返回第一次的下标,没有就返
回-1

    int indexOf(String str ,int fromIndex):返回指定子字符串在此字符串中第一次出现的索引,从指定的索引开始

    int lastIndexOf(xx):从后往前找当前字符串最后一次出现的下标,没有返回-1

    int lastIndexOf(String str ,int fromIndex):返回指定子字符串中最后一次出现的位置



    取字符串
    String substring(int beginIndex):返回一个新的字符串,它是此字符串空
beginIndex开始截取的

    String substring(int beginIndex,int endIndex):返回一个新的字符串,左闭右开区间





    char charAt(index):返回index位置的字符
    char[] toCharArray():字符串转字符数组

    static String valueOf(char[] data):返回指定数组中该字符序列的String

      String s = String.valueOf(new char[]{'a','b','c'}); 新new的


    开头和结尾
    boolean startsWith(xx):

    boolean startsWith(xx,int ):从哪个下标开始看

    boolean endsWith(xx):以什么结束


    String replace(char oldChar,char newChar)

    String replace(charSequence target,charSequence replacement):替换此字符串,可以用很多个替换一个

相信大家对这里的大部分api都不是很陌生,大家稍作练习,会用就行,其中trim()方法只是取出字符串前后的空白符,字符串中间的是去出不了的.

String s = "    hello  p     ";

s.trim();


打印出来是
---------------------
hello  p


2.StringBuffer和StringBuilder

提到这两个家伙,有人就要问了,为啥存储一个字符串要这么多种类来描绘呢,他们有什么不同嘛,了解完String类我们再来谈谈关于StringBuffer和StringBuilder与String类的异同点,方便小伙伴们在开发中能更为高效的选择.

 

1.三个类的对比StringBuffer String StringBuilder

    String:底层使用char型数组,其它两个也一样,JDK9之后使用byte型数组

    >String:不可变的字符序列       new一个,字面量就重新在常量池拿

    >StringBuffer:可变的字符序列;   在原位置修改 JDK1.0声明,线程安全的,相对效率低

    >StringBuilder:可变的字符序列; JDK5.0声明的,线程不安全的,相对效率高




    2.StringBuffer/StringBuilder可变性分析(源码分析)

    String s1 = new String();//char[] value = new char[0];

    String s2 = new String("abc");//char[] values = new char[]{'a','b','c'};

    他俩父类都是AbstractStringBuilder ,char数组没有用final修饰

    除了char[] value(存储字符序列),还有一个属性int count(实际存储字符的个数)

    StringBuilder sBuffer = new StringBuilder("abc")//char[] balue = new char[16];

    如果给了初始值,则长度就是16+字符串长度

    如果我不断地添加(append),加多了就得新创建数组,数组变一下,对象还是没有变

    一旦count要超过value.length,就要扩容,默认扩容为原有的2倍加2

    并且将原有value数组的元素复制到新的数组中



    3.源码启示
    >如果开发中需要频繁针对字符串进行增删查改功能,建议使用StringBuffer或者
StringBuilde更好

    >如果开发中不涉及线程安全问题,建议直接使用StringBuilder

    >如果开发中大体确定要操作的字符的个数,建议使用带参数capacity的构造器,避免多次扩容


    4.StringBuffer和StringBuilder的常用api
    增:append(xx)
    删:delete(int start, int end)
      :deleteCharAt(int index)

    改:replace(int start,int end,String str)
      setCharAt(int index,char c)

    查:charAt(int index)

    插:insert(int index, xx)

    长度,反转(反转的就是本身)



    5.对比三者的查询效率
    StringBuilder > StringBuffer > String

 


总结

本文是对String三大类做了较为详细的讲解,StringBuffer和StringBuilder是在考虑线程安不安全的情况下使用的,StringBuffer是有synchronized修饰的,StringBuilder则没有,所以StringBuffer是线程安全的,StringBuilder则不然。但两者底层都是有自动扩容机制的,所以是可变的,String是不可变的,我们可以这样理解:String的修改不是在原地修改而是从常量池中拿了一个新的现成的字符串,或者是重新new了一个新的对象,而其他两个类则是可以在本身底层的数组中进行扩容,从而实现了原地修改,就称为可变.

你可能感兴趣的:(java,intellij-idea)