Object类
== 运算符
- == 运算符既可以判断基本类型,又可以判断引用类型。
2. 如果判断基本类型,那么判断的是 值是否相等。
3. 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象,如果希望判断内容是否相同,则需要重写。int i = 10;
double d = 10.0;
System.out.println(i==d)//true
public class Test {
public static void main(String[] args) {
NotePad a = new NotePad();
Computer b = a;
//向上转型,a和b都指向同一个NotePad对象
System.out.println(a==b); //true 因为a和b地址相同
}
}
equals方法
- 是Object类的方法,只能判断引用类型。
2. 默认判断的是地址是否相等,子类往往重写该方法,用于判断内容是否相等(比如String类)。
需要注意一点,String类是否用new来初始化还是有区别的。比如str1和str2,实际上都指向同一块内容为”shang”的区域,因此str1和str2地址相同。而str3和str4,虽然指向的内容都是相同,但是由于用到了new,开辟了两个String对象,因此str3和str4指向两个内容相同但地址不同的String对象。String str1 = "shang";
String str2 = "shang";
System.out.println(str1 == str2); //true,str1和str2地址相同
System.out.println(str1.equals(str2)); //true
String str3 = new String("shang");
String str4 = new String("shang");
System.out.println(str3 == str4); //false
System.out.println(str3.equals(str4)); //true
System.out.println(str1 == str3); //false
System.out.println(str1.equals(str3)); //true
重写equals方法
用于判断对象的内容是否相同。public boolean equals(object obj){
if(this == obj){
return true;
} //如果是地址相同那么就返回true
if(!(obj instanceof Doctor)){
return false;
}//如果obj不是Doctor类或其子类,那么返回false
Doctor doctor = (Doctor)obj; //传入的参数向下转型
return this.name.equals(doctor.name)&&this.age == doctor.age;
}
查看JDK源码
1. 一般来说 IDEA 配置好JDK后,jdk的源码也就自动配置好了。
2. 如果没有的话点击 菜单File —> Project Stucture —> SDKs —> Sourcepath 然后点击加号 —> 找到jdk安装目录,把 src.zip 和 javafx-src.zip 加入即可。
3. 在需要查看某个方法源码时,将光标放在该方法上,输入 ctrl + b 即可。或者在方法上点击右键 —> go to —> Declarartion or Usages。
hashCode方法
- 提高具有哈希结构的容器的效率(如HashMap,HashTable等)。
2. 两个引用如果指向的是同一个对象,则哈希值肯定是一样的。
3. 两个引用如果指向的是不同对象,则哈希值是不一样的。
4. 哈希值主要是根据地址号计算的(通过将该对象的内部地址转换成一个整数实现的),不能将哈希值等价于地址。
5. hashCode如果有需要的话,也会重写。public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) { //value就是把对象toString后的结果
char val[] = value; //将字符串转化为字符数组
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i]; //哈希值计算方法
}
hash = h;
}
return h;
}
toString方法
默认返回:全类名(包名+类名)+ @ + 哈希值的十六进制。
重写:子类往往重写toString方法,用于返回对象的属性信息。public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
自动调用:当输出对象时,toString方法会被自动调用(重写会影响)。public class Test {
public static void main(String[] args) {
Person wang = new Person(10, "wang", 20);
System.out.println(wang);
//com.shang.Person@1b6d3586
}
}
finalize方法
- 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,写自己的业务逻辑代码(数据库连接,打开文件等),如果不重写就会调用 Object类的finalize。
2. 什么时候被回收:当某个对象没有任何引用时,jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法。
输入一个 f 就可以重写 finalize方法了。 finlize的调用类似C++的析构函数。public static void main(String[] args) {
Person p1 = new Person();
p1 = null; // 这时,p1就是一个垃圾,垃圾回收器会销毁对象
}
// 在销毁对象前,会调用该方法的finalize方法
3. 垃圾回收机制的调用,是由系统来决定(有自己的GC算法),也可以通过System.gc() 主动触发垃圾回收机制。(在实际开发中,几乎不会运用 finalize)
包装类(封装类型)
针对八种基本类型相应的引用类型——包装类。有了类的特点,就可以调用类中的方法。
装箱与拆箱
在JDK5之前,进行的是手动的装箱与拆箱。所谓装箱,就是进行 int -> Integer 的转换,而拆箱就是 Integer -> int 之间的转换(其它基本元素类似)。
int n1 = 100;
Integer integer = new Integer(n1);
Integer integer1 = Integer.valueOf(n1); //手动装箱(把基本数据变成对象)
int n2 = integer.intValue(); // 手动拆箱
在JDK5后,就可以自动装箱和自动拆箱。自动将一个原始数据类型转换为一个封装类型称为自动装箱,自动将一个封装类型转换为一个原始数据类型被称为自动拆箱。
装箱和拆箱 是编译器要做的工作,而不是虚拟机。
int n1 = 100;
Integer integer = n1; //自动装箱,底层使用的是 Integer.valueOf(n1)方法
int n2 = integer; //自动拆箱,底层使用的是 intValue()方法
Object obj1 = true?new Integer(1):new Double(2.0);
System.out.println(obj1); //最大精度为double,输出1.0
注意输出的是1.0而不是1,因为三元运算符是一个整体,在执行时会把所数据类型的精度上升到最大的那个。
Object obj1 = true?new Integer(1):new Integer(2);
System.out.println(obj1); // 最大精度为int,输出1
Integer与String相互转换
Integer -> String:
Integer n = 100;
String str1 = n + ""; // 第一种方法
String str2 = n.toString(); // 第二种方法
String str3 = String.valueOf(n); // 第三种方法,valueOf也可以使用int作为参数
String -> Integer:
String str = "12345";
Integer n1 = Integer.parseInt(str); // 第一种方法
Integer n2 = new Integer(str); // 第二种方法,构造器
Integer n3 = new Integer(str); // 第三种方法
常用方法
Integer创建机制
public static void main(String[] args) {
Integer a = new Integer(1);
Integer b = new Integer(1);
System.out.println(a==b); //a和b都是new出的对象,地址肯定不相同
Integer m = 1; //底层是Integer.valueOf
Integer n = 1;
System.out.println(m==n);
Integer x = 128;
Integer y = 128;
System.out.println(x==y);
}
对于使用 Integer.valueOf 创建的对象,注意 m和n 与 x和y 之间的区别:当使用自动装箱时,底层调用的是Integer的valueOf方法,它会根据传入数值的大小决定是否new一个对象。
public static Integer valueOf(int i) { //源码
if (i >= IntegerCache.low && i <= IntegerCache.high)
//low是-128,high是127
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
通过查看源码得知,如果传入的参数在 -128~127之间,不会创建新的对象,而是直接调用内部数组cache,返回一个数(但还是Integer对象,只不过地址相同)。而在其他范围的参数就会new一个新对象。
因此m和n是一个相同的Integer对象。而x和y则是地址不同的对象。
如果基本数据类型和类比较是否相等,那么就比较数值是否相等。
public static void main(String[] args) {
Integer n1 = 127;
int n2 = 127;
System.out.println(n1==n2); //true
Integer n3 = 128;
int n4 = 128;
System.out.println(n3==n4); //true
}
String类
基本信息
- String 对象用于保存字符串,也就是一组字符序列。
2. 字符串常量就是双引号括起的字符序列,比如 “jack”。
3. 字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节。
4. String 类有很多构造器。String s5 = new String(byte[] b);
- String是final类,不能被其他的类继承。
6. String 有属性 private final char value[] 用于存放字符串内容(所以本质还是字符数组)。注意value是final类型,由于数组名相当于数组首地址,因此value不能指向新的地址,但是单个字符内容是可以变化的。创建方式
方式一:先从常量池查看是否有 “hsp” 数据空间,如果有,字符串直接指向该空间。如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址。
方式二:先在堆中创建空间,里面维护了value属性,如果常量池里有 “hsp”,value指向常量池的”hsp”地址。如果常量池没有 “hsp”,重新创建,然后再指向。s2 最终指向的是堆中的空间地址。
综合训练 P497String 对象特性
一共创建了两个对象,”hello”与”haha”,值得注意的是,上面说的String的value属性是final类型,不能更换地址,指的是”hello”与”haha”不能更换地址。这两个才是String对象,而s1只是一个指向String对象的指针罢了,因此s1可以指向不同的对象,而”hello”和”haha”并不能更换地址。String s1 = "hello";
s1 = "haha";
创建了一个对象。String a = “hello” + “abc” 会被优化等价于 String a = “helloabc”。对于这种字符串常量相加的情况,编译器会自动优化,然后就等价于一个新的字符串常量对象赋给指针。String a = "hello" + "abc"; // 字符串常量相加
当将一个字符串与一个非字符串的值进行拼接时,后者会转换为字符串。
对于字符串对象相加,最关键的问题就是分析出 String c = a + b是怎么执行的。String a = "hello";
String b = "abc";
String c = a + b; //字符串对象相加
// 底层是
StringBuilder s = new StringBuilder();
s.append(a);
s.append(b);
c = s.toString();
public String toString() {
return new String(value, 0, count); //截取 0~count-1
}
底层是创建了一个 StringBuilder类,调用append方法把几个字符串对象相加,然后再调用toString方法返回一个新的字符串对象给c。
重要规则:String c1 = “ab” + “cd”; 常量相加,c1指向的是常量池中的地址。 String c2 = a + b; 对象相加,c2指向的是堆中的地址(对象地址)。
因此总共创建了三个对象(a,b,c分别对应的String,StringBuilder类调用后就销毁了)
分析: 主方法创建了一个Test对象,ex为对象指针,在栈中。对象实体在堆中。而在对象实体里,str为String类指针,指向String类的value属性,而value又指向常量池中的 “hsp”。ch是一个数组指针,指向堆中的数组。public class Test {
String str = new String("hsp");
final char[] ch = {'j','a','v','a'};
public void change(String str,char ch[]){ //注意str是形参,不同于真正的str
str = "java";
ch[0] = 'h';
}
public static void main(String[] args) {
Test ex = new Test();
ex.change(a.str,a.ch);
System.out.println(ex.str + " " + ex.ch);
}
}
然后调用ex的change方法,会在栈中开辟一个方法区,在方法区中,str和ch都是形参(当然也可以改名), str本来指向value, 更改后指向常量池中的”java”(但原先的str没变化!仍然指向value),但ch因为也指向字符数组,因此修改之后保持同步(注意final表示指针的指向不能更改,而不是指针指向的内容不能修改)。调用完方法后,形参被销毁。
最终输出 hsp hava
String类常用方法
使用split时请注意:public static void main(String[] args) {
String s = "aaa aaa aaa ";
String[] ss = s.split(" ");
for(int i=0;i<ss.length;i++){
System.out.println(ss[i]);
}
}
“aaa aaa aaa “,中间有三个空格,因此第一个空格返回”aaa”,而第二第三个就返回一个空串,并不是把所有空格都去除了,要考虑到空串的情况。
注:如果想对String进行操作,应该先用 toCharArray 方法把String转化成一个字符数组,对这个字符数组进行操作后再转换为String。 ```java String str = “abcdef”; char[] chars = str.toCharArray(); //字符串转换为字符数组 ……String format = String.format("%s,%s .%c",name,job,id);
System.out.println(format);
// 对字符数组进行一系列操作 String str1 = new String(chars); // 由字符数组建立一个新数组
<a name="lQHCk"></a>
### 空串与null串
空串是一个Java对象,有自己的串长度(0)和内容(空),**null串是指一个字符串中放入的特殊值null,表示目前没有任何对象与该变量关联(注意这两个是不同的概念!)。**
```java
if(str == null) //判断是否为null串
if(str.length()==0) //判断是否为空串
if(str.equals("") //判断是否为空串
码点与代码单元
char数据类型是一个采用UTF-16编码表示Unicode码点的代码单元。最常用的Unicode字符使用一个代码单元就可以表示(a,b,c等),而辅助字符(𝕆等)需要两个代码单元表示。
码点:是指一个编码表中的某个字符对应的代码值。(也就是字符在Unicode表中的位置)
在Java中一个Unicode占2个字节(byte),一个字节等于8比特位(bit),因此,每个Unicode码占用16个比特位。若一个字符的代码长度为16位,则为一个代码单元。若一个字符的代码长度有两个16的代码长度编码,则该字符有两个代码单元。
获取方法:
String.length():返回字符串代码单元的个数。
String.codePointCount(int startIndex,int endIndex):返回startInde到endIndex-1之间的码点个数。(也就是有多少个字符,因为一个字符对应一个位置(代码值))
String.charAt(int index):返回给定位置的代码单元,尽可能不要调用这个方法。
String.codePointAt(int index):返回给定位置的码点(也就是该字符在Unicode中的位置)
String.offsetByCodePoints(int startIndex,int cpCount):返回从stratIndex码点开始,cpCount个码点后的码点索引。
举例:hi𝕆 这个字符串实际上有三个码点,四个代码单元。
1.测试length函数和codePointCount函数
public class HelloJava {
public static void main(String[] args) {
String greeting="hi𝕆";
int t=greeting.length();
int h=greeting.codePointCount(0, t);
System.out.println(t);
System.out.println(h);
}
}
首先测试的是测量字符串长度的两种方法——用length方法得出字符串代码单元个数为4,因为𝕆这个符号是由两个代码单元得到的。而用count方法得出字符串码点个数为3,也就是有多少个字符。
2.测试charAt函数
public class HelloJava {
public static void main(String[] args) {
String greeting="hi𝕆";
char a=greeting.charAt(0);
char b=greeting.charAt(1);
char c=greeting.charAt(2);
char d=greeting.charAt(3);
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
由于charAt函数是返回给定位置的代码单元,因此在第2个和第三个位置返回的是相同的代码单元。charAt可以在没有辅助元素的字符串中使用。
3.测试codePointAt函数
public class HelloJava {
public static void main(String[] args) {
String greeting="hi𝕆";
int a=greeting.codePointAt(0);
int b=greeting.codePointAt(1);
int c=greeting.codePointAt(2);
int d=greeting.codePointAt(3);
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
codePointAt函数返回的是序号对应的字符在Unicode表中的位置,可以看出h和i位置是挨在一起的,并且𝕆符号是由两个不同代码单元组成的。
4.测试offsetByCodePoints函数
public class HelloJava {
public static void main(String[] args) {
String greeting="hi𝕆";
int a=greeting.offsetByCodePoints(0, 0);
int b=greeting.offsetByCodePoints(0, 1);
int c=greeting.offsetByCodePoints(0, 2);
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
经过的是码点数而不是代码单元,因此这个就可以看作是从序号为startIndex开始,经过cpCount个位置,对应的码点的序号。与代码单元就没有关系了。
总结:
String.codePointCount(int startIndex,int endIndex) 是最标准的求字符串长度的函数
int cpCount = String.codePointCount(0,String.length());
String.codePointAt(int index) 是最标准的求码点在Unicode位置的函数
String.offsetByCodePoints(int startIndex,int cpCount) 是最标准的求码点对应序号的函数
想要得到第i个码点,应该使用下列语句
int index = String.offsetByCodePoints(0,i);
int cp = String.codePointAt(index);
StringBuffer
基本介绍
StringBuffer的父类有value属性用于存放字符串,这个value不是final的。
String与StringBuffer
StringBuffer里的value属性只有在内存不够需要另外开辟空间时,才会更改指向。
构造器
String与StringBuffer相互转换
String转StringBuffer
String str = "青眼究极龙";
// 第一种方式:直接调用构造器
StringBuffer ss = new StringBuffer(str);
// 第二种方式:用append方法 (前提是先new出一个StringBuffer对象)
StringBuffer ss2 = new StringBuffer();
ss2 = ss2.append(str);
StringBuffer转String
// 第一种方式:StringBuffer的 toString方法
String s = ss.toString();
// 第二种方式:直接调用构造器
String s1 = new String(ss);
常用方法
// append方法
StringBuffer a = new StringBuffer("Hello");
a.append(",World").append("!"); //可以连续调用
System.out.println(a); // Hello,World!
String str = null;
StringBuffer ss = new StringBuffer();
ss.append(str); //底层调用的是 父类的 appendNull方法,传入null时自动转化为字符串
System.out.println(ss.length()); // 输出4
System.out.println(ss); //输出 null
StringBuffer ss2 = new StringBuffer(str); //底层调用 super(str.length()+16);
//抛出 NullPointerException
源代码
// delete方法 删除[a,b)的元素
StringBuffer b = new StringBuffer("AAABBBBCC");
System.out.println(b.delete(3,7)); // 删除区间 [3,7)的元素 输出 AAACC
// replace方法 把[0,4)的字符串替换为指定字符串
StringBuffer c = new StringBuffer("BlueEyes");
System.out.println(c.replace(0,4,"Red")); //输出 RedEyes
// indexOf方法,返回字串第一次出现的位置
StringBuffer d = new StringBuffer("AAAC");
System.out.println(d.indexOf("AAA")); //输出0
// insert方法,在指定索引插入指定字符串
System.out.println(d.insert(0,"qqq"));//在索引为0的位置插入字符串,其他的后移 AAACqqq
StringBuilder
3. 实现了 Serializable,说明StringBuilder对象是可以串行化(对象可以网络传输,可以保存到文件)。
4. StringBuilder对象字符序列仍然是存放在其父类的 char[] value。因此字符序列是在堆中。
5. StringBuilder的方法没有做互斥处理(没有synchronized关键字),因此应在单线程的情况下使用。
String,StringBulider,StringBuffer比较
效率
StringBuilder > StringBuffer > String
基本概念
使用原则
注意事项
Math类
- abs 绝对值
System.out.println(Math.abs(-11)); //输出11
- pow 求幂
3. ceil 向上取整 floor 向下取整 round 四舍五入(实际上是 floor(n + 0.5))System.out.println(Math.pow(2,3)); //2的三次方
System.out.println(Math.ceil(1.1));
System.out.println(Math.floor(1.1));
System.out.println(Math.round(1.4));
// Math.floor(n+0.5)
System.out.println(Math.round(1.5));
- sqrt 求开方
5. random 求随机数,返回一个 [0,1) 之间的double数。System.out.println(Math.sqrt(9));
获取 [a,b] 之间的一个随机整数:公式为 int num = (int)(a + Math.random()*(b - a + 1));
返回 [2,6]之间的整数实际上就等于返回 [2,7)之间的整数。System.out.println((int)(2 + Math.random()*(6-2+1))); //返回[2,6]之间的数
6. max 求最大值,min 求最小值。Arrays类
Arrays里面包含了一系列静态方法,用于管理或操作数组(比如排序和搜索)toString 返回数组的字符串形式
int[] a = {1,2,3,4,5};
System.out.println(Arrays.toString(a)); // 输出 [1, 2, 3, 4, 5]
sort排序(自然排序和定制排序)
因为数组是引用类型,因此sort会直接改变原数组。
需要重点理解的就是定制排序:它不仅需要传入一个数组,还需要实现一个匿名内部类(通过传入一个接口 Comparator 实现),传入的数组不能是基本类型,必须是包装类(如Integer),因为用到了泛型。int[] a = {31,2,5,999,-3};
Arrays.sort(a); //自然排序,从小到大排列
System.out.println(Arrays.toString(a));// [-3, 2, 5, 31, 999]
对 return o2 - o1 的理解:实际上sort调用了父类的一个 binarySort的方法,而 o2 - o1的结果会影响方法的走向。返回负数的时候,第一个参数排在前面。返回正数的时候,第二个参数排在前面。返回0的时候,谁在前面无所谓。public static void main(String[] args) {
Integer[] a = {31,2,5,999,-3}; // 不能是基本类型
Arrays.sort(a, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1; // 从大到小排
}
});
System.out.println(Arrays.toString(a)); //[999, 31, 5, 2, -3]
}
在C++中,实现自定义排序则是通过自定义函数cmp。
而在Java并不能用boolean来代表整形,因此只能用相减是否为正来判断该走哪一步。只要记住返回 o2 - o1 是从大到小排就行了。 因为返回负数,第一个参数排在前面,因此当需要从大到小排,也就是当o1>o2时,需要让o1排在前面,那么就返回一个负数。故 return o2-o1;int cmmp(object a,object b){
return a>b; // 从大到小排序
// 从小到大排序是 return a<b;
}
3. 用冒泡排序模拟定制排序
public static void bubbleSort(Integer a[],Comparator b){
for(int i = 0;i<a.length-1;i++){
for(int j = 0;j<a.length-i-1;j++){ //冒泡排序
if(b.compare(a[j],a[j+1])<0){ //根据compare的返回结果判定是否交换
// 这与真正的compare思想一致
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}//调用Comparator接口的compare方法
}
}
}
根据compare的具体实现,n1 - n2 < 0时进行交换,即n1小于n2时交换,也就是说最后是从大到小排列。
4. binarySearch 二分查找
通过二分搜索法进行查找,返回对应元素的索引,要求必须排好序。如果该数组中不存在该元素,则返回 -(low + 1),其中low最终是 查找元素应该位于的索引。
Integer[] a = {1,2,3,4,5};
int index1 = Arrays.binarySearch(a,5); // 输出4
int index2 = Arrays.binarySearch(a,6); // 输出-6 ,如果6存在的话索引应该是5
5. copyOf 数组元素的复制
Integer[] a = new Integer[]{1,2,3,4,5};
Integer[] newArr = Arrays.copyOf(a,3); //复制3个元素
System.out.println(Arrays.toString(newArr)); //[1, 2, 3]
如果拷贝的长度 > a.length ,那么就在新数组的后面增加 null,如果拷贝长度 < 0,就会抛出异常NegativeArraySizeException。
Integer[] newArr = Arrays.copyOf(a,10);
//[1, 2, 3, 4, 5, null, null, null, null, null]
Integer[] newArr = Arrays.copyOf(a,-1);
// Exception in thread "main" java.lang.NegativeArraySizeException
6. fill 数组元素的填充
Integer[] a = new Integer[]{1,2,3,4,5};
Arrays.fill(a,1000);
System.out.println(Arrays.toString(a)); // [1000, 1000, 1000, 1000, 1000]
7. equals 比较两个数组元素内容是否完全一致
Integer[] a = new Integer[]{1,2,3,4,5};
Integer[] b = new Integer[]{1,2,3,4,5};
Integer[] c = new Integer[]{1,3,2,4,5};
System.out.println(Arrays.equals(a,b)); // true
System.out.println(Arrays.equals(a,c)); // false,顺序不一样也不行
8. asList 将一组值转换成list(链表)
asList编译类型为 List(接口),运行类型为 java.util.Arrays$ArrayList,是Arrays类的。
List<Integer> asList = Arrays.asList(2,3,4,5,6,1);
System.out.println(asList.getClass()); // class java.util.Arrays$ArrayList
System类
- exit 退出当前程序。
2. arraycopy:复制数组元素,比较适合底层调用。一般还是使用 Arrays.copyOf 完成复制数组。System.exit(0); // 0表示一个正常的状态。
src:源数组(提供数据) srcPos:从源数组的哪个索引位置开始拷贝 dest:目标数组。 destPos:从源数组的数据拷贝到目标数组的哪个索引 length:从源数组拷贝多少个数据。System.arraycopy(src,srcPos,dest,destPos,length);
3. currentTimeMillens:返回当前时间距离1970-1-1的毫秒数。System.out.println(System.currentTimeMillis());
- gc:运行垃圾回收机制 System.gc();
大数类
1. BigInteger适合保存比较大的整形
2. BigDecinal适合保存精度更高的浮点型。
大数不能用符号加减乘除,而需要调用方法,这里只拿BigDecinal进行演示:
BigDecimal a = new BigDecimal("1.23213131391123131");
BigDecimal b = new BigDecimal("2.22222");
System.out.println(a.add(b)); //加
System.out.println(a.subtract(b)); //减
System.out.println(a.multiply(b)); //乘
这里只需要注意一点:除法的结果可能是个无限小数,如果是这样的话会报错。为了避免报错,我们可以指定精度(保留分子的精度)。
System.out.println(a.divide(b,BigDecimal.ROUND_CEILING)); //保留分子精度
Date类
第一代日期类
引用 java.util.Date(不要引入sql的Date类)
Date a = new Date();
Date b = new Date(111111);//1970-1-1开始经过的毫秒数
System.out.println(a); //用国外的方式输出当前日期 Wed Sep 29 21:02:51 CST 2021
System.out.println(b);
System.out.println(a.getTime()); //获取某个时间对应的毫秒数
第二代日期类
1. Calendar是一个抽象类,并且构造器为 private。
2. 可以通过 getInstance() 来获取实例。
3. 提供大量的方法和字段提供给程序员。
4. Calendar 没有专门的格式化方法,所以需要程序员自己来组合显示。
public static void main(String[] args) {
// Calendar a = new Calendar(); //报错,因为抽象类需要实现方法(但是超级多)
Calendar a = Calendar.getInstance();// 正确的获取对象方法
System.out.println(a); //输出全部信息
System.out.println(Calendar.YEAR);
System.out.println(a.get(Calendar.YEAR)); //输出年份
System.out.println(a.get(Calendar.MONTH) + 1); // 加一是因为从0开始计数
System.out.println(a.get(Calendar.DAY_OF_MONTH)); //输出第几天
System.out.println(a.get(Calendar.HOUR)); //输出今天的小时(12小时表示法)
System.out.println(a.get(Calendar.HOUR_OF_DAY)); //(24小时表示法)
System.out.println(a.get(Calendar.MINUTE)); //小时的分
System.out.println(a.get(Calendar.SECOND)); //分的秒
}
注意,如果想输出当前年份,光输出 Calendar.YEAR 是不行的,因为 Calendar.YEAR 等只是一个数(1,2,3,4等),而需要调用get方法才能正确输出对应信息(里面有类似的witch语句,根据数字输出对应的东西)。
第三代日期类
1. LocalDateTime类,用于获取年月日和时分秒。
//LocalDateTime ldt = new 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.getHour()); // 输出小时(当然还能得到分和秒)
...
2. LocalDate类,只能获取年月日。LocalTime类,只能获取时分秒。
LocalDate now = LocalDate.now(); //用于获取年月日
LocalTime now2 = LocalTime.now();//用于获取时分秒
3. DateTimeFormatter格式日期类
public class Test{
public static void main(String[] args) {
LocalDateTime ldt = LocalDateTime.now();
DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH小时mm分钟ss秒");
//这里yyyy等不区分大小写,但必须是这个字母。
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-mm-dd HH:mm:ss");
//字母对应时间,剩下的可以自己定义格式
String strDate1 = dtf1.format(ldt); //调用format方法,LocalDateTime类为参数
String strDate2 = dtf2.format(ldt);
System.out.println(strDate1);
System.out.println(strDate2);
}
}
4. 时间戳
1) 通过静态方法获取对象
Instant now = Instant.now();
//用静态方法 now 获取表示当前时间戳的对象
//Instant now = new Instant(); 错误
System.out.println(now); // 2021-10-03T14:36:07.540Z
2) 通过 Date类的from静态方法 可以把 Instant类转成 Date类
Date date = Date.from(now);
System.out.println(date.getTime());
3) 通过 Date对象的toInstant方法可以把 date类 转换成 Instant类
Instant instant = date.toInstant();
5. plus和minus
所有第三代日期类都可以使用,通过提供 plus 和 minus方法可以对当前时间进行加或减,注意不对原对象进行操作,而是返回一个经过操作之后的对象。