常用类

一、包装类

1、包装类基本知识

  Java是面向对象的语言,但并不是“纯面向对象”的,因为我们经常用到的基本数据类型就不是对象。但是我们在实际应用中经常需要将基本数据转化成对象,以便于操作。比如:将基本数据类型存储到Object[]数组或集合中的操作等等。
  为了解决这个不足,Java在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。
  包装类均位于java.lang包,八种包装类和基本数据类型的对应关系如表1-1所示:

表1-1基本数据类型对应的包装类

在这里插入图片描述
  在这八个类名中,除了Integer和Character类以外,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写而已。
  在这八个类中,除了Character和Boolean以外,其他的都是“数字型”,“数字型”都是java.lang.Number的子类。Number类是抽象类,因此它的抽象方法,所有子类都需要提供实现。Number类提供了抽象方法:intValue()、longValue()、floatValue()、doubleValue(),意味着所有的“数字型”包装类都可以互相转型。如图1-1和图1-2所示。
在这里插入图片描述

图1-1 Number类的子类

在这里插入图片描述

图1-2 Number类的抽象方法

  下面我们通过一个简单的示例认识一下包装类。

【示例1-1】初识包装类

public class WrapperClassTest {
    public static void main(String[] args) {
        Integer i = new Integer(10);
        Integer j = new Integer(50);
    }
}

  示例1-1的内存分析如图1-3所示:
在这里插入图片描述

图1-3 示例1-1内存分析图

2、包装类的用途

  对于包装类来说,这些类的用途主要包含两种:
  1. 作为和基本数据类型对应的类型存在,方便涉及到对象的操作,如Object[]、集合等的操作。
  2. 包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法(这些操作方法的作用是在基本数据类型、包装类对象、字符串之间提供相互之间的转化!)。

【示例1-2】包装类的使用

public class Test {
    /** 测试Integer的用法,其他包装类与Integer类似 */
    void testInteger() {
        // 基本类型转化成Integer对象
        Integer int1 = new Integer(10);
        Integer int2 = Integer.valueOf(20); // 官方推荐这种写法
        // Integer对象转化成int
        int a = int1.intValue();
        // 字符串转化成Integer对象
        Integer int3 = Integer.parseInt("334");
        Integer int4 = new Integer("999");
        // Integer对象转化成字符串
        String str1 = int3.toString();
        // 一些常见int类型相关的常量
        System.out.println("int能表示的最大整数:" + Integer.MAX_VALUE); 
    }
    public static void main(String[] args) {
        Test test  = new Test();
        test.testInteger();
    }
}

  执行结果如图1-4所示:
在这里插入图片描述

图1-4 示例1-2运行效果图

3、自动装箱和拆箱

  自动装箱和拆箱就是将基本数据类型和包装类之间进行自动的互相转换。JDK1.5后,Java引入了自动装箱(autoboxing)/拆箱(unboxing)。

自动装箱:
  基本类型的数据处于需要对象的环境中时,会自动转为“对象”。
  我们以Integer为例:在JDK1.5以前,这样的代码 Integer i = 5 是错误的,必须要通过Integer i = new Integer(5) 这样的语句来实现基本数据类型转换成包装类的过程;而在JDK1.5以后,Java提供了自动装箱的功能,因此只需Integer i = 5这样的语句就能实现基本数据类型转换成包装类,这是因为JVM为我们执行了Integer i = Integer.valueOf(5)这样的操作,这就是Java的自动装箱。

自动拆箱:
  每当需要一个值时,对象会自动转成基本数据类型,没必要再去显式调用intValue()、doubleValue()等转型方法。
  如 Integer i = 5;int j = i; 这样的过程就是自动拆箱。

  我们可以用一句话总结自动装箱/拆箱:

  自动装箱过程是通过调用包装类的valueOf()方法实现的,而自动拆箱过程是通过调用包装类的 xxxValue()方法实现的(xxx代表对应的基本数据类型,如intValue()、doubleValue()等)。
  自动装箱与拆箱的功能事实上是编译器来帮的忙,编译器在编译时依据您所编写的语法,决定是否进行装箱或拆箱动作,如示例8-3与示例8-4所示。

【示例1-3】自动装箱

Integer i = 100;//自动装箱 
//相当于编译器自动为您作以下的语法编译:
 Integer i = Integer.valueOf(100);//调用的是valueOf(100),而不是new Integer(100)

【示例1-4】自动拆箱

Integer i = 100;
int j = i;//自动拆箱
//相当于编译器自动为您作以下的语法编译:
int j = i.intValue();

  所以自动装箱与拆箱的功能是所谓的“编译器蜜糖(Compiler Sugar)”,虽然使用这个功能很方便,但在程序运行阶段您得了解Java的语义。例如示例1-5所示的程序是可以通过编译的:

【示例1-5】包装类空指针异常问题

public class Test1 {
    public static void main(String[] args) {
        Integer i = null;
        int j = i;
    }
}

  执行结果如图1-5所示:
在这里插入图片描述

图1-5 示例1-5运行效果图

  示例1-5的运行结果之所以会出现空指针异常,是因为示例1-5中的代码相当于:

public class Test1 {
    public static void main(String[] args) {
        //示例1-5的代码在编译时期是合法的,但是在运行时期会有错误,因为其相当于:
        Integer i = null; 
        int j = i.intValue();         
    }
}

  null表示i没有指向任何对象的实体,但作为对象名称是合法的(不管这个对象名称存是否指向了某个对象的实体)。由于实际上i并没有指向任何对象的实体,所以也就不可能操作intValue()方法,这样上面的写法在运行时就会出现NullPointerException错误。

【示例1-6】自动装箱与拆箱

public class Test2 {
    /**
     * 测试自动装箱和拆箱 结论:虽然很方便,但是如果不熟悉特殊情况,可能会出错!
     */
    public static void main(String[] args) {
        Integer b = 23; // 自动装箱
        int a = new Integer(20); //自动拆箱
        // 下面的问题我们需要注意:
        Integer c = null;
        int d = c; // 此处其实就是:c.intValue(),因此抛空指针异常。
    }
}

4、包装类的缓存问题

   整型、char类型所对应的包装类,在自动装箱时,对于-128~127之间的值会进行缓存处理,其目的是提高效率。
   缓存处理的原理为:如果数据在-128~127这个区间,那么在类加载时就已经为该区间的每个数值创建了对象,并将这256个对象存放到一个名为cache的数组中。每当自动装箱过程发生时(或者手动调用valueOf()时),就会先判断数据是否在该区间,如果在则直接获取数组中对应的包装类对象的引用,如果不在该区间,则会通过new调用包装类的构造方法来创建对象。
   下面我们以Integer类为例,看一看Java为我们提供的源码,加深对缓存技术的理解,如示例1-7所示。

【示例1-7】Integer类相关源码如下:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

   这段代码中我们需要解释下面几个问题:
   1. IntegerCache类为Integer类的一个静态内部类,仅供Integer类使用。
   2. 一般情况下 IntegerCache.low为-128,IntegerCache.high为127,IntegerCache.cache为内部类的一个静态属性,如示例1-8所示。

【示例1-8】IntegerCache类相关源码如下:

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];
    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;
        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
 
        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }
    private IntegerCache() {}
}

   由上面的源码我们可以看到,静态代码块的目的就是初始化数组cache的,这个过程会在类加载时完成。

   下面我们做一下代码测试,如示例1-9所示。

【示例1-9】测试代码

public class Test3 {
    public static void main(String[] args) {
        Integer in1 = -128;
        Integer in2 = -128;
        System.out.println(in1 == in2);//true 因为123在缓存范围内
        System.out.println(in1.equals(in2));//true
        Integer in3 = 1234;
        Integer in4 = 1234;
        System.out.println(in3 == in4);//false 因为1234不在缓存范围内
        System.out.println(in3.equals(in4));//true
    }
}

   执行结果如图1-6所示:

在这里插入图片描述

图1-6 示例1-9运行效果图

  示例1-9的内存分析如图1-7所示:
在这里插入图片描述

图1-7 示例1-9的内存分析图

注意:
   1. JDK1.5以后,增加了自动装箱与拆箱功能,如:

Integer i = 100;  int j = new Integer(100);

   2. 自动装箱调用的是valueOf()方法,而不是new Integer()方法。
   3. 自动拆箱调用的xxxValue()方法。
   4. 包装类在自动装箱时为了提高效率,对于-128~127之间的值会进行缓存处理。超过范围后,对象之间不能再使用==进行数值的比较,而是使用equals方法。

二、不可变和可变字符序列

1、String类

  String 类对象代表不可变的Unicode字符序列,因此我们可以将String对象称为“不可变对象”。 那什么叫做“不可变对象”呢?指的是对象内部的成员变量的值无法再改变。我们打开String类的源码,如图2-1所示:
在这里插入图片描述

图2-1 String类的部分源码

  我们发现字符串内容全部存储到value[]数组中,而变量value是final类型的,也就是常量(即只能被赋值一次)。 这就是“不可变对象”的典型定义方式。
  我们知道String的某些方法,比如:substring()是对字符串的截取操作,但本质是读取原字符串内容生成了新的字符串。测试代码如下:

【示例2-1】String测试代码

public class TestString1 {
    public static void main(String[] args) {
        String s1 = new String("abcdef");
        String s2 = s1.substring(2, 4);
        // 打印:ab199863
        System.out.println(Integer.toHexString(s1.hashCode()));
        // 打印:c61, 显然s1和s2不是同一个对象
        System.out.println(Integer.toHexString(s2.hashCode()));
    }
}

   执行结果如图2-2所示:
在这里插入图片描述

图2-2 示例2-1运行效果图

  在遇到字符串常量之间的拼接时,编译器会做出优化,即在编译期间就会完成字符串的拼接。因此,在使用==进行String对象之间的比较时,我们需要特别注意,如示例2-2所示。

【示例2-2】字符串常量拼接时的优化

public class TestString2 {
    public static void main(String[] args) {
        //编译器做了优化,直接在编译的时候将字符串进行拼接
        String str1 = "hello" + " java";//相当于str1 = "hello java";
        String str2 = "hello java";
        System.out.println(str1 == str2);//true
        String str3 = "hello";
        String str4 = " java";
        //编译的时候不知道变量中存储的是什么,所以没办法在编译的时候优化
        String str5 = str3 + str4;
        System.out.println(str2 == str5);//false
    }
}

  执行结果如图2-3所示:
在这里插入图片描述

图2-3 示例2-2运行效果图

  String类常用的方法有:
  1. String类的下述方法能创建并返回一个新的String对象: concat()、 replace()、substring()、 toLowerCase()、 toUpperCase()、trim()。
  2. 提供查找功能的有关方法: endsWith()、 startsWith()、 indexOf()、lastIndexOf()。
  3. 提供比较功能的方法: equals()、equalsIgnoreCase()、compareTo()。
  4. 其它方法: charAt() 、length()。

2、StringBuffer和StringBuilder

  StringBuffer和StringBuilder非常类似,均代表可变的字符序列。 这两个类都是抽象类AbstractStringBuilder的子类,方法几乎一模一样。我们打开AbstractStringBuilder的源码,如示例2-3所示:

【示例2-3】AbstractStringBuilder 部分源码

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char value[];
//以下代码省略
}

  显然,内部也是一个字符数组,但这个字符数组没有用final修饰,随时可以修改。因此,StringBuilder和StringBuffer称之为“可变字符序列”。那两者有什么区别呢?
  1. StringBuffer JDK1.0版本提供的类,线程安全,做线程同步检查, 效率较低。
  2. StringBuilder JDK1.5版本提供的类,线程不安全,不做线程同步检查,因此效率较高。 建议采用该类。

· 常用方法列表:
  1. 重载的public StringBuilder append(…)方法
  可以为该StringBuilder 对象添加字符序列,仍然返回自身对象。

  2. 方法 public StringBuilder delete(int start,int end)
  可以删除从start开始到end-1为止的一段字符序列,仍然返回自身对象。

  3. 方法 public StringBuilder deleteCharAt(int index)
  移除此序列指定位置上的 char,仍然返回自身对象。

  4. 重载的public StringBuilder insert(…)方法
  可以为该StringBuilder 对象在指定位置插入字符序列,仍然返回自身对象。

  5. 方法 public StringBuilder reverse()
  用于将字符序列逆序,仍然返回自身对象。

  6. 方法 public String toString() 返回此序列中数据的字符串表示形式。

  7. 和 String 类含义类似的方法:

public int indexOf(String str)
public int indexOf(String str,int fromIndex)
public String substring(int start)
public String substring(int start,int end)
public int length() 
char charAt(int index)

【示例2-4】StringBuffer/StringBuilder基本用法

public class TestStringBufferAndBuilder 1{
    public static void main(String[] args) {
        /**StringBuilder*/
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 7; i++) {
            sb.append((char) ('a' + i));//追加单个字符
        }
        System.out.println(sb.toString());//转换成String输出
        sb.append(", I can sing my abc!");//追加字符串
        System.out.println(sb.toString());
        /**StringBuffer*/
        StringBuffer sb2 = new StringBuffer("中华人民共和国");
        sb2.insert(0, "爱").insert(0, "我");//插入字符串
        System.out.println(sb2);
        sb2.delete(0, 2);//删除子字符串
        System.out.println(sb2);
        sb2.deleteCharAt(0).deleteCharAt(0);//删除某个字符
        System.out.println(sb2.charAt(0));//获取某个字符
        System.out.println(sb2.reverse());//字符串逆序
    }
}

  执行结果如图2-4所示:
在这里插入图片描述

图2-4 示例2-4运行效果图

3、不可变和可变字符序列使用陷阱

· String使用的陷阱
   String一经初始化后,就不会再改变其内容了。对String字符串的操作实际上是对其副本(原始拷贝)的操作,原来的字符串一点都没有改变。比如:
  String s =“a”; 创建了一个字符串
  s = s+“b”; 实际上原来的"a"字符串对象已经丢弃了,现在又产生了另一个字符串s+“b”(也就是"ab")。 如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的时间和空间性能,甚至会造成服务器的崩溃。
  相反,StringBuilder和StringBuffer类是对原字符串本身操作的,可以对字符串进行修改而不产生副本拷贝或者产生少量的副本。因此可以在循环中使用。

【示例2-5】String和StringBuilder在频繁字符串修改时效率测试

public class Test {
    public static void main(String[] args) {
        /**使用String进行字符串的拼接*/
        String str8 = "";
        //本质上使用StringBuilder拼接, 但是每次循环都会生成一个StringBuilder对象
        long num1 = Runtime.getRuntime().freeMemory();//获取系统剩余内存空间
        long time1 = System.currentTimeMillis();//获取系统的当前时间
        for (int i = 0; i < 5000; i++) {
            str8 = str8 + i;//相当于产生了10000个对象
        }
        long num2 = Runtime.getRuntime().freeMemory();
        long time2 = System.currentTimeMillis();
        System.out.println("String占用内存 : " + (num1 - num2));
        System.out.println("String占用时间 : " + (time2 - time1));
        /**使用StringBuilder进行字符串的拼接*/
        StringBuilder sb1 = new StringBuilder("");
        long num3 = Runtime.getRuntime().freeMemory();
        long time3 = System.currentTimeMillis();
        for (int i = 0; i < 5000; i++) {
            sb1.append(i);
        }
        long num4 = Runtime.getRuntime().freeMemory();
        long time4 = System.currentTimeMillis();
        System.out.println("StringBuilder占用内存 : " + (num3 - num4));
        System.out.println("StringBuilder占用时间 : " + (time4 - time3));
    }
}

  执行结果如图2-5所示:
在这里插入图片描述

图2-5示例2-5运行效果图

要点:
  1. String:不可变字符序列。
  2. StringBuffer:可变字符序列,并且线程安全,但是效率低。
  3. StringBuilder:可变字符序列,线程不安全,但是效率高(一般用它)。

三、时间处理相关类

“时间如流水,一去不复返”,时间是一个一维的东东。所以,我们需要一把刻度尺来表达和度量时间。在计算机世界,我们把1970 年 1 月 1 日 00:00:00定为基准时间,每个度量单位是毫秒(1秒的千分之一),如图3-1所示。
在这里插入图片描述

图3-1 计算机的时间概念

  我们用long类型的变量来表示时间,从基准时间往前几亿年,往后几亿年都能表示。如果想获得现在时刻的“时刻数值”,可以使用:

long now = System.currentTimeMillis();

   这个“时刻数值”是所有时间类的核心值,年月日都是根据这个“数值”计算出来的。我们工作学习涉及的时间相关类有如下这些:
在这里插入图片描述

图3-2 日期时间相关类

1、Date时间类(java.util.Date)

  在标准Java类库中包含一个Date类。它的对象表示一个特定的瞬间,精确到毫秒。
  1. Date() 分配一个Date对象,并初始化此对象为系统当前的日期和时间,可以精确到毫秒)。
  2. Date(long date) 分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。
  3. boolean after(Date when) 测试此日期是否在指定日期之后。
  4. booleanbefore(Date when) 测试此日期是否在指定日期之前。
  5. boolean equals(Object obj) 比较两个日期的相等性。
  6. long getTime() 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
  7. String toString() 把此 Date 对象转换为以下形式的 String:
  dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun、 Mon、Tue、Wed、 Thu、 Fri、 Sat)。

【示例3-1】Date类的使用

import java.util.Date;
public class TestDate {
    public static void main(String[] args) {
        Date date1 = new Date();
        System.out.println(date1.toString());
        long i = date1.getTime();
        Date date2 = new Date(i - 1000);
        Date date3 = new Date(i + 1000);
        System.out.println(date1.after(date2));
        System.out.println(date1.before(date2));
        System.out.println(date1.equals(date2));
        System.out.println(date1.after(date3));
        System.out.println(date1.before(date3));
        System.out.println(date1.equals(date3));
        System.out.println(new Date(1000L * 60 * 60 * 24 * 365 * 39L).toString());
    }
}

  执行结果如图3-3所示:
在这里插入图片描述

图3-3 示例3-1运行效果图

   查看API文档大家可以看到其实Date类中的很多方法都已经过时了。JDK1.1之前的Date包含了:日期操作、字符串转化成时间对象等操作。JDK1.1之后,日期操作一般使用Calendar类,而字符串的转化使用DateFormat类。

2、DateFormat类和SimpleDateFormat类

·DateFormat类的作用
   把时间对象转化成指定格式的字符串。反之,把指定格式的字符串转化成时间对象。
   DateFormat是一个抽象类,一般使用它的的子类SimpleDateFormat类来实现。

【示例3-2】DateFormat类和SimpleDateFormat类的使用

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDateFormat {
    public static void main(String[] args) throws ParseException {
        // new出SimpleDateFormat对象
        SimpleDateFormat s1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        SimpleDateFormat s2 = new SimpleDateFormat("yyyy-MM-dd");
        // 将时间对象转换成字符串
        String daytime = s1.format(new Date());
        System.out.println(daytime);
        System.out.println(s2.format(new Date()));
        System.out.println(new SimpleDateFormat("hh:mm:ss").format(new Date()));
        // 将符合指定格式的字符串转成成时间对象.字符串格式需要和指定格式一致。
        String time = "2007-10-7";
        Date date = s2.parse(time);
        System.out.println("date1: " + date);
        time = "2007-10-7 20:15:30";
        date = s1.parse(time);
        System.out.println("date2: " + date);
    }
}

   执行结果如图3-4所示:
在这里插入图片描述

图3-4 示例3-2运行效果图

   代码中的格式化字符的具体含义见表3-1:

在这里插入图片描述   时间格式字符也可以为我们提供其他的便利。比如:获得当前时间是今年的第几天。代码如下:

【示例3-3】时间格式字符的使用

import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDateFormat2 {
    public static void main(String[] args) {
        SimpleDateFormat s1 = new SimpleDateFormat("D");
        String daytime = s1.format(new Date());
        System.out.println(daytime);
    }
}

   执行结果如图3-5所示:

在这里插入图片描述

图3-5 示例3-3运行效果图

3、Calendar日历类

   Calendar 类是一个抽象类,为我们提供了关于日期计算的相关功能,比如:年、月、日、时、分、秒的展示和计算。
   GregorianCalendar 是 Calendar 的一个具体子类,提供了世界上大多数国家/地区使用的标准日历系统。

菜鸟雷区:
   注意月份的表示,一月是0,二月是1,以此类推,12月是11。 因为大多数人习惯于使用单词而不是使用数字来表示月份,这样程序也许更易读,父类Calendar使用常量来表示月份:JANUARY、FEBRUARY等等。

【示例3-4】GregorianCalendar类和Calendar类的使用

import java.util.*;
public class TestCalendar {
    public static void main(String[] args) {
        // 得到相关日期元素
        GregorianCalendar calendar = new GregorianCalendar(2999, 10, 9, 22, 10, 50);
        int year = calendar.get(Calendar.YEAR); // 打印:1999
        int month = calendar.get(Calendar.MONTH); // 打印:10
        int day = calendar.get(Calendar.DAY_OF_MONTH); // 打印:9
        int day2 = calendar.get(Calendar.DATE); // 打印:9
        // 日:Calendar.DATE和Calendar.DAY_OF_MONTH同义
        int date = calendar.get(Calendar.DAY_OF_WEEK); // 打印:3
        // 星期几 这里是:1-7.周日是1,周一是2,。。。周六是7
        System.out.println(year);
        System.out.println(month);
        System.out.println(day);
        System.out.println(day2);
        System.out.println(date);
        // 设置日期
        GregorianCalendar calendar2 = new GregorianCalendar();
        calendar2.set(Calendar.YEAR, 2999);
        calendar2.set(Calendar.MONTH, Calendar.FEBRUARY); // 月份数:0-11
        calendar2.set(Calendar.DATE, 3);
        calendar2.set(Calendar.HOUR_OF_DAY, 10);
        calendar2.set(Calendar.MINUTE, 20);
        calendar2.set(Calendar.SECOND, 23);
        printCalendar(calendar2);
        // 日期计算
        GregorianCalendar calendar3 = new GregorianCalendar(2999, 10, 9, 22, 10, 50);
        calendar3.add(Calendar.MONTH, -7); // 月份减7
        calendar3.add(Calendar.DATE, 7); // 增加7天
        printCalendar(calendar3);
        // 日历对象和时间对象转化
        Date d = calendar3.getTime();
        GregorianCalendar calendar4 = new GregorianCalendar();
        calendar4.setTime(new Date());
        long g = System.currentTimeMillis();
    }
    static void printCalendar(Calendar calendar) {
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1;
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int date = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 星期几
        String week = "" + ((date == 0) ? "日" : date);
        int hour = calendar.get(Calendar.HOUR);
        int minute = calendar.get(Calendar.MINUTE);
        int second = calendar.get(Calendar.SECOND);
        System.out.printf("%d年%d月%d日,星期%s %d:%d:%d\n", year, month, day,  
                        week, hour, minute, second);
    }
}

   执行结果如图3-6所示:
在这里插入图片描述

图3-6 示例3-4运行效果图

  编写程序,利用GregorianCalendar类,打印当前月份的日历,今天的日期是 2017-05-18 ,如图3-7所示为今日所在月份的日历:
在这里插入图片描述

图3-7 示例3-5运行效果图

【示例3-5】可视化日历的编写

import java.text.ParseException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Scanner;
public class TestCalendar2 {
    public static void main(String[] args) throws ParseException {
        System.out.println("请输入日期(格式为:2010-3-3):");
        Scanner scanner = new Scanner(System.in);
        String dateString = scanner.nextLine(); // 2010-3-1
        // 将输入的字符串转化成日期类
        System.out.println("您刚刚输入的日期是:" + dateString);
        String[] str = dateString.split("-");
        int year = Integer.parseInt(str[0]);
        int month = new Integer(str[1]);
        int day = new Integer(str[2]);
        Calendar c = new GregorianCalendar(year, month - 1, day); // Month:0-11
        // 大家自己补充另一种方式:将字符串通过SImpleDateFormat转化成Date对象,
        //再将Date对象转化成日期类
        // SimpleDateFormat sdfDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        // Date date = sdfDateFormat.parse(dateString);
        // Calendar c = new GregorianCalendar();
        // c.setTime(date);
        // int day = c.get(Calendar.DATE);
        c.set(Calendar.DATE, 1);
        int dow = c.get(Calendar.DAY_OF_WEEK); // week:1-7 日一二三四五六
        System.out.println("日\t一\t二\t三\t四\t五\t六");
        for (int i = 0; i < dow - 1; i++) {
            System.out.print("\t");
        }
        int maxDate = c.getActualMaximum(Calendar.DATE);
        // System.out.println("maxDate:"+maxDate);
        for (int i = 1; i <= maxDate; i++) {
            StringBuilder sBuilder = new StringBuilder();
            if (c.get(Calendar.DATE) == day) {
                sBuilder.append(c.get(Calendar.DATE) + "*\t");
            } else {
                sBuilder.append(c.get(Calendar.DATE) + "\t");
            }
            System.out.print(sBuilder);
            // System.out.print(c.get(Calendar.DATE)+
            //                ((c.get(Calendar.DATE)==day)?"*":"")+"\t");
 
            if (c.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
                System.out.print("\n");
            }
            c.add(Calendar.DATE, 1);
        }
    }
}

四、Math类

   java.lang.Math提供了一系列静态方法用于科学计算;其方法的参数和返回值类型一般为double型。如果需要更加强大的数学运算能力,计算高等数学中的相关内容,可以使用apache commons下面的Math类库。

Math类的常用方法:
   1. abs 绝对值
   2. acos,asin,atan,cos,sin,tan 三角函数
   3. sqrt 平方根
   4. pow(double a, double b) a的b次幂
   5. max(double a, double b) 取大值
   6. min(double a, double b) 取小值
   7. ceil(double a) 大于a的最小整数
   8. floor(double a) 小于a的最大整数
   9. random() 返回 0.0 到 1.0 的随机数
   10. long round(double a) double型的数据a转换为long型(四舍五入)
   11. toDegrees(double angrad) 弧度->角度
   12. toRadians(double angdeg) 角度->弧度

【示例4-1】Math类的常用方法

public class TestMath {
    public static void main(String[] args) {
        //取整相关操作
        System.out.println(Math.ceil(3.2));
        System.out.println(Math.floor(3.2));
        System.out.println(Math.round(3.2));
        System.out.println(Math.round(3.8));
        //绝对值、开方、a的b次幂等操作
        System.out.println(Math.abs(-45));
        System.out.println(Math.sqrt(64));
        System.out.println(Math.pow(5, 2));
        System.out.println(Math.pow(2, 5));
        //Math类中常用的常量
        System.out.println(Math.PI);
        System.out.println(Math.E);
        //随机数
        System.out.println(Math.random());// [0,1)
    }
}

   执行结果如图4-1所示:
在这里插入图片描述

图4-1 示例4-1运行效果图

  Math类中虽然为我们提供了产生随机数的方法Math.random(),但是通常我们需要的随机数范围并不是[0, 1)之间的double类型的数据,这就需要对其进行一些复杂的运算。如果使用Math.random()计算过于复杂的话,我们可以使用例外一种方式得到随机数,即Random类,这个类是专门用来生成随机数的,并且Math.random()底层调用的就是Random的nextDouble()方法。

【示例4-2】Random类的常用方法

import java.util.Random;
public class TestRandom {
    public static void main(String[] args) {
        Random rand = new Random();
        //随机生成[0,1)之间的double类型的数据
        System.out.println(rand.nextDouble());
        //随机生成int类型允许范围之内的整型数据
        System.out.println(rand.nextInt());
        //随机生成[0,1)之间的float类型的数据
        System.out.println(rand.nextFloat());
        //随机生成false或者true
        System.out.println(rand.nextBoolean());
        //随机生成[0,10)之间的int类型的数据
        System.out.print(rand.nextInt(10));
        //随机生成[20,30)之间的int类型的数据
        System.out.print(20 + rand.nextInt(10));
        //随机生成[20,30)之间的int类型的数据(此种方法计算较为复杂)
        System.out.print(20 + (int) (rand.nextDouble() * 10));
    }
}

  执行结果如图4-2所示:
在这里插入图片描述

图4-2 示例4-2运行效果图

注意:
  Random类位于java.util包下。

五、File类

1、File类的基本用法

   java.io.File类:代表文件和目录。 在开发中,读取文件、生成文件、删除文件、修改文件的属性时经常会用到本类。
   File类的常见构造方法:public File(String pathname)
   以pathname为路径创建File对象,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储,如示例5-1所示。

【示例5-1】文件的创建

import java.io.File;
public class TestFile1 {
    public static void main(String[] args) throws Exception {
        System.out.println(System.getProperty("user.dir"));
        File f = new File("a.txt"); //相对路径:默认放到user.dir目录下面
        f.createNewFile();//创建文件
        File f2 = new File("d:/b.txt");//绝对路径
        f2.createNewFile();
    }
}

   在eclipse项目开发中,user.dir就是本项目的目录。因此,执行完毕后,在本项目和D盘下都生成了新的文件(如果是eclipse下,一定按F5刷新目录结构才能看到新文件)。如图5-1所示。

在这里插入图片描述

图5-1 本项目目录中新增文件效果

通过File对象可以访问文件的属性:

表5-1 File类访问属性的方法列表
在这里插入图片描述

【示例5-2】测试File类访问属性的基本用法

import java.io.File;
import java.util.Date;
public class TestFile2 {
    public static void main(String[] args) throws Exception {
        File f = new File("d:/b.txt");
        System.out.println("File是否存在:"+f.exists());
        System.out.println("File是否是目录:"+f.isDirectory());
        System.out.println("File是否是文件:"+f.isFile());
        System.out.println("File最后修改时间:"+new Date(f.lastModified()));
        System.out.println("File的大小:"+f.length());
        System.out.println("File的文件名:"+f.getName());
        System.out.println("File的目录路径:"+f.getPath());
    }
}

   执行结果如图5-2所示:

在这里插入图片描述

图5-2 示例5-2运行效果图

通过File对象创建空文件或目录(在该对象所指的文件或目录不存在的情况下)

表5-2 File类创建文件或目录的方法列表

在这里插入图片描述
【示例5-3】使用mkdir创建目录

import java.io.File;
public class TestFile3 {
    public static void main(String[] args) throws Exception {
        File f = new File("d:/c.txt");
        f.createNewFile(); // 会在d盘下面生成c.txt文件
        f.delete(); // 将该文件或目录从硬盘上删除
        File f2 = new File("d:/电影/华语/大陆");
        boolean flag = f2.mkdir(); //目录结构中有一个不存在,则不会创建整个目录树
        System.out.println(flag);//创建失败
    }
}

  执行结果如图5-3所示:
在这里插入图片描述

图5-3 示例5-3运行效果图

【示例5-4】使用mkdirs创建目录

import java.io.File;
public class TestFile4 {
    public static void main(String[] args) throws Exception {
        File f = new File("d:/c.txt");
        f.createNewFile(); // 会在d盘下面生成c.txt文件
        f.delete(); // 将该文件或目录从硬盘上删除
        File f2 = new File("d:/电影/华语/大陆");
        boolean flag = f2.mkdirs();//目录结构中有一个不存在也没关系;创建整个目录树
        System.out.println(flag);//创建成功
    }
}

  执行结果如图5-4所示:
在这里插入图片描述

图5-4 示例5-4运行效果图

【示例5-5】File类的综合应用

import java.io.File;
import java.io.IOException;
public class TestFile5 {
    public static void main(String[] args) {
        //指定一个文件
        File file = new File("d:/sxt/b.txt");
        //判断该文件是否存在
        boolean flag= file.exists();
        //如果存在就删除,如果不存在就创建
        if(flag){
            //删除
            boolean flagd = file.delete();
            if(flagd){
                System.out.println("删除成功");
            }else{
                System.out.println("删除失败");
            }
        }else{
            //创建
            boolean flagn = true;
            try {
                //如果目录不存在,先创建目录
                File dir = file.getParentFile();
                dir.mkdirs();
                //创建文件
                flagn = file.createNewFile();
                System.out.println("创建成功");
            } catch (IOException e) {
                System.out.println("创建失败");
                e.printStackTrace();
            }          
        }
        //文件重命名(同学可以自己测试一下)
        //file.renameTo(new File("d:/readme.txt"));
    }
}

  第一次执行结果如图5-5所示:
在这里插入图片描述

图5-5 示例5-5第一次运行效果图

   第二次执行结果如图5-6所示:
在这里插入图片描述

图5-6 示例5-6第二次运行效果图

2、递归遍历目录结构和树状展现

  本节讲述递归算法,展示目录结构。可以先建立一个目录,下面增加几个子文件夹或者文件,用于测试。

【示例5-7】使用递归算法,以树状结构展示目录树

import java.io.File;
public class TestFile6 {
    public static void main(String[] args) {
        File f = new File("d:/电影");
        printFile(f, 0);
    }
    /**
     * 打印文件信息
     * @param file 文件名称
     * @param level 层次数(实际就是:第几次递归调用)
     */
    static void printFile(File file, int level) {
        //输出层次数
        for (int i = 0; i < level; i++) {
            System.out.print("-");
        }
        //输出文件名
        System.out.println(file.getName());
        //如果file是目录,则获取子文件列表,并对每个子文件进行相同的操作
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File temp : files) {
                //递归调用该方法:注意等+1
                printFile(temp, level + 1);
            }
        }
    }
}

  执行结果如图5-7所示:
在这里插入图片描述

图5-7 示例5-7运行效果图

六、枚举

   JDK1.5引入了枚举类型。枚举类型的定义包括枚举声明和枚举体。格式如下:

enum  枚举名 {
      枚举体(常量列表)
}

   枚举体就是放置一些常量。我们可以写出我们的第一个枚举类型,如示例6-1所示:

【示例6-1】创建枚举类型

enum Season {
    SPRING, SUMMER, AUTUMN, WINDER 
}

   所有的枚举类型隐性地继承自 java.lang.Enum。枚举实质上还是类!而每个被枚举的成员实质就是一个枚举类型的实例,他们默认都是public static final修饰的。可以直接通过枚举类型名使用它们。

建议:
   1. 当你需要定义一组常量时,可以使用枚举类型。
   2. 尽量不要使用枚举的高级特性,事实上高级特性都可以使用普通类来实现,没有必要引入枚举,增加程序的复杂性!

【示例6-2】枚举的使用

import java.util.Random;
public class TestEnum {
    public static void main(String[] args) {
        // 枚举遍历
        for (Week k : Week.values()) {
            System.out.println(k);
        }
        // switch语句中使用枚举
        int a = new Random().nextInt(4); // 生成0,1,2,3的随机数
        switch (Season.values()[a]) {
        case SPRING:
            System.out.println("春天");
            break;
        case SUMMER:
            System.out.println("夏天");
            break;
        case AUTUMN:
            System.out.println("秋天");
            break;
        case WINDTER:
            System.out.println("冬天");
            break;
        }
    }
}
/**季节*/
enum Season {
    SPRING, SUMMER, AUTUMN, WINDTER
}
/**星期*/
enum Week {
    星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期日
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
hutool工具常用包括类型转换工具(Convert)、字符串工具(StrUtil / StringUtils)、日期工具(DateUtil)、数字工具NumberUtil)、数组工具(ArrayUtil)、随机工具(RandomUtil)、比较器工具(ComparatorUtil)、多线程工具(ThreadUtil)、IO流工具(FileUtil)、集合工具(CollUtil / CollectionsUtils)、正则工具(ReUtil)、网络工具(NetUtil)、JSON工具(JSONUtil)、系统信息工具(SystemUtil)等等。这些工具提供了一系列常用的方法和功能,能够帮助开发者更加方便地进行类型转换、字符串处理、日期操作、数字处理、数组操作、随机数生成、多线程管理、IO流操作、集合操作、正则表达式匹配、网络操作、JSON处理、系统信息获取等等。通过使用hutool工具,开发者可以提高开发效率,减少代码量,提供更加稳定和高效的程序功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Hutool常用工具](https://blog.csdn.net/abst122/article/details/124091375)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [hutool 工具](https://download.csdn.net/download/LiHaoYang11/12153632)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [常用工具 (三) : Hutool 常用工具整理 (全)](https://blog.csdn.net/m0_37989980/article/details/126401041)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值