前言
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创建出来的字符串对象,那么最终对象引用指向堆内存地址值。
3.String不可变特点
String不可变的特点:
- 栈内存中的引用的内存地址值可以改变
- 常量池中的字符串内容不可以改变
String s1 = "abc"; System.out.println(s1); //String类不可改变是它本身的特点:具体体现是s1的内容就必须得是"abc"不能发生改变吗?×(可以发生改变) s1 = "aaa";//有使用字符串常量对s1变量进行赋值 System.out.println(s1);
如有想要修改字符串的内容,可以通过修改栈内存中的对象引用达成;
但是只要在常量池中开辟了空间,确定了要保存的数据,那么这个空间中保存的字符串内容就不可以发生改变;
4.String拼接机制
2种不同的字符串拼接方式会在内存中的触发不同的保存机制。
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是线程安全的。
参考