byte数组转string_String的相关问题

String的创建方式

可以使用下面两种方式创建String类型的对象:

 String s3 = "java";String s4 = new String("javaWeb");

这两种方式的区别:

s3:系统会在常量池里面创建一个hello的字符串对象。

s4:系统会在常量池里面创建一个javaWeb字符串对象,然后在堆内存里面再创建一个javaWeb字符串对象。

请看下面程序的打印结果:

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

开发中建议使用String s = "java";这种方式创建字符串对象,可以减少堆内存的使用。

注意:比较两个字符串是否一致最好使用equals方法

String类常用构造方法

 String()初始化新创建的 String对象,它代表了一个空字符序列。  String(byte[] bytes)通过使用平台的默认字符集解码指定的字节数组构造了一个新的 String。    String(byte[] bytes, int offset, int length)构建了一种新的 String通过解码指定的字节数组使用平台的默认字符集。 String(byte[] bytes, int offset, int length, String charsetName)构建了一种新的 String通过解码指定的字节数组使用指定的字符集。  String(byte[] bytes, String charsetName)通过使用指定的 charset解码指定的字节数组构造了一个新的 String。  String(char[] value)分配一个新的 String,它代表了目前包含在字符数组参数字符序列。  String(char[] value, int offset, int count)分配一个包含字符与字符数组数组参数的新 String。 

String类常用方法

  • char charAt(int index)

    • 获取index位置的字符

  • boolean contains(CharSequence s)

    • 判断字符串中是否包含某个字符串

  • boolean endsWith(String endStr)

    • 判断是否是以某个字符串结尾

  • boolean equalsIgnoreCase(String anotherString)

    • 忽略大小写比较两个字符串是否相等

  • byte[] getBytes()

    • 转换成byte数组

  • int indexOf(String str);

    • 取得指定字符在字符串的位置

  • int indexOf(String str, int fromIndex)

    • 从指定的下标开始取得指定字符在字符串的位置

  • int lastIndexOf(String str)

    • 从后面开始取得指定字符在字符串最后出现的的位置

  • int lastIndexOf(String str, int fromIndex)

    • 从后面开始指定的下标开始取得指定字符在字符串的位置

  • int length()

    • 获取字符串的长度

  • String replaceAll(String s1,String s2)

    • 替换字符串中的内容

  • String[] split(String s)

    • 根据指定的表达式拆分字符串

  • boolean startsWith(String s)

    • 判断是否是以某个字符串开始

  • String substring(int begin)

    • 根据传入的索引位置截子串

  • String substring(int beginIndex, int endIndex)

    • 根据传入的起始和结束位置截子串

  • char[] toCharArray()

    • 将字符串转换为char数组

  • void toUpperCase()

    • 转换为大写

  • void toLowerCase()

    • 转换为小写

  • String trim()

    • 去除首尾空格

  • String valueOf(Object obj)

    • 将其他类型转换为字符串类型

 public static void main(String[] args) {    String s1 = "javajavajava";    // char charAt(int index);获取index位置的字符    System.out.println(s1.charAt(5));    // boolean contains(CharSequence s);判断字符串中是否包含某个字符串    System.out.println(s1.contains("key"));    // boolean endsWith(String endStr);判断是否是以某个字符串结尾    System.out.println(s1.endsWith("a"));    // boolean equalsIgnoreCase(String anotherString);忽略大小写比较两个字符串是否相等    System.out.println(s1.equalsIgnoreCase("java"));    // byte[] getBytes();转换成byte数组    String s2 = "abc";    byte[] b1 = s2.getBytes();    for(int i=0; i<b1.length; i++){        System.out.print(b1[i] + " ");    }    System.out.println();    // int indexOf(String str);取得指定字符在字符串的位置    System.out.println(s1.indexOf("a"));    // int indexOf(String str, int fromIndex);从指定的下标开始取得指定字符在字符串的位置    String s3 = "javajavaasdxx";    System.out.println(s3.indexOf("a", 2));    // int lastIndexOf(String str);从后面开始取得指定字符在字符串最后出现的的位置    System.out.println(s3.lastIndexOf("a"));    // int lastIndexOf(String str, int fromIndex);从后面开始指定的下标开始取得指定字符在字符串的位置    System.out.println(s3.lastIndexOf("a", 5));    // int length();获取字符串的长度    System.out.println(s3.length());    // String replaceAll(String s1,String s2);替换字符串中的内容    String s4 = "javajavajavakey";    System.out.println(s4.replaceAll("key", "YYY"));    // String[] split(String s);根据指定的表达式拆分字符串    String s5 = "a,b,c,d";    String[] array1 = s5.split(",");    for(int i=0; i<array1.length; i++){        System.out.print(array1[i] + " ");    }    System.out.println();    // boolean startsWith(String s);判断是否是以某个字符串开始    System.out.println(s3.startsWith("ja"));    // String substring(int begin);根据传入的索引位置截子串    System.out.println(s3.substring(5));    // String substring(int beginIndex, int endIndex);根据传入的起始和结束位置截子串    System.out.println(s3.substring(6, 10));    // char[] toCharArray();将字符串转换为char数组    char[] array2 = s5.toCharArray();    for(int i=0; i<array2.length; i++){        System.out.print(array2[i] + " ");    }    System.out.println();    // void toUpperCase();转换为大写    System.out.println(s5.toUpperCase());    // void toLowerCase();转换为小写    System.out.println(s5.toLowerCase());    // String trim();去除首尾空格    String s6 = " java good ok ";    System.out.println(s6.trim());    // String valueOf(Object obj);将其他类型转换为字符串类型    Object o = new Object();    o = null;    System.out.println(String.valueOf(o));//建议使用这种方法转换字符串    //System.out.println(o.toString());//报出空指针错误}

String源码分析

String的不可变性

咱们先来看一下String源码里面的一则注解内容

61fdca83ca494ede4b352aa9d7a4c34d.png

这句话说明咱们java里面的字符串当创建成功后是不可变的。

对于Java初学者, 对于String是不可变对象总是存有疑惑。看下面代码:

 public static void main(String[] args) {    String s = "Hello Java";    System.out.println("s = " + s);    s = "Hello String";    System.out.println("s = " + s);}

其输出结果为:

669eb2aa6eb01c249dfaa6e2183294fd.png

这里首先我们先创建了一个String对象,其值为“Hello Java”,然后有把s的对象重新赋值成“Hello String”,咱们从打印的结果也能发现s对象对应的值确实改变了。那为什么还要说String对象是不可变了。其实在这里咱们存在着一个误区,这个误区就是:在这里s只是String的一个对象引用。s并不是String对象本身(String是引用数据类型)。

其内存结构图是:

34452bcbcdcd49738c2418dee374be18.png

咱们再来看看String的源码

 public final class String    implements java.io.Serializable, Comparable<String>, CharSequence {    /** The value is used for character storage. */    private final char value[];    /** Cache the hash code for the string */    private int hash; // Default to 0}

value[]是用来存储字符串内容的字符数组

hash:用来保存字符串对应的hash值

这两个属性都是被private和final修饰,说明外部是不能直接访问,并且一旦赋值成功以后是不能修改的。

类上被final修饰类不能被其它类所继承

在jdk1.8及其之前String存储使用用字符数组来存储内容,而jdk9之后字符串改为byte字节数组来存储这个是为了避免浪费空间

常量池

字符串常量池设计初衷:咱们使用的每个字符串都是一个String对象,并且开发中将会频繁使用字符串,如果像其他对像那样创建销毁将极大影响程序的性能。虚拟机(JVM)为了提高性能和减少内存开销,在实例化字符串的时候进行了优化,为字符串开辟了一个字符串常量池,类似于缓存区创建字符串常量时,首先判断字符串常量池是否存在该字符串,存在该字符串返回引用实例,不存在,实例化字符串,放入池中。

情景一

 public static void main(String[] args) {    String s1 = "abc";    String s2 = "abc";    System.out.println("s1 == s2 : "+(s1==s2));    System.out.println("s1.equals(s2) : " + (s1.equals(s2)));}

首先在字符串常量池中创建了一个字符串对象“abc”,第二次又想去创建一个字符串“abc”,但是由于字符串“abc”在常量池中存在,所以直接使用不会再去创建新的字符串对象

af1b51dd65363eb6845f544ce8a5b7cb.png

情景二

 public static void main(String[] args) {    String s1 = "Hello Java";    String s2 = "Hello Java";    String s3 = new String("Hello Java");    String s4 = new String("Hello Java");    System.out.println("s3 == s4 : "+(s3==s4));    System.out.println("s3.equals(s4) : "+(s3.equals(s4)));    System.out.println("s1 == s3 : "+(s1==s3));    System.out.println("s1.equals(s3) : "+(s1.equals(s3)));}

其运行结果为:

cc13e20ae37dac5d97b24c42eec32920.png

情景三

字符串使用“+”进行字符串的拼接

 public static void main(String[] args) {    String s1 = "Hello" + "Java";    String s2 = "HelloJava";    System.out.println("s1 = s2 : "+ (s1 == s2));}

414e4c4cede27009c8ed7ceb463bdad4.png

08f8b37009e576f5b6ec8504f71addf2.png

从上面两张图可以看出s1,s2同时指向的是常量池中的“HelloJava”,此时在常量池中的“Hello”和“Java”如果没有被其他对象引用这个时候这两个对象就成为垃圾,可能会被垃圾回收机制(GC)回收内存。

情景四

 public static void main(String[] args) {    String str1 = "Hello";    String str2 = "Java";    String str3 = str1 + str2;    String str4 = "HelloJava";    System.out.println("str3 = str4 : " + (str3 ==str4));}

咱们先来看一下String str3 = str1 + str2;这段代码的运行原理:

运行期JVM首先会在堆中创建一个StringBuilder类,同时用str1指向的拘留字符串对象完成初始化,然后调用append方法完成对str2所指向的拘留字符串的合并, 接着调用StringBuilder的toString()方法在堆中创建一个String对象,最后将刚生成的String对象的堆地址存放在局部变量str4中

d5569a07843f8eda2401e1a4d5d71080.png

be7e11042fd1036a959b9eda1c90d48c.png

情景五

 public static void main(String[] args) {    String str1 = "b";    String str2 = "a" + str1;    String str12 = "ab";    System.out.println("str2 = str12 : "+ (str2 == str12));    final String str3 = "b";    String str4 = "a" + str3;    String str34 = "ab";    System.out.println("str4 = str34 : "+ (str4 == str34));}

JAVA编译器对字符串给拼接的都是基本类型或者常量 是当成常量表达式直接求值来优化的。也就是在编译期间就会被优化  对于通过对象名拼接,会产生新的对象的,存储在堆(heap)中。

在编译期间优化不会产生新对象,在运行期间优化会产生新对象

String相关的+

String中的 + 常用于字符串的连接。看下面一个简单的例子:

 public static void main(String[] args) {    String a = "aa";    String b = "bb";    String c = "xx" + "yy" + a + "zz" + b;    System.out.println(c);}

javap -c

ac8e14449d420477cc0395a459db9d69.png

String的不可变性导致字符串变量使用+号的代价

 public static void main(String[] args) {    String s = "a" + "b" + "c";    String s1  =  "a";    String s2  =  "b";    String s3  =  "c";    String s4  =  s1  +  s2  +  s3;}

变量s的创建等价于 String s = "abc"; 由上面例子可知编译器进行了优化,这里只创建了一个对象。由上面的例子也可以知道s4不能在编译期进行优化,其对象创建相当于

 StringBuilder temp = new StringBuilder();temp.append(s1).append(s2).append(s3);temp.toString();

由上面的分析结果,可就不难推断出String 采用连接运算符(+)效率低下原因分析,形如这样的代码:

 public static void main(String[] args) {    String s = "";    for (int i = 0; i < 10; i++) {        s += "a";    }    System.out.println(s);}

每做一次 + 就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后 append 字符串,如此循环直至结束。如果我们直接采用 StringBuilder 对象进行 append 的话,我们可以节省 N - 1 次创建和销毁对象的时间。所以对于在循环中要进行字符串连接的应用,一般都是用StringBuffer或StringBulider对象来进行append操作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值