Java基础|1-07-Object类与常见API(二)@API篇
写在前面:
此系列文是笔者在学习Java系列课程的过程中,参考相关课件、视频讲解、课程代码,并结合一些文档、思维导图及个人理解,对所学内容做的阶段性梳理与总结。
- 写于:2021年1月28日
- 内容:Java后端系列笔记007(Java基础-Object类、常见API)
- 全文:12819字;读完需要:20分钟
文章目录
一、Object类
1.1 概述 [ 导图 ]
- 如果一个类没有特别指定父类,那么默认则继承自Object类。例如:
public class MyClass /*extends Object*/ {
// ...
}
1.2 toString方法
方法摘要
public String toString()
:返回该对象的字符串表示
- 其实该字符串内容就是:类名+@+内存地址值
- 如:com.sj.demo01.Object.Person@16e8e0a
- 具体值等于:
getClass().getName() + '@' + Integer.toHexString(hashCode())
- getClass().getName():代表返回对象所属名的类名,如:Person
- hashCode():代表返回该对象的哈希值
- 该方法将对象的内存地址进行哈希运算,返回一个int类型的哈希值
- Integer.toHexString(hashCode()):代表将对象的哈希值用十六进制表示
覆盖重写
- 直接打印对象的地址值没有意义,需要重写Object类中的toString方法(建议所有子类都重写此方法)
// 示例:自定义的Person类
public class Person {
private String name;
private int age;
@Override
//重写方法,打印对象的属性(name,age)
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
// 省略构造器与Getter Setter
}
- 在IntelliJ IDEA中,toString方法的自动重写:快捷键
alt+insert
→ 点击toString()
→ 选择需要包含的成员变量 → OK
- 看一个类是否重写了toString,直接打印这个类的对象即可。如果没有重写toString方法,那么打印的是对象的地址值
小贴士: 在我们直接使用输出语句输出对象名的时候,其实就是调用对象的toString()方法
1.3 equals方法
方法摘要
public boolean equals(Object obj)
:指示其他某个对象是否与此对象“相等”(这里的“相等”有默认和自定义两种方式)
//equals方法源码
public boolean equals(Object obj) {
return (this == obj);
}
- 参数Object obj:可以传递任意的对象
- == 比较运算符,返回的是一个布尔值 true false
- 基本数据类型:比较的是值
- 引用数据类型:比较的是两个对象的地址值
- this是谁?哪个对象调用的方法,方法中的this就是那个对象
- obj是谁?传递过来的参数
默认地址比较
- Object类的equals方法,默认“==”运算符,比较的是两个对象的地址值
- 只要不是同一个对象,结果必然为false
对象内容比较
- 如果希望进行对象的内容比较,需重写equals方法,比较两个对象的属性(name,age)
- 问题:隐含着一个多态
- 多态的弊端:无法使用子类特有的内容(属性和方法)
- Object obj = p2 = new Person(“小白”,19);
- 解决:可以使用向下转型(强转)把obj类型转换为Person
import java.util.Objects;
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
// 如果对象地址一样,则认为相同,提高程序的效率
if (this == o)
return true;
// 如果参数为空,或者类型信息不一样,则认为不同
/* getClass() != o.getClass() 使用反射技术,判断o是否是Person类型(等效于 obj instanceof Person); 增加类型判断,可防止类型转换异常ClassCastException */
if (o == null || getClass() != o.getClass())
return false;
//使用向下转型,把obj转换为Person类型
Person person = (Person) o;
// 要求基本类型相等,并且将引用类型交给java.util.Objects类的equals静态方法取用结果
return age == person.age && Objects.equals(name, person.name);
}
/* (了解即可) 当equals方法被重写时,通常有必要重写 hashCode 方法, 以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码 */
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
- 在IntelliJ IDEA中,自动生成equals方法:快捷键
alt+insert
→ 选择equals() and hashCode()
equals 和 == 的区别
1.4 Objects类
Objects类的equals方法
- 在比较两个对象的时候,Object的equals方法容易抛出空指针异常
NullPointerException
,示例:
public class Demo01Object {
public static void main(String[] args) {
String s1 = null;
String s2 = "abc";
// null是不能调用方法的,会抛出空指针异常
boolean b = s1.equals(s2); // NullPointerException
System.out.println(b);
}
}
- Objects类中的equals方法优化了这个问题:对两个对象进行比较,防止空指针异常,源码:
//源码
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
- Objects中equals的使用,示例:
import java.util.Objects;
public class Demo02Objects {
public static void main(String[] args) {
String s1 = null;
String s2 = "abc";
boolean b2 = Objects.equals(s1, s2);
System.out.println(b2);// false
}
}
Objects类是什么
java.util.Objects
类:
- 在JDK7添加了一个Objects工具类,它提供了一些方法来操作对象,它由一些静态的实用方法组成
- 这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象
二、日期时间类
2.1 Date类
- 代码演示:
import java.util.Date;
public class Demo01Date {
public static void main(String[] args) {
/* Date类的空参数构造方法 Date() 获取当前系统的日期和时间 */
System.out.println(new Date()); //Thu Jan 28 14:45:41 CST 2021
/* Date类的带参数构造方法 Date(long date) :传递毫秒值,把毫秒值转换为Date日期对象 */
System.out.println(new Date(0L)); // Thu Jan 01 08:00:00 CST 1970
/* long getTime() 把日期转换为毫秒值(相当于System.currentTimeMillis()方法) 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来,此 Date 对象表示的毫秒数 */
Date date = new Date(); //创建日期对象
long time = date.getTime();
System.out.println(time);//1611816684812
}
}
小贴士:
- 中国属于东八区,会把时间增加8个小时,所以我们的基准时间为:1970年1月1日8时0分0秒
- 在使用println方法时,会自动调用Date类中的toString方法。
Date类对Object类中的toString方法进行了覆盖重写,所以结果为指定格式的字符串。
2.2 DateFormat类
- 分别使用format、parse方法,代码演示:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo01DateFormat {
public static void main(String[] args) throws ParseException {
demo01();
demo02();
}
/* 使用DateFormat类中的方法【format】, 把日期格式化为文本 (把Date对象转换成String) */
private static void demo01() {
//1.创建SimpleDateFormat对象,构造方法中传递指定的模式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
//2.调用SimpleDateFormat对象中的方法format
//String format(Date date) 按照指定的模式,把Date日期,格式化为符合模式的字符串(文本)
Date date = new Date();
String d = sdf.format(date);
System.out.println(date);//Thu Jan 28 17:10:22 CST 2021
System.out.println(d);//2021年01月28日 17时10分22秒
}
/* 使用DateFormat类中的方法【parse】, 把文本解析为日期 (把String转换成Date对象) 注意: public Date parse(String source) throws ParseException parse方法声明了一个异常叫ParseException 如果字符串和构造方法的模式不一样,那么程序就会抛出此异常 调用一个抛出了异常的方法,就必须的处理这个异常,要么throws继续抛出这个异常,要么try catch自己处理 */
private static void demo02() throws ParseException {
//1.创建SimpleDateFormat对象,构造方法中传递指定的模式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
//2.调用SimpleDateFormat对象中的方法parse
//Date parse(String source) 把符合模式的字符串,解析为Date日期
Date date = sdf.parse("2021年01月21日 15时51分54秒");
System.out.println(date);// Thu Jan 21 15:51:54 CST 2021
}
}
2.3 练习
- 要求:请使用日期时间相关的API,计算出一个人已经出生了多少天
- 思路:
- 获取当前时间对应的毫秒值
- 获取自己出生日期对应的毫秒值
- 两个时间相减(当前时间– 出生日期)
- 代码实现:
public static void function() throws Exception {
System.out.println("请输入出生日期 格式 YYYY-MM-dd");
// 获取出生日期,键盘输入
String birthdayString = new Scanner(System.in).next();
// 将字符串日期,转成Date对象
// 创建SimpleDateFormat对象,写日期模式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 调用方法parse,字符串转成日期对象
Date birthdayDate = sdf.parse(birthdayString);
// 获取今天的日期对象
Date todayDate = new Date();
// 将两个日期转成毫秒值,Date类的方法getTime
long birthdaySecond = birthdayDate.getTime();
long todaySecond = todayDate.getTime();
//使用当前日期的毫秒值-出生日期的毫秒值
long secone = todaySecond-birthdaySecond;
if (secone < 0){
System.out.println("还没出生呢");
} else {
//把毫秒差值转换为天(s/1000/60/60/24)
System.out.println(secone/1000/60/60/24);
}
}
2.4 Calendar类
- Calendar静态方法的使用:
import java.util.Calendar;
public class Demo01CalendarInit {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance(); //多态
}
}
- Calendar类中提供很多成员常量,代表给定的日历字段:
字段值 | 含义 |
---|---|
YEAR | 年 |
MONTH | 月(从0开始,可以+1使用) |
DAY_OF_MONTH | 月中的天(几号) |
HOUR | 时(12小时制) |
HOUR_OF_DAY | 时(24小时制) |
MINUTE | 分 |
SECOND | 秒 |
DAY_OF_WEEK | 周中的天(周几,周日为1,可以-1使用) |
- 常用方法的代码演示:
import java.util.Calendar;
import java.util.Date;
public class Demo02Calendar {
public static void main(String[] args) {
demo04();
}
/* public int get(int field):返回给定日历字段的值。 参数:传递指定的日历字段(YEAR,MONTH...) 返回值:日历字段代表的具体的值 */
private static void demo01() {
// 使用getInstance方法获取Calendar对象
Calendar c = Calendar.getInstance();
// 设置年
int year = c.get(Calendar.YEAR);
// 设置月(西方的月份0-11 东方:1-12)
int month = c.get(Calendar.MONTH);
// 设置日
// int date = c.get(Calendar.DAY_OF_MONTH);
int date = c.get(Calendar.DATE);
System.out.print(year + "年" + month + "月" + date + "日");//2021年0月28日
}
/* public void set(int field, int value):将给定的日历字段设置为给定值。 参数: int field:传递指定的日历字段(YEAR,MONTH...) int value:给指定字段设置的值 */
private static void demo02() {
//使用getInstance方法获取Calendar对象
Calendar c = Calendar.getInstance();
//设置年为9999
c.set(Calendar.YEAR,9999);
//设置月为9月
c.set(Calendar.MONTH,9);
//设置日9日
c.set(Calendar.DATE,9);
//同时设置年月日,可以使用set的重载方法
c.set(8888,8,8);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int date = c.get(Calendar.DATE);
System.out.print(year + "年" + month + "月" + date + "日");//8888年8月8日
}
/* public abstract void add(int field, int amount): 根据日历的规则,为给定的日历字段添加或减去指定的时间量。 参数: int field:传递指定的日历字段(YEAR,MONTH...) int amount:增加/减少指定的值 正数:增加 负数:减少 */
private static void demo03() {
//使用getInstance方法获取Calendar对象
Calendar c = Calendar.getInstance();
//把年增加2年
c.add(Calendar.YEAR,2);
//把月份减少3个月
c.add(Calendar.MONTH,-3);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int date = c.get(Calendar.DAY_OF_MONTH);
System.out.print(year + "年" + month + "月" + date + "日");//2022年9月28日
}
/* public Date getTime(): 返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象。 把日历对象,转换为日期对象 */
private static void demo04() {
//使用getInstance方法获取Calendar对象
Calendar c = Calendar.getInstance();
Date date = c.getTime();
System.out.println(date);//Thu Jan 28 19:46:56 CST 2021
}
}
小贴士:
西方星期的开始为周日,中国为周一。
在Calendar类中,月份的表示是以0-11代表1-12月。
日期是有大小关系的,时间靠后,时间越大。
三、System类
3.1 currentTimeMillis方法
- 说明:currentTimeMillis方法实际上是 获取当前系统时间与1970年01月01日00:00点之间的毫秒差值
import java.util.Date;
public class SystemDemo {
public static void main(String[] args) {
//获取当前系统时间到1970 年 1 月 1 日 00:00:00经历了多少毫秒
System.out.println(System.currentTimeMillis());//1611816867285
}
}
- 练习:验证for循环打印数字1-9999所需要使用的时间(毫秒)
public class SystemTest1 {
public static void main(String[] args) {
// 程序执行前,获取一次毫秒值
long start = System.currentTimeMillis();
// 执行for循环
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
// 程序执行后,获取一次毫秒值
long end = System.currentTimeMillis();
System.out.println("程序共耗时:"+(end-start)+"毫秒");//程序共耗时:95毫秒
}
}
3.2 arraycopy方法
- 练习:将src数组中前3个元素,复制到dest数组的前3个位置上
- 复制元素前:src数组元素[1,2,3,4,5],dest数组元素[6,7,8,9,10]
- 复制元素后:src数组元素[1,2,3,4,5],dest数组元素[1,2,3,9,10]
import java.util.Arrays;
public class DemoSystemArrayCopy {
public static void main(String[] args) {
//定义源数组
int[] src = {
1,2,3,4,5};
//定义目标数组
int[] dest = {
6,7,8,9,10};
System.out.println("复制前:"+ Arrays.toString(dest)); //复制前:[6, 7, 8, 9, 10]
//使用System类中的arraycopy把源数组的前3个元素复制到目标数组的前3个位置上
System.arraycopy(src,0,dest,0,3);
System.out.println("复制后:"+ Arrays.toString(dest)); //复制后:[1, 2, 3, 9, 10]
}
}
四、StringBuilder类
4.1 StringBuilder的原理
- 由于String类的对象内容不可改变,所以每当进行字符串拼接时,总是会在内存中创建一个新的对象 —— 既耗时,又浪费空间
- 为了解决这一问题,可以使用
java.lang.StringBuilder
类
java.lang.StringBuilder类:字符串缓冲区,可以提高字符串的效率
4.2 构造方法
public StringBuilder()
:构造一个不带任何字符的字符串生成器,其初始容量为 16 个字符public StringBuilder(String str)
:构造一个字符串生成器,并初始化为指定的字符串内容
public class Demo01StringBuilder {
public static void main(String[] args) {
//空参数构造方法
StringBuilder sb1 = new StringBuilder();
System.out.println(sb1); // (空白)
//带字符串的构造方法
StringBuilder sb2 = new StringBuilder("java");
System.out.println(sb2); // java
}
}
4.3 常用方法
public StringBuilder append(...)
:添加任意类型数据的字符串形式,并返回当前对象自身public String toString()
:将当前StringBuilder对象转换为String对象
append方法
- append方法具有多种重载形式,可以接收任意类型的参数
- 任何数据作为参数都会将对应的字符串内容添加到StringBuilder中,如:
public class Demo02StringBuilder {
public static void main(String[] args) {
//创建StringBuilder对象
StringBuilder builder = new StringBuilder();
//public StringBuilder append(任意类型):添加数据
//append方法返回的是this,调用方法的对象builder,this==builder
StringBuilder builder2 = builder.append("hello");//把builder的地址赋值给了bu2
//对比一下
System.out.println("builder:"+builder);//builder:hello
System.out.println("builder2:"+builder2);//builder2:hello
System.out.println(builder == builder2); //比较的是地址 true
// 可以添加 任何类型,且使用append方法无需接收返回值
builder.append("hello");
builder.append("world");
builder.append(true);
builder.append(100);
// 在我们开发中,会遇到调用一个方法后,返回一个对象的情况。然后使用返回的对象继续调用方法。
// 这种时候,我们就可以把代码现在一起,如 append方法一样,代码如下
//【链式编程】
builder.append("hello").append("world").append(true).append(100);
System.out.println("builder:"+builder);
}
}
备注:StringBuilder已经覆盖重写了Object当中的toString方法
toString方法
- 通过toString方法,StringBuilder对象将会转换为不可变的String对象。如:
public class Demo03StringBuilder {
public static void main(String[] args) {
// 链式创建
StringBuilder sb = new StringBuilder("Hello").append("World").append("Java");
// 调用方法
String str = sb.toString();
System.out.println(str); // HelloWorldJava
}
}
StringBuilder和String的相互转换
- String --> StringBuilder:
- 可以使用StringBuilder的构造方法
StringBuilder(String str)
:构造一个字符串生成器,并初始化为指定的字符串内容
- StringBuilder --> String:
- 可以使用StringBuilder中的toString方法
public String toString()
:将当前StringBuilder对象转换为String对象
public class Demo04StringBuilder {
public static void main(String[] args) {
// String --> StringBuilder
String str = "hello";
System.out.println("str:"+str);// str:hello
StringBuilder bu = new StringBuilder(str);
// 往StringBuilder中添加数据
bu.append("world");
System.out.println("bu:"+bu); // bu:helloworld
// StringBuilder --> String
String s = bu.toString();
System.out.println("s:"+s); //s:helloworld
}
}
五、包装类
5.1 概述
- 基本类型对应的包装类:
基本类型 | 对应的包装类(位于java.lang包中) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
5.2 装箱与拆箱
装箱 (基本类型的数据 --> 包装类)
- 概念:把基本类型的数据,包装到包装类中
- 构造方法:
Integer(int value)
:构造一个新分配的 Integer 对象,它表示指定的 int 值Integer(String s)
:构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值
★ 传递的字符串,必须是基本类型的字符串,否则会抛出异常(如:“100” 正确 ;“a” 抛异常)
- 静态方法:
static Integer valueOf(int i)
:返回一个表示指定的 int 值的 Integer 实例static Integer valueOf(String s)
:返回保存指定的 String 的值的 Integer 对象
//使用构造方法
Integer in1 = new Integer(4);
Integer in2 = new Integer("1");
//使用静态方法
//Integer in3 = Integer.valueOf("a");//NumberFormatException数字格式化异常
Integer in4 = Integer.valueOf(4);//使用包装类中的valueOf方法
拆箱 (包装类 --> 基本类型的数据)
- 概念:在包装类中取出基本类型的数据
- 成员方法
int intValue()
:以 int 类型返回该 Integer 的值
int num = in1.intValue();
5.3 自动装箱与自动拆箱
- 从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。 例如:
//示例1
Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;
//加法运算完成后,再次装箱,把基本数值转成对象。
//示例2
ArrayList<Integer> list = new ArrayList<>();
//ArrayList集合无法直接存储整数,可以存储Integer包装类
list.add(1); //-->自动装箱 list.add(new Integer(1));
int a = list.get(0); //-->自动拆箱 list.get(0).intValue();
5.4 基本类型与字符串之间的转换
基本类型 --> 字符串(String)(三种方式)
- 基本类型的值+"" :最简单的方法(工作中常用) ,如:24+""
- 包装类的静态方法toString(参数),不是Object类的toString() 重载
static String toString(int i)
:返回一个表示指定整数的 String 对象
- String类的静态方法valueOf(参数)
static String valueOf(int i)
:返回 int 参数的字符串表示形式
字符串(String) --> 基本类型
- 使用包装类的静态方法parseXXX(“字符串”);
- Integer类:
static int parseInt(String s)
- Double类:
static double parseDouble(String s)
- Integer类:
代码演示:
public class Demo03Integer {
public static void main(String[] args) {
//基本类型->字符串(String)
int i1 = 100;
String s1 = i1+"";
System.out.println(s1+200);//100200
String s2 = Integer.toString(100);
System.out.println(s2+200);//100200
String s3 = String.valueOf(100);
System.out.println(s3+200);//100200
//字符串(String)->基本类型
int i = Integer.parseInt(s1);
System.out.println(i-10);
// 如果字符串参数的内容无法正确转换为对应的基本类型,
//则会抛出java.lang.NumberFormatException 异常
int a = Integer.parseInt("a");// NumberFormatException
System.out.println(a);
}
}