双非本科准备秋招(8.3)——重学String

结构图

Serializable接口:实现java.io.Serializable 接口的类是可序列化的。

Comparable接口:重写compareTo方法,进行比较。

CharSequence接口:JDK1.4开始,为了统一字符串操作的标准提供的接口。

构造器

String提供了很多构造器:

比如随机挑选一个:

public String(char value[], int offset, int count) {
    this(value, offset, count, rangeCheck(value, offset, count));
}

能把一个char数组转换成String,offset为起始位置,count为长度。

char[] a = {'a', 'a', 'a', 'a', 'a'};
        String s = new String(a, 1, 4);
        System.out.println(s);//aaaa

常用方法

String重写了equals方法,比较的是内容,默认的Object的equals比较的是地址

演示:

String s = "0123456789a";
        String t = "0123456789A";
//      equalslgnoreCase忽略大小写的判断内容是否相等
        boolean b = s.equalsIgnoreCase(t);
        System.out.println("equalslgnoreCase:" + b);

//        length 获取字符的个数,字符串的长度
        System.out.println("length;" + s.length());

//        charAt 根据下标返回字符
        System.out.println("charAt:" + s.charAt(0));

//        contains是否包含
        boolean contains = s.contains("a");
        System.out.println("contains:" + contains);

//        indexOf 获取字符在字符串对象中第一次出现的索引,索引从0开始,如果找不到,返回-1
        String k = "ababa";
        System.out.println("indexOf:" + k.indexOf("a"));

//        lastIndexOf 获取字符子啊字符串中最后一次出现的索引,索引从O开始,如果找不到,返回-1
        System.out.println("lastIndexOf:" + k.lastIndexOf("a"));

//        substring截取指定范围的子串,经典左闭右开
        System.out.println("substring:" + s.substring(0, 6));

//        toUpperCase  toLowerCase   concat  转大写、转小写、字符串拼接
        String upperCase = s.toUpperCase();
        System.out.println("toUpperCase:" + upperCase);
        String lowerCase = upperCase.toLowerCase();
        System.out.println("toLowerCase:" + lowerCase);
        String concat = s.concat(t);
        System.out.println("concat: " + concat);

//        replace替换字符串中的字符
        String replace = s.replace("a", "sm");
        System.out.println("replace:" + replace);

//        split分割字符串  \转义符
        String str = "a\\b\\c\\d";
        String[] split = str.split("\\\\");//第一个\是转义字符,意思是第二个\是\。所以这里表示以\\分割
        System.out.println("split:" + split[0] + " " + split[1] + "...");

//        compareTo 比较两个字符串的大小
        int i = s.compareTo(t);//按顺序每个字符进行比较,返回s[i]-t[i]的值,这里返回'a'-'A'
        System.out.println("compareTo:" + i);

//        toCharArray 转换成字符数组,做题挺有用的,转成字符数组可以用增强for循环。
        char[] charArray = s.toCharArray();
        System.out.println("toCharArray:" + charArray[0] + " " + charArray[1] + "...");

//        format 格式字符串,%s字符串%c字符%d整型%.2f 浮点型案例
//        梦回c语言printf
        String name = "sm";
        char gender = '男';
        double score = 4.23;
        String sss = String.format("你好,%s,性别%c,绩点%.2f", name, gender, score);
        System.out.println("format:" + sss);

控制台输出:

equalslgnoreCase:true
length;11
charAt:0
contains:true
indexOf:0
lastIndexOf:4
substring:012345
toUpperCase:0123456789A
toLowerCase:0123456789a
concat: 0123456789a0123456789A
replace:0123456789sm
split:a b...
compareTo:32
toCharArray:0 1...
format:你好,sm,性别男,绩点4.23

String是不可变的&重写equals方法

直接看看String的源码。

1、final修饰的类不能被继承,所以String不会被子类破坏String的不可变性(immutable)。

2、value数组被final修饰(指向的地址不可修改)并且设为私有,而且没有对外界提供修改value的方法

在java9之后,value数组使用byte类型来实现,之前是char类型。byte是1个字节,char是2个字节,可以节省一半空间。

StringBuilder与StringBuffer与StringJoiner

StringBuilder、StringBuffer继承AbstractStringBuilder类,不过这个类中的value没有被final和private修饰

StringBuider最重要的方法就是append,支持链式编程,用来做字符串拼接,StringBuffer操作与其一致。

  StringBuffer sb = new StringBuffer();
  sb.append("[").append("a").append("]");

关于线程安全:

String 中对象不变,常量,线程当然安全。

StringBuilder 不安全,没有加Synchronized关键字。

StringBuffer 安全,把各种修改数据的方法都加上了Synchronized关键字实现。

StringJoiner是JDK8提供的,会让代码更加简洁。

可以指定三个参数,分别是delimiter,prefix,suffix,分别代表分隔符,开始元素,结束元素

//      可以指定左右和间隔符
        StringJoiner sj = new StringJoiner(", ", "[", "]");

使用add拼接,而不是append

for(int i = 0; i < a.length; i++){
            sj.add(a[i] + "");
        }

选择+还是append

String类重载了”+“”+=“运算符,本质上是调用StringBuilder的append()方法,然后又调用toString()方法得到一个String对象。

每次循环都要创建StringBuilder对象,所以循环里少用String的+,因此直接使用StringBuilder性能会比String高,因为只需要创建一个对象。

String s = "";for (int i = 0; i < 4; i++) { s += "a";}

但是JDK9的字符串的“+”改成了动态方法makeConcatWithConstants(),所以JDK9以后可以放心使用。

字符串常量池是什么

JVM为了提升性能和减少内存消耗,针对字符串专门开辟的区域,避免字符串重复创建,这里存放的是字符串对象的引用。

一个经典问题:

String s = new String("a");

这句话创建了几个对象?

答案是一个或者两个。

两个的情况:

首先先在堆中创建一个String对象,然后判断字符串常量池是否保存了对应字符串的引用,没有保存,在堆中创建对应的字符串对象(第2个对象)并且将字符串对象的引用保存到字符串常量池中。

一个的情况:

首先先在堆中创建一个String对象,然后判断字符串常量池是否保存了对应字符串的引用,保存了就直接返回字符串对象的引用。

再说一个方法,String.intern(),这是个native方法

文档的意思大概是,如果常量池已经有了这个字符串对象的引用,那么直接返回这个字符串(string from the pool),要是没有,那么久在常量池添加一个,然后返回这个字符串对象的引用。

那么可以看看以下代码的执行:

  String s1 = "a";
  String s2 = s1.intern();
  String s3 = new String("a");
  String s4 = s3.intern();
  ​
  System.out.println(s1==s2);
  System.out.println(s3==s4);
  System.out.println(s1==s4);

s1拿到的是 常量池中字符串对象"a"的引用。

s2返回的是 常量池中字符串对象"a"的引用。

所以s1 == s2(地址相等)

s3 返回的是 堆中String对象的引用。

s4返回的是 常量池中字符串对象"a"的引用。

所以s3和s4指向的不是同一个地址,刚才说的new String("a")会创建两个对象,就是这种情况。

再理解一下String不可变性。

String s = "a";

s = "bb";

一开始s是 常量池中字符串对象"a"的引用。

然后s是 常量池中字符串对象"bb"的引用。

"a"和"bb"这两个字符串对象都存在,确实没有变,变的只是s对象指向的地址。

常量折叠Constant Folding

javac编译器会把常量表达式求出来作为常量,嵌套在最终代码中。

对于 String s = "str" + "ing"; 编译器会优化成 String s = "string";

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值