第十三章
常用类
13.1 包装类
13.1.1、包装类(wrapper)的分类:
①针对八种基本数据类型相应的引用类型
②有了类的特点,就可以调用类中的方法
查看每一个的类的父类,从中就可以知道哪些类可以被调用:
13.1.2 包装类和基本数据的转换
1、在jdk5之前用的是手动的装箱和拆箱的方式,装箱就是:基本类型 -> 包装类型,反之就是拆箱
2、在jadk5(包含jdk5)以后,就是用自动的装箱和拆箱方式了但是底层源码还是调用手动的 valueOf() 方法
我们通过 int 和 Integer 的转换为案例来演示:
public class WrapperType {
public static void main(String[] args) {
int n1 = 50;
//jdk5前 手动装箱:
Integer integer1 = new Integer(n1);//相当于就是创建一个Integer类对象,然后传入int型的变量参数
Integer integer2 = Integer.valueOf(n1);//使用valueOf()方法去装箱
//jdk5前 手动拆箱:
int n2 = integer2.intValue();//使用intValue()方法去拆箱,如果拆箱别的,就是 类对象名.数据类型Value
//jdk5后 自动装箱
Integer integer3 = n1;//其实底层调用的还是手动装箱的valueOf()方法
//jdk5后 自动拆箱
int n3 = integer3;//其实底层调用的还是手动的 intValue()方法,
}
}
对于其他的类型,都可以去自己类比尝试去转换试试,注意,byte是字节哦,也就是说是一个字节数组对象哈
测试:
A01:判断下列代码是否正确
Double d = 100d;
Float f = 1.5;
//课堂测试A01:在main中
Double d = 100d;//true,因为这里自动的调用了手动装箱中的Double.valueOf(100d)方法,换句话就是100d已经是一个对象了
Float f = 1.5f;//false,因为1.5是一个基本数据类型,并没有被转换成类对象,所以会报错,如果是 1.5f 则就是对的
A02:判断下列代码输出是否一致:
//课堂测试A02:在main中
Object obj1 = true ? new Integer(1) : new Double(2.0);
//这里是一个三元运算符,因为Integer和Double都是Object的子类,
// 但是呢,Integer在三元运算符中的前一位判断中,那么最后对象是建立的Integer对象,注意提高优先级
System.out.println(obj1);//输出Integer对象:1.0,不是 1,原因在下面注意地方
Object obj2;//定义一个Object类
if (true){//如果正确,那么创建Integer对象
obj2 = new Integer(1);
}else {//如果错误,那么创建Double对象
obj2 = new Double(2.0);
}
System.out.println(obj2);//Integer对象:1,不会提高优先级
注意:在做三元运算符的时候,要把整个三元运算符看作一个整体, 如果两个的精度不一致,会提高精度,无论输出是谁,但是精度必须是两者中最高的,
精度:double > float > long > int >char
13.1.3 包装类和String数据的转换
以 Integer 和 String 作为案例:
public class WrapperString {
public static void main(String[] args) {
//1、包装类(Integer -> String)
Integer i = 10;
//方法一:
String str1 = i +" ";
//这里至少基于i的数字去加上一个空格去组成“1“,然后去赋值给一个str1对象,对于i的数据类型是没有改变的
//方法二:
String s = i.toString();//因为包装类基本上都有toString方法,所以可以直接调用
//方法三:
String s1 = String.valueOf(i);//使用valueOf方法
//2、String -> 包装类(Integer)
String str2 = "123";//注意是必须能够转换的
//方法一:
Integer integer1 = Integer.parseInt(str2);//使用了自动装箱
//方法二:
Integer integer2 = Integer.valueOf(str2);//使用valueOf方法
//方法三:
Integer integer = new Integer(str2);//直接new一个Integer对象,通过构造器去转换
}
}
13.1.4 Integer和Character类的常用方法
1、Integer:
2、String:
可以通过以下方法去查看不同类的方法,这些方法都是可以直接 lei类型 . 方法名用的,要用啥就去查就行
步骤一:
步骤二:
方法图:
13.1.5 Integer 经典面试题
阅读以下代码,看看输出什么
public class IntegerExercise01 {
public static void main(String[] args) {
method();
}
public static void method(){
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);//false,两个对象,肯定不同,所有false
Integer m = 1;//装箱,使用了 Integer.valueOf(1)
Integer n = 1;//装箱,使用了 Integer.valueOf(1) 1在-128~127之间,所以没问题
System.out.println(m == n);//ture,所以相同的,不能直接自己去判断,要进入底层源码去查看是否一致
/*查看Integer.valueOf( )方法的底层源码:
//这是源码方法上面提示中的代码,显示了i的取值范围
This method will always cache values in the range -128 to 127,
public static Integer valueOf(int i) {//功能方法
if (i >= Integer.IntegerCache.low && i <= Integer.IntegerCache.high)
//判断 i 的值是否在 最小值(low:-128)、最大值(high:127)之间,如果在那么就是传入数组返回
return Integer.IntegerCache.cache[i + (-Integer.IntegerCache.low)];
return new Integer(i);
//如果不在最小值(low:-128)、最大值(high:127)之间,那么就是返回一个Integer对象
}*/
Integer x = 128;//128 不在最小值(low:-128)、最大值(high:127)之间,所以是返回的对象
Integer y = 128;//128 不在最小值(low:-128)、最大值(high:127)之间,所以是返回的对象
System.out.println(x == y);//false,因为是两个对象,所以不相等,
Integer i1 = 127;
int i2 = 127;//基本数据类型
// 注意:只要有基本数据类型,不管其他的是什么类型,都是判断值是否相等
System.out.println(i1 == i2);
}
输出:
false
true
false
true
13.2 String类 !!!
13.2.1String类的理解和创建对象
1、String对象用于保存字符串,也就是一组字符序列
实现接口: 继承Object类
2、字符串常量对象是用双引号括起的字符序列,例如 ”123“,”小王“
3、字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)都是占两个字节
4、String类常用的构造器:
所有构造器可以在diagram图里点击这个可查看:
5、String类实现了Serializble接口,说明String可以串行化,也就是可以在网络中传输
6、String类实现了Comparable接口,说明String对象可以比较大小
7、String 是 final 类,是不能被其他类继承的
8、String 有属性:private final char value[ ];用于存放字符串内容,相当于一个字符串数组,注意:因为 value 是 final 类型的,所以不能修改(是不能修改value的地址,可以修改地址里面的值
13.2.2 String对象的创建方法
方式一:直接赋值
String s = ” hsp “;
分析:
先在常量池中去查看是否有 ”hsp“ 的数据空间,如果有 s 直接指向常量池中数据的地址,如果没有,就在常量池中去创建一个数据空间,然后再指向,总之就是:直接赋值的 s 最终指向是指向常量池的
方式二:调用构造器
String s2 = new String( ” hsp “ )
分析:
先在堆中创建空间,空间里面有维护 final修饰的 value属性,value属性指向常量池中的数据空间,如果没有数据空间,那么就创建一个,如果有,就通过value去指向常量池中的数据空间地址,然后 S2 对象再去指向value属性的堆地址,
两种方法的内存分布图:
13.2.3 String练习题
A01:分析下列代码的输出:
public class String02 {
public static void main(String[] args) {
String a ="123";
String b ="123";
System.out.println(a.equals(b));//TRUE,equals是判断d,b数组里面的字符串是否相等
System.out.println(a == b);//TRUE,
/*过程:
1、因为直接赋值,所以 a 对象先去找常量池中有没有”123“,
2、显然一开始a没有找到”123“,那就再常量池中去创建了一个”123“的数据空间,设地址:0x11
3、常量池中有了”123“后,a就直接去指向”123“的地址(0x11)了,所以a的地址:0x11
4、然后创建一个b字符串,b也去常量池中找是否有”123“数据空间
5、显然找到了,因为之前a创建了,所以b就直接去指向”123“的数据空间,地址:0x11*/
}
}
输出:
true
true
A02:
分析下列代码的输出:
public class StringExercise03 {
public static void main(String[] args) {
String a = "123";// a指向常量池的”123“
String b = new String("123");// b指向堆中对象
System.out.println(a.equals(b));//true,比较数值
System.out.println(a == b);//false,比较对象,两者的对象地址所指向不同
System.out.println(a == b.intern());//true,
// intern()方法:返回字符串对象的规范表示,详细解释见下面解析:
System.out.println(b == b.intern());//false,b是一个对象,b.intern是直接指向常量池
}
}
输出:
true
false
true
false
解析:
intern()方法API的解释:当调用intern方法时,如果池已经包含与equals(Object)方法确定的相当于此String对象的字符串,则返回来自常量池的字符串。 否则,此String对象将添加到常量池中,并返回对此String对象的引用。
通俗理解:只要String对象调用了 intern ( ) 方法,那么这个对象就是指向常量池的
A03:
分析下列代码的输出:
public class StringExercise04 {
public static void main(String[] args) {
Person person1 = new Person();
person1.name = "123";
Person person2 = new Person();
person2.name = "123";
System.out.println(person1.name.equals(person2.name));
System.out.println(person1.name == person2.name);
System.out.println(person1.name == "123");
}
}
class Person{
public static String name;
}
输出:
true
true
true
简易内存分布图:
13.2.4 字符串特性
1、 String是一个final类,代表不可变的字符序列
2、字符串是不可变的,一个字符串对象一旦被分配,其内容是不可以变的,要是修改字符串的值,那就重新建一个对象去指向就行
案例1:
分析下面代码,创建了多少个对象(由内存布局图可以看出来,一共创建了两个对象)
案例2:
分析下面代码,创建了多少个对象
案例3:
小结:c = a + b,底层是,先创建个StringBuilder sb = new StringBuilder()对象,然后 sb对象去调用sb.append(a)方法,再去调用sb.append(b)方法,append( )方法是在原来的字符串基础上去追加,sb是在堆中
重要规则:String c = “a” + “b”;该对象是常量相加,是指向常量池,而, String c = a + b;是对象相加,指向堆
案例4:
分下下列代码输出:
public class StringExercise06 {
public static void main(String[] args) {
String s1 = "hello";//s1指向池中 "hello"
String s2 = "abc";//s2指向池中 "abc"
String s3 = "helloabc";//s3指向池中"hellosbc"
String s4 = (s1 + s2).intern();//s4指向 是池中"hellozbc"
System.out.println(s3 == s4);//true 因为都指向池中
System.out.println(s3.equals(s4));//true 因为字符串内容相等
}
}
输出:
true
true
A04:
分析下列代码的输出
public class Test1 {
String str = new String("hello");
final char[] ch = {'j', 'a', 'v', 'a'};
public void change(String str, char ch[]) {
str = "java";
ch[0] = 'h';
}
public static void main (String[] args){
Test1 ex = new Test1();
ex.change(ex.str, ex.ch);
System.out.print(ex.str + " and ");
System.out.println(ex.ch);
}
}
输出:
hello and hava
内存分布图:
整体分析:
①:main方法中创建一个ex对象,所以进行类加载,ex中的属性就有str (属于String,所以 str 中还有 final 修饰的 value 属性)和 ch [ ] 数组,内容为 java
②:ex调用change方法,那就开辟一个新栈,方法中由两个参数 str (参数中的str和对象的str一样,指向对象堆中的 value属性,但是呢,方法中又去将str 赋值为 java ,因为value是final 修饰的,所以方法中的str就不再指向堆中的value 了,就在指向常量池中创建的java )和 ch(参数中的ch 和对象属性ch 一样,都指向 一个数组引用,方法中去修改ch[0]d 值,那自然对象堆中的ch[0]就被修改了)
③ 方法change结束后,change栈就消了,然后继续下面的代码,因为堆中的str没有被修改 ,而堆中的ch数组内容被修改了,所以输出就是: hello and hava
13.2.5 String常见方法.
案例:
public class StringMethod {
public static void main(String[] args) {
String s1 = "jiangzai";
String s2 = " * xiaowang ";
//1、equals 区分大小写,判断内容是否相等
System.out.println(s1.equals("JiangZai"));//false
//2、equalsIgnoreCase 不区分大小写,判断内容是否相等
System.out.println(s1.equalsIgnoreCase("JiangZai"));//true
//3、length 获取字符的个数,字符串的长度
System.out.println(s1.length());//8
//4、indexOf 获取字符或者局部字符串在整个字符串中,索引从0开始,第一次出现的索引,没有就返回-1
System.out.println(s1.indexOf("ex"));//获取字符串ex首次出现在s1中的索引,没有,所以-1
System.out.println(s1.indexOf('a'));//获取字a首次出现在s1中的索引,2
//5、lastIndexOf 获取字符或者局部字符串在整个字符串中,索引从0开始,最后一次出现的索引,没有就返回-1
System.out.println(s1.lastIndexOf('a'));//获取字a最后一次出现在s1中的索引,6
//6、substring(i,j) 获取指定索引(i,j)之间的字符串,索引从0开始,也就是获取i-(j-1)之间,注意是之间,所以不包括j
System.out.println(s1.substring(1,4));//获取s1:jiangzai中,索引为1-(4-1)之间的字符串 ian
//7、trim 去掉字符串最前面和最后面的空格
System.out.println(s2.trim());//要是不使用 肯定输出: * xiaowang ,但是trim所以输出:* xiaowang
//8、charAt 获取某索引处的字符,注意不能用 Str[index]数组这种方式
System.out.println(s1.charAt(1));//只能输出单个字符 i
}
}
输出:
false
true
8
-1
2
6
ian
* xiaowang
i
public class StringMethod1 {
public static void main(String[] args) {
String s1 = "xiaowang";
String s2 = "XIAOWANG";
//1.toUpperCase 将字符串转换成大写
System.out.println(s1.toUpperCase(Locale.ROOT));//XIAOWANG
//2.toLowerCase 将字符串转换成小写
System.out.println(s2.toLowerCase(Locale.ROOT));//xiaowang
//3.concat 在字符串后面拼接字符串,可以多次拼接
System.out.println(s1.concat(" sds").concat("tnt"));//xiaowang sdstnt
//4.replace 替换字符串中的字符(前面是久字符,后面是新字符),这里是区分大小写的
//注意:在这里,s1.replace 是返回替换后的对象,
// 相当于有一个对象去接收了s1.replace的结果,但是对于s1本身是没有影响的
System.out.println(s1.replace('a','A'));//xiAowAng
System.out.println(s1);//s1.replace对于s1没有任何影响 输出:xiaowang
//7.toCharArray 转换成字符数组
char [] cha = s1.toCharArray();//既然要转换成字符数组,那就要用一个字符数组来接收
System.out.println("=====转化成字符数组后=======");
for (int i = 0; i < cha.length ; i++) {
System.out.print(cha[i]+"\t");//不换行输出,用制表位去分割
}
//8.format 格式化字符串,
}
}
输出:
XIAOWANG
xiaowang
xiaowang sdstnt
xiAowAng
xiaowang
=====转化成字符数组后=======
x i a o w a n g
split的解析:
public class StringMethodSplit2 {
public static void main(String[] args) {
//5.split 分割字符串,用普通字符分割时
String poem = "鹅鹅鹅,曲项向天歌,白毛浮绿水,红草拨清波";
System.out.println("=========分割前的古诗========");
System.out.println(poem);
String [] split = poem.split(",");//()中间的是字符串中有的字符
//因为一开始是一长串字符串,然后分割后就变成了很多串,所以要用数组去接收被分割的串
//因为是字符串数组,所以要用for循环去输出
System.out.println("=========分割后的古诗========");
for (int i = 0; i < split.length ; i++) {
System.out.println(split[i]);
}
//5.split 分割字符串,有特殊字符时,用转义符 \\ 修饰
String s2 = "C:\\aa\\bb\\cc";
System.out.println("=========分割前的文件路径========");
System.out.println(s2);
//String [] split2 =s2.split("\\");//在字符串中以 \\ 去分割时,,会抛出错误
String [] split2 = s2.split("\\\\");//需要使用转移符 \\ 去修饰特殊字符
System.out.println("=========分割后的文件路径========");
for (int i = 0; i <split2.length ; i++) {
System.out.println(split2[i]);
}
}
}
输出:
=========分割前的古诗========
鹅鹅鹅,曲项向天歌,白毛浮绿水,红草拨清波
=========分割后的古诗========
鹅鹅鹅
曲项向天歌
白毛浮绿水
红草拨清波
=========分割前的文件路径========
C:\aa\bb\cc
=========分割后的文件路径========
C:
aa
bb
cc
compareTo分析:
public class StringMethod3CompareTo {
public static void main(String[] args) {
String s1 = "xiaowang";
String s2 = "XIAOWANG";
//6.compareTo 比较来给你个字符串的大小,因为String有继承compare接口,所以可以比较
System.out.println(s1.compareTo(s2));// 返回:(a-A) = 32
//查看compareTo的底层:
// public int compareTo(String anotherString) {
// int len1 = value.length; //len1去接收第一个字符串的长度(s1长度:8)
// int len2 = anotherString.value.length; //len1去接收第一个字符串的长度(s1长度:8)
// int lim = Math.min(len1, len2); //取两个字符串中最小的长度(lim = 8)
// char v1[] = value; //用v1字符数组取接收第一个字符串s1
// char v2[] = anotherString.value; //用v1字符数组取接收第二个字符串s2
//
// int k = 0;
// while (k < lim) {//判断k和最小长度的大小,要是k的值小于最小长度,那么就执行下面代码,否则直接执行最后一句
// char c1 = v1[k];//取第一个字符数组的第一个字符
// char c2 = v2[k];//取第二个字符数组的第一个字符
// if (c1 != c2) {//判断所取的字符相等不,不相等直接跳到k++去自加
// return c1 - c2;//不相等就用第一个的ASCII码减去第二个的ASCII码,然后返回其值
// }
// k++;//接着将k自加,
// }
// return len1 - len2;//如果 k >= lim 后,那就直接返回第一个字符串的长度减去第二个字符串的长度
// }
//说白了,compareTo的过程就是,
// 1.先对比字符串中每一个对应的字符,相等就一直比较,直到不同或者比较晚都相同再处理
// 2.比较到不同的时候,就返回:前面字符串的那个字符的ASCII码去减去后面字符串那个字符的ASCII码
// 3.要是比较到某一个的最后一个字符,但是另外字符串还有字符的时候,
// 就直接返回:前面字符串的长度减去后面字符串的长度
// 4.要是两个字符串的值都相等,长度也相等,那么就返回0
}
}
输出:
32
format分析:
public class StringMethodFormat {
public static void main(String[] args) {
String name = "江仔";
int age = 22;
char sex = '男';
double score = 90/6;
//输出学生的信息
//方法1; 用字符串拼接 不是很方便更换信息
System.out.println("我是"+name+"今年"+age+"岁,性别"+sex+",得分"+score);
//方法2: 用format方法
String info = String.format("我是%s今年%d岁,性别%c,得分%.2f",name,age,sex,score);
System.out.println(info);
//方法3: 用一个String字符串去接收format
String formatStr = "我是%s今年%d岁,性别%c,得分%.2f";
String info2 = String.format(formatStr,name,age,sex,score);
System.out.println(info2);
//注意:
//1.%s %c %d %f 这些都是占位符 ,后面输入相应类型的变量就可以被替换
//2.%s 用字符串String替换
//3.%c 用字符char替换
//4.%d 用整数int替换
//5.%f 用小小数替换,可以去设定保留几位小数,%.2f保留两位,在.后面加上数字就代表保留几位
}
}
输出:
我是江仔今年22岁,性别男,得分15.0
我是江仔今年22岁,性别男,得分15.00
我是江仔今年22岁,性别男,得分15.00
13.3 StringBuffer类
13.3.1 StringBuffer 理解
基本介绍:
① StringBuffer是java.lang包下面的,代表可变的字符序列,可以对字符串内容进行增删
② 很多方法和String相同,但StringBuffer是可变长度的
③ StringBuffer是一个容器
实例介绍:
1.StringBuffer 的直接父类是 AbstractStringBuilder
2.StringBuffer 实现了 Serializable 接口,所以StringBuffer对象是可以串行化的
3.在父类中 AbstractStringBuilder 有char[] value属性,并且不是final修饰的,该 value 数组存放的字符串内容(这里便是 hello 字符串),是存放在堆中的
4.StringBuffer 是一个final类,所以不能被继承
5.因为StringBuffer的内容存放在 char value[] 中,所以在内容有变化的时(增删) 不用每一次都更换地址(也就是不用每一次都创建对象),所以效率高于String
13.3.2 String VS StringBuffer
① String保存的是字符串常量,里面的值不能被修改,每次String类的更新,实际上就是更改地址,效率很低
② StringBuffer 保存的是字符串变量,里面的值可以被修改,每次StringBuffer的更新,实际上都是在更新内容,不用更改地址,效率比较高
13.3.3 StringBuffer 构造器
- StringBuffer()
构造一个没有字符的字符串缓冲区,初始容量为16个字符。
- StringBuffer(int capacity)
构造一个没有字符的字符串缓冲区和指定的初始容量(capacity)。
- StringBuffer(String str)
构造一个初始化为指定字符串内容的字符串缓冲区。(数组长度为 内容长度 + 16)
13.3.4 String 和 StringBuffer 的转换
1.String —> StringBuffer
public class StringToStringBuffer {
public static void main(String[] args) {
//String ---> StringBuffer
String str = "hello";
//方法1: 使用构造器
//注:返回的对象才是StringBuffer类,而对于str是没有任何影响的
StringBuffer stringBuffer = new StringBuffer(str);
//方法2:使用append方法
//注:append方法是将其后面的参数的字符串,给附加到StringBuffer序列,可以是多种数据类型
StringBuffer stringBuffer1 = new StringBuffer();
stringBuffer1 = stringBuffer1.append(str);
}
}
- StrinBuffer ----> String
public class StringToStringBuffer {
public static void main(String[] args) {
//StrinBuffer ----> String
StringBuffer stringBuffer2 = new StringBuffer("xiaowang");
//方法1: 使用StringBuffer的toString方法
String s = stringBuffer2.toString();
//方法2: 使用构造器
String s1 = new String(stringBuffer2);
}
}
13.3.5 StringBuffer 常见方法
所有方法,可以通过查看API和查看StringBuffer的关系图去看
- 增:append()方法
注:可以增加各种类型的字符,在API中可以具体查看 - 删:delete(start,end) 方法
注:删除的是 start ~ end 中间的字符,索引从0开始,start包含,end不包含 - 改:replace(start,end,string)
注:修改的是 start ~ end 中间的字符,所要替换的可以不是具体删除字符个数的字符串 - 查:indexOf()
注:返回的是 所要查的字符第一次所出现的索引,没有返回-1 - 插:insert()
注:是在 所要插入索引处,去插入要插入的字符,原来字符自动后移 - 获取长度:length()
注:获取字符串的总长度
public class StringBufferMethod {
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer("hello");
System.out.println("原字符串= "+stringBuffer);
//1.append(),注:可以增加各种类型的字符,在API中可以具体查看
stringBuffer.append(",");//"hello,"
stringBuffer.append("小王");//"hello,小王"
stringBuffer.append(100).append(true).append(52.21);//在这里是将其里面内容字符化了
System.out.println("append方法后= "+stringBuffer);//输出:hello,小王100true52.21
//2.delete() 注:删除的是 start ~ end 中间的字符,索引从0开始,start包含,end不包含
stringBuffer.delete(6,8);
System.out.println("delete方法后= "+stringBuffer);//输出:hello,100true52.21
//3.replace()方法 注:修改的是 start ~ end 中间的字符,所要替换的可以不是具体删除字符个数的字符串
stringBuffer.replace(5,9,"小王");
System.out.println("replace方法后= "+stringBuffer);
//4.查:indexOf() 注:返回的是 所要查的字符第一次所出现的索引,没有返回-1
System.out.println("indexOf方法后= "+stringBuffer.indexOf("小王"));//5
//5.插:insert() 注:是在 所要插入索引处,去插入要插入的字符,原来字符自动后移
System.out.println("insert方法后= "+stringBuffer.insert(5,"江仔"));
//6. length() 注:获取字符串的总长度
System.out.println(stringBuffer.length());
}
}
输出:
原字符串= hello
append方法后= hello,小王100true52.21
delete方法后= hello,100true52.21
replace方法后= hello小王true52.21
indexOf方法后= 5
insert方法后= hello江仔小王true52.21
18
13.3.6 StringBuffer 测试
A01:分析下列代码的输出,有错指明:
public class StringBufferExercise01 {
public static void main(String[] args) {
String str = null;//ok
StringBuffer sb = new StringBuffer();
sb.append(str);//查看底层源码,调用了父类AbstractStringBuilder的appendNull()方法
/* appendNull()方法:
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c; 字符数组长度就为 null的长度 4
return this;
}*///其本质就是将 null给字符化了,形成了字符数组,内容为‘n’,‘u’,‘l’,‘l’
System.out.println(sb.length());//输出4
System.out.println(sb);//null
StringBuffer sb1 = new StringBuffer(str);//str是一个空对象,查构造器底层源码:
/*StringBuffer(str)构造器
public StringBuffer(String str) {
super(str.length() + 16);//这里str是null,所以回报出空指针异常 NullPointException
append(str);
}
* */
System.out.println(sb1);//异常发生没有处理,所以不执行
}
}
输出:
4
null
Exception in thread "main" java.lang.NullPointerException
at java.lang.StringBuffer.<init>(StringBuffer.java:139)
at com.xiaowang.StringBuffer_.StringBufferExercise01.main(StringBufferExercise01.java:27)
Process finished with exit code 1
A02:
输入商品名称和商品价格,要求打印效果示例
商品名 商品价格
手机 123,456,78
要求:价格的小数点前面每三位用逗号隔开
public class StringBufferExercise02 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入商品名称:");
String name = scanner.next();
System.out.println("请输入商品价格:");
String price = scanner.next();
StringBuffer sbPrice = new StringBuffer(price);
//String转换成StringBuffer,方便调用StringBuffer的方法
for (int i =sbPrice.indexOf(".")-3 ; i >0 ; i-=3) {
//从小数点的索引开始去判断,先减了3后再去判断
sbPrice = sbPrice.insert(i , ",");//添加,
}
System.out.println("商品名称"+"\t"+"商品价格");//输出值
System.out.println(name+"\t\t"+sbPrice);
}
}
13.4 StringBuilder类
13.4.1 StringBuilder理解
基本介绍:
1、是一个可变的字符序列,和 StringBuffer 的API是兼容的,但不保证同步(也就不是线程安全的,是单线程),该类被用作StringBuffer的替换,用在字符串缓冲区被单个线程使用的时候i(会更快)。
2、StringBuilder 的主要操作是:append(增)和 insert(插),方法是可以重载的,可以接收任意类型的数据
继承关系图:
说明是可以去简易替换StringBuffer的,而且主要用于单线程
实例介绍:
1.StringBuilder 继承了 AbstractStringBuilder类
2.实现了 Serializable ,说明StringBuilder对象是可以串行化的(对象可以网络传输,也可以保存到文件)
3.StringBuilder是 final类 ,所以不能被继承
4.StringBuilder 对象字符序列仍然存放在父类AbstractStringBuilder的
char[] value 属性中,所以也是在堆中
5. StringBuilder 的方法,没有做互斥处理,即没有 synchronized 关键字,因此在单线程情况下去使用
13.5 String VS StringBuffer VS Stringbuilder
13.5.1 基本区别
13.5.2 效率测试
效率:
StringBuilder > StringBuffer >String
案例测试:
//通过让三个类去执行同一个拼接的方法,去统计其所用的时间去比较其效率
public class StringVSStringBufferVSStringbuilder {
public static void main(String[] args) {
//1.String类效率测试
String str = " ";//定义一个空串
long startTime = 0l;//存放开始时间
long endTime = 0l;//存放结束时间
startTime = System.currentTimeMillis();//获取当前时间去赋值给开始时间
for (int i = 0; i < 20000; i++) {//拼接20000次
str = str + i;
}
endTime = System.currentTimeMillis();//获取完成后时间
System.out.println("String所执行的时间"+(endTime - startTime));
//2.StringBuffer类效率测试
StringBuffer stringBuffer = new StringBuffer(" ");//定义一个StringBuffer对象
startTime = System.currentTimeMillis();//获取当前时间去赋值给开始时间
for (int i = 0; i < 20000; i++) {//拼接20000次
stringBuffer.append((i));
}
endTime = System.currentTimeMillis();//获取完成后时间
System.out.println("StringBuffer所执行的时间"+(endTime - startTime));
//2.StringBuilder类效率测试
StringBuilder stringBuilder = new StringBuilder(" ");//定义一个StringBuffer对象
startTime = System.currentTimeMillis();//获取当前时间去赋值给开始时间
for (int i = 0; i < 20000; i++) {//拼接20000次
stringBuilder.append((i));
}
endTime = System.currentTimeMillis();//获取完成后时间
System.out.println("StringBuilder所执行的时间"+(endTime - startTime));
}
}
输出:
String所执行的时间1013
StringBuffer所执行的时间1
StringBuilder所执行的时间0
总结:
效率:StringBuilder > StringBuffer >String,每一次的运行时间是不一样的,但是呢,StringBuilder的安全系数不高
13.5.3 三者选择
13.6 Math类
1、基本介绍
Math类用于计算数学中的运算方法,因为方法都是static静态的,所以直接用Math.方法名可以使用
2、常用方法
1.abs 求绝对值
2.pow 求幂
3.ceil 向上取整
4.floor 向下取整
5.round 四舍五入
6.sqrt 开平方
7.random 获取随机数
8.max 求两个数的最大值
9.min 求两个数的最小值
案例理解:
public class MathMethod {
public static void main(String[] args) {
//1.abs 求绝对值
int abs = Math.abs(-9);//括号里面可以是多种数据类型
System.out.println(abs);//输出:9
//2.pow 求幂
double pow = Math.pow(12,2);//只能是double类型
System.out.println(pow);//输出:144.0
//3.ceil 向上取整
double ceil = Math.ceil(12.3);//只能是double类型
System.out.println(ceil);//输出:13.0
//4.floor 向下取整
double floor = Math.floor(12.3);//只能是double类型
System.out.println(floor);//输出:12.0
//5.round 四舍五入,有long(接近long) 和 float(接近int) 类型的round方法,
long round = Math.round(13.254);//数据中可以是float和double数据类型
System.out.println(round);//输出:13
//6.sqrt 开平方
double sqrt = Math.sqrt(12.5);//只能是double类型
System.out.println(sqrt);//输出:3.5355339059327378
//7.random 获取随机数
double random = Math.random();//返回值为 double值为正号,大于等于 0.0 ,小于 1.0 。
System.out.println(random);
int random1 = (int) Math.random();//随机生成int随机数,用强制转换,转换了都是0
System.out.println(random1);
//8.max 求两个数的最大值
System.out.println(Math.max(1.2,1.5));//可以是多种数据类型,输出:1.5
//9.min 求两个数的最小值
System.out.println(Math.min(1.2,1.5));//可以是多种数据类型,输出:1.2
//获取a-b之间的随机数,例子:返回2--7之间的随机整数
for (int i = 0; i <10 ; i++) {
System.out.println((int)(2+Math.random()*(7-2+1)));
}
}
}
获取随机数加强:
随机生成a-b之间的随机整数,例如(2-7)之间是整数
public class MathMethod {
public static void main(String[] args) {
//获取a-b之间的随机数,例子:返回2--7之间的随机整数
//分析:
//1. Math.random()是获取0-1之间的随机小数,0可取,1不可取, 0 <= Math.random() < 1
//2.获取a-b之间是随机数,就: a + (Math.random()*(b-a+1))
// 分析: 因为 0 <= Math.random() < 1 ,(b-a)< (b-a+1) < (b-a+1)
// 所以:0*(b-a) <= Math.random()*(b-a+1) < 1*( b-a+1 )
// 所以:0 <= Math.random()*(b-a+1) < (b-a+1)
// 又因为:a+0 <= Math.random()*(b-a+1) < a+(b-a+1)
// 所以:a <= Math.random()*(b-a+1) < b+1
//3.要想获得整数,就需要用 int 去强制转换所获得的随机值,单纯 (int)Math.random()=0
//4.所以要想获得a-b之间的随机整数就:(int)(a + (Math.random()*(b-a+1)))
//5.用a=2,b=7,循环十次来测试是否正确
for (int i = 0; i <10 ; i++) {
System.out.print(((int)(2+Math.random()*(7-2+1)))+"\t");
//用制表位去改输出格式,就不用换行输出了
}
}
}
输出:
2 2 4 7 3 7 7 2 7 6
13.7 Arrays类
基本介绍:
包含一系列静态方法,用于管理或操作数组(比如排序或者搜索)
13.7.1常用方法:
13.7.1.1 toString()方法
从API可以看出来,toString方法可以输出多种数据类型的数组
案例演示:
比较for循环和toString方法
public class ArraysMethod {
public static void main(String[] args) {
Integer [] integer = {1,20,15,50};
//1.遍历数组
//老方法for循环
System.out.println("======for循环======");
for (int i = 0; i <integer.length ; i++) {
System.out.print(integer[i]+"\t");//制表位分开
}
System.out.println();
//toString方法
System.out.println("======toString循环======");
System.out.println(Arrays.toString(integer));
//查看toString底层源码:
/*
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {//其实也是用了for循环
b.append(String.valueOf(a[i]));//将其每一个字符用valueOf去字符化,然后用append去拼接
if (i == iMax)
return b.append(']').toString();
b.append(", ");//中间用 , 隔开
}*/
}
}
输出:
======for循环======
1 20 15 50
======toString循环======
[1, 20, 15, 50]
13.7.1.2 sort排序
① 是从小到大排序,从API可以看出sort方法可以有很多数据类型,每一种数据类型都有两种排序方式,一个是整个数组进行排序,另一个是指定某个索引范围内的数进行排序
int为例:
案例分析:
public class ArraysMethodSort {
public static void main(String[] args) {
Integer [] arr={1,-1,0,5,3};
//1.sort整体排序
Arrays.sort(arr);//数组是引用型的,所以排序过后,会影响原始的integer数组顺序
System.out.println("======sort排序后数组=====");
System.out.println(Arrays.toString(arr));//用toString方法去输出
//2.sort指定范围排序(注意:包含前面不包含后面)
Integer [] arr1 = {-1,8,5,9,3,2,6};
Arrays.sort(arr1,1,6);//索引为1 <= arr1[i] < 6之间的数组排序:
//所以输出:-1,2,3,5,8,9,6
System.out.println(Arrays.toString(arr1));
}
}
输出:
======sort排序后数组=====
[-1, 0, 1, 3, 5]
[-1, 2, 3, 5, 8, 9, 6]
② 定制排序
案例分析:
public class ArraysMethodSort {
public static void main(String[] args) {
//3.定制排序:(自己通过debug去调试去深刻理解)
//主要是判断public int compare(Object o1, Object o2)方法的返回值是 >0 还是 <0,
//这就会影响最后的排序方法(从大到小,还行,从小到大)
Integer [] arr2 = {1,9,5,6,2,4,7};
Arrays.sort(arr2, new Comparator() {//new Comparator是一个接口,整个是一个匿名内部类
@Override
public int compare(Object o1, Object o2) {//当执行的时候,就会进入compare底层代码
Integer i1 = (Integer) o1;//向下转型
Integer i2 = (Integer) o2;
return i1-i2;//这里可以通过(i1-i2),或者(i2-i1)的大小去判断排序的方式(从大到小还是从小到大)
}
});
}
}
输出:
i1-i2的结果:[1, 2, 4, 5, 6, 7, 9]
i2-i1的结果:[9, 7, 6, 5, 4, 2, 1]
定制排序比较难以理解,我们通过自己去写一个定制排序来进一步理解:
import java.util.Arrays;
import java.util.Comparator;
public class ArraysExercise {
public static void main(String[] args) {
int [] arr = {1,5,6,0,8,4};
// bubbleSort(arr);
customSort(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {//o1,o2也就是后面要传入的arr[j],arr[j+1]
int i1 = (Integer)o1;//先拆箱将其int化
int i2 = (Integer)o2;
return i1-i2;//在这里可以是return i2-i1;
//return i2-i1;
// 在这里的值,就相当于是c.compare(arr[j],arr[j+1])方法 的大小判断了
}
});
System.out.println(Arrays.toString(arr));
}
//化繁为简:
//1.数组排序平常都是用冒泡排序(但是当要修改排序方式的时候,只能去修改方法中的代码去改变不方便)
public static void bubbleSort(int [] arr){
int temp = 0;//中间变量
for (int i = 0; i < arr.length-1; i++) {//因为外层要少循环一次底层
for (int j = 0; j < arr.length-1-i; j++) {//内层循环
//从小到大排序(要是从大到小:(arr[j] < arr[j+1])即可)
if (arr[j] > arr[j+1]){
temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
}
//定制排序(通过Comparator接口就可以直接实现排序方法)
public static void customSort(int [] arr, Comparator c){
int temp1 = 0;//中间变量
for (int i = 0; i < arr.length-1; i++) {//因为外层要少循环一次底层
for (int j = 0; j < arr.length-1-i; j++) {//内层循环
//主要确定排序方式的就是if中的语句,
// 所以将其改成接口,通过匿名内部类去确定排序方式更加方便
if (c.compare(arr[j],arr[j+1])>0){//如果c.compare(arr[j],arr[j+1])>0则从小到大,反之从大到小
temp1 = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp1;
}
}
}
}
}
总结:指定排序的方法主要体现了接口编程的思想,这样是很方便的。
13.7.1.3 binarySearch 二分法
13.7.1.4 copyOf 数组元素的复制
13.7.1.5 fill数组元素填充
13.7.1.6 equals 比较两个数组
13.7.1.7 asList 转换集合
以上方法统统用案例演示:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @Author 小王
* @DATE: 2022/4/9
*/
public class ArraysMethod01 {
public static void main(String[] args) {
Integer [] arr = {1,2,3,5,6};
// 3.binarySearch二分法查找,必须是排好序的数组去使用,可以正序可以负序
// 注: 要查找的数据前面的数据保证是有顺序的就行
// 如果数组中存在该元素就输出索引,
System.out.println(Arrays.binarySearch(arr,2));//输出:1
//如果数组中不存在,就返回 -(low + 1); low是应该存在位置,通过底层去查看
System.out.println(Arrays.binarySearch(arr,4));//-(3+1)==-4
//4.copyOf 数组元素的复制
// Arrays.copyOf(arr,arr.length);
// arr是所要去拷贝的数组,arr.length是确定拷贝多少个数据
// 如果所要拷贝的数据长度不够,那就用null去填充
// 如果arr.length = 0,则返回空数组
// 如果arr.length < 0,则抛出NegativeArraysSizeException异常
// 注:copyOf方法,底层使用的方法是:arraycopy
Integer [] arr1 = Arrays.copyOf(arr,0);
System.out.println("拷贝后arr1="+Arrays.toString(arr1));
//5.fill数组元素填充
// 5.1 Arrays.fill(arr2,num);填充后的结果是将数组中所有的数据改成所填充的num
// 5.2 Arrays.fill(arr2,fromIndex,toIndex,num);将fromIndex,toIndex之间的数给替换成num
Integer [] arr2 = {1,2,3,4};
System.out.println("填充前的arr2=" +Arrays.toString(arr2));
Arrays.fill(arr2,0,2,20);//将索引0-2之间的数替换成20
System.out.println("部分填充后的arr2="+Arrays.toString(arr2));
Arrays.fill(arr2,20);//所有的都替换成num = 20
System.out.println("填充后的arr2="+Arrays.toString(arr2));
//6.equals 比较两个数组内容是否完全一样
Integer [] arr3 = {1,2,3};
Integer [] arr4 = {1,2,3};
boolean equals = Arrays.equals(arr3,arr4);//用一个boolean变量去接收判断后的结果
System.out.println(equals);//相同返回ture,不同返回false
//7.asList 将一组值,转换成list(集合)
// 7.1 asList方法会把(1,2,3,4,5)转换成一个集合
// 7.2 asLIst的编译类型是:List(接口类)
// 7.3 asList的运行类型是:java.util.Arrays$ArrayList
// 是Arrays类里面的一个静态内部类:底层:
// private static class ArrayList<E> extends AbstractList<E>
// implements RandomAccess, java.io.Serializable
List asList = Arrays.asList(1,2,3,4,5);
System.out.println("asList="+asList);
System.out.println("asList的运行类型="+asList.getClass());
}
}
输出:
1
-4
拷贝后arr1=[]
填充前的arr2=[1, 2, 3, 4]
部分填充后的arr2=[20, 20, 3, 4]
填充后的arr2=[20, 20, 20, 20]
true
asList=[1, 2, 3, 4, 5]
asList的运行类型=class java.util.Arrays$ArrayList
13.7.1.7 Arrays练习题
题目:
定义Book类,包含name和price,按price两种方式排序,按书名长度排序,,Book类有4个对象
book[0]=new Book(“红楼梦新版”,100);
book[1]=new Book(“西游记”,80);
book[2]=new Book(“水浒传20年”,120);
book[3]=new Book(“三国演义”,50);
- 提示:可以通过Comparator接口匿名内部类,也就是定制排序*/
分析:
1.为了方便切换从大到小还是从小到大,我们使用Comparator接口去实现。
2.我们先自己去写接口编程方法去实现价格的排序:
package com.xiaowang.arrays;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
/**
* @Author 小王
* @DATE: 2022/4/9
*/
/*定义Book类,包含name和price,按price两种方式排序,,Book类有4个对象
* 提示:可以通过Comparator接口匿名内部类,也就是定制排序*/
public class ArraysExercise {
public static void main(String[] args) {
Book [] book = new Book[4];
book[0]=new Book("红楼梦新版",100);
book[1]=new Book("西游记",80);
book[2]=new Book("水浒传20年",120);
book[3]=new Book("三国演义",50);
//因为是通过price去排序,book是Book类,price是double类,所以用double数组去存放book的价格
double [] price = new double[4];
for (int i = 0; i < book.length; i++) {
price[i]=book[i].getPrice();
}
customSort(price, new Comparator() {
@Override
public int compare(Object o1, Object o2) {//o1,o2也就是后面要传入的arr[j],arr[j+1]
double i1 = (Double) o1;//先拆箱将其int化
double i2 = (Double) o2;
// return ((int)i1)-((int)i2);//从小到大
return ((int)i2)-((int)i1);//从大到小
//因为是重写判断price对象相减的值,所以我们需要去强制转换数据类型
// 在这里的值,就相当于是c.compare(arr[j],arr[j+1])方法 的大小判断了
}
});
System.out.println(Arrays.toString(price));
}
//定制排序(通过Comparator接口就可以直接实现排序方法)
public static void customSort(double [] arr, Comparator c){
double temp1 = 0;//中间变量
for (int i = 0; i < arr.length-1; i++) {//因为外层要少循环一次底层
for (int j = 0; j < arr.length-1-i; j++) {//内层循环
//主要确定排序方式的就是if中的语句,
// 所以将其改成接口,通过匿名内部类去确定排序方式更加方便
if (c.compare(arr[j],arr[j+1])>0){//如果c.compare(arr[j],arr[j+1])>0则从小到大,反之从大到小
temp1 = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp1;
}
}
}
}
}
class Book{
private String name ;
private double price;
public Book(String name, double price) {
this.name = name;
this.price = price;
}
public double getPrice() {
return price;
}
}
输出:
[120.0, 100.0, 80.0, 50.0]
此时只有价格被排序输出,同理也可以使用此方法去通过书名长度去排序,但是。我们的要求根据价格或者书名长度去输出是整个书的信息,所以判断对象得要是Book对象,所以我们可以在刚刚的基础上去修改代码
如果每一次我们都自己去写定制排序的内部结构的话,也是很麻烦的,而且本来Java就有定制排序的方法,我们直接用就可以
package com.xiaowang.arrays;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
/**
* @Author 小王
* @DATE: 2022/4/9
*/
public class ArraysExercise01 {
public static void main(String[] args) {
Book1 [] book1 = new Book1[4];
book1[0]=new Book1("红楼梦新版",100);
book1[1]=new Book1("西游记",80);
book1[2]=new Book1("水浒传2020年",120);
book1[3]=new Book1("三国演义",50);
//使用Arrays自己有的定制排序方法
// 1. price排序
Arrays.sort(book1, new Comparator<Book1>() {
@Override
public int compare(Book1 o1, Book1 o2) {//这里o1,o2传入的是book对象
return ((int)o1.getPrice())-((int)o2.getPrice());//从小到大排序
// return ((int)o2.getPrice())-((int)o1.getPrice());//从大到小排序
}
});
System.out.println("==price排序==");
System.out.println(Arrays.toString(book1));
//1.name长度排序
Arrays.sort(book1, new Comparator<Book1>() {
@Override
public int compare(Book1 o1, Book1 o2) {
return o1.getName().length()-o2.getName().length();//名字长度从短到长
// return o2.getName().length()-o1.getName().length();//名字长度从长到短
}
});
System.out.println("==name排序==");
System.out.println(Arrays.toString(book1));
}
}
class Book1{
private String name;
private double price;
public Book1(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
@Override
public String toString() {
return "Book1{" +
"name='" + name + '\'' +
", price=" + price +
'}'+'\n';
}
}
输出:
==price排序==
[Book1{name='三国演义', price=50.0}
, Book1{name='西游记', price=80.0}
, Book1{name='红楼梦新版', price=100.0}
, Book1{name='水浒传2020年', price=120.0}
]
==name排序==
[Book1{name='西游记', price=80.0}
, Book1{name='三国演义', price=50.0}
, Book1{name='红楼梦新版', price=100.0}
, Book1{name='水浒传2020年', price=120.0}
]
总结:
Arrays的sort方法,真的很有用,特别是Comparator接口编程的sort方法,减少了很多时间。
13.8 System类
13.8.1 常见方法
1.exit 退出当前程序
案例:
import java.util.Arrays;
/**
* @Author 小王
* @DATE: 2022/4/9
*/
public class System_ {
public static void main(String[] args) {
//1.exit 退出当前程序
System.out.println("xiaowang");
System.exit(0);//0 表示一个状态,是正常状态
System.out.println("jiangzai");//因为前面退出了当前程序,所以下面的jiagnzai不会被执行
}
}
输出:
xiaowang
2.arraycopy:复制数组元素
案例:
import java.util.Arrays;
/**
* @Author 小王
* @DATE: 2022/4/9
*/
public class System_ {
public static void main(String[] args) {
//2.arraycopy:复制数组元素,比较合适底层调用
/*arraycopy方法底层源码:
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);*/
/*五个参数解读:
src – the source array. 源数组
srcPos – starting position in the source array. 从源数组的哪个索引开始拷贝
dest – the destination array. 目标数组,即要把源数组数据拷贝到哪个数组
destPos – starting position in the destination data. 把源数组的数据拷贝到目标数组哪个索引
length – the number of array elements to be copied. 需要拷贝的个数(不能超过源数组的个数)
*/
int [] arr={1,2,3};
int [] arr1 = new int[3];
System.arraycopy(arr,1,arr1,1,2);//应该输出:[0,2,3]
System.out.println("拷贝后的数组arr1="+Arrays.toString(arr1));
}
}
输出:
拷贝后的数组arr1=[0, 2, 3]
3.currentTimeMillis()
案例:
package com.xiaowang.system_;
import java.util.Arrays;
/**
* @Author 小王
* @DATE: 2022/4/9
*/
public class System_ {
public static void main(String[] args) {
//3.currentTimeMillis() 返回当前时间,距离(1970-1-1)年的毫秒数
System.out.println(System.currentTimeMillis());
}
}
4.gc 虚拟机回收
解析:
13.9 BigInteger和BigDecimal类
基本介绍:
1、BigInteger 适合保存较大的整型
常用方法案例:
package com.xiaowang.bigNum_;
import java.math.BigInteger;
/**
* @Author 小王
* @DATE: 2022/4/10
*/
public class BigInteger_ {
public static void main(String[] args) {
//long l = 1234567899999999999999999999999;//超出了long的范围
//平时定义一个大的数的时候,一般都是long类型,但是当我们的数大于long了后,就需要用BigInteger
//1.BigInteger因为是一个类,所以在定义一个超级大的数的时候,用对象去表示
//2.要注意,当去创建BigInteger对象的时候,里面的值要用字符串去表示,在底层会去自动转换成整型
BigInteger bigInteger1 = new BigInteger("1234567899999999999999999999999");
BigInteger bigInteger2 = new BigInteger("1000");
//3.BigInteger可以做加减乘除,但是呢,因为这是一个类对象,所以是不可以直接用 + - * /这些去直接做运算的
//4.所以BigInteger的运算就直接调用BigInteger类对象相应的方法去处理就好,可以查看体系图了解BigInteger的方法
//5. add()加法
System.out.println("加法:"+bigInteger1.add(bigInteger2));
//6. subtract() 减法
System.out.println("减法:"+bigInteger1.subtract(bigInteger2));
//7. multiply() 乘法
System.out.println("乘法:"+bigInteger1.multiply(bigInteger2));
//8. divide() 除法
System.out.println("除法:"+bigInteger1.divide(bigInteger2));
}
}
输出:
加法:1234567900000000000000000000999
减法:1234567899999999999999999998999
乘法:1234567899999999999999999999999000
除法:1234567899999999999999999999
2、BigDecimal适合保存精度更高的浮点型(小数)
常用方法案例:
package com.xiaowang.bigNum_;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* @Author 小王
* @DATE: 2022/4/10
*/
public class BigDecimal_ {
public static void main(String[] args) {
double num = 1234567899999999999999.9999999;//很明显double数值的大小超过了double的最大数,所以会被截取一段输出
System.out.println("double类型的num:"+num);//输出:1.2345679E21
//平时定义一个大的小数的时候,一般都是double类型,但是当我们的数大于double了后,就需要用BigDecimal
//1.BigDecimal因为是一个类,所以在定义一个超级大的数的时候,用对象去表示
//2.要注意,当去创建BigDecimal对象的时候,里面的值要用字符串去表示,在底层会去自动转换成浮点型
BigDecimal bigDecimal1 = new BigDecimal("1234567899999999999999.9999999");
BigDecimal bigDecimal2 = new BigDecimal("100.0001");
//3.BigDecimal可以做加减乘除,但是呢,因为这是一个类对象,所以是不可以直接用 + - * /这些去直接做运算的
//4.所以BigDecimal的运算就直接调用BigDecimal类对象相应的方法去处理就好,可以查看体系图了解BigDecimal的方法
//5. add()加法
System.out.println("加法:"+bigDecimal1.add(bigDecimal2));
//6. subtract() 减法
System.out.println("减法:"+bigDecimal1.subtract(bigDecimal2));
//7. multiply() 乘法
System.out.println("乘法:"+bigDecimal1.multiply(bigDecimal2));
//8. divide() 除法
// System.out.println("除法:"+bigDecimal1.divide(bigDecimal2));//会有异常(除不尽,有无限小数)
//注意:对于divide()除法的时候,有可能会抛出异常:
// ArithmeticException: Non-terminating decimal expansion(除不尽异常)
//为了使其能够正常运行,我们可以通过BigDecimal.ROUND_CEILING去完成
//BigDecimal.ROUND_CEILING解读:
//这个的意思是,保留分母的小数位数,也就是,分母有几位,得出来的结果就是保留几位小数
System.out.println("除法:"+bigDecimal1.divide(bigDecimal2,BigDecimal.ROUND_CEILING));
System.out.println("除法:"+bigDecimal1.divide(bigDecimal2));//会有异常(除不尽,有无限小数)
}
}
输出:
double类型的num:1.2345679E21
加法:1234567900000000000100.0000999
减法:1234567899999999999899.9998999
乘法:123456913456789999999999.99998999999
除法:12345666654333345666.6543334
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1693)
at com.xiaowang.bigNum_.BigDecimal_.main(BigDecimal_.java:35)
13.10 日期类
13.10.1 第一代 Date类
Date类是第一代时间类
构造器:
方法案例分析:
package com.xiaowang.date_;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author 小王
* @DATE: 2022/4/10
*/
public class Date01 {
public static void main(String[] args) throws ParseException {
//1.获取当前系统的时间,(
// 1.1 使用Date()构造器)
Date date1 = new Date();
System.out.println("Date当前日期:"+date1);
// 1.2 使用Date(long)构造器
Date date2 = new Date(123456);//里面传的是long 毫秒数
//2.这里的Date是在 java.util.Date包里面,不是 java.sql.Date(这个是链接数据库时候的)
// 3.默认的输出日期格式,是国外的输出方式,因此通常都要转换格式
// 3.1 创建SimpleDateFormat对象,可以指定相应的输出格式
// 3.2 相应格式是有h标准规定的,字母不能乱写,可以去查API了解每个字母的含义
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss E");
System.out.println("转换后日期:"+simpleDateFormat.format(date1));//format:将日期转换为指定格式的字符串
//4.可以把格式化的String字符串,转换成对应的Date
// 注:所需要转换的String字符串时间的格式,必须要和你定义的SimpleDateFormat类对象的格式一样,不然要报错
String date = "2022年04月10日 14:50:11 星期日";//必须和上面SimpleDateFormat类对象的时间格式一样
Date date3 = simpleDateFormat.parse(date);//这里会抛出一个转换异常,所以我们通常要去接收这个异常用throws
System.out.println("转换后时间:"+date3);//这时的转换是国外的输出格式,所以可以再用一次format去指定格式
System.out.println("格式转换后时间:"+simpleDateFormat.format(date3));//
}
}
输出:
Date当前日期:Sun Apr 10 14:57:53 CST 2022
转换后日期:2022年04月10日 14:57:53 星期日
转换后时间:Sun Apr 10 14:50:11 CST 2022
格式转换后时间:2022年04月10日 14:50:11 星期日
注:
① 这里的Date是在 java.util.Date包里面,不是 java.sql.Date(这个是链接数据库时候的)
② 创建SimpleDateFormat对象,相应格式是有标准规定的,字母不能乱写,可以去查API了解每个字母的含义
③ 所需要转换的String字符串时间的格式,必须要和你定义的SimpleDateFormat类对象的格式一样,不然要报错
13.10.2 第二代 Calendar类
Calendar是第二类时间类
构造器:
字段:
Calendar的字段超级多,使用的时候最好去查API
案例分析:
package com.xiaowang.date_;
import java.util.Calendar;
/**
* @Author 小王
* @DATE: 2022/4/10
*/
public class Calendar_ {//日历类
public static void main(String[] args) {
//1.Calendar是一个抽象类,并且是private私有的
//2.可以通过 getInstance()方法来获取实例
//3.Calendar提供了大量的方法和字段去使用,可以查看Calendar底层或者API
//实例演示:
//1.获取实例对象
//Calendar calendar1 = new Calendar();//因为是抽象类,所以通过new一个对象就是错的
Calendar calendar = Calendar.getInstance();//因为是抽象的,所以通过方法来获取实例
System.out.println(calendar);//这里输出的是Calendar的所有字段
//2.获取某个日历对象的某个日历字段
//通过Calendar对象去调用get(Calendar.字段名)方法去获取
//Calendar有很多字段,可以通过API去查每一个字段的意思
System.out.println("Calendar年:"+calendar.get(Calendar.YEAR));
//注意:获取月份的时候,要+1,因为month是从0开始编号的
System.out.println("Calendar月:"+(calendar.get(Calendar.MONTH)+1));
System.out.println("Calendar日:"+calendar.get(Calendar.DAY_OF_MONTH));
//注意:获取小时的时候,有12小时的,也有24小时的
System.out.println("Calendar时:"+calendar.get(Calendar.HOUR));//Calendar.HOUR 是 12小时
System.out.println("Calendar时:"+calendar.get(Calendar.HOUR_OF_DAY));//Calendar.HOUR_OF_DAY 是 24小时
//3.Calendar没有专门的格式转换方法,一般都是程序员自己去设定
System.out.println(calendar.get(Calendar.YEAR)+"年"+(calendar.get(Calendar.MONTH)+1)+"月"+calendar.get(Calendar.DAY_OF_MONTH)+"日");
}
}
注:
1.Calendar是一个抽象类,并且是private私有的,通常通过 getInstance()方法来获取实例
2.获取月份的时候,要+1,因为month是从0开始编号的
3.Calendar没有专门的格式转换方法,一般都是程序员自己去设定
4.Calendar.HOUR 是 12小时
5. Calendar.HOUR_OF_DAY 是 24小时
13.10.3 第三代日期类
为什么会出现第三代日期类?
第三类日期类是在jdk8后才出现的
13.10.3.1日期时间类
第三代是在 java.time.包里面
第三代的体系图很复杂,同时实现了很多接口,所以所包含的方法也很多
案例方法分析:
package com.xiaowang.date_;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
* @Author 小王
* @DATE: 2022/4/10
*/
public class LocalDate_ {
public static void main(String[] args) {
//1.使用now()方法去获得当前日期及时间的对象
LocalDateTime localDateTime = LocalDateTime.now();//获得当前日期及时间的对象
System.out.println("格式化前的时间:"+localDateTime);
LocalDate localDate = LocalDate.now();//获取当前日期对象,也就是只获得 年月日 同时没有获取时分秒的方法
LocalTime localTime = LocalTime.now();//获取当前时间对象,也就是只获得 时分秒 同时没有获取年月日的方法
//2.仅仅只要获取某一个字段,那么直接用get相关方法就行,在diagrams体系图中可以查看所有方法,也可以查看API
System.out.println("年"+localDateTime.getYear());
System.out.println("月"+localDateTime.getMonth());//获取的是英文月份
System.out.println("月"+localDateTime.getMonthValue());//获取的是中文数字月份
System.out.println("日"+localDateTime.getDayOfMonth());//获取这个月的某一天
System.out.println("时"+localDateTime.getHour());//获取的是24小时的时间
System.out.println("分"+localDateTime.getMinute());
System.out.println("秒"+localDateTime.getSecond());
}
}
输出:
格式化前的时间:2022-04-10T16:16:34.746
年2022
月APRIL
月4
日10
时16
分16
秒34
13.10.3.2格式化时间类
DateTimeFormatter类是java.time.format包里面的一格式化时间的一个类,是用DateTimeFormatter类对象去调用其format方法去格式化,
DateTimeFormatter类对象的获取是:
格式化对象方法是
案例分析:
package com.xiaowang.date_;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
* @Author 小王
* @DATE: 2022/4/10
*/
public class LocalDate_ {
public static void main(String[] args) {
//1.使用now()方法去获得当前日期及时间的对象
LocalDateTime localDateTime = LocalDateTime.now();//获得当前日期及时间的对象
System.out.println("格式化前的时间:"+localDateTime);
//3.格式化时间
// 使用DateTimeFormatter对象去格式化
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");//使用指定的模式创建格式化程序。
String format = dateTimeFormatter.format(localDateTime);//返回后的字符串对象才是格式化过后的
System.out.println("格式化后的时间:"+format);
}
}
输出:
格式化前的时间:2022-04-10T16:26:03.098
格式化后的时间:2022年04月10日 16时26分03秒
13.10.3.3 instant 时间戳
instant是在java.time 包里面的
案例分析;
package com.xiaowang.date_;
import java.time.Instant;
import java.util.Date;
/**
* @Author 小王
* @DATE: 2022/4/10
*/
public class Instant_ {
public static void main(String[] args) {
//1.通过静态方法 now()去获取instant对象实例
Instant now = Instant.now();
System.out.println(now);//返回当前时间点的时间戳
//2.如果要将instant类对象转换成一个Date对象就是用from()方法即可
Date date = Date.from(now);
System.out.println(date);//返回当前时间的Date对象
//3.如果将Date对象转换成一个instant对象就用toInstant()方法即可
Instant instant = date.toInstant();
System.out.println(instant);
}
}
输出:
2022-04-10T08:35:05.827Z
Sun Apr 10 16:35:05 CST 2022
2022-04-10T08:35:05.827Z
注意:第三类日期类有很多很多的方法,使用的时候,可以通过API去查看每一个方法的使用。
13.11 练习题
13.11.1 A01:String翻转
题目:
将字符串指定部分进行反转,比如将“abcde”,反转成“aedcb”,
要求:写一个方法去完成
public static String reverse(String str,int startIndex,int endIndex)
分析:
package com.xiaowang.homWork_;
/**
* @Author 小王
* @DATE: 2022/4/10
*/
/*将字符串指定部分进行反转,比如将“abcde”,反转成“aedcb”,
* 要求:写一个方法去完成
* public static String reverse(String str,int startIndex,int endIndex)*/
public class Homework01 {
public static void main(String[] args) {
String str ="abcde";
System.out.println("交换前="+str);
str = reverse("abcde", 1, 4);
//因为是要将iyge字符串给翻转,所以将翻转后的还是赋值给本字符串,就不用再去new一个String
System.out.println("翻转后="+str);
}
public static String reverse(String str, int startIndex, int endIndex) {
//1.先将字符串转换成一个数组,因为String是不可以直接修改的,
char [] arrstr = str.toCharArray();
//2.用一个char字符去做中间变量
char temp = ' ';
//3.用for循环去翻转数组间的字符
for (int i = startIndex,j = endIndex; i < j; i++,j--) {
temp = arrstr[i];
arrstr[i] = arrstr[j];
arrstr[j] = temp;
}
//4.然后再将字符数组转换成String返回
String str1 = String.valueOf(arrstr);
return str1;
}
}
以上方法虽然可以实现功能,但是不稳定,不坚固,当所传入的数组为null,或者startIndex和endIndex越界什么的就会直接报错,所以,为了避免这些错误,我们需要去考虑找方法使其代码更加稳固。
修改后的代码:
package com.xiaowang.homWork_;
/**
* @Author 小王
* @DATE: 2022/4/10
*/
/*将字符串指定部分进行反转,比如将“abcde”,反转成“aedcb”,
* 要求:写一个方法去完成
* public static String reverse(String str,int startIndex,int endIndex)*/
public class Homework01 {
public static void main(String[] args) {
String str ="abcde";
System.out.println("交换前="+str);
try {
str = reverse("abcde", 1, 4);//因为这里可能会有异常,所以我们去处理一下
} catch (NullPointerException e) {
System.out.println(e.getMessage());//输出我们的异常信息
return;//出错了就不往下面执行了
} catch (ArrayIndexOutOfBoundsException e){
System.out.println(e.getMessage());
return;
}
//因为是要将iyge字符串给翻转,所以将翻转后的还是赋值给本字符串,就不用再去new一个String
System.out.println("翻转后="+str);
}
public static String reverse(String str, int startIndex, int endIndex) {
//为了使代码更稳固我们需要去处理一些异常,
// 从正确的想法去取反就可以包括所有的错误想法
/*整体作为一个异常
if (!(str != null && startIndex<endIndex && endIndex<str.length())){
throw new RuntimeException("参数不正确");
//也可以将具体的异常类型写出来,然后相应的去输出
}*/
//空指针异常
if (!(str!=null)){
throw new NullPointerException("空指针异常");
} //越界异常
else if (!(startIndex<endIndex && endIndex<str.length())){
throw new ArrayIndexOutOfBoundsException("越界异常");
}
//1.先将字符串转换成一个数组,因为String是不可以直接修改的,
char [] arrstr = str.toCharArray();
//2.用一个char字符去做中间变量
char temp = ' ';
//3.用for循环去翻转数组间的字符
for (int i = startIndex,j = endIndex; i < j; i++,j--) {
temp = arrstr[i];
arrstr[i] = arrstr[j];
arrstr[j] = temp;
}
//4.然后再将字符数组转换成String返回
String str1 = String.valueOf(arrstr);
return str1;
}
}
13.11.2 A02:注册登录
题目:
用户输入用户名,密码,邮箱去注册一个账号,要是不满足以下要求,就返回相应的异常信息
要求,
1.用户名只能是2或3或4长度
2.密码只能是6位,并且全为数字
3.邮箱必须有@和. 并且@要在.的前面,并且都不能在首位
分析:
package com.xiaowang.homWork_;
import java.util.Scanner;
/**
* @Author 小王
* @DATE: 2022/4/11
*/
/*思路:
* 1.化繁为简,先写死的字符串去实现
* 2.先写一个方法去判断所要输入的信息是否正确
* 3.再写每一个判断所需要用到的方法(1.判断是否全为数字 2.@和.的存在和顺序),在第二步判断中直接去调用具体的判断方法
* 4.每写完一个判断方法就在main中去用字符串去测试一下
* 5.用字符串测试没问题后,再去用Scanner去接收再去测试
* 6.如果要求更多也可以在先有基础上继续加条件去校验
* */
public class HomeWork02 {
public static void main(String[] args) {
System.out.println("请输入你的用户名,密码和邮箱");
Scanner scanner = new Scanner(System.in);
String name = scanner.next();
String password = scanner.next();
String email = scanner.next();
try {
message(name,password,email);
} catch (Exception e) {
System.out.println(e.getMessage());
return;//有错输出就不再执行
}
System.out.println("注册成功");
}
//写一个方法来做判断
public static void message(String name,String password,String email){
if (!(name.length()>=2 && name.length()<=4)){//判断用户名长度
throw new RuntimeException("用户名长度错误");
}else if (!(password.length()==6 && checkStrIsNum(password))){
throw new ArithmeticException("密码长度不正确或者不全为数字");
}else if (!(checkStrIsEmail(email))){
throw new NumberFormatException("邮箱格式不正确");
}
}
//写个方法用来判断输入的字符串是否全是数字,利用isDigit方法去完成,
//也可以将字符串转换成字符数组,然后去判断每一个索引处值的ASCII码是否在0-9之间
public static boolean checkStrIsNum(String str){
for (int i = 0; i < str.length(); i++) {
// System.out.println(str.charAt(i));
// str.charAt(i)方法是返回指定索引处的值
if (!Character.isDigit(str.charAt(i))){//利用Character.isDigit的方法去判断是否是数字
//如果不是返回false
return false;
}
}
return true;//如果使数字就返回true
}
//写个方法用来判断邮箱中是否包含@和. 并且@再.的前面
public static boolean checkStrIsEmail(String str) {
/*这是我最先想到的方法
String str1 ="@";
String str2 =".";
//1.先判断是否有这两个符号
boolean have = str.contains(str1) && str.contains(str2);
if (have){//如果含有那两个字符则进行顺序的判断,并且肯定不能开头
if (str.indexOf(str1)<str.indexOf(str2) && str.indexOf(str1)>0){
return true;
}
}
return false;*/
//第二种方法
//先获取字符串中两个符号的索引值
int i= str.indexOf("@");//如果是字符串中没有这个字符,那么就返回-1
int j= str.indexOf(".");
if (i>0 && j>i){
return true;
}//如果是满足条件,那么返回true
//如果没有这两个字符,那么i和j都是-1,上面if判断也是错的,所以还是会返回false
return false;
}
}
13.11.3 A03:人名调序输出
题目:
编写Java程序,输入形式为:"Wang hai Yang"的人名,然后以Yang,Wang.H输出
注意:中间用,隔开,最后是.H是中间单词的大写首字母
分析:
package com.xiaowang.homWork_;
import java.util.Locale;
/**
* @Author 小王
* @DATE: 2022/4/11
*/
/*
* 编写Java程序,输入形式为:"Wang hai Yang"的人名,然后以Yang,Wang.H输出
* 注意:中间用,隔开,最后是.H是中间单词的大写首字母*/
public class HomeWork03 {
public static void main(String[] args) {
String name = "Wang hai Yang";
form(name);
}
/*
* 编写一个方法去完成:
* 1.分割字符串,split(),按照空格去分割
* 2.对分割了的String[],进行格式化 String.format()
* 3.因为最后输出的是中间数的大写首字母,所以先将其所有转成大写,然后去得到第一个索引的字母
* 3.进行校验
* */
public static void form(String str){
if (str == null) {
System.out.println("数组不能为空");
}
String[] str1=str.split(" ");
if (str1.length !=3){
System.out.println("输入格式不正确");
return;
}else
System.out.println(String.format("%s,%s.%c",str1[2],str1[0],str1[1].toUpperCase().charAt(0)));
//str1[1].toUpperCase().charAt(0)解读
//1.先将str[1]字符串全大写
//2.再去获取索引为0的字符
}
}
输出:
Yang,Wang.H
13.11.4 A04:统计个数
题目:
输入字符串,判断里面有多少个大写字母,多少个小写字母,多少个数字
分析:
package com.xiaowang.homWork_;
/**
* @Author 小王
* @DATE: 2022/4/12
*/
/*
* 输入字符串,判断里面有多少个大写字母,多少个小写字母,多少个数字*/
public class HomeWork04 {
public static void main(String[] args) {
String str = "asd1325zxASSA2121";
count(str);
}
/*思路分析:
* 1.编写一个方法去完成需求count(String str)
* 2.通过ASCII码去判断
* 3.可以直接用String方法的charAt()方法去判断
* 也可以先将其字符化char[],通过char[]去判断也行
* */
public static void count(String str){
//1.先判断是否为空
if (str ==null){
System.out.println("字符串输入有误");
return;//为空就不执行了
}
int numA=0;//统计大写
int numa=0;//统计小写
int nums=0;//统计数字
for (int i = 0; i <str.length() ; i++) {
if (str.charAt(i)>='A' && str.charAt(i)<='Z'){
numA++;
}else if (str.charAt(i)>='a' && str.charAt(i)<='z'){
numa++;
} else if (str.charAt(i) >= '1' && str.charAt(i) <= '9') {
nums++;
}
}
System.out.println("大写字母:"+numA+"\t"+"小写字母:"+numa+"\t"+"数字:"+nums);
}
}
输出:
大写字母:4 小写字母:5 数字:8
13.11.5 A05:String内部结构
分析下列代码的输出:
分析:
package com.xiaowang.homWork_;
/**
* @Author 小王
* @DATE: 2022/4/12
*/
public class HomeWork05 {
public static void main(String[] args) {
String s1="xiaowang";//池中s1为"xiaownag"
Animal a = new Animal(s1);//堆中有一个name属性去指向池中的s1,但是a是直接指向堆中name属性的
Animal b = new Animal(s1);//堆中有一个name属性去指向池中的s1,但是a是直接指向堆中name属性的
System.out.println(a==b);//false,这是判断是否是同一个对象,a和b指向不同的name对象属性
System.out.println(a.equals(b));//false,这里判断的还是a和b是否是同一个对象
//一开始可能都会觉得,equals是判断内容是否相等,那是在a和b都是String类型的变量的时候才相等,而这里:
// 因为:a和b都是Animal对象,所以调用的也是Animal对象的equals,
// 又因为Animal对象的equals方法并没有被重写,所以相当于还是判断是否是同一个对象
//底层源码:
/*
public boolean equals(Object obj) {
return (this == obj);//可以看到,返回的是父类obj的equals方法,并没有被重写,,所以比较的是对象
}*/
System.out.println(a.name == b.name);//true,a.name 和 b.name 都是指向池中s1的"xiaownag"
String s4 =new String("xiaowang");//堆中有一个value属性去指向池中的s1,但是s4 是直接指向堆中value属性的
String s5 ="xiaowang";//s5直接指向池中s1的“xaiowang”
System.out.println(s1 == s4);//false,s4指向堆中value,s1指向池中“xiaowang”,所以不等
System.out.println(s4 == s5);//false,s4指向堆中value,s5指向池中s1,所以不等
String t1 ="hello"+s1;
//字符串和对象相加的时候,是在先创建一个StringBuilder,然后相加后,返回给堆中的一个value再传给对象
String t2 ="helloxiaowang";
System.out.println(t1.intern() == t2);//true
//intern():返回字符串对象的规范表示
}
}
class Animal{
String name;
public Animal(String name){
this.name =name;
}
}
输出:
false
false
true
false
false
true
常用类篇幅就在这结束了吖,这是整个Java学习中的第十三章哦,觉得还不错的可以查看我完整的Java笔记哦:
Java学习第二阶段(仍然在继续更新中~~~~)
Java学习第一阶段