包装类
包装类
JDK5以前需要我们手动装箱和拆箱
装箱就是基本数据类型转换成包装类型
拆箱就是包装类型转换成基本数据类型自动装箱底层使用的是Integer.valueOf()方法
自动拆箱底层使用的是intValue()方法
package com.example.wrapper_; /** * @Author 郜庆辉 * @Time 2022/5/10 16:30 * @Version 1.0 */ public class Integer01 { public static void main(String[] args) { // JDK5以前需要我们手动装箱和拆箱 // 装箱就是基本数据类型转换成包装类型 // 拆箱就是包装类型转换成基本数据类型 //手动装箱 //int -> Integer int n1 = 100; Integer integer = new Integer(n1); Integer integer1 = Integer.valueOf(n1); //手动拆箱 //Integer -> int int i = integer.intValue(); //JDK5之后是自动装箱和拆箱 //自动装箱底层使用的是Integer.valueOf()方法 //自动装箱 int m = 10; Integer integer2 = m; //底层使用的是Integer.valueOf(m) //自动拆箱底层使用的是intValue()方法 int m1 = integer2; //底层使用的是intValue()方法 } }
包装类型和String类型的相互转换
package com.example.wrapper_; import java.awt.geom.Arc2D; /** * @Author 郜庆辉 * @Time 2022/5/10 21:35 * @Version 1.0 */ public class WrapperVSString { public static void main(String[] args) { //包装类(Integer) -> String Integer i = 100; //自动装箱 //方式1 String str1 = i + ""; //方式2 String str2 = i.toString(); //方式3 String str3 = String.valueOf(i); // String -> 包装类(Integer) String str4 = "1234"; Integer i1 = Integer.parseInt(str4); //使用到自动装箱 Integer i2 = new Integer(str4); //构造器 System.out.println("ok"); //包装类(Double) -> String Double k = 100.0; String str5 = k + ""; String str6 = k.toString(); String str7 = String.valueOf(k); // String -> 包装类(Double) Double d1 = Double.parseDouble(str4); Double d2 = new Double(str4); System.out.println(d1 + " " + d2); } }
Integer面试题
package com.example.wrapper_; /** * @Author 郜庆辉 * @Time 2022/5/10 22:23 * @Version 1.0 */ public class WrapperExercise01 { public static void main(String[] args) { Integer i = new Integer(1); //这里是创建了两个不同的对象,返回F Integer j = new Integer(1); System.out.println(i == j); /* //我们来看一下valueOf的源码 1.如果 i 在 IntegerCache.low(-128)~IntegerCache.high(127),就直接从数组返回 2.如果不在 -128~127,就直接 new Integer(i) public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } */ //主要看范围,如果范围是-128-127就直接返回,如果不再这个范围内就创建新的对象就 new Integer(xx); //m和n是1,所以直接返回不需要创建对象,结果为T Integer m = 1; //底层是Integer.valueOf(1) Integer n = 1; //底层是Integer.valueOf(1) System.out.println(m == n); Integer x = 128; //超过上面所说的范围,新创建两个对象,两个对象肯定为F Integer y = 128; System.out.println(x == y); } }
package com.example.wrapper_; /** * Integer面试题 * @Author 郜庆辉 * @Time 2022/5/11 0:21 * @Version 1.0 */ public class WrapperExercise02 { public static void main(String[] args) { //示例一:只要是创建两个对象的,都不相等 Integer i1 = new Integer(127); Integer i2 = new Integer(127); System.out.println(i1 == i2);//F //示例二 Integer i3 = new Integer(128); Integer i4 = new Integer(128); System.out.println(i3 == i4);//F //示例三:没创建对象的就看范围是否超出,超出也是自动创建对象,不超出就值比较 Integer i5 = 127;//底层 Integer.valueOf(127) Integer i6 = 127;//-128~127 System.out.println(i5 == i6); //T //示例四 Integer i7 = 128; Integer i8 = 128; System.out.println(i7 == i8);//F //示例五 Integer i9 = 127; //Integer.valueOf(127) Integer i10 = new Integer(127); System.out.println(i9 == i10);//F //示例六 Integer i11=127; int i12=127; //只要有基本数据类型,判断的是值是否相同 System.out.println(i11==i12); //T //示例七 Integer i13=128; int i14=128; System.out.println(i13==i14);//T } }
String类
1.String对象用汉语保存字符串,也就是一组字符序列
2."tom"是字符串常量,双引号括起来的字符序列
3.字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
4.String类有很多构造器
常用的有:
String s1 = new String();
String s2 = new String(String original);
String s3 = new String(char[] a);
String s4 = new String(char[] a,int startIndex,int count);
String s5 = new String(byte b);5.String类实现了接口Serializable 【String 可以串行化:可以在网络传输】
String类实现了接口Comparable 【String对象可以比较大小】6.String是final类,不能被继承
7.String有属性private final char value[];用于存放字符串内容,可以说String本质就是char数组
8.一定要注意:value是一个final类型,不可以被修改,
这里不可以被修改是value不可以指向新的地址,字符串里面的单个字符是可以修改的
两种创建String对象的区别:
- 直接赋值String s = hsp";
- 调用构造器String s2 = new String("hsp");
1.方式一:先从常量池查看是否有“hsp”数据空间,如果有就直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池中的空间地址;
2.方式二:先在堆中创建空间,里面维护了value属性,指向常量池中的hsp空间。如果常量池没有“hsp”,重新创建,如果有,直接通过value指向。s2最后指向的是堆中的空间地址。
下面是String比较的练习题:
package com.example.string_; /** * @Author 郜庆辉 * @Time 2022/5/11 17:17 * @Version 1.0 */ public class StringExercise01 { public static void main(String[] args) { //a直接指向常量池的"gqh"地址 String a = "gqh"; //b指向堆中的对象地址,而堆中的对象value指向常量池的"gqh" String b = new String("gqh"); //String类将equals方法重写,比较的是字符串中的字符是否相等,结果为T System.out.println(a.equals(b)); //a指向的是常量池的地址,b指向的是堆中的地址,结果为F System.out.println(a == b); //a指向是常量池的地址,b.intern()方法最终返回的时常量池的地址(对象),结果为T System.out.println(a == b.intern()); //b是堆的地址,b.intern()方法最终返回的时常量池的地址(对象),结果为F System.out.println(b == b.intern()); /* 知识点: 当调用intern方法时,如果常量池已经包含一个等于此String对象的字符串(用equals(Object)方法确定), 则返回常量池中的字符串。否则,将此String对象添加到常量池中,并返回此String对象的引用。 解读:b.intern()方法最终返回的时常量池的地址(对象) */ } }
下面是String比较的练习题2:
package com.example.string_; /** * @Author 郜庆辉 * @Time 2022/5/11 17:44 * @Version 1.0 */ public class StringExercese02 { public static void main(String[] args) { //创建一个由p1指向的Person对象 Person p1 = new Person(); //p1.name = "gaoqinghui" 表示在常量池中创建了一个gaoqinghui的数据空间,由p1的对象中的value属性指向这个数据空间 p1.name = "gaoqinghui"; //创建一个由p2指向的Person对象 Person p2 = new Person(); //p2.name = "gaoqinghui" 表示由p2的对象中的value属性指向刚才p1创建的gaoqinghui的数据空间,现在p1和p2的value都指向这个数据空间 p2.name = "gaoqinghui"; //比较的是字符串是否相等,结果为true System.out.println(p1.name.equals(p2.name)); //现在p1.name和p2.name都指向的是常量池中的同一个存放gaoqinghui的数据空间 System.out.println(p1.name == p2.name); //p1.name指向的是存放gaoqinghui的地址空间,而后面比较的gaoqinghui字符串是一个常数,不是new出来的,所以他本身就在常量池中,结果为true System.out.println(p1.name == "gaoqinghui"); //不是同一个对象,比较结果为false String s1 = new String("bcde"); String s2 = new String("bcde"); System.out.println(s1 == s2); } } class Person { public String name; }
面试题1:
下面语句是什么意思:创建了几个对象
String s1 = "hello"; s1 = "haha"; 解读:创建了两个对象
- String是一个final类,代表不可变的字符序列
- 字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的。
String s1 = "hello";先在常量池中创建了一个”hello”的地址空间,
然后又执行s1 = “haha”,底层会在常量池中寻找是否有haha这个地址空间,如果有的话就让s1指向它,如果没有的话就创建一个haha地址空间让s1指向。s1指向新的haha这个地址空间,s1就不指向“hello”;
面试题2:
下面语句创建了几个对象
String a = "hello" + "abc"; 答:创建了一个对象;
编译器会自动帮我们优化
String a = "hello" + "abc"; 等价于String a = "helloabc";
面试题3:
package com.example.string_; import javax.print.DocFlavor; /** * @Author 郜庆辉 * @Time 2022/5/11 18:23 * @Version 1.0 */ public class StringExercise03 { public static void main(String[] args) { //解读: //1.先创建一个StringBuilder sb = StringBuilder() //2.执行sb.append("hello"); //3.sb.append("abc"); //4.String c = sb.toSting() //最后其实是c指向堆中的对象(String) value[] -> 池中 "helloabc" String a = "hello"; String b = "abc"; String c = a + b; //一共有3个对象 //底层是StringBuilder sb = new StringBuilder(); sb.append(a); //sb.append(b); sb是在堆中,并且append是在原来字符串的基础上追加的 // 重要规则: // Sting c1 = "ab" + "cd";是常量相加,看的是池 //String c1 = a + b;变量相加,是在堆中 } }
分析下面代码的流程:面试题
package com.example.string_; /** * @Author 郜庆辉 * @Time 2022/5/12 15:16 * @Version 1.0 */ public class StringExercise04 { public static void main(String[] args) { //对下面的代码进行分析 /* 主方法main在堆中创建了一个用test1引用的对象,Test1对象中有一个str成员属性指向堆中的另一个String对象,String中有个value属性指向的是常量 池中的"gqh"常量。test1中还有一个char类型的数组,ch指向这个数组,这个数组的存储位置在堆中的另一个空间。 这时主方法调用了change方法,我们已经知道main方法在栈中,只要我们调用方法时就会在栈中创建一个空间存放方法。 这时change方法中的str指向的就是String对象中的value属性。 ch指向的也是刚才我们创建的ch数组 str = "java"; 当执行这句话时,str就直接指向的时常量池中的“java”常量对象空间,如果没有"java"常量对象空间则创建, 这时我们原来str指向的哪个String对象的value属性断开。 ch[0] = 'h';我们知道这个数组是final的,地址不可修改,但是我们这个直接修改的就是堆中的字符串数组的单个元素。 当我们在主函数中输出时,test1.str输出的main方法栈中的str,main方法的str指向的还是哪个String对象,输出的还是原来的”gqh“ 而输出test1.ch时,由于ch和test1.ch指向的都是同一个字符串数组,输出的是已经改的的”hava“ 总结:main栈中的str指向的是String对象的value属性。 change栈指向的直接就是常量池的"java";两者指向的并不一样,输出的也不一样 而main栈和change栈指向的都是同一个数组。前者是指向的是String对象的ch对象指向的数组,后者直接指向的就是数组,两者一样 */ Test1 test1 = new Test1(); test1.change(test1.str, test1.ch); System.out.print(test1.str + " and " ); System.out.println(test1.ch); } } class Test1 { String str = new String("gqh"); final char[] ch = {'j','a','v','a'}; public void change(String str,char ch[]){ str = "java"; ch[0] = 'h'; } }
StringBuffer类
java.lang.StringBuffer代表可变的字符序列,可以堆字符串内容进行增删
很多方法与String相同,但StringBuffer是可变长度的StringBuffer是一个容器
1.StringBuffer的直接父类是AbstractStringBuilder
2.StringBuffer实现了Serializable,即StringBuffer的对象可以串行化
3.在父类中,AbstractStringBuilder有属性char[] value,不是final该value数组存放字符串内润,因此存放在堆中
4.StringBuffer是一个final类,不能被继承
5.因为StringBuffer字符内容是存在 char[] value,所有在变化(增加/删除)的时候不需要每次都更换地址(即不是每次创建新的对象),因为效率高于String
String VS StringBuffer
1)String保存的字符串常量,里面的值不能更改,每次String类的更新实际上就是更改了地址(相当于创建了新的对象),效率低。private final char value[];
2)StringBuffer保存的是字符串变量,里面的值可以修改,每次StringBuffer的更新实际上可以更新内容,不需要每次更新地址。但是如果数组满了,也需要重新创建对象(更新地址); char[] value; 这个value是指向堆中的。
3)如果创建一个String对象,堆中的value属性是指向常量池的。
4)如果创建一个StringBuffer对象,堆中的value属性是指向堆中的。
创建StringBuffer
//1.默认方法里什么都没有的话就创建一个大小为16的char[],用于存放字符内容,大小是16 StringBuffer stringBuffer = new StringBuffer(); //2.通过构造器传进去一个100就是给char[]数组指定大小。char[]大小就是100 StringBuffer stringBuffer1 = new StringBuffer(100); //3.通过传一个字符串创建StringBuffer,char[]的大小就是你传入的字符串的长度 + 16 就是str.length + 16 StringBuffer stringBuffer2 = new StringBuffer("hello");
String和StringBuffer的相互转换
// String -> StringBuffer String str = "hello"; //方法一:使用构造器 //注意:返回的才是StringBuffer对象,对str本身没有影响 StringBuffer stringBuffer = new StringBuffer(str); //方式二:使用的是append方法 StringBuffer stringBuffer1 = new StringBuffer(); stringBuffer1 = stringBuffer1.append(str); // StringBuffer -> String StringBuffer stringBuffer2 = new StringBuffer("郜庆辉牛逼"); //方式一:使用StringBuffer提供的toString方法 String s = stringBuffer2.toString(); //方式二:使用构造器 String s1 = new String(stringBuffer2);
StringBuffer的常用方法
package com.example.stringbuffer_; /** * @Author 郜庆辉 * @Time 2022/5/12 21:13 * @Version 1.0 */ public class StringBufferMethod { public static void main(String[] args) { StringBuffer s = new StringBuffer("hello"); //增 s.append(","); s.append("张三丰"); s.append("郜庆辉"); s.append("赵敏").append("100").append("生活").append(10.2); System.out.println(s); //这里直接调用s这个对象的引用是默认调用对象的toString方法 //hello,张三丰郜庆辉赵敏100生活10.2 //删 //删除索引为>=start && <end处的字符 //删除11-14之间的字符,包括11,不包括14 [11,14) s.delete(11,14); System.out.println(s); //hello,张三丰郜庆100生活10.2 //改 //使用周芷若替换索引为6-9之间的字符,前闭后开 [6-9) s.replace(6,9,"周芷若"); System.out.println(s); //hello,周芷若郜庆100生活10.2 //查 //查找指定的字串在字符串第一次出现的索引,如果找不到返回-1 int indexOf = s.indexOf("周芷若"); System.out.println(indexOf); //6 //插 //在索引11的位置之前插入辉,原来索引为11的位置自动后移 s.insert(11, "辉"); System.out.println(s); //hello,周芷若郜庆辉100生活10.2 System.out.println(s.length()); System.out.println(s); } }
StringBuilder类
- 一个可变的字符序列,此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuilder不是线程安全)。该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快。
- 在StringBuilder上的主要操作时append和insert方法,可重载这些方法,以接受任意类型的数据。
String、StringBuffer和StringBuild的比较
- StringBuilder和StringBuffer非常类似,均代表可变的字符序列,而且方法也一样
- String:不可变字符序列,效率低,但是复用率高;
- StringBuffer:可变字符序列,效率较高(增删)、线程安全;
- StringBuilder:可变字符序列,效率最高,线程不安全;
String的使用注意说明:
String s = “a”;//创建了一个字符串 s += “b”; //实际上原来的“a”字符串对象已经丢弃了,现在又产生了一个字符串s + “b”(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能; 结论:如果我们对String做大量修改,不要使用String
String、StringBuffer和StringBuild的选择
使用的原则,结论:
1.如果字符串存在大量的修改操作,一般使用StringBuffer或StringBuilder
2.如果字符串存在大量的修改操作,并在单线程的情况,使用StringBuilder
3.如果字符串存在大量的修改操作,并在多线程的情况,使用StringBuffer
4.如果我们字符串很少修改,被多个对象引用,使用String,比如配置信息等。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报