常用类(中)
一、StringBuffer类
(一)概述
我们如果对字符串进行拼接操作,每次拼接,都会构建一个新的String对象,既耗时,又浪费空间。而StringBuffer就可以解决这个问题,当对字符串进行修改的时候,需要使用 StringBuffer类(或StringBuilder类)。StringBuffer是线程安全的可变字符序列。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
(二)StringBuffer和String的区别
1.相同点
String和StringBuffer他们都可以存储和操作字符串,即包含多个字符的字符串数据。
2.不同点
- String类是字符串常量,是不可更改的常量,改变的只是其内存地址的指向。而StringBuffer是使用缓冲区的,是字符串变量,它的对象是可以扩充和修改的。
- 对于StringBuffer而言,本身是一个具体的操作类,所以不能像String那样采用直接赋值的方式进行对象实例化,必须通过构造方法完成。
(三)构造方法
public StringBuffer()
:无参构造方法
public StringBuffer(int capacity)
: 指定容量的字符串缓冲区对象
public StringBuffer(String str)
: 指定字符串内容的字符串缓冲区对象
(四)常用方法
public int capacity()
:返回当前容量。 理论值
public int length()
: 返回长度(字符数)。 实际值
(五)添加功能
public StringBuffer append(String str)
: 可以把字符串(任意基本类型数据都可以)添加到字符串缓冲区里面,并返回字符串缓冲区本身
public StringBuffer insert(int offset,String str)
:在指定位置把字符串(任意基本类型的数据都可以)插入到字符串缓冲区里面,并返回字符串缓冲区本身
案例演示1
public class MyTest {
public static void main(String[] args) {
//StringBuffer()无参构造
//构造一个其中不带字符的字符串缓冲区,其初始容量为 16 个字符。
//创建一个字符串缓冲区
StringBuffer sb = new StringBuffer();
//获取字符串缓冲区的容量
int capacity = sb.capacity();
System.out.println(capacity);//16
//获取字符串缓冲区的长度
int length = sb.length();
System.out.println(length);//由于字符串缓冲区内此时没有字符串,长度为0
// 往字符串缓冲区中追加内容
StringBuffer sb2 = sb.append(100); //往字符串缓冲区中追加了一个int类型的数据,返回的还是字符串缓冲区本身
System.out.println(sb==sb2);//判断是否是同一个缓冲区
System.out.println(sb.capacity());
System.out.println(sb.length());
sb.append("abdefafdasfasdf").append("sdfsdfsfdf");//链式编程
System.out.println(sb.capacity());
System.out.println(sb.length());
System.out.println(sb);//等价于sb.toString(),查看字符串缓冲区里的内容
}
}
字符串缓冲区(容器)容量大小的说明:
如果长度小于16则默认容器的大小为16。如果大于16则会调用expandCapacity()
进行容量的扩展。扩展的规则是把旧的容量(capacity大小)*2+2,然后与容器中字符串长度比较,如果新的容量大于长度,则使用新的容量,如果容量还是小于长度,则继续扩展。
所以案例中长度大于16则会直接扩展到34——(16*2)+2。如果长度大于34,则继续扩展到70——(34*2)+2
案例演示2
public class MyTest {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append(100).append(true).append("abv").append("cccc");
sb.append(new char[]{'a','c','d'});//可以把任意类型数据添加到字符串缓冲区里面
sb.append(new Object());//添加的是Object对象地址转成的字符串值
System.out.println(sb);
//StringBuffer(String str)
//构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。
StringBuffer sb1 = new StringBuffer("abc");
System.out.println(sb1);
}
}
案例演示3
public class MyTest {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("I love ").append("Java");
//在指定的索引前插入内容
sb.insert(2, "don't ");//返回的还是原来的容器
System.out.println(sb);
}
}
(六)删除功能
public StringBuffer deleteCharAt(int index)
:删除指定位置的字符,并返回字符缓冲区本身
public StringBuffer delete(int start,int end)
:删除从指定位置开始到指定位置结束的内容,并返回字符缓冲区本身
案例演示
public class MyTest {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("hello world");
//根据指定索引删除单个字符,返回的还是容器本身
sb.deleteCharAt(0);
System.out.println(sb);
StringBuffer sb2 = new StringBuffer("hello world");
//根据开始索引,和结束索引删除一段数据,含头不含尾,返回的还是容器本身
sb2.delete(0, 6);
System.out.println(sb2);
}
}
(七)替换和反转功能
1.替换功能
public StringBuffer replace(int start,int end,String str)
: 从start开始到end用str替换,返回的还是字符串缓冲区本身
2.反转功能
public StringBuffer reverse()
:字符串反转,返回的还是字符串缓冲区本身
案例演示
public class MyTest {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("hello world");
//从开始索引,到结束索引(含头不含尾),替换成新的内容,返回的还是容器本身
sb.replace(0, 5, "goodbye");
System.out.println(sb);
StringBuffer sb1 = new StringBuffer("abcd");
//反转容器中的内容,返回的还是容器本身
sb1.reverse();
System.out.println(sb1);
}
}
(八)检索功能
和String的检索功能不同的是,参数只有String类型的。
int indexOf(String str)
: 返回第一次出现的指定子字符串在该字符串中的索引。
int indexOf(String str, int fromIndex)
: 从指定的索引处开始,返回第一次出现的指定子字符串在该字符串中的索引。
int lastIndexOf(String str)
: 返回最后一次出现的指定子字符串在此字符串中的索引。
int lastIndexOf(String str, int fromIndex)
: 返回指定字符串在此字符串中从开头到指定位置中最后一次出现处的索引。
案例演示
public class MyTest {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("七月的风,八月的雨");
//int indexOf(String str): 返回第一次出现的指定子字符串在该字符串中的索引。
int index=sb.indexOf("月");
System.out.println(index);
//int indexOf(String str, int fromIndex): 从指定的索引处开始,返回第一次出现的指定子字符串在该字符串中的索引。
index = sb.indexOf("月",sb.indexOf("月")+1);
System.out.println(index);
//int lastIndexOf(String str): 返回最后一次出现的指定子字符串在此字符串中的索引。
index=sb.lastIndexOf("的");
System.out.println(index);
//int lastIndexOf(String str, int fromIndex): 返回指定字符串在此字符串中从开头到指定位置中最后一次出现处的索引。
index=sb.lastIndexOf("的",5);
System.out.println(index);
}
}
(九)截取功能
public String substring(int start)
:从指定位置截取到末尾
public String substring(int start,int end)
: 从指定位置开始截取到结束位置(含头不含尾)
注意事项
返回值类型不再是StringBuffer本身,而是String类型
案例演示
public class MyTest {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("我想要带你去浪漫的土耳其,还要一起去东京和巴黎");
//从指定的索引处,截取到末尾,返回值是截取到的字符串
String s = sb.substring(0);
System.out.println(s);
String s1 = sb.substring(0, sb.indexOf("其")+1); //含头不含尾
System.out.println(s1);
}
}
(十)StringBuffer和String的相互转换
public class MyTest {
public static void main(String[] args) {
//String类对象转换为StringBuffer类对象
String str="abc";
//方式1:StringBuffer构造方法
StringBuffer sb = new StringBuffer(str);
//方式2:append()
StringBuffer sb2 = new StringBuffer().append(str);
//方式3:insert()
StringBuffer sb3 = new StringBuffer().insert(0, str);
//StringBuffer类对象转换为String类对象
StringBuffer sb4= new StringBuffer("abc");
//方式1:toString()
String string = sb4.toString();
//方式2:substring()
String substring = sb4.substring(0);
//方式3:String构造方法
//String(StringBuffer buffer)分配一个新的字符串,它包含字符串缓冲区参数中当前包含的字符序列。
String s = new String(sb4);
}
}
练习题1
需求:把数组中的数据按照指定个格式拼接成一个字符串
举例:
int[] arr = {1,2,3};
输出结果:
“[1, 2, 3]”
用StringBuffer的功能实现
public class MyTest {
public static void main(String[] args) {
int[] arr={1,2,3};
StringBuffer sb = new StringBuffer("[");
for (int i = 0; i < arr.length; i++) {
if(i==arr.length-1){
sb.append(arr[i]).append("]");
}else{
sb.append(arr[i]).append(",");
}
}
System.out.println(sb);
}
}
练习题2
需求:把字符串反转
举例:键盘录入"abc"
输出结果:“cba”
用StringBuffer的功能实现
public class MyTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String s = sc.nextLine();
StringBuffer sb = new StringBuffer(s);
sb.reverse();
System.out.println("反转后:"+sb);
}
}
(十一)StringBuffer和StringBuilder的区别
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
除此之外,其他的使用方法都和StringBuffer相同。
(十二)String和StringBuffer分别作为参数传递
参数传递有两种,分别是值传递和引用传递。
- 值传递,即方法操作的是参数变量(也就是原型变量的一个值的拷贝)改变的也只是原型变量的一个拷贝而已,而非变量本身。所以变量原型并不会随之改变。
- 引用传递,也叫做传址,即当方法传入的参数为非基本类型时(也就是说是一个对象类型的变量),方法改变参数变量的同时变量原型也会随之改变。
基本类型作为参数传递,是值传递;引用类型作为参数传递,通常是引用传递。
我们来看个例子:
public class MyTest {
public static void main(String[] args) {
//基本类型作为参数传递,形参的改变,不影响实参
//引用类型作为参数传递,形参的改变,会影响实参
//String 类型虽是引用类型,但作为参数传递,跟基本类型一样
String str = "abc";
changeString(str);
System.out.println(str);
//StringBuffer 类型作为参数传递,会影响实参
StringBuffer sb = new StringBuffer("你好世界");
changeStringBuffer(sb);
System.out.println(sb);
}
private static void changeStringBuffer(StringBuffer sb) {
sb.reverse();
}
private static void changeString(String str) {
str = str + "dddd";
}
}
String类型在Java语言中属于引用类型啊!它在方法中的改变为什么没有被保存下来呢?
解释:我们通过查看String类的源码发现,String的存储实际上通过char[]来实现的,那么String就可以看作是是char[]的包装类。包装类的特质之一就是在对其值进行操作时会体现出其对应的基本类型的性质。因此,String类型作为参数传递时,和基本类型作为参数传递的效果是一样的。
二、二分查找
(一)原理
前提:数组有序
首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
(二)代码实现
public class MyTest {
public static void main(String[] args) {
//二分查找的前提:数组元素必须有序
int[] a={-1,0,1,1,5,5,10,13,17,20,30,42,46,55,56,70,94};
//找到a数组17所在的索引
int index = findIndex(a, 17);
System.out.println(index);
}
private static int findIndex(int[] a,int b){
int startIndex=0;
int endIndex=a.length-1;
int middleIndex=(startIndex+endIndex)/2;
while(startIndex<=endIndex){
if(b==a[middleIndex]){
return middleIndex;
}else if(b<a[middleIndex]){
endIndex=middleIndex-1;
middleIndex=(startIndex+endIndex)/2;//重新计算中间索引
}else if(b>a[middleIndex]){
startIndex=middleIndex+1;
middleIndex=(startIndex+endIndex)/2;//重新计算中间索引
}
}
return -1;//如果没找到,返回-1
}
}
三、Arrays类
(一)概述
它是针对数组进行操作的工具类。
提供了排序,查找等功能。
使用时导包:import java.util.Arrays
(二)成员方法
都是静态方法,直接拿类名来调用:
public static String toString(int[] a)
将数组拼成字符串形式
public static void sort(int[] a)
将int型数组按升序进行排序
public static void sort(int[] a, int fromIndex, int toIndex)
将int型数组的指定范围按升序进行排序
public static int binarySearch(int[] a,int key)
二分查找
public static boolean equals (int[] a, int[] a2)
如果两个指定的 int 型数组彼此相等,则返回 true
public static void fill (int[] a, int val)
将指定的 int 值分配给指定 int 型数组的每个元素
public static void fill (int[] a, int fromIndex, int toIndex, int val)
将指定的 int 值分配给指定 int 型数组指定范围中的每个元素
public static int[] copyOf(int[] original, int newLength)
复制指定的数组,截取或用 0 填充(如有必要),以使副本具有指定的长度。
案例演示1
通过Arrays类的功能来进排序和二分查找
import java.util.Arrays;
public class MyTest {
public static void main(String[] args) {
int[] a = {46, 55, 13, 42, 17, 94, 5, 70, 10, 30, 56, 1, 0, 20, 1, 5, -1};
Arrays.sort(a,7,a.length);//对7索引处到结尾这一段进行排序
System.out.println(Arrays.toString(a));//将数组拼成字符串形式
Arrays.sort(a);//对整个数组进行排序
System.out.println(Arrays.toString(a));
//此时数组已经有序,满足二分查找的前提。
int i = Arrays.binarySearch(a, 56);//二分查找
System.out.println(i);
}
}
案例演示2
import java.util.Arrays;
public class MyTest {
public static void main(String[] args) {
int[] a = {46, 55, 13, 42, 17, 94, 5, 70, 10, 30, 56, 1, 0, 20, 1, 5, -1};
int[] b = {46, 55, 13, 42, 17, 94, 5, 70, 10, 30, 56, 1, 0, 20, 1, 5, -1};
//static boolean equals (int[] a, int[] a2)
boolean equals = Arrays.equals(a, b);
System.out.println(equals);
//static void fill ( int[] a, int val)
Arrays.fill(a,0);
System.out.println(Arrays.toString(a));
//static void fill ( int[] a, int fromIndex, int toIndex, int val)
Arrays.fill(b,5,8,0);
System.out.println(Arrays.toString(b));
//public static int[] copyOf(int[] original, int newLength)
int[] ints = Arrays.copyOf(b, 5);//复制b数组前5个元素
System.out.println(Arrays.toString(ints));
}
}
四、Integer类
(一)包装类
在讲Integer类之前就要介绍一下包装类
-
为什么会有基本类型包装类?
为了对基本数据类型进行更多更方便的操作,Java就针对每一种基本数据类型提供了对应的包装类类型。 -
基本数据类型和对应的包装类
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
(二)Integer类的概述和构造方法
假如给你一个很大的整型数据,你能实现判断这个数据是否在int范围内吗,并将这个数据转化为二进制、八进制、十六进制形式。
这时,包装类的好处便体现出来了。
Integer类中提供的常量和方法:
static int MAX_VALUE
值为 231-1 的常量,它表示 int 类型能够表示的最大值。
static int MIN_VALUE
值为 -231 的常量,它表示 int 类型能够表示的最小值。
static String toBinaryString(int i)
返回一个整数参数的二进制无符号整数的字符串表示形式
static String toOctalString(int i)
返回一个整数参数的八进制无符号整数的字符串表示形式
static String toHexString(int i)
返回一个整数参数的十六进制无符号整数的字符串表示形式
public class MyTest {
public static void main(String[] args) {
int num=100000000;
boolean b= num>=Integer.MIN_VALUE&&num<=Integer.MAX_VALUE?true:false;
System.out.println(b);
String s = Integer.toBinaryString(num);
String s1 = Integer.toOctalString(num);
String s2 = Integer.toHexString(num);
System.out.println(s);
System.out.println(s1);
System.out.println(s2);
}
}
1.概述
Integer 类在对象中包装了一个基本类型 int 的值,该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还提供了处理 int 类型时非常有用的其他一些常量和方法。
2.构造方法
public Integer(int value)
将一个int型的值构造成一个Integer对象
public Integer(String s)
将一个字面值为数字的字符串构造成一个Integer对象
案例演示
public class MyTest {
public static void main(String[] args) {
int num=120;
//将一个int型的值构造成一个Integer对象
Integer i1 = new Integer(num);
//将一个字面值为数字的字符串构造成一个Integer对象
Integer i2 = new Integer("abc");//会报错!NumberFormatException 数字格式异常
//该构造方法要的是字面值为数字的字符串
Integer i3 = new Integer("121");
}
}
(三)String和int类型的相互转换
Integer中的成员方法:
String toString ()
返回表示该 Integer 对象的字符串对象。
static String toString (int i)
返回一个表示指定整数的字符串对象。
int intValue ()
以 int 类型返回该 Integer 对象的值。
static int parseInt (String s)
返回字符串参数代表的有符号的十进制整数
案例演示
public class MyTest {
public static void main(String[] args) {
int num=100;
String s="50";
//int类型转换为String类型
//方式1(和""进行拼接)
String ss=num+"";
System.out.println(ss);
//方式2(String类的valueOf方法)
String s1 = String.valueOf(num);
System.out.println(s1);
//方式3(重写的toString方法)
Integer i1 = new Integer(num);
String s2 = i1.toString();
System.out.println(s2);
//方式4(静态的toString方法)
String s3 = Integer.toString(num);
System.out.println(s3);
//String类型转换为int类型
//方式1(String->Integer->int)
Integer i2 = new Integer(s);
int a = i2.intValue();
System.out.println(a);
//方式2(静态方法parseInt)
int b = Integer.parseInt(s);
System.out.println(b);
}
}
(四)自动装箱与自动拆箱
-
JDK5的新特性:
自动装箱:把基本类型转换为包装类类型
自动拆箱:把包装类类型转换为基本类型 -
手动装箱:
static Integer valueOf ( int i)
返回一个表示指定的 int 值的 Integer 对象。
static Integer valueOf (String s)
返回一个表示指定的 String 的值的 Integer 对象。
还有Integer的构造方法 -
手动拆箱:
int intValue ()
以 int 类型返回该 Integer 对象的值。
案例演示
public class MyTest {
public static void main(String[] args) {
int num=50;
//自动装箱
Integer i1=100;//通过将int型数据直接赋值给Integer对象的方式为自动装箱
//自动拆箱
int sum=num+i1;//将Integer类型自动拆箱为int类型后与int类型进行运算
System.out.println(sum);
i1+=200;//这段代码既有自动拆箱又有自动装箱
//手动装箱
//方式1(构造方法)
Integer i2 = new Integer(num);
//方式2(静态的valueOf方法)
Integer i3 = Integer.valueOf(num);
Integer i4 = Integer.valueOf("50");
//手动拆箱(intValue方法)
int sum1 = i3.intValue()+num;
System.out.println(sum1);
}
}
注意事项
在使用时,Integer x = null;代码就会出现NullPointerException(空指针异常)。
建议先判断是否为null,然后再使用。
思考题
看程序写结果
public class MyTest {
public static void main(String[] args) {
Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1 == i2);
System.out.println(i1.equals(i2));
System.out.println("-----------");
Integer i3 = new Integer(128);
Integer i4 = new Integer(128);
System.out.println(i3 == i4);
System.out.println(i3.equals(i4));
System.out.println("-----------");
Integer i5 = 128;
Integer i6 = 128;
System.out.println(i5 == i6);//false 因为 超过了一个字节的范围 会new 一个Integer对象
System.out.println(i5.equals(i6));
System.out.println("-----------");
Integer i7 = 127;
Integer i8 = 127;
System.out.println(i7 == i8);//true 没有超过一个字节的范围 因为在方法区中存在一个 字节常量池 范围-128---127
System.out.println(i7.equals(i8));
}
}
解析:
- 当通过构造方法创建Integer对象时,每new一次都是不同的对象,因此将两个Integer的对象用==号进行比较,结果一定是false。
- 而Integer重写了父类的equals方法,比较的是值是否相等。
- 由于Integer直接赋值的时候会进行自动的装箱,那么这里就需要注意两个问题,Integer中有一个缓存类IntegerCache,其中默认缓存了范围为-128<= x<=127的值,那么当直接赋值在这个区间的值的时候,不会创建新的Integer对象,而是从缓存中获取已经创建好的Integer对象,只要在这个范围内,无论这个值赋值几次,取的都是同一个对象,因此i7==i8;当赋的值大于这个范围的时候,才通过构造方法来创建新的Integer对象,因此i5不等于i6。
- new Integer(127) 和Integer a = 127也会不相等,前者会创建对象,存储在堆中,而后者因为在-128到127的范围内,不会创建新的对象,而是从IntegerCache中获取的。那么Integer a = 128, 大于该范围,才会直接通过new Integer(128)创建对象,进行手动装箱。
其实,Integer自动装箱是调用了valueOf的代码:
从源码中的if中的代码能看到,i>=IntegerCache.low&&i<=IntegerCache.high
的时候,从IntegerCache中获取实例,否则,直接new Integer(i);
掌握了包装类中的Integer类,其他基本数据类型的包装类也就一通百通了:
public class MyTest {
public static void main(String[] args) {
//构造方法
boolean b=true;
Boolean a = new Boolean(b);
String str="false";
Boolean a1 = new Boolean(str);
//自动装箱
Boolean a2=true;
//手动拆箱
boolean b1 = a.booleanValue();
//手动装箱
Boolean a3 = Boolean.valueOf(true);
//String类型转换为boolean类型
String str2 = "false";
boolean b2 = Boolean.parseBoolean(str2);
}
}