前言
Java的4类8种基本数据类型,功能过于单一无法满足快速开发需要。
JDK1.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类可以帮我们生成随机数字。
package CommonClass; import java.util.Random; public class Test08 { public static void main(String[] args) { //学习Math.random()返回带正号的double值,该值大于等于0.0且小于1.0 System.out.println(Math.random()); //学习Random类 //(1) 利用带参数构造器创建对象 Random r1 = new Random(System.currentTimeMillis()); int x = r1.nextInt(); System.out.println(x); //(2) 利用空参构造器创建对象:实际底层还是调用了参数为纳秒的有参构造器。 Random random = new Random(); //返回大于等于0且小于10的整数 System.out.println(random.nextInt(10)); //返回大于等于0.0且小于1.0的值 System.out.println(random.nextDouble()); } }
六、String类型
字符串是在编程语言中比较常见的类型,来看看一看Java是如何实现字符串类的。
1.字符串是什么?
1.1:字符串在Java的lang包中实现,所以无需导入直接使用
1.2:The {@code String} class represents character strings.
String这个类就代表一串字符,也就是说羊肉串代表1串羊肉。
All string literals in Java programs, such as {@code "abc"}, are implemented as instances of this class.
在Java中,所有字面常量,例如“abc”,都属于String这个类的实例。
//字符串张根"就是String的1个实例 String a="张根";
1.3.字符串底层就是字符数组
2.字符串常用方法
package string_class; import java.lang.reflect.Array; import java.util.Arrays; public class test01 { public static void main(String[] args) { //字符串张根"就是String的1个实例 String a="张根"; System.out.println(a); //字符串的长度 System.out.println(a.length()); //判断字符串是否为空? System.out.println(a.isEmpty()); //返回字符串中的字符 System.out.println(a.charAt(0)); //判断2个字符串是否相等 String s1="ABC"; String s2="abc222rrrrr"; System.out.println(s2.equals(s1)); //比较2个字符串 // 字符串完全相等返回0, // 部分字符相等,返回不同部分字符ASCII码差值 System.out.println(s1.compareTo(s2)); //字符串的切片 System.out.println(s2.substring(6)); System.out.println(s2.substring(1, 6)); //字符串拼接 System.out.println(s1.concat(s2)); //字符串替换 String s6="live"; System.out.println(s6.replace('i', 'o')); //字符串分割之后变成数组 String s7="l-o-v-e"; System.out.println(Arrays.toString(s7.split("-"))); //大小写转换 System.out.println(s7.toLowerCase()); System.out.println(s7.toUpperCase()); //清除收尾的空格 String s8=" love "; System.out.println(s8.trim()); //其他类型转成字符串类型 System.out.println(String.valueOf(1)); System.out.println(String.valueOf(false)); } }
3.实现Java字符串的优秀源码
字符串equals方法源码
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
字符串compareTo方法源码
public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } return len1 - len2; }
字符串的getChars方法
str.getChars(0, len, Char[], count);
本质是执行了
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
源字符数组[start:end]的这一段,从目标Char[]的第几个位置开始复制,复制到目标Char[]之中。
4.字符串内存分析
在Java中,通过不同的方式创建字符串变量 ,会有不同的不同的内存存储机制。
方式1:字符串拼接创建字符串变量
package strings; public class test01 { public static void main(String[] args) { String s1 = "a" + "b" + "c"; String s2 = "ab" + "c"; String s3 = "a" + "bc"; String s4 = "abc"; String s5 = "abc" + ""; } }
在编译阶段,编译器会对以上代码进行优化,直接合并称为完成的字符串,我们可以反编译验证。
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package strings; public class test01 { public test01() { } public static void main(String[] args) { String s1 = "abc"; String s2 = "abc"; String s3 = "abc"; String s4 = "abc"; String s5 = "abc"; } }
常量池中的值是唯一的
方式2:使用new关键字创建字符串变量
如果我们使用String类实例化的方式,创建字符串变量;
内存就会开辟2个空间;
在堆中开辟内存存储对象,在常量池创建"abc"的值,对象引用常量池中的值
package strings; public class test01 { public static void main(String[] args) { String s5 = "abc" + ""; System.out.println(s1 == s5); String s6 = new String("abc");//true //使用String类实例化创建字符串变量,会在堆中创建对象,再由对象引用常量池中"abc"的值 System.out.println(s5 == s6); //false } }
方式3:有变量参与字符串拼接形式创建字符串变量
在声明字符串变量时,如果有另一个变量参与,编译器就不会对这个变量进行优化;
package strings; public class test01 { public static void main(String[] args) { String a = "abc"; //在编译时编译器,不知道a变量就是"abc"字符串,所以并不会进行编译器优化,直接合并成为1个完成的字符串; String b = a + "def"; System.out.println(b); } }
以上代码在编译时,编译器不知道a变量就是"abc"字符串,所以并不会进行编译器优化,直接合并成为1个完成的字符串;
D:\java_code\Java_code\out\production\Java_code\strings>javap -c test01.class Compiled from "test01.java" public class strings.test01 { public strings.test01(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2 // String abc 2: astore_1 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 10: aload_1 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #6 // String def 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_2 23: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream; 26: aload_2 27: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 30: return }
七、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是线程安全的。
参考