Part 1 Java基础类库
一、包装类(Wrapper Class)
1.包装类基本知识
Java是面向对象的编程语言,但其内置的基本数据类型并不是对象,不支持面向对象的编程机制,但在实际使用时,我们需要将基本数据类型转换为对象,以便于操作。比如将基本数据类型存储到Object[]数组或集合中,为了解决这个问题,Java提供了包装类,为8种基本数据类型分别定义了相应的引用类型,并称之为基本数据类型的包装类。
包装类位于java.lang包中,基本数据类型和包装类的对应关系如下:
| byte | Byte |
|---|---|
| boolean | Boolean |
| short | Short |
| char | Character |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
其中6个是数字型,一个字符型和一个布尔型。为了满足6个数字型对象间的相互转型,Java中的这6个数字型包装类都继承Number抽象类,Number抽象类中定义了抽象方法:intValue()、longValue()、floatValue()、doubleValue()、byteValue()等用来实现相互类型转换的方法。
2.包装类的作用
- 作为和基本数据类型对应的类型存在,方便涉及到对象的操作,如Object[]、集合等的操作。
包含每种基本数据类型的相关属性,如最大值、最小值等,以及相关的操作方法,用来实现基本数据类型、包装类对象、字符串之间的相互转化。
3.包装类的使用
基本数据类型转为为包装类对象
- 包装类对象转化为基本数据类型
- 字符串转化为包装类对象
- 包装类对象转化为字符串
- 基本数据类型转化为字符串的简单方法
所对应的基本数据类型相关的属性
public class Test {public static void main(String[] args) {//基本数据类型转化为包装类对象//Integer a = new Integer(5);//从Java9开始这种使用方法被废弃,但依然可用Integer b = Integer.valueOf(8);//调用Integer的类方法实现转化//包装类对象转化为基本数据类型double c = a.doubleValue();//字符串转化为包装类对象Integer i1 = Integer.valueOf("123");Integer i2 = Integer.parseInt("234");//将包装类对象转化为字符串String str = i1.toString();//基本数据类型转化为字符串的简单方法var str1 = "" + 5;//一些相关属性和方法System.out.println(Integer.MAX_VALUE);//int数据类型的最大值System.out.println(Integer.max(1, 2));//返回最大值}}//2147483647//2
4.自动装箱和自动拆箱
自动装箱和自动拆箱就是将基本数据类型和包装类之间进行自动的相互转化。
4.1 自动装箱
基本数据类型处于需要对象的环境中时,会自动转化为对象。
在JDK1.5之前,Integer i = 5这样的代码是错误的,必须要通过Integer i = new Integer(5)实现基本数据类型和包装类间的转化,这种转化较为繁琐。JDK1.5之后,提供了自动装箱(Autoboxing)和自动拆箱(Autounboxing),只需使用Integer i = 5就能实现基本数据类型到包装类的转化,这是JVM为我们执行了Integer i = Integer.valueOf(5)这样的操作,这就是自动装箱。4.2 自动拆箱
当需要一个值时,对象会自动转化为基本数据类型,没必要显式调用intValue()等转型方法。
//虽然包装类型的变量是引用数据类型,但包装类的实例可用直接与数值类型进行比较,//这里就用了自动拆箱,把包装类实例所包装的值取出来比较Integer a = 6;System.out.println("5的包装类实例是否大于4" + (a > 4));
4.3 包装类的空指针问题
虽然编译器为帮我们进行自动装箱和拆箱,使用这个功能很方便,但我们依然得了解其语义,否则有些代码可用通过编译,但在运行时会出错。
public class Test {public static void main(String[] args) {//自动装箱Integer a = 5;//编译器帮我们改为:Integer a = Integer.valueOf(5);//自动拆箱int b = a;//编译器改为:int b = a.intValue();//包装类的空指针异常问题,即可通过编译,但运行报错Integer c = null;int d = c;//int d = c.intValue();// 提示NullPointerException异常,即对象为null,我们调用了它的属性或方法}}
4.4 包装类的缓存问题
整型、char类型所对应的包装类,在自动装箱时,对于-128~127之间的值会进行缓存处理,其目的是提高效率。
缓存处理的原理为:如果数据在-128~127这个区间,那么在类加载时就为该区间的每个数值创建了一个对象,将这256个对象保存在cache数组中。每当自动装箱时,先判断数值是否在这个区间之间,如果在则直接返回cache数值中对象的引用,如果不在,则调用包装类的构造方法重写创建一个对象。public class Test {public static void main(String[] args) {//包装类的缓存问题Integer a = 1000;Integer b = 1000;System.out.println(a == b);//两个不同对象System.out.println(a.equals(b));//逻辑上相等}}//false//truepublic class Test {public static void main(String[] args) {//包装类的缓存问题Integer a = 1000;Integer b = 1000;Integer c = 123;//当数值范围在[-128,127]之间时,并不会新创建一个对象,而是返回缓存数组中的元素Integer d = 123;System.out.println(a == b);//两个不同对象System.out.println(a.equals(b));//逻辑上判断System.out.println(c == d);//两个不同对象,但显示相等}}//false//true//true
二、String、StringBuffer和StringBuilder类
1.可变字符序列和不可变字符序列
String类是不可变字符序列,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。
而StringBuffer和StringBuilder类的对象都是可变字符序列,通过它们提供的方法可以改变这个字符串对象的序列,一旦生成了最终想要的字符串,可以使用toString方法将其转变为String不可变序列。
这三个字符串类的内部都是使用一个字符数组来存储字符串,但String类的字符数组使用final修饰,而StringBuffer和StringBuilder没有用final修饰,随时可以修改,所以被称为可变字符序列。2.StringBuffer和StringBuilder的区别
两个类基本相同,构造器、方法也够基本相同,因此在使用上没什么区别。
- StringBuffer是JDK1.0提供的类,线程安全,做线程同步检查,效率较低。
- StringBuilder是JDK1.5提供的类,线程不安全,不做线程同步检查,但效率较高。
在通常情况下,如果需要创建一个可变字符序列,应该优先使用StringBuilder类。在涉及到线程安全的时候,可以使用StringBuffer。
3.常用方法和属性
- StringBuffer和StringBuilder有两个属性:length和capacity,其中length属性表示其包含的字符序列的长度,与String类的length不同的是,StringBuffer和StringBuilder的length是可以改变的,可以通过length()和setLength(int len)方法来访问和修改其字符序列的长度。capacity属性表示字符串容量,capacity通常比length大,程序通常无须关心capacity属性。
常用方法列表(StringBuilder和StringBuffer一样适用): | public StringBuffer append(String s) | 将指定字符串追加到此字符串后方,并返回自身对象 | | —- | —- | | public StringBuffer reverse() | 将字符串逆序排序取代原来的字符串,并返回自身对象 | | puclic StringBuffer delete(int start, int end) | 删除start到end-1的一段字符序列,并返回自身对象 | | puclic StringBuffer insert(int offset, int i) | 在指定位置插入int类型的字符,并返回自身对象 | | puclic StringBuffer replace(int start, int end,String str) | 使用给定的字符串替换指定的字符串,并返回自身对象 |
和String类似的方法: | public int indexOf(String str) | | —- | | public int indexOf(String str, int fromIndex) | | public String substring(int start) | | public String substring(int start, int end) | | public int length() | | char charAt(int index) |
public class Test {public static void main(String[] args) {StringBuilder sb = new StringBuilder("abc");sb.append(123);sb.append(456);System.out.println(sb);sb.append('d').append("ef").append(1.0).append(true);System.out.println(sb);StringBuilder sb1 = new StringBuilder("A");//以后最常碰见的就是这种一个循环去增加字符串的内容for (int i = 0; i < 10; i++) {sb1.append(i);}System.out.println(sb1);}}//abc123456//abc123456def1.0true//A0123456789
public class Test {public static void main(String[] args) {StringBuilder sb = new StringBuilder("a");//insert插入sb.insert(0, "b ").insert(0, "c ");System.out.println(sb);StringBuilder p = new StringBuilder("abcdefg");//删除子串p.delete(0, 2);//删除第一个和第二个字符p.deleteCharAt(0).deleteCharAt(0);//删除某个字符System.out.println(p.charAt(0));//获取某个字符System.out.println(p.reverse());//逆序}}//c b a//e//gfe
注:在插入删除时,要注意如果往头部插入,则在后面的会在前面;在删除时,要注意索引越界问题,即删除的地方在字符长度之外。
4.不可变字符序列的使用陷阱
4.1 String类使用陷阱
String类一经初始化,内容就不可改变。对String对象的操作实际上是对其副本(原拷贝对象)的操作,原来的字符串没有改变。如果多次执行改变字符串内容的操作,会导致大量副本字符串对象留在内存中,降低效率,如果这样的操作放到循环中,会极大影响程序的时空消耗,甚至造成服务器崩溃。
4.2 效率测试
public class Test {public static void main(String[] args) {//测试String的时空消耗String str = "";long num1 = Runtime.getRuntime().freeMemory();//获取系统剩余内存long time1 = System.currentTimeMillis();//获取系统当前时间for (int i = 0; i < 10000; i++) {str = str + i;//相当于产生了10000个对象}long num2 = Runtime.getRuntime().freeMemory();long time2 = System.currentTimeMillis();System.out.println("String占用的内存为:" + (num1 - num2));System.out.println("String占用的实际为:" + (time2 - time1));//测试StringBuilder的时空消耗StringBuilder sb = new StringBuilder("");long num3 = Runtime.getRuntime().freeMemory();long time3 = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {sb.append(i);}long num4 = Runtime.getRuntime().freeMemory();long time4 = System.currentTimeMillis();System.out.println("StringBuilder占用的内存为:" + (num3 - num4));System.out.println("StringBuilder占用的时间为:" + (time3 - time3));}}//String占用的内存为:6732776//String占用的实际为:144//StringBuilder占用的内存为:0//StringBuilder占用的时间为:0
三、时间处理类
1.Date类
Java提供了Date类来处理日期、时间,Date对象即包含日期,也包含时间。其余时间处理类都是围绕java.util.Date展开的,这里所说的Date类指java.util.Date类。
1.1 Date类的构造器
- Date():生成一个代表当前日期时间的Date对象,该构造器在底层调用System.currentTimeMillis()获得long整数作为日期参数。
Dage(long date):根据传入的long型整数来生成一个Date对象,该构造器的参数表示创建的Date对象和GMT1970年1月1日00:00:00之间的时间差,以毫秒为计时单位。
1.2 Date类的方法
boolean after(Date when):测试该日期是否在指定日期when之后。
- boolean before(Date when):测试该日期是否在指定日期when之前。
- long getTime():返回该时间对应的long型整数,即从GMT1970年1月1日00:00:00到该对象之间的时间差。
- void setTime(long time):设置Date对象的时间。 ```java import java.util.Date;
public class Test { public static void main(String[] args) { long a = Long.MAX_VALUE / (1000L 3600 24 * 365);//计算long型整数最大能表示多少年 System.out.println(a);//大约表示到2.9亿年后 long nowNum = System.currentTimeMillis();//获取当前时刻的毫秒数 System.out.println(nowNum);
Date d1 = new Date();//获取当前系统时间Date d2 = new Date(nowNum + 1000L * 3600 * 24 * 365);//获取当前系统时间之后1年的时间System.out.println(d1);System.out.println(d2);System.out.println(d1.before(d2));//判断d1是否在d2之前}
} //292471208 //1627873517224 //Mon Aug 02 11:05:17 CST 2021 //Tue Aug 02 11:05:17 CST 2022 //true
总体来说,Date是一个设计很糟糕的类,因此Java官方推荐尽量少使用Date类。如果需要对日期、时间进行加减运算操作,或获取指定时间的年月日分秒等信息,可以使用Calendar工具类。<a name="QkDhj"></a>### 2.DateFormat和SimpleDateFormat类<a name="boxMC"></a>#### 2.1 DateFormat类的作用把时间对象转化为指定格式的字符串,或把指定格式的字符串转化为时间对象。DateFormat是一个抽象类,一般使用它的子类SimpleDateFormat来实现上述功能。<a name="DCCVF"></a>#### 2.2 使用```javapublic class Test {public static void main(String[] args) throws ParseException {//创建一个SimpleDateFormat类对象,// 指定时间的字符串形式,其中使用的字母都是有特定含义的,不能随便使用SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd");//将时间对象转化为字符串String daytime = df1.format(new Date());System.out.println(daytime);System.out.println(df2.format(new Date()));//将字符串转化为时间对象,字符串的格式要与指定的格式一致String time1 = "2021-8-2 13:00:00";Date date1 = df1.parse(time1);String time2 = "2021-8-2";Date date2 = df2.parse(time2);System.out.println(date1);System.out.println(date2);System.out.println(date1.getTime());//返回long型整数的毫秒数System.out.println(date2.getTime());}}//2021-08-02 12:10:25//2021-08-02//Mon Aug 02 13:00:00 CST 2021//Mon Aug 02 00:00:00 CST 2021//1627880400000//1627833600000
2.3 格式化字符的含义
下面是常用的格式化字符,如果需要使用到其他格式化字符再去查表,只需熟悉这几个常用的即可。
| y | 年 |
|---|---|
| M | 年中的月份 |
| w | 年中的周数 |
| W | 月份中的周数 |
| D | 年中的天数 |
| d | 月份中的天数 |
| F | 月份中的星期 |
| E | 周几 |
import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;public class Test {public static void main(String[] args) throws ParseException {//创建一个SimpleDateFormat类对象,// 指定时间的字符串形式,其中使用的字母都是有特定含义的,不能随便使用SimpleDateFormat df1 = new SimpleDateFormat("今天是今年的第w周,第D天,这个月的第d天");System.out.println(df1.format(new Date()));SimpleDateFormat df2 = new SimpleDateFormat("今天是E");System.out.println(df2.format(new Date()));}}//今天是今年的第32周,第214天,这个月的第2天//今天是周一
3.Calendar类
Date类无法进行日期计算,如往前100月是哪天,往后100年是哪天,Date类因为设计缺陷没有办法解决,因此提供了Calendar类来更好地处理日期和时间。
Calendar类是一个抽象类,为我们提供了日期计算的相关功能,它是所有日历类的模板,并提供一些所有日历通用的方法,但它不能直接实例化,Java提供了一个GregorianCalendar类,是Calendar类的子类,提供全世界大部分国家地区使用的标准日历系统,即我们平常所说的公历。
注:月份的表示从0开始,0是一月。因为大多数人习惯使用单词来描述月份而不是用数字,这样使程序更易读,在Calendar类中使用常量来表示月份,如JANUARY、FEBRUARY等。
import java.util.Calendar;import java.util.Date;import java.util.GregorianCalendar;public class Test {public static void main(String[] args) {//2021年8月2号,周一//月份从0开始,1月是0...GregorianCalendar calendar = new GregorianCalendar(2021, 7, 2, 12, 45, 50);int year = calendar.get(Calendar.YEAR);//打印2021int month = calendar.get(Calendar.MONTH);//打印10int day = calendar.get(Calendar.DAY_OF_MONTH);//打印2int day1 = calendar.get(Calendar.DATE);//打印2,Date和DAY_OF_MONTH同义int date = calendar.get(Calendar.DAY_OF_WEEK);//1-7,周日是1,周一是2,周六是7...System.out.println(year);//2021System.out.println(month);//7System.out.println(day);//2System.out.println(day1);//2System.out.println(date);//2,周一//设置日期GregorianCalendar calendar1 = new GregorianCalendar();calendar1.set(Calendar.YEAR, 2021);calendar1.set(Calendar.MONTH, 8);calendar1.set(Calendar.DATE, 2);//日期计算GregorianCalendar calendar2 = new GregorianCalendar(2021,8,2);calendar2.add(Calendar.MONTH, -7);//减7个月calendar2.add(Calendar.DATE, 1);//加1天//日历对象和时间对象间的转化Date d = calendar2.getTime();GregorianCalendar calendar3 = new GregorianCalendar();calendar3.setTime(new Date());}}
Java正确返回月份和星期几:https://blog.csdn.net/lovekang/article/details/84675964
