首页 > 编程语言 >java常用类库(API)

java常用类库(API)

时间:2022-12-16 18:34:17浏览次数:48  
标签:类库 java String System API println public out

该章节学习方法:
不要背,只需要将具体方法的作用记下来,需要用的时候直接查文档

API的使用和常用包的概述

  • java.lang包,是java语言的核心包,包中的所有内容由java虚拟机自动导入,如System String

  • java.util包 该包是java语言的工具包 里面包含大量的工具类和集合类 如Scanner Random List集合

  • java.io包 是java语言的输入输出包,里面提供了大量读写文件的类

  • java.net包 java网络包 提供了大量网络编程相关的类

  • java.sql 是java语言中的数据库包,提供了大量操作数据库的类和接口

  • Math类:Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。
    隶属于java.lang包,该包由JVM自动导入
    定义原型:
    public final class Math(该类为最终类,不可被继承)

该类里面的所有方法都是静态方法,不需要对象去调用,直接用类名调用


对于工具类:私有化构造方法,并且将方法定义成静态的


idea快捷键:ctrl+N搜索类的源码 ctrl+B 查看所选中的类或者方法的源码

  • abs
    注意:在取值范围之内,没有对应的负数与之对应,所取绝对值的结果将有误
    如:byte:-128--127 当求-128的绝对值将会出现有误(但是会得到错误的数值,但是编译器并不报错

可以使用Math.absExct()方法进行调用,如果调用到这种情况,编译器将抛出错误(jdk15)

  • ceil 向上取整 往数轴的正方法进一

  • floor 向下取整:去尾法

  • round 四舍五入
    四舍五入

  • max:获取2个数的较大值

  • min 获取2个数的较小值

  • pow(double a,double b)表示返回a的b次幂的值(double的取值范围是最大的,传入的数据将自动转为double)

细节:

  • 如果b为0-1的小数,其效果是开根号
  • b还可以传入负数

建议:在以后用该方法时,第二个参数一般传入大于1的正整数
如果需要开平方可以调用:Math.sqrt()
如果需要开立方可以调用:Math.cbrt()

  • random 返回一个值为double的随机数,范围[0.0,1.0) 注意开闭区间
    用的不多,并不友好(看懂这种写法方便,以后的代码交流)

获取1-100之间的随机数

在java 中创建随机数的写法,可以查看该方法的源码:创建一个Romdom对象去调用里面的nextInt方法

 //Math类的方法的用法
 /*
 * max min 返回最大最小值
 * abs 取绝对值 absExact
 * ceil 向上取值
 * floor向下取值
 * round 四舍五入
 * pow 求a的b次幂
 * random 产生区间[0.0,1.0)的随机数
 */
 
     public class MathTest{
	 public static void main(String[]args){
		System.out.println(Math.abs(-2.34));// 2.34该方法重载 可以返回所有整形的绝对值
		int a=-2147483648;
		//System.out.println(Math.abs(a));//-2147483648 当没有对应的正数与之对应,答案将错
		//System.out.println(Math.absExact(a));2147483648
		System.out.println(Math.max(2,3));//返回2个数的最大值
		System.out.println(Math.min(-2,-4));//返回2个数的较小值
		System.out.println(Math.ceil(9.23));//10.0向上取值
		System.out.println(Math.floor(-1.23));//-2.0 向下取值
		System.out.println(Math.round(9.51));//10 四色五入返回接近整数的long
		System.out.println(Math.pow(2,3));//a的b次方 要求第二个参数尽量传入大于1的整数
		System.out.println(Math.sqrt(4));//返回传入数的根号2次方
		System.out.println(Math.cbrt(8));//返回传入数的根号3次方
		System.out.println(Math.random());//返回[0.0,1.0)的double数
		for(int i=0;i<20;i++){
			System.out.println((Math.floor(Math.random()*100))+1);//得到0-100的整数
		}
		
		
		
	 }
 }
  • 两道简单的练习题

算法改进判断质数

  • 没有改进
//改进判断一个数是否是质数


public class PrimeTest{
    public static int count=0;
	public static boolean isPrime(int number){
		boolean flag=true;//默认为质数
		for(int i=2;i<number;i++){
			count++;
			if(number%i==0)
				flag=false;
				
				
		}
		return flag;
	}
	public static void main(String[]args){
		
		System.out.println(isPrime(23));//true
		System.out.println("判别次数为:"+count);//21
	}
}

改进之后
改进原理:每对因数成对出现,并且,一个因数大于等于sqrt(numger) 一个因数小于等于sqrt(number)
所有只需要遍历到 sqrt(number)即可

  • 改进后
//改进判断一个数是否是质数


public class PrimeTest{
    public static int count=0;
	public static boolean isPrime(int number){
		boolean flag=true;//默认为质数
		for(int i=2;i<=Math.sqrt(number);i++){
			count++;
			if(number%i==0)
				flag=false;
				
				
		}
		return flag;
	}
	public static void main(String[]args){
		
		System.out.println(isPrime(23));//true
		System.out.println("判别次数为:"+count);//3
	}
}

  • 求100-999的水仙花数的个数
 //统计100-990的水仙花数的个数
 //水仙花数:每个位数的立方和等于该数
 public class WaterFlower{
	 public static int WaterCount(){
		 int count=0;
	 for(int i=100;i<=999;i++){
		int ge=i%10;
		int shi=i/10%10;
		int bai=i/100%10;
		if((Math.pow(ge,3)+Math.pow(shi,3)+Math.pow(bai,3))==i)
				count++;
	 }
	 return count;
}
	 public static void main(String[]args){
		 System.out.println(WaterCount());//4
	 }
 }

System类该类的所有方法都是静态的,构造方法封装,类内不允许产生对象**

  • exit()
    当传入数值为0 表示虚拟机正常停止
    当传入数值非0时,表示虚拟机异常停止
    当我们需要将整个程序结束时,可以调用这个方法

  • currentTimeMillis
    可以用以测量一个程序或者算法的执行时间

  • 时间原点的概念
    将c语言的诞生之日,1970年0分零秒作为时间原点
    在中国有时差,则在中国时间原点为1970年8点0分

    返回从时间原点到现在的时间的毫秒表示

  • arraycopy

    细节:
    1.如果源数组和目标数组都是基本数据类型,那么两者的数据类型需要保证一致,否则会报错(ArrayStoreException)
    2.拷贝的时候需要考虑数组长度,超出长度也会报错(.ArrayIndexOutOfBoundsException)
    3.如果数据源数组和目标数组都是引用数据类型,那么子类类型可以赋值给父类类型

  • 同类型的引用类型

//验证System类的一些常用方法
class Person{
	private String name;
	private int age;
	private String sex;
	public Person(){};//无参构造
	public Person(String name,int age,String sex){
		setName(name);
		setAge(age);
		setSex(sex);
	}
	public String getName(){
		return this.name;
	}
	public int getAge(){
		return this.age;
	}
	public String getSex(){
		return this.sex;
	}
	public void setName(String name){
		this.name=name;
	}
	public void setAge(int age){
		this.age=age;
	}
	public void setSex(String sex){
		this.sex=sex;
	}
}

class Student extends Person{
	public Student(String name,int age,String sex){
		super(name,age,sex);
	}
}


public class SystemTest{
	public static void main(String[]args){
		
		
			Student []array1=new Student[3];
			array1[0]= new Student("张三",23,"男");
			array1[1]= new Student("小美",23,"女");
			array1[2]= new Student("张三",23,"男");
		
		
		Student[]array2=new Student[3];
		System.arraycopy(array1,0,array2,0,3);
		for(int i=0;i<3;i++){
			System.out.println("姓名:"+array2[i].getName()+" 年龄:"+array2[i].getAge()+" 性别:"+array2[i].getSex());
		}
		
	}
}

output:
姓名:张三 年龄:23 性别:男
姓名:小美 年龄:23 性别:女
姓名:张三 年龄:23 性别:男
                  
  • 子类赋值给父类
//验证System类的一些常用方法
class Person{
	private String name;
	private int age;
	private String sex;
	public Person(){};//无参构造
	public Person(String name,int age,String sex){
		setName(name);
		setAge(age);
		setSex(sex);
	}
	public String getName(){
		return this.name;
	}
	public int getAge(){
		return this.age;
	}
	public String getSex(){
		return this.sex;
	}
	public void setName(String name){
		this.name=name;
	}
	public void setAge(int age){
		this.age=age;
	}
	public void setSex(String sex){
		this.sex=sex;
	}
}

class Student extends Person{
	public Student(String name,int age,String sex){
		super(name,age,sex);
	}
}


public class SystemTest{
	public static void main(String[]args){
		
		
			Student []array1=new Student[3];
			array1[0]= new Student("张三",23,"男");
			array1[1]= new Student("小美",2,"女");
			array1[2]= new Student("张三",23,"男");
		
		
		Person[]array2=new Person[3];//父类数组
		System.arraycopy(array1,0,array2,0,3);//发生向上转型
		for(int i=0;i<3;i++){
			Student stu=(Student)array2[i];//向下转型
			System.out.println("姓名:"+stu.getName()+" 年龄:"+stu.getAge()+" 性别:"+stu.getSex());
		}
		
	}
}

output:
姓名:张三 年龄:23 性别:男
姓名:小美 年龄:2 性别:女
姓名:张三 年龄:23 性别:男


Runtime类

System的exit方法在底层调用的是RunTime的exit方法

  • exec 用java代码去执行命令(还没有学该方法的使用先忽略)

  • getRuntime 用于获取Runtime的对象
    而且每次获取的对象都是同一个(具体实现可以参见该类的源代码)
    该对象表示的是当前系统的运行环境(如果每次都可以new一个新的对象,这就笼套了)

  • exit 停止虚拟机
    0 正常停止 非0 异常停止

//验证Runtime类的常用方法
public class RuntimeTest{
	public static void main(String[]args){
		Runtime.getRuntime().exit(0);//直接获取对象然后调用
		System.out.println("我执行了吗");
		
	}
}
  • avaialbleProcessors获取cpu的线程数
//验证Runtime类的常用方法
public class RuntimeTest{
	public static void main(String[]args){
		System.out.println(Runtime.getRuntime().availableProcessors());//8
		
	}
}
  • maxMemory获取JVM能够获取的最大内层 单位byte
  • totalMemoryJVM已经获得的内存 单位byte
  • freeMemory JVM中剩余的内存 单位byte
//验证Runtime类的常用方法
public class RuntimeTest{
	public static void main(String[]args){
		//JVM能获取的最大内存 单位 byte
		System.out.println(Runtime.getRuntime().maxMemory()/1024/1024);
		//JVM已经获取的总内存
		System.out.println(Runtime.getRuntime().totalMemory()/1024/1024);
		//JVM剩余内存
		System.out.println(Runtime.getRuntime().freeMemory()/1024/1024);
		
	}
}
output:
3596
243
240

-exec 在javc中执行 cmd命令


打开cmd ,输入notepad即可打开notepad 也不是所有的命令都可以执行
细节:该方法需要进行异常处理
但是该方法在调用的时候可能会有异常 idea 快捷键:alt+回车 查看idea提供的解决方案

  • 拓展
    shutdown 关机 后面需要加上参数
    参数:-s 默认在1分钟后关机
  • -s -t 指定关机时间(单位秒)
  • -a 取消关机操作
  • -r 关机并重启

案例:结合图形化界面+关机操作 恶搞基友

备注:该处没有学,记得之后在学

Object和 Objects

  • Object类


Object类中仅仅提供了一个空参构造
因为父类中往往聚集子类公有的特征,而Object的子类没有共有的特征,所有其仅有空参构造

为什么在子类中的构造方法默认调用父类的无参构造,而不是其他有参构造?
因为在他们的顶级父类Object类中只存在无参构造

idea中的快捷键:ctrl+f12 (在按ctrl+b查找具体类的源代码后)索引该类的具体方法

toString方法

public class ObjectTest{
	public static void main(String[]args){
		Object obj=new Object();
		System.out.println(obj.toString());//java.lang.Object@15db9742	
	}
}

返回该类的字符串表示:可以简单认为@前面的为该对象的类名,后面为该对象的地址值

对于打印语句的详解:

细节:为什么打印对象但是没有调用 toString方法,但还是可以将对象的字符串表示打印出来
其实在底层还是调用了toString方法,具体实现可以在idea中参考源码**

虽然父类的toString方法是返回对象的字符串表示(地址),可是我们大多数情况下,这种个对象的地址值对我们没有意义,
我们需要的是打印对象的具体属性,对此我们的解决的方法是覆写Object类中的toString方法

class Person {
	public Person(String name,int age,String sex){
		this.age=age;
		this.name=name;
		this.sex=sex;
	}
	public int age;
	public String name;
	public String sex;
	//覆写父类toString
	public String toString(){
		return "姓名:"+this.name+" 性别: "+this.sex+" 年龄:"+this.age;
	}
}
public class ObjectTest{
	public static void main(String[]args){
		Person pr=new Person("张三",23,"男");
		//System.out.println(pr);没有覆写toString Person@15db9742
		System.out.println(pr);//姓名:张三 性别: 男 年龄:23
	}
}

对于toString方法的结论:如果我们打印一个对象,如果我们想看到对象的属性,我们就需要对toString方法进行覆写,在重写时对属性进行拼接

插件Ptg实现对类的getter setter 构造方法 覆写toString方法的一步到位

equals方法

object类中的equals方法默认比较2个对象的地址,不满足我们比较对象的要求,需要覆写

idea快捷键:alt+insert 生成

//验证Object类中的方法
//toString 返回对象的字符串表示
//equals 比较2个对象的地址
//
class Person {
	public Person(String name,int age,String sex){
		this.age=age;
		this.name=name;
		this.sex=sex;
	}
	public int age;
	public String name;
	public String sex;
	//覆写父类toString
	public String toString(){
		return "姓名:"+this.name+" 性别: "+this.sex+" 年龄:"+this.age;
	}
	 public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Persons persons = (Persons) o;
        return age == persons.age && Objects.equals(name, persons.name) && Objects.equals(sex, persons.sex);
    }
}
public class ObjectTest{
	public static void main(String[]args){
		Person pr=new Person("张三",23,"男");
		Person pr1=new Person("张三",23,"男");
		//System.out.println(pr.equals(pr1));false 该equals为父类中的方法 是对地址的比较
		System.out.println(pr.equals(pr1));
	}
}

(结论:如果我们没有重写equals方法 默认是调用Object里面的equals方法,是比较对象的地址
一般地址的比较对我们意义不大,一般会重写equals方法,进行属性的比较

理解equals方法的重要例题

如果本类中存在equals则调用的是本类的equals方法,如果本类没有则调用的是其父类的equals方法(注意Object类中的equals是比较对象的地址的)

对象克隆

  • clone方法
    如果一个接口中没有定义方法,表明该接口是一个标记性的接口,一旦实现了这个接口的类才可以进行某些行为

注意clone在Object类中的定义是protected的,只能在本类中,相同的包中 不同包的子类中被调用
运用场景
一个人在玩游戏,并且有很多的游戏记录,现在由于水平不行,需要将账号的信息转移的另一个区,这时就可以用对象克隆,将用户的信息定义称对象

  • 浅克隆

问题:次数2个数组的引用都可以控制这个数组(和static属性相似

  • 深克隆

    对于字符串如果是以直接赋值的方式,自深拷贝会直接复制其地址,如果属性是数组则会创建一个新的数组,拷贝将会用新的数组,如果
    属性是一个一般对象的话,我觉得也会新创建一个对象,用新的地址

    在object类中默认的是浅克隆
    总结:

  • 如果需要深克隆需要重写clone


发现自己的问题:
发现自己对于java的内存不太了解

在以后的开发中如果要用到深克隆,我们一般不自己写,一般使用第三方工具,放到lib文件中

Object类的总结:

  • Objects类

问题:

  • equals方法如果是null在调用将会产生空指针异常
    该方法的实现源码:
//验证Objects类的一些常用方法
class Student{
	public Student(String name,int age,String sex){
		this.age=age;
		this.name=name;
		this.sex=sex;
	}
	public int age;
	public String name;
	public String sex;
	//假设我已经完成了对equals方法的重写,可以实现对象的比较
	
}
public class ObjectsTest{
	public static void main(String[]args){
		Student s1=new Student("张三",12,"男");
		Student s2=null;
		//System.out.println(s2.equals(s1));将会出现 java.lang.NullPointerException
		System.out.println(Objects.equals(s1,s2));//false
		
		
	}
}

空指针将会直接返回flase

Objects类的总结:

大数

BigInteger类

问题:
如果想表示的整数超出了最大的整数范围,将无法进行表示


第一个构造方法,第二个参数为Random的对象,参数为字符串,该字符串里面的数值只能是整数

注意:
BigInteger的对象一旦被创建则内部记录的值不能被改变

  • 使用第一个构造:获取随机大整数
package com.cn.java;

import java.math.BigInteger;
import java.util.Random;

public class BigIntegerTest {
    
    public static void main(String[] args) {
        Random r=new Random();
        BigInteger a=new BigInteger(2,r);//得到0-2^2的随机数
        System.out.println(a);
    }

}


  • 使用第二个构造方法 获取指定的大整数(以后用的最多)
package com.cn.java;

import java.math.BigInteger;
import java.util.Random;

public class BigIntegerTest {

    public static void main(String[] args) {
        Random r=new Random();
        BigInteger a=new BigInteger("99999999999999999999999999999999999999999999999999999999999999");//得到0-2^2的随机数
        System.out.println(a);
    }

}
output:
99999999999999999999999999999999999999999999999999999999999999

如果里面的字符串不是整数将会出现异常

  • 第三种构造(获取指定进制的大整数)用的不多,第一个参数为该进制的数

package com.cn.java;

import java.math.BigInteger;
import java.util.Random;

public class BigIntegerTest {

    public static void main(String[] args) {

        BigInteger a=new BigInteger("100",16);
        System.out.println(a);
    }

}

output:
256

  • 静态方法获取对象
package com.cn.java;

import java.math.BigInteger;
import java.util.Random;

public class BigIntegerTest {

    public static void main(String[] args) {

       BigInteger a=BigInteger.valueOf(199);
        System.out.println(a);
    }

}
output:
199

静态方法和构造方法获取对象方式的异同点

1.静态方法所获取的对象只能在long 的范围内
2.静态方法对常用的数字-16-16进行了优化,提前把-16-16的BigInteger对象创建好了(应该和包装类的缓冲池技术一样)
在源代码中可以看到,其的static块中创建了-16-16的对象,对于0直接在内部创建了一个对象

以后创建BigInteger的对象,如果是在long范围之内的建议用静态方法创建,如果超过了long或者不确定对象的大小,用pubilc BigInteger (String val)进行创建

如果理解对象内部的数据不可变?

package com.cn.java;

import java.math.BigInteger;
import java.util.Random;

public class BigIntegerTest {

    public static void main(String[] args) {
        BigInteger r1= BigInteger.valueOf(2);
        BigInteger r2= BigInteger.valueOf(3);
        BigInteger r3=r1.add(r2);
        System.out.println(r3==r1);//false
        System.out.println(r3==r2);//false

    }

}

对象不可变原理,和字符串的字符串不可变原理应该相同


BigInteger的常用成员方法

BigInteger是一个对象,对象不能直接加减乘除的,对于对象的的计算,我们需要用方法去完成

注意:方法divideAndRemainder方法返回的是一个BigInteger数组数组,索引0表示商,索引1表示余数

注意:在调用 max或者min方法的时候,将不会创建新的对象,将会将较大的或者较小的对象返回

package com.cn.java;

import java.math.BigInteger;
import java.util.Random;

public class BigIntegerTest {

    public static void main(String[] args) {
       //创建对象:-16-16的对象用静态方法更好
        BigInteger r1=BigInteger.valueOf(3);
        BigInteger r2=BigInteger.valueOf(4);
        //加法
        BigInteger r3=r1.add(r2);
        System.out.println(r3);
        //减法
        BigInteger r4=r1.subtract(r2);
        System.out.println(r4);
        //乘法
        BigInteger r5=r1.multiply(r2);
        System.out.println(r5);
        //除法
        BigInteger r6=r2.divide(r1);
        System.out.println(r6);
       //获取商和余数
        BigInteger arr[]=r2.divideAndRemainder(r1);//该方法返回对象数组
        System.out.println(arr[0]);//下标为0 为商
        System.out.println(arr[1]);//下标为1 为余数
        //比较对象时候相同:equals也被BigInteger类重写 比较的是对象
        Boolean a=r1.equals(r2);
        System.out.println(a);//false 如果数值相同将会返回ture
        //次幂:该方法的参数为int型的整数
        BigInteger r7=r2.pow(4);
        System.out.println(r7);
       //max min
        BigInteger r8=r1.max(r2);
        System.out.println(r8);//4
        //该处不会创建新的对象 会将较大的对象进行返回
        System.out.println(r8==r1);//false
        System.out.println(r8==r2);//true
      //将BigInteger变回基本数据类型:但是超出范围将会报错
       BigInteger b=BigInteger.valueOf(23);
       int i=b.intValue();

    }

}


BigInteger的底层储存原理


BigDecimal类

由于小数float double的留给小数部分的储存空间有限,如果一个小数所需要的程序空间超出了最大储存空间,计算机将会将超出的数值舍弃,这将会造成数据不精确

计算机会将我们所写的数值先用二进制进行储存,然后再用我们需要的数值进行表示出来

但在银行等金融行业或一些精密仪器制造行业,会要求数据的准确

和BigInteger一样也是不可变的

**注意:对于构造方法public BigDecimal(double val)创建的对象可能还是不精确的,具体缘由参见java文档**

如果需要创建精确的对象建议使用`public BigDecimal(double val)进行创建

程序演示构造方法

package com.cn.java;



import java.math.BigDecimal;


public class BigDecimalTest {
    public static void main(String[] args) {
        //第一种构造方式:传递double的数值构造出来的对象的数值并不精确
        BigDecimal r1=new BigDecimal(0.1);
        BigDecimal r2=new BigDecimal(0.09);
        //System.out.println(r1);//0.1000000000000000055511151231257827021181583404541015625
        //System.out.println(r2);//0.0899999999999999966693309261245303787291049957275390625
       //传递字符串进行构造:创建出来的对象精确
        BigDecimal r3=new BigDecimal("0.1");
        BigDecimal r4=new BigDecimal("0.09");
        System.out.println(r3);//0.1
        System.out.println(r4);//0.09
        System.out.println(r3.add(r4));//0.19
        System.out.println(0.09+0.01);//0.09999999999999999与上述代码形成对比 没有用大数对于部分的小数的一些数值将会被舍弃
    }




}

3.通过静态方法创建对象

   BigDecimal r5=BigDecimal.valueOf(5.);//实际上以5.0参与计算
        System.out.println(r5);//5

细节:
1.用double的数值创建对象,的对象数值不精确,以后不建议使用
用String和静态方法创建对象的区别

1.如果要表示的数字不大,没有超过double的取值范围,建议使用静态方法
2.如果我们表示的数值比较大,超出了double的取值范围,建议使用构造方法
3.如果我们传递的数值是0-10的整数,包含0 和10(但是如果传入的是小数也会重新new对象),那么方法会返回已经创建好的对象,不会重新New(具体实现请参见源码)

常见成员方法

package com.cn.java;
import java.math.BigDecimal;
public class BigDecimalTest {
    public static void main(String[] args) {

        BigDecimal r1=BigDecimal.valueOf(10.0);
        BigDecimal r2= BigDecimal.valueOf(2.0);
        //加法
        BigDecimal r3=r1.add(r2);//12.0
        System.out.println(r3);
        //减法
        BigDecimal r4=r1.subtract(r2);
        System.out.println(r4);//8.0
        //乘法
        BigDecimal r5=r1.multiply(r2);
        System.out.println(r5);//20.00



       }




}

idea快捷键:ctrl+p看方法的参数

package com.cn.java;
import java.math.BigDecimal;
import java.math.RoundingMode;

public class BigDecimalTest {
    public static void main(String[] args) {

        BigDecimal r1=BigDecimal.valueOf(10.0);
        BigDecimal r2= BigDecimal.valueOf(3.0);
        //除法
       // BigDecimal r3= r1.divide(r2);//如果数值为循环小数 用这个方法将会出现异常
        //System.out.println(r3);//ArithmeticException

        BigDecimal r3=r1.divide(r2,2,BigDecimal.ROUND_HALF_UP);//此时第三个属性为BigDecimal类里面的一个属性
        System.out.println(r3);//3.33

        //java认为将这种计算的舍入模式写在BigDecimal类中不太好,所以另外编写了枚举为舍入模式
        //如下是新的写法
        // BigDecimal r3 =r1.divide(r2,2, RoundingMode.HALF_UP);


       }




}

对于舍入模式的解释(可以在文档中进行查看讲的很清楚)

举例:5.5 舍入 6
-1.1 舍入 -2

举例:5.5 舍入:5

  • HALF-UP:四舍五入
  • HALF-DOWN:和前面四舍五入不一样的是为5时将会向下舍入

当我们需要那种舍入模式,就去对应的枚举里面找,一般有的是四舍五入

BigDeciaml的底层储存方式

底层没有采用分段储存,可能因为变成二进制之后的位数过于多,所以java没有采用分段储存

java得到该字符串后会将该字符串遍历,然后储存在字符数组中,在底层储存这些字符对应的ascii马

当字符为负数时,将会把负号也储存下来,但是如果是正数,正号将不会被储存


当需要了解BigInteger 类和BigDecimal类的底层储存方式,可以用idea 的debeg模式进行了解

正则表达式

正则表达式可以校验字符串是否满足一定的规则,并用来校验数据格式的合法性

  • 案例分析
    判断一个qq号是否符合要求,规则:6-20(包含6位和20位)位之间,0不能在开头,必须全部是数字

用传统方法实现

package com.cn;

public class Regex {
    public static void main(String[] args) {
    String qq="0123443";
        System.out.println(chackQQ(qq));
    }
   public static   boolean chackQQ(String qq){
        //长度
        int len=qq.length();
        if(len<6||len>20)
            return false;
        //是否以0开头
        //char a=qq.charAt(0);//取出第一个字符
        //if(a=='0')
       if(qq.startsWith("0"))//判断是否以特点字符开头
            return false;
       //字符串是否都是数字
       for (int i = 0; i < qq.length(); i++) {
           if(qq.charAt(i)<'0'||qq.charAt(i)>'9')
               return  false;
       }
       return true;

   }


}


regex:代表正则表达式
可以看出这种方法逻辑的实现比较复杂,代码量较多

验证一个字符串是否满足一定的规则:可以使用字符串的matches方法,用于验证该字符串是否满足对应的正则表达式
public boolean matches(String regex)
告知此字符串是否匹配给定的正则表达式。

书写正则表达式的规则:按照需要满足的规则从左往右依次书写
(第一个数字不能为0 故先表示不能为0 的规则)

用正则表达式实现:

package com.cn;

public class Regex {
    public static void main(String[] args) {
    String qq="12344334";
        System.out.println(qq.matches("[1,9]\\d{5,7}"));//数字范围1-9,\\d表示数字,{}代表数字的个数
    }


}

正则表达式的作用

正则表达式中的方括号不是数组,是一个范围

  • 具体正则规则

字符类规则举例

package com.cn;

public class Regex {
    public static void main(String[] args) {
        //1.[abc]表示只能是abc中的一个字符
        System.out.println("a".matches("[abc]"));//true
        System.out.println("ab".matches("[abc]"));//false:“ab”和abc中的一个比较当然位false
        System.out.println("ab".matches("[abc][abc]"));//true a和第一个正则比较b和第二个正则比较
        //2.[^abc]表达和除了字符abc之外的字符相比较
        System.out.println("a".matches("[^a]"));//false
        System.out.println("abc".matches("[^bdc]"));//false 和前面的一样是单个字符进行比较
        //3.[a-zA-Z]表示从a-z和A-Z的范围的字符
        System.out.println("!".matches("[a-zA-Z]"));//false
        System.out.println("d".matches("[a-zA-Z]"));//true
        //4.[a-z[A-Z]] 表示的意思和上面相同,但这种写法更易于理解
        System.out.println("!".matches("[a-z[A-z]]"));//false
        System.out.println("d".matches("[a-z[A-Z]]"));//true
        //&&表示交集
        //5.[a-z&&[def]]表示从a-z的字符和def这三个字符的交集字符  很明显就是def字符
        System.out.println("d".matches("[a-z&&[def]]"));//true
        //注意:如果此时的&&写成了&表示的就是且的意思
       // 举例:
        System.out.println("d".matches("[a-c&&[def]]"));//false :交集位空
        System.out.println("d".matches("[a-c&[def]]"));//true 且
        //6.[a-z&&[^abc]] 表示a-z和除了字符abc字符的交集
        System.out.println("w".matches("[a-z&&[^abc]]"));//true
        //7.[a-z&&[^a-d]] 表示a-z的范围和除了a-d这个范围的交集
        System.out.println("a".matches("[a-z&&[^a-d]]"));//false
        System.out.println("e".matches("[a-z&&[^a-d]]"));//true

    }
}

  • 预定义字符
package com.cn;

public class Regex {
    public static void main(String[] args) {
       //. 表示任意字符
        System.out.println("@".matches("."));//true
        System.out.println("#@".matches("."));//false:只能校验一个字符
        //\d 表示一个数字字符:[0-9]
        System.out.println("4".matches("\\d"));//true  \\  因为\有特殊的含义\\用于转义\
        System.out.println("a".matches("\\d"));;//false
        // \D 表示非数字字符:[^[0-9]]
        System.out.println("2".matches("\\D"));//false
        System.out.println("@".matches("\\D"));//true
        // \s表示一个空白字符 包含 [\t\n\x0B\f\r]
        System.out.println("\t".matches("\\s"));//true
        //\S表示非空白字符
        //注意:空字符不是空白字符
        System.out.println("a".matches("\\S"));//true
        //\w表示单词字符[a-zA-Z_0-9]
        System.out.println("#".matches("\\w"));//false
        System.out.println("c".matches("\\w"));//true
        //\W表示非单词字符
        System.out.println("#".matches("\\W"));//true
        System.out.println("c".matches("\\W"));//false
    }
}

对于正则表达式的具体规则可以在Pattern类中查看或者查找String类找到matches方法介绍中的超链接

如果想校验多次,可以让前面的内容出现多次,用到下面的规则

package com.cn;

public class Regex {
    public static void main(String[] args) {
      //X?  X出现一次或者0次
        System.out.println("z".matches("[a-z]?"));//true
        System.out.println("".matches("[a-z]?"));//true
        // X* X出现0次或者多次
        System.out.println("agdedeeeeeeee".matches("[a-z]*"));//出现多次
        //X+ X出现一次或者多次
        System.out.println("".matches("[a-z]+"));//false :没有0次
        //X{n} Xe正好出现n次
        System.out.println("abcd".matches("[a-z]{4}"));//true
        System.out.println("adcde".matches("[a-z]{4}"));//false:5个字符只出现4次不能匹配
        //X{n,} 至少出现n次
        System.out.println("abc".matches("[a-z]{3,}"));//true:刚好出现n次
        System.out.println("agdeeecddff".matches("[a-z]{3,}"));//true至少出现3次
        //X{n,m} 至少出现n次但不超过m次 [n,m]
        System.out.println("abc".matches("[a-z]{3,5}"));//true
        System.out.println("abcdef".matches("[a-z]{3,5}"));//false 出现不能超过5次
    }
}

正则表达式的练习

  • 手机号码验证
    验证心得:拿着一个正确的数据从左到右去验证每一位
    正确数据:13453456743(共11位)
    **第一部分:第一位只能是1 ** 表示:[1]
    第二部分:第二位在3-9之间 表示:[3-9]
    第三部分:后面的9位都在0-9之间 表示:\d{9} 后面出现9次
    可以得出正则表达式:
    String regex="1[3-9]\d{9}";
package com.cn;

public class Regex {
    public static void main(String[] args) {
        /*验证手机号码是否正确
        **验证心得:拿着一个正确的数据从左到右去验证每一位**
*正确数据:13453456743(共11位)*
**第一部分:第一位只能是1  ** 表示:[1]
**第二部分:第二位在3-9之间** 表示:[3-9]
**第三部分:后面的9位都在0-9之间** 表示:\\d{9} 后面出现9次
可以得出正则表达式:
String regex="1[3-9]\\d{9}";

         */
        String regex1="1[3-9]\\d{9}";//1写成[1]也行
        System.out.println("13453456743".matches(regex1));//true 满足情况
        System.out.println("12453456789".matches(regex1));// false第二位不满足
        System.out.println("134234564323".matches(regex1));//false超过11位 不满足
    }
}

  • 验证座机号码
    正确的座机号:

    通过观察我们可以将这个号码分为3个部分:
  • 区号:0\d{2,3}
    第一位固定是0 后面是任意数字可以出现2-3次
  • -:-? 出现一次或者0次
    通过观察-可以出现也可以不出现
  • 号码:[1-9]\d{5,10}
    号码的第一位非零,即在1-9之间,后面的都是数字,我们可以将号码的数量控制在5-10之间
package com.cn;

public class Regex {
    public static void main(String[] args) {
      //通过上面的分析我们可以退出正则表达式
        String regex="0\\d{2,3}-?[1-9]\\d{4,9}";
        System.out.println("027-23456".matches(regex));//true
        System.out.println("0276-34567".matches(regex));//true
        System.out.println("127-23456".matches(regex));//false
        System.out.println("027-2345".matches(regex));//false
    }
}

  • 邮箱号码

一般在实际的开发中,我们一般会百度正则表达式然后将其该成自己想要的正则表达式即可
可以安装 any-rule插件用于正则表达式的生成
^表达向上开始匹配 $表示一直匹配到末尾,maches方法已经具备这个功能,故将其删除,将?:也删除,将一个\的再加一个\进行转义**

正则表达式正则二十四时间制

package com.cn;

public class Regex {
    public static void main(String[] args) {

        String regex="([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d";
        System.out.println("23:34:56".matches(regex));//true
        System.out.println("08:34:56".matches(regex));//true
        System.out.println("21:87:23".matches(regex));//false


    }
}

  • 根据分钟和秒钟的规则一致可以进行分组优化(用()代表分组)

正则表达式的练习2
[]表示里面的内容任意出现一个
练习:

package com.cn;

public class Regex {
    public static void main(String[] args) {
    /*1.正则用户名是否符合要求
      大小写字母,数字,下划线 一共4-16位

     */
      //可以看出都满足统一规则:故分为一个部分 \\w{4,16}  或者[a-zA-Z0-9_]{4,16}
        String regex1="\\w{4,16}";
        System.out.println("hello4".matches(regex1));//true
        //正则身份证号码是否符合要求
        /*3.简单要求:18位,前17位为任意数字,最后一位可以是数字可以是大写或小写的x*/
      //分部:
        //第一位为第一部分:[1-9] 第一位数字不能为0
        //往后16位第二部分:\\d{16}  前17位为任意数字并且有且仅有17位
        //最后一位为第三部分:\\d[xX]
        //String regex2="[1-9]\\d{16}[\\dxX]";//这种写法难以理解
        String regex2="[1-9]\\d{16}(\\d|x|X)";//这种写法更易于理解 如果不写()将会用|前面的所有组成的范围
        System.out.println("12343234532345675X".matches(regex2));//true
        /*复杂要求:按照身份证号码的格式严格要求*/
       

    }
}

  • 忽略大小写的匹配方式
//忽略大小写字母的匹配
        //1.(?i)abc表示忽略abc的大小写
        String regex5="(?i)abc";//该字符可以是abc的大小写
        System.out.println("abc".matches(regex5));//true
        System.out.println("aBc".matches(regex5));//true
        System.out.println("aBC".matches(regex5));//true
     //2.a(?i)bc 表示忽略bc的大小写
        String regex6="a(?i)bc";
        System.out.println("aBC".matches(regex6));//true
        System.out.println("abC".matches(regex6));//true
     //3.a((?i)b)c  仅忽略b的大小写
        String regex7="a((?i)b)c";
        System.out.println("abC".matches(regex7));//false
        System.out.println("aBc".matches(regex7));//true

身份证号码的严格校验
编写正则的心得
1.按照正确的数据进行拆分
2.寻找每一部分的规律,并编写正则表达式
3.将每一部分拼接在一起,就是最终的结果
4.书写的时候从左往右去书写
正确数据:421127 20050412 1318
总体分为三个部分:

  • 前6位表示地址信息
    **要求第一个数字不为0后面5位为任意数字 该部分正则位:[1-9]\d{5}

  • 中间8位表示出生年月日
    分为年月日:
    年:**正则为:(18|19|20)\d{2}
    1.前部分:18 19 20
    2.后部分:为任意数字
    月:01-09 10 11 12 将其分成2种情况 (0[1-9]|1[0-2])
    日:01-31 也可以分组:01-09 10-19 20-29 30-31 (0[1-9]|1[0-9]|2[0-9]|3[0-1])

  • 后4位:任意数字出现3次,第四位可以是任意数字也可以是大写x或者小写x 正则:\d{3}(\d|(?i)x)

严格身份证校验正则:

package com.cn;

public class Regex {
    public static void main(String[] args) {
        //注意:使用|的时候一定要使用()严格控制其选取的范围
     String regex1="[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|1[0-9]|2[0-9]|3[0-1])\\d{3}(\\d|(?i)x)";
        System.out.println("42112720010426131x".matches(regex1));
    }
}


正则表达式用于爬取数据

  • 用法一:爬取本地的信息
    文本举例
    在java中用的最多的就是java8和java11,因为这2个版本是官方长期支持的版本,下一个长期支持的版本了java17,他们都很棒呢

2个类

  • Pattern类 表示正则表达式
  • Matcher类 文本匹配器,作用按照正则表达式的规则去匹配字符串,从头开始读取(在大串中去找符合匹配规则的子串
package com.cn;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Regex {
    public static void main(String[] args) {
       //文本串
        String str="在java中用的最多的就是java8和java11,因为这2个版本是官方长期支持的版本,下一个长期支持的版本了java17,他们都很棒呢";
        //实例化正则表达式的对象
        //其对象只能用静态方法实例化
        Pattern p=Pattern.compile("java\\d{0,2}");
        //实例化文本匹配器
        Matcher m=p.matcher(str);
        //关于find方法的解释:
        //拿着文本匹配器从头开始读取,在文本串中寻找满足规则的字串
        //如果没有find返回false
        //如果有则返回true,并在底层记录字串的起始索引和结束索引+1


       boolean a= m.find(); //该处记录了开始和结束索引1,5
        //group返回查找到的字串并返回(底层使用记录的索引用subString(1,5)进行截取)
        System.out.println(m.group());//java
        
        //第二次爬取的爬取的时候会接着第一次的索引继续往后爬取
        
        System.out.println("-----------------------第二次爬取----------------------");
        boolean b=m.find();
        System.out.println(m.group());//java8



    }
}

可以看出一个问题:因为我们并不知道文本有多少个我们需要的信息,不知道要爬取多少次,所以对于这种情况我们可以使用循环来进行改进

  • 循环优化
  //文本串
        String str="在java中用的最多的就是java8和java11,因为这2个版本是官方长期支持的版本,下一个长期支持的版本了java17,他们都很棒呢";
        //用循环对此过程进行改进
        Pattern p=Pattern.compile("java\\d{0,2}");

        //实例化文本匹配器
        Matcher m=p.matcher(str);//拿着m去匹配符合p规则的字串
      while(m.find()){//一直读到文本匹配器不能匹配到文本为止(注意:该处已经完成匹配了)

          System.out.println(m.group() );
      }


爬取网络信息(很有趣)

本地爬取综合训练

package com.cn;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Regex {
    public static void main(String[] args) {
       //文本串
        String str="来某马程序员学习java,电话:18451457456,13587459875或者练习邮箱:[email protected] 座机电话:01036517895,010-98951256" +
                "邮箱:[email protected],热线电话:400-618-9090,400-6184000,4006189090" ;
        /*
        手机电话正则:1[3-9]\\d{9}
        邮箱正则:\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2}
        座机正则:0\\d{2,3}-?[1-9]\d{4,9}
        热线电话正则 400-?[1-9]\\d{2}-?[1-9]\\d{3}
         */
        //在匹配的过程中应该是符合一个规则就可以

        //获取正则表达式的对象
        Pattern p=Pattern.compile("(1[3-9]\\d{9})|(\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2})|(0\\d{2,3}" +
                "-?[1-9]\\d{4,9})|(400-?[1-9]\\d{2}-?[1-9]\\d{3})");
      //获取文本匹配器的对象
        Matcher m=p.matcher(str);
        while(m.find()){
            System.out.println(m.group());
        }
    }
}

带条件爬取 贪婪爬取和识别正则的2个方法

  • 带条件的数据爬取
 //文本串
        String str="java自从95年问世以来,经历了很多版本,目前企业中用的最多的是java" +
                "8和java11,因为这2个版本是长期支持的版本,下一个长期支持的版本是java17" +
                "相信在不久的将来java17也会逐渐登上历史的舞台";
        //定义正则表达式
        String regex1="java(?=8|11|17)";//这里的?理解成占位符,代表前面的东西,=理解成连接符(搜寻java11 java8 java17 但只显示java )
       //实例化正则表达式对象
        Pattern p=Pattern.compile(regex1);
        //实例化文本匹配器的对象
        Matcher m=p.matcher(str);
        while(m.find()){
            System.out.println(m.group());
        }

需求二

  //文本串
        String str="java自从95年问世以来,经历了很多版本,目前企业中用的最多的是java" +
                "8和java11,因为这2个版本是长期支持的版本,下一个长期支持的版本是java17" +
                "相信在不久的将来java17也会逐渐登上历史的舞台";
        //定义正则表达式
        //String regex1="java(8|11|17)"; 写法一
        String regex1="java(?:8|11|17)"; // 写法二:java可以和:后面的拼接
       //实例化正则表达式对象
        Pattern p=Pattern.compile(regex1);
        //实例化文本匹配器的对象
        Matcher m=p.matcher(str);
        while(m.find()){
            System.out.println(m.group());
        }
  • 需求3
 //文本串
        String str="java自从95年问世以来,经历了很多版本,目前企业中用的最多的是java" +
                "8和java11,因为这2个版本是长期支持的版本,下一个长期支持的版本是java17" +
                "相信在不久的将来java17也会逐渐登上历史的舞台";
        
        //定义正则表达式
       String regex1="java(?!8|11|17)";// !意思为去除 理解为除了java8 java11 java17的java文本

       //实例化正则表达式对象
        Pattern p=Pattern.compile(regex1);
        //实例化文本匹配器的对象
        Matcher m=p.matcher(str);
        while (m.find()) {
            System.out.println(m.group());
        }
output:java

贪婪爬取和非贪婪爬取

贪婪爬取:爬取数据的时候尽可能多的获取数据
非贪婪爬取:爬取数据的时候尽可能少的获取数据

只写+和*表示贪婪匹配
+? 表示非贪婪匹配
*? 表示非贪婪匹配
举例:
ab+(此时的默认就是贪婪爬取)
贪婪爬取:abbbbbbbbbbbbbbbbbb
非贪婪爬取:ab
在java中默认的就是贪婪爬取,如果我们在数量词+ *后面加上?就是非贪婪爬取

-贪婪爬取和非贪婪爬取

package com.cn;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Regex {
    public static void main(String[] args) {

        //文本串
        String str="java自从95年问世以来,经历了很多版本,abbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa目前企业中用的最多的是java" +
                "8和java11,因为这2个版本是长期支持的版本,下一个长期支持的版本是java17" +
                "相信在不久的将来java17也会逐渐登上历史的舞台";
          //获取正则表达式的对象
        Pattern p=Pattern.compile("ab+");//贪婪爬取  非贪婪爬取 正则改为 ab+? 输出为ab
        //获取文本匹配器的对象
        Matcher m=p.matcher(str);
        while(m.find()){
            System.out.println(m.group());
        }

    }
}
//output:abbbbbbbbbbbbbbbbbbbbbbbb

正则表达式在字符串方法中的使用
idea中ctrl+p看函数的参数

正则表达式用来判断字符串是否满足一定的规则,通过既定的规则我们又可以查找符合这一规则的字符串
由此的得出正则表达式的作用:
1.判断字符串是否满足规则
2.查找一段文本中满足规则的字符串

而String 方法用所使用的是正则表达式的第二项作用
方法原型
public String replaceAll(String regex String newstr)
public String[]split(String regex)按照正则表达式所匹配的字符串进行分割

package com.cn;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Regex {
    public static void main(String[] args) {
    //文本串
        String str="张三eesegewete23453433gvvd李四dfgredee2344544王五";
        /*需求:1.将名字之间的字母和数字都替换成vs
               2.把字符串中的名字切割出来

         */
        //替换:将正则表达式所查找的内容替换成预定的内容
        String regex1="[\\w&&[^_]]+";//该内容出现一次或多次
        String str1=str.replaceAll(regex1,"vs");
        System.out.println(str1);//张三vs李四vs王五
        //分割:表示将正则表达式匹配的内容从中抠出,将剩下的内容以字符串数组的形式返回
        String regex2="[\\w&&[^_]]+";
        String[]str3=str.split(regex2);
      for(int i=0;i<str3.length;i++){
          System.out.print(str3[i]);//张三李四王五
      }





    }
}

方法原型中为String regex表示该参数识别正则表达式
捕获分组和非捕获分组


对于一些字符串后面出现的一些字符要求和前面已知的字符一致,对此我们可以使用捕获分组来实现

package com.cn;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Regex {
    public static void main(String[] args) {
    //需求1:判断一个字符串的开始字符和结束字符是否一致,只考虑一个字符
        //举例:a123a  b34c3b  @23%3454@  a34433c(false)
        String regex1="(.).+\\1";//将第一个数分为一组 中间任意出现(至少一次)最后必须和第一组分组一致 故重用第一分组

        System.out.println("a123a".matches(regex1));//true
        System.out.println("b34c3b".matches(regex1));//true
        System.out.println("@23%3454@".matches(regex1));//true
        System.out.println("a34433c".matches(regex1));//false
        System.out.println("-----------------------------------------------");
     //需求2:判断一个字符串的开始部分和结束部分是否一致,可以有多个字符
        //举例:abc12334abc  123erer23123 b233db  #!$erdfd2333#!$  abciiend34bd(false)
        String regex2="(.+).+\\1";//至于为什么第一个分组读任意字符只会读到和后面重复的那几位 我认为可能了编译器比较强大
        System.out.println("abc12334abc".matches(regex2));//true
        System.out.println("123erer23123".matches(regex2));//true
        System.out.println("b233db".matches(regex2));//true
        System.out.println("#!$erdfd2333#!$".matches(regex2));//true
        System.out.println("abciiend34bd".matches(regex2));//false


    }
}

当看到xxx一致,则使用分组

  • 需求3
  //需求3:判断一个字符串的开始部分和结束部分是否一致,开始部分内部每个字符一致
       // 举例: aaa344ffaaa  www7889www  11deeeerd11
        //当看到xxx一致则用到分组
        //该需求明显有2个一致的关系,根据关系可以使用分组嵌套
        System.out.println("-----------------------------");
        String regex3="((.+)\\2).+\\1";
        System.out.println("aaa344ffaaa".matches(regex3));//true
        System.out.println("www7889www".matches(regex3));//true
        System.out.println("11deeeerd11".matches(regex3));//true

String方法的replaceAll split等识别正则表达式的方法,在爬取字符串内容的时候,也是像爬虫一样,会将字符串从头到尾都识别一般,不会爬取到一个内容就停止

replaceAll 替换的过程则是 爬取到一个符合正则表达式的内容就与后面的内容进行替换,然后以此反复,直到文本爬取完为止

  • 验证 分组内容在组外的使用
    我们的目标是用正则表达式可以表示即将被替代的内容
package com.cn;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Regex {
    public static void main(String[] args) {
   /*需求:将 我我我我要要要学学学编编编程程程程程
      替换成:我要学编程

    */

    //1.我我我我  --》我
    //2.要要要 ----》 要
    //3. 编编编----》 编
    //4. 程程程程程------》程
    //用分组表示重复的部分 并且重复的部分至少出现一次
        //正则表达式
        String str="我我我我要要要学学学编编编程程程程程";
        String regex="(.)\\1+";
        //$组外使用分组内容
        System.out.println(str.replaceAll(regex,"$1"));//我要学编程
    }
}

非捕获分组

非捕获分组的特征符号可以写也可以不写,了解这些是为了更好的读懂代码

jdk7前的时间相关类

Date 时间类(java.util包中)

时间的一些常识知识

  • 同一经线的时间相同
  • 以0度经线(本初子午线)的时间为世界时间(东部更快,西部更慢)
  • 中国在东8区,比世界时间快8个小时(和世界时间相比+8h)

得出结论:这种计算时间的误差过大,已经不再作为标准的时间被使用

  • 标准时间

据说用这种方式计算的时间号称2000万年才误差1s

阶段性总结

  • 根据Date类的描述猜想Date类的底层实现

    查看源码可以知道,java官方关于Date类的内部实现和我们猜想的几乎一致(实现方式有些差异)

我们需要了解以下方法

  • public Date() 空参构造 表示当前时间
  • public Date(long date) 有参构造表示指定的时间
  • public void setTime(long time) 修改时间的毫秒值
  • public long getTime() 获取时间对象的毫秒值

计算机默认将1970年1月1日0:0:0作为时间原点,中国位于东8区,中国的时间原点为1970年1月1日8:0:0(中国的计算机在出厂时已经设置好,不用+8h)

在计算时间时,我们需要给出的是时间原点到目标时间时间段的毫秒值(用时间原点的毫秒值+间隔时间的毫秒值)

package com.cn;

import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;

import java.util.Date;

public class DateTest {
    public static void main(String[] args) {
        //空参构造表示当前时间
        Date d=new Date();
        //输出该对象的时候调用该类toString方法:将该对象转化成该种字符串表示
        System.out.println(d);//Tue(星期) Dec(月) 13(日) 09:26:48(时分秒) CST 2022(年)
        //有参构造表示指定的时间
        //表示从时间原点开始过了0毫秒的时间
        Date d2=new Date(0L);//指定的时间是0 这表示的是时间原点 中国是东8区比时间原点多8h
        System.out.println(d2);//Thu Jan 01 08:00:00 CST 1970
        //用setTime 修改时间
        d2.setTime(1000L);//1000毫秒 1s 修改成从时间原点开始过了100毫秒(1s)的时间
        System.out.println(d2);//Thu Jan 01 08:00:01 CST 1970
        //getTime 获取当前时间的毫秒值
        System.out.println(d2.getTime());//1000 当前距离时间原点的毫秒值
    }

}

package com.cn;

import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;

import java.util.Date;

public class DateTest {
    public static void main(String[] args) {
       //需求1:打印距离时间原点一年的时间
        Date d1=new Date((365*24*3600*1000L));
        System.out.println(d1);//Fri Jan 01 08:00:00 CST 1971
        //需求2:定义2个Date对象比较一下 哪个时间在前哪个时间在后
        //也可以使用Random进行随机生成整数(即生成1000或2000)
        Date d2=new Date(1000);
        Date d3=new Date(2000);
        long time2=d2.getTime();
        long time3=d3.getTime();
        if(time2>time3){
            System.out.println("d3的时间在前");
        }else{
            System.out.println("d2的时间在前");
        }//output:d2的时间在前
    }

}

simpleDateFormat(java.text中)

我们之所以学习该类,是为了解决Date类关于时间的表示不符合习惯的问题,这种时间的表达方式不符合国人的阅读习惯

simpleDateFormat格式化时间很常用

  • simpleDateFormat类得到构造方法和常用方法

Date类表示时间,而simpleDateFormat的作用仅仅是将时间格式化,可以猜想其必须和Date类的对象一起才能发挥作用

  • simpleDateFormat类格式化的演示

具体的格式规则可参见API文档

package com.cn;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;

import java.text.SimpleDateFormat;
import java.util.Date;

//演示simepleDate类的常用方法
public class simpleDateFormatTest {

    /*public simpleDateFormat()  默认格式
    public simpleDateFormat(String pattern) 指定格式

    public final String format(Date data)  格式化(Date 对象--》字符串)
    public Date parse(String source)  解析(字符串--》日期对象)*/

    public static void main(String[] args) {
        //用无参构造默认的格式化方式
        //定义时间
        Date d1=new Date(0L);
        //获取格式化字符串的对象
        SimpleDateFormat sdf1=new SimpleDateFormat();
        String str1=sdf1.format(d1);//用字符串接收将Date对象转化成指定格式的字符串
        System.out.println(str1);//70-1-1 上午8:00
        //带参构造创建对象,指定格式
        SimpleDateFormat sdf2=new SimpleDateFormat("yyyy年 M月 d日 hh:mm:ss E");//指定格式
        String str2=sdf2.format(d1);
        System.out.println(str2);//1970年 1月 1日 08:00:00 星期四
    }





}

第二个作用解析:将字符串解析成Date对象

package com.cn;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

//演示simepleDate类的常用方法
public class simpleDateFormatTest {

    /*public simpleDateFormat()  默认格式
    public simpleDateFormat(String pattern) 指定格式

    public final String format(Date data)  格式化(Date 对象--》字符串)
    public Date parse(String source)  解析(字符串--》日期对象)*/

    public static void main(String[] args) throws ParseException {
        //将格式化成字符串的时间解析成(转化)Date对象
        //定义格式化时间的字符串
        String str="2015-03-23 08:34:56";
        //注意:如果字符串时间的格式和创建对象不一致将会报错
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
       Date date= sdf.parse(str);
        System.out.println(date);//Mon Mar 23 08:34:56 CST 2015
    }





}

练习

package com.cn;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
//2000-11-11---->2000年11月11日
//演示simepleDate类的常用方法
public class simpleDateFormatTest {
    public static void main(String[] args) throws ParseException {
        //初始格式
       String str="2000-11-11";
      SimpleDateFormat stf=new SimpleDateFormat("yyyy-MM-dd");
      //将此格式的字符串转化为Date对象
       Date date=stf.parse(str);
       //将Date对象格式化
        SimpleDateFormat sdf2=new SimpleDateFormat("yyyy年M月d日");
        String str2=sdf2.format(date);//用str2接收格式化后的字符串表示
        System.out.println(str2);//2000年11月11日
    }





}

package com.cn;


import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
//判断是否成功参加了秒杀活动
//演示simepleDate类的常用方法
public class simpleDateFormatTest {
    public static void main(String[] args) throws ParseException {

    //思路:如果参加秒杀活动的时间在活动开始时间和结束时间之间则成功,否则就失败
        //可以将格式化后字符串表示的时间 解析成Date对象,然后换算成毫秒值进行比较

        //1.解析
        String starTime="2023年11月11日 00:00:00";//开始时间
        String endTime="2023年11月11日 00:10:00";//结束时间
        String orderTime="2023年11月11日 00:01:00";//参加时间
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");//定义格式
     
            Date dStr = sdf.parse(starTime);
            Date dEnd = sdf.parse(endTime);
            Date dOrder = sdf.parse(orderTime);
      
            
        //将Date对象换算成毫秒值
        long start=dStr.getTime();
        long end=dStr.getTime();
        long order=dStr.getTime();
        //进行比较
        if(order>=start&&order<=end){
            System.out.println("参加活动成功");
        }else {
            System.out.println("参加活动失败");
        }




    }





}

Calendar(日历)类


确实对于这样的需求,用我们已经学了的类也可以实现,但是可以看到这样实现出来有点复杂


Calendar类是一个抽象类,不能实例化对象,而是通过一个静态方法获取到子类的对象,然后进行向上转型

实例化对象演示

//Calendar是抽象类
        //底层原理:
        //会根据系统的不同时区来获取不同的子类对象,默认为当前时间
        //会将时间中的纪元,年,月,日,时,分,秒,星期,等等放在一个数组当中
        Calendar a=Calendar.getInstance();
        System.out.println(a);//截取一段YEAR=2022,MONTH=11,WEEK_OF_YEAR=51,WEEK_OF_MONTH=3,DAY_OF_MONTH=13,

当需要修改日历的时间

package com.cn;
//测试Calendar类中的方法
/*
第一类
    public static getCalendar()  静态方法获取日历的对象
第二类
    public final Date getTime()  获取日期的对象
    public final void setTime(Date d) 给日历设置日期对象
第三类
    public long getTimeInMillis() 获取时间的毫秒值
    public void setTimeInMillis(long millis) 给日历设置时间的毫秒值
第四类
    public int get(int field)  获取日期中某个字段(属性)的信息
    public void set(int field ,int value) 修改日期中某个字段的信息
    public void add(int field,int amount) 为某个字段增加/减少值
 */

import java.util.Calendar;
import java.util.Date;

public class CalendarTest {
    public static void main(String[] args) {
        //Calendar是抽象类
        //底层原理:
        //会根据系统的不同时区来获取不同的子类对象,默认为当前时间
        //会将时间中的纪元,年,月,日,时,分,秒,星期,等等放在一个数组当中
        //获取Calendar对象的细节:
        //1.月份:获取的范围是:0-11 如果获取的是0,则是1月,都往后推一个月
        //2.星期:在老外的眼里 星期日是一个星期的第一天
        // 1(星期日) 2(星期一) 3 (星期二)4(星期三) 5(星期四) 6(星期五) 7(星期六)都往前推一天
        //默认为当前时间
        Calendar a=Calendar.getInstance();
        System.out.println(a);//YEAR=2022......
        //需要修改日历的时间
        Date d =new Date(0L);
        a.setTime(d);
        System.out.println(a);//YEAR=1970
        //getTime获取日历的Date对象
        Date d1=a.getTime();
        System.out.println(d1);//Thu Jan 01 08:00:00 CST 1970

    }
}

和时间毫秒值相关的get和set方法和前面的用法一致,不再演示

  • get方法获取属性
package com.cn;
//测试Calendar类中的方法
/*
第四类
    public int get(int field)参数为该属性在该属性数组中的索引  获取日期中某个字段(属性)的信息
    public void set(int field ,int value) 修改日期中某个字段的信息
    public void add(int field,int amount) 为某个字段增加/减少值
 */

import java.util.Calendar;
import java.util.Date;

public class CalendarTest {
    public static void main(String[] args) {
        //Calendar是抽象类
        //底层原理:
        //会根据系统的不同时区来获取不同的子类对象,默认为当前时间
        //会将时间中的纪元,年,月,日,时,分,秒,星期,等等放在一个数组当中
        /*该数组中的属性的索引为
            0 纪元
            1 年
            2 月
            3 一年中的第几周
            4 一个月中的第几周
            5 一个月中的第几天(日)
         */
        //获取Calendar对象的细节:
        //1.月份:获取的范围是:0-11 如果获取的是0,则是1月,都往后推一个月
        //2.星期:在老外的眼里 星期日是一个星期的第一天
        // 1(星期日) 2(星期一) 3 (星期二)4(星期三) 5(星期四) 6(星期五) 7(星期六)都往前推一天
        //默认为当前时间

       //获取日历中的属性信息
        Calendar c=Calendar.getInstance();//获取日历实例
        //获取该日历对象的年月日星期
        int year=c.get(1);
        int month=c.get(2);
        int day=c.get(5);
        System.out.println(year+","+month+","+day);//2022,11,13(实际的月份应该+1)


    }
}

可以看到我们获取了日历的年月日的属性值,但是当我想去获取星期时,发现我忘记了星期这个属性的索引

为此java的设计者也发现了这个问题,故他们将对应的索引值,设计成了常量,这样更容易记忆

设计的源码掠影

对此我们可以修改我们的代码

package com.cn;
//测试Calendar类中的方法
/*
第四类
    public int get(int field)参数为该属性在该属性数组中的索引  获取日期中某个字段(属性)的信息
    public void set(int field ,int value) 修改日期中某个字段的信息
    public void add(int field,int amount) 为某个字段增加/减少值
 */

import java.util.Calendar;
import java.util.Date;

public class CalendarTest {
    public static void main(String[] args) {
        //Calendar是抽象类
        //底层原理:
        //会根据系统的不同时区来获取不同的子类对象,默认为当前时间
        //会将时间中的纪元,年,月,日,时,分,秒,星期,等等放在一个数组当中
        /*该数组中的属性的索引为
            0 纪元
            1 年
            2 月
            3 一年中的第几周
            4 一个月中的第几周
            5 一个月中的第几天(日)
         */
        //获取Calendar对象的细节:
        //1.月份:获取的范围是:0-11 如果获取的是0,则是1月,都往后推一个月
        //2.星期:在老外的眼里 星期日是一个星期的第一天
        // 1(星期日) 2(星期一) 3 (星期二)4(星期三) 5(星期四) 6(星期五) 7(星期六)都往前推一天
        //默认为当前时间

       //获取日历中的属性信息
        Calendar c=Calendar.getInstance();//获取日历实例
        //获取该日历对象的年月日星期
        int year=c.get(Calendar.YEAR);
        int month=c.get(Calendar.MONTH)+1;//实际的月份+1
        int day=c.get(Calendar.DAY_OF_MONTH);//日
        int week=c.get(Calendar.DAY_OF_WEEK)-1;//星期:实际的星期-1
        System.out.println(year+","+month+","+day+","+week);//2022,12,13,2


    }
}

但是也有一个问题,就是星期的表示方式不符合国人的习惯-----用查表法进行优化

package com.cn;
//测试Calendar类中的方法
/*
第四类
    public int get(int field)参数为该属性在该属性数组中的索引  获取日期中某个字段(属性)的信息
    public void set(int field ,int value) 修改日期中某个字段的信息
    public void add(int field,int amount) 为某个字段增加/减少值
 */

import java.util.Calendar;
import java.util.Date;

public class CalendarTest {
    public static void main(String[] args) {
        //Calendar是抽象类
        //底层原理:
        //会根据系统的不同时区来获取不同的子类对象,默认为当前时间
        //会将时间中的纪元,年,月,日,时,分,秒,星期,等等放在一个数组当中
        /*该数组中的属性的索引为
            0 纪元
            1 年
            2 月
            3 一年中的第几周
            4 一个月中的第几周
            5 一个月中的第几天(日)
         */
        //获取Calendar对象的细节:
        //1.月份:获取的范围是:0-11 如果获取的是0,则是1月,都往后推一个月
        //2.星期:在老外的眼里 星期日是一个星期的第一天
        // 1(星期日) 2(星期一) 3 (星期二)4(星期三) 5(星期四) 6(星期五) 7(星期六)都往前推一天
        //默认为当前时间

       //获取日历中的属性信息
        Calendar c=Calendar.getInstance();//获取日历实例
        //获取该日历对象的年月日星期
        int year=c.get(Calendar.YEAR);
        int month=c.get(Calendar.MONTH)+1;//实际的月份+1
        int day=c.get(Calendar.DAY_OF_MONTH);//日
        int week=c.get(Calendar.DAY_OF_WEEK)-1;//星期:实际的星期-1
        System.out.println(year+","+month+","+day+","+getWeek(week));//2022,12,13,星期二


    }
    public static String getWeek(int index){//查表法:让数据和索引产生关系
        String arr[]={"","星期一","星期二","星期三","星期四","星期五","星期六","星期日"};
        return arr[index];
    }
}

修改属性

package com.cn;

import java.util.Calendar;
import java.util.Date;

public class CalendarTest {
    public static void main(String[] args) {

       //获取日历中的属性信息
        Calendar c=Calendar.getInstance();//获取日历实例
        //获取该日历对象的年月日星期
        int year=c.get(Calendar.YEAR);
        int month=c.get(Calendar.MONTH)+1;//实际的月份+1
        int day=c.get(Calendar.DAY_OF_MONTH);//日
       int week=c.get(Calendar.DAY_OF_WEEK);
        System.out.println(year+","+month+","+day+","+getWeek(week));//2022,12,13,星期二
        //修改日历中某个字段的信息
      c.set(Calendar.YEAR,1999);
      //注意:月份的范围为0-11
        //当所给的值超过11也不会报错,将会化为年加在年上面,month该为剩余的月份
      c.set(Calendar.MONTH,13);
        System.out.println(c.get(Calendar.MONTH));//1
        System.out.println(c.get(Calendar.YEAR));//2000

    }
    public static String getWeek(int index){//查表法:让数据和索引产生关系
        String arr[]={"","星期一","星期二","星期三","星期四","星期五","星期六","星期日"};
        return arr[index];
    }
}

增加或减少

package com.cn;

import java.util.Calendar;
import java.util.Date;

public class CalendarTest {
    public static void main(String[] args) {

       //获取日历中的属性信息
        Calendar c=Calendar.getInstance();//获取日历实例
        //获取该日历对象的年月日星期
        int year=c.get(Calendar.YEAR);
        int month=c.get(Calendar.MONTH)+1;//实际的月份+1
        int day=c.get(Calendar.DAY_OF_MONTH);//日
       int week=c.get(Calendar.DAY_OF_WEEK);
        System.out.println(year+","+month+","+day+","+getWeek(week));//2022,12,13,星期二
//add为某个字段增加或减少指定的值
        c.add(Calendar.MONTH,1);//将月份增加一个月
        c.add(Calendar.MONTH,-1);//将月份减少一个月

    }
    public static String getWeek(int index){//查表法:让数据和索引产生关系
        String arr[]={"","星期一","星期二","星期三","星期四","星期五","星期六","星期日"};
        return arr[index];
    }
}

jdk8新增的时间相关类(这些类的对象都是不可变的)

前期例题回顾

  • 该题的代码回顾

    可以发现这样的代码有点复杂,需要将字符串时间解析到Date对象,然后又要将对象转化为对应的毫秒值才能进行比较

  • 相同的功能用jdk8的时间类实现

  • jdk8相关类的优点

    思考:1.jdk8中可以有方法实现对象的比较和jdk7相比不用化为毫秒值比较,2.jdk8的类在多线程的环境下不会导致数据安全的问题,利用对象不可变来实现的,和字符串不可变的实现原理应该类似,原串不可变,修改将会创建一个新的对象

Instant类表示的时间不带时区,如果需要表示中国的时间需要手动加8h

  • 共有10个类根据其功能可以分为以下4类

java定义时区不是按照经线东1区,2区...来定义的
注意:java没有定义北京,我们一般使用上海

  • 常用类

    idea快捷键:在控制台里面,点一下控制台然后ctrl+f 在输出结果里面查找指定的内容

idea快捷键:ctr+alt+v自动生成左边(即接收变量)

package com.cn;

import java.time.ZoneId;
import java.util.Set;

//测试时区类中的相关方法
/*
    static Set<String> getAvailableZoneIds()  获取java中的所有时区 以集合的方式返回
    static ZoneId systemDefault()  获取系统默认的时区
    static ZoneId of()  获取一个指定的时区
 */
public class ZoneldTest {
    public static void main(String[] args) {
        //获取所有时区
       Set<String> zonelds=ZoneId.getAvailableZoneIds();
        System.out.println(zonelds.size());//600 该集合长度为600说明java一共定义了600个时区
        System.out.println(zonelds);

        //获取当前系统默认的时区
        ZoneId a=ZoneId.systemDefault();//获取系统默认时区
        System.out.println(a);//Asia/Shanghai  Asia/Singapore(修改后的打印结果)
        //我们可以在设置中的日期与时间中把计算机默认的时区修改,打印的就是修改后的时区

        //获取一个指定的时区
        ZoneId of = ZoneId.of("Asia/Shanghai");//该方法当当不写参数或者所给时区不存在都会报错
        System.out.println(of);//Asia/Shanghai

    }
}

Instant类(注意和jdk7中的时间类比较学习)

常见方法

Instant方法示例

package com.cn;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

//验证Instant类中的常用方法 注意:Instant类获取的都是标准时间(中国时间需要+8h)
public class InstantTest {
    public static void main(String[] args) {
        //1.获取当前时间的Instant类的对象
        Instant now = Instant.now();
        System.out.println(now);//2022-12-13T11:35:52.211Z 当前的标准时间
        //2.根据秒毫秒纳秒获取Instant对象
        Instant instant1 = Instant.ofEpochMilli(0L);//获取距离时间原点0毫秒的对象
        System.out.println(instant1);//1970-01-01T00:00:00Z
        Instant instant2 = Instant.ofEpochSecond(1L);//获取距离时间原点0s的对象
        System.out.println(instant2);//1970-01-01T00:00:01Z
        Instant instant3 = Instant.ofEpochSecond(1L, 1000000000L);//获取距离时间原点1s+1e9纳秒(1s)的对象
        System.out.println(instant3);//1970-01-01T00:00:02Z
        //3.指定时区atZone(注意该方法不是静态方法,需要对象调用) 该方法的参数是一个ZoneId对象
        //获取的是一个指定时区的时间
        ZonedDateTime shanghai = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
        System.out.println(shanghai);//2022-12-13T19:57:36.970+08:00[Asia/Shanghai] (会加或减去会标准时间间隔的时间并减少时区标识)


    }
}

package com.cn;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

//验证Instant类中的常用方法 注意:Instant类获取的都是标准时间(中国时间需要+8h)
public class InstantTest {
    public static void main(String[] args) {
        //1.isxxx 判断比较时间的前后顺序(不需要化成毫秒进行比较)
       //isBefore 判断调用者的时间是否在参数表示时间的前面
       //isAfter 判断调用者的时间是否在参数表示时间的后面
        Instant instant1 = Instant.ofEpochMilli(0L);//时间1
        Instant instant2 = Instant.ofEpochMilli(1000L);//时间2
      boolean result1=instant1.isBefore(instant2);//时间1是否在时间2的前面
      boolean result2=instant1.isAfter(instant2);//时间1是否在时间2的后面
        System.out.println(result1);//true
        System.out.println(result2);//false

        //2.minxxxx减少时间的系列方法  方法一个Instant对象  改变之后的时间将会创建一个新的对象
        //先创建一个已知的时间
        Instant instant3 = Instant.ofEpochSecond(6L);
        System.out.println(instant3);//1970-01-01T00:00:06Z
        Instant instant4 = instant3.minusSeconds(2L);//让时间减少2s
        System.out.println(instant4);//1970-01-01T00:00:04Z
        //3.plusxxx增加时间的系列方法
        Instant instant5 = instant3.plusSeconds(4L);//让其增加4s
        System.out.println(instant5);//1970-01-01T00:00:10Z


    }
}

以前的2个类为基础,学习后面的类后很简单,注意相似之处

zoneDateTime类(带时区的时间)

这些方法的使用和前面基本相同,只是该对象不是标准时间,是当前时区的对象,故没有亲自演示

DateTimeFormatter类

对时间进行格式化

  • 常用方法
package com.cn;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

//验证DateTimeFormatter类的常用方法
public class DateTimeFormatterTest {
    public static void main(String[] args) {
        /*
            static DateTimeFormatter ofPattern(格式)  获取格式对象
            String format(时间对象)  按照指定方法进行格式化 并返回格式化后对象的字符串表示
         */
        //获取时间对象(带时区)
        ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
        System.out.println(time);//原来表达形式:2022-12-13T20:52:27.268+08:00[Asia/Shanghai]
        //获取格式化器的对象
        DateTimeFormatter dtf= DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
        System.out.println(dtf.format(time));//格式化后:2022年12月13日 20:54:35 星期二
    }
}

LocalDate(年 月 日) LocalTime(时 分 秒) LocalDateTime(年 月 日 时 分 秒)

下面对这3个类的方法进行概括
注意:如果LocalDate对象用get方法就只能获取年月日 LocalTime对象的化只能获取到时分秒 LocalDateTime则都可以获取到

LocalDateTime用以下方法转化为LocalDate和LocalTime

LocalDate全部代码实践

package com.cn;

import java.sql.SQLOutput;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;

//验证LocalDate类的常用方法
public class LocalDateTest {
    public static void main(String[] args) {
        //1.获取当前时间的日历对象 static XX now()
        LocalDate nowDate=LocalDate.now();
        System.out.println(nowDate);//2022-12-13
        //2.获取指定时间的日历对象 static xxx of()
        LocalDate time1 = LocalDate.of(1999, 3, 4);//获取指定时间的日历对象
        System.out.println(time1);//1999-03-04
        //3.用get获取属性值
        LocalDate time2 = LocalDate.of(1987, 12, 3);//先获取一个时间
        //获取年
        int year=time2.getYear();
        System.out.println(year);//1987
       //获取月
        //方式1(该方式获取的是月份的对象)
        Month month = time2.getMonth();//获取月
        System.out.println(month);//DECEMBER
        System.out.println(month.getValue());//12(将其转化成阿拉伯数字)
        //方式2(换一个直接返回数值的方法)
        int month1=time2.getMonthValue();
        System.out.println(month1);//12
        //获取日
        int day=time2.getDayOfMonth();
        System.out.println(day);//3
        //获取一年中的第一天
        int dayofyear=time2.getDayOfYear();
        System.out.println(dayofyear);//337
        //返回星期
        //方式1:会返回星期的对象
        DayOfWeek dayOfWeek = time2.getDayOfWeek();
        System.out.println(dayOfWeek);//THURSDAY
        System.out.println(dayOfWeek.getValue());//4
        //方式2:换一个返回数值的方法(好像没有此方法)

        //isxxx 判断时间的前后
       LocalDate time3=LocalDate.of(2001,2,3);
       LocalDate time4=LocalDate.of(2001,2,5);
        System.out.println(time3.isBefore(time4));
        System.out.println(time1.isAfter(time4));
        //minxxx开头的方法表示减少 此处只能减少年月日
       LocalDate time5 = LocalDate.of(2021, 4, 28);//一个时间
        LocalDate time6 = time5.minusYears(1L);//减少一年
        System.out.println(time6);//2020-04-28
        //with开头的方法表示修改(只能修改年月日)
         LocalDate time7 = LocalDate.of(2008, 4, 5);//测试时间
        LocalDate time8 = time7.withMonth(7);//将月份修改成7月
        System.out.println(time8);//2008-07-05
        //plus开头表示增加 只能增加年月日
         LocalDate time9 = LocalDate.of(2009, 3, 7);//测试时间
       LocalDate time10= time9.plusDays(23);//将日增加23天
        System.out.println(time10);//2009-03-30


    }
}

一点小应用(有点没懂)

LocalTime 时分秒

和前面类基本一样,不再演示

到is为止,不包含is方法

在秒杀活动中我们只关注时分秒,这个类就很有用了

LocalDateTime

和前面基本一样,不再演示
第四部分:工具类

period 计算年月日的时间间隔

package com.cn;

import java.time.LocalDate;
import java.time.Period;

//验证Period类用于计算年月日的时间间隔
public class PeriodTest {
    public static void main(String[] args) {
        //2个测试时间
        LocalDate today=LocalDate.now();//当前时间的对象
        LocalDate time = LocalDate.of(2000, 4, 5);//瞎编了一个时间
       Period between = Period.between(time, today);//用第二个参数减第一个参数  返回一个间隔时间的对象
        System.out.println("相隔的时间"+between);//相隔的时间P22Y8M8D
        //如果这样不清晰可以获取Period属性的值
        System.out.println(between.getYears());//获取年
        System.out.println(between.getMonths());//获取月
        System.out.println(between.getDays());//获取日
        System.out.println(between.toTotalMonths());//获取时间间隔的总月数

    }
}

Duration类(时分秒 毫秒 纳秒)

该类和前面用法相似,故不再演示

ChronoUnit类(最为常用)

包装类

为什么有用包装类
1.如果不用Object不能接收所有类型
2.集合不能储存基本数据类型

以前的写法

这2中创建对象的方式---缓冲池技术,将一部分常用的对象提前被创建


将返回类型设计长Strng可以避免0开头和长度限制

调用parsexxx方法时,所提供的字符串里面数值的类型必须和函数xxx一致,如parseInt 所提供的字符串必须为"12322"不能为"12.3"否则将会出现异常
改进键盘录入

通过分析我并不认为前面改良(都用nextLine读入,然后用对应的parsexxx将字符串转化为对应的基础类型)这种方式更好,因为当一行读入的字符串的值存在空格等字符,在转化时会出现NumberFormatException在使用时还是要将数据一行一行的输入
综合练习1

package com.cn;
import java.util.ArrayList;
import java.util.Scanner;
//nextxxx和nextLine输入分析
/*
    next nextDouble nextFloat nextInt 读取数据时遇到回车 制表符 空格就和停止
 */
//案例分析&综合练习
public class Test {
    public static void main(String[] args) {
        //要求:键盘录入1-100之间的整数,并添加到集合中直到集合中所有数据的和超过200
        //创建一个集合
        ArrayList<Integer> list = new ArrayList<>();//暂且将其看成可以存放Integer的类
        Scanner st = new Scanner(System.in);
        int sum = 0;
        while (sum <= 200) {
            System.out.println("请输入一个整数:");
           String str=st.nextLine();
           int num=Integer.parseInt(str);//暂且使用这种方式来转化一下
            if (num >= 1 && num <= 100) {
                list.add(num);//自动装箱成Integer
                sum += num;
            } else {
                System.out.println("输入数据范围有误,重新输入");
            }

        }
        System.out.println("集合中的数据如下:");
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }
}


练习2*

package com.cn;
//案例分析&综合练习
import java.util.Scanner;
public class Test {
    public static void main(String[] args) {
        //题意:字符串只能是数字不能是其他字符,最少一位最多10位,0不能开头
      /*该题试错档案:
        1.该题不能用paasexx函数将字符串转化位整形,但可以使用valueof转化(这种方式是可以但是没有考虑到底层实现)

     */
        //1.定义一个字符串
        String str="9993456789";
        //2.正则表达式校验字符串
        //习惯:先过滤异常的情况,然后再写正确的情况
        if(!(str.matches("[1-9]\\d{0,9}"))){//如果数据不正常
            System.out.println("数据格式不正确");
        }else {
            System.out.println("数据格式正确");
            //3.具体转化流程
            long answer=0;//储存结果
            //遍历字符串,利用ASCII码进行转化
                for(int i=0;i<str.length();i++){
                    long num=str.charAt(i)-'0';//  该处以前没有注意到可以这样转化(好像是忘了)(第一次用valueof)
                    //把每一个数字放到answer中去(第一次想到的是用数组储存)
                    answer=answer*10+num;

            }
            System.out.println(answer);
        }
    }
}

练习3

思路1:利用除基取余法+栈的思想配合字符串连接符+实现

package com.cn;
//案例分析&综合练习
import java.util.Scanner;
public class Test {
    public static void main(String[] args) {
       //题意:定义一个方法实现toBinaryString方法的效果:将一个十进制整数字符串表示的二进制
        //用除基取余法实现
        //思路:可以用除基取余法配合栈来实现 然后用字符串连接符+进行拼接
        //Integer自带转化
        System.out.println(Integer.toBinaryString(100));//1100100
        //自己实现的转化
        System.out.println(toBinaryString(100));//1100100

    }
    public static String toBinaryString(int number){
        //用栈优化实现逆序

        int b[]=new int[100];
        int i=-1;//i为栈顶下标
        while(number!=0){
            int remainder=number%2;//获取余数
            b[++i]=remainder;
            number=number/2;

        }
        //
        String answer="";
        for(int j=i;j>=0;j--){
            answer+=b[j];
        }
        return answer;
    }
}

思路2:基数取余法配合StringBuder+逆序拼接
StringBuder可以将一些基础数据类型进行拼接成字符串

package com.cn;
//案例分析&综合练习
import java.util.Scanner;
public class Test {
    public static void main(String[] args) {
      //基数取余法+StringBuder的逆序拼接(其实感觉没有我想的那个方法好哈哈哈哈)
        System.out.println(toBinaryString(100));//100

    }
    public static String toBinaryString(int number){
        StringBuilder sb=new StringBuilder();
        while(true){
            if (number == 0) {
                break;
            }
            int remainder=number%2;//储存余数
          //进行拼接
            sb.insert(0,remainder);//每次将余数插入到0索引的位置
            number=number/2;
        }
        return sb.toString();//该处好像对象不会直接调用toString方法
    }
}

练习4

package com.cn;
//案例分析&综合练习
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class Test {
    public static void main(String[] args) throws ParseException {
     //使用代码实现计算你活了多少天(jdk7时间类实现)
       //思路:获取出生的时间和现在的时间相减即可
       //目前时间
       long nowTime=new Date().getTime();//目前时间的毫秒值
        //出生时间:将字符串解析成Date对象然后获得其毫秒值
        String birTime="2001-04-26";
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
      Date birthday= sdf.parse(birTime);
      long birthdayTime=birthday.getTime();//获取自己出生时间的毫秒值
        //处理间隔时间
      long time1=nowTime-birthdayTime;//间隔时间毫秒值
        long days=time1/1000/60/60/24;
        System.out.println("你活了"+days+"天");//你活了7902天

    }
}

jdk8时间相关类实现

package com.cn;
//案例分析&综合练习
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Scanner;
public class Test {
    public static void main(String[] args) throws ParseException {
     //使用代码实现计算你活了多少天(jdk8时间类实现)
       //思路:获取出生的时间和现在的时间相减即可
      //目前的时间
        LocalDate day1=LocalDate.now();
        //出生时间
        LocalDate day2=LocalDate.of(2001,4,26);
         long between = ChronoUnit.DAYS.between(day2, day1);
        System.out.println("你活了"+between+"天");//你活了7902天
    }
}

练习5

jdk7实现

package com.cn;
//案例分析&综合练习

import java.util.Calendar;

public class Test {
    public static void main(String[] args)  {
     //要求:判断任意一个年份是不是闰年(jkk7相关类实现)
        /*
            二月有29年是闰年
            &一年有366天是闰年
         */
        //思路1:通过年判断
        /*
            获取 xxx年1月1日 0:0:0
            &   xxx年12月31日23:59:59
            计算出相隔时间 和365比较
         */
      //思路2:通过月份比较
        /*
            xxxx年 2月1日 0:0:0:
            xxxx年 3月1日 0:0:0
            求出间隔时间和29比较
         */
       //但这样做比较麻烦,可以有日历类来实现
        //1.(年来实现)将日历设置成xxx年1月1日,把日历往前减一天
        //统计这一天是一年的第多少天
      



        //2(月来实现):把日历设置成3月1日,把前减一天,看看这一天是2月的多少号
        Calendar a=Calendar.getInstance();//目前时间的日历
        //修改成3月1日
        a.set(2000,2,1);//此处的2代表3月份
        //将日历往前减1天
        a.add(Calendar.DAY_OF_MONTH,-1);
        //获取日
         int day = a.get(Calendar.DAY_OF_MONTH);
        System.out.println(day);//29

    }
}

jdk8的时间类实现

package com.cn;
//案例分析&综合练习

import java.time.LocalDate;
import java.util.Calendar;

public class Test {
    public static void main(String[] args)  {
     //jdk8实现  jdk8中的月份范围是正常的1-12(只和年月日有关用LocalDate即可)
        //1.2月的天数判断
        LocalDate d1=LocalDate.of(2000,3,1);
        //往前减1天
         LocalDate days = d1.minusDays(1);
         //获取这一天是一个月的多少号
        final int daysDayOfMonth = days.getDayOfMonth();
        System.out.println(daysDayOfMonth);//29
        //王炸:一个专门的方法来判断是不是闰年
        //闰年返回true
        //不是闰年返回false
         System.out.println(d1.isLeapYear());//true
        //一年的天数判读不再演示
    }
}

常用类StringBuilder

见字如意,用于字符串拼接,我们知道字符串不可变,如果把字符串进行拼接,将会创建一个新的对象,如果我们需要进行大量的拼接,将会创建很多对象,使得我们的程序很慢,影响我们的性能,所以StringBuilder应运而生


如果我们不知道起什么变量名,我们可以使用类型的首字母,如StringBuilder sb,java中一种起名的习惯
基本操作

package com.cn;
//测试StringBuilder基本用法
public class StringBuilderTest {
    public static void main(String[] args) {
        //StringBuild可以看成是一个容器,其对象是可变的,一般用于字符串拼接提高效率
        //构造方法创建对象
        StringBuilder sb=new StringBuilder();//空参构造 容器里面没有内容
        StringBuilder sb1=new StringBuilder("233");//有参构造,容器里面有字符串233
        //添加元素:可以添加多种数据类型
        sb1.append(1.2);//返回的对象是sb1,不会重新创建对象
        sb1.append("iiei");//同上
        sb1.append('a');//同上
        System.out.println(sb1);//2331.2iieia (打印对象时默认调用toString 变成字符串)
        //获取长度
        System.out.println(sb1.length());//11
        //反转
        System.out.println(sb1.reverse());//aieii2.1332
        //toString方法
        String str=sb1.toString();
        str.split("");//变成字符串才能进行字符串操作
    }

}

链式编程---用方法的结果持续调用方法而不使用变量接替

  • 字符串链式编程示例
    因为这些字符串的方法的返回值都是一个字符串

同理StringBuilder的很多方法的返回值都是同一个StringBuilder对象

  • 改写前面的代码

    练习
package com.cn;

import java.util.Scanner;

//测试StringBuilder基本用法
public class StringBuilderTest {
    public static void main(String[] args) {
        //关于判断对称字符串思路很简单:将输入的字符串反转后比较字符串即可
        Scanner st=new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String str=st.next();
         String result= new StringBuilder(str).reverse().toString();//链式编程一行即可
        if(str.equals(result)){
            System.out.println("是对称字符串");
        }else {
            System.out.println("不是对称字符串");
        }

    }

}

StringBuilder的运用场景

  • 字符串的拼接
  • 字符串的反转

练习

package com.cn;

import java.util.Scanner;

//测试StringBuilder基本用法
public class StringBuilderTest {
    public static void main(String[] args) {
       //将数组里面的数据拼接成一个字符串返回
        //定义数组
        int[]a={1,2,3};
       String result= arrtostring(a);
        System.out.println(result);//[1,2,3]

    }
    public static String arrtostring(int[]arr){
        StringBuilder sb=new StringBuilder();
        sb.append('[');//先将左括号放入容器
        for(int i=0;i<arr.length;i++){
            if(i==arr.length-1){
                sb.append(arr[i]);
            }else{
                sb.append(arr[i]).append(",");
            }
        }
        sb.append(']');
        return sb.toString();
    }

}

StringJoiner类


回顾以上代码,对于字符串的拼接这种方法显然大大的提升了程序的速度,但这种写法也显得有点复杂,StringJoiner就可以让拼接又快,代码还简单

  • 直观体验一下



对StringJoiner的常用方法进行演示

package com.cn;
//测试StringJoiner类的使用
import java.util.StringJoiner;

public class StringJoinerTest {
    public static void main(String[] args) {
        /*
           注意点:1.add方法目前只能添加字符串(但并不影响使用)
                  2.这里length方法算出来的长度包含了间隔符号 开始符号和结束符号
         */
        //构造方法
        //1.确定间隔符号的构造
        StringJoiner sj=new StringJoiner("----");
        sj.add("1").add("2").add("3");
        System.out.println(sj);//1----2----3
        //2.确定间隔符号 开始符号 结束符号的构造
        StringJoiner sj1=new StringJoiner("---","[","]");
        sj1.add("m").add("n").add("l");
        System.out.println(sj1);//[m---n---l]
        System.out.println(sj1.length());//11
        //toString
        System.out.println(sj1.toString());//[m---n---l]

    }
}

思考感悟:如果拼接的对象没有 格式要求(即就是一个字符串)还是用StringBuilder好,虽然将StringJoiner的单参构造复制为空字符也可以但是没有那么便捷

结论:没有格式用StringBuilder,有格式用StringJoiner

字符串相关类的底层原理

字符串拼接的底层原理

  • 分为以下2种情况

    当拼接的时候都是字符串常量,没有变量,将会触发字符串的优化机制,在编译阶段结束最终的结果

idea快捷键ctrl+n直接搜索对应类的源码

有变量参与的拼接情况(jdk7(包含7)的拼接原理)

在拼接的时候,会在内存中创建一个StringBuilder对象,将拼接的内容放到该容器中,但是此时其还是一个对象,此时还会调用toString方法用于返回到字符串,通过查看toString的源码,可以看到,它创建了一个String对象,进行发挥,可见该部其至少创建了2个对象

后面会以此返回会继续创建新的对象..

  • 需要在思考
    虽然说字符串连接符的底层用的也是StringBuilder,但是它每2(至少1个是变量)个进行拼接就会创建一个StringBuilder对象,当拼接数量一多就会带来效率问题
    jdk8(含8)的拼接原理
    原理概括:编译器会预估拼接后的字符串长度,在内存中创建该长度的数组,将每个字符串放进数组中,然后将其变成一个字符串
    该种方式,最后只用创建一个String对象即可,但是预估也耗时间,所以还是不建议使用

StringBuilder提高效率的远离图
StringBuilder对象可变,创建出来的对象不会放到常量池

  • 常见面试题

    2种原理:不管是那种,进行拼接时,都会返回一个新的Sring对象,所以地址不同
  • 题2
    该处拼接的都是常量,将会触发优化机制,在编译阶段,就会拼接成字符串,在运行时常量池里面有将会在常量池中寻找然后复用

原理3,4总结

StringBuilder源码分析

简单的说就是StringBuilder的底层是byte数组,默认容量为16,长度是0,当添加的容量小于16则会直接储存,当大于16则会扩容,扩容后还是不够则会以实际长度为准

扩容的过程
1.如果存入的长度<16,则会直接存,长度为存入长度,容量16
2.如果存入长度>16但小于 162+2(第一次扩容后的长度),长度为存入长度,容量为34
3.如果存入的长度>16
2+2(第一次扩容的长度) 长度为存入的长度,容量为存入的长度

Arrays类

compare方法将第一个数组和第二个数组按照字典序进行比较,大于返回1 小于返回-1 ,等于返回0

equals方法可以直接比较2个数组是否相等

二分查找的前提--有序

  • 二分查找(Arrays中有方法直接实现)

  • 实现参考

比较器

我们知道Arrays类中提供了sort类用于数组的排序,这个方法有很多重载,可以适用于多种类数据的排序,其中有一个下面的方法,可以实现对对象的排序
public static void sort(Object []a)

下面我们将验证这个方法的使用

package com.cn;

import java.util.Arrays;

class  Books{
    private  String name;
    private  float price;
    public Books(String name,float price){
        this.name=name;
        this.price=price;
    }
    public String toString(){
        return "图书名称:"+this.name+"图书价格:"+this.price+"\n";
    }
}

public class ArraysTest {
    public static void main(String[] args) {
        Books books[] = {
            new Books("java编程思想", 23.3f),
                    new Books("java宝典", 34.2f), new Books("java", 45.5f)};
        System.out.println(Arrays.toString(books));
           Arrays.sort(books);//ClassCastException


        }
    }


但是可以发现的是该程序会抛出异常, com.cn.Books cannot be cast to java.lang.Comparable

jdk文档中说,如果数组里面的元素没有实现Comparable,则sort就不能实现
如果数组里面是整数,要确定其之间的大小关系,直接使用各种关系运算符即可,可以此时的数组里面的内容是一个个对象,里面包含的是一个个堆内存信息这该如果进行比较

比较堆的对象关系,严格来说是比较堆里面的属性的大小关系,而属性内容的比较就必须使用比较器来支持

Comparable比较器

Comparable接口上追加泛型的主要因素在于,如果真的要进行一组对象的比较,那么这组对象应该拥有相同的类型

以上Book类中实现了Comparable接口,这样就可以直接在Book类中利用compareTo方法实现比较规则的定义,那么就可以通过Arrays.sort()方法实现对象数组的排序

comparaTo方法有三种返回结果:大于(1或比0大的数值)小于(-1比0小的数值)等于(0)

Comparator比较器

Comparable比较器是在类定义的时候就为类设计的额外功能,但有一些情况下一些类在设计的时候没有考虑到排序的需求, 这个使用可以使用我们的Comparator比较器可以实现我们挽救的比较需求


可以看到当使用Comparator比较器进行比较时,需要覆写Comparator的compare方法进行比较规则的编写,而然后再用Arrays.sort()方法进行具体调用时,调用的不是原来的方法,而是带有指定比较器的方法,但是其编写的规则和Comparable规则编写基本相同至少单独的一个类

一个在定义类的时候就带有比较功能,一个在类外格外定义一个比较器

因为该比较器在定义的时候可以使用的是函数式的编程所以也可以使用lambda表达式

该过程也可以使用lambda表达式进行实现,由于还没有学习,故没有实践,以后再说

Comparator除了基本的排序支持外,其内部还存在大量的数据排序的处理操作,例如reverse

  • 关于2种比较器的总结

我们让所以的对象都实现了比较器这个接口,我们将对象数组的地址传入的sort函数中,我们可以猜想到一定在sort方法的实现源码里面用到了这个接口对数组里面的对象进行比较,由于如果没有实现这个接口,而使用sort对对象数组进行比较,会报ClassException(数组里对象的类型--->Comparable),可以推断数组里面的对象一定向上转型为Comprable然后才依据比较规则进行操作的

标签:类库,java,String,System,API,println,public,out
From: https://www.cnblogs.com/swtaa/p/16933888.html

相关文章

  • Java同步器简述
    一、概述在并发编程领域,有两大核心问题:一个是互斥,即同一时刻只允许一个线程访问共享资源;另一个是同步,即线程之间如何通信、协作。主要原因是,对于多线程实现实现并发,一直以......
  • 微信小程序报错“getLocation:fail the api need to be declared in the requiredPriv
    解决微信小程序获取定位报错上个礼拜在调试一个微信小程序的时候,在手机允许小程序获取定位、定位授权成功的情况下,发现安卓手机能获取定位,但是苹果手机获取不到定位,我就开......
  • JAVA结业测试题及答案
    JAVA结业试题二1下列哪些方法互为重载方法,哪些方法是覆盖方法。答案用号码标识:4分publicclassA{  ①publicstaticvoidmethod(Stringstr){};  ②protected int......
  • JAVA面试题-CORE JAVA部分
    JAVA面试题-COREJAVA部分                   --感谢SD0501班毕业学员李晓宾的提供,希望对面试学员和在校学员有所帮助。1. 在main(String[]args)方法内......
  • 在Asp.Net WebApi中使用DotNetZip压缩响应流过滤器
    usingIonic.Zlib;usingSystem;usingSystem.IO;usingSystem.Linq;usingSystem.Net.Http;usingSystem.Threading;usingSystem.Threading.Tasks;usingSystem.......
  • JavaScript中this关键字使用方法详解
       在面向对象编程语言中,对于this关键字我们是非常熟悉的。比如C++、C#和Java等都提供了这个关键字,虽然在开始学习的时候觉得比较难,但只要理解了,用起来是非常方便和意义......
  • java编码工具
    3.编码工具​工欲善其事,必先利其器。目前编码工具琳琅满目,大家可根据自己需求自己选择工具。3.1文本编辑器收费UltraEditEditPlus免费​subli......
  • APAP:销售订单创建BAPI
    销售订单创建BAPI: SD_SALESDOCUMENT_CREATEDATA:lt_zsdt008LIKETABLEOFzsdt008,ls_zsdt008LIKELINEOFlt_zsdt008,ls_zsdt001TYPEzsdt001,......
  • Android Studio工程目录介绍,Ubuntu/Debian 系统切换Java(JVM),修改Java版本,JAVA_HOME
    AndroidStudio工程目录介绍 du1@dePc:~/asProjs/sms$./gradlewcleanERROR:JAVA_HOMEissettoaninvaliddirectory:/home/du1/aset/as/sdkPleasesettheJAVA......
  • ABAP:采购订单创建BAPI
    采购订单创建BAPI:BAPI_PO_CREATE1DATA:lt_zsdt008LIKETABLEOFzsdt008,ls_zsdt008LIKELINEOFlt_zsdt008,ls_zsdt001TYPEzsdt001,lt_z......