前言
Java的4类8种基本数据类型,功能过于单一无法满足快速开发需要。
Java中内置了很多已经写好的类,这些类具备指定的功能,以供我们来进行使用,避免了重复造轮子。
一、引入包装类
1.包装类是什么?
之前我们定义变量,只能使用Java基本数据类型,这些基本数据类型有4类八种。
- 1 整型: byte 、short 、int 、long
- 2 浮点型:float 、 double
- 3 字符型:char
- 4 布尔型:boolean
对于这些基本数据类型来说,它们值就是1个数字。没有任何方法和属性,不方便我们使用和操作。
所以为了使基本数据类型拥有面向对象的特征,功能更加丰富,Java对它的基本数据类型进行进一步的功能封装,就产生了包装类。
包装类被封装在Java的lang包里,这就意味着我们无需导入直接使用;
2.包装类有哪些?
对应以上的四类八种基本数据类型,包装类有以下。
3.自动装箱和自动拆箱
自动装箱就是自动将基本数据类型转换为包装器类型;
自动拆箱就是 自动将包装器类型转换为基本数据类型;
二、Integer类
Integer类是对int基本数据类型的封装。
package CommonClass;
public class Test01 {
public static void main(String[] args) {
//Interger的属性
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
//物极必反
System.out.println(Integer.MAX_VALUE + 1);
System.out.println(Integer.MIN_VALUE - 1);
//字符串转换成数字
Integer i1 = new Integer("12");
Integer i2 = Integer.parseInt("12");
System.out.println(i1 + i2);
//自动装箱和自动拆箱:int类型和Integer类型之间可以进行快速的类型转换
Integer i3 = new Integer(12);
int num4 = i3;
System.out.println(num4);
//Interger的方法:返回3种结果:小于返回-1/等于返回0/大于返回1
int num5 = i1.compareTo(i2 + i2);//(x < y) ? -1 : ((x == y) ? 0 : 1);
System.out.println(num5);
//equals:Integer类虽然继承了Object类,但是对Object的equals()进行了重新,比较的是底层封装的值,而不是内存地址。
//如果Integer的值在-128到127之间比较值,否则比较对象的地址
Integer i7 = 22;
Integer i8 = 22;
System.out.println(i7 == i8);
System.out.println(i7.equals(i8));
//intValue():将integer转换为int类型
int i9 = i8.intValue();
System.out.println(i9);
//toString():把integer转换为String类型
System.out.println(i7.toString());
}
}
三、时间日期类
在编程的过程中,我们经常从前端或者数据库获取到字符串类型的时间。
这时我们就需要把String类型的时间字符串,转换成便于Java处理的时间格式。
或者把Java中的时间类型,转成字符串响应给浏览器或者存储到数据库。
所以每1种编程语言都会有一种String<====>Date之间的转换机制。
1.java.util.Date
java.util.Date是java中最常用的时间类型,以下的几个时间相关类,我们都围绕java.util.Date展开。
package DateClass;
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date d = new Date();
System.out.println(d);
System.out.println(d.toLocaleString());//2021-11-2 10:17:24
System.out.println(d.getYear());
System.out.println(d.getMonth());
System.out.println(d.getTime()); //时间戳: 1635819720476
System.out.println(System.currentTimeMillis()); //时间戳:1635819720523
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
System.out.println(i);
}
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
}
2.java.sql.Date
package CommonClass;
import java.sql.Date;
public class Test02 {
//导入import java.sql.Date;
public static void main(String[] args) {
//sql.Date继承至java.util.Date;public class Date extends java.util.Date
Date d = new Date(1635820261871L);
System.out.println(d);
/*
*(1)java.util.Date和java.sql.Date的区别?
*java.util.Date:年月日 时分秒
* java.sql.Date:只有年月日
*
* (2)java.util.Date和java.sql.Date的联系?
*java.util.Date是java.sql.Date的父类:java.sql.Date继承至java.util.Date
*
*(3)java util.Date和java.sql.Date相互转换。
*java.util.Date转换成java.sql.Date
*
*
* */
//java.util.Date转换成java.sql.Date类型
java.util.Date date = new java.sql.Date(1635820261871L); //创建java.util.Date的对象
//方式1:向下转型:就是把父类强转成子类
java.sql.Date date1 = (java.sql.Date) date; //把java.util.Date转换成java.sql.Date
System.out.println(date1);
//方式2:利用构造器
Date date2 = new Date(date.getTime());
//java.sql.Date转换成java.sql.util类:向上转换,父类引用指向子类对象,是自动进行的!
java.util.Date date3 = new java.sql.Date(1635820261871L);
}
}
3.String转换成java.util.Date类型
日常开发过程中,我们经常需要把字符串(String)转换成时间类型。
package CommonClass;
public class Test03 {
public static void main(String[] args) {
//(1)String转成java.sql.Date
java.sql.Date date=java.sql.Date.valueOf("2015-6-25");
//(2)java.sql.Date转成java.util.Date
java.util.Date date1=date;
System.out.println(date.getTime());
//以上代码有局限性,字符串的格式只能是年-月-日(2015-6-25)形式,换成其他格式(2015/6/25)就会出现以下异常。
/*
* Exception in thread "main" java.lang.IllegalArgumentException
at java.sql.Date.valueOf(Date.java:143)
at CommonClass.Test03.main(Test03.java:6)
* */
}
}
以上代码有局限性,字符串的格式只能是年-月-日(2015-6-25)形式,如果是其他格式(2015/6/25)就会出现以下异常。
Exception in thread "main" java.lang.IllegalArgumentException
at java.sql.Date.valueOf(Date.java:143)
at CommonClass.Test03.main(Test03.java:6)
4.自定时间转换格式
我们可以自定义一种时间格式,用于完成Date和String之间的相互转换;
package CommonClass;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test03 {
public static void main(String[] args) {
//1.自定义一种时间格式化标准
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//2.使用parse把String转成java.util.Date
try {
Date date = df.parse("2019-04-06 12:23:54");
System.out.println(date);
} catch (ParseException e) {
System.out.println("转换失败");
}
System.out.println(df);
//3.使用format把Date(java.util.Date)转成String
String curentTime = df.format(new Date());
System.out.println(curentTime);
}
}
5.Calendar
我们可以使用Calendar类获取当前时间的年月日,也可以给Calendar设置一个其他的时间。
package CommonClass;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class Test04 {
public static void main(String[] args) {
//1.Calendar是一个抽象类,不可以直接创建对象
Calendar cal = new GregorianCalendar();
Calendar cal2 = Calendar.getInstance();
System.out.println(cal);
//2.Calendar常用的方法
//2.1get方法
System.out.println(cal.get(Calendar.YEAR));
System.out.println(cal.get(Calendar.MONTH));
System.out.println(cal.get(Calendar.DATE));
System.out.println(cal.get(Calendar.DAY_OF_WEEK)); //获取星期
//获取当月日期的最大天数
System.out.println(cal.getActualMaximum(Calendar.DATE));
//获取当月日期的最小天数
System.out.println(cal.getActualMinimum(Calendar.DATE));
//2.2 set方法:可以改变Calendar中的年、月、日等时间信息
cal.set(Calendar.YEAR, 1993);
cal.set(Calendar.MONTH, 6);
cal.set(Calendar.DATE, 28);
System.out.println(cal.get(Calendar.YEAR)); //获取年
System.out.println(cal.get(Calendar.MONTH));//获取月
System.out.println(cal.get(Calendar.DATE)); //获取日
//String--->Calendar的转换
java.sql.Date date = java.sql.Date.valueOf("2020-02-20");
cal.setTime(date);//把时间设置成2020-02-20
System.out.println(cal.get(Calendar.YEAR));
System.out.println(cal.get(Calendar.MONTH));
System.out.println(cal.get(Calendar.DATE));
}
}
6.日历
package CalendarClass;
import java.util.Calendar;
import java.util.Scanner;
import java.util.Calendar;
public class Test01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请录入你想要查看的日期 例如 2021-02-22的格式:");
String strDate = sc.next();
java.sql.Date sqlDate = java.sql.Date.valueOf(strDate);
Calendar cal = Calendar.getInstance();
cal.setTime(sqlDate);
//获取今天是几号
int currentDay = cal.get(Calendar.DATE);
//获取本月的最大天数
int maxDay = cal.getActualMaximum(Calendar.DATE);
//将日期调为本月1号
cal.set(Calendar.DATE, 1);
//获取本月的1号是星期几
int firstDayOfWeek = cal.get(Calendar.DAY_OF_WEEK) - 1;
int counter = 0;
System.out.println("日\t一\t二\t三\t四\t五\t六\t");
for (int i = 0; i < firstDayOfWeek; i++) {
System.out.print("\t");
}
counter = counter + firstDayOfWeek;
for (int i = 1; i < maxDay; i++) {
if (i == currentDay) {
System.out.printf("*%d\t", i);
} else {
System.out.printf("%d\t", i);
}
counter++;
if (counter % 7 == 0) {
System.out.println();
}
}
}
}
7.LocalDateTime
JDK1.0中使用的java.util.Date类,属于第一批日期时间API
JDK1.1引入Calendar类,属于第二批日期时间API
但是JDK1.1有以下缺陷
可变性:像日期和时间这样的类应该是不可变的。
偏移性:Date中的年份是从1900开始,而月份是从0开始。
格式化:格式化只对Date有用,Calendar则不行。
在JDK1.8之后新增了第3批日期时间API,LocalDate、LocalTime、LocalDateTime。
package CommonClass;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class Test05 {
public static void main(String[] args) {
//1.完成实例化
//方法1:now():获取当前的日期,时间,日期+时间
LocalDate localDate = LocalDate.now(); //2021-11-04
System.out.println(localDate);
LocalTime localTime = LocalTime.now(); //15:03:35.536
System.out.println(localTime);
LocalDateTime localDateTime = LocalDateTime.now(); //2021-11-04T15:03:35.537
System.out.println(localDateTime);
//方法2:of():设置日期,时间,日期+时间
LocalDate of = LocalDate.of(2010, 5, 6);
System.out.println(of);
LocalTime of1 = LocalTime.of(12, 35, 56);
System.out.println(of1);
LocalDateTime of2 = LocalDateTime.of(2010, 12, 16, 23, 3);
System.out.println(of2);
//LocalDate和LocalTime用的不如LocalDateTime多
//get()
System.out.println(localDateTime.getYear()); //年
System.out.println(localDateTime.getMonth()); //月
System.out.println(localDateTime.getMonthValue()); //月份数字
System.out.println(localDateTime.getDayOfMonth()); //日
System.out.println(localDateTime.getDayOfWeek()); //星期几
System.out.println(localDateTime.getHour()); //时
System.out.println(localDateTime.getMinute()); //分
System.out.println(localDateTime.getSecond()); //秒
//with:
LocalDateTime localDateTime1 = localDateTime.withMonth(12);
System.out.println(localDateTime1);
//LocalDateTime使用with()修改时间,修改的是1个新的对象。具有不可变性
System.out.println(localDateTime);
//LocalDateTime提供了加、减操作
LocalDateTime localDateTime2 = localDateTime.plusMonths(4);//4个月之后
System.out.println(localDateTime2);
LocalDateTime localDateTime3 = localDateTime.minusMonths(4); //4个月之前
System.out.println(localDateTime3);
}
}
8.DateTimeFormatter
我们可以使用DateTimeFormatter,完成LocalDateTime和String之间的相互转换。
package CommonClass;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAccessor;
public class Test06 {
public static void main(String[] args) {
//方式一:
//formatter可以帮助我们完成LocalDateTime和String之间的相互转换
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
//LocalDateTime-->String
LocalDateTime now = LocalDateTime.now();
String dateString = formatter.format(now);
System.out.println(dateString); //2021-11-04T16:00:02.718
//String-->LocalDateTime
TemporalAccessor date = formatter.parse("2021-11-04T16:00:02.718");
System.out.println(date);
//方式二:
//参数:
/*
FormatStyle.LONG--2021年11月4日 长格式
FormatStyle.MEDIUM---2021-11-4 中格式
FormatStyle.SHORT:21-11-4 短格式
*/
DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);
//LocalDateTime-->String
LocalDateTime now1 = LocalDateTime.now();
String dateString1 = formatter1.format(now1);
System.out.println(dateString1);
//String-->LocalDateTime
TemporalAccessor date1 = formatter1.parse("21-11-4");
System.out.println(date1);
//方式三:较为常用
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
//LocalDateTime-->String
LocalDateTime now2 = LocalDateTime.now();
String dateString2 = formatter2.format(now2);
System.out.println(dateString2); //2021-11-04 04:15:22
//String-->LocalDateTime
TemporalAccessor date2 = formatter2.parse("2021-11-04 04:15:22");
System.out.println(date2);
}
}
四、Math类
Math类是封装在java.lang中常用类,对外提供一些数学运算的API。
package CommonClass;
// import static java.lang.Math.*;
// 静态导入时:如果本类中有静态方法和导入的包同名,会优先执行本类中的静态方法,遵循就近原则。
public class Test07 {
public static void main(String[] args) {
//math常用属性
System.out.println(Math.PI);
//math常用方法
System.out.println("随机数" + Math.random());//随机返回0.0 -1.0之间的小数
System.out.println("绝对值" + Math.abs(-90));
System.out.println("向上取值" + Math.ceil(9.1)); //天花板:ceiling
System.out.println("向上取值" + Math.floor(9.9)); //地板:floor
System.out.println("四舍五入" + Math.round(3.8));
System.out.println("比较2数取大" + Math.max(6, 9));
System.out.println("比较2数取小" + Math.min(6, 9));
}
}
五、随机数生成
java.util.Random类可以帮我们生成随机数,但是不可以指定生成随机数的下限。
不过我们可以通过(n-m+1)+n这个公式生成指定区间的随机整数。
//生成m-n之间的随机数字
//(n-m+1)+n
public class RandomDemo3 {
public static void main(String[] args) {
Random random = new Random();
//生成50-100之间的随机数
int randomInt = random.nextInt(100 - 50 + 1) + 50;
System.out.println(randomInt);
}
}
六、String类型
String类在java.lang包下,开发过程中我们使用这个包中类的频率比较高,所以Java规定使用java.lang包时无需导包,直接使用即可;
字符串:Java程序中所有双引号包裹的内容都是String类的对象,就可以调用String类的方法。
1.字符串对象4种创建方式
在Java中可通过4种方式方式创建1个String对象,这4种方式创建的String对象在内存中位置也是不同的。
- 前3种String创建方式,都使用了new关键字
- 而第4种直接使用字符串常量进行赋值
//在Java中可通过4种方式方式,创建1个String对象,这4种方式创建的String对象在内存中位置也是不同的
//方式1.无参构造器创建
String s1 = new String();
//方式2.字符数组参数
char[] charArray = new char[]{'A', 'B', 'C'};
String s2 = new String(charArray);
//方式3.字符串常量参数
String s3 = new String("abc");
//方式4.以上3种String创建方式,都使用了new关键字,而第4种直接使用字符串常量进行赋值
String s4 = "ABC";
String s5 = "ABC";
String s6 = "abc";
2.字符串4种初始化机制
以上4种创建字符串对象的方式,都会触发内存中不同的初始化机制。
2.1.方式1
使用new关键字创建出来的String对象,会在堆内存中开存空间保存具体的内容,s1是栈内存中的1个引用;
2.2.方式2:
先在堆内存中创建chars字符数组
new String的时候通过读取chars字符数组的值,将字符拼接成1个字符串
char[] chars = new char[]{'A', 'B', 'C'};
String s2 = new String(chars);
由于S2是new出来的在堆内存保存内容。
2.3.方式3
2.3.1.String常量池是什么?
由于开发过程中字符串的使用非常非常频繁;
如果多个字符串保存的内容是一样的,这些内容相同的字符串对象都在堆内存中保存具体的值, 势必造成堆内存消耗严重。
针对以上问题,Java会在堆内存中单独开辟一块用于保存字符串内容的内存空间,称为字符串常量池。
常量池中专门保存字符串常量,用于给栈内存中的字符串引用,提供共享的数据。
2.3.2.String常量池触发机制
只要在Java代码中出现了String常量,就会触发常量池,这种内存优化机制;
String s3 = new String("abc");
当系统执行这上一行代码时,JVM检测到代码中出现了1个"abc"字符串常量,就会触发常量池内存优化机制:
检查常量池中是否已经存在"abc"这个字符串?
2.3.2.1.机制A
如果常量池中不存在内容"abc",则在常量池空间中开辟1块内存空间用于保存内容"abc",并且给这块空间生成1个地址值(Ox11);
由于字符串对象s3是new出来的,还需要在堆内存开辟内存保存 “abc” 在常量池中的内存地址值(Ox11);
所以很对人说这种创建String的方式,“创建了2个字符串对象”。
2.3.2.2.机制B
如果常量池中存在内容"abc",则返回该内容在常量池中的内存地址值,无需再另外开辟堆内存空间了。
String s4="ABC"; //没有使用new就不会开辟堆内存空间,直接指向常量池中的内存地址
String s5="ABC";
String s6="abc";
/*
JVM检测到"ABC"这个字符串常量,触发常量池内存优化机制:
先判断常量池中有没有"ABC""这个字符串?
如果常量池中没有"ABC,没有就在常量池中开辟1块内存空间保存内容"abc"
如果常量池中有"ABC",直接返1个内存地址值"
*/
内存图
2.3.3.String常量池总结
- 字符串常量池本质是在多个字符串内容相同的情况下,对堆内存的优化机制。
- 通过new创建出来的字符串对象,那么最终对象引用指向堆内存地址值。
- 通过常量赋值创建出来的字符串对象,直接指向常量池的地址值,无需另外开辟堆内存空间了,
- 1个变量引用只能指向1个堆内存地址(堆内存单独开辟空间)
- 在内容相同的情况下,多个变量引用可以指向同1个字符串常量池的内存地址 (常量池在内容相同的情况下,可以共享数据);
3.String不可变特点
String不可变的特点:
- 栈内存中的引用的内存地址值可以改变
- 常量池中的字符串内容不可以改变
String s1 = "abc";
System.out.println(s1);
//String类不可改变是它本身的特点:具体体现是s1的内容就必须得是"abc"不能发生改变吗?×(可以发生改变)
s1 = "aaa";//有使用字符串常量对s1变量进行赋值
System.out.println(s1);
如有想要修改字符串的内容,可以通过修改栈内存中的对象引用达成;
但是只要在常量池中开辟了空间,确定了要保存的数据,那么这个空间中保存的字符串内容就不可以发生改变;
当执行s1="aa"的时候,并没有修改oxa空间里面的内容"abc",而是在常量池中新开辟一块新的空间保存“aaa”,并且把新的地址值赋值给s1,从而完成s1内容的修改。
4.String拼接机制
2种不同的字符串拼接方式会在内存中的触发不同的保存机制。
如果字符串拼接时有变量参与,那么JVM会创建1个StringBuilder对象(字符串拼接对象)来完成拼接,类对象需要new进行实例化,最终字符串内容会保存在堆内存里。
如果字符串拼接时没有变量参与,可以无视拼接符,最终字符串内容会保存在常量池里。
5.String对比机制
- 如果相等比较运算符(==)比较的是两个基本数据类型,比较的是具体的内容!
- 如果相等比较运算符(==)比较的是两个引用数据类型,实际上是比较左右两个变量引用的堆内存地址值!
/*
* 相同比较运算符(==)可以用于比较两个基本数据类型是否相等?也可以用于比较两个引用数据类型是否相等?
* ==比较两个基本数据类型,比较的是具体的内容
* ==比较两个引用数据类型,实际上是比较左右两个变量引用的堆内存地址值是否相等?
* */
int[] arr1 = new int[]{1, 2, 3}; //arr1->保存在堆内存的-->oxa地址
int[] arr2 = new int[]{1, 2, 3}; //arr2->保存在堆内存的-->oxb地址
System.out.println(arr1 == arr2); //对比结果就是false
6.String常用方法(API)
了解了字符串的创建、不可变、拼接和对比的底层机制之后,就可以在编程时,更加正确、合理地使用字符串了;
String str = "今天天气不错,电闪雷鸣";
//1.获取字符串长度,数组是通过length属性
System.out.println(str.length());
//2.通过字符索引获取字符
char c = str.charAt(1);
System.out.println(c);
//3.获取字符在字符串中的索引
int indexOne = str.indexOf("天");
System.out.println(indexOne);
int indexTwo = str.indexOf("田七");
System.out.println(indexTwo);
//4.判断字符串包含关系
boolean isContains = str.contains("天");
System.out.println(isContains);
//5.字符串拼接返回1个新字符串
String newString = str.concat("我要去放风筝");
System.out.println(newString);
七、StringBuilder和StringBuffer
在Python中只有1种字符串,而Java的字符串分2类3种:
- 不可变字符串:Sting
- 可变字符串:StringBuilder、StringBuffer
既然Java中已经有了String,为什么又有了StriingBuilder和StringBufer呢?
因为StringBuilder和StringBuffer是可变的数据类型,相对于String来说会更节省内存;
那么StringBuilder和StringBufferr又是如何实现可变的呢?
1.为什么StringBuilder是可变数据类型?
因为StringBuilder对象,包含value和count这2个属性,其中value属性为字符数组,count保存长度。
对StringBuilder做的修改操作,例如append、insert、delete其本质都是对象的value属性也就是
value = new char[capacity];
在执行getChars操作,然后
System.arraycopy(value, end, value, start + len, count - end);
//str是传进来的字符串参数
str.getChars(value, start);
count = newCount;
return this;
而getChars的本质就是
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
所以对象的的属性改变了,但对象本身的内存地址不变;
package string_class;
import OOP.Test;
abstract class Test01 {
String name = "111";
Test01() {
}
public void walk() {
System.out.println(this.name);
}
}
public class test02 extends Test01 {
public static void main(String[] args) {
test02 t = new test02();
test02 t3 = new test02();
t.name = "t1";
t.walk();
t3.name = "t3";
t3.walk();
//创建1个StringBuilder的对象
String s1 = "wabcdefghizk";
char[] val = new char[6];
s1.getChars(1, 3, val, 4);
System.out.println(val);
//表面上调用1个StringBuilder()的空构造器,
// 其实底层是super(16),对value数组进行初始化,并且初始化的长度为16;
StringBuilder sb1 = new StringBuilder();
// 表面上调用 StringBuilder(3)的有参构造器,传入1个int类型的数字
// 实际底层就是对value数组进行初始化操作,长度为传入的数组
StringBuilder sb2 = new StringBuilder(3);
//表面上调用的是 StringBuilder("ABC")StringBuilder的有参构造器
// 实际底层super(str.length() + 16);
StringBuilder sb3 = new StringBuilder("ABC");
//append().apend()链式操作
sb1.append("def").append("class");
//StringBuilder是可变类型
System.out.println(sb1==sb1.append("www"));
}
}
2.StringBuilder常用的方法
package string_class;
public class Test03 {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("abc");
//增
sb.append("这是梦想");
//删
sb.delete(3, 6);
//插入
sb.insert(3, "s");
char c1 = sb.charAt(3);
System.out.println(c1);
//替换
sb.replace(3, 5, "我好累");
//切片:返回1个新的string
String subs=sb.substring(2,3);
}
}
2.StringBuilder和StringBuffer区别?
两者使用方法一致,唯一不同是StringBuffer是线程安全的。