Day8 trim方法String的偏底层字符串常量池intern方法装箱拆箱集合的ArrayList,LinkedListStringBuilder/Buffer

张开发
2026/4/17 23:25:19 15 分钟阅读

分享文章

Day8 trim方法String的偏底层字符串常量池intern方法装箱拆箱集合的ArrayList,LinkedListStringBuilder/Buffer
感觉自己学的有点多了前面的知识没太复习写代码的时候都得回头看看一些方法是谁的。我的妈呀。还是得多回头看。先复盘吧trim方法String字符串的方法删去字符串前后位置中的空白位。什么是空白位空格\n\r\f\t这些都是空白位//trim() //去除字符串前后位置的空白位 \r 回车 \n 换行 \t 制表符 \f 换页都算空白位 str \r \n a b c \t \f; //\r 回车 \n 换行 \t System.out.println(str); System.out.println(-------------); String result str.trim(); System.out.println(result);这个运行结果输出就是a b c再复习复习String字符串的其他方法split indexOf/LastIndexOf CharAt replace/replaceAll length substring toCharArray equalsString的偏底层String的类定义中他的字符串的真实值其实是用final修饰的即String实例化出的对象内容不可以改变String对象是不可变对象、不可变量那么String的引用修改内部内容时是如何作用的其实是生成了一个新的String对象然后引用再指向这个新的对象。另一个问题就出现了你要是这样的话是不是就会产生大量的内存空间浪费也可能导致内存溢出OOM所以为了解决这个问题就整出来了个字符串缓冲池和可变字符串StringBuilder和StringBuffer。字符串常量池常量池在堆中存储通过值来声明的字符串String对象目的为了能够达到减少内存消耗对象重用的效果Java中提供了字符串常量池减少字符串定义的消耗工作原理当Java程序需要定义创建一个字符串不会直接开辟空间而是先到字符串常量池中查阅查看是否有对应的字符串内容副本如果有则直接使用字符串常量池中的对象。如果没有的话在常量池创建一个副本然后再使用此副本注不是任何创建或定义字符串的方法都会存入缓冲池只有使用值的方式声明的字符串才会进入常量池,即让1234作为一个String字符串对象进入常量池具体代码String a 1234; String a1 1234;1234就是一个String对象通过值的方式声明存储在缓冲池如果其他String引用也通过值这种方式声明如a1也会将缓冲池中的1234String对象地址给a1所以aa1是成立的。而如果是new的会先去常量池查找有没有这个String对象如果有就会在堆中开辟一个新的空间将常量池中的String对象的值赋给新实例化的对象并将这个新的对象的地址给引用。如果常量池中没有就会在常量池中先实例化这个对象new出来的String对象的值会指向常量池中这个对象的值下面是代码解释//并不是所有的字符串都会加入字符串常量池 //只要是new都是一个新的独立的内容 String s1 1234; String s2 1234; String s3 new String(1234); //1234是作为值来参与new String的初始化函数的他会先去常量池去找有没有1234这个对象如果有就创建一个新的对象 // 这个对象全部指向常量池中的对象的值如果常量池中没有那就先在常量池中先创建这个对象然后在堆中 //声明的对象会指向常量池中的对象的值 new String(1234); //这个语句执行会创建一个或两个对象所以new String(1234);可能会创建一个或两个对象StringBuilder/Buffer字符串拼接时或使用拼接时每次拼接都会产生一个新的字符串 当需要连续多次拼接时会产生大量的中间串会消耗大量内存 为了避免产生中间串java提供了可变字符串类型,可变长度不是final private了这是优化内存空间的另一个方法这两个是可变字符串数组类其中的值不是用final修饰的可以对原有的字符串数组进行扩展和延申不过扩展延申也是重新开辟了一个空间只是old和新的开辟的空间大小不同。他们默认开辟的空间大小为16后面扩容每次扩为原来的二倍。这两个是可变数组但是也是可以转为正常数组的。String result ; for(int i 0;i100;i){ result resulti; } System.out.println(result); //字符串拼接时或使用拼接时每次拼接都会产生一个新的字符串 //当需要连续多次拼接时会产生大量的中间串会消耗大量内存 //为了避免产生中间串java提供了可变字符串类型,可变长度不是final private了 //StringBuilder StringBuffer StringBuilder stringBuilder new StringBuilder(); stringBuilder new StringBuilder(20);//修改容量 stringBuilder new StringBuilder(a);//将a当作一个容量去存储的a前面是capacity容量为a的ACII编码值 stringBuilder new StringBuilder(123);//将123这个字符串放里面不是声明容量和声明容量方法不同 for(int i 0;i100;i){ stringBuilder.append(i); } //StringBuilder 和StringBuffer都是可变字符串 /// 为什么会有两个一个是线程安全 Builder安全一个是线程不安全 //内部提供了一个可变化的value数组 //默认初始容量为16每次扩容扩两倍 result stringBuilder.toString(); System.out.println(result); System.out.println(stringBuilder.length()); //这个就不会产生中间字符串对象因为他是可变字符数组String的常量编译优化问题以及值String对象和newString对象的拼接String s1 1234; String s2 1234; String s3 new String(1234); //1234是作为值来参与new String的初始化函数的他会先去常量池去找有没有1234这个对象如果有就创建一个新的对象 // 这个对象全部指向常量池中的对象的值如果常量池中没有那就先在常量池中先创建这个对象然后在堆中 //声明的对象会指向常量池中的对象的值 new String(1234); //这个语句执行会创建一个或两个对象 String s4 new String(1234); String s5 1234;//编译代码时已经将12 34拼接完成运行代码时就是s51234 String s6 1234; String s7 12new String(34);//字符串只要不是编译时就能确定的结果需要在运行时拼接 // 那就会创建一个新的对象让引用指向他 String s7a 12new String(34); String s8a 12; String s8b 34; String s8 s8as8b; String s9 s8as8b; ///常量编译优化----------得了解 System.out.println(s1s5);//T/F ------true System.out.println(s1s2);//true System.out.println(s1s3);//false System.out.println(s3s4);//false System.out.println(s1s6);//常量编译优化true System.out.println(s5s6);//常量编译优化true System.out.println(s1s7);//false, System.out.println(s3s7);//false System.out.println(s7s7a);//false System.out.println(s1s8);//false System.out.println(s8s9);//false只要是值操作在编译的时候就可以确定拼接后的结果。如果有String开辟新空间或者引用拼接那就会创建一个新对象让引用指向他intern方法并不是所有字符串都会加入常量池String str new String(new char[]{1,2,3});这个就不会在常量池生成对象。那么如何让他进入常量池即使用intern方法intern的使用方法String str new String(new char[]{1,2,3}); String str1 str.intern();intern方法的作用过程先去常量池寻找有没有这个String对象如果有则返回这个对象的地址如果没有则在常量池创建对象并返回地址注当两个字符串的equals比较为true两个字符串的inter方法比较一定为true装箱拆箱Java是一个面向对象的语言但是也有非对象的数据类型比如基本数据类型为了圆这个坑就创建了基本数据类型类即封装类基本数据类型和封装类间可以转换//byte short int long char float double boolean //封装类型Byte Short Integer Long Character Float Double Boolean //整数和浮点数类型的封装类都是继承自Number超级父类是Number所有的封装类都有一个超级父类Number类装箱基本数据类型转为对应的封装类拆箱封装类转为对应的基本数据类型//基本数据类型和对应的封装类型可以相互转换 byte b 12; Byte b1 12; b new Byte(12);//JDK9后就废弃了这个方法但是不影响 b Byte.valueOf(12); //基本数据类型转为对应封装类型叫做装箱 //基本数据类型的封装类型转为基本数据类型这个过程叫做拆箱 Integer a 12;//装箱 int c new Integer(12);//拆箱封装类型的缓存机理类似于常量池封装类型也有自己的常用池整数类型的封装类型有缓存但不是所有的数都有缓存范围是从-128~127且Integer封装类型的缓存可以通过修改JVM的参数来修改缓存大小boolean也有缓存因为就true和fales。Integer i1 12; Integer i2 new Integer(12); // Integer i2 Integer.valueOf(15); System.out.println(i1i2);//false Integer i3 12; System.out.println(i1i3);//true Integer i4 -128; Integer i5 -128; System.out.println(i4i5);//true i4 128; i5 128; System.out.println(i4i5);//false Character c1 a; Character c2 a; System.out.println(c1c2);//true int numa 1000; Integer numb1000; //基本数据类型和引用装箱类型比较将引用类型拆箱后再比较和缓存就没有关系了 System.out.println(numanumb);//true超出了缓存范围就会创建一个对应类型的对象。ArrayListLinkedList这个先从实现类说起慢慢说到集合集合Collection)集合是一个接口有三类容器List Set Map其中List Set MapList下有三个实现类ArrayList LinkedList VectorList和Set有一个共同的父接口CollectionArrayListArrayList顾名思义数组链表通过动态数组保存数据使用数组实现链接private static final int DEFAULT_CAPACITY 10; private static final Object[] EMPTY_ELEMENTDATA {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA {}; transient Object[] elementData; // non-private to simplify nested class access private int size;查看ArrayList的源码发现他的elementData;并不是final修饰size也不是final修饰这就说明他是一个可变动态数组且初始默认大小是10。每个节点可以存储任何类型的对象初始大小为10每次扩容变为原来的1.5倍每个下标都是一个对象,对象可以是任意类型他们的超父类是Object类所有的List都是有序集合存入是什么顺序就是什么顺序了后面有无序的然后再说一下ArrayList中的方法1list1引用名.add()向链表中加入一个数据对象如果是基本数据类型则会进行自动的装箱操作然后保存对应的封装对象。也可以向其中存null值有不可以存null值的因为后面有的容器需要比较hash值进行无序存储比如TreeSet大部分的Map2list1.set(下标位置对象或值)3list1.remove(下标位置)4list1.get(下标位置)//返回下标中的对象5list1.size返回数组大小LinkedListLinkedList使用链表实现的还是用双向链表实现的每个链表都是由结点构成方法和ArrayList实现方法相同但是底层实现方式不相同。所以ArrayList和LinkedList在功能上的性能会有差距。ArrayList查找快增删慢LinkedList查找慢增删快因为ArrayList可以通过下标定位数组位置直接找到对应对象所以查找快但是增删的话需要将后面的所有对象都移动所以增删慢。而LinkedList查找需要从头节点或者尾结点开始遍历查找所以查找慢但是删除的话只需要改变他们的fore指针和next指针就可以实现结点的删除或增加所以增删快。为什么要用双向链表因为这样检索的效率会提升。可以从两个方向查找数据依据下标所在位置分别从前或后找到对应位置加快检索效率他查找数据可以从两个方向查找。注List可以存储重复的值以及null值。List的遍历使用foreach方法for(Object obj:list){代码块}

更多文章