包装类介绍
Java的包装类(Wrapper Classes)是Java语言为八种基本数据类型(byte, short, int, long, float, double, char, boolean)提供的对应类。这些包装类允许我们将基本数据类型当作对象来处理,并提供了许多有用的方法和常量。以下是每个基本数据类型对应的包装类:
- Byte:对应基本数据类型byte
- Short:对应基本数据类型short
- Integer:对应基本数据类型int
- Long:对应基本数据类型long
- Float:对应基本数据类型float
- Double:对应基本数据类型double
- Character:对应基本数据类型char
- Boolean:对应基本数据类型boolean
包装类的主要用途
- 提供了一系列实用的方法:例如,Integer类提供了将字符串转换为整数的方法(Integer.parseInt()),Double类提供了比较两个浮点数是否相等的方法(Double.compare())等。
- 实现了数据类型之间的转换:包装类提供了将基本数据类型转换为字符串、将字符串转换为基本数据类型等方法,方便了数据类型的转换操作。
- 支持泛型操作:在Java集合框架中,由于泛型只能接受类类型,而不能接受基本数据类型,因此我们需要使用包装类来存储和操作基本数据类型。
- 提供了常量:例如,Integer类提供了表示最大值、最小值、字节大小等常量。
此外,从Java 5开始,引入了自动装箱和拆箱机制,这使得基本数据类型和其对应的包装类之间的转换变得更加便捷。装箱是将基本数据类型转换为对应的包装类对象的过程,而拆箱则是将包装类对象转换回对应的基本数据类型的过程。
自动装箱:将基本数据类型自动转换为对应的包装类对象。
自动拆箱:将包装类对象自动转换为对应的基本数据类型。
public class A {public static void main(String[] args) {//JDK5之前//手动装箱int int1 = 10;Integer integer1 = new Integer(int1); // 使用构造函数进行装箱Integer integer2 = Integer.valueOf(int1); // 使用静态工厂方法进行装箱//手动拆箱Integer integer3 = 10;int int3 = integer3.intValue(); // 使用intValue()方法进行拆箱//--------------------------------------------------------//JDK5之后//自动装箱int int4 = 10;Integer integer4 = int4;//系统自动调用new Integer(int4)//自动拆箱Integer integer5 = 100;int int5 = integer5;//系统自动调用integer5.intValue()进行拆箱}
}
其他类的用法与其相似,在此不再赘述。
隐式的精度提升和自动装箱
例如在三元运算符中,如果在表达式中混合使用不同的基本数据类型时,较低精度的类型会被自动提升为较高精度的类型。例如,当int和double一起使用时,int会被自动提升为
double
,因为double
具有更高的精度。这种提升是自动的,无需显式转换。public class A {public static void main(String[] args) {int num1=10;double num2=11;//方法一System.out.println((true)?num1:num2);//方法二if(true)System.out.println(num1);elseSystem.out.println(num2);} }
执行结果:
10.0
10
看似两者方法执行了相同的操作,但三元运算符会自动将精度较低的 int1 进行精度提升。
这种情况也同样适用自动装箱:将数据类型由 double 替换为 Integer,n1 也会因为三元运算符由int转换为Integer。但这个过程是自动装箱并不涉及精度的提升,它只是将基本数据类型转换为其对应的包装类对象。
包装类和String类相互转换
包装类(Integer)转String
1、使用连接运算符"+": 使用+
运算符将基本数据类型或它们的包装类与字符串连接起来,这会自动将非字符串类型转换为字符串。
2、使用toString方法:所有的包装类都重写了Object
类的toString()
方法,以返回表示该对象的字符串。
3、使用String.valueof()方法: String 类提供了一个静态的valueOf()
方法,该方法可以接受各种类型(包括基本数据类型和它们的包装类)的参数,并返回其字符串表示。
public class Go {//主方法public static void main(String[] args) {Integer i=10;//使用连接运算符"+"String str1=i+"";//使用toString()方法String str2=i.toString();//使用String.valueOf()方法String str3=String.valueOf(i);}
}
String转包装类
1、使用构造方法创建新对象:每个包装类都提供了一个接受字符串参数的构造方法,用于将字符串转换为该包装类的实例。
2、使用包装类中的的方法:例如Integer.parseInt()、Integer.valueOf()。这两个方法虽然返回的都是整形变量,但前者返回的是基本数据类型 int,后者返回的是包装类 Integer 对象。
public class Go {//主方法public static void main(String[] args) {String str="123";//使用构造器,返回一个新的Integer类对象Integer num1 = new Integer(str);//使用包装类方法Integer num2 = Integer.parseInt(str);// 返回int型变量,再自动装箱Integer num3 = Integer.valueOf(str);// 返回包装类对象Integer}
}
Integer创建机制
在Integer类中有着一个预存数组。这个数组包含
-128
到127
(包括两端)之间所有的Integer 对象。当你创建 Integer 类对象时,如果值处于该范围之间,系统就会返回缓存中的对象实例,而不是创建一个新的对象。在这个范围之外时,系统则会创建新实例并返回。但需要注意的是当你使用 new Integer( int类变量 ) 构造函数来显式地创建一个新的Integer对象时。new 关键字总是会创建一个新的对象,即使该值已经存在于缓存中。
public class A {public static void main(String[] args) {Integer a = Integer.valueOf(127);Integer b = Integer.valueOf(127);System.out.println(a == b); // 输出 true,因为 a 和 b 在范围内,引用相同的对象Integer c = Integer.valueOf(128);Integer d = Integer.valueOf(128);System.out.println(c == d); // 输出 false,因为 c 和 d 在范围外,是不同的对象实例Integer e=new Integer(100);Integer f=new Integer(100);System.out.println(e==f);// 输出 false,因为new关键字会自动创建不同的对象实例} }
String类
创建方式
在Java中,创建String对象的常用方式有两种:
-
直接赋值:
这是最简单和最常用的方式。
当使用双引号直接给String
变量赋值时,Java会在字符串常量池中查找是否已经存在相同的字符串。
如果存在,则直接引用该字符串;
否则,在字符串常量池中创建一个新的字符串对象。
注意,String str3 = "str1" + "str2"; 这行代码的结果和String str3 = "str1str2";是等价的。在这两种情况下,str3都会指向字符串常量池中的"str1str2"字符串。
String str1 = "Hello";
//等同于
String str1="He"+"llo";
-
使用
new
关键字:
使用new
关键字创建一个新的String
对象,该对象总是位于堆内存中,并且不会在字符串常量池中查找或创建。
String str2 = new String("Hello");
注意事项
- String用于保存保存字符串,也就是一组字符序列
- 字符串中的字符使用Unicode字符编码,一个字符(不区分字母或汉字)占两个字节
- String类有很多构造器,构成了构造器的重载,常用的构造器:
- String(): 创建一个空字符串。
- String(String original): 创建一个字符串。
- String(char[] value): 使用字符数组初始化字符串。
- String(byte[] bytes): 使用字节数组初始化字符串(假设字节是默认的字符集)。
- String(char[] value, int offset, int count): 使用字符数组的一部分初始化字符串。
- String实现了三个接口:
- Serializable:String可以串行化,即可以在网络上传输
- Comparable:允许String对象进行比较
- CharSequence:各种常用方法
- String类是final类,不可被其他类继承
- String有属性private final char value[];用于存放字符串内容,因其被final修饰,所以String对象所指向的地址无法被修改。(即String一旦创建,所指向的地址就已确定,无法再指向新的地址。它的内容也是不可变的,每次对
String
的修改操作(如连接、替换等)实际上都会创建一个新的String
对象。)String str = "Hello"; // str 现在是 "Hello",我们不能改变它包含的内容 // 下面的代码会创建一个新的 String 对象,而不是修改 str str = str + " World"; // 现在 str 是 "Hello World"char[] str1 = {'a','a','a'};//普通字符串//使用字符数组模拟String对象final char[] str2 = {'b','b','b'};final char[] str3 = {'c','c','c'};str2[0]=str3[0];//正确,内容可被修改str1=str2;//正确,使str1指向str2,str2=str3;//错误,有final修饰,str2所指向的地址不可被修改
String常用方法
1、equals():区分大小写,判断内容是否相等
2、equalsIgnoreCase():不区分大小写,判断内容是否相等
3、indexOf():获取指定字符或字符串在字符串中第一次出现的索引(索引从0开始),找不到则返回-1
4、lastIndexOf():获取指定字符或字符串在字符串中最后一次出现的索引(索引从0开始),找不到则返回-1
5、substring():截取指定范围的字符串
String str="0123456789";
System.out.println(str.substring(2));
//从索引"2"开始,截取后面所有内容,输出23456789
System.out.println(str.substring(2,5));
//从索引"2"开始,截取到索引"'5'-1"的内容,输出234
6、trim():去除前后空格
7、charAt():获取字符串指定位置的字符
8、toUpperCase():全部转换为大写,toLowerCase()全部转换为小写
9、concat():连接字符串
10、replace():替换指定字符串
String str = "no.1 no.2 no.3";
System.out.println(str.replace("no","No"));
//将str中所有的"no"替换为"No"
11、split():基于指定的正则表达式或字符来拆分字符串。
//一、基本语法
//以指定字符或字符串来划分原字符串
String str ="yes or no";
String[] str1=str.split(" ");
//将str以" "来划分str1[0]=yes,str1[1]=or,str1[3]=no
//二、使用limit参数
//以指定字符或字符串来划分原字符串,且划分为limit份
String str ="yes or no";
String[] str1=str.split(" ",2);
//将str以" "来划分str1[0]=yes,str1[1]=or,str1[3]=no
//三、使用正则表达式
//如果以特殊字符对字符串进行划分,需加入转义符\
String str ="E\\java\\test";
String[] str1=str.split("\\\\");
//因为特殊字符为"\\",有两个,所以需要加入两个转义符,即"\\\\"
//将str以"\\"来划分str1[0]=E,str1[1]=java,str1[3]=test
12、toCharArray():将字符串转化为字符数组
该方法会逐个比较两字符串的编码大小,并返回前者编码减去后者编码的结果;如果两字符串前缀相同(如qwer和qwerty),则返回两字符串长度相减的值;相同则返回0。
StringBuffer类
- StringBuffer代表可变的字符序列,与String类不同,它可以对字符串内容进行增删操作。
- StringBuffer的直接父类是AbstractStringBuilder类,并且实现了Serializable接口,意味着StringBuffer的对象可以串行化。
- StringBuffer内部使用一个char[]数组来存放字符串内容,该数组不是final修饰的,因此内容可以变化。
- StringBuffer是一个final类,不能被其他类继承。
String和StringBuffer的区别
- String保存的是字符串常量,里面的值不能更改;StringBuffer保存的是字符串变量,里面的值可以更改。
- String每次更新实际上都是更改地址,效率较低(栈->堆->常量池);StringBuffer的更新实际上可以更新内容,不用每次都更新地址,效率较高(栈->堆)。
构造方法
StringBuffer()
创建一个不带任何字符的StringBuffer对象,其初始容量为16个字符。
StringBuffer str1 = new StringBuffer();
//str1容量为16个字符
StringBuffer(int)
创建一个不带任何字符的,但具有指定初始容量的StringBuffer对象
StringBuffer str2 = new StringBuffer(7);
//str2容量为7个字符
StringBuffer(String)
创建一个StringBuffer对象,其内容初始化为字符串String,并增加16个字符的容量。
StringBuffer str3 = new StringBuffer("hello");
//字符串"hello"长度为5,则str3容量为5+16=21个字符
String和StringBuffer相互转换
在Java中,String类对象实例一旦被创建,它的内容就不能被改变。而 StringBuffer
类是可变的。因此,我们不能直接将String类实例直接转换为StringBuffer类实例,因为两者是两个完全不同的类。但你可以通过其他方法来实现这一转换。
String转StringBuffer
1、使用StringBuffer的构造器
String s="hello";
StringBuffer str = new StringBuffer(s);
但要注意这里str才是StringBuffer类对象,s仍是String类对象,未发生任何改变。
2、使用append方法
String s="hello";
//创建一个空的StringBuffer类对象
StringBuffer str1 = new StringBuffer();
//使用append方法将“s”赋给“str1”
str1=str1.append(s);
这并不是直接将 String 类转换为 StringBuffer 类,而是先创建一个空的 StringBuffer 类对象,再使用 append 方法将 String 的内容添加到其中。
StringBuffer转String
1、使用toString()方法
StringBuffer str=new StringBuffer("hello");
String s=str.toString();
2、使用String类的构造器
StringBuffer str=new StringBuffer("hello");
String s = new String(str);
StringBuffer类常用方法
其常用方法也离不开增、删、查、改四种基础方法
增:append()方法
向字符串的末尾追加指定的内容,可以接受不同类型的参数,并将它们转换为字符串表示形式后追加到缓冲区末尾:
StringBuffer str = new StringBuffer("hello");str.append(",world.");str.append("我").append(100).append(true).append(10.5);
最终str的值:hello,world.我100true10.5
删:delete()方法
删除字符串中指定位置的字符或指定范围内的字符。deleteCharAt(int index)方法用于删除指定索引处的字符,而delete(int start, int end)方法用于删除从 start 到 end 之间的字符。
StringBuffer str = new StringBuffer("0123456789");
str.deleteCharAt(8);//删除“8”,str为012345679
str.delete(2,5);//删除str[2]-str[4],str为015679
查:indexOf()方法
其使用方法等同于String类中的indexOf方法,如果找到指定字符或字符串,则返回首次出现的索引(索引从0开始),找不到则返回-1。同样的,也有lastIndexOf()方法:获取指定字符或字符串在字符串中最后一次出现的索引(索引从0开始),找不到则返回-1。
改:replace()方法
替换字符串中指定范围内的字符序列。replace(int start, int end, String str)方法用于将指定范围内的字符替换为新的字符串。
StringBuffer str = new StringBuffer("0123456789");
str.replace(2,4,"hello");//将[2,4)序列的字符替换为指定字符串
//str的值:01hello456789
str.replace(2,7,"23");//将[2,7)序列的字符替换为指定字符串
System.out.println(str);
//str的值:0123456789
如果方法内两int变量的值相同,则会直接在该序列后添加指定字符串,不会删除原字符串中的内容。
StringBuffer str = new StringBuffer("0123456789");
str.replace(2,2,"hello");//两int变量的值相同
//str的值:01hello23456789
插:insert()方法
在字符串的指定位置之前插入指定的内容,该位置以及之后的内容自动后移。
StringBuffer str = new StringBuffer("0123456789");
str.insert(2,"sss");//在“2”之前插入内容“sss”
//str的值:01sss23456789
判断长度:length()方法
返回字符串的当前长度,即其中包含的字符数。
StringBuilder类
StringBuilder是一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API ,但不保证线程安全问题。
该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单线程调用时,在单线程时应尽量使用 StringBuilder ,而多线程时则使用StringBuffer。
String | StringBuffer | StringBuilder | |
是否可变 | 不可变序列 | 可变序列 | 可变序列 |
效率 | 低 | 较高 | 最高 |
优点 | 内存复用率高 | 多线程安全 | 单线程效率高 |
内存服用率高:当我们创建多个内容相同的String对象时,系统只会在常量池中创建一个String对象,所以内存的复用率较高。
效率不同:当我们对String对象进行增删改等操作时,系统实际上会创建一个新的新的字符串来存放处理后的String对象,原本的String对象会被抛弃,但仍留着内存之中,因此效率较低;而 StringBuilder 和 StringBuffer 可以直接对原对象进行修改,所以效率较高。
线程安全:因为 StringBuffer 被 synchronized 关键字修饰,这意味着多个线程可以同时访问同一个 StringBuffer 对象而不会导致数据不一致的问题,而 StringBuilder 则可能会导致线程问题出现。