包装类:八种基本数据类型对应的引用类型即包装类,包装类有了类的特点,就可以调用类中的方法。
(3)Byte、Short、Integer、Long、Float、Double
idea 中显示上面关系图的方法(以 Integer 为例):
(1)在 Integer 上右键,依次选择 Diagram ➡ Show Diagram
(2)进去之后就能看到下图
(3)若想添加其他类,就选中相关父类,按下空格,输入要添加的类。如添加 Byte 包装类,就在 Number 类上按下空格,输入 Byte。
(4)添加 Byte 类后的效果
(5)拓展1:显示类中的方法,按下图中的图标
拓展2:显示类中的属性,按下图中的图标
装箱:基本类型 ➡ 包装类型
拆箱:包装类型 ➡ 基本类型
Jdk5 前采用手动装箱、拆箱的方式,Jdk5 及以后可以采用自动装箱、拆箱的方式。
下面演示装箱和拆箱,以 int 和 Integer 为例,其他包装类的用法类似。
int n1 = 100;
//Jdk5之前手动装箱: int -> Integer
Integer integer = new Integer(n1);
Integer integer1 = Integer.valueOf(n1);
//Jdk5之前手动拆箱: Integer -> int
int i = integer.intValue();
//Jdk5及之后自动装箱:底层调用的是valueOf方法
Integer integer2 = n1;
//Jdk5及之后自动装箱:底层调用的是intValue方法
int n2 = integer2;
【例】
(1)下面代码是否正确?
Double d = 100d;//ok,自动装箱Double.valueOf(100d);
Float f = 1.5f;//ok,自动装箱Float.valueOf(1.5f);
(2)下面两段代码输出结果分别是什么?
// 三元运算符中,精度最高的是double,所以整体会提高精度
// obj1为1.0
Object obj1 = true? new Integer(1) : new Double(2.0);
System.out.println(obj1);//1.0
Object obj2;
if(true)
obj2 = new Integer(1);
else
obj2 = new Double(2.0);
System.out.println(obj2);//1
以 Integer 和 String 转换为例,其它类似:
(1)包装类(Integer) ➡ String
Integer i = 100;//自动装箱
// 方法1:
String str1 = i + "";
// 方法2:
String str2 = i.toString();
// 方法3:
//String类的valueOf方法,参数类型是Object
//其他包装类的valueOf方法,参数类型都是对应的基本数据类型
//String不在八大包装类范围内,没有对应的基本数据类型,所以valueOf方法的参数不太一样
String str3 = String.valueOf(i);
(2)String ➡ 包装类(Integer)
String str4 = "123456";
// 方法1:
//Integer.parseInt返回的是int,自动装箱后赋值给i1
Integer i1 = Integer.parseInt(str4);
// 方法2:Integer类有两个构造方法,参数分别是int和String类型
Integer i2 = new Integer(str4);
System.out.println(Integer.MIN_VALUE);//返回最小值
System.out.println(Integer.MAX_VALUE);//返回最大值
System.out.println(Character.isDigit('a'));//判断是不是数字
System.out.println(Character.isLetter('a'));//判断是不是字母
System.out.println(Character.isUpperCase('a'));//判断是不是大写
System.out.println(Character.isLowerCase('a'));//判断是不是小写
System.out.println(Character.isWhitespace('a'));//判断是不是空格
System.out.println(Character.toUpperCase('a'));//转成大写
System.out.println(Character.toLowerCase('A'));//转成小写
输出结果:
-2147483648
2147483647
false
true
false
true
false
A
a
看看下面代码,输出什么结果?
Integer i = new Integer(1);
Integer j = new Integer(1);
//"=="判断对象地址是否相等,即是否是同一个对象
System.out.println(i == j); //false
//自动装箱,底层调用Integer.valueOf
//若被装箱的值在-128~127,就直接返回(不太严谨,下面细讲)
//若被装箱的值不在-128~127,就会像上面一样new Integer()
Integer m = 1;
Integer n = 1;
System.out.println(m == n); //true
Integer x = 128;
Integer y = 128;
System.out.println(x == y);//false
可以下断点追踪源码来查看 valueOf 方法,也可以在 ”Integer x = 128“ 的 Integer 上 Ctrl + B,并打开类的 Structure,找到 valueOf 方法。
源码中,IntegerCache.low 是 -128,IntegerCache.high 是 127,可以通过 Ctrl + B 查看。
将鼠标放在 catch 上,点击 “+”,可以查看 catch 数组元素,catch 数组元素是 -128 ~ 127:
在源码中,若被装箱的值在-128~127 时,会从 catch 数组中返回对应元素,看到 catch 数组中的内容后,我们就能理解源码中的处理方式了。
理解了上面的内容后,来看下面几个例子:
//示例一
//只要都是new的,一定不相等
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
//示例三
//-128~127,都是catch数组中的
Integer i5=127;
Integer i6=127;
System.out.println(i5==i6);//T
//示例四
//-128~127以外的,都是new出来的
Integer i7=128;
Integer i8=128;
System.out.println(i7==i8);//F
//示例五
//一个是从catch数组中的,一个是new出来的,是不同的对象
Integer i9=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 对象用于保存字符串,也就是一组字符序列。
字符串常量对象是用双引号括起的字符序列。例如:“你好”、“12.97”、“boy" 等。
字符串的字符使用 Unicode 编码,一个字符占两个字节(字母、汉字都是)。
在前面介绍包装类时,很多类都实现了 Serializable 和 Comparable 接口。String 类同样也实现了这两个接口。那么,实现这两个接口的作用是什么呢?
(1)实现了 Serializable 接口,说明可以串行化 / 序列化,即:可以在网络上传输、保存到文件。
(2)实现了 Comparable 接口,说明对象可以相互比较。
String类有很多构造器(构造器的重载),常用的有:
String s1 = new String();
String s2 = new String(String 初始化字符串);
String s3 = new String(char[] a);
String s4 = new String(char[] a,int startIndex,int count)
String s5 = new String(byte[] b)
String 是 final 类,不能被其他的类继承。
String 类有属性 private final char value[],用于存放字符串内容,要注意:value 是 final 类型,不可以修改(指的是value的指向不能改变,但是单个字符可以改变)。
final char[] value = {'a', 'b', 'c'};
value[0] = 'A';//正确
char[] value2 = {'c', 'd', 'e'};
value = value2;//错误
创建 String 对象的两种方式:
方式一:直接赋值 String s = "hsp";
先从常量池查看是否有 “hsp” 。若有,直接指向;若没有,重新创建,然后指向。s 最终指向的是常量池的空间地址。
方式二:调用构造器 String s2 = new String("hsp");
先在堆中创建对象,对象内含 value 属性,用于指向常量池的 hsp 空间。若常量池没有 “hsp",重新创建;若有,直接通过 value 指向。s2 最终指向的是堆中的对象地址。
在比较对象时,”==“ 比较是地址是否相等,即是否是同一个对象;equals 比较的是对象的内容是否相等。
【例1】
String a = "abc";
String b ="abc";
System.out.println(a.equals(b));//true
System.out.println(a==b);//true
【例2】
String a = "hsp";
String b =new String("hsp");
System.out.println(a.equals(b));//true
System.out.println(a==b);//false
System.out.println(a==b.intern());//true
System.out.println(b==b.intern());//false
注:对象 . intern() 时,会根据对象的内容去常量池中查找该内容的地址,并返回该地址。
【例3】
String s1 = "hspedu";
String s2 = "java";
String s4 = "java";
String s3 = new String("java");
System.out.println(s2 == s3);//false
System.out.println(s2 == s4);//true
System.out.println(s2.equals(s3));//true
System.out.println(s1 == s2);//false
【例4】
Person p1 = new Person();
p1.name = "hspedu";
Person p2 = new Person();
p2.name = "hspedu";
System.out.println(p1.name.equals(p2.name));//true
System.out.println(p1.name == p2.name);//true
System.out.println(p1.name == "hspedu");//true
String s1 = new String("bcde");
String s2 = new String("bcde");
System.out.println(s1==s2);//false
【例1】画出内存
String s = "hello";
s = "haha";
【例2】下面的代码创建了几个对象?
String a = "hello"+"abc";
答:只创建了一个对象。编译器底层做了优化,会判断创建的常量池对象,是否有引用指向。所以不会将 “hello” 与 “abc” 放入常量池。上面的代码会被优化为String a = "helloabc;"
【例3】下面的代码创建了几个对象?画出内存图。
String a = "hello";
String b = "abc";
String c = a + b;
(1)先创建对象 StringBuilder sb = new StringBuilder()
(2)执行 sb.append(a);
(3)再执行 sb.append(b);
(4)最后 String c= sb.toString()
在 toString 源码中可以看到,最后返回的是一个新创建的字符串对象。
所以,内存结构图为:
【例4】分析下面代码的输出
String s1="hspedu";//s1指向池中的"hspedu"
String s2="java";//s2指向池中的"java”
String s5="hspedujava";//s5指向池中的“hspedujava"
String s6=(s1+s2).intern();//s6指向池中的 "hspedujava"
System.out.println(s5 == s6); //true
System.out.println(s5.equals(s6));//true
【例5】画出下列代码的内存结构图
public class Test1 {
String str = new String("hsp");
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){
//在静态方法中访问本类的非静态成员,要先创建对象再访问
//创建对象时,调用顺序:静态代码块和静态属性初始化,普通代码块和普通属性的初始化,调用构造方法
//该类中只依次进行静态属性初始化、调用隐式构造方法(可不分析)
//所以创建对象时,先为str的初始化创建对象,再初始化ch数组
Test1 ex = new Test1();
ex.change(ex.str, ex.ch);
System.out.print(ex.str + " and ");
System.out.println(ex.ch);
}
}
String类是保存字符串常量的。每次更新都需要重新开辟空间,效率较低,因此 java 设计者还提提供了 StringBuilder 和 StringBuffer 来增强 String 的功能,并提高效率(后面我们还会详细介绍 StringBuilder 和 StringBuffer)。
看一段代码:
String s = new String("");
for(int i = 0;i < 80000;i++) {
s += "hello";
}
这段代码一直不停第地给 s 重新赋值,一直创建对象,效率低下。
(1) equals():区分大小写,判断内容是否相等。
String str1 = "hello";
String str2 = "Hello";
System.out.println(str1.equals(str2));//false
(2) equalslgnoreCase():忽略大小写,判断内容是否相等。
boolean res = "john".equalsIgnoreCase("John");
System.out.println(res);//true
(3) length():获取字符串长度。(注意:获取数组长度用的是 arr.length
)
System.out.println("韩顺平".length());//3
(4) indexOf():获取字符或子串在字符串中第一次出现的位置索引(从0开始),若找不到,返回 -1。
String s1 = "wer@terwe@g";
System.out.println(s1.indexOf('@'));//3
System.out.println(s1.indexOf("we"));//0
(5) lastlndexOf():获取字符或子串在字符串中最后一次出现的位置索引(从0开始),若找不到,返回 -1。
String s2 = "wer@terwe@g@";
System.out.println(s2.lastIndexOf('@'));//11
System.out.println(s2.lastIndexOf("we"));//7
(6) substring():截取指定范围的子串。str.substring(n)
从索引为 n 的字符开始,截取到最后;str.substring(n1,n2)
从索引为 n1 的字符开始,截取到索引为 n2 -1 的字符。
String name="hello,张三";
System.out.println(name.substring(6));//输出“张三”
System.out.println(name.substring(2,5));//输出“llo”
(7) trim():去掉字符串前后的空白(空格、\t、\n)
String str = " \t hello \n ";
System.out.println(str);
System.out.println(str.trim());
输出结果:
hello
hello
(8) charAt():获取某索引处的字符,注意不能使用 str[index] 这种方式.。
String str = "0123456";
System.out.println(str.charAt(4));//4
(9) toUpperCase():将字符串中的字母全部转变为大写
String str = "HeLlowOrld";
System.out.println(str.toUpperCase());//HELLOWORLD
(10) toLowerCase():将字符串中的字母全部转变为小写
String str = "HeLlowOrld";
System.out.println(str.toLowerCase());//helloworld
(11) concat():字符串拼接
String str1 = "hello ";
String str2 = "world";
System.out.println(str1.concat(str2).concat(" today"));
//输出:hello world today
(12) replace():s.replace(str1, str2)
将字符串 s 中的所有 str1 替换为 str2
String str = "jack tom jerry tom jerry";
System.out.println(str.replace("tom","smith"));
//输出:jack smith jerry smith jerry
(13) split():使用分割符对字符串进行分割,某些分割符需要转义,比如 “\” 等。
【例1】
String str = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
String[] strArr = str.split(",");
for (int i=0; i<strArr.length; i++){
System.out.println(strArr[i]);
}
输出结果:
锄禾日当午
汗滴禾下土
谁知盘中餐
粒粒皆辛苦
【例2】
String filePath = "F:\\aaa\\bbb";
String[] fileArr = filePath.split("\\\\");
for (int i = 0; i < fileArr.length; i++) {
System.out.println(fileArr[i]);
}
输出结果:
F:
aaa
bbb
(14) toCharArray():将字符串转成字符数组
String str = "hello";
char[] arr = str.toCharArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
输出结果:
h
e
l
l
o
(15) compareTo(): 比较两个字符串的大小。若前者大,返回正数;若后者大,返回负数;若相等,返回 0。
细节:
String str1 = "jack";
String str2 = "tom";
String str3 = "jackabc";
System.out.println(str1.compareTo(str2));//-10
System.out.println(str1.compareTo(str3));//-3
(16) format():将字符串格式化
%s、%d、%.nf、%c 等称为占位符,这些占位符会由后面变量来替换。其中,%.nf 可以被 float 或 double 类型来替换,且会四舍五入。
String name = "john";
int age = 10;
double score = 56.847;
char gender='男';
String info =
"我的姓名是"+name+",年龄是"+age+",成绩是"+score+",性别是"+gender;
System.out.println("info = " + info);
String formatstr = "我的姓名是%s,年龄是%d,成绩是%.2f,性别是%c";
String info2 = String.format(formatstr, name, age, score, gender);
System.out.println("info2 = " + info2);
输出结果:
info = 我的姓名是john,年龄是10,成绩是56.847,性别是男
info2 = 我的姓名是john,年龄是10,成绩是56.85,性别是男
StringBuffer() :指定父类 AbstractStringBuilder 中的 value 字符数组大小为默认值16,内容为空。
StringBuffer stringBuffer = new StringBuffer();
指定父类 AbstractStringBuilder 中的 value 字符数组大小为100,内容为空。
StringBuffer stringBuffer1 = new StringBuffer(100);
指定父类 AbstractStringBuilder 中的 value 字符数组大小为 “指定的字符串长度”+16,内容为 “指定的字符串”。
StringBuffer stringBuffer2 = new StringBuffer("hello");
java.lang.StringBuffer 代表可变的字符序列,可以对字符串内容进行增删。
很多方法与 String 相同,但 StringBuffer 是可变长度的。
StringBuffer 是一个容器。
(1) String ➡ StringBuffer
//方式1
String str = "hello tom";
StringBuffer stringBuffer = new StringBuffer(str);
//方式2
StringBuffer stringBuffer1 = new StringBuffer();
stringBuffer1.append(str);
(2) StringBuffer ➡ String
StringBuffer stringBuffer2 = new StringBuffer("hello");
//方式1
String str1 = stringBuffer2.toString();
//方式2
String str2 = new String(stringBuffer2);
(1) append():追加
StringBuffer s = new StringBuffer("hello");
s.append(',');
s.append("jack").append(100).append(true).append(10.5);
System.out.println(s);//hello,jack100true10.5
(2) delete():删除 [start,end)
的字符
StringBuffer s1 = new StringBuffer("01234567");
s1.delete(2, 5);
System.out.println(s1);//01567
(3) replace():替换[start,end)
的字符
StringBuffer s2 = new StringBuffer("01234567");
s2.replace(2,5,"aaaa");
System.out.println(s2);//01aaaa567
(4) indexOf():查找指定的子串在字符串第一次出现的索引,如果找不到返回 -1
StringBuffer s3 = new StringBuffer("01234567");
System.out.println(s3.indexOf("456"));//4
(5) insert():在指定索引处插入字符串
StringBuffer s4 = new StringBuffer("01234567");
s4.insert(5, "aa");
System.out.println(s4);//01234aa567
(6) length():长度
StringBuffer s5 = new StringBuffer("01234567");
System.out.println(s5.length());//8
【例1】下面代码输出什么
String str = null;
StringBuffer sb = new StringBuffer();
sb.append(str);//把str直接当成”null“字符串追加到了sb的尾部
System.out.println(sb.length());//4
System.out.println(sb);//null
【例2】下面代码是否会抛出异常
String str = null;
StringBuffer sb1 = new StringBuffer(str);
System.out.println(sb1);
上面的代码会报空指针异常,原因是第二行在创建 StringBuffer 对象时,会将父类中的 value 字符数组的大小置为str.length()+16
,而此时的 str 为空,所以报空指针异常。在 StringBuffer 类的构造器中可以看到:
【例3】给定一个数字,小数点前每 3 位用逗号隔开。
String str = "87123564.59";
StringBuffer stringBuffer = new StringBuffer(str);
for (int i = stringBuffer.indexOf(".")-3; i > 0 ; i-=3) {
stringBuffer.insert(i, ",");
}
System.out.println(stringBuffer);//87,123,564.59
StringBuilder 可以在一些场景下替换 StringBuffer 的原因是:两者的继承、实现关系相同:
char[] value
中。因此,字符序列是存放在堆中的。String:不可变字符序列,效率低,但是复用率高(不同变量可以指向常量池中同一个字符串常量)。
StringBuffer:可变字符序列、效率较高(增删)、线程安全
String 使用注意事项,先看一段代码:
string s = "a";
s += "b";
上面代码中,先初始化 s 为 ”a“,之后又更新 s。这样 s 之前指向的字符串就被丢弃。如果是在循环中,就会导致大量废弃字符串对象占用空间,影响程序的性能。因此,如果要对 String 做大量修改,就不要使用 String。
效率:StringBuilder > StringBuffer > String
String、StringBuffer和StringBuilder 的适用场景总结:
Math 类有许多与数学运算有关的方法,如:指数、对数、平方根和三角函数。这些方法一般都是静态方法,直接使用即可。
关于下列方法的返回类型,可以 Ctrl + B 定位到源码中去查看。
(1) abs(): 求绝对值。
int abs = Math.abs(-9);
System.out.println(abs);//9
(2).pow(): 求幂,返回 double 类型。
double pow = Math.pow(-2, 4);
System.out.println(pow);//16.0
(3) ceil():向上取整,返回 double 类型。
double ceil = Math.ceil(-3.0001);
System.out.println(ceil);//-3.0
(4) floor(): 向下取整,返回 double 类型。
double floor = Math.floor(-4.999);
System.out.println(floor);//-5.0
(5) round(): 四舍五入,返回 double 类型。相当于Math.floor(该参数+0.5)
long round = Math.round(-5.001);
System.out.println(round);//-5
(6) sqrt():开平方。
double sqrt = Math.sqrt(9);
System.out.println(sqrt);//3.0
System.out.println(Math.sqrt(-9));//NAN(not a number)
(7) random():生成随机数。Math.random()
生成的是 [0, 1) 的随机数,生成 [a, b] 的随机整数:(int) (a + Math.random() * (b - a + 1))
int a = 2, b = 7;
for (int i = 0; i < 10; i++) {
System.out.print((int) (a + Math.random() * (b - a + 1)) + " ");
}
输出结果:
2 2 6 4 2 7 4 2 4 7
(8) max():求两个数的最大值。有重载的方法,可以求 int、float、double、long 类型的两个数的较大值。
int max = Math.max(2, 9);
System.out.println(max);//9
(9) min():求两个数的最小值。有重载的方法,可以求 int、float、double、long 类型的两个数的较小值。
int min = Math.min(2, 9);
System.out.println(min);//2
Arrays 类包含一些静态方法,用于管理或操作数组(如:排序、搜索)。
(1) toString():返回数组的字符串形式
Integer[] arr = {1, 2, 3, 4, 5};
System.out.println(Arrays.toString(arr));//输出:[1, 2, 3, 4, 5]
为什么这么输出?看 Arrays.toString 方法的源码:
源码的含义是:将数组中的每个元素先转成字符串,再追加到一个 StringBuilder 对象中。在所有元素追加之前和之后,还追加了 “[”
和 “]”
。最后将这个 StringBuilder 对象转成 String 返回。
(2) sort():排排序(默认排序和定制排序)
默认排序:
Integer[] arr = {5, 1, 3, 2, 4};
//因为数组是引用类型,所以通过sort排序后,会直接影响到实参arr
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));//[1, 2, 3, 4, 5]
定制排序:
sort 方法传入两个参数:① 待排序的数组 arr;② 实现了Comparator 接口的匿名内部类,该匿名内部类要实现 compare方法。
从小到大排序:
Integer[] arr = {5, 1, 3, 2, 4};
Arrays.sort(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i1 - i2;
}
});
System.out.println(Arrays.toString(arr));
//[1, 2, 3, 4, 5]
从大到小排序:将 compare 方法中的 return 语句改为return i2 - i1;
(3) binarySearch(): 在升序数组中,用二分查找的方式查找指定元素,并返回索引;若元素不存在,返回 -(low + 1)
,low
是该元素按照大小顺序应该所在的位置。
Integer[] arr = {1, 2, 3, 4, 5};
System.out.println(Arrays.binarySearch(arr, 4));//3
System.out.println(Arrays.binarySearch(arr, 100));//-6=-(5+1)
(4) copyOf():数组元素的复制
newArr = Arrays.copyOf(arr, n)
表示从 arr 数组中,拷贝 n 个元素到 newArr 数组中。
若 n > arr.length,就在新数组后面补 null;
若 n < 0,就抛出异常 “NegativeArraySizeException”。
该方法的底层使用的是 System.arraycopy()。
Integer[] arr = {1, 2, 3, 4, 5};
Integer[] newArr1 = Arrays.copyOf(arr, arr.length);
System.out.println(Arrays.toString(newArr1));
Integer[] newArr2 = Arrays.copyOf(arr, arr.length-1);
System.out.println(Arrays.toString(newArr2));
Integer[] newArr3 = Arrays.copyOf(arr, arr.length+1);
System.out.println(Arrays.toString(newArr3));
输出结果:
[1, 2, 3, 4, 5]
[1, 2, 3, 4]
[1, 2, 3, 4, 5, null]
拷贝元素的个数 < 0 时,抛出 NegativeArraySizeException 异常:
Integer[] arr = {1, 2, 3, 4, 5};
Integer[] newArr4 = Arrays.copyOf(arr, -1);
System.out.println(Arrays.toString(newArr4));
(5) fill():数组元素的填充,可以理解为:替换原来的所有元素
Integer[] arr = new Integer[]{9, 3, 2};
Arrays.fill(arr, 99);
System.out.println(Arrays.toString(arr));
//输出:[99, 99, 99]
(6) equals():比较两个数组元素内容是否完全一致,完全一致返回 true;否则返回false。
Integer[] arr1 = {1, 2, 3, 4, 5};
Integer[] arr2 = {1, 2, 3, 4, 5};
System.out.println(Arrays.equals(arr1, arr2));//true
(7) asList():将一组值,转换成 list。
下面代码中,asList 方法会将(2, 3, 4, 5, 6, 1)数据转成一个 List 集合。
返回的 asList 编译类型为 List(一个接口),该接口下有很多实现子类,asList 运行类型就是其中一个实现子类 ArrayList(Arrays 类中的一个静态内部类)。
List asList = Arrays.asList(2,3,4,5,6,1);
System.out.println("asList="+ asList);
System.out.println("asList的运行类型:"+asList.getClass());
输出结果:
asList=[2, 3, 4, 5, 6, 1]
asList的运行类型:class java.util.Arrays$ArrayList
自定义 Book 类,里面包含 name 和 price 属性。有 4 个书对象,按 price 降序排序。
public class Exercise {
public static void main(String[] args) {
Book[] books = new Book[4];
books[0] = new Book("红楼梦",100);
books[1] = new Book("金瓶梅",90);
books[2] = new Book("青年文摘",5);
books[3] = new Book("Java从入门到放弃",300);
Arrays.sort(books, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Book book1 = (Book) o1;
Book book2 = (Book) o2;
//方法返回值已经固定为int,按原来的思路直接返回book1.getPrice() - book2.getPrice()不可行,因为这是double值
//可以转换思路,先用一个double变量接收该double值,根据double值的正负返回一个正整数、负整数或0
double priceDiff = book1.getPrice() - book2.getPrice();
//如果发现输出顺序与预期相反,调整返回的-1与1的顺序就行
if(priceDiff > 0){
return -1;//只要是负数就行
}else if(priceDiff < 0){
return 1;//只要是正数就行
}else {
return 0;
}
}
});
System.out.println(Arrays.toString(books));
}
}
class Book{
private String name;
private double price;
public Book(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 "Book{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
输出结果:
[Book{name='Java从入门到放弃', price=300.0}, Book{name='红楼梦', price=100.0}, Book{name='金瓶梅', price=90.0}, Book{name='青年文摘', price=5.0}]
(1) exit():一般就用 exit(0) 表示程序退出。0表示一个状态,正常的状态。
System.out.println("ok1");
System.exit(0);
System.out.println("ok2");
输出结果:
ok1
(2) arraycopy():复制数组元素,比较适合底层调用。我们一般使用 Arrays.copyOf 完成复制数组(底层用的就是 System.arraycopy())。
用法:System.arraycopy(源数组, 源数组起始位置, 目标数组, 目标数组起始位置, 待拷贝的元素个数)
,如果拷贝的元素个数过多,就报数组越界异常。
int[] src={1,2,3};
int[] dest = new int[3];//初始时为{0, 0, 0}
System.arraycopy(src, 0, dest, 1, 2);
System.out.println(Arrays.toString(dest));
输出结果:
[0, 1, 2]
(3) currentTimeMillens():返回当前时间距离 1970 年 1 月 1 日 0 时的毫秒数。
System.out.println(System.currentTimeMillis());//1665405672194
(4) gc():运行垃圾回收机制 System.gc();
当需要处理很大的整数时,long 会不够用:
这时,可以用 Biglnteger 类来解决(无论多大的数都可以)。
BigInteger bigInteger = new BigInteger("10000000000000000000");
BigInteger 不能直接加减乘除,要使用对应的方法。
BigInteger bigInteger1 = new BigInteger("10000000000000000000");
BigInteger bigInteger2 = new BigInteger("100");
System.out.println(bigInteger1.add(bigInteger2));
System.out.println(bigInteger1.subtract(bigInteger2));
System.out.println(bigInteger1.multiply(bigInteger2));
System.out.println(bigInteger1.divide(bigInteger2));
输出结果:
10000000000000000100
9999999999999999900
1000000000000000000000
100000000000000000
BigInteger 底层处理的是字符串,处理完之后再转成 BigInteger。
当小数要求位数较多时,double 就不能满足需求了:
double num = 12.12345628746872354395483d;
System.out.println(num);
输出结果:
12.123456287468724
这时可以用 BigDecimal 类来解决。
BigDecimal bigDecimal = new BigDecimal("12.12345628746872354395483");
System.out.println(bigDecimal);
输出结果:
12.12345628746872354395483
BigDecimal 也不能直接加减乘除,要使用对应的方法。
BigDecimal bigDecimal1 = new BigDecimal("12.12345628746872354395483");
BigDecimal bigDecimal2 = new BigDecimal("2");
System.out.println(bigDecimal1.add(bigDecimal2));
System.out.println(bigDecimal1.subtract(bigDecimal2));
System.out.println(bigDecimal1.multiply(bigDecimal2));
//除法直接这样写,可能会撇出异常ArithmeticException
System.out.println(bigDecimal1.divide(bigDecimal2));
输出结果:
14.12345628746872354395483
10.12345628746872354395483
24.24691257493744708790966
6.061728143734361771977415
上面代码中,除法可能会抛出异常。原因是:可能出现除不尽的情况。若除数换成 3 ,就会抛出异常。解决办法是:在 divide 方法的第二个参数处指定精度 BigDecimal.ROUND_CEILING
。这样的话,如果有除不尽的,就会保留分子的精度(小数位数)。
BigDecimal bigDecimal1 = new BigDecimal("12.12345628746872354395483");
BigDecimal bigDecimal2 = new BigDecimal("3");
System.out.println(bigDecimal1.divide(bigDecimal2, BigDecimal.ROUND_CEILING));
输出结果:
4.04115209582290784798495
//小数点后的位数与bigDecimal1相等
BigDecimal 底层处理的是字符串,处理完之后再转成 BigDecimal。
(1) 获取当前系统时间
//不要引入 java.sql.Date,要引入 java.util.Date
Date d1 = new Date();
System.out.println("当前日期:" + d1);
输出结果:
当前日期:Mon Oct 10 22:12:37 CST 2022
默认输出的日期格式是国外的方式,因此通常需要对格式进行转换:
Date d1 = new Date();
SimpleDateFormat sdf= new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
String format =sdf.format(d1);//format:将日期转换成指定格式的字符串
System.out.println("当前日期:"+format);
输出结果:
当前日期:2022年10月10日 10:15:58 星期一
(2) 通过指定毫秒数得到时间
Date d2 = new Date(9234567);
System.out.println("d2:" + d2);
输出结果:
d2=Thu Jan 01 10:33:54 CST 1970
(3) 把一个格式化的 String 转成对应的 Date
String s ="1996年01月01日 10:20:30 星期一";
//在把String->Date时,使用的sdf格式需要和你给的string的格式一样,否则会抛出转换异常
SimpleDateFormat sdf= new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
Date parse = sdf.parse(s);
System.out.println("parse:"+ parse);
输出结果:
parse:Mon Jan 01 10:20:30 CST 1996
第二代日期类,主要就是 Calendar 类(日历)。继承和实现关系如下图:
Calendar 类是一个抽象类
Calendar 类的构造器是 protected,可以通过 getInstance()来获取实例(是Calendar子类的实例吗,Calendar 作为一个抽象类不能直接实例化啊)
Calendar c = Calendar.getInstance();
System.out.println(c);
输出结果:
java.util.GregorianCalendar[time=1667034489446,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai"...
Calendar 类提供了大量的方法和字段,但没有专门的格式化方法,所以需要自行组合显示。
Calendar c = Calendar.getInstance();
System.out.println("年:"+c.get(Calendar.YEAR));
//月份从0开始
System.out.println("月:"+(c.get(Calendar.MONTH)+1));
System.out.println("日:"+ c.get(Calendar.DAY_OF_MONTH));
//HOUR_OF_DAY是24进制,HOUR是12进制
System.out.println("小时:"+c.get(Calendar.HOUR_OF_DAY));
System.out.println("分钟:"+c.get(Calendar.MINUTE));
System.out.println("秒:"+c.get(Calendar.SECOND));
System.out.println(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-" + c.get(Calendar.DAY_OF_MONTH));
输出结果:
年:2022
月:10
日:29
小时:17
分钟:20
秒:36
2022-10-29
JDK 1.0 中包含了一个 java.util.Date 类,但是它的大多数方法已经在 JDK1.1 引入 Calendar 类之后被弃用了。而 Calendar 也存在问题:
(1) 可变性:日期和时间这样的类应该是不可变的,但 Calendar 是可变的。
(2) 偏移性:月份都从 0 开始,比较奇怪。
(3) 格式化:没有用于格式化的方法。
(4) 不是线程安全的;不能处理闰秒等(每隔2天,多出1s)。
**(1) LocalDate、LocalTime、LocalDateTime **
LocalDate:获取日期字段,只包含年月日
LocalTime:获取时间字段,只包含时分秒
LocalDateTime:获取日期+时间
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
System.out.println("年:"+ldt.getYear());
System.out.println("月:"+ldt.getMonth());//输出月份的英文
System.out.println("月:"+ldt.getMonthValue());//输出月份的值
System.out.println("日:"+ldt.getDayOfMonth());
System.out.println("时:"+ldt.getHour());
System.out.println("分:"+ldt.getMinute());
System.out.println("秒:"+ldt.getSecond());
输出结果:
2022-10-29T17:52:42.107
年:2022
月:OCTOBER
月:10
日:29
时:17
分:52
秒:42
LocalDate 与 LocalTime 的使用与 LocalDateTime 相同,方法调用方式也一样。
LocalDate ld = LocalDate.now();
LocalTime lt = LocalTime.now();
(2) DateTimeFormatter 格式日期类
LocalDateTime ldt = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
String strDate = dtf.format(ldt);
System.out.println(strDate);
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String strDate2 = dtf2.format(ldt);
System.out.println(strDate2);
输出结果:
2022年10月29日 18时47分59秒
2022-10-29 18:47:59
(3) Instant 时间戳
Instant 时间戳与 Date 类转换的方式:
//通过静态方法now()获取当前的时间戳对象
Instant now = Instant.now();
System.out.println(now);
//Instant → Date
Date date = Date.from(now);
System.out.println(date);
//date → Instant
Instant instant = date.toInstant();
System.out.println(instant);
输出结果:
2022-10-29T10:57:23.719Z
Sat Oct 29 18:57:23 CST 2022
2022-10-29T10:57:23.719Z
(4) plus / minus 增加 / 减少日期的某个部分
LocalDateTime ldt = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime plusLdt = ldt.plusDays(890);
System.out.println("890天后:"+dtf.format(plusLdt));
LocalDateTime minusLdt = ldt.minusMinutes(3456);
System.out.println("3456分钟前:"+dtf.format(minusLdt));
输出结果:
890天后:2025-04-06 19:09:55
3456分钟前:2022-10-27 09:33:55
【例1】将字符串中的指定部分进行反转。比如:将 ”abcdef“ 反转为 “aedcbf“。
通过编写方法public static String reverse(String str,int start,int end)
实现。
public class StrReverse {
public static void main(String[] args) {
String str = "abcdef";
str = reverse(str, 1, 4);
System.out.println(str);
}
public static String reverse(String str, int start, int end){
//String转字符数组
char[] charArr = str.toCharArray();
for (int i = start, j = end; i < j; i++, j--) {
char temp = charArr[i];
charArr[i] = charArr[j];
charArr[j] = temp;
}
//字符数组转String
return new String(charArr);
}
}
输出结果:
aedcbf
增加异常处理机制后的代码:
public class StrReverse {
public static void main(String[] args) {
String str = "abcdef";
try {
str = reverse(str, 1, 4);
} catch (Exception e) {
System.out.println(e.getMessage());
return;
}
System.out.println(str);
}
public static String reverse(String str, int start, int end){
if (!(str!=null && start >= 0 && start < end && end < str.length())){
throw new RuntimeException("参数不正确");
}
//如果上面抛出异常,下面的代码就不再执行
char[] charArr = str.toCharArray();
for (int i = start, j = end; i < j; i++, j--) {
char temp = charArr[i];
charArr[i] = charArr[j];
charArr[j] = temp;
}
return new String(charArr);
}
}
【例2】输入用户名、密码、邮箱,如果信息录入正确,则提示注册成功,否则生成异常对象。要求:
(1)用户名长度为 2 或 3 或 4
(2)密码的长度为 6,要求全是数字
(3)邮箱中包含 @ 和 . 并且 @ 在 . 的前面
public class Register {
public static void main(String[] args) {
boolean res = false;
try {
//该句出现异常时,try中的剩余语句不再执行
regs("tom", "123456", "[email protected]");
System.out.println("注册成功");
} catch (Exception e) {
System.out.println(e.getMessage());//输出错误信息
}
}
public static void regs(String userName, String pwd, String email){
//过关斩将法
if (userName==null || pwd==null || email==null){
throw new RuntimeException("信息不能为空");
}
if (!(userName.length()>=2 && userName.length()<=4)){
throw new RuntimeException("用户名不正确");
}
if (!(pwd.length() == 6 && isDigit(pwd))){
throw new RuntimeException("密码不正确");
}
int atIndex = email.indexOf('@');
int dotIndex = email.indexOf('.');
if (!(atIndex!=-1 && dotIndex!=-1 && atIndex<dotIndex)){
throw new RuntimeException("邮箱不正确");
}
}
private static boolean isDigit(String pwd) {
char[] charArray = pwd.toCharArray();
for (int i = 0; i < pwd.length(); i++) {
if (charArray[i]<'0' || charArray[i]>'9'){
return false;
}
}
return true;
}
}
【例3】编写 Java 程序,输入形式为:Willian Jefferson Clinton 的人名,以 Clinton,Willian.J 的形式打印出来。其中 .J 是中间单词的首字母。
public class OptName {
public static void main(String[] args) {
String str = "Willian Jefferson Clinton";
try {
printName(str);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
private static void printName(String str) {
if (str == null){
throw new RuntimeException("名字不能为空");
}
String[] strArr = str.split(" ");
if (strArr.length != 3){
throw new RuntimeException("名字应为三个单词");
}
String format = String.format("%s,%s.%c", strArr[2], strArr[0], strArr[1].toUpperCase().charAt(0));
System.out.println(format);
}
}
【例4】输入字符串,判断里面有多少个大写字母,多少个小写字母,多少个数字。
public class Homework4 {
public static void main(String[] args) {
String str = null;
try {
printNum(str);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
private static void printNum(String str) {
if (str == null){
throw new RuntimeException("字符串不能为null");
}
int upperCount = 0;
int lowerCount = 0;
int digitCount = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i)>='A' && str.charAt(i)<='Z'){
upperCount++;
}else if (str.charAt(i)>='a' && str.charAt(i)<='z'){
lowerCount++;
}else if (str.charAt(i)>='0' && str.charAt(i)<='9'){
digitCount++;
}
}
System.out.println(upperCount);
System.out.println(lowerCount);
System.out.println(digitCount);
}
}
小结 String 的遍历:
str.toCharArray()
将 String 转成字符数组,再遍历字符数组;str.charAt(i)