Java语法进阶之常用类

发布于:2025-08-18 ⋅ 阅读:(15) ⋅ 点赞:(0)

首先,阅读这篇文章之前呢,我先把这篇文章讲的大纲说一下,主要分为四大类,大家平时在开发的时候,其实也会用得到,分别为包装类字符串相关类时间处理相关类其他常用类

文章目录

  • 1 包装类
    • 1.1 基础数据类型的包装类
    • 1.2 包装类基础知识
    • 1.3 包装类的用途
    • 1.4 自动拆箱、自动装箱
  • 2 字符串相关类
    • 2.1 Stirng类源码分析
    • 2.2 StringBuffer和StringBuilder
    • 2.3 注意点
      • StringBuffer注意事项:
      • StringBuilder注意事项:
      • 通用注意事项:
  • 3. 时间处理相关类
      • Date类使用注意事项:
      • SimpleDateFormat使用注意事项:
      • Calendar类使用注意事项:
      • Java 8日期时间API使用注意事项:
      • 通用注意事项:
  • 4.其他常用类
    • 4.1 Math和Random
      • Math类使用注意事项:
      • Random类使用注意事项:
      • ThreadLocalRandom使用注意事项:
      • 随机数应用注意事项:
    • 4.2 File
      • 基本用法
      • 注意事项
    • 4.3 枚举
      • 基本用法
      • 注意事项
      • 高级用法
  • 总结

1 包装类

1.1 基础数据类型的包装类

在这里插入图片描述

阅读底层源码,其实不难发现,Java为每一个基本数据类型提供了相应的包装类对象。

1.2 包装类基础知识

​ Java是面向对象的语言,但是并不是“纯面向对象”的,这里解释一下,因为我们经常用到的基本数据类型就不是对象,(关于面向对象可以去看我基础教程里的面向对象,这里不做赘述),但实际在应用过程中。通常会将基础数据类型转换为对象,比如Object[] (父类),或者集合里等等。

​ 为了解决这个问题,Java在设计类的时候,为每个数据类型都涉及了一个对应的类,八个基本技术类型所对应的类叫做包装类。可以通过调用java.lang包来引入,

​ 基础数据类型对应的包装类

基本数据类型 包装类
byte Byte
boolean Boolean
short Short
char Character
int Integer
long Long
float Float
double Double

接下来看这个图片

在这里插入图片描述

我们来到JDK给我们封装的抽象类Number中,我们会发现这里所有的子类都需要提供实现,为上述的几个方法,intValue(),longValue(),floatValue(),doubleValue(),等等,

接下来我们通过代码来认识一下包装类

public class Text {
    public static void main(String[] args) {
        String a=new String("Hello");
        String b=String.valueOf("Hello");
    }
}

你会发现,我怎么写了两种方法呢,但是实际上这两种方法都是可以的,但是我个人还是推荐新手使用第一种写法的。等使用熟练之后在使用第二种写法,我们继续看jdk给我们封装的代码,你会发现他重写了好多valueof方法,Object是所有类型的父类,详情大家可以去读一下官方文档。

在这里插入图片描述

1.3 包装类的用途

对于包装类来说,这些类的用途如下:

  • 作为和基本数据类型对应的类型的存在,方便设计对象的操作
  • 包含相关属性,比如最大值、最小值,以及相关的操作方法等等。

使用示例:这里以为Integer为例,这里给打架介绍一些常用的类型之间的相互转换,其他的可以自行根据我的语法去试一下

public class Text {
    public static void main(String[] args) {
       //基本类型转化为Integer对象
        Integer a=Integer.valueOf(11);
        //Integer转化为int
        int b=a.intValue();
        //字符串转化为integer对象
        Integer c=Integer.parseInt("123");
        //int转化为字符串
        String d=String.valueOf(123);
        //字符串转化为char数组
        char[] e=d.toCharArray();
        //char数组转化为字符串
        String f=new String(e);
        //字符串转化为byte数组
        byte[] g=d.getBytes();
    }
}

1.4 自动拆箱、自动装箱

这里解释一下这两个名字

  • 自动装箱:假设基本类型的数据处于需要对象的环境中,会自动转换为对象,例如:Integer i=5 编译器会自动转换成Integer i=Integer.valueOf(5)
  • 自动拆箱:直接上示例代码。原理跟装箱是一样的。Integer i=Integer.valueOf(5),int j=i,编译器会自动转换为int j=i.intValue*()

其实拆装箱的本质就是让编译器来帮忙,根据所写代码的环境,编译器去决定是否进行这两个动作。

2 字符串相关类

String类代表不可变的字符序列,StringBuilder和StringBuffer待变可变字符序列,你看底层源码会发现就是有没有final的区别(final在方法前代码一次赋值之后不可以在二次赋值)

2.1 Stirng类源码分析

String对象代表不可变的字符序列,因此我们把String叫做不可变对象,为什么是“不可变呢”,我们来看一下String类的源码,你会发现每个变量前都有一个final。指的是对象内部的成员变量的值无法在改变。并且你会发现字符串的内容全部都存在value数组中,而且value是final型,所以这就是不可变对象的定义方式,通过阅读底层源码,我们对不可变对象有了更直观的了解

在这里插入图片描述

接下来呢,我们通过示例代码。来学习String字符串具体的用法

其实这些并不是全部的用法,我们还是可以通过查看底层代码,看官方文档去学习,这里只是给大家写了一些平时开发中可能会用到的一些用法。

public class Text {
    public static void main(String[] args) {
        //String类的简单使用
        String s1 = "hello";
        String s2 = "world";
        String s3 = s1 + s2;

        // 输出拼接后的字符串
        System.out.println(s3);
        // 输出字符串长度
        System.out.println(s3.length());
        // 输出字符串中第一个字符
        System.out.println(s3.charAt(0));
        // 输出字符'o'第一次出现的位置
        System.out.println(s3.indexOf("o"));
        // 输出从索引0开始到5(不包含5)的子字符串
        System.out.println(s3.substring(0, 5));
        // 将字符串转换为大写
        System.out.println(s3.toUpperCase());
        // 将字符串转换为小写
        System.out.println(s3.toLowerCase());
        // 比较字符串是否与"hello world"相等
        System.out.println(s3.equals("hello world"));
        // 比较字符串是否与"Hello World"相等(忽略大小写)
        System.out.println(s3.equalsIgnoreCase("Hello World"));
        // 检查字符串是否包含"hello"
        System.out.println(s3.contains("hello"));
        // 检查字符串是否以"hello"开头
        System.out.println(s3.startsWith("hello"));
        // 检查字符串是否以"world"结尾
        System.out.println(s3.endsWith("world"));
        // 将字符串中的"hello"替换为"hi"
        System.out.println(s3.replace("hello", "hi"));
        // 按空格分割字符串
        System.out.println(s3.split(" "));
        // 去除字符串两端的空白字符
        System.out.println(s3.trim());
        // 检查字符串是否为空
        System.out.println(s3.isEmpty());
        // 检查字符串是否为空白(Java 11+)
        System.out.println(s3.isBlank());
        // 比较字符串与"hello world"的字典顺序
        System.out.println(s3.compareTo("hello world"));
        // 比较字符串与"Hello World"的字典顺序(忽略大小写)
        System.out.println(s3.compareToIgnoreCase("Hello World"));
        // 在字符串末尾添加"!!!"
        System.out.println(s3.concat("!!!"));
        // 返回字符串的规范表示形式
        System.out.println(s3.intern());
        // 返回字符'o'最后一次出现的位置
        System.out.println(s3.lastIndexOf("o"));
        // 检查字符串是否匹配"hello world"正则表达式
        System.out.println(s3.matches("hello world"));
        // 按空格分割字符串后取第一个元素
        System.out.println(s3.split(" ")[0]);
        // 将字符串转换为字符数组
        System.out.println(s3.toCharArray());
        // 返回指定索引处的字符Unicode代码点
        System.out.println(s3.codePointAt(0));

    }
}

2.2 StringBuffer和StringBuilder

这两个有啥区别呢?

  • StringBuffer:线程安全,做线程同步检查,但是效率贼低
  • StirngBuilder:线程不安全,不做线程同步检查,因此效率相对较高一点。

废话不多说,上示例代码学习!

public class Text {
    public static void main(String[] args) {
        // StringBuffer示例
        System.out.println("=== StringBuffer示例 ===");
        StringBuffer sb1 = new StringBuffer();

        // append方法 - 添加内容
        sb1.append("hello");
        sb1.append(" ");
        sb1.append("world");
        System.out.println("append后: " + sb1);

        // insert方法 - 在指定位置插入内容
        sb1.insert(5, ",");
        System.out.println("insert后: " + sb1);

        // delete方法 - 删除指定范围的内容
        sb1.delete(5, 6);
        System.out.println("delete后: " + sb1);

        // deleteCharAt方法 - 删除指定位置的字符
        sb1.deleteCharAt(5);
        System.out.println("deleteCharAt后: " + sb1);

        // reverse方法 - 反转字符串
        sb1.reverse();
        System.out.println("reverse后: " + sb1);
        sb1.reverse(); // 再次反转回来

        // replace方法 - 替换指定范围的内容
        sb1.replace(0, 5, "hi");
        System.out.println("replace后: " + sb1);

        // capacity方法 - 获取当前容量
        System.out.println("capacity: " + sb1.capacity());

        // length方法 - 获取当前长度
        System.out.println("length: " + sb1.length());

        // setLength方法 - 设置长度
        sb1.setLength(5);
        System.out.println("setLength后: " + sb1);

        // ensureCapacity方法 - 确保容量
        sb1.ensureCapacity(20);
        System.out.println("ensureCapacity后: " + sb1.capacity());

        // substring方法 - 获取子字符串
        sb1 = new StringBuffer("hello world");
        System.out.println("substring: " + sb1.substring(6));
        System.out.println("substring: " + sb1.substring(0, 5));

        // charAt方法 - 获取指定位置的字符
        System.out.println("charAt(0): " + sb1.charAt(0));

        // setCharAt方法 - 设置指定位置的字符
        sb1.setCharAt(0, 'H');
        System.out.println("setCharAt后: " + sb1);

        // toString方法 - 转换为String
        String str = sb1.toString();
        System.out.println("toString后: " + str);

        // StringBuilder示例
        System.out.println("\n=== StringBuilder示例 ===");
        StringBuilder sb2 = new StringBuilder();

        // append方法 - 添加内容
        sb2.append("hello");
        sb2.append(" ");
        sb2.append("world");
        System.out.println("append后: " + sb2);

        // insert方法 - 在指定位置插入内容
        sb2.insert(5, ",");
        System.out.println("insert后: " + sb2);

        // delete方法 - 删除指定范围的内容
        sb2.delete(5, 6);
        System.out.println("delete后: " + sb2);

        // deleteCharAt方法 - 删除指定位置的字符
        sb2.deleteCharAt(5);
        System.out.println("deleteCharAt后: " + sb2);

        // reverse方法 - 反转字符串
        sb2.reverse();
        System.out.println("reverse后: " + sb2);
        sb2.reverse(); // 再次反转回来

        // replace方法 - 替换指定范围的内容
        sb2.replace(0, 5, "hi");
        System.out.println("replace后: " + sb2);

        // capacity方法 - 获取当前容量
        System.out.println("capacity: " + sb2.capacity());

        // length方法 - 获取当前长度
        System.out.println("length: " + sb2.length());

        // setLength方法 - 设置长度
        sb2.setLength(5);
        System.out.println("setLength后: " + sb2);

        // ensureCapacity方法 - 确保容量
        sb2.ensureCapacity(20);
        System.out.println("ensureCapacity后: " + sb2.capacity());

        // substring方法 - 获取子字符串
        sb2 = new StringBuilder("hello world");
        System.out.println("substring: " + sb2.substring(6));
        System.out.println("substring: " + sb2.substring(0, 5));

        // charAt方法 - 获取指定位置的字符
        System.out.println("charAt(0): " + sb2.charAt(0));

        // setCharAt方法 - 设置指定位置的字符
        sb2.setCharAt(0, 'H');
        System.out.println("setCharAt后: " + sb2);

        // toString方法 - 转换为String
        str = sb2.toString();
        System.out.println("toString后: " + str);

        // StringBuffer和StringBuilder的主要区别示例
        System.out.println("\n=== 线程安全测试 ===");
        // StringBuffer是线程安全的
        StringBuffer threadSafe = new StringBuffer();
        threadSafe.append("thread safe");

        // StringBuilder不是线程安全的
        StringBuilder notThreadSafe = new StringBuilder();
        notThreadSafe.append("not thread safe");

        System.out.println("StringBuffer: " + threadSafe);
        System.out.println("StringBuilder: " + notThreadSafe);

        // 性能对比示例
        System.out.println("\n=== 性能对比 ===");
        long startTime, endTime;

        // 测试StringBuffer性能
        startTime = System.currentTimeMillis();
        StringBuffer sbPerf = new StringBuffer();
        for (int i = 0; i < 100000; i++) {
            sbPerf.append(i);
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuffer耗时: " + (endTime - startTime) + "ms");

        // 测试StringBuilder性能
        startTime = System.currentTimeMillis();
        StringBuilder sb2Perf = new StringBuilder();
        for (int i = 0; i < 100000; i++) {
            sb2Perf.append(i);
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuilder耗时: " + (endTime - startTime) + "ms");
    }
}

ps:这里的相关代码有点长,但是也确实是比较常用的,建议大家自己上手敲一下,不要光看,敲熟练了,用起来不就熟练了么

2.3 注意点

使用StringBuffer和StringBuilder时需要注意以下几点:

StringBuffer注意事项:

  1. 线程安全

    • StringBuffer是线程安全的,所有公共方法都带有synchronized关键字
    • 在多线程环境中可以直接使用,但会带来一定的性能开销
  2. 性能考虑

    • 由于同步机制,StringBuffer的性能通常比StringBuilder慢10%左右
    • 在单线程环境中,优先使用StringBuilder
  3. 容量管理

    • StringBuffer的初始容量为16,当内容超过容量时会自动扩容
    • 扩容时会产生新的数组并复制旧内容,影响性能
    • 对于大容量数据,建议使用ensureCapacity()预先设置容量
  4. 方法链调用

    • StringBuffer的修改操作返回this,支持方法链调用
    • 例如:sb.append(“a”).append(“b”).append(“c”)

StringBuilder注意事项:

  1. 线程不安全

    • StringBuilder不是线程安全的,不能在多线程环境中直接使用
    • 性能更好,因为没有同步开销
  2. 性能优势

    • 在单线程环境中性能优于StringBuffer
    • 适用于大量字符串拼接操作
  3. 容量管理

    • 与StringBuffer相同的容量管理机制
    • 建议预估数据量,使用ensureCapacity()优化性能
  4. API兼容性

    • StringBuilder与StringBuffer的API基本相同
    • 可以方便地在两者之间切换

通用注意事项:

  1. 索引范围

    • 所有涉及索引的方法(如insert、delete、substring等)都要注意索引范围
    • 索引从0开始,end索引是 exclusive 的
    • 越界会抛出StringIndexOutOfBoundsException
  2. 字符串截取

    • substring()方法返回的是新的String对象,而不是StringBuffer/StringBuilder的子串
    • 修改原对象不会影响已获取的子串
  3. 类型转换

    • toString()方法将内容转换为String对象
    • 转换后与原对象脱离关系
  4. 内存管理

    • 对于大量字符串操作,考虑重用StringBuffer/StringBuilder对象
    • 避免频繁创建新对象
  5. 性能优化

    • 尽量减少扩容操作
    • 对于已知大小的数据,预先设置容量
    • 在循环中避免创建新的StringBuffer/StringBuilder
  6. equals()方法

    • StringBuffer/StringBuilder没有重写equals()方法
    • 比较的是引用而不是内容,应使用toString().equals()
  7. 空值处理

    • append()、insert()等方法可以接受null参数
    • 会将null转换为字符串"null"进行处理
  8. 字符集编码

    • 在涉及编码转换时要注意
    • 建议明确指定字符集
  9. 方法链调用

    • 支持方法链调用,提高代码可读性
    • 例如:sb.append(“a”).insert(1, “b”).delete(2, 3)
  10. 与String的选择

    • 少量字符串操作使用String
    • 大量修改操作使用StringBuffer/StringBuilder
    • 循环中字符串拼接使用StringBuffer/StringBuilder

这些注意事项能帮助你在实际开发中更好地使用StringBuffer和StringBuilder,写出更高效、更可靠的代码。

3. 时间处理相关类

在正式开始之前呢,需要给大家强调一点编程的时间刻度,它是以1970年1月1日00:00:00定位基准时间。一定要注意!不要后期写代码发现哎,怎么不对,就是你的基准时间没有记住

上代码!!!

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

public class DateDemo {
    public static void main(String[] args) throws ParseException {
        // 1. Date类的使用
        System.out.println("=== Date类的基本使用 ===");
        
        // 创建Date对象,表示当前时间
        Date date = new Date();
        System.out.println("当前时间: " + date);
        
        // 获取当前时间的毫秒值
        long time = date.getTime();
        System.out.println("当前时间的毫秒值: " + time);
        
        // 通过毫秒值创建Date对象
        Date dateFromMillis = new Date(time);
        System.out.println("通过毫秒值创建的Date对象: " + dateFromMillis);
        
        // 比较两个日期
        Date date1 = new Date();
        Thread.sleep(1000); // 暂停1秒
        Date date2 = new Date();
        System.out.println("date1是否在date2之前: " + date1.before(date2));
        System.out.println("date1是否在date2之后: " + date1.after(date2));
        
        // 2. SimpleDateFormat类的使用
        System.out.println("\n=== SimpleDateFormat类的使用 ===");
        
        // 创建SimpleDateFormat对象,指定格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        // 格式化日期
        String dateStr = sdf.format(date);
        System.out.println("格式化后的日期: " + dateStr);
        
        // 解析日期字符串
        String dateStr2 = "2023-12-25 12:00:00";
        Date date3 = sdf.parse(dateStr2);
        System.out.println("解析后的日期: " + date3);
        
        // 常用日期格式模式
        System.out.println("\n=== 常用日期格式模式 ===");
        SimpleDateFormat[] sdfArray = {
            new SimpleDateFormat("yyyy-MM-dd"),
            new SimpleDateFormat("yyyy/MM/dd"),
            new SimpleDateFormat("yyyy年MM月dd日"),
            new SimpleDateFormat("HH:mm:ss"),
            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"),
            new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒"),
            new SimpleDateFormat("yy/MM/dd HH:mm"),
            new SimpleDateFormat("E yyyy-MM-dd")
        };
        
        Date now = new Date();
        for (SimpleDateFormat format : sdfArray) {
            System.out.println(format.format(now));
        }
        
        // 3. Calendar类的使用
        System.out.println("\n=== Calendar类的使用 ===");
        
        // 获取Calendar实例
        Calendar calendar = Calendar.getInstance();
        
        // 获取年月日时分秒
        System.out.println("年份: " + calendar.get(Calendar.YEAR));
        System.out.println("月份: " + (calendar.get(Calendar.MONTH) + 1)); // 月份从0开始
        System.out.println("日: " + calendar.get(Calendar.DAY_OF_MONTH));
        System.out.println("小时: " + calendar.get(Calendar.HOUR_OF_DAY));
        System.out.println("分钟: " + calendar.get(Calendar.MINUTE));
        System.out.println("秒: " + calendar.get(Calendar.SECOND));
        
        // 设置日期
        calendar.set(2023, Calendar.DECEMBER, 25);
        System.out.println("设置后的日期: " + sdf.format(calendar.getTime()));
        
        // 日期加减
        calendar.add(Calendar.DAY_OF_MONTH, 10); // 加10天
        System.out.println("加10天后的日期: " + sdf.format(calendar.getTime()));
        
        calendar.add(Calendar.MONTH, -2); // 减2个月
        System.out.println("减2个月后的日期: " + sdf.format(calendar.getTime()));
        
        // 获取特定字段的最大最小值
        System.out.println("一年的最大月份数: " + calendar.getActualMaximum(Calendar.MONTH));
        System.out.println("一个月的最大天数: " + calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        
        // 4. Java 8日期时间API的使用
        System.out.println("\n=== Java 8日期时间API的使用 ===");
        
        // 获取当前日期
        java.time.LocalDate today = java.time.LocalDate.now();
        System.out.println("当前日期: " + today);
        
        // 获取当前时间
        java.time.LocalTime nowTime = java.time.LocalTime.now();
        System.out.println("当前时间: " + nowTime);
        
        // 获取当前日期时间
        java.time.LocalDateTime nowDateTime = java.time.LocalDateTime.now();
        System.out.println("当前日期时间: " + nowDateTime);
        
        // 格式化日期
        java.time.format.DateTimeFormatter formatter = 
            java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        System.out.println("格式化后的日期时间: " + nowDateTime.format(formatter));
        
        // 日期计算
        java.time.LocalDate dateAfter = today.plusDays(10);
        java.time.LocalDate dateBefore = today.minusMonths(2);
        System.out.println("10天后的日期: " + dateAfter);
        System.out.println("2个月前的日期: " + dateBefore);
        
        // 周期性日期
        java.time.temporal.WeekFields weekFields = 
            java.time.temporal.WeekFields.of(java.util.Locale.getDefault());
        System.out.println("当前年份的第几周: " + today.get(weekFields.weekOfWeekBasedYear()));
        System.out.println("当前月份的第几周: " + today.get(weekFields.weekOfMonth()));
    }
}

Date类使用注意事项:

  1. Date类已过时
    • Date类的大部分方法已过时(@Deprecated)
    • 新代码建议使用Java 8的java.time包中的类
  2. 线程安全问题
    • Date类本身是线程安全的
    • 但SimpleDateFormat不是线程安全的,不要作为共享变量
  3. 时区问题
    • Date类不存储时区信息
    • 在处理跨时区数据时要注意时区转换
  4. 精度问题
    • Date类精确到毫秒
    • 在某些场景下可能需要更高或更低的精度

SimpleDateFormat使用注意事项:

  1. 线程安全性
    • SimpleDateFormat不是线程安全的
    • 解决方案:
      a. 每次创建新实例
      b. 使用ThreadLocal
      c. 使用synchronized
      d. Java 8使用DateTimeFormatter(线程安全)
  2. 格式模式
    • yyyy:4位年份
    • MM:月份(01-12)
    • dd:日期(01-31)
    • HH:24小时制(00-23)
    • hh:12小时制(01-12)
    • mm:分钟(00-59)
    • ss:秒(00-59)
    • SSS:毫秒(000-999)
  3. 异常处理
    • parse()方法可能抛出ParseException
    • 需要处理无效的日期格式

Calendar类使用注意事项:

  1. 月份从0开始
    • get(Calendar.MONTH)返回0-11,需要+1才是实际月份
  2. 不可变性
    • Calendar是可变类,修改会影响原对象
    • 需要时可以先创建副本
  3. 性能考虑
    • Calendar比Date性能稍差
    • 频繁操作时考虑使用Java 8的日期时间API

Java 8日期时间API使用注意事项:

  1. 不可变性
    • Java 8日期时间类是不可变的
    • 修改操作会返回新的对象
  2. 时区处理
    • 提供了更好的时区支持
    • 使用ZonedDateTime处理带时区的日期时间
  3. 线程安全
    • 所有类都是线程安全的
    • 无需额外的同步措施
  4. API设计
    • 方法命名更清晰
    • 提供了更多的实用方法

通用注意事项:

  1. 日期格式一致性
    • 确保日期格式与解析格式一致
    • 注意不同地区可能有不同的日期格式
  2. 时区处理
    • 明确时区信息
    • 使用UTC时间作为标准
  3. 性能优化
    • 避免频繁创建日期格式化对象
    • 重用格式化实例(线程安全的情况下)
  4. 边界检查
    • 处理日期加减时注意边界情况
    • 如2月的闰年问题
  5. 国际化支持
    • 考虑不同地区的日期格式
    • 使用Locale类处理地区差异
  6. 日期范围
    • 注意Date类支持的范围
    • 避免使用超出范围的日期
  7. 时间计算
    • 使用专门的日期时间API进行计算
    • 避免手动计算日期差值
  8. 存储和传输
    • 考虑使用时间戳存储日期
    • JSON序列化时注意日期格式

这些注意事项能帮助你在开发中更好地处理日期时间相关的功能,写出更健壮、更可靠的代码。

4.其他常用类

4.1 Math和Random

在平时的开发过程中,其实大家少不了计算,多多少少都会涉及到一些,这里废话不多说,直接通过示例代码去学习相关用法

import java.util.Random;

public class MathRandomDemo {
    public static void main(String[] args) {
        // 1. Math类的基本使用
        System.out.println("=== Math类的基本使用 ===");
        
        // 基本运算
        System.out.println("绝对值: " + Math.abs(-10));        // 10
        System.out.println("向上取整: " + Math.ceil(3.14));     // 4.0
        System.out.println("向下取整: " + Math.floor(3.14));    // 3.0
        System.out.println("四舍五入: " + Math.round(3.14));    // 3
        System.out.println("四舍五入: " + Math.round(3.56));    // 4
        
        // 三角函数
        System.out.println("sin(90°): " + Math.sin(Math.PI/2));  // 1.0
        System.out.println("cos(0°): " + Math.cos(0));            // 1.0
        System.out.println("tan(45°): " + Math.tan(Math.PI/4));   // 1.0
        
        // 对数和指数
        System.out.println("e的2次方: " + Math.exp(2));           // 约7.389
        System.out.println("以e为底的对数: " + Math.log(Math.E));  // 1.0
        System.out.println("以10为底的对数: " + Math.log10(100)); // 2.0
        System.out.println("2的3次方: " + Math.pow(2, 3));       // 8.0
        
        // 其他常用方法
        System.out.println("最大值: " + Math.max(10, 20));        // 20
        System.out.println("最小值: " + Math.min(10, 20));        // 10
        System.out.println("平方根: " + Math.sqrt(16));           // 4.0
        System.out.println("随机数(0-1): " + Math.random());      // 0.0-1.0之间的随机数
        
        // 2. 生成指定范围的随机数
        System.out.println("\n=== 生成指定范围的随机数 ===");
        
        // 生成1-10的随机整数
        int random1 = (int)(Math.random() * 10 + 1);
        System.out.println("1-10的随机数: " + random1);
        
        // 生成50-100的随机整数
        int random2 = (int)(Math.random() * 51 + 50);
        System.out.println("50-100的随机数: " + random2);
        
        // 生成0.0-1.0的随机小数
        double random3 = Math.random();
        System.out.println("0.0-1.0的随机小数: " + random3);
        
        // 生成2.5-5.5的随机小数
        double random4 = Math.random() * 3 + 2.5;
        System.out.println("2.5-5.5的随机小数: " + random4);
        
        // 3. Random类的使用
        System.out.println("\n=== Random类的使用 ===");
        
        // 创建Random对象
        Random random = new Random();
        
        // 生成各种类型的随机数
        System.out.println("nextInt(): " + random.nextInt());           // 任意整数
        System.out.println("nextInt(10): " + random.nextInt(10));      // 0-9的整数
        System.out.println("nextLong(): " + random.nextLong());         // 任意长整型
        System.out.println("nextDouble(): " + random.nextDouble());     // 0.0-1.0的随机小数
        System.out.println("nextFloat(): " + random.nextFloat());       // 0.0-1.0的随机浮点数
        System.out.println("nextBoolean(): " + random.nextBoolean());   // 随机布尔值
        
        // 4. 使用Random生成指定范围的随机数
        System.out.println("\n=== 使用Random生成指定范围的随机数 ===");
        
        // 生成1-10的随机整数
        int random5 = random.nextInt(10) + 1;
        System.out.println("1-10的随机数: " + random5);
        
        // 生成50-100的随机整数
        int random6 = random.nextInt(51) + 50;
        System.out.println("50-100的随机数: " + random6);
        
        // 生成0.0-1.0的随机小数
        double random7 = random.nextDouble();
        System.out.println("0.0-1.0的随机小数: " + random7);
        
        // 生成2.5-5.5的随机小数
        double random8 = random.nextDouble() * 3 + 2.5;
        System.out.println("2.5-5.5的随机小数: " + random8);
        
        // 5. Random的种子设置
        System.out.println("\n=== Random的种子设置 ===");
        
        // 使用固定种子创建Random对象
        Random fixedRandom1 = new Random(100);
        Random fixedRandom2 = new Random(100);
        
        // 相同种子生成的随机数序列相同
        System.out.println("固定种子生成的随机数1: " + fixedRandom1.nextInt());
        System.out.println("固定种子生成的随机数2: " + fixedRandom2.nextInt());
        
        // 6. 使用ThreadLocalRandom(Java 7+)
        System.out.println("\n=== 使用ThreadLocalRandom ===");
        
        // 在多线程环境中使用ThreadLocalRandom
        int threadRandom = java.util.concurrent.ThreadLocalRandom.current().nextInt(1, 11);
        System.out.println("ThreadLocalRandom生成的1-10随机数: " + threadRandom);
        
        // 7. 数学常量
        System.out.println("\n=== 数学常量 ===");
        
        System.out.println("圆周率π: " + Math.PI);
        System.out.println("自然对数e: " + Math.E);
        
        // 8. 高级数学运算
        System.out.println("\n=== 高级数学运算 ===");
        
        // 指数运算
        System.out.println("2的10次方: " + Math.pow(2, 10));  // 1024.0
        
        // 对数运算
        System.out.println("以2为底的对数: " + Math.log(8) / Math.log(2));  // 3.0
        
        // 弧度和角度转换
        double angle = 45;
        double radian = Math.toRadians(angle);
        System.out.println("45度转换为弧度: " + radian);  // 约0.785
        System.out.println("弧度转换为角度: " + Math.toDegrees(radian));  // 45.0
        
        // 9. 数值处理
        System.out.println("\n=== 数值处理 ===");
        
        // 取整方法
        System.out.println("3.14向上取整: " + Math.ceil(3.14));    // 4.0
        System.out.println("3.14向下取整: " + Math.floor(3.14));   // 3.0
        System.out.println("3.14四舍五入: " + Math.round(3.14));   // 3
        System.out.println("3.56四舍五入: " + Math.round(3.56));   // 4
        
        // 绝对值
        System.out.println("-10的绝对值: " + Math.abs(-10));      // 10
        System.out.println("-3.14的绝对值: " + Math.abs(-3.14));   // 3.14
        
        // 符号函数
        System.out.println("10的符号: " + Math.signum(10));      // 1.0
        System.out.println("-10的符号: " + Math.signum(-10));     // -1.0
        System.out.println("0的符号: " + Math.signum(0));        // 0.0
        
        // 10. 随机数应用示例
        System.out.println("\n=== 随机数应用示例 ===");
        
        // 生成验证码
        String captcha = generateCaptcha(6);
        System.out.println("6位验证码: " + captcha);
        
        // 随机打乱数组
        int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        shuffleArray(array);
        System.out.print("打乱后的数组: ");
        for (int num : array) {
            System.out.print(num + " ");
        }
        System.out.println();
        
        // 随机选择元素
        String[] names = {"张三", "李四", "王五", "赵六", "钱七"};
        String randomName = selectRandom(names);
        System.out.println("随机选择的名字: " + randomName);
    }
    
    // 生成指定长度的数字验证码
    private static String generateCaptcha(int length) {
        Random random = new Random();
        StringBuilder captcha = new StringBuilder();
        for (int i = 0; i < length; i++) {
            captcha.append(random.nextInt(10));
        }
        return captcha.toString();
    }
    
    // 打乱数组顺序(Fisher-Yates洗牌算法)
    private static void shuffleArray(int[] array) {
        Random random = new Random();
        for (int i = array.length - 1; i > 0; i--) {
            int index = random.nextInt(i + 1);
            // 交换元素
            int temp = array[index];
            array[index] = array[i];
            array[i] = temp;
        }
    }
    
    // 从数组中随机选择一个元素
    private static <T> T selectRandom(T[] array) {
        Random random = new Random();
        return array[random.nextInt(array.length)];
    }
}

Math类使用注意事项:

  1. 基本数学运算

    • abs():求绝对值
    • ceil():向上取整
    • floor():向下取整
    • round():四舍五入
    • max():求最大值
    • min():求最小值
  2. 三角函数

    • sin():正弦
    • cos():余弦
    • tan():正切
    • 参数是弧度,不是角度
    • 使用Math.toRadians()转换角度到弧度
    • 使用Math.toDegrees()转换弧度到角度
  3. 对数和指数

    • exp():e的x次方
    • log():自然对数
    • log10():以10为底的对数
    • pow():x的y次方
  4. 随机数生成

    • Math.random():生成0.0到1.0的随机double值
    • 生成指定范围随机数的公式:
      • 整数:(int)(Math.random() * (max - min + 1) + min)
      • 小数:Math.random() * (max - min) + min
  5. 数学常量

    • Math.PI:圆周率π
    • Math.E:自然对数e

Random类使用注意事项:

  1. 创建Random对象

    • 默认使用当前时间作为种子
    • 可以指定固定种子:new Random(seed)
  2. 生成随机数

    • nextInt():生成随机整数
    • nextInt(n):生成0到n-1的随机整数
    • nextLong():生成随机长整型
    • nextDouble():生成0.0到1.0的随机double值
    • nextFloat():生成0.0到1.0的随机float值
    • nextBoolean():生成随机布尔值
  3. 生成指定范围随机数

    • 整数:random.nextInt(max - min + 1) + min
    • 小数:random.nextDouble() * (max - min) + min
  4. 种子设置

    • 相同种子生成的随机数序列相同
    • 适用于需要可重复随机数的场景
  5. 线程安全

    • Random类是线程安全的
    • 但在多线程环境下性能可能受影响
    • 高并发场景考虑使用ThreadLocalRandom

ThreadLocalRandom使用注意事项:

  1. 适用场景

    • Java 7引入
    • 适用于多线程环境
    • 性能优于Random
  2. 基本用法

    • 获取实例:ThreadLocalRandom.current()
    • 生成随机数:nextInt(origin, bound)
  3. 优点

    • 线程局部随机数生成
    • 无需同步开销
    • 更好的性能

随机数应用注意事项:

  1. 验证码生成

    • 数字验证码:使用nextInt(10)
    • 字母验证码:随机生成大小写字母
    • 混合验证码:数字和字母组合
  2. 数组随机排序

    • 使用Fisher-Yates洗牌算法
    • 保证每个排列等概率出现
  3. 随机选择

    • 确保概率均等
    • 注意边界条件
  4. 安全性考虑

    • 不要使用Math.random()或Random生成安全相关的随机数
    • 使用SecureRandom生成密码学安全的随机数
  5. 性能优化

    • 避免频繁创建Random对象
    • 重用Random实例
    • 高并发场景使用ThreadLocalRandom
  6. 随机数质量

    • Random使用线性同余算法
    • 随机性可能不够好
    • 对随机性要求高的场景考虑其他算法
  7. 边界处理

    • 注意随机数范围包含边界值
    • 验证随机数是否在预期范围内
  8. 可预测性

    • 固定种子生成的随机数序列可预测
    • 避免在安全相关场景使用固定种子

这些注意事项能帮助你在开发中更好地使用Math和Random类,写出更可靠、更高效的代码。

4.2 File

看见File想到什么了!没错,就是文件,我们平时访问网站的时候上传文件啊,下载文件啊,底层其实都是通过File来使用的

File的作用:读取文件、创建文件、删除文件、修改文件

File类是Java中用于表示文件或目录路径名的抽象表示形式。它提供了操作文件和目录的各种方法,但需要注意的是,File类本身不包含文件内容,只是对文件系统中的文件或目录进行操作。

基本用法

import java.io.File;
import java.io.IOException;

public class FileDemo {
    public static void main(String[] args) {
        // 1. 创建File对象
        // 使用绝对路径创建
        File file1 = new File("C:\\example\\test.txt");
        
        // 使用相对路径创建(相对于当前工作目录)
        File file2 = new File("test.txt");
        
        // 使用父路径和子路径创建
        File dir = new File("C:\\example");
        File file3 = new File(dir, "test.txt");
        
        // 2. 文件/目录操作
        // 创建文件
        try {
            boolean created = file1.createNewFile();
            System.out.println("文件是否创建成功: " + created);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 创建目录
        File newDir = new File("C:\\example\\newdir");
        boolean dirCreated = newDir.mkdir(); // 只能创建一级目录
        System.out.println("目录是否创建成功: " + dirCreated);
        
        File multiDir = new File("C:\\example\\parent\\child");
        boolean multiDirCreated = multiDir.mkdirs(); // 可以创建多级目录
        System.out.println("多级目录是否创建成功: " + multiDirCreated);
        
        // 删除文件或空目录
        boolean deleted = file1.delete();
        System.out.println("是否删除成功: " + deleted);
        
        // 3. 文件/目录信息查询
        File testFile = new File("test.txt");
        
        // 判断是否存在
        System.out.println("文件是否存在: " + testFile.exists());
        
        // 判断是文件还是目录
        System.out.println("是否是文件: " + testFile.isFile());
        System.out.println("是否是目录: " + testFile.isDirectory());
        
        // 获取文件名
        System.out.println("文件名: " + testFile.getName());
        
        // 获取绝对路径
        System.out.println("绝对路径: " + testFile.getAbsolutePath());
        
        // 获取规范路径(处理符号链接等)
        try {
            System.out.println("规范路径: " + testFile.getCanonicalPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 获取父路径
        System.out.println("父路径: " + testFile.getParent());
        
        // 获取文件大小(字节)
        System.out.println("文件大小: " + testFile.length() + " 字节");
        
        // 获取最后修改时间
        System.out.println("最后修改时间: " + testFile.lastModified());
        
        // 4. 目录操作
        File dir2 = new File("C:\\example");
        
        // 列出目录下的文件和子目录
        String[] files = dir2.list();
        System.out.println("目录下的文件和子目录:");
        for (String str : files) {
            System.out.println(str);
        }
        
        // 列出目录下的文件和子目录(返回File对象)
        File[] fileArray = dir2.listFiles();
        System.out.println("\n目录下的文件和子目录(File对象):");
        for (File f : fileArray) {
            System.out.println(f.getName());
        }
        
        // 判断目录是否为空
        System.out.println("目录是否为空: " + (dir2.list().length == 0));
        
        // 重命名文件或目录
        File renamedFile = new File("renamed.txt");
        boolean renamed = testFile.renameTo(renamedFile);
        System.out.println("是否重命名成功: " + renamed);
    }
}

注意事项

  1. 路径分隔符

    • Windows系统使用反斜杠\,但在Java字符串中需要写成\\(因为\是转义字符)
    • 推荐使用正斜杠/,因为它在所有平台上都能工作
    • 也可以使用File.separator常量获取系统默认的路径分隔符
  2. 路径类型

    • 绝对路径:从根目录开始的完整路径
    • 相对路径:相对于当前工作目录的路径
  3. 文件系统差异

    • 不同操作系统(Windows、Linux、Mac等)对文件名的处理可能不同
    • 某些文件名在Windows下是无效的(如<, >, :, ", /, \, |, ?, *
  4. 权限问题

    • 文件操作需要相应的系统权限
    • 某些操作可能会抛出SecurityException
  5. 异常处理

    • 文件操作可能会抛出IOException等异常,应该进行适当的异常处理
  6. 线程安全

    • File类本身是线程安全的,但其操作依赖于底层文件系统,可能不是线程安全的
  7. 临时文件

    • 使用createTempFile()方法创建临时文件时,JVM退出时不会自动删除
    • 需要手动删除临时文件,或者在JVM退出时使用deleteOnExit()方法
  8. 符号链接

    • 在处理符号链接时,注意getCanonicalPath()getAbsolutePath()的区别
    • getCanonicalPath()会解析符号链接,而getAbsolutePath()不会
  9. 文件删除

    • 只能删除空目录,非空目录需要先删除其中的内容
    • 删除操作不会将文件移至回收站,而是直接从文件系统中删除
  10. 文件大小限制

    • 某些文件系统可能对文件大小有限制
    • length()方法返回的是long类型,但某些文件系统可能无法处理大文件
  11. 文件监控

    • File类不提供文件变化监控功能,如需监控文件变化,可以使用WatchService API
  12. 文件内容操作

    • File类不提供读写文件内容的方法,需要使用FileInputStreamFileOutputStreamFileReaderFileWriter等类

通过合理使用File类的方法并注意上述事项,可以有效地进行文件和目录的操作。

4.3 枚举

说起枚举大家可能就有点陌生了,但是我举个例子大家就能明白了,比如我们上学,有班主任、班长、语文课代表等等的身份,如果我们在存储数据的时候输入这些是不是就太麻烦了,我们就可以定义一个枚举用数字来表示对应的value,

基本用法

// 1. 基本枚举定义
public enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// 2. 带有属性和方法的枚举
public enum Planet {
    MERCURY(3.303e+23, 2.4397e6),
    VENUS(4.869e+24, 6.0518e6),
    EARTH(5.976e+24, 6.37814e6),
    MARS(6.421e+23, 3.3972e6);
    
    private final double mass;    // 质量
    private final double radius;  // 半径
    
    // 构造函数必须是私有的
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
    
    // 公共方法
    public double mass() {
        return mass;
    }
    
    public double radius() {
        return radius;
    }
    
    // 计算表面重力
    public double surfaceGravity() {
        return mass / (radius * radius);
    }
}

// 3. 使用枚举
public class EnumTest {
    public static void main(String[] args) {
        // 遍历枚举所有值
        for (Day day : Day.values()) {
            System.out.println(day);
        }
        
        // 使用枚举实例
        Planet earth = Planet.EARTH;
        System.out.println("地球质量: " + earth.mass());
        
        // switch语句中使用枚举
        Day today = Day.MONDAY;
        switch (today) {
            case MONDAY:
                System.out.println("今天是星期一");
                break;
            case FRIDAY:
                System.out.println("今天是星期五");
                break;
            default:
                System.out.println("今天是其他工作日");
        }
        
        // 枚举比较
        if (today == Day.MONDAY) {
            System.out.println("确实星期一");
        }
        
        // 获取枚举名称和值
        System.out.println(today.name()); // 输出: MONDAY
        System.out.println(today.ordinal()); // 输出: 0 (表示在枚举中的位置)
    }
}

注意事项

  1. 构造函数私有化
    • 枚举的构造函数必须是private的,默认就是private的
    • 不能声明为public或protected的构造函数
  2. 继承限制
    • 枚举类不能继承其他类,因为它们已经隐式继承了java.lang.Enum类
    • 但可以实现接口
  3. 线程安全
    • 枚举实例是线程安全的,在Java中,枚举的实现是线程安全的
  4. 序列化安全性
    • 枚举提供了内置的序列化机制,可以防止反序列化时创建新的枚举实例
  5. values()和valueOf()方法
    • 编译器会自动为每个枚举类型添加values()方法,返回枚举的所有值
    • valueOf(String name)方法可以将字符串转换为对应的枚举实例,如果不存在会抛出IllegalArgumentException
  6. 枚举字段和方法
    • 枚举可以有字段、方法和构造函数
    • 枚举字段可以是实例变量或静态变量
  7. switch语句
    • Java 7之前,switch只能用于基本类型和枚举
    • Java 7及以后,switch也可以用于String类型
  8. 单例模式
    • 枚举是实现单例模式的最佳方式,因为它是线程安全的,且由JVM保证不会创建多个实例
  9. 命名规范
    • 枚举常量通常使用大写字母,单词间用下划线分隔
    • 枚举类型名通常使用大写字母,单词间用下划线分隔
  10. 性能考虑
    • 枚举在内存中是单例的,不会创建多个实例
    • 枚举的values()方法每次调用都会创建一个新数组,建议缓存结果

高级用法

这里给大家写一下枚举的高级用法

// 1. 实现接口的枚举
interface Describable {
    String description();
}

public enum Size implements Describable {
    SMALL("小号"),
    MEDIUM("中号"),
    LARGE("大号");
    
    private String description;
    
    Size(String description) {
        this.description = description;
    }
    
    @Override
    public String description() {
        return description;
    }
}

// 2. 使用枚举实现策略模式
public enum Operation {
    PLUS {
        double apply(double x, double y) { return x + y; }
    },
    MINUS {
        double apply(double x, double y) { return x - y; }
    },
    TIMES {
        double apply(double x, double y) { return x * y; }
    },
    DIVIDE {
        double apply(double x, double y) { return x / y; }
    };
    
    abstract double apply(double x, double y);
}

// 3. 带有抽象方法的枚举
public enum TrafficLight {
    RED {
        @Override
        public String getNext() {
            return "GREEN";
        }
    },
    GREEN {
        @Override
        public String getNext() {
            return "YELLOW";
        }
    },
    YELLOW {
        @Override
        public String getNext() {
            return "RED";
        }
    };
    
    public abstract String getNext();
}

ps:这个高级用法看不懂的话可以不用看,后续我会更新一个项目的专栏,里边有一套通用的模板,这里边就会介绍枚举类在项目中的用法

总结

本教程介绍了Java中一些最常用的类,包括字符串处理、包装类、日期时间、集合框架、IO流和异常处理。掌握这些常用类对于Java开发至关重要。在实际开发中,建议多查阅Java官方文档,以获取更详细的信息和更高级的用法。

文章到这里就结束了!如果有哪里写的不对,欢迎各位批评指正 ,也可以私信我或者评论,看到会及时回复


网站公告

今日签到

点亮在社区的每一天
去签到