1.包装类
1.1 基本数据类型的包装类
1.2 Number类
Number 类是抽象类,因此它的抽象方法,所有子类都需要提供实现。Number 类提供了抽象方法:intValue()、longValue()、floatValue()、doubleValue(),意味着所有的“数字型”包装类都可以互相转型。
【示例】初识包装类
public class WrapperClassTest {
public static void main(String[ ] args) {
Integer i = new Integer(10); //从 java9 开始被废弃
Integer j = Integer.valueOf(50); //官方推荐
}
}
示例内存分析如图所示:
1.3 自动拆箱和装箱
自动装箱(autoboxing)和拆箱(unboxing):将基本数据类型和包装类自动转换。
【示例】自动装箱
Integer i = 100;//自动装箱
//相当于编译器自动为您作以下的语法编译:
Integer i = Integer.valueOf(100);//调用的是 valueOf(100),而不是 new Integer(100)
【示例】自动拆箱
Integer i = 100;
int j = i;//自动拆箱
//相当于编译器自动为您作以下的语法编译:
int j = i.intValue();
1.4 包装类的缓存问题
整型、char类型所对应的包装类,在自动装箱时,对于-128~127之间的值会进行缓存处理,其目的是提高效率。
缓存原理为:如果数据在-128~127这个区间,那么在类加载时就已经为该区间的每个数值创建了对象,并将这256个对象存放到一个名为cache的数组中。每当自动装箱过程发生时(或者手动调用valueOf()时),就会先判断数据是否在该区间,如果在则直接获取数组中对应的包装类对象的引用,如果不在该区间,则会通过new调用包装类的构造方法来创建对象。
【示例】Integer 类相关源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
- IntegerCache类为Integer类的一个静态内部类,仅供Integer类使用。
- 一般情况下 IntegerCache.low为-128,IntegerCache.high为127
【示例】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的,这个过程会在类加载时完成。
总结
- 自动装箱调用的是 valueOf()方法,而不是 new Integer()方法。
- 自动拆箱调用的 xxxValue()方法。
- 包装类在自动装箱时为了提高效率,对于-128~127 之间的值会进行缓存处理。超过范围后,对象之间不能再使用==进行数值的比较,而是使用 equals 方法。
2.字符串相关类
String 类代表不可变的字符序列
StringBuilder 类和 StringBuffer 类代表可变字符序列。
2.1 String 类源码分析
String 类对象代表不可变的 Unicode 字符序列,因此我们可以将 String 对象称为“不可变对象”。 那什么叫做“不可变对象”呢?指的是对象内部的成员变量的值无法再改变。
我们打开 String 类的源码,如图所示:
我们发现字符串内容全部存储到 value[ ]数组中,而变量 value 是 final 类型的,也就是常量(即只能被赋值一次)。 这就是“不可变对象”的典型定义方式。
在遇到字符串常量之间的拼接时,编译器会做出优化,即在编译期间就会完成字符串的拼接。因此,在使用==进行 String 对象之间的比较时,我们要特别注意,如示例所示。
【示例】字符串常量拼接时的优化
public class TestString2 {
public static void main(String[ ] args) {
//编译器做了优化,直接在编译的时候将字符串进行拼接
String str1 = "hello" + " java";//相当于 str1 = "hello java";
String str2 = "hellojava";
System.out.println(str1 == str2);//true
String str3 = "hello";
String str4 = " java";
//编译的时候不知道变量中存储的是什么,所以没办法在编译的时候优化
String str5 = str3 + str4;
System.out.println(str2 == str5);//false
}
}
2.2 StringBuffer 和 StringBuilder 可变字符序列
StringBuffer 和 StringBuilder 都是可变的字符序列。
- StringBuffer 线程安全,做线程同步检查, 效率较低。
- StringBuilder 线程不安全,不做线程同步检查,因此效率较高。建议采用该类。
【示例】StringBuffer/StringBuilder 基本用法
public class TestStringBufferAndBuilder{
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,下面的方法同样适用 StringBuilder*/
StringBuffer sb2 = new StringBuffer("Java");
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.3 不可变和可变字符序列使用陷阱
String 一经初始化后,就不会再改变其内容了。对 String 字符串的操作实际上是对其副本(原始拷贝)的操作,原来的字符串一点都没有改变。比如:
String s ="a"; 创建了一个字符串
s = s+“b”; 实际上原来的"a"字符串对象已经丢弃了,现在又产生了另一个字符串s+“b”(也就是"ab")。 如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的时间和空间性能,甚至会造成服务器的崩溃。
相反,StringBuilder 和 StringBuffer 类是对原字符串本身操作的,可以对字符串进行修改而不产生副本拷贝或者产生少量的副本。因此可以在循环中使用。
【示例】String 和 StringBuilder 在字符串频繁修改时的效率测试
public class Test {
public static void main(String[ ] args) {
/**使用 String 进行字符串的拼接*/
String str8 = "";
long num1 = Runtime.getRuntime().freeMemory();//获取系统剩余内存空间
long time1 = System.currentTimeMillis();//获取系统的当前时间
for (int i = 0; i < 5000; i++) {
str8 = str8 + i;//相当于产生了 5000 个对象
}
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));
}
}
执行结果如图所示:
3.时间处理相关类
“时间如流水,一去不复返”,时间是一维的。所以,我们需要一把刻度尺来表达和度量时间。在计算机世界,我们把 1970 年 1 月 1 日 00:00:00 定为基准时间,每个度量单位是毫秒(1 秒的千分之一),如图所示。
我们用 long 类型的变量来表示时间,从基准时间前后几亿年都能表示。
这个“时刻数值”是所有时间类的核心值,年月日都是根据这个“数值”计算出来的。
3.1 Date 时间类(java.util.Date)
【示例】Date 类的使用
System.currentTimeMillis():时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数
long nowNum = System.currentTimeMillis(); //当前时刻对应的毫秒数
Date d = new Date(); //当前时刻的对象
System.out.println(d.getTime()); //返回时间对应的毫秒数
Date d2 = new Date(1000L * 3600 * 24 * 365 * 150); //距离 1970年 150 年
System.out.println(d2);
3.2 DateFormat 类和 SimpleDateFormat 类
DateFormat 类的作用
把时间对象转化成指定格式的字符串。反之,把指定格式的字符串转化成时间对象。
DateFormat 是一个抽象类,一般使用它的的子类 SimpleDateFormat 类来实现。
【示例】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 = "2049-10-1";
Date date = s2.parse(time);
System.out.println("date1: " + date);
time = "2049-10-1 20:15:30";
date = s1.parse(time);
System.out.println("date2: " + date);
}
}
字母 | 日期或时间元素 | 表示 | 示例 |
---|---|---|---|
G | Era 标志符 | Text | AD |
y | 年 | Year | 1996; 96 |
M | 年中的月份 | Month | July; Jul; 07 |
w | 年中的周数 | Number | 27 |
W | 月份中的周数 | Number | 2 |
D | 年中的天数 | Number | 189 |
d | 月份中的天数 | Number | 10 |
F | 月份中的星期 | Number | 2 |
E | 星期中的天数 | Text | Tuesday; Tue |
a | Am/pm 标记 | Text | PM |
H | 一天中的小时数(0-23) | Number | 0 |
k | 一天中的小时数(1-24) | Number | 24 |
K | am/pm | 中的小时数(0-11) | Number |
h | am/pm 中的小时数(1-12) | Number | 12 |
m | 小时中的分钟数 | Number | 30 |
s | 分钟中的秒数 | Number | 55 |
S | 毫秒数 | Number | 978 |
z | 时区 | General time zone | Pacific Standard Time; PST; GMT-08:00 |
Z | 时区 | RFC 822 time zone | 0800 |
【示例】获取今天时本年度第几天
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.3 Calendar 日历类
Calendar 类是一个抽象类,为我们提供了关于日期计算的功能,比如:年、月、日、时、分、秒的展示和计算。
获取Calendar实例的方法
- 使用Calendar.getInstance()方法
- 调用它的子类GregorianCalendar的构造器。
注意月份的表示,一月是 0,二月是 1,以此类推,12 月是 11。 因为大多数人习惯于使用单词而不是使用数字来表示月份,这样程序也许更易读,父类 Calendar 使用常量来表示月份:JANUARY、FEBRUARY 等等。
【示例】GregorianCalendar 类和 Calendar 类的使用
import java.util.*;
public class TestCalendar {
public static void main(String[ ] args) {
// 得到相关日期元素
GregorianCalendar calendar = new GregorianCalendar(2049, 9, 1, 22, 10, 50);
int year = calendar.get(Calendar.YEAR); // 打印:2049
int month = calendar.get(Calendar.MONTH); // 打印:9
int day = calendar.get(Calendar.DAY_OF_MONTH); // 打印:1
int day2 = calendar.get(Calendar.DATE); // 打印:1
// 日:Calendar.DATE 和 Calendar.DAY_OF_MONTH 同义
int date = calendar.get(Calendar.DAY_OF_WEEK); // 打印:1
// 星期几 这里是: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, 2049);
calendar2.set(Calendar.MONTH, Calendar.OCTOBER); // 月份数:0-11
calendar2.set(Calendar.DATE, 1);
calendar2.set(Calendar.HOUR_OF_DAY, 10);
calendar2.set(Calendar.MINUTE, 20);
calendar2.set(Calendar.SECOND, 23);
printCalendar(calendar2);
// 日期计算
GregorianCalendar calendar3 = new GregorianCalendar(2049, 9, 1, 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());
}
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);
}
}
【示例】Calendar.getInstance()
@Test
public void test3() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
//产生
Calendar c = Calendar.getInstance(); //当前时间new Date()
//得到field
System.out.println(c.get(Calendar.YEAR)); //得到年
System.out.println(c.get(Calendar.MONTH)); //得到月 传统日期月从0开始,周从周日=1开始
System.out.println(c.get(Calendar.DAY_OF_MONTH));
System.out.println(c.get(Calendar.HOUR_OF_DAY));
//设置field
c.set(Calendar.MONTH, 0); //传统日期月从0开始
System.out.println(sdf.format(c.getTime()));//得到date
//减3个月
c.add(Calendar.MONTH, -3);
System.out.println(sdf.format(c.getTime()));//得到date
//创建1970-1-1 8:0:0.0时间
//方式1:
String dateStr = "1970-01-1 08:0:0.0";
//把字符串解析成日期,string->date
Date date1 = sdf.parse(dateStr);
System.out.println(date1.getTime());
//方式2
Calendar c2 = Calendar.getInstance();
c2.set(1970, 0, 1,8,0,0);
c2.set(Calendar.MILLISECOND, 0);
System.out.println(sdf.format(c2.getTime()));
//当前时间的4周后
Calendar c3 = Calendar.getInstance(); //当前时间
c3.add(Calendar.WEEK_OF_YEAR, 5);
System.out.println(sdf.format(c3.getTime()));
}
3.4 JDK8新日期API
Java 8中引入的java.time API 已经纠正了过去的缺陷,将来很长一段时间内它都会为我们服务。
Java 8 吸收了 Joda-Time 的精华,以一个新的开始为 Java 创建优秀的 API。新的 java.time 中包含了所有关于本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)和持续时间(Duration)的类。历史悠久的 Date 类新增了 toInstant() 方法,用于把 Date 转换成新的表示形式。这些新增的本地化时间日期 API 大大简化了日期时间和本地化的管理。
- java.time – 包含值对象的基础包
- java.time.chrono – 提供对不同的日历系统的访问
- java.time.format – 格式化和解析时间和日期
- java.time.temporal – 包括底层框架和扩展特性
- java.time.zone – 包含时区支持的类
3.4.1 LocalDateTime
LocalDate、 LocalTime、 LocalDateTime 类是其中较重要的几个类,它们的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的本地日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
- LocalDate代表IOS格式(yyyy-MM-dd)的日期,可以存储 生日、纪念日等日期。
- LocalTime表示一个时间,而不是日期。
- LocalDateTime是用来表示日期和时间的, 这是一个最常用的类之一。
注: ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法,也就是公历。
方法 | 描述 |
---|---|
now() / * now(ZoneId zone) | 静态方法, 根据当前时间创建对象/指定时区的对象 |
of() | 静态方法, 根据指定日期/时间创建对象 |
getDayOfMonth()/getDayOfYear() | 获得月份天数(1-31) /获得年份天数(1-366) |
getDayOfWeek() | 获得星期几(返回一个 DayOfWeek 枚举值) |
getMonth() | 获得月份, 返回一个 Month 枚举值 |
getMonthValue() / getYear() | 获得月份(1-12) /获得年份 |
getHour()/getMinute()/getSecond() | 获得当前对象对应的小时、 分钟、 秒 |
withDayOfMonth()/withDayOfYear()/ withMonth()/withYear() | 将月份天数、 年份天数、 月份、 年份修改为指定的值并返回新的对象 |
plusDays(), plusWeeks(), plusMonths(), plusYears(),plusHours() | 向当前对象添加几天、 几周、 几个月、 几年、 几小时 |
minusMonths() / minusWeeks()/ minusDays()/minusYears()/minusHours() | 从当前对象减去几月、 几周、 几天、 几年、 几小时 |
3.4.2 Instant
- Instant:时间线上的一个瞬时点。 这可能被用来记录应用程序中的事件时间戳。
- 在处理时间和日期的时候,我们通常会想到年,月,日,时,分,秒。然而,这只是时间的一个模型,是面向人类的。第二种通用模型是面向机器的,或者说是连续的。在此模型中,时间线中的一个点表示为一个很大的数,这有利于计算机处理。 在UNIX中,这个数从1970年开始,以秒为的单位;同样的,在Java中,也是从1970年开始,但以毫秒为单位。
- java.time包通过值类型Instant提供机器视图,不提供处理人类意义上的时间单位。 Instant表示时间线上的一点,而不需要任何上下文信息,例如,时区。概念上讲, 它只是简单的表示自1970年1月1日0时0分0秒( UTC)开始的秒数。 因为java.time包是基于纳秒计算的,所以Instant的精度可以达到纳秒级。
- (1 ns = 10-9 s) 1秒 = 1000毫秒 =106微秒=109纳秒
方法 | 描述 |
---|---|
now() | 静态方法, 返回默认UTC时区的Instant类的对象 |
ofEpochMilli(long epochMilli) | 静态方法 数之后的,Instant 返回在类的对象 1970-01-01 00:00:00基础上加上指定毫秒 |
atOffset(ZoneOffset offset) | 结合即时的偏移来创建一个 OffsetDateTime |
toEpochMilli() | 返回1970-01-01 00:00:00到当前时间的毫秒数, 即为时间戳 |
**注意:**时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数
3.4.3 DateTimeFormatter
java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:
- 预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
- 本地化相关的格式。如: ofLocalizedDateTime(FormatStyle.LONG)
- 自定义的格式。如: ofPattern(“yyyy-MM-dd hh:mm:ss”)
方 法 | 描 述 |
---|---|
ofPattern(String pattern) | 静 态 方 法 , 返 回 一 个 指 定 字 符 串 格 式 的 DateTimeFormatter |
format(TemporalAccessor t) | 格式化一个日期、 时间, 返回字符串 |
parse(CharSequence text) | 将指定格式的字符序列解析为一个日期、 时间 |
@Test
public void test5() throws ParseException {
//得到当前时间
//LocalDateTime:(UTC)日期时间,理解成Date + Calander
//LocalDate:日期
//LocalTime:时间
LocalDateTime l1 = LocalDateTime.now();
System.out.println("l1 = " + l1);
//得到field
System.out.println(l1.getMonth().getValue()); //新时间api,月从1开始
System.out.println(l1.getYear());
System.out.println(l1.get(ChronoField.HOUR_OF_DAY)); //时
//设置field,支持chain方法链,设置1月1日
LocalDateTime l2 = l1.withMonth(1).withDayOfMonth(1);
System.out.println(l2);
//加3个月,加3周
// l2.plusMonths(3).plus(3, ChronoUnit.WEEKS)
LocalDateTime l3 = l2.plusMonths(3).plusWeeks(3);
System.out.println(l3);
//减3月
LocalDateTime l4 = l3.minusMonths(3);
System.out.println(l4);
//创建1970-1-1 8:0:0.0时间
LocalDateTime l5 = LocalDateTime.of(1970, 1, 1, 0, 0, 0, 0);
System.out.println(l5);
//时间戳操作 Instant
Instant i1 = l5.toInstant(ZoneOffset.UTC); //得到utc时间的Instant
System.out.println(i1.toEpochMilli());
// ZoneOffset.getAvailableZoneIds().forEach(s -> System.out.println(s));
LocalDateTime l6 = LocalDateTime.of(1970, 1, 1, 8, 0, 0, 0);
Instant i2 = l6.toInstant(ZoneOffset.ofHours(8)); //得到东8区的Instant
System.out.println(i2.toEpochMilli()); //utc时间的1970.1.1.0点,北京时间的8点,是时间戳的0
//格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒.SSS");
System.out.println(formatter.format(LocalDateTime.now()));
// formatter.parse() //解析一个时间
}
4.其他常用类
4.1 Math 类
【示例】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.2 System和Random 类
System
- System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。该类位于java.lang包。
- 由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的, 所以也可以很方便的进行调用。
- 成员变量
- System类内部包含in、 out和err三个成员变量,分别代表标准输入流(键盘输入),标准输出流(显示器)和标准错误输出流(显示器)。
- 成员方法
- native long currentTimeMillis():该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。
- void exit(int status):该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表异常退出。 使用该方法可以在图形界面编程中实现程序的退出功能等。
- void gc():该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中垃圾回收算法的实现以及系统执行时的情况。
- String getProperty(String key):该方法的作用是获得系统中属性名为key的属性对应的值。系统中常见的属性名以及属性的作用如下表所示:
public class SystemDemo1 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in); //得到用户控制台输入
System.out.println("请输入i,输入0退出");
int i = in.nextInt();
if (i == 0) {
System.exit(0);
}
System.out.println("i=" + i);
}
@Test
public void test1() {
Scanner in = new Scanner(System.in); //得到用户控制台输入
System.out.println("请输入i,输入0退出");
int i = in.nextInt();
if (i == 0) {
System.exit(0);
}
System.out.println("i=" + i);
}
@Test
public void test2() {
System.out.println(System.currentTimeMillis());
}
@Test
public void test3() {
String javaVersion = System.getProperty("java.version");
System.out.println("java的version:" + javaVersion);
String javaHome = System.getProperty("java.home");
System.out.println("java的home:" + javaHome);
String osName = System.getProperty("os.name");
System.out.println("os的name:" + osName);
String osVersion = System.getProperty("os.version");
System.out.println("os的version:" + osVersion);
String userName = System.getProperty("user.name");
System.out.println("user的name:" + userName);
String userHome = System.getProperty("user.home");
System.out.println("user的home:" + userHome);
String userDir = System.getProperty("user.dir");
System.out.println("user的dir:" + userDir);
}
}
Random
public void addShutdownHook(Thread hook):捕获jvm退出时间
import java.util.Random;
public class TestRandom {
public static void main(String[ ] args) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("捕获了jvm退出--释放资源");
}
}));
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));
}
}
4.3 File 类
File 类的基本用法
java.io.File 类:代表文件和目录,用于:读取文件、创建文件、删除文件、修改文件。
【示例】使用 File 类创建文件
File 类的常见构造方法:public File(String pathname)
以 pathname 为路径创建 File 对象,如果 pathname 是相对路径,则默认的当前路径在系统属性 user.dir 中存储。
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();
}
}
user.dir 就是本项目的目录。上面代码执行后,在本项目和 D 盘下都生成了新的文件。
1.通过 File 对象可以访问文件的属性:
2.通过 File 对象创建空文件或目录(在该对象所指的文件或目录不存在的情况下)
4.4 BigInteger和BigDecimal
BigInteger
- Integer类作为int的包装类,能存储的最大整型值为231-1, Long类也是有限的,最大为263-1。 如果要表示再大的整数,不管是基本数据类型还是他们的包装类都无能为力,更不用说进行运算了。
- java.math包的BigInteger可以表示不可变的任意精度的整数。 BigInteger 提供所有 Java 的基本整数操作符的对应物,并提供java.lang.Math 的所有相关方法。另外, BigInteger 还提供以下运算:模算术、 GCD 计算、质数测试、素数生成、位操作以及一些其他操作。
- 构造器 BigInteger(String val): 根据字符串构建BigInteger对象
- 常用方法
- public BigInteger abs():返回此 BigInteger 的绝对值的 BigInteger。
- BigInteger add(BigInteger val) :返回其值为 (this + val) 的 BigInteger
- BigInteger subtract(BigInteger val) :返回其值为 (this - val) 的 BigInteger
- BigInteger multiply(BigInteger val) :返回其值为 (this * val) 的 BigInteger
- BigInteger divide(BigInteger val) :返回其值为 (this / val) 的 BigInteger。整数相除只保留整数部分。
- BigInteger remainder(BigInteger val) :返回其值为 (this % val) 的 BigInteger。
- BigInteger[] divideAndRemainder(BigInteger val):返回包含 (this / val) 后跟(this % val) 的两个 BigInteger 的数组。
- BigInteger pow(int exponent) :返回其值为 (thisexponent) 的 BigInteger。
BigDecimal
- 一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中,要求数字精度比较高,故用到java.math.BigDecimal类。
- BigDecimal类支持不可变的、任意精度的有符号十进制定点数。
- 构造器
- public BigDecimal(double val)
- public BigDecimal(String val)
- 常用方法
- public BigDecimal add(BigDecimal augend)
- public BigDecimal subtract(BigDecimal subtrahend)
- public BigDecimal multiply(BigDecimal multiplicand)
- public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
public class BigIntegerDemo {
@Test
public void test1() {
System.out.println(Integer.MAX_VALUE);
Integer i1 = 2147483647;
// Integer i2 = 2147483648;
BigInteger i3= new BigInteger("10");
System.out.println(i3);
BigInteger i4 = new BigInteger("15");
BigInteger i5 = i3.add(i4);
System.out.println(i5);
BigInteger[] arrs = i3.divideAndRemainder(new BigInteger("3"));
System.out.println(Arrays.toString(arrs));
}
}
4.5 正则表达式
正则表达式,又称规则表达式,(Regular Expression,在代码中常简写为regex、regexp或RE),是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。
简单来说,正则表达式就是一个很牛逼的字符串处理工具。
正则在线工具 : https://c.runoob.com/front-end/854/?optionGlobl=global
4.5.1 String类中的正则表达式
public class StringRegexDemo1 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (true) {
System.out.println("请输入qq");
String s1 = in.nextLine();
//匹配s1中是否是6-12位数字
System.out.println(s1.matches("^[0-9]{6,12}$"));
}
}
@Test
public void test1() {
String s = "Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的高级程序设计语言Java 可运行于多个平台,如 windows,Mac OS 及其他多种 UNIX 版本的系统";
// s = s.replaceAll("[a-zA-Z]{1,}", "hello");
s = s.replaceAll("[a-zA-Z]+", "hello");
s = s.replaceAll("\\d+", "world");
System.out.println(s);
}
@Test
public void test2() {
String s1 = "aaa,bbb,123,456,hello,word";
String[] arrs = s1.split(",");
for (int i = 0; i < arrs.length; i++) {
System.out.println(arrs[i]);
}
}
@Test
public void test3() {
String s2 = "hello12333world4567welcome34to4beijing";
String[] arrs2 = s2.split("\\d+");
for (int i = 0; i < arrs2.length; i++) {
System.out.println(arrs2[i]);
}
}
}
4.5.2 正则表达式常用类
(1) Pattern类
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
- compile():通过正则得到一个Pattern
- matchers():方便正则表达式只是用一次,进行匹配
- matcher():产生Matcher对象,多次正则匹配推荐使用
(2) Matcher类
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
- 索引方法
索引方法提供了有用的索引值,精确表明输入字符串中在哪能找到匹配:
- public int start()返回以前匹配的初始索引。
- public int start(int group) 返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引
- public int end()返回最后匹配字符之后的偏移量。
- public int end(int group)返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。
- 查找方法:
查找方法用来检查输入字符串并返回一个布尔值,表示是否找到该模式:
- public boolean lookingAt() 尝试将从区域开头开始的输入序列与该模式匹配。
- public boolean find()尝试查找与该模式匹配的输入序列的下一个子序列。
- public boolean find(int start)重置此匹配器,然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。
- public boolean matches()尝试将整个区域与模式匹配。
- group()返回上一个匹配项匹配的输入子序列。
- group(int group)返回上一个匹配操作期间给定组捕获的输入子序列。
- 替换方法
替换方法是替换输入字符串里文本的方法:
- public Matcher appendReplacement(StringBuffer sb, String replacement)实现非终端添加和替换步骤。
- public StringBuffer appendTail(StringBuffer sb)实现终端添加和替换步骤。
- public String replaceAll(String replacement)替换模式与给定替换字符串相匹配的输入序列的每个子序列。
- public String replaceFirst(String replacement) 替换模式与给定替换字符串匹配的输入序列的第一个子序列。
- public static String quoteReplacement(String s)返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给Matcher类的appendReplacement 方法一个字面字符串一样工作。
public class RegexDemo1 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
//
Pattern pattern = Pattern.compile("^[0-9]{5,12}$"); //正则的编译对象
while (true) {
System.out.println("请输入qq");
String s1 = in.nextLine();
Matcher matcher = pattern.matcher(s1);
System.out.println(matcher.matches()); //全部匹配
// System.out.println(s1.matches("^[0-9]{5,12}$"));
}
}
@Test
public void test1() {
//正则编译对象,只匹配一次可以使用matches方法
System.out.println(Pattern.matches("^[0-9]{5,12}$", "77546313")); //正则是否全部匹配
System.out.println(Pattern.matches("^[0-9]{5,12}$", "123456")); //正则是否全部匹配
System.out.println(Pattern.matches("^[0-9]{5,12}$", "333333")); //正则是否全部匹配
Pattern pattern = Pattern.compile("^[0-9]{5,12}$");
pattern.matcher("77546313").matches();
pattern.matcher("123456").matches();
pattern.matcher("333333").matches();
}
@Test
public void test2() {
//全部匹配
Pattern pattern = Pattern.compile("\\d{5,12}");
Matcher matcher = pattern.matcher("12345a34343434");
System.out.println(matcher.matches()); //全部匹配
}
@Test
public void test3() {
//全部匹配
Pattern pattern = Pattern.compile("\\d{5,12}");
Matcher matcher = pattern.matcher("12345a34343434abc9878989");
while (matcher.find()) {//查找匹配,如果匹配返回ture
String target = matcher.group();//得到上一次匹配的子序列
System.out.println(target + "-" + matcher.start() + "-" + matcher.end()); //start得到上一个匹配的索引开始
}
}
@Test
public void test4() {
String content =
"Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的高级程序设计语言。\n"
+ "Java 可运行于多个平台,如 windows,Mac OS 及其他多种 UNIX 版本的系统。\n"
+ "本教程通过简单的实例将让大家更好的了解 Java 编程语言。\n"
+ "移动操作系统 Android 大部分的代码采用 Java 编程语言编程。";
Pattern pattern = Pattern.compile("([a-zA-Z]+) | (\\d+)");
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
String target = matcher.group(2); //0:全部的正则捕获组,1:的一个小括号的正则补货组
System.out.println(target);
}
}
@Test
public void test6() {
String content =
"Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的高级程序设计语言。\n"
+ "Java 可运行于多个平台,如 windows,Mac OS 及其他多种 UNIX 版本的系统。\n"
+ "本教程通过简单的实例将让大家更好的了解 Java 编程语言。\n"
+ "移动操作系统 Android 大部分的代码采用 Java 编程语言编程。";
//创建一个Pattern对象
Pattern pattern = Pattern.compile("Java");
//创建一个匹配对象
Matcher matcher = pattern.matcher(content);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
//将Java替换为java后,将最后匹配到之前的子串都添加到sb对象中
matcher.appendReplacement(sb, "java");
System.out.println("sb:" + sb);
System.out.println("---------分割线-----------");
}
//将最后匹配到的子串添加到sb中
matcher.appendTail(sb);
System.out.println("sb: " + sb);
}
}
4.6 正则表达式的语法
4.6.1 正则转义符
元符号-转义号\
\符号说明:在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义字符,否则检测不到结果,甚至会报错。
需要用到转义符的字符有:. * + ( )$ / \ ? [ ] ^ { }
注意:在Java的正则表达式中,两个\代表其他语言中的一个\
4.6.2 正则表达式支持字符
正则表达式所支持的合法字符
字符 | 解释 |
---|---|
X | 字符x(x 可代表任何合法的字符) |
\0mnn | 八进制数 0mnn 所表示的字符 |
\xhh | 十六进制值 0xhh 所表示的字符 |
\uhhhh | 十六进制值 0xhhhh 所表示的 Unicode 字符 |
\t | 制表符(“\u0009”) |
\n | 新行(换行)符(‘\u000A’) |
\r | 回车符(‘\u000D’) |
\f | 换页符(‘\u000C’) |
\a | 报警(bell)符(‘\u0007’) |
\e | Escape 符(‘\u001B’) |
\cx | x 对应的的控制符。例如,\cM匹配 Ctrl-M。x 值必须为 A~Z 或 a~z 之一。 |
正则表达式中的特殊字符
特殊字符 | 说明 |
---|---|
$ | 匹配一行的结尾。要匹配 $ 字符本身,请使用$ |
^ | 匹配一行的开头。要匹配 ^ 字符本身,请使用^ |
() | 标记子表达式的开始和结束位置。要匹配这些字符,请使用(和) |
[] | 用于确定中括号表达式的开始和结束位置。要匹配这些字符,请使用[和] |
{} | 用于标记前面子表达式的出现频度。要匹配这些字符,请使用{和} |
* | 指定前面子表达式可以出现零次或多次。要匹配 * 字符本身,请使用* |
+ | 指定前面子表达式可以出现一次或多次。要匹配 + 字符本身,请使用+ |
? | 指定前面子表达式可以出现零次或一次。要匹配 ?字符本身,请使用? |
. | 匹配除换行符\n之外的任何单字符。要匹配.字符本身,请使用. |
\ | 用于转义下一个字符,或指定八进制、十六进制字符。如果需匹配\字符,请用\ |
| | 指定两项之间任选一项。如果要匹配丨字符本身,请使用| |
预定义字符
预定义字符 | 说明 |
---|---|
. | 可以匹配任何字符 |
\d | 匹配 0~9 的所有数字 |
\D | 匹配非数字 |
\s | 匹配所有的空白字符,包括空格、制表符、回车符、换页符、换行符等 |
\S | 匹配所有的非空白字符 |
\w | 匹配所有的单词字符,包括 0~9 所有数字、26 个英文字母和下画线_ |
\W | 匹配所有的非单词字符 |
方括号表达式
方括号表达式 说明 表示枚举 例如 [abc]表示 a、b、c 其中任意一个字符; [gz]表示 g、z 其中任意一个字符 表示范围:- 例如 [a-f]表示 a~f 范围内的任意字符; [\u0041-\u0056]表示十六进制字符 \u0041 到 \u0056 范围的字符。范围可以和枚举结合使用,如 [a-cx-z],表示 ac、xz 范围内的任意字符 表示求否:^ 例如 [^abc]表示非 a、b、c 的任意字符; [^a-f]表示不是 a~f 范围内的任意字符 表示“与”运算:&& 例如 [a-z&&[def]]是 a~z 和 [def] 的交集,表示 d、ef[a-z&&^bc]]是 a~z 范围内的所有字符,除 b 和 c 之外[ad-z] [a-z&&[m-p]]是 a~z 范围内的所有字符,除 m~p 范围之外的字符 表示“并”运算 并运算与前面的枚举类似。例如 [a-d[m-p]]表示 [a-dm-p]
补充:
Java正则表达式默认是区分字母大小写的,如要实现不区分大小写
(?i)abc表示abc都不区分大小写
a(?i)bc表示bc不区分大小写
a((?i)b)c表示只有b不区分大小写
Pattern pattern=Pattern.compile(regStr,Pattern.CASE_INSENSITIVE);
//当创建Pattern对象时,指定Pattern.CASE_INSENSITIVE,表示匹配不区分字母大小写
细节:Java匹配默认贪婪匹配,即尽可能匹配多的,比如"a{3,4}“,表示匹配aaa或者aaaa,但是优先匹配aaaa,例如原字符串中包括"aaaaaa”,匹配"a{3,4}“时,会找到"aaaa”
4.6.3 捕获组(capture group)
从正则表达式左侧开始,每出现一个左括号"("记做一个分组,分组编号从 1 开始。0 代表整个表达式。常用分组构造形式(pattern)
对于时间字符串:2017-04-25,表达式如下
(\d{4})-((\d{2})-(\d{2}))
有 4 个左括号,所以有 4 个分组:
编号 | 捕获组 | 匹配 |
---|---|---|
0 | (\d{4})-((\d{2})-(\d{2})) | 2017-04-25 |
1 | (\d{4}) | 2017 |
2 | ((\d{2})-(\d{2})) | 04-25 |
3 | (\d{2}) | 04 |
4 | (\d{2}) | 25 |
4.7 正则表达式的常见应用
- 验证字符串是否全是汉字
Pattern pattern=Pattern.compile("^[\u0391-\uffe5]+$");
- 验证字符串是否是邮编
Pattern pattern=Pattern.compile("^\\d{6}$");
- 验证字符串是否是QQ号码
Pattern pattern=Pattern.compile("^[1-9]d{4,9}$");
- 验证字符串是否是手机号码(要求:必须以13,14,15,18开头的11位数)
Pattern pattern=Pattern.compile("^1[3|4|5|8]\\d{9}$");
- 验证字符串是否是url
Pattern pattern=Pattern.compile("^((http|https)://)([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.#]*)?$");
4.4 枚举
JDK1.5 引入了枚举类型。枚举类型的定义包括枚举声明和枚举体。格式如下:
enum 枚举名 {
枚举体(常量列表)
}
- 当需要定义一组常量时,强烈建议使用枚举类 使用说明:
- 使用 enum 定义的枚举类默认继承了 java.lang.Enum类,因此不能再继承其他类
- 枚举类的构造器只能使用 private 权限修饰符
- 枚举类的所有实例必须在枚举类中显式列出(, 分隔 ; 结尾)。列出的实例系统会自动添加 public static final 修饰
- 必须在枚举类的第一行声明枚举类对象
- Enum类的主要方法:
- values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
- valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
- toString():返回当前枚举类对象常量的名称
枚举体就是放置一些常量。我们可以写出我们的第一个枚举类型,如示例所示:
【示例】创建枚举类型
enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
所有的枚举类型隐性地继承自 java.lang.Enum。枚举实质上还是类!而每个被枚举的
成员实质就是一个枚举类型的实例,他们默认都是 public static final 修饰的。可以直接通
过枚举类型名使用它们。
【示例】枚举的使用
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 WINTER:
System.out.println("冬天");
break;
}
}
}
/**季节*/
enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
/**星期*/
enum Week {
星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期日
}
【示例】订单状态的枚举
package a_enum;
/**
* 订单状态: Nonpayment(未付款)、 Paid(已付款) 、 Delivered(已发货)、Return(退货)、 Checked(已确认) Fulfilled(已配货)、
*/
public class StateDemo3 {
public static void main(String[] args) {
OrderState[] states = OrderState.values();
for (int i = 0; i < states.length; i++) {
System.out.println(states[i] + "-" + states[i].getValue() + "-" + states[i].getCode());
}
}
public enum OrderState {
//static OrderState NON_PAYMENT = new OrderState("未付款")
NON_PAYMENT(1, "未付款"),
PAID(2, "已付款"),
DELIVERED(3, "已发货"),
RETURN(4, "退货"),
CHECKED(5, "已确认"),
FULFILLED(6, "已配货");
private OrderState(int code, String value) {
this.code = code;
this.value = value;
}
private int code;
private String value;
public String getValue() {
return value;
}
public int getCode() {
return code;
}
}
}