针对八种基本数据类型相应的引用类型 —— 包装类
包装类和基本数据的转换:
(1) jdk5之前采用手动装箱和拆箱的方式
(2) jdk5及以后采用自动装箱和拆箱的方式
(3)自动装箱底层调用的是valueOf方法
【装箱:基本数据类型 -> 包装类; 拆箱: 包装类 -> 基本数据类型】
手动装箱:
int n = 100;
Integer integer = new Integer(n);
Integer integer1 = Integer.valueOf(n);
手动拆箱:
int n2 = integer.intValue();
自动装箱:
//底层是用Integer.valueOf(parameter)实现的
Integer integer2 = n;
自动拆箱
//底层采用 parameter.intValue();
int n3 = integer2;
三元运算符是一个整体,精度要看整体的最高精度
Integer i = 100;
//利用toString()方法完成Integer转换成String
String s1 = i.toString();
String s2 = String.valueOf(i);
String s3 = i + "";
//String转化为Integer
String str = "123";
Integer i1 = Integer.paraseInt(str);
Integer i2 = new Integer(str);
//示例一
Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1 == i2);
//示例二
Integer i3 = new Integer(128);
Integer i4 = new Integer(128);
System.out.println(i3 == i4);
//示例三
Integer i5 = 127;
Integer i6 = 127;
System.out.println(i5 == i6);
//示例四
Integer i7 = 128;
Integer i8 = 128;
System.out.println(i7 == i8);
//示例五
Integer i9 = 127;
Integer i10 = new Integer(127);
System.out.println(i9 == i10);
//示例六
Integer i11 = new Integer(127);
int i12 = 127;
System.out.println(i11 == i12);
//示例七
Integer i13 = 128;
int i14 = 128;
System.out.println(i13 == i14);
输出结果如下:
对产生的结果分析:
==
比较时肯定返回false 【所以示例一、二、五都为 false
】Integer.valueOf()
方法实现的,通过Ctrl+B快捷键查看源码 public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
当参数大于IntegerCache.low,小于IntegerCache.high时,是直接在缓冲数组中取的值;当不在这个范围时才会创建一个新Integer对象
进一步查询源码可知:IntegerCache.low为-128, IntegerCache.high为127
所以示例三中,参数127在范围内,所以两个对象的地址相同,返回true;
示例四中,参数128不在范围内,所以两个对象的地址不同,返回false。
==
比较,判断的就是 值是否相同【所以示例六、七返回true】String s1 = new String();
String s2 = new String(parameter);
String s3 = new String(char [] ch);
String s4 = new String(char [] c, int startIndex, int count);
可以修改字符的内容,例如:s = "you";
,此时s对应的地址仍然是0x123
,只是内容发生了变化
String s1 = "study";
String s2 = new String("study");
(1)通过构造器创建的对象:现在堆中创建空间,里面维护了value属性,指向常量池的空间,如果常量池有该字符串,则通过value直接指向,否则在常量池中重新创建,最终指向堆中的空间地址。
(2)通过直接赋值的对象:先从常量池中查看是否有该字符串的数据空间,如果有直接指向;如果没有则重新创建,然后指向。最终指向的是常量池的空间地址。
堆中存放的是引用对象的地址,如果问对象的属性,那么是在常量池中的地址。
Person p = new Person();
p.name = "赵先生";
此时这个对象p指向的是在堆中引用对象的地址,而p.name是在方法区的常量池中的地址
对于intern()
方法,其作用是获取字符串对象在常量池中的地址
String s1 = "zbc";
String s2 = new String("zbc");
System.out.println(s1 == s2);
System.out.println(s1 == s2.intern());
s1对象直接赋值获得的,s2 对象是通过构造器获得的。所以s1的地址是该字符串在常量池中的地址,s2的地址是该引用对象在堆中的地址。
所以输出的第一个结果为 false
s2.intern() 返回的是s2在常量池中的地址,因为s2和s1的字符串内容相同,所以在常量池中的地址也相同。
所以输出的第二个结果为 true
(1) final 无论是String类、还是其中保存数据的 value[ ] 都是final类型的
众所周知, final定义的变量有成为常量,在初始化之后就不能修改
String str = "sunny";
str = "day";
这样是没有问题的,但是不是将原来字符串的内容修改了呢?
起初,在堆的常量池中,创建了sunny
,并将引用对象指向这个地址;之后在常量池中检测是否存在day
,如果不存在就创建这个字符串,然后将引用对象指向这个新的地址,存在就直接指向这个地址即可。
所以说,并不是修改了原来的字符串,而是创建了一个新的对象
(2) String类的字符串拼接
第一种,几个字符串常量拼接
String s1 = "hello" + "world";
这种只会创建一个对象,在常量池中保存 helloworld
字符串,s1指向这个地址
第二种,几个字符串对象拼接
String str1 = "hello";
String str2 = "world";
String str3 = str1 + str2;
这种情况与第一种方式不同,因为 str1 和 str2是已经存在的字符串对象,它的实际创建过程是这样的:
先会创建一个StringBuffer类的对象,然后调用两次 append()方法,将这两个字符串拼接到一起,最后调用 toString()方法,将StringBuffer类的对象转化为String类的对象,返回给str3。
所以str3在内存中,实际是在堆中创建了一块空间,指向常量池的 helloworld
,最后将堆中value[ ]
的地址赋给 str3
(3) 创建对象,调用方法,属性修改的内存分析
public class Main{
String s = new String("powferful");
final char [] ch = {z, a, p, p, i, n, e, s, s};
public void change(String s, char [] ch){
s = "badly";
ch[0] = 'h';
}
public static void main (String [] args){
Main life = new Main();
life.change(life.s, life.ch);
System.out.println(life.s + " and " + life.ch);
}
}
程序从主方法开始,创建对象是在堆中创建的,对象有两个属性,数组的空间也是在堆中分配的
当调用一个方法时,就会生成一个栈,此时执行change方法,在栈中生成了一个change的方法栈,其中对象s是通过直接赋值获得的,所以在常量池中创建对象badly
,然后返回给 s;数组ch直接指向堆中的地址,并修改了它的第一个字符
当方法调用结束,该空间被释放,此时执行打印输出,调用的 s 和 ch仍然是主方法栈中的。
所以输出, powerful and happiness
。
(1) String类是用于保存字符串常量的,每次更新都需要重新开辟空间,效率较低。
String str1 = "perfect";
String str2 = "Perfect";
//区分大小写比较
System.out.println(str1.equals(str2));
//不区分大小写比较
System.out.println(str1.equalsIgnoreCase(str2));
//从前向后寻找
System.out.println(str1.indexOf(e));
//从后向前寻找
System.out.println(str1.lastIndexOf(e));
//获取字符串长度
System.out.println(str1.length());
//截取指定索引后的全部字符串
System.out.println(str1.substring(3));
//从指定索引开始截取指定范围的字符串 【此时为索引0-2】
System.out.println(str1.substring(0, 3));
方法名 | 作用 |
---|---|
equals | 比较字符串内容是否相同 |
equalsIgnoreCase | 不区分大小写比较内容是否相同 |
length | 获取字符串长度 |
indexOf | 返回字符串中第一次出现该字符的索引 |
lastIndexOf | 返回字符串中最后一次出现该字符的索引 |
substring | 根据指定索引截取字符串 |
(2) String类提供的第二组常用方法
package com.zwh.java.string_;
/**
* @author Bonbons
* @version 1.0
*/
public class String01 {
public static void main(String[] args) {
/*
//第一组方法
String str1 = "perfect";
String str2 = "Perfect";
//区分大小写比较
System.out.println(str1.equals(str2));
//不区分大小写比较
System.out.println(str1.equalsIgnoreCase(str2));
//从前向后寻找
System.out.println(str1.indexOf('e'));
//从后向前寻找
System.out.println(str1.lastIndexOf('e'));
//获取字符串长度
System.out.println(str1.length());
//截取指定索引后的全部字符串
System.out.println(str1.substring(3));
//从指定索引开始截取指定个数的字符串
System.out.println(str1.substring(0, 3));
*/
//第二组方法
String str3 = "Lucky";
//转成大写
System.out.println(str3.toUpperCase());
//转成小写
System.out.println(str3.toLowerCase());
//拼接
System.out.println(str3.concat(" day"));
//替换
System.out.println(str3.replace("Luck", "Suit"));
//分割
String poem = "两只黄鹂鸣翠柳,一行白鹭上青天";
String [] p = poem.split(",");
for(String s : p){
System.out.println(s);
}
//转化为字符数组
System.out.println(str3.toCharArray()[0]);
//比较字符串大小,前者大返回正数,后者大返回负数,相等返回零
//如果两个字符串,一个为其中另一个的前部分,那么返回的为两个字符串长度之差(前面-后面)
String person1 = "jack";
String person2 = "john";
System.out.println(person1.compareTo(person2)); //一个不同字符的差值
//格式化填充字符串
String name = "小明";
String sex = "男";
int age = 18;
double score = 88.888;
//%.2f 会保留小数点后两位,而且还会进行四舍五入处理
String strFormat = "我叫 %s, 性别 %s,今年 %d 岁, 期末成绩为 %.2f。";
System.out.println(String.format(strFormat, name, sex, age, score));
}
}
方法名 | 作用 |
---|---|
toUpperCase | 将字符串中字母转化为大写 |
toLowerCase | 将字符串中字母转化为小写 |
concat | 拼接字符串 |
replace | 将字符串中指定元素替换成新元素 |
split | 根据指定字符进行分割,返回一个字符串数组 |
toCharArray | 将字符串转化为字符数组 |
compareTo | 比较字符串大小 |
format | 由占位符组成的字符串,后面跟要填充的字符串 |
(1)StringBuffer类是final类型的,不能被继承
(2)该类继承了AbstractStringBuilder,实际该类就是通过父类的 char [] value
来存储字符串的
(3)该类实现了Serializable接口,该类的对象可以串行化
(4)StringBuffer字符内容是存储在 char [] value中,所以进行增加或删除操作时,不用每次都更换地址【创建新的对象】
(5) StringBuffer的常用构造器
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer(30);
StringBuffer sb = new StringBuffer("night");
//利用构造器
String s = "good";
StringBuffer sb1 = new StringBuffer(s);
//利用拼接
StringBuffer sb2 = new StringBuffer();
sb2.append(s);
//利用StringBuffer类提供的toString方法
String s1 = sb1.toString();
//利用构造器
String s2 = new String(sb2);
方法名 | 作用 |
---|---|
append(String s) | 添加字符串s(拼接) |
delete(int start, int end) | 删除索引 start <= index < end 处的字符串 |
replace(int start, int end, String s) | 将[start,end)范围的字符串替换为字符串s |
indexOf(String s) | 查找指定子串s在字符串中第一次出现的位置 |
lastIndexOf(String s) | 查找指定子串s在字符串中最后一次出现的位置 |
insert(int index, String s) | 在索引index位置插入字符串s |
length() | 获取字符串的长度 |
(1) 利用StringBuffer构造器创建的对象和利用 append() 方法创建的对象有什么区别?
String s = null;
StringBuffer sb = new StringBuffer();
sb.append(s);
System.out.println(sb.length());
System.out.println(sb);
当传入的字符串对象为空时,append() 方法会判断是否为空串
如果为空,那么就会调用AbstractStringBuffer类的 appendNull() 方法
从该方法中,我们可以看出实际是添加了一个 null 的字符串
之后正常输出,sb字符串的长度为4,输出内容 null
String s = null;
StringBuffer sb = new StringBuffer(s);
会产生空指针异常报错
在调用该构造器时,会获取字符串的长度,因为传入的为null,所以此处str也为空
(2) 将从键盘获得的价格的整数部分每三位添加一个逗号【hhh,说实话有点像算法题】
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String price = scanner.next();
StringBuffer sb = new StringBuffer(price);
//遍历寻找插入点
for(int i = sb.lastIndexOf(".") - 3; i > 0; i -= 3){
//插入","
sb.insert(i, ",");
}
System.out.println(sb);
}
}
StringBuilder 不是线程安全的,一般用于单线程的情况下,这种情况下比StringBuffer要快
StringBuilder类提供与StringBuffer类兼容的API,主要操作为append和insert,可重载这些方法以接收任何类型的数据
StringBuilder类与StringBuffer类相同,继承了AbstractStringBuilder类,利用父类的字符数组存储可变的字符序列
StringBuilder类是final的,不允许被继承,实现了Serializable接口,其对象是可以串行化的【对象可以网络传输,可以保存到文件】
StringBuilder的方法没有synchronized关键字【没有做互斥处理】,因此在单线程的情况下使用
String、 StringBuffer、 StringBuilder三者的比较:
(1)String 不可变字符序列,效率低,重复率高
(2)StringBuffer 可变字符序列,效率高,线程安全
(3)StringBuilder 可变字符序列,效率最高,线程不安全
方法名 | 作用 |
---|---|
abs | 求绝对值 |
pow(a, b) | 求幂运算 |
ceil | 向上取整 |
floor | 向下取整 |
round | 四舍五入 |
sqrt | 求开方 |
random | 求随机数 |
max | 最大值 |
min | 最小值 |
public class MathTest01 {
public static void main(String[] args) {
//求绝对值
int num1 = -5;
System.out.println("求-5的绝对值:" + Math.abs(num1));
//求幂
System.out.println("求2的3次幂:" + Math.pow(2,3));
//向上取整
double num2 = 3.54;
System.out.println(num2 + "向上取整:" + Math.ceil(num2));
//向下取整
System.out.println(num2 + "向下取整:" + Math.floor(num2));
//四舍五入
System.out.println(num2 + "四舍五入的结果为:" + Math.round(num2));
//求开方
System.out.println("4开平方:" + Math.sqrt(4));
//随机数
System.out.println("生成1-10的随机数:" + Math.random()* 10);
//最大值
System.out.println(num1 + " and " + num2 + " 的最大值:" + Math.max(num1, num2));
//最小值
System.out.println(num1 + " and " + num2 + " 的最小值:" + Math.min(num1, num2));
}
}
方法名 | 作用 |
---|---|
toString() | 返回数组的字符串形式 |
sort | 将数组排序【正序或逆序】 |
binarySearch | 可查找有序数组中的元素索引 |
copyOf | 拷贝指定数组中指定个数的元素 |
fill | 为数组填充相同的指定元素 |
equals | 判断两个数组的元素是否完全相同 |
asList | 将数组转化为集合【不能进行集合的操作】 |
(1) toString() 方法
public class ArraysTest {
public static void main(String[] args) {
//toString()方法,将数组转为字符串输出
int [] num = {2, 5, 8, 0};
System.out.println(Arrays.toString(num));
}
}
实现原理:
public static String toString(Object[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(String.valueOf(a[i]));
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
对于数组类型,还可以为任意其他基础数据类型,若采用那些类型的数组,取值时用 a[i]
即可。
int [] arr = null;
int [] arr = new int[0];
当数组不为空时,才能去判断长度。
对于数组长度定义为零只是一种定义,对于存储本身没有意义,但是可以作为一些辅助作用。
(2) sort() 方法
public class ArraysTest {
public static void main(String[] args) {
int [] num = {2, 5, 8, 0};
//sort()方法,将数组排序,最常见的是从小到大的排序
Arrays.sort(num);
System.out.println(Arrays.toString(num));
}
}
数组是引用类型,排序操作会直接影响原数组
实现逆序排序
public class ArraysTest {
public static void main(String[] args) {
//此处使用的包装类
Integer[] num = {2, 5, 8, 0};
Arrays.sort(num, new Comparator() {
//利用匿名内部类来实现接口
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2 - i1;
}
});
System.out.println(Arrays.toString(num));
}
}
实现原理:
1)Arrays.sort(arr, new Comparator())
2)最终到TimSort类的 binarySort方法
3)执行下面的代码,会根据动态绑定机制 c.compare()执行我们输入的匿名内部类
while(left < right){
int mid = (left + right) >>> 1;
if(c.compare(pivot, a[mid]) < 0)
right = mid;
else
left = mid + 1;
}
4)new Comparator(){
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2 - i1;
}
}
5)接口返回的值正负会决定是正序还是逆序
(3) binarySearch() 方法
class Main{
public static void main(String [] args){
int num = {0, 1, 2, 3, 4, 5, 8};
int index = Arrays.binarySearch(num, 3);
System.out.println("3的索引为:" + index);
}
}
当数组中不存在目标值时,返回的值为该目标值应该在数组中的位置加一取反
return -(low + 1);
假设目标值为6,那么如果存在该数组中的索引为6,索引方法返回 -7。
(4)copyOf() 方法
int [] n1 = {2, 1, 0};
int [] n2 = Arrays.copyOf(n1, 2);
System.out.println(Arrays.toString(n2));
如果拷贝个数小于零,那么就会报错
如果拷贝个数大于原数组的大小,那么其余位置会补空 【null】
(5) fill() 方法
int [] num = new int[3];
Arrays.fill(num,6);
System.out.println(Arrays.toString(num));
为指定数组填充指定的元素,也可以覆盖掉原来的数组元素
(6) equals() 方法
int [] n1 = {1, 2, 3};
int [] n2 = {1, 2, 3};
System.out.println(Arrays.equals(n1, n2));
比较方法中的两个数组的元素是否完全相同,如果相同返回true,如果存在不同则返回false。
(7) asList() 方法
int [] num = {1, 2, 3};
List list = Arrays.asList(num);
会根据传入的数组返回一个List类型的集合,编译类型为List接口,运行类型是这个接口中的一个内部类 ArrayList
import java.util.Arrays;
import java.util.Comparator;
/**
* @author Bonbons
* @version 1.0
*/
public class ArraysSortCustom {
public static void main(String[] args) {
//初始化一个待排序的数组
int [] num = {0, 3, 1, 5, 2, 8, 4};
//调用排序方法
bubbleSort(num, new Comparator(){
@Override
public int compare(Object o1, Object o2) {
//强转为整型数据后,采用自动拆箱转化为int类型
int i1 = (Integer)o1;
int i2 = (Integer)o2;
return i1 - i2;
}
});
//打印数组
System.out.println("排序后的数组为:" + Arrays.toString(num));
}
//创建一个冒泡+binarySort的方法
public static void bubbleSort(int [] arr, Comparator c){
//总共需要比较arr.length - 1 趟,每趟确定一个元素的位置
for (int i = 0; i < arr.length - 1; i++) {
//比较相邻两个元素的大小
for (int j = 0; j < arr.length - 1; j++) {
//此时是正序排序
if(c.compare(arr[j], arr[j + 1]) > 0){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
(2)通过书的价格来对书进行排序,该类包含两个属性 名字和价格。
package com.zwh.java.math_;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
/**
* @author Bonbons
* @version 1.0
*/
public class ArraysTest {
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从入门到放弃w",30);
//根据价格从小到大将书排序
Arrays.sort(books, new Comparator<Book>() {
@Override
public int compare(Book o1, Book o2) {
double priceDiff = o1.getPrice() - o2.getPrice();
//逆序更改此处的值即可
if(priceDiff > 0)
return 1;
else if(priceDiff < 0)
return -1;
else
return 0;
}
});
//输出Book类的信息
System.out.println(Arrays.toString(books));
}
}
class Book{
private String name;
private int price;
//构造器
public Book(){}
public Book(String name, int price) {
this.name = name;
this.price = price;
}
//Get和Set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
方法名 | 作用 |
---|---|
exit | 退出当前程序 |
arraycopy | 拷贝数组元素 |
currentTimeMillens | 返回当前时间 |
gc | 垃圾回收机制 |
(1)System.exit(int parameter)
通过启动关闭序列来终止当前正在运行的虚拟机。
状态参数为零,代表正常退出;非零时代表异常终止。
(2)System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
int [] newArr = new int[8];
int [] curArr = {1, 2, 3, 4};
System.arraycopy(curArr, 0, newArr, 0, 4);
System.out.println(Arrays.toString(newArr));
(3)System.currentTimeMillis()
返回当前时间与1970年1月1日凌晨之间的差值,一般以毫秒级为单位
//可以用来测试一个代码块的运行时间
long cur = System.currentTimeMillis();
int count = 0;
for(int i = 0; i < 10000000; i++){
if(i % 2 == 0){
count++;
}
}
long cur2 = System.currentTimeMillis();
System.out.println("运行该算法需要的时间为:" + (cur2 - cur) + "ms");
(4) System.gc()
import java.math.BigInteger;
public class BDMethod {
public static void main(String[] args) {
//存储一个大数,用long存储会提示Integer number too large
BigInteger num = new BigInteger("8931741723712837");
System.out.println(num);
//对BigInteger类型的数据进行算术运算
BigInteger num2 = new BigInteger("123");
//加法采用add() 方法
System.out.println(num.add(num2));
//减法采用subtract()方法
System.out.println(num.subtract(num2));
//乘法采用multiply()方法
System.out.println(num.multiply(num2));
//除法采用divide()方法
System.out.println(num.divide(num2));
}
}
add() 加法
subtract() 减法
multiply() 乘法
divide() 除法
在使用除法因为可能会出现除不尽的时候,就会抛出异常,我们可以采用
BigDecimal bd1 = new BigDecimal("1.231545623212315");
BigDecimal bd2 = new BigDecimal("9.3124124515");
System.out.println(bd1.divide(bd2, BigDecimal.ROUND_CEILING));
这样就会保留与分子相同的精度
Date类为第一代日期类,可以精确到毫秒级,但是现在其中很多方法和构造器已经被弃用,部分开发仍然会使用该类
采用的是国外的时间格式
public class Date01 {
public static void main(String[] args) {
Date d1 = new Date();
System.out.println(d1);
}
}
所以我们需要进行格式化,利用SimpleDateFormat的对象来完成格式化
对于格式化时,字母的使用大小写是有区别的,可以参考上面的表格
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
System.out.println(sdf.format(d1));
(1) 利用传入的毫秒数来设置时间 【不能超过长整型的范围】
当前Date类还保留一个 public Date(Long date)
的构造器,通过传入的一个长整型的毫秒数来获取时间。
Date d2 = new Date(832490849);
System.out.println(sdf.format(d2));
如果为正数,那么为1970年1月1日以后的时间
如果为负数,那么为1970年1月1日之前的时间
(2) 利用指定的字符串来设置时间 【获得的时间仍为Date类的初始格式】
利用了SimpleDateFormat类的parse方法
待转化的字符串的格式,要与创建的SimpleDateFormat类的对象格式相同
String s = "2022年5月21日 00:00:00 星期六";
//在主方法处抛出异常
Date d3 = sdf.parse(s);
System.out.println(d3);
System.out.println(sdf.format(d3));
此处通过主方法抛出了异常,因为该方法是这样的parse(String text, ParsePosition pos)
text - A String ,其中一部分应解析。
pos - 具有 ParsePosition的索引和错误索引信息的 ParsePosition对象。
Calendar类是一个抽象类,我们通常使用的是它的私有无参构造方法
通过提供的getInstance()来获得实例
调用方法创建的该类对象,是将当前时间的所有信息都存储到该类的字段中
Calendar c = Calendar.getInstance();
System.out.println(c);
如果我们想直观的来查看时间,就要通过 get(字段) 方法从该对象中取出需要的字段信息
System.out.println(c.get(Calendar.YEAR)+"年"
+(c.get(Calendar.MONTH)+1)+"月"
+c.get(Calendar.DAY_OF_MONTH)+"日"
+c.get(Calendar.HOUR)+"时"
+c.get(Calendar.MINUTE)+"分"
+c.get(Calendar.SECOND)+"秒");
Calendar类没有提供对应的格式化类,所以需要自己来完成需要的时间字段拼接【时间的格式自由度变高】
注意:该类中有许多字段,例如这样:HOUR 、HOUR_OF_DAY
它们代表的含义是不同的,前者是12小时制,后者是24小时制
类似的字段还有很多,可以根据需要选取
Calendar类自由度很高的情况下,也出现很多问题:
(1)可变性:日期与时间这样的类应该都是不可变的
(2)偏移性:月份是从0月开始
(3)格式化:格式化对Calendar类无法使用
(4)线程不安全,不能处理润秒的问题(每隔2天多出1s)
在 jdk8 之后,加入了第三代日期类
LocalDate 代表年月日
LocalTime 代表时分秒
LocalDateTime 代表年月日 时分秒
以LocalDateTime为例
该类实现了很多接口,只有一个构造器
public LocalDateTime(LocalDate, LocalTime)
我们一般通过该类提供的now() 方法来利用当前时间返回LocalDateTime的对象
public class Main{
public static void main(String[] args) {
LocalDateTime dlt1 = LocalDateTime.now();
System.out.println(dlt1);
}
}
该类提供了很多方法,此处选取几个为例,具体可以参考相关JDK的api
public class LocalDate01 {
public static void main(String[] args) {
LocalDateTime dlt1 = LocalDateTime.now();
System.out.println(dlt1);
//常见方法
System.out.println("年 = " + dlt1.getYear());
System.out.println("月 = " + dlt1.getMonthValue());
System.out.println("日 = " + dlt1.getDayOfMonth());
System.out.println("时 = " + dlt1.getHour());
System.out.println("分 = " + dlt1.getMinute());
System.out.println("秒 = " + dlt1.getSecond());
}
}
对于月的获取有两个方法 getMonth() 与 getMonthValue(),前者返回的是字符串【英文月份】,后者显示的是数字【第几个月】
对当前日期进行算术运算(加、减)
LocalDateTime ldt1 = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm:ss E");
System.out.println("当前时间为: " + dtf.format(ldt1));
System.out.println("十年之后的日子: " + dtf.format(ldt1.plusYears(10)));
System.out.println("五个月前的日子: " + dtf.format(ldt1.minusMonths(5)));
与SimpleDateFormat类似,DateTimeFormatter为该类提供格式化
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm:ss");
String s = dtf.format(dlt1);
System.out.println(s);
在创建DateTimeFormatter对象时,利用了该类的ofPattern()方法,使用指定的模式创建格式化程序。
在利用该对象调用format方法将LocalDateTime类型的时间格式化
Instant时间戳与第一代时间Date类似,可以与其相互转换
利用静态方法now()获取当前时间戳的对象
Instant now = Instant.now();
System.out.println(now);
将时间戳对象转化为Date对象
Date date = Date.from(now);
将Date对象转化为时间戳对象
Instant instant = date.toInstant();