包装类

包装类

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对象的区别:

  1. ​ 直接赋值String s = hsp";
  2. 调用构造器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";

解读:创建了两个对象

  1. String是一个final类,代表不可变的字符序列
  2. 字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的。

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类

  1. 一个可变的字符序列,此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuilder不是线程安全)。该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快。
  2. 在StringBuilder上的主要操作时append和insert方法,可重载这些方法,以接受任意类型的数据。

String、StringBuffer和StringBuild的比较

  1. StringBuilder和StringBuffer非常类似,均代表可变的字符序列,而且方法也一样
  2. String:不可变字符序列,效率低,但是复用率高;
  3. StringBuffer:可变字符序列,效率较高(增删)、线程安全;
  4. 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,比如配置信息等。

posted @   忧愁小松鼠  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
点击右上角即可分享
微信分享提示