String类概述
Java的所有字符串文字 例如“abc” 都可以称作是String类对象。
String代表字符串类型,可以定义字符串变量指向字符串对象,特点:String常被成为不可变字符串,它的对象在创建后不能被更改。
String是不可变字符串的原因
因为每次给String用“”赋值都是在堆内存的字符串常量池中创建出新的对象,因此在进行字符串的修改的时候其实都是在堆内存中产生了新的对象并且指向它。而原来的字符串是没有改变的。因此成为不可变字符串。在栈内存中存储的是String类对象的地址,但是打印出来为什么不是地址呢?这个是因为里面的一些小的改动 让他直接打印出来的就是该字符串
String字符串创建对象的几种方式
//创建字符串对象的几种方式 //方式一 : 直接用双引号得到字符串对象。 String s = "abc"; System.out.println(s); //方式二 用构造器传参来创建字符串对象。 String s1 = new String("abcd"); System.out.println(s1); //3. public String(char[] c) 根据字符数组的内容来创建字符串对象。 char[] chars = {'a','b','c'}; String s3 = new String(chars); //4.public String(bytes[] b) 根据字节数组的内容 来创建字符串对象 byte[] bytes = {97, 98, 99 ,65 ,66 ,67}; String s4 = new String(bytes); System.out.println(s4);
在这几种方法中,后面的不常用,基本上用的都是第一种,那么这几种方法创建String对象有什么区别呢?
在前面讲过,以 “ ” 方式给出的对象他不会改变,意思就是你用这种方式创建完对象之后,他会存储在堆内存中的字符串常量池中,而且相同的内容跟只会在其中存储一份。因此你不管用这个方式创建多少个对象,只要值相同,他们在栈内存中的变量存储的地址都是指向常量池中的同一个对象。
String ss1 = "abc"; String ss2 = "abc"; System.out.println(ss1 == ss2);
但是如果是以new构造器得到的字符串对象,他就有所不同,例如字符拼接这种方式,因为这种方式现在堆内存中开辟空间存放字符数组,当你通过构造器去创建对象时,他其实是在堆内存中开辟了两个空间去将数组中的字符拼接之后放入堆内存中的空间,然后再将堆内存中空间的地址赋值给栈内存中创建的对象的空间,因此在判断时为false。
char[] chars = {'a','b','c'}; String s3 = new String(chars); String s5 = new String(chars);System.out.println(s3 == s5);
额外小拓展: 这种情况下为什么两个变量指向同一个地址,因为在编译阶段,当把程序扔给class文件编译的时候,编译器有一个小优化,就是当你在编译阶段他就会给你自动拼接完毕,而不会在运行之后再去拼接,那样会降低性能。
String ss = "abc"; String aa = "a" + "b" + "c"; System.out.println(ss == aa);
String类的比较
一般的我们用Equals方法和EqualsIgnoreCase(忽略大小写进行比较)来进行比较,这两个是String类提供的方法是直接比较内容的。那我们为什么不用 == 来比较String类呢? 因为在字符串中用==比较的话,他比较的是栈内存中变量的值,而String类型的变量中存储的是堆内存中String类对象的地址,因此用==号判断String类对象很不方便。
String ss = "abc";
Scanner sc = new Scanner(System.in); String a = sc.next(); System.out.println(a == ss);
ArrayList
集合与数组类似,也是一种容器,用来装数据,数组的特点:类型确定,长度固定。缺点:在个数不能确定,且要进行增删数据的时候,数组是不太合适的。
集合的特点:大小不固定,启动后可以动态变化也就是自动扩容,类型也可以选择不固定。集合非常适合做元素个数不确定,且要进行增删操作的业务场景。
Arraylist通过无参构造创建对象。 Arraylist<> list = new Arryaylist<>();
泛型概述
Arraylist<E> 这种就叫做泛型,可以在编译阶段约束集合对象只能操作某种数据类型。
集合中只能存储引用类型,不支持基本数据类型。
因此在定义一个集合的时候,应该使用泛型去定义。
小案例:注意事项 删除80一下的数字
为什么会出现这种情况呢? 因为在你遍历集合的时候,当出现满足条件的对象后,remove函数会将其删除,但是这个时候集合删除完这个对象后,他后面的对象会自动向前移动,也就是比如删除了79后集合为[98,87,59,50,66,100],但是你循环中的i还在++因此如果两个满足条件的数相邻的话,就会出现这种bug,那应该怎么去解决呢?
在满足条件之后i--就可以了,或者改变for循环中的条件,从后往前遍历这样也可以解决。
StringBuilder和StringJoiner
StringBuilder可以看成是一个容器,创建之后里面的内容是可变的。
作用:提高字符串的操作效率。
例如: String s6 = s1 + s2 +s3 +s4 + s5 这种情况的拼接字符串就会产生很多没必要的字符串对象,这些中间变量会占用内存浪费资源。因此就可以利用StringBuilder对象进行拼接,从始至终只有StringBuilder一个对象,这样就不会浪费内存。
注意:因为StringBuilder是Java已经写好的类 java在底层做了一些特殊处理。因此打印对象不是地址值而是属性值。
创建对象方法:
空参构造
带参构造
StringBuilder的方法
append 添加方法 向后拼接字符 返回值还是StringBuilder对象
reverse 字符串反转
toString 将StringBuilder对象转换成字符串对象
StringJoiner
StringJoiner与StringBuilder的原理是一样的,只不过在操作上比Stringbuilde更加简洁,StringJoiner可以指定字符拼接中间的间隔符号,而且StringJoiner没有空参构造。有两个带参构造方法
public class StringJoinerDemo { public static void main(String[] args) { //创建对象,指定中间的间隔符号 StringJoiner sj = new StringJoiner("---"); //添加元素 sj.add("aaa").add("bbb").add("ccc"); System.out.println(sj); } }
public StringJoiner(间隔符号) 创建一个对象,指定拼接时的间隔符号
public StringBulider(间隔符号,开始符号,结束符号) 创建一个对象,指定拼接时的间隔符号、开始符号、结束符号。
方法:
public StringJoiner add(添加内容) 添加数据 并返回对象本身
public int length() 返回长度(字符出现的个数)
public String toString() 返回一个字符串(该字符串就是拼接之后的结果)