StringBuffer类
概述:线程安全的可变字符序列
线程安全与不安全:
不安全就是指存在同步操作同一数据的行为,效率高
实际开发过程中,效率和安全问题很难平衡
StringBuffer的特点:
1、线程安全,没有同步操作,效率低
2、StringBuffer是一个可变序列
3、StringBuffer又称之为字符串缓冲区,把它当作一个字符串去操作,只不过它与String相比是可以修改内容的
4、在任何时间点,它包含着一些特定的字符序列,但是可以通过某些方法修改这些字符序列的长度和内容
StringBffer与String的区别:
1、StringBuffer的长度和内容都可以发生改变,String不可以
2、String每创建一个新的字符串都会开辟一个新的常量池区域;StringBuffer会提前给出容量,可以进行字符串的拼接,而不会重新开辟空间。
StringBuffer的构造方法:
public StingBuffer():构造一个没有字符的字符串缓冲区,初始容量为16个字符
public StringBuffer(int capacity):构造一个没有字符的字符串缓冲区和指定的初始容量
public StringBuffer(String str):构造一个初始化内容为指定字符串内容的字符串缓冲区
这里引出:
public int capacity():返回当前容量。
public int length():返回长度(字符数)。
代码举例:
public class Test1 {
public static void main(String[] args) {
StringBuffer sb1=new StringBuffer();
System.out.println("sb1:"+sb1);
System.out.println("字符串sb1的容量为"+sb1.capacity());
System.out.println("字符串sb2的长度为:"+sb1.length());
StringBuffer sb2=new StringBuffer(40);
System.out.println("sb2:"+sb2);
System.out.println("字符串sb2的容量为:"+sb2.capacity());
System.out.println("字符串sb2的长度为:"+sb2.length());
StringBuffer sb3=new StringBuffer("hello");
System.out.println("sb3:"+sb3);
System.out.println("字符串sb3的容量为:"+sb3.capacity());
System.out.println("字符串sb3的长度为:"+sb3.length());
}
}
输出结果:
根据输出结果来看:
由于sb1,sb2以及sb3输出的都不是地址值,因此说明StringBuffer类中重写了toString方法
另外对于sb3的容量的解释:为初始化容量(16)+字符串内容容量(5)
StringBuffer中的各种功能:
StringBuffer的添加功能:
public StringBuffer append(String str):
通过观察API发现:不光可以追加字符串,还可以是任意数据类型的追加到StringBuffer中
返回的StringBuffer是字符串缓冲区本身
无论什么类型,追加到StringBuffer中全部变成了字符串
public StringBuffer insert(int index,String str):
将字符串str插入到此字符序列中,返回字符串缓冲区本身,按顺序插入到指定index的该序列中,向上移动原来位于该位置的任何字符,并将该序列的长度加到参数的长度。
代码举例:
public class Test2 {
public static void main(String[] args) {
StringBuffer s1=new StringBuffer();
StringBuffer s2 = s1.append("hello");
System.out.println(s1);//hello
System.out.println(s2);//hello
System.out.println(s1==s2);//true
// s1与s2比较结果为true进一步说明返回的是StringBuffer本身
s2.append(10);
s2.append('a');
s2.append(true);
s2.append(13.24);
System.out.println(s2);
// 无论什么类型都变成了字符串
// 这里结果为:hello10atrue13.24
// 链式编程追加
s2.append(10).append('a').append(true).append(13.24);
System.out.println(s2);
// 结果:hello10atrue13.2410atrue13.24
System.out.println("================================");
// 插入操作:
s2.insert(5,"HADOOP");
System.out.println(s2);
// 结果:helloHADOOP10atrue13.2410atrue13.24
// 如果插入的是null,四个字符'null'也会被插入到该序列中,但是不可以直接插入:
// s2.insert(5,null);
Object o=null;
s2.insert(5,o);
System.out.println(s2);
// 结果:hellonullHADOOP10atrue13.2410atrue13.24
}
}
StringBuffer的删除功能:
public StringBuffer deleteCharAt(int index):
删除指定索引处的字符,该序列缩短了一个char
注意:如果index为负数或者大于等于length(),运行时会报错。
index的值最大可以取到实际存储字符串的长度-1。
public StringBuffer delete(int start,int end):
删除此序列的子字符串中的字符,子字符串开始于指定start索引处并延伸到字符索引end-1,或者没有这样的子字符串序列存在,则结束。
注意:如果start等于end,则不做任何修改,且删除位置索引为:start<=index<end,索引end处的字符不会被删除,但是start处的字符会被删除。故start索引位置一定要是已经存在的索引,否则会报错。
代码举例:
public class Test3 {
public static void main(String[] args) {
StringBuffer sc1=new StringBuffer("bigdata");
System.out.println("删除前的字符串为:"+sc1);//bigdata
StringBuffer sc2 = sc1.deleteCharAt(2);
System.out.println("删除后的字符串为:"+sc2);//bidata
System.out.println(sc1==sc2);
// true说明删除操作的是同一个StringBuffer对象
// 删除指定子字符串
sc2.delete(1,4);
System.out.println("删除指定子字符串后为:"+sc2);
// 删除全部字符串
sc2.delete(0, sc1.length());
System.out.println("删除全部字符串后为:"+sc2);
// 也可以写成:
// sc2.delete(0,4)/sc2.delete(0,6)等
}
}
输出结果:
StringBuffer中的替换功能:
public StringBuffer replace(int start,int end,String str):
用指定Str中的字符替换此序列的子字符串中的String,子字符串开始于指定索引start处并延伸到字符索引end-1处,或者没有这样的子字符串的存在,则结束
第一子字符串的字符被删除,然后指定str被插入在start位置
代码举例:
public class StringBufferDemo4 {
public static void main(String[] args) {
//创建一个StringBuffer对象
StringBuffer sb = new StringBuffer();
//往StringBuffer中添加一些内容
sb.append("java").append("mysql").append("hadoop").append("hive");
System.out.println(sb);
//public StringBuffer replace(int start,int end,String str)
sb.replace(5,10,"你真好");
System.out.println(sb);
}
}
输出结果:
StringBuffer的反转功能:
public StringBuffer reverse():
导致该字符序列被序列的相反代替,如果序列中包含任何替代对,则将它们视为单个字符进行反向操作
代码举例:
public class StringBufferDemo5 {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("我爱恩知李");
System.out.println("反转之前:" + sb);
System.out.println("反转之后:");
StringBuffer sb2 = sb.reverse();
System.out.println("sb:"+sb);
System.out.println("sb2:"+sb2);
// 操作的是同一个StringBuffer对象
}
}
输出结果:
StringBuffer的截取功能:
public String substring(int start):
返回一个新的String,其中包含此字符序列当前包含的字符的子序列。
public String substring(int start,int end):
返回一个新的String,其中包含此序列中当前包含的字符的子序列。子字符串开始于指定的start索引,并扩展到索引end-1处。
两种截取不会影响原来的StringBuffer的数据,且返回值为String类型
代码举例:
public class StringBufferDemo6 {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("java").append("mysql").append("hive").append("ETL").append("spark");
System.out.println(sb);
String s1 = sb.substring(4);
System.out.println("截取的内容是:"+s1);
System.out.println("StringBuffer的内容为:"+sb);
String s2 = sb.substring(9, 13);
System.out.println("截取的内容是:"+s2);
System.out.println("StringBuffer的内容为:"+sb);
}
}
输出结果:
String与StringBuffer之间的转换
为什么要进行转换?
A转换成B,是为了使用B中的特有功能
B再转换成A,可能是引用最终的结果需要的是A类型的数据,所以还得转换回来
将String转换成StringBuffer:
1、通过StringBuffer的构造方法
2、通过StringBuffer的append方法将String类型数据追加到StringBuffer中。
将StringBuffer转换成String:
1、通过toString方法转换
2、通过截取功能substring方法,substring方法的返回值刚好是String类型
3、通过String的构造方法
代码举例:
/*
String与StringBuffer之间的转换
*/
public class Test4 {
public static void main(String[] args) {
String s = "hello";
// StringBuffer s1 = s;
// 这里不可直接使用StringBuffer接收String类型数据,会报错:
// java: 不兼容的类型: java.lang.String无法转换为java.lang.StringBuffer
System.out.println(s);
System.out.println("String转换为StringBuffer:");
// 1、通过StringBuffer的构造方法
StringBuffer s1 = new StringBuffer(s);
System.out.println("通过StringBuffer的构造方法转换:" + s1);
// 2、通过append方法:
StringBuffer s2 = new StringBuffer();
s2.append(s);
System.out.println("通过append方法转换:" + s2);
System.out.println("StringBuffer转换为String:");
StringBuffer sb = new StringBuffer("bigdata");
// 1、通过tostring方法
System.out.println(sb);
String sb1 = sb.toString();
System.out.println("通过tostring方法转换:"+sb1);
// 2、通过subString方法
String sb2=sb.substring(0);
System.out.println("通过subString方法转换:"+sb2);
// 3、通过String的构造方法
// String sb3 = new String();
String sb3 = new String(sb);
System.out.println("通过String的构造方法转换:"+sb3);
}
}
输出结果:
代码实现:
在API中有一个与StringBuffer几乎相同的类:StringBuilder,包括其中的构造方法与成员方法基本完全一样。
StringBuffer、StringBuilder、String之间的区别:
1、StringBuffer是线程同步安全的,数据安全,效率低;StringBuilder不是线程安全的,数据不安全,效率高。
2、String的内容不可以改变,StringBuffer与StringBuilder是可变序列。
3、StringBuffer方法上有synchronized关键字
StringBuffer与数组的区别:
他们都可以看成一个容器,存放一些数据。但是StringBuffer中的数据都是一个一个字符。
数组可以存放不同类型的数据,但是同一个数组中只能存放同一种数据类型的数据
探究String作为参数传递和StringBuffer作为参数传递的区别:
代码:
public class Test5 {
public static void main(String[] args) {
// String作为参数传递时:
String s1="hello";
String s2="world";
System.out.println("s1:"+s1+"\ts2:"+s2);
change(s1,s2);
System.out.println("s1:"+s1+"\ts2:"+s2);
System.out.println("============================");
// StringBuffer作为参数传递时;
StringBuffer sb1 = new StringBuffer("hello");
StringBuffer sb2 = new StringBuffer("world");
System.out.println("sb1:"+sb1+"\tsb2:"+sb2);
change(sb1,sb2);
System.out.println("sb1:"+sb1+"\tsb2:"+sb2);
}
public static void change(String s1,String s2){
s1=s2;
s2=s1+s2;
System.out.println("s1;"+s1+"\ts2:"+s2);
}
public static void change(StringBuffer sb1,StringBuffer sb2){
sb1=sb2;
sb2=sb1.append(sb2);
System.out.println("sb1:"+sb1+"\tsb2:"+sb2);
}
}
输出结果:
代码解读:
String中的初始化使变量s1与s2直接指向常量池的地址值访问常量
s1=s2:s2指向常量池的地址值赋给了s1,此时s1:world,s2:world
s2=s1+s2:将s1与s2做字符串拼接后在常量池中寻找是否有worldworld,没有开辟新空间存放worldworld。
方法调用完毕,从栈内存中消失,此时s1返回到指向hello,s2返回指向world。
StringBuffer使用构造方法初始化,在堆内存中开辟空间存放对象,使栈中的sb1指向堆中的地址0x01,堆中的地址0x01指向常量池中地址值为0x001的hello,sb2同理指向堆中的0x02,0x02指向地址值为0x002的world。
sb1=sb2:将堆中sb2的指向赋给sb1,
此时sb1----堆中0x02----常量池0x002"world",sb2----堆中0x02----常量池0x002"world"
sb2=sb1.append(sb2):sb1追加sb2赋值给sb2,同样在常量池中寻找,没有则开辟空间0x003,由于sb2指向的是堆中的地址,所以赋值操作更改了堆中的地址0x02为0x03,使其指向常量池中的0x003。
此时sb1----堆中0x03----常量池0x003"worldworld",sb2----堆中0x03----常量池0x003"worldworld"
方法调用结束,从栈中消失,sb1返回到指向0x01,sb2返回到指向0x02,此时堆中的0x02已经被更改为0x03所以sb2指向0x03
故最后一行输出为:sb1:hello,sb2:worldworld
StringBuffer作为参数传递图解:
总结:当多个StringBuffer作为参数进行传递时,谁发生了操作,谁就会受到影响。
例题:
使用StringBuffer实现把数组拼接成一个字符串
代码实现:
public class Test6 { public static void main(String[] args) { char[] chars={'h','e','l','l','o'}; // 创建一个空的StringBuffer对象 StringBuffer s = new StringBuffer(); // 使用for循环遍历数组每个字符利用append将其追加到s中 for(int i=0;i<chars.length;i++){ s.append(chars[i]); } // 将字符串缓冲区转换成字符串 String s2 = s.toString(); System.out.println(s2); } }
反转字符串:输入一个字符串,输出其反转字符串
代码实现:
import java.util.Scanner; public class Test7 { public static void main(String[] args) { Scanner sc =new Scanner(System.in); System.out.println("请输入要反转的字符串:"); String s = sc.next(); // 方式1:使用字符串拼接 String s1=""; // 将字符串转换成字符数组 char[] chars = s.toCharArray(); // 遍历数组反向获取字符 for(int i=chars.length-1;i>=0;i--){ s1+=chars[i]; } System.out.println("反转后的字符串为:"+s1); System.out.println("============================="); // 使用StringBuffer的反转功能 StringBuffer s2 = new StringBuffer(s); s2.reverse(); // 将StringBuffer类型转换回String类型 String s3 = s2.toString(); System.out.println("反转后的字符串为"+s3); System.out.println("=========使用链式编程============="); System.out.println(new StringBuffer(s).reverse().toString()); } }
判断一个字符串是否为对称字符串:例:abc不是对称字符串,mnanm是对称字符串
代码实现:
import java.util.Scanner; public class Test8 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入一个字符串:"); String s = sc.next(); // 方式1:一个一个去比较: char[] chars = s.toCharArray(); boolean flag = true; for (int start = 0, end = chars.length - 1; start <= end; start++, end--) { if (chars[start] != chars[end]) { System.out.println("该字符串不是对称字符串"); flag=false; break; } } if(flag){ System.out.println("该字符串是对称字符串"); } // 方式2:使用字符串反转比较: StringBuffer s2 = new StringBuffer(s); s2.reverse(); String s3 = s2.toString(); if(s3!=null){ if(s3.equals(s)){ System.out.println("该字符串是对称字符串"); } } } }
Arrays类
概述:Arrays类是针对于数组做操作的类,该类包含用于操作数组的各种方法(如排序和搜索)
Arrays类中的常用成员方法:
public static String toString(int[] a):将数组转换成一个字符串
public static void sort(int[] a):对数组进行排序操作
public static int binarySearch(int[] a,int key):对数组进行二分查找操作
代码举例:
import java.util.Arrays;
public class Test9 {
public static void main(String[] args) {
int[] arr={12,43,32,76,342,76,3,2,1};
String s = Arrays.toString(arr);
System.out.println(s);
System.out.println("---------------------------------------------------");
Arrays.sort(arr);
System.out.println("排序后的数组为:"+Arrays.toString(arr));
System.out.println("---------------------------------------------------");
int index = Arrays.binarySearch(arr, 43);
int index2 = Arrays.binarySearch(arr, 100);
System.out.println("进行二分查找43的结果:"+index);
System.out.println("进行二分查找100的结果:"+index2);
}
}
输出结果:
由于进行二分查找的前提是有序,所以在进行binarySearch()方法前必须要先排序。
对于二分查找的结果需要观察binarySearch()方法的源码:
binarySearch()的源码:
private static int binarySearch0(int[] a, int fromIndex, int toIndex,
int key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = a[mid];
if (midVal < key)
low = mid + 1;
else if (midVal > key)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found.
}
观察源码,大致同二分查找相同,但若没有要寻找的字符,返回值是- (low + 1)。
日期类Date
顾名思义,该类是针对日期及时间进行操作的类
日期类的构造方法:
Date(): 分配一个 Date对象,并初始化它,以便它代表它被分配的时间,测量到最近的毫秒。
但是日期类输出的时间不是常见形式的时间,是系统时间的顺序:周 月 日 时 分 秒 上下午 年
想要让它的输出结果是我们平时所见到的顺序,需要另外一个类:
SimpleDateFormat(String pattern):
日期格式化,使用给定模式 SimpleDateFormat并使用默认的 FORMAT语言环境的默认日期格式符号。
API中的常用格式:
代码举例:
public class DateDemo {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
// 使用SimpleDateFormet进行日期格式化
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd EEE a hh:mm:ss ");
String s = simpleDateFormat.format(date);
System.out.println(s);
}
}
输出结果:
结果中第一行为系统时间,第二行为格式化后的时间
常见格式化格式:
yyyy:年; MM:月 ; dd:日 ; HH:24小时制度; hh:12小时制度 ; mm:表示分钟 ; ss:表示秒;
Math类:
概述:Math类包含执行基本数字运算的方法,如基本指数,对数,平方根和三角函数。
Math类中的成员方法:
public static int abs(int a):绝对值
public static double ceil(double a):向上取整
public static double floor(double a):向下取整
public static int max(int a,int b):取两个int类型的值中的最大值
public static int min(int a,int b):取两个int类型的值中的最小值
public static double pow(double a,double b):幂运算,a的b次幂
public static double random():获取一个随机数生成器
public static int round(float a):四舍五入
public static double sqrt(double a):正平方根运算
代码举例:
public class MathDemo {
public static void main(String[] args) {
// 绝对值
int num1 = Math.abs(-10);
System.out.println("绝对值:"+num1);
//public static double ceil(double a)
double num2 = Math.ceil(12.04);
System.out.println("向上取整:"+num2);
//floor
double num3 = Math.floor(12.84);
System.out.println("向下取整:"+num3);
// max:取最大值
int num4=Math.max(4,8);
System.out.println("最大值:"+num4);
// min:取最小值
int num5=Math.min(4,8);
System.out.println("最小值:"+num5);
// pow:幂运算
double num6=Math.pow(2,3);
System.out.println("幂运算:"+num6);
// 四舍五入
int num7= (int) Math.round(12.56);
System.out.println("四舍五入:"+num7);
//正平方根
double num9 = Math.sqrt(3);
System.out.println("平方根:"+num4);
}
}
输出结果:
对于random方法:引入RanDom类
RanDom类:
对于生成的随机数进行输出,使用random.nextInt()方法:返回下一个随机数,int类型
代码举例:
public class RandomDemo {
public static void main(String[] args) {
//public Random()
Random random = new Random();
// int i = random.nextInt();
// System.out.println(i);
int i = random.nextInt(10)+1; // 0-9
System.out.println(i);
//public Random(long seed)
Random random1 = new Random(10000000000L);
int j=random1.nextInt();
System.out.println(j);
}
}
输出结果:在1到10的范围内和在一百亿的范围内分别生成一个随机数,不包括一百亿。
包装类:
需求1:有100这个数据,计算出它的二进制,八进制,十六进制
需求2:如何使用代码求出int类型数据范围
通过观察需求后发现,我们得出原本的基本数据类型无法调用任何方法和属性
为了对基本数据类型进行更多的操作,更方便的操作,Java就针对每一个基本数据类型都提供了一个对应的类类型。 我们称之为为包装类类型。
包装类类型:byte----Byte;short----Short;int----Integer;long----Long;float----Float;double----Double;char----Character;boolean----Boolean
Integer类中有:
public static String toBinaryString(int i):
在基数2中返回整数参数的字符串表示形式为无符号整数,即求2进制数
public static String toHaxString(int i):
返回整数参数的字符串表示形式,作为16位中的无符号整数,即求16进制数
public static Sring toOctalString(int i):
在基数8中返回整数参数的字符串表示形式为无符号整数,即求8进制数
static int MAX_VALUE:持有最大值一个 int
可以有2^31 -1。
static int MIN_VALUE:持有最小值一个 int
可以有-2^31 -1
代码举例:
public class Test10 {
public static void main(String[] args) {
String s=Integer.toBinaryString(101);
System.out.println("101的二进制数:"+s);
String s2=Integer.toHexString(101);
System.out.println("101的十六进制数:"+s2);
String s3=Integer.toOctalString(101);
System.out.println("101的八进制数:"+s3);
int maxvalue=Integer.MAX_VALUE;
int minvalue=Integer.MIN_VALUE;
System.out.println("int类型的最大值为:"+maxvalue);
System.out.println("int类型的最小值为:"+minvalue);
}
}
输出结果:
包装类一般是用于基本数据类型与字符串之间做转换
int类型的数据与String类型做互相转换
int -- String
static String valueOf(int i) :返回 int参数的字符串 int形式。
String -- Integer -- int
public static int parseInt(String s):将字符串参数解析为带符号的十进制整数。
代码举例:
public class PackageClassDemo2 {
public static void main(String[] args) {
//int -- String
int num = 100;
//方式1:static String valueOf(int i) 返回 int参数的字符串 int形式。
String s = String.valueOf(num); // 100 -->"100"
System.out.println(s);
System.out.println("==================================");
//方式2:int -- Integer -- String
// String string = new String(num);
//Integer(int value) 构造一个新分配的 Integer对象,该对象表示指定的 int值。
Integer integer = new Integer(num);
System.out.println(integer); // Integer重写了toString方法
String s1 = integer.toString();
System.out.println(s1);
//方式3:字符串拼接
String s2 = ""+num;
System.out.println(s2);
//方式4:public static String toString(int i)
String s3 = Integer.toString(100);
System.out.println(s3);
System.out.println("==========================");
//String-->int
String s4 = "200";
// String s4 = "你好";
//方式1:String--Integer--int
//Integer(String s)
//构造一个新分配 Integer对象,表示 int由指示值 String参数。
Integer integer1 = new Integer(s4);
System.out.println(integer1);
//public int intValue()将 Integer的值作为 int 。
int number = integer1.intValue();
System.out.println(number);
int i = integer1; //在包装类中称之为自动拆箱
Integer i2 = 300; //在包装类中称之为自动装箱
}
}
该输出结果相同,但是区别在于数据类型不同