枚举
枚举(Enum)是一种由确定取值区间的数据类型,其本质是一种类,具有简洁、安全、方便等特点。枚举的值被约束到一个特定的范围,只能取这个范围以内的值。
枚举的定义与类很相似,实用enum关键字来描述,语法如下。需要注意的是枚举中的常量实用逗号进行分割。
public enum Week {
MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY;
}
枚举中的每一个常量都对应的是一个枚举实例,只不过表示的含义不同。拿上面这个例子来说,Java在编译期会帮我们生成一个 Week 类,并且继承自java.lang.Enum,被 final 修饰,表示该类不可被继承。同时还生成了7个 Week 的实例对象分别对应枚举中定义的7个日期,因为枚举的静态常量直接对应其实例化对象,所以对于枚举的使用如下所示。
public class Main {
public static void main(String[] args) {
System.out.println(Week.MONDAY);
}
}
Math
Math类为开发者提供了一系列的数学方法,同时还提供了两个静态常量E(自然对数的底数)和 PI(圆周率)。Math类中的所有方法全部都是静态的,通过类名直接调用。使用方法如下所示。
public class Main {
public static void main(String[] args) {
System.out.println("圆周率 = " + Math.PI);
System.out.println("自然常数 = " + Math.E);
System.out.println("9的平方根 = " + Math.sqrt(9));
System.out.println("64的立方根 = " + Math.cbrt(64));
System.out.println("2的4次方 = " + Math.pow(2,4));
System.out.println("两数中的最大数:" + Math.max(10,20));
System.out.println("两数中的最小数:" + Math.min(10,20));
System.out.println("-10.5的绝对值 = " + Math.abs(-10.5));
System.out.println("大于10.01的最大整数" + Math.ceil(10.01));
System.out.println("小于10.99的最小整数" + Math.floor(10.99));
System.out.println("随机数" + Math.random());
System.out.println("5.6四舍五入:" + Math.rint(5.6));
System.out.println("5.6f 四舍五入:" + Math.round(5.6f));
System.out.println("5.6四舍五入:" + Math.round(5.6));
}
}
// 输出
圆周率 = 3.141592653589793
自然常数 = 2.718281828459045
9的平方根 = 3.0
64的立方根 = 4.0
2的4次方 = 16.0
两数中的最大数:20
两数中的最小数:10
-10.5 的绝对值 = 10.5
大于10.01的最大整数11.0
小于10.99的最小整数10.0
随机数0.7027884654726971
5.6 四舍五入:6.0
5.6f 四舍五入:6
5.6 四舍五入:6
Random
Random 是用来产生一个随机数的类,并且可以任意指定一个区间,在此区间内产生一个随机数。
| 方法 | 描述 |
|---|---|
| public Random() | 创建一个无参的随机数构造器,使用系统时间(ms)作为默认种子 |
| public Random(long seed) | 使用long数据类型的种子创建一个随机数构造器 |
| public boolean nextBoolean() | 返回下一个伪随机数,它取自此随机数生成器序列的boolean值 |
| public double nextDouble() | 返回下一个伪随机数,它取自此随机数生成器序列的,在0.0 和 1.0 之间均匀分布的double值 |
| public float nextFloat() | 返回下一个伪随机数,它取自此随机数生成器序列的,在0.0 和 1.0 之间均匀分布的float值 |
| public int nextInt() | 返回下一个伪随机数,它取自此随机数生成器的序列中均匀分布的int值(int取值范围内) |
| public int nextInt(int n) | 返回下一个伪随机数,它取自此随机数生成器序列的,在 0 和 n 之间均匀分布的int值 |
| public long nextLong() | 返回下一个伪随机数,它取自此随机数生成器的序列中均匀分布的long值(long取值范围内) |
| public synchronized void setSeed(long sedd) | 使用单个long种子设置此随机数生成器的种子 |
public class Main {
public static void main(String[] args) {
Random random = new Random();
for (int i = 0; i < 3; i++) {
System.out.println("第" + i + "个 boolean 随机数:" + random.nextBoolean());
}
System.out.println(" ");
for (int i = 0; i < 3; i++) {
System.out.println("第" + i + "个 double 随机数:" + random.nextDouble());
}
System.out.println(" ");
for (int i = 0; i < 3; i++) {
System.out.println("第" + i + "个 float 随机数:" + random.nextFloat());
}
System.out.println(" ");
for (int i = 0; i < 3; i++) {
System.out.println("第" + i + "个 int 随机数:" + random.nextInt());
}
System.out.println(" ");
for (int i = 0; i < 3; i++) {
System.out.println("第" + i + "个 int 随机数:" + random.nextInt(100));
}
System.out.println(" ");
for (int i = 0; i < 3; i++) {
System.out.println("第" + i + "个 long 随机数:" + random.nextLong());
}
}
}
// 输出
第0个 boolean 随机数:false
第1个 boolean 随机数:true
第2个 boolean 随机数:false
第0个 double 随机数:0.5582185121166758
第1个 double 随机数:0.1518792705262828
第2个 double 随机数:0.36210695241138213
第0个 float 随机数:0.34393734
第1个 float 随机数:0.34196997
第2个 float 随机数:0.19409823
第0个 int 随机数:413390215
第1个 int 随机数:979753680
第2个 int 随机数:1839787433
第0个 int 随机数:58
第1个 int 随机数:20
第2个 int 随机数:45
第0个 long 随机数:6457104626097317921
第1个 long 随机数:8103479363559581586
第2个 long 随机数:-8593379668973925522
String
String是开发中使用频率很高的类,Java通过String类来创建和操作字符串数据。
String实例化
String类对象的实例化方式有两种,第一种是直接赋值的方式,第二种是通过构造函数创建实例化对象的方式。
public class Main {
public static void main(String[] args) {
// 第一种方式
String str1 = "hello";
// 第二种方式
String str2 = new String("hello");
System.out.println(str1);
System.out.println(str2);
}
}
// 输出
hello
hello
通过下面的代码比较两种方式的不同。
public class Main {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
String str4 = new String("hello");
System.out.println(str1 == str2);
System.out.println(str3 == str4);
}
}
// 输出
true
false
通过运行结果可以发现,str1所引用的内存地址和str2所引用的内存地址相同,即str1和str2指向堆内存中的同一块位置。而str3和str4则指向堆内存中不同的位置。
造成这两种不同情况的原因是:第一种直接赋值的方式如“String str1 = “hello””,会首先在栈内存中开辟一块空间来保存变量str1,同时在堆内存中开辟一块空间存储“hello”,然后将堆内存的地址赋给栈内存中的str1,即str1存储的是“hello”的内存地址。Java同时在堆内存中提供一个字符串常量池,专门用来存储String类型的对象。在实例化一个String对象时,首先会在字符串常量池中查找 如果该字符已经创建,则直接返回它的内存地址。如果字符串常量池不存在该字符串,就先创建再返回,即字符串常量池中不会创建重复的字符串对象。所有第一种创建的方式返回值是true。字符串常量池只适用于直接赋值的方式。
第二种创建对象的方式底层原理则是:通过构造函数创建对象会在堆内存中开辟对应的空间来存储,通过 new String(“hello”) 和 new String(“hello”) 创建了两个对象,虽然值相等,但是会开辟两块内存空间来存储。所有内存地址是不同的。
String字符串通过 “==” 是判断两个字符串对象的内存地址是否相等。判断两个字符串对象的值是否相等,可以调用String的 equals()方法。
public class Main {
public static void main(String[] args) {
String str3 = new String("hello");
String str4 = new String("hello");
System.out.println(str3.equals(str4));
}
}
// 输出
true
实际上 String 类在存储字符串时,会将字符串的值保存在byte类型的数组中,数组一旦创建,其长度就是不可改变的。既然长度不可改变,也就意味着byte类型所存储的字符串值不可修改。一旦修改,就会重新创建一个 String 对象,用新对象的byte数组来存储修改之后的字符串。即如果修改了String对象的值,它就已经不是之前的对象了,而是一个新的对象。
public class Main {
public static void main(String[] args) {
String str3 = new String("hello");
String str4 = new String("hello");
System.out.println("修改前str3的内存地址:" + System.identityHashCode(str3));
System.out.println("修改前str4的内存地址:" + System.identityHashCode(str4));
str3 += "world";
System.out.println("修改后str3的内存地址:" + System.identityHashCode(str3));
System.out.println("修改后str4的内存地址:" + System.identityHashCode(str4));
}
}
// 输出
修改前str3的内存地址:1956725890
修改前str4的内存地址:356573597
修改后str3的内存地址:1735600054
修改后str4的内存地址:356573597
| 方法 | 描述 |
|---|---|
| public String() | 创建一个值为空的对象 |
| public String(String str) | 创建一个值为str的对象 |
| public String(char value[]) | 将一个char型数组转为字符串对象 |
| public String(char value[], int offset, int count) | 将一个指定范围的char型数组转为字符串对象 |
| public String(byte[] bytes) | 将一个byte型数组转为字符串对象 |
| public String(byte[] bytes, int offset, int length) | 将一个指定范围的byte型数组转为字符串对象 |
| public int length() | 返回字符串的长度 |
| public boolean isEmpty() | 判断字符串是否为空 |
| public char charAt(int index) | 返回字符串中指定位置的字符 |
| public byte[] getBytes() | 将字符串转为byte型数组 |
| public boolean equals(Object anObject) | 判断两个字符串是否相等 |
| public boolean equalsIgnoreCase(String anotherString) | 判断两个字符串是否相等并且忽略大小写 |
| public int compareTo(String anotherString) | 对两个字符串进行排序 |
| public boolean startsWith(String prefix) | 判断是否以指定的值开头 |
| public boolean endsWith(String suffix) | 判断是否以指定的值结尾 |
| public int hashCode() | 获取字符串的散列值 |
| public int indexOf(String str) | 从头开始查找指定字符的位置 |
| public int indexOf(String str, int fromIndex) | 从指定位置开始查找指定字符的位置 |
| public String substring(int beginIndex) | 截取字符串从指定位置开始到结尾 |
| public String substring(int beginIndex, int endIndex) | 截取字符串从指定位置开始到指定位置结束 |
| public String concat(String str) | 追加字符串 |
| public String replaceAll(String regex, String replacement) | 替换字符串 |
| public String[] split(String regex) | 用指定的字符串对目标字符进行分割,返回数组 |
| public String toLowerCase() | 将字符串转为小写 |
| public String toUpperCase() | 将字符串转为大写 |
| public char[] toCharArray() | 将字符串转为char型数组 |
StringBuffer
String的底层是用数组来存值的,如果开发中需要对某个字符串进行频繁的修改,由于数组长度不可改变,所以使用String就不合适了,会造成内存空间的浪费。因此可以使用StringBuffer来解决这个问题。
StringBuffer 和 String 类似,底层也是用一个数组来存储字符串的值,并且数组的默认长度为16,即一个空的StringBuffer对象,数组长度为16。
当我们调用有参构造创建一个StringBuffer对象时,数组长度就不是16了,而是根据当前对象的值来决定数组的长度,“值得长度 + 16”作为数组得长度。
所以一个 StringBuffer 创建完成之后,有16字节的空间可以对其值进行修改。如果修改的值范围超出了16字节,则调用ensureCapacityInternal()方法继续对底层数组进行扩容,并且保持引用不变。
public class Main {
public static void main(String[] args) {
StringBuffer str3 = new StringBuffer("hello");
StringBuffer str4 = new StringBuffer("hello");
System.out.println("修改前str3的内存地址:" + System.identityHashCode(str3));
System.out.println("修改前str4的内存地址:" + System.identityHashCode(str4));
str3.append("world");
System.out.println("修改后str3的内存地址:" + System.identityHashCode(str3));
System.out.println("修改后str4的内存地址:" + System.identityHashCode(str4));
}
}
// 输出
修改前str3的内存地址:1956725890
修改前str4的内存地址:356573597
修改后str3的内存地址:1956725890
修改后str4的内存地址:356573597
StringBuffer 常用方法如下
| 方法 | 描述 |
|---|---|
| public StringBuffer() | 无参构造,创建一个空得 StringBuffer |
| public StringBuffer(String str) | 有参构造 |
| public synchronized int length() | 返回StringBuffer的长度 |
| public synchronized char charAt(int index) | 返回字符串中指定位置的字符 |
| public synchronized StringBuffer append(String str) | 追加字符 |
| public synchronized StringBuffer delete(int start, int end) | 删除指定区间内的字符串 |
| public synchronized StringBuffer deleteCharAt(int index) | 删除指定位置的字符 |
| public synchronized StringBuffer replace(int start, int end, String str) | 将指定区间内的值替换为str |
| public synchronized String substring(int start) | 截取字符串从指定位置开始到结尾 |
| public synchronized String substring(int start, int end) | 截取字符串从指定位置开始到指定位置结束 |
| public synchronized StringBuffer insert(int offset, String str) | 向指定位置插入str |
| public int indexOf(String str) | 从头开始查找指定字符的位置 |
| public int indexOf(String str, int fromIndex) | 从指定的位置开始查找指定字符的位置 |
| public synchronized StringBuffer reverse() | 进行反转 |
| public synchronized String toString() | 返回StringBuffer对应的String |
日期类
实际开发中对日期的使用是必不可少的,Java对日期的使用也提供了良好的封装,主要包括java.util.Date和java.util.Calendar。
Date
Date类直接通过构造函数实例化其对象即可。Date对象表示当前的系统时间。
public class Main {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
}
}
从运行结果可以看到,已经获取了当前的系统时间,但是显示的格式并不符合我们平时使用的格式,可以通过 java.text.SimpleDateFormat类对 Date对象进行格式化,将日期格式转换为熟悉的格式。
| 标记 | 描述 |
|---|---|
| y | 年,yyyy表示 4 位数的年份信息 |
| M | 月,MM表示 2 位数的月份信息 |
| m | 分钟,mm表示 2 位数的分钟信息 |
| d | 天,dd表示 2 位数的天信息 |
| H | 小时,HH表示 2 位数的 24 小时制下的小时信息 |
| h | 小时,hh表示 2 位数的 12 小时制下的小时信息 |
| s | 秒,ss表示 2 位数的秒信息 |
| S | 毫秒,SSS表示 3 位数的毫秒信息。 |
public class Main {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
// 格式化时间
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
System.out.println(simpleDateFormat.format(date));
}
}
Calendar
通过Date类可以获取当前系统时间,如果想要对日期数据进行逻辑操作,例如计算从当前时间算起15天后的日期是几月几号,Date是没有办法实现的。因此可以通过Calendar类来完成日期数据的逻辑运算。
使用Calendar进行日期运算的基本思路是先将日期数据赋给Calendar,再调用Calendar的方法完成相关运算。Calendar类提供了很大静态常量,用来记录日期数据。
| 常量 | 描述 |
|---|---|
| public static final int YEAR | 年 |
| public static final int MONTH | 月 |
| public static final int DAY_OF_MONTH | 天,以月为单位,即当天是该月中的第几天 |
| public static final int DAY_OF_YEAR | 天,以年为单位,即当天是该年中第几天 |
| public static final int HOUR_OF_DAY | 小时 |
| public static final int MINUTE | 分钟 |
| public static final int SECOND | 秒 |
| public static final int MILLISECOND | 毫秒 |
Calendar常用方法
| 方法 | 描述 |
|---|---|
| public static Calendar getInstance() | 获取系统对应的Calendar实例化对象 |
| public void set(int field, int value | 给静态常量赋值 |
| public int get(int field) | 取出静态常量 |
| public final Date getTime() | 获取Calendar对应的Date对象 |
public class Main {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
// 判断某一天在一年中属于第几天
calendar.set(Calendar.YEAR, 2000);
calendar.set(Calendar.MONTH, 11); // 1月为0,12月为11
calendar.set(Calendar.DAY_OF_MONTH, 31);
int day = calendar.get(Calendar.DAY_OF_YEAR);
System.out.println("2000年12月31日是所在年的第 " + day + " 天");
// 计算2000年12月31日往后推12天的日期
calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) + 12);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
System.out.println("12天后的日期:" + simpleDateFormat.format(calendar.getTime()));
// 计算2001年1月12日往前推10天的日期
calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - 10);
System.out.println("12天前的日期:" + simpleDateFormat.format(calendar.getTime()));
}
}
// 输出
2000年12月31日是所在年的第 366 天
12天后的日期:2001-01-12
12天前的日期:2001-01-02