第二十一章
一、Object类
1.1 Object类的toString方法
/*
java.lang.Object
类Object是类层次结构的父类/根类
每个类都使用Object作为超父类
所有对象(包括数组)都实现这个类的方法
*/
public class Demo01ToString {
public static void main(String[] args) {
/*
Persong类 默认继承了object类 所以可以使用toString方法
返回该对象的字符串表示
*/
Person person = new Person("张三",18);
String s= person.toString();
System.out.println(s);//地址值cn.itcast.day12.demo01.Person@16b98e56
//直接打印对象的名字,其实就是调用对象的toString方法
System.out.println(person);
//看一个类是否重写了toString。直接打印这个类的对象即可,如果没有重写toString方法,那么打印的是对象的地址值
Random r = new Random();
System.out.println(r);//java.util.Random@70177ecd
Scanner sc = new Scanner(System.in);
System.out.println(sc);//打印的不是地址所以Scanner类重写了toString方法
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
System.out.println(list);//[1, 2, 3] 所以重写了toString方法
}
}
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/*
直接打印对象的地址值没有意义,需要重写Object类中的toString方法
打印对象的属性(name,age)
按住alt + ins 选择toString()
*/
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// @Override
// public String toString() {
// // return "abc";
// return "Person{name="+name+",age="+age+"}";
// }
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
1.2 Object类的equals方法
person类默认继承了Object类,所以可以使用Object类的equals方法
Boolean equals(Object obj)指示其他某个对象是否与此对象相等
equals方法源码:
public boolean equals(Object obj){
return (this == obj);
}
参数:
Object obj:可以传递任意对象
==比较运算符,返回的是一个布尔值 true false
基本数据类型:比较的是值
引用数据类型:比较的是两个对象的地址值
this 是调用equals方法的对象 obj是传递来的参数
public class Demo02Equals {
public static void main(String[] args) {
Person p1 = new Person("张三",28);
Person p2 = new Person("李四",20);
System.out.println("p1:" + p1);//p1:cn.itcast.day12.demo01.Person@7530d0a
System.out.println("p2:" + p2);//p2:cn.itcast.day12.demo01.Person@27bc2616
p1 = p2;//把p2的地址值赋值给p1
boolean b = p1.equals(p2);
System.out.println(b);//false地址不一样 //赋值后结果是true
}
}
1.2.1 重写Object类的equals方法
equals方法不重写比较的是地址值,重写后可以比较内容
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/*
直接打印对象的地址值没有意义,需要重写Object类中的toString方法
打印对象的属性(name,age)
按住alt + ins 选择toString()
*/
// @Override
// public String toString() {
// return "Person{" +
// "name='" + name + '\'' +
// ", age=" + age +
// '}';
// }
// @Override
// public String toString() {
// // return "abc";
// return "Person{name="+name+",age="+age+"}";
// }
/*
Object类的equals方法,默认比较的是两个对象的地址值,没有意义
所以我们要重写equals方法,比较两个对象的属性(name,age)
问题:
隐含着一个多态
多态的弊端:无法使用子类特有的内容(属性和方法)
Object obj = p2 = new Person("张三",18)
解决:
可以使用向下转型(强转)把Obj类型转换为Person
alt + ins 选择equals and hashcode可以直接生成此段代码
*/
// @Override //手写
// public boolean equals(Object obj) {
// //增加一个判断,传递的参数obj是this本身
// if (obj == this){
// return true;
//
// }
// //增加一个判断,传递的参数obj如果是null,直接返回false,提高程序效率
// if (obj == null){
// return false;
// }
// //增加一个判断防止ClassCastException
// if (obj instanceof Person){
// //使用向下转型,吧Obj转换为Person类型
// Person p = (Person) obj;
// //比较两个对象的属性
// //一个对象是this(p1)另一个是p(obj->p2)
// boolean b = this.name.equals(p.name) && this.age == p.age;
// return b;
// }
// //不是Person类型直接返回false
// return false;
//
// }
//alt + ins 重写equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
// getClass() != o.getClass() 使用反射技术,判断o是否是person类型 ,等效于 obj instanceof Person
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
1.3 Objects 类的equals方法
java.util.Objects 类,
Java 7新增了Objects工具类,它提供了一些工具方法来操作对象,这些工具方法大多是“空指针”安全的,如如你不能确定一个引用变量是否为null,如果贸然的调用该变量的toString()方法,则可能引发NullPointerExcetpion异常;但如果使用Objects类提供的toString(Object o)方法,就不会引发空指针异常,当o为null时,程序将返回一个"null"字符串。
该类是使用final修饰,不能被继承,该类的构造方法被私有化(使用private修饰),不能直接创建对象,该类中所有的方法都是静态方法,可以使用类型直接调用(该类不需要创建对象)
Objects类给我们提供了一些常用的操作对象的方法,我们可以直接使用,这是非常方便的。尤其是requireNonNull()方法,在我们写方法时需要判断传入的参数是否为null可以使用该方法及其方便。
Objects中的equals()方法是如何避免空指针异常的?
1 首先比较两个对象中的内存的地址,如果一致,则不会进行第二次比较,直接返回true;
2 如果对象的内存地址不一致,则a先判空,再调用对象的equals()方法进行比较
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
在这里插入代码片
public class Demo03Objects {
public static void main(String[] args) {
//String s1 = "abc";
String s1 = null;
String s2 = "aBc";
// boolean b = s1.equals(s2);//NullPointerException null是不能调用方法的,会出现空指针异常
// System.out.println(b);
/*
Objects 类的equals方法,对象个对象进行比较,防止空指针异常
public static boolean equals (Object a , Object b){
return (a == b) || ( a != null && a.equals(b));
}
*/
boolean b2 = Objects.equals(s1,s2);//Objects 类中的equals 添加了是否为空判断
System.out.println(b2);
}
}
日期时间类
2.1毫秒值的作用
/*
java.util.DateL:表示日期和时间的类
类:Date表示特定的瞬间,精确到毫秒
毫秒:千分之一秒
特定的瞬间:一个时间点,一刹那时间
毫秒值的作用:
可以对时间和日期进行计算:2099-01-03 到 2088-01-01一共有多少天
可以日期转换为毫秒进行计算,计算完毕,再把毫秒转换为日期
把日期转换为毫秒:
当前的日期:2088-01-01
时间原点(0毫秒):1970年1月1日 00:00:00(英国格林尼治时间)
就是计算当前日期到时间原点之间一共经历了多少毫秒
注意:中国属于东八区,会把时间增加八小时1970年1月1日 08:00:00
把毫秒转换为日期:
1天 = 24 * 60 *60 = 86400 秒 *1000 = 86400000毫秒
*/
public class Demo01Date {
public static void main(String[] args) {
System.out.println(System.currentTimeMillis());//1591933367566 获取当前系统时间到1970年1月1日 00:00:00经历了多少毫秒
}
}
2.2 Date类的构造方法和成员方法
import java.util.Date;
public class Demo02Date {
public static void main(String[] args) {
demo01();
demo02();
demo03();
}
/*
long getTime()
返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
*/
private static void demo03() {
Date date =new Date();
long time = date.getTime();
System.out.println(time);//1591934255533
}
/*
Date类的带参数构造方法
Date(long date):传递毫秒值,把毫秒值转换为Date日期
*/
private static void demo02() {
Date date = new Date(0L);
System.out.println(date);//Thu Jan 01 08:00:00 CST 1970
Date date1 = new Date(1591933367566L);//Fri Jun 12 11:42:47 CST 2020 毫秒值转换为日期(从1970年1月1日 00:00:00开始加)
System.out.println(date1);
}
/*
Date类的空参数构造方法:
Date()获取当前系统的日期和时间
*/
private static void demo01() {
Date date = new Date();
System.out.println(date);//Fri Jun 12 11:57:35 CST 2020
}
}
2.3 DateFormat类和SimpleDateFormat类的介绍
DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)允许进行格式化(也就是日期 -> 文本)、解析(文本-> 日期)和标准化。将日期表示为 Date 对象,或者表示为从 GMT(格林尼治标准时间)1970 年 1 月 1 日 00:00:00 这一刻开始的毫秒数。
格式化:按照指定格式从Date对象转换为Spring对象
解析:按照指定的格式,从String对象转换为Date对象
java.text.DateForamt:是日期/时间格式化子类的抽象类
作用:
格式化(日期-》文本 文本-》日期)
成员方法:
String format(Date date) 按照指定模式,吧Date日期格式化为符合模式的字符串
Date parse(String source) 把符合模式的字符串,解析为Date日期
DateFormat类是一个抽象类,无法直接创建对象使用,可以用DateFormat类的子类java.text.DateFormat
java.text.DateFormat
构造方法:
SimpleDateFormat(String pattern)
用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
参数:
String pattern:传递指定的模式
模式:区分大小写的
y 年
M 月
d 日
H 时
s 秒
写对应的模式,会把模式替换为对应的日期和时间
“yyyy-MM-dd-HH-mm:ss”
注意:模式中的字母不能更改,链接模式的符号可以改变
2.3.1 DateFormat类中的Format和parse方法
public class Demo03DataFormat {
public static void main(String[] args) throws ParseException {
method1();
method2();
}
/*
使用DateFormat类中的方法format,把日期格式化为文本
使用步骤:
1.创建SimpleDateFormat对象,构造方法中传递指定的模式
2.调用SimpleDateFormat对象中的方法parse,把符合构造方法中模式的字符串,解析为Date日期
注意:
public Date parse(String source) throws ParseException
parse方法生命了一个异常叫ParseException
如果字符串和构造方法的模式不一样,那么程序就会抛出异常
调用一个抛出了异常的方法,就必须处理这个异常,要么throws继续抛出这个异常,要么try cath自己处理
*/
private static void method2() throws ParseException {
//创建SimpleDateFormat对象,构造方法中传递指定的模式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
//调用SimpleDateFormat对象中的方法parse,把符合构造方法中模式的字符串,解析为Date日期
Date date = sdf.parse("2020年06月12日 13时31分47秒");
System.out.println(date);
}
/*
使用DateFormat类中的方法format,把日期格式化为文本
使用步骤:
1.创建SimpleDateFormat对象,构造方法中传递指定的模式
2.调用SimpleDateFormat对象中的方法format,按照构造方法中指定的模式,
*/
private static void method1() {
//创建SimpleDateFormat对象,构造方法中传递指定的模式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
Date date = new Date();
//调用SimpleDateFormat对象中的方法format,按照构造方法中指定的模式,
String d = sdf.format(date);
System.out.println(date);
System.out.println(d);
}
}
2.4 练习 计算一个人出生了多少天
练习:
请使用日期相关API,计算出一个人已经出生了多少天
分析:
1.使用Scanner类中的方法next,获取出生日期
2.使用DateFormat类中的parse,把字符串的出生日期,解析为Date格式的出生日期
3.把Date格式的出生日期转换成毫秒值
4.获取当前的日期转换成毫秒值
5.使用当前日期的毫秒组织-出生日期的毫秒值
6.把毫秒值转换成天(s/1000/60/60/24)
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class Demo04Test {
public static void main(String[] args) throws ParseException {
//1.使用Scanner类中的方法next,获取出生日期
Scanner sc = new Scanner(System.in);
System.out.println("请输入您的出生日期,格式为:YYYY-MM-dd");
String birthdayDateString = sc.next();
//2.使用DateFormat类中的parse,把字符串的出生日期,解析为Date格式的出生日期
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd");
Date birthdayDate =simpleDateFormat.parse(birthdayDateString);
//3.吧date格式的出生日期转换为毫秒值
long birthdayDateTime = birthdayDate.getTime();
//4.获取当前的日期,转换为毫秒值
long currentTime = new Date().getTime();
//5.当前日期减去出生日期
long time = currentTime - birthdayDateTime ;
//6.把毫秒值转换为天
System.out.println(time / 1000 /60 /60 /24);
}
}
2.5 Calendar类
Calendar 类是一个抽象类
Calendar 提供了一个类方法 getInstance,以获得此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化:
Calendar rightNow = Calendar.getInstance();
/*
java.util.Calendar:日历类
Calendar类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等
日历字段之间的转换提供了一些方法
Calendar类无法直接创建对象,里边有一个静态方法叫getInstance(),该方法返回了Calendar类的子类对象。
static Calendar getInstance()
使用默认时区和语言环境获得一个日历。
*/
public class Demo05Calendar {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();//用一个父类接收子类的对象 即多态
System.out.println(calendar);
}
}
2.5.1 Calendar类常用成员方法
/*
成员方法的参数:
public static final int YEAR = 1; 年
public static final int MONTH = 2; 月
public static final int DATE = 5; 月中的某一天
public static final int DAY_OF_MONTH = 5; 月中的某一天
public static final int HOUR = 10; 时
public static final int MINUTE = 12; 分
public static final int SECOND = 13; 秒
*/
public class Demo06Calendar {
public static void main(String[] args) {
demo01();
demo02();
demo03();
demo04();
}
/*
第一个方法:
public int get (int field):返回给定日历字段的值
参数:传递指定日历字段(YEAR,MONTH)
返回值:日历字段代表的具体的值
*/
private static void demo01() {
//使用getInstance方法获取Calendar对象
Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR);
System.out.println(year);
int month = c.get(Calendar.MONTH);
System.out.println(month + 1); //西方 0 -11月
int date = c.get(Calendar.DAY_OF_MONTH);
System.out.println(date);
System.out.println("------------------------------------------");
}
/*
第二个方法:
pulic 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,6,2);
int year = c.get(Calendar.YEAR);
System.out.println(year);
int month = c.get(Calendar.MONTH);
System.out.println(month + 1); //西方 0 -11月
int date = c.get(Calendar.DAY_OF_MONTH);
System.out.println(date);
System.out.println("------------------------------------------");
}
/*
第三个方法:
public abstract void add (int field , int amount):根据日历的规则,为给定的日历字段添加或减去指定的时间量
把指定的字段【增加/减少】指定的值
参数:
int field : 传递给定的日历字段(YEAR,MONTH..)
int amount: 正数增加,复数减少
*/
private static void demo03() {
//使用getInstance获取Calendar对象
Calendar c= Calendar.getInstance();
//把年增加两年
c.add(Calendar.YEAR,2);
//减少两月
c.add(Calendar.MONTH,-2);
int year = c.get(Calendar.YEAR);
System.out.println(year);
int month = c.get(Calendar.MONTH);
System.out.println(month + 1);
System.out.println("------------------------------------------");
}
/*
第四个方法:
public Date getTime():返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象
*/
private static void demo04() {
//使用getInstance获取Calendar对象
Calendar c= Calendar.getInstance();
System.out.println(c);
//日历转换为Date
Date date = c.getTime();
System.out.println(date);
}
}
三、System类
import javax.crypto.spec.PSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
public class Demo07System {
public static void main(String[] args) {
demo01();
demo02();
}
/*
static long currentTimeMillis()
返回以毫秒为单位的当前时间。
用来验证程序效率。
验证for循环打印数字1-9999所需要使用的时间
*/
private static void demo01() {
//程序执行前获取一次毫秒值
long time = System.currentTimeMillis();
System.out.println(time);
//执行for循环
for (int i = 0; i < 9999; i++) {
System.out.println(i);
}
//程序执行后获取一次毫秒值
long e = System.currentTimeMillis();
System.out.println("程序耗时:"+ (e - time) +"毫秒");//192毫秒
System.out.println("------------------------------------");
}
/*
static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。
src 源数组
srcPos 源数组中的起始位置(起始索引)
dest 目标数组
destPos 目标数据的起始位置
length 要复制的数组元素的数量
练习:
将src数组中前三个元素,复制到dest数组的前三个位置上,
复制元素前:src数组元素[1,2,3,4,5],
dest数组元素[6,7,8,9,10]
复制元素后:src数组元素[1,2,3,4,5],dest数组元素[1,2,3,9,10]
*/
private static void demo02() {
int[] array1 = {1,2,3,4,5};
int[] array2 = {6,7,8,9,10};
System.out.println("复制前:"+ Arrays.toString(array2));
System.arraycopy(array1,0,array2,0,3);
System.out.println("复制后:"+ Arrays.toString(array2));
}
}
四、StringBuilder类
StringBuilder类 是字符缓冲区,可以提高字符串的操作效率
看成(一个长度可以变化的字符串)底层也是一个数组,但是没有呗final修饰,可以改变长度
4.1StringBuilder类的构造方法
/*
java.lang.StringBuilder类:字符串缓冲区,可以提高字符串的效率
构造方法:
StringBuilder()
构造一个不带任何字符的字符串生成器,其初始容量为 16 个字符。
*/
public class Demo08StringBuilder {
public static void main(String[] args) {
//空参数构造方法
StringBuilder bu1 = new StringBuilder();
System.out.println("bu1:" + bu1);//bu1:
//带字符串的构造方法
StringBuilder bu2 = new StringBuilder("abc");
System.out.println(bu2);//abc
}
}
4.2StringBuilder类–append方法
/*
StringBuilder append(参数类型 参数名称):添加任意类型数据的字符串形式,并返回当前对象自身
*/
public class Demo02StringBuilder {
public static void main(String[] args) {
//创建StringBuilder对象
StringBuilder bu = new StringBuilder();
//使用append方法往StringBuilder中添加数据
//apend方法返回的是this,调用的方法对象bu,this == bu
StringBuilder bu2 = bu.append("abc");//把bu的地址赋值给了bu2
System.out.println(bu);//abc
System.out.println("bu2:" + bu2);//abc
System.out.println(bu == bu2);//比较的是地址值 true
//使用append方法无须接收返回值
// bu.append("abc");
// bu.append(1);
// bu.append(true);
// bu.append(8.8);
// bu.append('中');
// System.out.println(bu);//abcabc1true8.8中
/*
链式编程:方法返回值是一个对象,可以继续调用方法
*/
System.out.println("abc".toUpperCase().toLowerCase().toUpperCase().toLowerCase());
bu.append("abc").append(1).append(8.8).append('中');
System.out.println(bu);
}
}
4.3StringBuilder类–toString方法
/*
StringBuilder和String可以相互转换:
String -->StringBuilder:可以是同StringBuilder的构造方法
StringBuilder(String str)构造一个字符串生成器,并初始化为指定的字符串内容
StringBuilder -->String:可以使用StringBuilder中的toString方法
public String toString():将当前的StringBuilder对象转换为String对象
*/
public class Demo09StringBuilder {
public static void main(String[] args) {
//String--》StringBuilder
String str = "Hello";
System.out.println("str = " + str);
StringBuilder bu = new StringBuilder(str);
//王StringBuilder中添加数据
bu.append("world");
System.out.println("bu = " + bu);
String s =bu.toString();
System.out.println("s = " + s);
}
}
五、包装类
5.1 包装类的概念
5.1 装箱与开箱
基本类型与对应的包装类对象之间,来回转换的过程称为“装箱”与”拆箱“:
装箱:
从基本类型转换为对应的包装类对象
构造方法:
Integer(int value)构造一个新分配的Integer对象,它表示指定的int值。
Integer(String s)构造一个新分配的Interger对象,他表示String参数所指示的int值
传递的字符串,必须是基本类型的字符串,否则会抛出异常
"100"正确 “a”抛异常
静态方法:
static Integer valueOf(int i)
返回一个表示指定的 int 值的 Integer 实例。
static Integer valueOf(String s)
返回保存指定的 String 的值的 Integer 对象。
拆箱:在包装类中取出基本类型的数据(包装类->基本类型的数据)
成员方法:
int intValue()
以 int 类型返回该 Integer 的值。
public class Demo10Integer {
public static void main(String[] args) {
//装箱:把基本类型的数据包装到包装类中(基本类型的数据-》包装类)
//构造方法
Integer in1 = new Integer("1");//方法上有横线,说明方法过时了
System.out.println(in1);//1 重写了toString方法,所以输出的是值,不是地址
Integer in2 = new Integer("1");
System.out.println(in2);//1
//静态方法
Integer in3 = Integer.valueOf(1);
System.out.println(in3);
//Integer in4 = Integer.valueOf("a");//NumberFormatException数字格式化异常,
Integer in4 = Integer.valueOf(1);
System.out.println(in4);
//拆箱:在包装类中去除基本类型的数据(包装类-->基本类型的数据)
int i = in1.intValue();
System.out.println(i);//1
}
}
5.3 基本类型与字符串类型之间的相互转换
/*
基本类型与字符串类型之间的相互转换:
基本类型->字符串(String)
1.基本类型的值+"" 最简单的方法,工作中常用
2.包装类的静态方法toString(参数),不是Object类的toString()重载
String toString()
返回一个表示该 Integer 值的 String 对象。
3.String 类的静态方法valueOf(参数)
static String valueOf(int i)
返回 int 参数的字符串表示形式。
字符串->基本类型
使用包装类的静态方法parseXXX("数值类型的字符串");
Integer类:static int parseInt(String s)
Double类:static double parseDouble(String s)
*/
public class Demo12Integer {
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 + 100);//100100
String s3 = String.valueOf(100);
System.out.println(s3 + 300);//100300
//字符串-->基本类型
int i = Integer.parseInt(s1);
System.out.println(i - 10);//90
//
// int a = Integer.parseInt("a");//NumberFormatException ,字符不能转为字符
// System.out.println(a);
}
}