Java常用类—包装类,字符串类,日期类

Java常用类

包装类(JDK1.8.0)

内容

基本数据类型 包装类 所在类/位置 继承类/实现接口
byte Byte rt.jar—>java.lang rt.jar—>java.lang.Number
boolean Boolean rt.jar—>java.lang rt.jar—>java.lang.Comparable接口
short Short rt.jar—>java.lang rt.jar—>java.lang.Number
char Character rt.jar—>java.lang rt.jar—>java.lang.Comparable接口
int Integer rt.jar—>java.lang rt.jar—>java.lang.Number
long Long rt.jar—>java.lang rt.jar—>java.lang.Number
float Float rt.jar—>java.lang rt.jar—>java.lang.Number
double Double rt.jar—>java.lang rt.jar—>java.lang.Number

优点

  1. 某些方法的参数必须是对象,为了让基本数据类型的数据能作为参数,需要用到包装类
  2. 包装类还能提供更多功能
  3. 其他特别重要的功能:比如可以实现字符串与基本类型之间的转换
package com.wrapper;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class Demo01 {
    public static void main(String[] args) {
        //1.某些方法的参数必须是对象,为了让基本数据类型的数据能作为参数,需要用到包装类
        List list = new ArrayList();    //集合,ArrayList()长度可变
        list.add(new String("Eleike"));
        list.add(new Date());
        //自动装箱,JDK1.4之后对数值型数据自动包装
        list.add(82);   //存入的实际上是Integer的对象,写全是:list.add(new Integer(82));
        list.add(3.14159);  //等于list.add(new Double(3.14159));
        System.out.println(list);
        System.out.println("===============================================");
        //2.包装类还能提供更多功能
        System.out.println(Integer.MAX_VALUE);
        System.out.println(Integer.MIN_VALUE);
        System.out.println(Integer.SIZE);   //占位数
        System.out.println(Integer.TYPE);   //类型
        System.out.println("===============================================");
        //3.其他特别重要的功能:比如可以实现字符串与基本类型之间的转换
        int age = Integer.parseInt("66");   //将字符型66转换为整数型66
        int weight = Integer.parseInt("75");
        double height = Double.parseDouble("1.81");
        System.out.println("age:"+age+"\nweiget:"+weight+"\nheight:"+height);
    }
}

自动装箱和自动拆箱

自动装箱使用的代码是Integer.valueOf(15)方法而非new Integer(15)

自动拆箱使用的代码是in1.intValue()方法

package com.wrapper;

public class Demo02 {
    public static void main(String[] args) {
        //自动装箱
        Integer in1 = 15;
        //自动装箱并非执行new Integer(15),而是执行Integer中的Integer.valueof(15)方法
        Integer in2 = new Integer(15);  //错误,只有在手动装箱中见到
        Integer in3 = Integer.valueOf(15);  //自动装箱执行的隐藏代码

        //自动拆箱
        //拆箱是把Integer中int类型的成员常量value赋值给了i1
        int i1 = in1;
        int i2 = in1.intValue();    //自动拆箱执行的隐藏代码


        System.out.println(i1 == in1);  //true,int类型和Integer对象用`==`比较时Integer对象会自动拆箱,所以实际上比较的是Integer对象中int类型的成员常量value
        System.out.println(in1.equals(i1)); //实际内容相同,所以true
        System.out.println(i1 == i2);   //true

        System.out.println(in1 == in2); //false,创建的对象地址不同
        System.out.println(in1 == in3); //true
    }
}

IntegerCache(Integer静态内部类)

  • java为了提升效率,java在IntegerCache中定义了一个静态数组Cache,长度为256,范围是-128127,包含了从-128127的Integer对象

  • 自动装箱执行的是Integer.valueOf();方法,此方法中如果传入的数值在-128~127之间,将会直接返回Cache数组中的指定值,否则通过new Integer()重新创建对象

所以当两个对象自动装箱的数据在-128~127之间且相等时,两个对象用==比较返回为true

package com.wrapper;

public class Demo03 {
    public static void main(String[] args) {
        //在范围内
        Integer in1 = -128;
        Integer in2 = -128;
        Integer in3 = 127;
        Integer in4 = 127;
        //在范围外
        Integer in5 = 128;
        Integer in6 = 128;
        //结果
        System.out.println(in1 == in2); //true
        System.out.println(in1.equals(in2));    //true
        System.out.println(in3 == in4); //true
        System.out.println(in3.equals(in4));    //true
        System.out.println(in5 == in6); //false
        System.out.println(in5.equals(in6));    //true
    }
}

字符串相关类

内容

  1. String类:不可变字符序列
  2. StringBuilder类:可变字符序列
  3. StringBuffer类:可变字符序列,现在几乎被StringBuilder类代替

String类

rt.jar—>java.lang包下

  • 不可变字符串,类中所有方法都不会影响改变其内容
  • JDK9之前String类底层是char数组,JDK9时由char数组改变为byte数组,节省空间,同时通过一个coder成员变量作为编码格式的标志(判断是否有输入中文),以此来确定是使用LATIN1还是使用UTF-16,这实在String生成时自动确定的(StringBuilder和StringBuffer也是如此)

构造方法

public String();	//空构造

public String(byte[] bytes);	//把字节数组转成字符串

public String(byte[] bytes,int index,int length);	//把字节数组的一部分转成字符串

public String(char[] value);	//把字符数组转成字符串

public String(char[] value,int index,int count);	//把字符数组的一部分转成字符串

public String(String original);		//把字符串常量值转成字符串

常用方法

package com.wrapper;

import java.util.Arrays;

//String是不可变字符串,类中所有方法都不会影响改变其内容
public class Demo04 {
    public static void main(String[] args) {
        //如何创建String对象
        String str1 = "赞美太阳zmtyZMTYzmty";   //String str = " ";
        //str1 = "新字符串";  并非修改原字符串,而是让str1指向内容为"新字符串"的字符串

        //如何使用String对象
        //1.常用方法
        System.out.println(str1.length());  //只算字符个数,不是字节的个数,所以结果为16
        System.out.println(str1.isEmpty()); //判断是否为空
        //System.out.println(str1.isBlank()); JDK11新增方法,判断空格也认为是空
        System.out.println(str1.startsWith("赞美太阳"));    //判断是否以"赞美太阳"开头
        System.out.println(str1.endsWith("zmty"));  //判断是否以"zmty"结尾
        System.out.println(str1.toLowerCase()); //字母全小写 会产生一个新字符串
        System.out.println(str1.toUpperCase()); //字母全大写 会产生一个新字符串
        byte[] arr = str1.getBytes();	//返回当前字符串的字节数组,io流中会用到
        System.out.println(str1);   //内容不影响
        System.out.println("========================================");
        //2.通过索引找子串
        System.out.println(str1.charAt(4)); //索引为4的字符
        System.out.println(str1.substring(8));  //从索引8到最后
        System.out.println(str1.substring(8,11));   //索引8到11(8<=x<11),不包含11
        System.out.println("========================================");
        //3.通过子串找索引
        System.out.println(str1.indexOf("zm")); //从前向后,第一次zm的索引
        System.out.println(str1.indexOf("zm",10));  //从前向后,索引10开始第一次zm的索引
        System.out.println(str1.lastIndexOf("zm")); //从后往前
        System.out.println("========================================");
        //4.分割字符串
        String str2 = "Dark,souls,3,Dack,Dark,Daok";
        String[] arr1 = str2.split(",");    //以","分割字符串,返回String数组
        System.out.println(Arrays.toString(arr1));
        System.out.println("========================================");

        char[] arr2 = str2.toCharArray();   //分割成字符数组
        System.out.println(arr2);
        System.out.println(Arrays.toString(arr2));
        System.out.println("========================================");
        //5.子串替换
        String out = str2.replace("Dark","黑暗");    //把Dark替换成 黑暗
        System.out.println(out);
        System.out.println("========================================");

        out = str2.replaceAll("Da[\\w]k","魂");  //把制定内容替换成 魂 ,支持正则表达式
        System.out.println(out);
        System.out.println("========================================");
        //6.去除两端空格
        String str3 = "   去除两端   空格   ";
        out = str3.trim();  //去除字符串中两端的空格
        System.out.println(out);
        System.out.println("========================================");
        //7.末尾增加子串
        String str4 = "少年不识愁滋味";
        str4 = str4.concat(",爱上层楼,");   //字符串末尾增加子串
        str4 = str4.concat("爱上层楼,");
        str4 = str4.concat("为赋新词强说愁。");
        //也可连用
        str4 = str4.concat("如今识尽愁滋味,").concat("欲说还休,").concat("欲说还休,").concat("却道天凉好个秋。");
        System.out.println(str4);
        System.out.println("========================================");
        //8.判断是否包含子串
        String str5 = "如果你是龙也好...";
        System.out.println(str5.contains("也好"));    //判断是否包含子串 true
        System.out.println(str5.contains("你好"));    //false
        //9.获取字节/字符数组
        String str6 = "abcdefgh";
        str6.getBytes();	//转换成字节数组
        str6.toCharArray();	//转换成字符数组
    }
}

equals()

String中的equals()是先比较地址是否相同,地址相同则内容相同然后返回结果,地址不同再继续比较内容,之后返回结果

String直接赋值创建是让变量名指向常量池中对应字符串地址

String用new对象来创建是让变量名指向堆内存中实例对象的地址,然后对象中的成员常量value指向常量池中对应字符串地址

package com.string;

public class Demo02 {
    public static void main(String[] args) {
        //new是在堆内存中创建实例对象,变量名str1和str2分别指向堆内存中自己的实例对象地址
        String str1 = new String("zmty");
        String str2 = new String("zmty");
        System.out.println(str1 == str2);   //false
        System.out.println(str1.equals(str2));  //true
        System.out.println("==========================");

        //直接赋值是让变量名直接指向常量池(堆内存)中对应字符串的地址,内容相同则指向同一个字符串常量
        String str3 = "zmty";
        String str4 = "zmty";
        System.out.println(str3 == str4);   //true,指向地址相同
        System.out.println(str3.equals(str4));  //true
        System.out.println("==========================");

        String str5 = new String("hello world");    //str5指向实例对象地址
        String str6 = "hello world";    //str6指向常量池中"hello world"的地址
        System.out.println(str5 == str6);   //false
        System.out.println(str5.equals(str6));  //true
        System.out.println("==========================");

        //null和空字符串的区别
        String str7 = null; //null是不指向任何地址
        String str8 = new String(" ");  //指向实例对象地址
        String str9 = " ";  //指向常量池中空字符串地址
        System.out.println(str7 == str8);   //false
        System.out.println(str7 == str9);   //false
        System.out.println(str8.length());  //1
        try {
            System.out.println(str7.length());  //null不指向任何地址所以直接输出其长度会出错
        } catch (Exception e) {
            System.out.println("异常:"+e);    //java.lang.NullPointerException异常
        }

    }
}

compareTo()

package com.string;

public class Demo03 {
    public static void main(String[] args) {
        //comparTo()按照两个字符串中长度短的字符串长度比较,比较两个字符串中第一个不同的字符的ASCII码值大小,返回差值,相同则返回字符串长度差值
        String str1 = "abczmty";
        String str2 = "adczmty";
        System.out.println(str2.compareTo(str1));   //2,第2个字符不同,d比b大2

        //长度不同,只比较前3个
        String str3 = "abc";
        String str4 = "abcdef";
        //前3个内容相同,则返回长度差值
        System.out.println(str4.compareTo(str3));   //3,str4长度比str3长度长3

        //长度不同,比较前6个
        String str5 = "abcdef";
        String str6 = "accdefghi";
        //前6个内容中第二个字符不同
        System.out.println(str6.compareTo(str5));   //1,c比b大1
    }
}

其他方法自行jdk文档

StringBuilder

创建时底层字符串长度16+输入的字符数,扩充时是长度*2+2

  • StringBuilder是可变字符序列,所有的操作都是改变其自身内容
  • 扩充字符串时,字符数量超过初始定义上限会自动扩增(创建新字符数组的方式)
  • 扩充时对象名的指向不改变,仍然指向同一个实例对象,实例对象底层中的char数组value的指向发生变化,指向一个新的字符数组

常用方法

package com.string;
//StringBuilder是可变字符序列,其内所有方法都会影响它且只影响自身的内容,除扩充外都不会产生新的字符串
public class Demo04 {
    public static void main(String[] args) {
        //1.创建StringBuilder,只能new创建对象
        StringBuilder stb1 = new StringBuilder("如果");
        //StringBuilder sb1 = new StringBuilder(12);	是底层创建的char数组长度为12
        //StringBuilder st2 = "如果"; 错误,无法执行
        //2.末尾添加
        //添加后若超过定义的空间长度则会自动扩充,创建一个新的字符数组并让其长度等于现长度(添加了内容后的长度)*2+2
        stb1.append("你是龙");
        System.out.println(stb1.length() + "\t" + stb1.capacity()); //capacity()方法是输出字符串所创建的空间长度(底层字符数组长度)
        stb1.append(",也好");
        System.out.println(stb1.length() + "\t" + stb1.capacity());
        System.out.println(stb1.toString());
        System.out.println("===========================");
        //3.中间添加
        stb1.insert(stb1.indexOf("也好"),"灰心哥说:");    //第一个填索引,第二个要添加的内容
        System.out.println(stb1);
        System.out.println("===========================");
        //4.替换
        //stb1.replace(要替换的索引开始,要替换的索引结束(不包含),要替换的内容);
        stb1.replace(stb1.indexOf("也好"),stb1.indexOf("也好")+2,"吾誓杀汝");
        System.out.println(stb1);
        System.out.println("===========================");
        //5.删除
        stb1.delete(6,11);
        System.out.println(stb1);
        System.out.println("===========================");
        //6.输出
        System.out.println(stb1.toString());    //正常输出
        System.out.println(stb1.reverse()); //顺序颠倒

    }
}

StringBuilder和StringBuffer的区别

StringBuffer带锁,安全性强,效率低,方法带关键字synchronized,给方法上锁

StringBuilder不带锁,安全性低,但是效率较高

方法用法通用

日期类

结构

  • java.util.Date(YYYY-MM-DD hh:mm:ss)
    • java.sql.Date(YYYY-MM-DD)只有年月日
    • java.sql.Time(hh:mm:ss)只有时分秒
    • java.sql.Timestamp(时间戳)
  • java.util.Calendar (继承自java.util.Timezone)
    • java.util.GergorianCalendar
  • java.text.DateFormat (日期和字符串转换相关),抽象类
    • java.text.SimpleDateFormat
  • java.time(JDK8新引入的)
    • Instant 代表的是时间,相当于Date
    • LocalDate 代表日期,如2021-12-09
    • LocalTime 代表时刻,如12:30:59
    • ZonedDateTime 代表一个包含时区的完整的日期和时间,偏移量是以UTC/ 格林威治时间为基准
    • Period 代表时间段
    • ZoneOffset 代表时间偏移量,如:+8:00
    • Clock 代表时钟,如获得目前美国纽约时间
    • DateTimeFormatter 日期和字符串格式转换

Date类

java.util.Date

package com.date;

import java.util.Date;

public class Demo01 {
    public static void main(String[] args) {
        //System.currentTimeMillis()获取从1970年1月1日00:00:00到当前时间的毫秒数
        System.out.println(System.currentTimeMillis());
        //1.获取当前时间
        Date date1 = new Date(); //底层调用System.currentTimeMillis()获取了当前日期
        Date date2 = new Date(System.currentTimeMillis());  //与上一行一样
        //也可输入毫秒数来定义时间,得到的是1970年1月1日0点加上传入的毫秒数的时间,负数往前算
        Date date3 = new Date(1000L*60*60*24*31);   //得到1970年2月1日0点时间(中央时区时间),输入数据建议long类型,避免长度不够

        System.out.println("中央时区格式:"+date1);  //中央时区格式,英国伦敦
        System.out.println("本地时间格式:"+date1.toLocaleString()); //本地时间格式,东八区
        System.out.println("date3:"+date3.toLocaleString());
        System.out.println("===============================");
        //2.操作当前时间
        //当前时间:2021/12/8 17:46:54 周三
        System.out.println(date1.getYear());    //121   结果+1900等于当前年份
        System.out.println(date1.getMonth());   //11    0~11, 11为12月
        System.out.println(date1.getDate());    //8     8号
        System.out.println(date1.getDay());     //3     星期几,星期天为0,0~6
        System.out.println(date1.getHours());   //17    小时
        System.out.println(date1.getMinutes()); //46    分钟
        System.out.println(date1.getSeconds()); //54    秒
        System.out.println(date1.getTime());    //1638956814911 毫秒数
    }
}

java.sql.Date

package com.date;

import java.sql.Date;

public class Demo02 {
    public static void main(String[] args) {
        //java.sql.Date()没有无参构造,只能用System.currentTimeMillis()来获取当前时间
        java.sql.Date sdate1 = new java.sql.Date(System.currentTimeMillis());
        System.out.println(sdate1); //2021-12-08    只有年月日
        //创建指定日期时间对象
        java.sql.Date sdate2 = java.sql.Date.valueOf("1999-03-06");
        System.out.println(sdate2);
    }
}

java.sql.Time和java.sql.Timestamp用法与上面java.sql.Date类似

DataFormat类

SimpleDateFormat()是DateFormat()抽象类的唯一实现类

DateFormat类的主要作用就是将日期字符串和日期对象的相互转换

日期和时间格式化编码:

字母 描述 示例
G 纪元标记 AD
y 四位年份 2001
M 月份 July or 07
d 一个月的日期 10
h A.M./P.M. (1~12)格式小时 12
H 一天中的小时 (0~23) 22
m 分钟数 30
s 秒数 55
S 毫秒数 234
E 星期几 Tuesday
D 一年中的日子 360
F 一个月中第几周的周几 2 (second Wed. in July)
w 一年中第几周 40
W 一个月中第几周 1
a A.M./P.M. 标记 PM
k 一天中的小时(1~24) 24
K A.M./P.M. (0~11)格式小时 10
z 时区 Eastern Standard Time
' 文字定界符 Delimiter
" 单引号 `

实例:

package com.date;

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

public class Demo03 {
    public static void main(String[] args) throws ParseException {	//方法抛出异常
        //接收到的日期字符串
        String str1 = "2021-12-08 21:24:30";

        //把String转成Date对象
        DateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  //输入想要的日期格式
        Date date1 = sdf1.parse(str1);   //会抛出异常
        System.out.println(date1.toLocaleString());
        System.out.println("===========================");

        //把Date对象转成String
        DateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        String str2 = sdf2.format(date1);
        System.out.println(str2);
    }
}

printf格式化日期常用操作

转 换 符 说 明 示 例
c 包括全部日期和时间信息 星期六 十月 27 14:21:20 CST 2007
F "年-月-日"格式 2007-10-27
D "月/日/年"格式 10/27/07
r "HH:MM:SS PM"格式(12时制) 02:25:51 下午
T "HH:MM:SS"格式(24时制) 14:28:16
R "HH:MM"格式(24时制) 14:28

实例:

package com.date;

import java.util.Date;

public class Demo06 {
    public static void main(String[] args) {
        Date date1 = new Date();
        System.out.printf("%tc\n",date1);   //星期三 十二月 08 22:12:29 CST 2021
        System.out.printf("%tF\n",date1);   //2021-12-08
        System.out.printf("%tD\n",date1);   //12/08/21
        System.out.printf("%tr\n",date1);   //10:12:29 下午
        System.out.printf("%tT\n",date1);   //22:12:29
        System.out.printf("%tR\n",date1);   //22:12
    }
}

其他用法查看二级标题printf输出格式

Calendar类

抽象类,创建时实例其实现类GregorianCalendar,或者使用Calendar内静态方法Calendar.getInstance()(推荐)

常用

package com.date;

import java.util.Calendar;
import java.util.GregorianCalendar;

public class Demo07 {
    public static void main(String[] args) {
        //Calendar类,抽象类,new它的实现类
        Calendar cal1 = new GregorianCalendar();    //获取当前时间
        Calendar cal2 = Calendar.getInstance();     //获取当前时间,更建议使用这种方法
        //设置当前时间
        cal2.set(Calendar.DATE,10); //设置成10号
        cal2.set(Calendar.YEAR,2022);   //设置成2022年
        cal2.add(Calendar.DATE,1);  //加一天,变成11号
        //输出当前时间
        System.out.println(cal2);
        System.out.println(cal2.get(Calendar.YEAR));
        System.out.println(cal2.get(Calendar.MONTH)+1);   //0~11
        System.out.println(cal2.get(Calendar.DATE));
        System.out.println(cal2.get(Calendar.WEEK_OF_MONTH));
        System.out.println(cal2.get(Calendar.DAY_OF_MONTH));
        System.out.println(cal2.get(Calendar.DAY_OF_WEEK)); //从周天开始算,周天第一天
        System.out.println(cal2.getMaximum(Calendar.DATE)); //当前月最大天数
    }
}

实例之日历打印

package com.date;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;

public class PrintCalendar {
    public static void main(String[] args) throws ParseException {
        String str1;
        while (true){
            System.out.println("请输入时间(yyyy-MM-dd):");
            Scanner scanner = new Scanner(System.in);
           try {
                str1 =scanner.nextLine();   //监视输入异常
            } catch (Exception e) {
                System.out.println("输入异常,请重新输入:");  //出现异常跳过此次循环,重新输入
                continue;
            }
           scanner.close();
           break;
        }

        DateFormat sdf1 =new SimpleDateFormat("yyyy-MM-dd");
        Date date1 = sdf1.parse(str1);  //按指定格式将日期字符串转换为Date对象
        Calendar cld1 = Calendar.getInstance(); //创建一个Calendar对象
        cld1.setTime(date1);    //设置为输入的时间
        int today = cld1.get(Calendar.DATE);    //记录下日期中的day
        cld1.set(Calendar.DATE,1);  //将时间设为那月的1号
        System.out.println("日\t一\t二\t三\t四\t五\t六");  //打印日历头
        for (int i = 1; i < cld1.get(Calendar.DAY_OF_WEEK); i++) {  //根据1号是周几来确定第一行空格长度
            System.out.printf("\t");
        }
        //循环打印日历内容
        //System.out.format("\33[35;1m这是紫红色");此方法打印时可以设置字体属性
        //设置颜色格式:"\33[前景色编号(选填);背景色编号(选填);2m(必填,1m加粗,2m正常,3m斜体,4m下划线)要输出的内容"
        while (true){
            if (cld1.get(Calendar.DAY_OF_WEEK) == 7){   //判定是否是一周中的最后一天
                if (cld1.get(Calendar.DATE) == today){  //判定是否是用户输入的那天
                    if (cld1.get(Calendar.DATE) == cld1.getMaximum(Calendar.DATE)){ //判断是否是本月最后一天
                        //System.out.println(cld1.get(Calendar.DATE));
                        System.out.format("\33[35;1m%d\n",cld1.get(Calendar.DATE)); //指定日期红色标注
                        break;  //最后一天则跳出
                    }else{
                        //System.out.println(cld1.get(Calendar.DATE));
                        System.out.format("\33[35;1m%d\n",cld1.get(Calendar.DATE)); //不是最后一天正常打印加回车
                    }
                } else {    //不是用户输入的那天
                    if (cld1.get(Calendar.DATE) == cld1.getMaximum(Calendar.DATE)){ //判断是否是本月最后一天
                        //System.out.println(cld1.get(Calendar.DATE));
                        System.out.format("\33[0;2m%d\n",cld1.get(Calendar.DATE));
                        break;
                    }else{
                        //System.out.println(cld1.get(Calendar.DATE));
                        System.out.format("\33[0;2m%d\n",cld1.get(Calendar.DATE));
                    }
                }
            }else { //不是本周最后一天
                if (cld1.get(Calendar.DATE) == today){  //判断是否是用户输入的那天
                    if (cld1.get(Calendar.DATE) == cld1.getMaximum(Calendar.DATE)){ //是否是本月最后一天
                        //System.out.print(cld1.get(Calendar.DATE));
                        System.out.format("\33[35;1m%d\t",cld1.get(Calendar.DATE));
                        break;
                    }else{
                        //System.out.print(cld1.get(Calendar.DATE) + "\t");
                        System.out.format("\33[35;1m%d\t",cld1.get(Calendar.DATE));
                    }
                }else{
                    if (cld1.get(Calendar.DATE) == cld1.getMaximum(Calendar.DATE)){
                        //System.out.print(cld1.get(Calendar.DATE));
                        System.out.format("\33[0;2m%d\t",cld1.get(Calendar.DATE));
                        break;
                    }else{
                        //System.out.print(cld1.get(Calendar.DATE) + "\t");
                        System.out.format("\33[0;2m%d\t",cld1.get(Calendar.DATE));
                    }
                }
            }
            cld1.add(Calendar.DATE,1);  //日期加1天
        }
    }
}

结果:

日历打印结果

  • System.out.format("\33[35;1m这是紫红色");此方法打印时可以设置字体属性
  • 设置颜色格式:"\33[前景色编号(选填);背景色编号(选填);2m(必填,1m加粗,2m正常,3m斜体,4m下划线)要输出的内容"

Date和Calendar的相互转化

  1. Calendar转化为Date:
  • Calendar cal=Calendar.getInstance();
    Date() date=cal.getTime();
    
  1. Date转化为Calendar:
  • Date date=new Date();
    Calendar cal=Calendar.getInstance();
    cal.setTime(date);
    

java.time下类

JDK8增加

package com.date;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class Demo08 {
    public static void main(String[] args) {
        //LocalDateTime/LocalDate/LocalTime获取当前时间
        LocalDateTime ldt1 = LocalDateTime.now();
        System.out.println(ldt1);
        LocalDate ld1 = LocalDate.now();
        System.out.println(ld1);
        LocalTime lt1 = LocalTime.now();
        System.out.println(lt1);
        System.out.println("=====================================");
        //也可像Calendar类一样获取相关的时间信息
        System.out.println(ldt1.getMonth());    //获得月份  结果是月份英文
        System.out.println(ldt1.getYear());     //获取年份
        System.out.println(ldt1.getDayOfMonth());
        System.out.println(ldt1.getDayOfWeek());    //周几的英文,底层还是判断一周的第几天,1~7(周天是第一天),然后区数组中按照数字-1的索引去除对应的英文
        System.out.println("=====================================");
        //指定当前时间
        //按照参数输入日期时间内容
        LocalDateTime ldt2 = LocalDateTime.of(2021,10,21,12,12,12);
        System.out.println(ldt2);
        LocalDate ld2 = LocalDate.of(2021,12,9);
        System.out.println(ld2);
        LocalTime lt2 = LocalTime.of(22,46,58);
        System.out.println(lt2);
        System.out.println("=======================================");

        //时间字符串和时间日期对象间的转换
        String str1 = "2021年12月09日 22时50分56秒";
        //DateTimeFormatter创建一种时间格式
        DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");    //代表时间的HH一定要大写
        LocalDateTime ldt3 = LocalDateTime.parse(str1,dtf1);    //按照dtf1的格式把str1转换为日期对象
        System.out.println(ldt3);
        String str2 = ldt3.format(dtf1);    //按照dtf1的格式把ldt3的内容转换为String字符串
        System.out.println(str2);
        //!!!DateFormatter与TimeFormatter使用方法与上面相同!!!
    }
}

其他类及使用方法自行百度

Date与LocalDateTime的相互转换

  1. 将Date转换为LocalDateTime

    • Date date = new Date(); 
      Instant instant = date.toInstant(); 
      ZoneId zoneId = ZoneId.systemDefault(); 
      LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); 
      
  2. 将LocalDateTime转换为Date

    • ZoneId zoneId = ZoneId.systemDefault(); 
      LocalDateTime localDateTime = LocalDateTime.now(); 
      ZonedDateTime zdt = localDateTime.atZone(zoneId);
      Date date = Date.from(zdt.toInstant()); 
      

Math类

常用方法:

package com.math;

//Math类常用方法
public class Demo01 {
    public static void main(String[] args) {
        System.out.println(Math.PI);    //输出π
        System.out.println(Math.random());  //取随机数,底层是创建一个random对象
        System.out.println(Math.abs(-99));  //绝对值 99    absolute

        System.out.println(Math.floor(5.5));    //5.0   向下取整
        System.out.println(Math.ceil(5.5));     //6.0   向上取整
        System.out.println(Math.round(5.5));    //6     四舍五入

        System.out.println(Math.pow(5,2));  //25.0  幂函数,5^2
        System.out.println(Math.pow(2,5));  //32.0  2^5

        System.out.println(Math.signum(-50));   //-1.0  负数返回-1.0
        System.out.println(Math.signum(50));    //1.0   正数返回1.0
        System.out.println(Math.signum(0));     //0.0     o返回0.0

        System.out.println(Math.sqrt(25));  //5.0   开平方
    }
}

Random(随机数类)

可以指定随机数类型和范围,还可以传入种子

避免重复可以传入时间戳

常用方法:

package com.math;

import java.util.Random;

//随机数
public class DemoRandom {
    public static void main(String[] args) {
        //1.无参构造创建random对象
        Random rand1 = new Random();    //无参构造,底层世界获取时间等一些信息确保随机性
        System.out.println(rand1.nextDouble()); //随机一个double数,范围[0.0,1.0)
        System.out.println(rand1.nextInt());
        for (int i = 0; i < 10; i++) {
            System.out.println(rand1.nextInt(20));     //范围[0,20)
        }
        System.out.println("======================================");
        //2.有参构造创建random对象
        //2.1传入固定参数
        Random rand2 = new Random(10);  //输入固定参数后每次给出的随机数会相同,因为种子固定
        System.out.println(rand2.nextDouble()); //范围[0.0,1.0)
        System.out.println(rand2.nextInt());
        for (int i = 0; i < 10; i++) {
            System.out.println(rand2.nextInt(20));     //范围[0,20)
        }
        System.out.println("======================================");
        //2.2传入可变参数,如时间
        Random rand3 = new Random(System.currentTimeMillis());
        System.out.println(rand3.nextDouble()); //范围[0.0,1.0)
        System.out.println(rand3.nextInt());
        for (int i = 0; i < 10; i++) {
            System.out.println(rand3.nextInt(20));     //范围[0,20)
        }
    }
}

枚举类

基础

关键词enum,取代class修饰类

当你要定义多个常量时适合用枚举类

创建方式:

  • enum修饰类名
  • 在类中写入要定义的常量,无需任何修饰,多个用逗号隔开

使用:

  • 只需在要限制的常量定义为对应类名的自建枚举类型即可
  • 这样在传值的时候只能传在枚举类中定义的常量

实例—性别限制

package com.enumdemo.demogender;

//启动类
public class Application {
    public static void main(String[] args) {
        Person01 p1 = new Person01("Eileke",Gender.男);  //输入性别时只能选择枚举类Gender中存在的
        System.out.println(p1);
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
package com.enumdemo.demogender;

//枚举类,关键词enum,只需要在内写入要定义的常量即可,多个用逗号隔开
public enum Gender {
    男,女
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
package com.enumdemo.demogender;

public class Person01 {
    private String name;
    private Gender sex;	//定义为枚举类进行限制

    public Person01() {
    }

    public Person01(String name, Gender sex) {
        this.name = name;
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Gender getSex() {
        return sex;
    }

    public void setSex(Gender sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Person01{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                '}';
    }
}

实例—季节限制

package com.enumdemo.demoswitch;

//主类
public class Application01 {
    public static void main(String[] args) {
        SeasonHappy sh1 = new SeasonHappy("Eleike",Season.春);
        sh1.play();
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
package com.enumdemo.demoswitch;

//枚举类
public enum Season {
    春,夏,秋,冬
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
package com.enumdemo.demoswitch;

public class SeasonHappy {
    private String name;
    private Season season;	//设置为枚举类

    public SeasonHappy() {
    }

    public SeasonHappy(String name, Season season) {
        this.name = name;
        this.season = season;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Season getSeason() {
        return season;
    }

    public void setSeason(Season season) {
        this.season = season;
    }

    public void play(){
        switch (season){	//switch中只需要传入枚举变量
            case 春:	//case后填对应枚举中定义的常量
                System.out.println(this.name + "在" + this.season + "天喜欢踏青,放风筝");
                break;
            case 夏:
                System.out.println(this.name + "在" + this.season + "天喜欢踢足球,健身");
                break;
            case 秋:
                System.out.println(this.name + "在" + this.season + "天喜欢学习,睡觉");
                break;
            case 冬:
                System.out.println(this.name + "在" + this.season + "天喜欢睡觉,吃饭...逐渐堕落");
                break;
        }
    }

    @Override
    public String toString() {
        return "SeasonHappy{" +
                "name='" + name + '\'' +
                ", season=" + season +
                '}';
    }
}

Timer类

理论基础

小顶堆:用数组存储完全二叉树

  • 插入元素,插入到尾部然后上浮

  • 删除元素,永远取根节点,然后将尾部元素(最大的元素)放到堆顶,然后下沉

时间轮算法:

  1. 链表和数组实现时间轮
    • 遍历数组,每个数组元素存储一个链表,链表节点放置任务,遍历到了就取出执行
  2. round型时间轮
    • 存储方式同上,每个数组元素上记录一个round,遍历到了就round减1,为0时取出执行
    • 缺点:需要遍历所有任务,效率低
  3. 分层时间轮
    • 存储方式同上,但使用多个不同时间维度的轮
      • 天轮:记录几点执行
      • 月轮:记录几号执行
    • 月轮遍历到了,就将任务取出放在天轮里,即可实现几号几点执行

运行方式

Timer底层两个成员变量:

  • queue 源码private final TaskQueue queue = new TaskQueue();

    • 内部类TaskQueue
    • 任务队列,存放我们添加的任务,实际上就是小顶堆
  • thread 源码private final TimerThread thread = new TimerThread(queue);

    • 内部类TimerThread,死循环不断检查是否有任务要执行,有就执行它
    • 实际执行任务的线程

Timer对于任务的开始时间的两个参数(参数名不一定完全一致)

  • 预设的执行时间nextExecutTime
  • 按照我们传入的开始时间和延迟时间,程序会预先算出每次任务的理论开始时间
  • 比如我12:00:00开始执行,每次任务延迟2秒,那么理论上每次任务的执行时间是12:00:00 12:00:02 12:00:04...
  • 真正的执行时间ExecutTime,取决于上一个任务的结束时间
    • 实际程序运行后任务执行时的时间是按照上一次任务的结束时间加上延迟时间来确定的
    • 比如虽然我从12:00:00开始每次任务延迟2秒,但如果第一次任务用了3秒完成,那实际上第一次任务的结束时间是12:00:03 那么第二次的开始时间实际上是12:00:05,之后以此类推

Timer对于添加任务的两种方法

  • schedule(),按照真正的执行时间ExecutTime来确定每次任务的开始时间,但由于执行任务往往会消耗一定时间,所以
    该方法很容易引起任务推迟,造成任务丢失(即相同时间内任务执行次数减少)
  • scheduleAtFixedRate(),会严格按照预设的执行时间nextExecutTime来执行任务,会造成任务时间混乱

造成以上问题的原因是Timer执行任务的方式是单线程,很容易造成任务堵塞,任务超时

所以Timer对任务调度是基于绝对时间的,对系统时间敏感

实例(任务丢失)

package com.timer;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
//研究Timer类的问题/不足
public class TimerDemo01 {
    public static void main(String[] args) {
        Timer t = new Timer();  //从创建Timer对象开始,Timer线程就启动了
        for (int i = 0; i < 2; i++) {
            TimerTask task = new FooTimerTask("foo" + i);
            //执行我们FooTimerTask中run方法内容,从当前时间开始执行,每次任务间隔两秒
            t.schedule(task,new Date(),2000);   //只添加任务而非启动Timer线程
            //t.scheduleAtFixedRate(task,new Date(),2000);
            /*
            Timer对于任务的开始时间的两个参数(参数名不一定完全一致)
            1.预设的执行时间nextExecutTime
              按照我们传入的开始时间和延迟时间,程序会预先算出每次任务的理论开始时间
              比如我12:00:00开始执行,每次任务延迟2秒,
              那么理论上每次任务的执行时间是12:00:00  12:00:02    12:00:04...
            
            2.真正的执行时间ExecutTime,取决于上一个任务的结束时间
              实际程序运行后任务执行时的时间是按照上一次任务的结束时间加上延迟时间来确定的
              比如虽然我从12:00:00开始每次任务延迟2秒,但如果第一次任务用了3秒完成,
              那实际上第一次任务的结束时间是12:00:03
              那么第二次的开始时间实际上是12:00:05,之后以此类推
            ///////////////////////////////////////////////////////////////////////////////////////////////////////
            Timer对于添加任务的两种方法
            1.schedule(),按照真正的执行时间ExecutTime来确定每次任务的开始时间,但由于执行任务往往会消耗一定时间,所以
              该方法很容易引起任务推迟,造成任务丢失(即相同时间内任务执行次数减少)
            2.scheduleAtFixedRate(),会严格按照预设的执行时间nextExecutTime来执行任务,会造成任务时间混乱
            ///////////////////////////////////////////////////////////////////////////////////////////////////////
            造成以上问题的原因是Timer执行任务的方式是单线程,很容易造成任务堵塞,任务超时
             */
        }
    }
}

class FooTimerTask extends TimerTask {
    private String name;

    public FooTimerTask(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        try {
            System.out.println("name=" + name + ",StartDate=" + new Date());    //打印任务开始时间
            Thread.sleep(3000);     //延迟3秒
            System.out.println("name=" + name + ",EndDate=" + new Date());  //打印任务结束时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ScheduledExecutorService类(Timer优化版)

任务队列改为多线程,要设置线程池数量

使用方法:

package com.scheduled_executor;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

//Timer类的优化版ScheduledExecutorService类使用方法
public class ScheduledExecutorDemo01 {
    public static void main(String[] args) {
        //设置线程池数量
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 5; i++) {
            //传入一个实际任务类,第一次的延迟时间,每次任务的间隔时间,时间单位
            ses.scheduleAtFixedRate(new Task("Task-" + i),0,2, TimeUnit.SECONDS);
        }
    }
}

class Task implements Runnable{

    private String name;

    public Task(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        try {
            System.out.println(name + ",StartDate = " + new Date());
            Thread.sleep(5000);
            System.out.println(name + ",EndDate = " + new Date());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

printf输出格式(C语言知识点)

printf第一个参数必须是String

摘自菜鸟教程

import java.util.Date;
 
/**
 * 使用printf输出
 */
/**关键技术点
 * 使用java.io.PrintStream的printf方法实现C风格的输出
 * printf 方法的第一个参数为输出的格式,第二个参数是可变长的,表示待输出的数据对象
 */
public class Printf {
 
       public static void main(String[] args) {
              /*** 输出字符串 ***/
              // %s表示输出字符串,也就是将后面的字符串替换模式中的%s
              System.out.printf("%s", new Integer(1212));
              // %n表示换行
              System.out.printf("%s%n", "end line");
              // 还可以支持多个参数
              System.out.printf("%s = %s%n", "Name", "Zhangsan");
              // %S将字符串以大写形式输出
              System.out.printf("%S = %s%n", "Name", "Zhangsan");
              // 支持多个参数时,可以在%s之间插入变量编号,1$表示第一个字符串,3$表示第3个字符串
              System.out.printf("%1$s = %3$s %2$s%n", "Name", "san", "Zhang");
             
              /*** 输出boolean类型 ***/
              System.out.printf("true = %b; false = ", true);
              System.out.printf("%b%n", false);
 
              /*** 输出整数类型***/
              Integer iObj = 342;
              // %d表示将整数格式化为10进制整数
              System.out.printf("%d; %d; %d%n", -500, 2343L, iObj);
              // %o表示将整数格式化为8进制整数
              System.out.printf("%o; %o; %o%n", -500, 2343L, iObj);
              // %x表示将整数格式化为16进制整数
              System.out.printf("%x; %x; %x%n", -500, 2343L, iObj);
              // %X表示将整数格式化为16进制整数,并且字母变成大写形式
              System.out.printf("%X; %X; %X%n", -500, 2343L, iObj);
             
              /*** 输出浮点类型***/
              Double dObj = 45.6d;
              // %e表示以科学技术法输出浮点数
              System.out.printf("%e; %e; %e%n", -756.403f, 7464.232641d, dObj);
              // %E表示以科学技术法输出浮点数,并且为大写形式            
              System.out.printf("%E; %E; %E%n", -756.403f, 7464.232641d, dObj);
              // %f表示以十进制格式化输出浮点数
              System.out.printf("%f; %f; %f%n", -756.403f, 7464.232641d, dObj);
              // 还可以限制小数点后的位数
              System.out.printf("%.1f; %.3f; %f%n", -756.403f, 7464.232641d, dObj);
             
              /*** 输出日期类型***/
              // %t表示格式化日期时间类型,%T是时间日期的大写形式,在%t之后用特定的字母表示不同的输出格式
              Date date = new Date();
              long dataL = date.getTime();
              // 格式化年月日
              // %t之后用y表示输出日期的年份(2位数的年,如99)
              // %t之后用m表示输出日期的月份,%t之后用d表示输出日期的日号
              System.out.printf("%1$ty-%1$tm-%1$td; %2$ty-%2$tm-%2$td%n", date, dataL);
              // %t之后用Y表示输出日期的年份(4位数的年),
              // %t之后用B表示输出日期的月份的完整名, %t之后用b表示输出日期的月份的简称
              System.out.printf("%1$tY-%1$tB-%1$td; %2$tY-%2$tb-%2$td%n", date, dataL);
             
              // 以下是常见的日期组合
              // %t之后用D表示以 "%tm/%td/%ty"格式化日期
              System.out.printf("%1$tD%n", date);
              //%t之后用F表示以"%tY-%tm-%td"格式化日期
              System.out.printf("%1$tF%n", date);
             
              /*** 输出时间类型***/
              // 输出时分秒
              // %t之后用H表示输出时间的时(24进制),%t之后用I表示输出时间的时(12进制),
              // %t之后用M表示输出时间的分,%t之后用S表示输出时间的秒
              System.out.printf("%1$tH:%1$tM:%1$tS; %2$tI:%2$tM:%2$tS%n", date, dataL);
              // %t之后用L表示输出时间的秒中的毫秒
              System.out.printf("%1$tH:%1$tM:%1$tS %1$tL%n", date);
              // %t之后p表示输出时间的上午或下午信息
              System.out.printf("%1$tH:%1$tM:%1$tS %1$tL %1$tp%n", date);
             
              // 以下是常见的时间组合
              // %t之后用R表示以"%tH:%tM"格式化时间
              System.out.printf("%1$tR%n", date);
              // %t之后用T表示以"%tH:%tM:%tS"格式化时间
              System.out.printf("%1$tT%n", date);
              // %t之后用r表示以"%tI:%tM:%tS %Tp"格式化时间
              System.out.printf("%1$tr%n", date);
             
              /*** 输出星期***/
              // %t之后用A表示得到星期几的全称
              System.out.printf("%1$tF %1$tA%n", date);
              // %t之后用a表示得到星期几的简称
              System.out.printf("%1$tF %1$ta%n", date);
             
              // 输出时间日期的完整信息
              System.out.printf("%1$tc%n", date);
       }
}
/**
 *printf方法中,格式为"%s"表示以字符串的形式输出第二个可变长参数的第一个参数值;
 *格式为"%n"表示换行;格式为"%S"表示将字符串以大写形式输出;在"%s"之间用"n$"表示
 *输出可变长参数的第n个参数值.格式为"%b"表示以布尔值的形式输出第二个可变长参数
 *的第一个参数值.
 */
/**
 * 格式为"%d"表示以十进制整数形式输出;"%o"表示以八进制形式输出;"%x"表示以十六进制
 * 输出;"%X"表示以十六进制输出,并且将字母(A、B、C、D、E、F)换成大写.格式为"%e"表
 * 以科学计数法输出浮点数;格式为"%E"表示以科学计数法输出浮点数,而且将e大写;格式为
 * "%f"表示以十进制浮点数输出,在"%f"之间加上".n"表示输出时保留小数点后面n位.
 */
/**
 * 格式为"%t"表示输出时间日期类型."%t"之后用y表示输出日期的二位数的年份(如99)、用m
 * 表示输出日期的月份,用d表示输出日期的日号;"%t"之后用Y表示输出日期的四位数的年份
 * (如1999)、用B表示输出日期的月份的完整名,用b表示输出日期的月份的简称."%t"之后用D
 * 表示以"%tm/%td/%ty"的格式输出日期、用F表示以"%tY-%tm-%td"的格式输出日期.
 */
/**
 * "%t"之后用H表示输出时间的时(24进制),用I表示输出时间的时(12进制),用M表示输出时间
 * 分,用S表示输出时间的秒,用L表示输出时间的秒中的毫秒数、用 p 表示输出时间的是上午还是
 * 下午."%t"之后用R表示以"%tH:%tM"的格式输出时间、用T表示以"%tH:%tM:%tS"的格式输出
 * 时间、用r表示以"%tI:%tM:%tS %Tp"的格式输出时间.
 */
/**
 * "%t"之后用A表示输出日期的全称,用a表示输出日期的星期简称.
 */

杂记

  • java.lang包下的类不用导入直接使用
  • 集合自动装箱操作只支持1.4以后版本jdk
  • System.out.format("\33[35;1m这是紫红色");此方法打印时可以设置字体属性
    • 设置颜色格式:"\33[前景色编号(选填);背景色编号(选填);2m(必填,1m加粗,2m正常,3m斜体,4m下划线)要输出的内容"

小问题

  1. Calendar是抽象类,无法直接创建对象,那他的getInstance()方法如何绕过抽象类返回了一个Calendar的对象?

    • 底层是调用了其实现类中创建子类对象的方法,并返回一个子类对象
    • 所以该方法内部最终返回的是具体的实现类,也就是子类,而不是我们所理解的Calendar本身实例化的类。
  2. java.sql.Date是否真的是java.util.Date的子类?可以使用java.util.Date的public类

    • 虽然不在一个宝下,但是java.sql.Date确实是java.util.Date的子类,可以使用继承的所有方法,在与Calendar类的转化上效果也相同
posted @ 2021-12-15 18:12  Eleike  阅读(100)  评论(0)    收藏  举报