首先,阅读这篇文章之前呢,我先把这篇文章讲的大纲说一下,主要分为四大类,大家平时在开发的时候,其实也会用得到,分别为包装类、字符串相关类、时间处理相关类、其他常用类。
文章目录
- 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注意事项:
线程安全:
- StringBuffer是线程安全的,所有公共方法都带有synchronized关键字
- 在多线程环境中可以直接使用,但会带来一定的性能开销
性能考虑:
- 由于同步机制,StringBuffer的性能通常比StringBuilder慢10%左右
- 在单线程环境中,优先使用StringBuilder
容量管理:
- StringBuffer的初始容量为16,当内容超过容量时会自动扩容
- 扩容时会产生新的数组并复制旧内容,影响性能
- 对于大容量数据,建议使用ensureCapacity()预先设置容量
方法链调用:
- StringBuffer的修改操作返回this,支持方法链调用
- 例如:sb.append(“a”).append(“b”).append(“c”)
StringBuilder注意事项:
线程不安全:
- StringBuilder不是线程安全的,不能在多线程环境中直接使用
- 性能更好,因为没有同步开销
性能优势:
- 在单线程环境中性能优于StringBuffer
- 适用于大量字符串拼接操作
容量管理:
- 与StringBuffer相同的容量管理机制
- 建议预估数据量,使用ensureCapacity()优化性能
API兼容性:
- StringBuilder与StringBuffer的API基本相同
- 可以方便地在两者之间切换
通用注意事项:
索引范围:
- 所有涉及索引的方法(如insert、delete、substring等)都要注意索引范围
- 索引从0开始,end索引是 exclusive 的
- 越界会抛出StringIndexOutOfBoundsException
字符串截取:
- substring()方法返回的是新的String对象,而不是StringBuffer/StringBuilder的子串
- 修改原对象不会影响已获取的子串
类型转换:
- toString()方法将内容转换为String对象
- 转换后与原对象脱离关系
内存管理:
- 对于大量字符串操作,考虑重用StringBuffer/StringBuilder对象
- 避免频繁创建新对象
性能优化:
- 尽量减少扩容操作
- 对于已知大小的数据,预先设置容量
- 在循环中避免创建新的StringBuffer/StringBuilder
equals()方法:
- StringBuffer/StringBuilder没有重写equals()方法
- 比较的是引用而不是内容,应使用toString().equals()
空值处理:
- append()、insert()等方法可以接受null参数
- 会将null转换为字符串"null"进行处理
字符集编码:
- 在涉及编码转换时要注意
- 建议明确指定字符集
方法链调用:
- 支持方法链调用,提高代码可读性
- 例如:sb.append(“a”).insert(1, “b”).delete(2, 3)
与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类使用注意事项:
- Date类已过时:
- Date类的大部分方法已过时(@Deprecated)
- 新代码建议使用Java 8的java.time包中的类
- 线程安全问题:
- Date类本身是线程安全的
- 但SimpleDateFormat不是线程安全的,不要作为共享变量
- 时区问题:
- Date类不存储时区信息
- 在处理跨时区数据时要注意时区转换
- 精度问题:
- Date类精确到毫秒
- 在某些场景下可能需要更高或更低的精度
SimpleDateFormat使用注意事项:
- 线程安全性:
- SimpleDateFormat不是线程安全的
- 解决方案:
a. 每次创建新实例
b. 使用ThreadLocal
c. 使用synchronized
d. Java 8使用DateTimeFormatter(线程安全)
- 格式模式:
- 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)
- 异常处理:
- parse()方法可能抛出ParseException
- 需要处理无效的日期格式
Calendar类使用注意事项:
- 月份从0开始:
- get(Calendar.MONTH)返回0-11,需要+1才是实际月份
- 不可变性:
- Calendar是可变类,修改会影响原对象
- 需要时可以先创建副本
- 性能考虑:
- Calendar比Date性能稍差
- 频繁操作时考虑使用Java 8的日期时间API
Java 8日期时间API使用注意事项:
- 不可变性:
- Java 8日期时间类是不可变的
- 修改操作会返回新的对象
- 时区处理:
- 提供了更好的时区支持
- 使用ZonedDateTime处理带时区的日期时间
- 线程安全:
- 所有类都是线程安全的
- 无需额外的同步措施
- API设计:
- 方法命名更清晰
- 提供了更多的实用方法
通用注意事项:
- 日期格式一致性:
- 确保日期格式与解析格式一致
- 注意不同地区可能有不同的日期格式
- 时区处理:
- 明确时区信息
- 使用UTC时间作为标准
- 性能优化:
- 避免频繁创建日期格式化对象
- 重用格式化实例(线程安全的情况下)
- 边界检查:
- 处理日期加减时注意边界情况
- 如2月的闰年问题
- 国际化支持:
- 考虑不同地区的日期格式
- 使用Locale类处理地区差异
- 日期范围:
- 注意Date类支持的范围
- 避免使用超出范围的日期
- 时间计算:
- 使用专门的日期时间API进行计算
- 避免手动计算日期差值
- 存储和传输:
- 考虑使用时间戳存储日期
- 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类使用注意事项:
基本数学运算:
- abs():求绝对值
- ceil():向上取整
- floor():向下取整
- round():四舍五入
- max():求最大值
- min():求最小值
三角函数:
- sin():正弦
- cos():余弦
- tan():正切
- 参数是弧度,不是角度
- 使用Math.toRadians()转换角度到弧度
- 使用Math.toDegrees()转换弧度到角度
对数和指数:
- exp():e的x次方
- log():自然对数
- log10():以10为底的对数
- pow():x的y次方
随机数生成:
- Math.random():生成0.0到1.0的随机double值
- 生成指定范围随机数的公式:
- 整数:(int)(Math.random() * (max - min + 1) + min)
- 小数:Math.random() * (max - min) + min
数学常量:
- Math.PI:圆周率π
- Math.E:自然对数e
Random类使用注意事项:
创建Random对象:
- 默认使用当前时间作为种子
- 可以指定固定种子:new Random(seed)
生成随机数:
- nextInt():生成随机整数
- nextInt(n):生成0到n-1的随机整数
- nextLong():生成随机长整型
- nextDouble():生成0.0到1.0的随机double值
- nextFloat():生成0.0到1.0的随机float值
- nextBoolean():生成随机布尔值
生成指定范围随机数:
- 整数:random.nextInt(max - min + 1) + min
- 小数:random.nextDouble() * (max - min) + min
种子设置:
- 相同种子生成的随机数序列相同
- 适用于需要可重复随机数的场景
线程安全:
- Random类是线程安全的
- 但在多线程环境下性能可能受影响
- 高并发场景考虑使用ThreadLocalRandom
ThreadLocalRandom使用注意事项:
适用场景:
- Java 7引入
- 适用于多线程环境
- 性能优于Random
基本用法:
- 获取实例:ThreadLocalRandom.current()
- 生成随机数:nextInt(origin, bound)
优点:
- 线程局部随机数生成
- 无需同步开销
- 更好的性能
随机数应用注意事项:
验证码生成:
- 数字验证码:使用nextInt(10)
- 字母验证码:随机生成大小写字母
- 混合验证码:数字和字母组合
数组随机排序:
- 使用Fisher-Yates洗牌算法
- 保证每个排列等概率出现
随机选择:
- 确保概率均等
- 注意边界条件
安全性考虑:
- 不要使用Math.random()或Random生成安全相关的随机数
- 使用SecureRandom生成密码学安全的随机数
性能优化:
- 避免频繁创建Random对象
- 重用Random实例
- 高并发场景使用ThreadLocalRandom
随机数质量:
- Random使用线性同余算法
- 随机性可能不够好
- 对随机性要求高的场景考虑其他算法
边界处理:
- 注意随机数范围包含边界值
- 验证随机数是否在预期范围内
可预测性:
- 固定种子生成的随机数序列可预测
- 避免在安全相关场景使用固定种子
这些注意事项能帮助你在开发中更好地使用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);
}
}
注意事项
路径分隔符:
- Windows系统使用反斜杠
\
,但在Java字符串中需要写成\\
(因为\
是转义字符) - 推荐使用正斜杠
/
,因为它在所有平台上都能工作 - 也可以使用
File.separator
常量获取系统默认的路径分隔符
- Windows系统使用反斜杠
路径类型:
- 绝对路径:从根目录开始的完整路径
- 相对路径:相对于当前工作目录的路径
文件系统差异:
- 不同操作系统(Windows、Linux、Mac等)对文件名的处理可能不同
- 某些文件名在Windows下是无效的(如
<
,>
,:
,"
,/
,\
,|
,?
,*
)
权限问题:
- 文件操作需要相应的系统权限
- 某些操作可能会抛出
SecurityException
异常处理:
- 文件操作可能会抛出
IOException
等异常,应该进行适当的异常处理
- 文件操作可能会抛出
线程安全:
- File类本身是线程安全的,但其操作依赖于底层文件系统,可能不是线程安全的
临时文件:
- 使用
createTempFile()
方法创建临时文件时,JVM退出时不会自动删除 - 需要手动删除临时文件,或者在JVM退出时使用
deleteOnExit()
方法
- 使用
符号链接:
- 在处理符号链接时,注意
getCanonicalPath()
和getAbsolutePath()
的区别 getCanonicalPath()
会解析符号链接,而getAbsolutePath()
不会
- 在处理符号链接时,注意
文件删除:
- 只能删除空目录,非空目录需要先删除其中的内容
- 删除操作不会将文件移至回收站,而是直接从文件系统中删除
文件大小限制:
- 某些文件系统可能对文件大小有限制
length()
方法返回的是long
类型,但某些文件系统可能无法处理大文件
文件监控:
- File类不提供文件变化监控功能,如需监控文件变化,可以使用
WatchService
API
- File类不提供文件变化监控功能,如需监控文件变化,可以使用
文件内容操作:
- File类不提供读写文件内容的方法,需要使用
FileInputStream
、FileOutputStream
、FileReader
、FileWriter
等类
- File类不提供读写文件内容的方法,需要使用
通过合理使用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 (表示在枚举中的位置)
}
}
注意事项
- 构造函数私有化:
- 枚举的构造函数必须是private的,默认就是private的
- 不能声明为public或protected的构造函数
- 继承限制:
- 枚举类不能继承其他类,因为它们已经隐式继承了java.lang.Enum类
- 但可以实现接口
- 线程安全:
- 枚举实例是线程安全的,在Java中,枚举的实现是线程安全的
- 序列化安全性:
- 枚举提供了内置的序列化机制,可以防止反序列化时创建新的枚举实例
- values()和valueOf()方法:
- 编译器会自动为每个枚举类型添加values()方法,返回枚举的所有值
- valueOf(String name)方法可以将字符串转换为对应的枚举实例,如果不存在会抛出IllegalArgumentException
- 枚举字段和方法:
- 枚举可以有字段、方法和构造函数
- 枚举字段可以是实例变量或静态变量
- switch语句:
- Java 7之前,switch只能用于基本类型和枚举
- Java 7及以后,switch也可以用于String类型
- 单例模式:
- 枚举是实现单例模式的最佳方式,因为它是线程安全的,且由JVM保证不会创建多个实例
- 命名规范:
- 枚举常量通常使用大写字母,单词间用下划线分隔
- 枚举类型名通常使用大写字母,单词间用下划线分隔
- 性能考虑:
- 枚举在内存中是单例的,不会创建多个实例
- 枚举的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官方文档,以获取更详细的信息和更高级的用法。
文章到这里就结束了!如果有哪里写的不对,欢迎各位批评指正 ,也可以私信我或者评论,看到会及时回复