第十三章、Java基础补充----常用类
本人也是刚入门Java语言,可能会有一些地方出现错误,描述的不对。如果发现不对的地方请及时指出,好对其进行修改。这样不仅可以让我学到东西,也可以让其他刚入门的朋友学习更正确的内容。
所有内容仅供参考。不具有完全的准确性!
注:关于Java的所有内容都会参考到尚硅谷在网上公开的学习视频及其提供的PPT
一、字符串相关的类
(一)String类
- String是一个final类,代表不可变的字符序列。
- String对象的字符内容是存储在一个字符数组value[]中的。在JDK 1.8 以后,改用byte类型的数组存放。
JDK 1.7 及以前
JDK 1.8以后:
- String 实现了Serializable接口,表示字符串是支持序列化的
实现了Comparable接口,表示字符串是可以进行比较的 - String内部定义了final char[] value用于存储字符串数据 (字符串是一个char型的数组,数组长度确定后是不能修改的,即下边所述的不可变性。即使修改后的长度和原有的相同,也需要重新指定一块内存区。因为char数组是final的)
- String:代表不可变的字符序列,即:不可变 性 (表示在方法区中的值是不可以修改的,当修改Sring变量值时,其实是在方法区中重新指定了一块内存区,而不是修改了原有的内存区信息)
体现:- 当对字符串重新赋值时,需要重新指定内存区赋值,不能使用原有value(final char[] value)内存区进行赋值
- 当对现有的字符串进行连接操作时,也需要重新指定内存区赋值,不能使用原有value(final char[] value)内存区进行赋值
- 当调用String类中的replace()修改指定字符或字符串时,也需要重新指定内存区赋值,不能使用原有value(final char[] value)内存区进行赋值replace(char oldChar,char newChar)方法修改的是指定字符串中所有的oldchar
- 通过字面量的方式给字符串赋值(区别于new的方式),此时的字符串值声明在字符串常量池中。
字面量定义方式 —>直接赋值的方式 - 字符串常量池中是不会存放相同内容的字符串的,(如果有两个变量的值相同吗,则指向同一个内存区)
String s1 = "abc";//字面量的定义方式
String s2 = "abc";
System.out.println(s1 == s2);//比较s1和s2的地址值
s1 = "hello";
System.out.println(s1 == s2);//再比较s1和s2的地址值
System.out.println(s1);//hello
System.out.println(s2);//abc
输出结果:
true
false
hello
abc
关于不可变性:
面试题:String s = new String(“abc”);这种方式创建对象,在内存中实际创建了几个对象?
答案:两个,一个在堆空间中new结构,另一个对应char[]对应常量池中"abc"
String类型与 char[]型数组之间的转换:
String —> char[] 使用toCharArray() char[] ch = s1.toCharArray();
char[] —> String 调用String构造器 String s2 = new String(chars);
public void method4(){
String s1 = new String("abc123");
char[] ch = s1.toCharArray();
for (int i = 0; i < ch.length; i++) {
System.out.println(ch[i]);//abc123
}
char[] chars = {'a','b','v'};
String s2 = new String(chars);
System.out.println(s2);
}
String 与 byte[]型数组之间的转换
String —> byte[] 使用getBytes() byte[] b = s1.getBytes();
byte[] —> String 调用String构造器
public void method5(){
String s1 = "abc123";
byte[] b = s1.getBytes();
// for (int i = 0; i < b.length; i++) {
// System.out.print(b[i] + "\t");//97 98 99 49 50 51
// }
//或
System.out.println(Arrays.toString(b));
String s2 = new String();
System.out.println(s2);
}
(二)StringBuffer
也是字符串类
java.lang.StringBuffer代表可变的字符序列,JDK1.0中声明,可以对字符串内容进行增删,此时不会产生新的对象。(可变字符序列也就是可以在原有内存的基础上进行值的修改。)
很多方法与String相同。
作为参数传递时,方法内部可以改变值。
StringBuffer类不同于String,其对象必须使用构造器生成。有三个构造器:
StringBuffer():初始容量为16的字符串缓冲区
StringBuffer(int size):构造指定容量的字符串缓冲区
StringBuffer(String str):将内容初始化为指定字符串内容
public void method2(){
StringBuffer sb1 = new StringBuffer("abc");
sb1.append(1);
System.out.println(sb1);//abc1
sb1.append('2');
System.out.println(sb1);//abc12
sb1.append(false);
System.out.println(sb1);//abc12false
System.out.println(sb1.length()); //10 (虽然有boolean型,但实际仍是根据字符串长度)
//删除指定范围内容 从 2开始(包含)到4(不包含)
sb1.delete(2,4);
System.out.println(sb1);//ab2false
//删除指定索引内容
sb1.deleteCharAt(1);
System.out.println(sb1);//a2false
//在指定索引位置插入字符或字符串,原位置上的字符或字符串后移
sb1.insert(2,'a');
System.out.println(sb1);
//转为逆序
System.out.println(sb1.reverse());//eslafa2a
}
(三)StringBuilder
同为字符串类
和StringBuffer几乎完全一样,区别就是StringBuffer是线程安全的,
StringBuilder线程不安全。
(四)String、StringBuffer、StringBuilder三者的区别
-
String:不可变的字符序列;底层使用char[]存储
-
StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]存储
-
StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储
-
在JDK 8.0以后底层都改为了byte数组。
-
三者的效率:
从高到低排列:StringBuilder > StringBuffer > String
二、JDK8之前日期时间API
- System类中currentTimeMillis();
- java.util.Date和子类java.sql.Date
- SimpleDateFormat
- Calendar
(一)System类的方式
System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。
此方法适于计算时间差
例:
long l = System.currentTimeMillis();
System.out.println(l);
输出结果
1609071625604
(二) java.util.Date及子类java.sql.Date
- java.util.Date类
- 两个构造器的使用
构造器一:Date():创建一个对应当前时间的Date对象
构造器二:创建指定毫秒数的Date对象 - 两个方法的使用
toString():显示当前的年、月、日、时、分、秒
getTime():获取当前Date对象对应的毫秒数。(时间戳) - java.sql.Date对应着数据库中的日期类型的变量
如何实例化
如何将java.util.Date对象转换为java.sql.Date对象
- 两个构造器的使用
@Test
public void test2(){
//构造器一:Date():创建一个对应当前时间的Date对象
// java.util.Date类
Date date1 = new Date();
System.out.println(date1.toString());//Sat Feb 16 16:35:31 GMT+08:00 2019
System.out.println(date1.getTime());//1550306204104
//构造器二:创建指定毫秒数的Date对象
Date date2 = new Date(155030620410L);
System.out.println(date2.toString());//Sat Nov 30 16:03:40 CST 1974
//创建java.sql.Date对象
java.sql.Date date3 = new java.sql.Date(35235325345L);
System.out.println(date3);//1971-02-13
//如何将java.util.Date对象转换为java.sql.Date对象
//情况一:
// Date date4 = new java.sql.Date(2343243242323L);
// java.sql.Date date5 = (java.sql.Date) date4;
//情况二:
Date date6 = new Date();
java.sql.Date date7 = new java.sql.Date(date6.getTime());
}
(三)java.text.SimpleDataFormat类
Date类的API不易于国际化,大部分被废弃了
SimpleDateFormat 对日期Date类的格式化和解析
-
两个操作:
① 格式化:日期 —>字符串
② 解析:格式化的逆过程,字符串 —> 日期 -
SimpleDateFormat的实例化:new + 构造器
// 创建指定格式的SimpleDateFormat类的对象
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date1 = new Date();
//格式化
String str = simpleDateFormat1.format(date1);
System.out.println(str);
//解析
Date date2 = simpleDateFormat1.parse(str);
System.out.println(date2);
输出结果
2020-12-27 08:38:59
Sun Dec 27 08:38:59 CST 2020
将字符串格式转换为sql下的Date
public void testExer() throws ParseException {
String birth = "2020-1-1";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
//字符串格式和SimpleDateFormat类的指定格式必须一样
Date date = simpleDateFormat.parse(birth);
System.out.println(date);
//先转换成util下的Date 再通过util下的Date调用getTime()转换成sql下的Date
java.sql.Date birthDate = new java.sql.Date(date.getTime());
System.out.println(birthDate);
}
注意:解析的时候,字符串的格式必须严格按照SimpleDateFormat类的对象指定的格式,否则会抛异常
(四)Calendar类:日历类
Calendar是一个抽象基类,主用用于完成日期字段之间相互操作的功能。
public void testCalender(){
//实例化
//方式一: 创建其子类GregorianCalendar的对象 ---省略
//方式二:调用其静态方法getInstance()
Calendar instance = Calendar.getInstance();
//此时返回的是class java.util.GregorianCalendar的对象
System.out.println(instance.getClass());//java.util.GregorianCalendar
//常用方法
//get 返回给定日历字段的值。
int days = instance.get(Calendar.DAY_OF_WEEK);
System.out.println(days);
System.out.println(instance.get(Calendar.DAY_OF_YEAR));
//set 将给定的日历字段设置为给定值。
instance.set(Calendar.DAY_OF_WEEK, 6);
days = instance.get(Calendar.DAY_OF_WEEK);
System.out.println(days);
System.out.println(instance.get(Calendar.DAY_OF_YEAR));//332
//add 为给定的日历字段添加或减去指定的时间量。
instance.add(Calendar.DAY_OF_YEAR,-331);//表示将获取到的时间减去331天
days = instance.get(Calendar.DAY_OF_WEEK);
System.out.println(days);
System.out.println(instance.get(Calendar.DAY_OF_YEAR));
//getTime 返回一个表示此 Calendar 时间值(从历元至现在的毫秒偏移量)的 Date 对象。
// Calendar ---> Date
Date date = instance.getTime();
System.out.println(date); //Wed Jan 01 14:35:15 CST 2020 星期 月 日 CST:中部标准时间(Central Standard Time)
//setTime 使用给定的 Date 设置此 Calendar 的时间。
//注 :使用 Date(Long.MAX_VALUE) 或 Date(Long.MIN_VALUE) 调用 setTime() 可能产生来自 get() 的错误字段值。
// Date ---> Calendar
instance.setTime(date);
days = instance.get(Calendar.DAY_OF_WEEK);
System.out.println(days);
}
注意:
获取月份时:一月是0,二月是1,以此类推,12月是11
获取星期时:周日是1,周二是2 , 。。。。周六是7
三、JDK8中新日期时间API
(一)为什么要引入新的时间API
因为Date类和Calendar类存在一些问题:
可变性:像日期和时间这样的类应该是不可变的。
偏移性:Date中的年份是从1900开始的,而月份都从0开始。
格式化:格式化只对Date用,Calendar则不行。
此外,它们也不是线程安全的;不能处理闰秒等。
例:
public void testDate(){
//偏移量
Date date1 = new Date(2020,9,8);
System.out.println(date1);
}
当我们想获取2020年9月8号这一天,但此时得到的结果却并不是我们想要的。
输出结果:
Fri Oct 08 00:00:00 CST 3920
发现输出的是3920年,比我们想要的多了1900年。月份也比我们想要的多了一个月。这就是偏移量的问题。
在JDK 8.0版本发布了新的时间相关API以后,就完全解决了以上问题。
(二)java 8 中新的日期时间API涉及到的包
(三)java 8 中新的日期时间提供的类
本地日期 - - - LocalDate
本地时间 - - - LocalTime
本地日期时间 - - - LocalDateTime
时区 - - - ZonedDateTime
持续时间 - - - Duration
- LocalDateTime相较于LocalDate、LocalTime,使用频率要高
- 类似于Calendar
实例化:
//now():获取当前的日期、时间、日期+时间
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDate);
System.out.println(localTime);
System.out.println(localDateTime);
//of():设置指定的年、月、日、时、分、秒。没有偏移量
LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 43);
System.out.println(localDateTime1);
常用方法
//getXxx():获取相关的属性
System.out.println(localDateTime.getDayOfMonth());
System.out.println(localDateTime.getDayOfWeek());
System.out.println(localDateTime.getMonth());
System.out.println(localDateTime.getMonthValue());
System.out.println(localDateTime.getMinute());
//体现不可变性
//withXxx():设置相关的属性
LocalDate localDate1 = localDate.withDayOfMonth(22);
System.out.println(localDate);
System.out.println(localDate1);
LocalDateTime localDateTime2 = localDateTime.withHour(4);
System.out.println(localDateTime);
System.out.println(localDateTime2);
//不可变性
LocalDateTime localDateTime3 = localDateTime.plusMonths(3);
System.out.println(localDateTime);
System.out.println(localDateTime3);
LocalDateTime localDateTime4 = localDateTime.minusDays(6);
System.out.println(localDateTime);
System.out.println(localDateTime4);
(四)Instant的使用
- 时间线上的一个瞬时点。 这可能被用来记录应用程序中的事件时间戳。
- Instant的精度可以达到纳秒级。
- 类似于 java.util.Date类
ublic void test2(){
//now():获取本初子午线对应的标准时间
Instant instant = Instant.now();
System.out.println(instant);//2019-02-18T07:29:41.719Z
//添加时间的偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8)); //表示东八区
System.out.println(offsetDateTime);//2019-02-18T15:32:50.611+08:00
//toEpochMilli():获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数 ---> Date类的getTime()
long milli = instant.toEpochMilli();
System.out.println(milli);
//ofEpochMilli():通过给定的毫秒数,获取Instant实例 -->Date(long millis)
Instant instant1 = Instant.ofEpochMilli(1550475314878L);
System.out.println(instant1);
}
(五)日期时间格式化类:DateTimeFormatter
-
格式化或解析日期、时间
-
类似于SimpleDateFormat
-
预定义的标准格式。
如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG)
自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)
方式一:预定义
// 方式一:预定义的标准格式
DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_DATE_TIME;
//格式化 : 日期 --->字符串
LocalDateTime now = LocalDateTime.now();
String format = isoDateTime.format(now);
System.out.println(now); //2020-11-29T15:01:31.485934300 日期格式
System.out.println(format);//2020-11-29T15:01:01.848602 字符串格式
//解析 字符串 ---> 日期
//格式必须保持一致
TemporalAccessor parse = isoDateTime.parse("2020-11-29T15:01:31.4859343");
System.out.println(parse);//{},ISO resolved to 2020-11-29T15:01:31.485934300
方式二:本地化相关的格式
DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
//格式化
String str3 = formatter2.format(LocalDate.now());
System.out.println(str3);//2019-2-18
//解析方式与方式一相同
方式三:自定义的格式(重要)
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
//格式化
String str4 = formatter3.format(LocalDateTime.now());
System.out.println(str4);//2019-02-18 03:52:09
//解析
TemporalAccessor accessor = formatter3.parse("2019-02-18 03:52:09");
System.out.println(accessor);
四、Java比较器
Java中的对象,正常情况下,只能进行比较:== 或 != 。不能使用 > 或 < 的但是在开发场景中,我们需要对多个对象进行排序,就需要比较对象的大小。就用到了下边两个排序接口:
自然排序:java.lang.Comparable
定制排序:java.util.Comparator
(一)自然排序
- 像String、包装类等也都实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。
- 像String、包装类重写compareTo()方法以后,进行了从小到大的排列
- 重写compareTo(obj)的规则:
如果当前对象this大于形参对象obj,则返回正整数,
如果当前对象this小于形参对象obj,则返回负整数,
如果当前对象this等于形参对象obj,则返回零。 - 对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。在compareTo(obj)方法中指明如何排序
public class Goods implements Comparable{
private String name;
private double price;
//指明商品比较大小的方式:照价格从低到高排序,再照产品名称从高到低排序
@Override
public int compareTo(Object o) {
// System.out.println("**************");
if(o instanceof Goods){
Goods goods = (Goods)o;
//方式一:
if(this.price > goods.price){
return 1;
}else if(this.price < goods.price){
return -1;
}else{
// return 0;
return -this.name.compareTo(goods.name);
}
//方式二:
// return Double.compare(this.price,goods.price);
}
// return 0;
throw new RuntimeException("传入的数据类型不一致!");
}
// getter、setter、toString()、构造器:省略
}
(二)定制排序
- 背景:
当元素的类型没实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序 - 重写compare(Object o1,Object o2)方法,比较o1和o2的大小:
如果方法返回正整数,则表示o1大于o2;
如果返回0,表示相等;
返回负整数,表示o1小于o2。
Comparator com = new Comparator() {
//指明商品比较大小的方式:照产品名称从低到高排序,再照价格从高到低排序
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Goods && o2 instanceof Goods){
Goods g1 = (Goods)o1;
Goods g2 = (Goods)o2;
if(g1.getName().equals(g2.getName())){
return -Double.compare(g1.getPrice(),g2.getPrice());
}else{
return g1.getName().compareTo(g2.getName());
}
}
throw new RuntimeException("输入的数据类型不一致");
}
}
(三)比较两种方式
Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
Comparator接口属于临时性的比较。
五、其他类
(一)System类
- System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。该类位于java.lang包。
- 由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便的进行调用。
- 常用方法:
native long currentTimeMillis()
void exit(int status)
void gc()
String getProperty(String key)
public void test1() {
String javaVersion = System.getProperty("java.version");
System.out.println("java的version:" + javaVersion);
String javaHome = System.getProperty("java.home");
System.out.println("java的home:" + javaHome);
String osName = System.getProperty("os.name");
System.out.println("os的name:" + osName);
String osVersion = System.getProperty("os.version");
System.out.println("os的version:" + osVersion);
String userName = System.getProperty("user.name");
System.out.println("user的name:" + userName);
String userHome = System.getProperty("user.home");
System.out.println("user的home:" + userHome);
String userDir = System.getProperty("user.dir");
System.out.println("user的dir:" + userDir);
}
(二)Math类
java.lang.Math提供了一系列静态方法用于科学计算。其方法的参数和返回值类型一般为double型。
常用方法:
(三)BigInteger
Integer类作为int的包装类,能存储的最大整型值为2^31-1, Long类也是有限的,最大为2^63 - 1。如果要表示再大的整数,不管是基本数据类型还是他们的包装类都无能为力,更不用说进行运算了。
java.math包的BigInteger可以表示不可变的任意精度的整数。当需要使用的数值超出了Long类型的长度时,就可以使用该类。
BigInteger bi = new BigInteger("1243324112445554485234324324325235245346567657653");
常用方法:
(四)BigDecimal
一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中,要求数字精度比较高,故用到java.math.BigDecimal类。
BigDecimal类支持不可变的、任意精度的有符号十进制定点数。
常用方法