今日内容

  • 多态(重点)
    • 面向对象的三大特征之一:封装,继承,多态。
  • 代码块
    • 已经讲完了
    • 包我们介绍一下就好了
  • 权限修饰符
    • 介绍一下: private -> 缺省 -> protected -> public
  • 内部类(很抽象)
    • 知识完整性(5大成分之一)
    • 只关注语法即可,实际开发几乎无用,主要是sun公司自己用,我们能理解即可!!
    • 匿名内部类(重点,必须掌握的)
  • Object类
    • API使用工程师。90%的技术都是别人写好的.我们直接调用。
    • 从这里开始,几乎没有语法了,全部是别人写好的技术我们直接调用即可,调用API。
    • 都是别人做好的技术,我们拿来用:
      • MySQL , JDBC, Mybatis,HTML , CSS , JS , JQuery ,UI框架 , WEB开发, Servlet , JSP
      • Tomcat , Spring家族的技术(Spring , Spring MVC Spring Data JPA ) , Spring Boot
      • Spring Cloud

教学目标

  • 能够说出使用多态的前提条件

  • (1)必须有继承或者实现关系!
    (2)必须存在父类类的变量引用子类类型的对象!
    (3)存在方法重写!
  • 理解多态的向上转型(自动类型转换)
  • 自动类型转换。Animal a = new Cat();
  • 理解多态的向下转型
  • 强制类型转换。
  • Animal a= new Cat();
  • Cat c = (Cat)a;
  • 能够完成笔记本案例
  • 参见代码!
  • 能够说出权限修饰符作用范
  • private 本类
  • 缺省 本类 本包其他类
  • protected 本类 本包其他类 其他包下的子类中
  • public 任何地方
  • 说出内部类的概念
  • 定义在一个类里面的类就是内部类。
  • 能够说出Object类的特点
  • 祖宗类,它的功能,全部类都 可以使用!!
  • 能够重写Object类的toString方法
  • 自动生成:重写返回对象内容输出。
  • 能够重写Object类的equals方法
  • 自动生成:比较对象的内容,制定比较规则。

第一章 多态[重点]

1.1 多态的形式

多态是继封装、继承之后,面向对象的第三大特性。

多态是出现在继承或者实现关系中的

多态体现的格式

  1. 父类类型 变量名 = new 子类/实现类构造器;
  2. 变量名.方法名();

多态的前提:有继承关系,子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。

1.2 多态的案例演示

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法。如果子类没有重写该方法,就会调用父类的该方法

总结起来就是:编译看左边,运行看右边。

代码如下:

定义父类:

  1. public class Animal {
  2. public void eat(){
  3. System.out.println("动物吃东西!")
  4. }

定义子类:

  1. class Cat extends Animal {
  2. public void eat() {
  3. System.out.println("吃鱼");
  4. }
  5. }
  6. class Dog extends Animal {
  7. public void eat() {
  8. System.out.println("吃骨头");
  9. }
  10. }

定义测试类:

  1. public class Test {
  2. public static void main(String[] args) {
  3. // 多态形式,创建对象
  4. Animal a1 = new Cat();
  5. // 调用的是 Cat 的 eat
  6. a1.eat();
  7. // 多态形式,创建对象
  8. Animal a2 = new Dog();
  9. // 调用的是 Dog 的 eat
  10. a2.eat();
  11. }
  12. }

1.3 多态的定义和前提

多态: 是指同一行为,具有多个不同表现形式。

从上面案例可以看出,Cat和Dog都是动物,都是吃这一行为,但是出现的效果(表现形式)是不一样的。

前提【重点】

  1. 继承或者实现【二选一】
  2. 方法的重写【意义体现:不重写,无意义】
  3. 父类引用指向子类对象【格式体现】

    父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

1.4 多态的好处

实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。代码如下:

定义父类:

  1. public abstract class Animal {
  2. public abstract void eat();
  3. }

定义子类:

  1. class Cat extends Animal {
  2. public void eat() {
  3. System.out.println("吃鱼");
  4. }
  5. }
  6. class Dog extends Animal {
  7. public void eat() {
  8. System.out.println("吃骨头");
  9. }
  10. }

定义测试类:

  1. public class Test {
  2. public static void main(String[] args) {
  3. // 多态形式,创建对象
  4. Cat c = new Cat();
  5. Dog d = new Dog();
  6. // 调用showCatEat
  7. showCatEat(c);
  8. // 调用showDogEat
  9. showDogEat(d);
  10. /*
  11. 以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代
  12. 而执行效果一致
  13. */
  14. showAnimalEat(c);
  15. showAnimalEat(d);
  16. }
  17. public static void showCatEat (Cat c){
  18. c.eat();
  19. }
  20. public static void showDogEat (Dog d){
  21. d.eat();
  22. }
  23. public static void showAnimalEat (Animal a){
  24. a.eat();
  25. }
  26. }

由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当然可以把Cat对象和Dog对象,传递给方法。

当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致,所以showAnimalEat完全可以替代以上两方法。

不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写showXxxEat方法了,直接使用showAnimalEat都可以完成。从而实现了实现类的自动切换。

所以,多态的好处,体现在,可以使程序编写的更简单,并有良好的扩展。

1.5 多态的弊端

我们已经知道多态编译阶段是看左边父类类型的,如果子类有些独有的功能,此时多态的写法就无法访问子类独有功能了

  1. class Animal{
  2. public void eat(){
  3. System.out.println("动物吃东西!")
  4. }
  5. class Cat extends Animal {
  6. public void eat() {
  7. System.out.println("吃鱼");
  8. }
  9. public void catchMouse() {
  10. System.out.println("抓老鼠");
  11. }
  12. }
  13. class Dog extends Animal {
  14. public void eat() {
  15. System.out.println("吃骨头");
  16. }
  17. }
  18. class Test{
  19. public static void main(String[] args){
  20. Animal a = new Cat();
  21. a.eat();
  22. a.catchMouse();//编译报错,编译看左边,Animal没有这个方法
  23. }
  24. }

1.6 引用类型转换

1.6.1 为什么要转型

多态的写法就无法访问子类独有功能了。

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点”小麻烦”。所以,想要调用子类特有的方法,必须做向下转型。

回顾基本数据类型转换

  • 自动转换: 范围小的赋值给范围大的.自动完成:double d = 5;
  • 强制转换: 范围大的赋值给范围小的,强制转换:int i = (int)3.14

    多态的转型分为向上转型(自动转换)与向下转型(强制转换)两种。

1.6.2 向上转型(自动转换)

  • 向上转型:多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。
    当父类引用指向一个子类对象时,便是向上转型。
    使用格式:
  1. 父类类型 变量名 = new 子类类型();
  2. 如:Animal a = new Cat();

原因是:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。所以子类范围小可以直接自动转型给父类类型的变量。

1.6.3 向下转型(强制转换)

  • 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
    一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

使用格式:

  1. 子类类型 变量名 = (子类类型) 父类变量名;
  2. 如:Aniaml a = new Cat();
  3. Cat c =(Cat) a;

1.6.4 案例演示

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点”小麻烦”。所以,想要调用子类特有的方法,必须做向下转型。

转型演示,代码如下:

定义类:

  1. abstract class Animal {
  2. abstract void eat();
  3. }
  4. class Cat extends Animal {
  5. public void eat() {
  6. System.out.println("吃鱼");
  7. }
  8. public void catchMouse() {
  9. System.out.println("抓老鼠");
  10. }
  11. }
  12. class Dog extends Animal {
  13. public void eat() {
  14. System.out.println("吃骨头");
  15. }
  16. public void watchHouse() {
  17. System.out.println("看家");
  18. }
  19. }

定义测试类:

  1. public class Test {
  2. public static void main(String[] args) {
  3. // 向上转型
  4. Animal a = new Cat();
  5. a.eat(); // 调用的是 Cat 的 eat
  6. // 向下转型
  7. Cat c = (Cat)a;
  8. c.catchMouse(); // 调用的是 Cat 的 catchMouse
  9. }
  10. }

1.6.5 转型的异常

转型的过程中,一不小心就会遇到这样的问题,请看如下代码:

  1. public class Test {
  2. public static void main(String[] args) {
  3. // 向上转型
  4. Animal a = new Cat();
  5. a.eat(); // 调用的是 Cat 的 eat
  6. // 向下转型
  7. Dog d = (Dog)a;
  8. d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
  9. }
  10. }

这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。

1.6.6 instanceof关键字

为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:

  1. 变量名 instanceof 数据类型
  2. 如果变量属于该数据类型或者其子类类型,返回true
  3. 如果变量不属于该数据类型或者其子类类型,返回false

所以,转换前,我们最好先做一个判断,代码如下:

  1. public class Test {
  2. public static void main(String[] args) {
  3. // 向上转型
  4. Animal a = new Cat();
  5. a.eat(); // 调用的是 Cat 的 eat
  6. // 向下转型
  7. if (a instanceof Cat){
  8. Cat c = (Cat)a;
  9. c.catchMouse(); // 调用的是 Cat 的 catchMouse
  10. } else if (a instanceof Dog){
  11. Dog d = (Dog)a;
  12. d.watchHouse(); // 调用的是 Dog 的 watchHouse
  13. }
  14. }
  15. }

第二章 内部类

2.1 概述

2.1.1 什么是内部类

将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。可以把内部类理解成寄生,外部类理解成宿主。

内部类是Java类的五大成份之一,也是我们最后一个需要学习的成份。

2.1.2 什么时候使用内部类

一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用

  1. 人里面有一颗心脏。
  2. 汽车内部有一个发动机。
  3. 为了实现更好的封装性。

2.2 内部类的分类

按定义的位置来分

  1. 静态内部类,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
  2. 实例内部内,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)
  3. 局部内部类,类定义在方法内
  4. 匿名内部类。一般定义在方法中,或者可执行代码中

2.3 静态内部类

静态内部类特点

  • 有static修饰的内部类,属于外部类本身的。
  • 总结:静态内部类与其他类的用法完全一样。只是访问的时候需要加上外部类.内部类。
  • 拓展:静态内部类可以直接访问外部类的静态成员。

内部类的使用格式

  1. 外部类.内部类。

静态内部类对象的创建格式

  1. 外部类.内部类 变量 = new 外部类.内部类构造器;

案例演示

  1. // 外部类:Outer01
  2. class Outer01{
  3. private static String sc_name = "黑马程序";
  4. // 内部类: Inner01
  5. public static class Inner01{
  6. // 这里面的东西与类是完全一样的。
  7. private String name;
  8. public Inner01(String name) {
  9. this.name = name;
  10. }
  11. public void showName(){
  12. System.out.println(this.name);
  13. // 拓展:静态内部类可以直接访问外部类的静态成员。
  14. System.out.println(sc_name);
  15. }
  16. }
  17. }
  18. public class InnerClassDemo01 {
  19. public static void main(String[] args) {
  20. // 创建静态内部类对象。
  21. // 外部类.内部类 变量 = new 外部类.内部类构造器;
  22. Outer01.Inner01 in = new Outer01.Inner01("张三");
  23. in.showName();
  24. }
  25. }

2.4 实例内部类

实例内部类特点

  • 无static修饰的内部类,属于外部类对象的。
  • 宿主:外部类对象。

内部类的使用格式

  1. 外部类.内部类。 // 访问内部类的类型都是用 外部类.内部类

实例内部类创建对象格式

  1. 外部类.内部类 变量 = new 外部类构造器.new 内部类构造器;
  • 拓展1:实例内部类不能定义静态成员。
  • 拓展2:实例内部类可以直接访问外部类的私有和静态成员。
    案例演示
  1. public class InnerClassDemo02 {
  2. public static void main(String[] args) {
  3. // 宿主:外部类对象。
  4. // Outer02 out = new Outer02();
  5. // 创建内部类对象。
  6. Outer02.Inner02 in = new Outer02().new Inner02("张三");
  7. in.showName();
  8. }
  9. }
  10. class Outer02{
  11. // 实例内部类,属于外部类对象的。
  12. // 拓展:实例内部类不能定义静态成员。
  13. public class Inner02{
  14. // 这里面的东西与类是完全一样的。
  15. private String name;
  16. public Inner02(String name) {
  17. this.name = name;
  18. }
  19. public void showName(){
  20. System.out.println(this.name);
  21. }
  22. }
  23. }

2.5 实例内部类面试题

请在?地方向上相应代码,以达到输出的内容

注意:内部类访问外部类对象的格式是:外部类名.this

  1. public class Demo05 {
  2. public static void main(String[] args) {
  3. Body.Heart heart = new Body().new Heart();
  4. heart.jump();
  5. }
  6. }
  7. class Body { // 身体
  8. private int weight = 30;
  9. // 在成员位置定义一个类
  10. class Heart {
  11. private int weight = 20;
  12. public void jump() {
  13. int weight = 10;
  14. System.out.println("心脏在跳动 " + ?); // 10
  15. System.out.println("心脏在跳动 " + ?); // 20
  16. System.out.println("心脏在跳动 " + ?); // 30
  17. }
  18. }
  19. }

2.6 局部内部类

  • 局部内部类 :定义在方法中的类。

定义格式:

  1. class 外部类名 {
  2. 数据类型 变量名;
  3. 修饰符 返回值类型 方法名(参数列表) {
  4. // …
  5. class 内部类 {
  6. // 成员变量
  7. // 成员方法
  8. }
  9. }
  10. }

局部内部类编译后仍然是一个独立的类,编译后有$还有一个数字。Chinese$1Chopsticks.class

2.7 匿名内部类【重点】

2.7.1 概述

匿名内部类 :是内部类的简化写法。它的本质是一个带具体实现的 父类或者父接口的 匿名的 子类对象
开发中,最常用到的内部类就是匿名内部类了。

2.7.2 引入

实际上,如果我们希望定义一个只要使用一次的类,就可考虑使用匿名内部类。匿名内部类的本质作用

是为了简化代码

之前我們使用接口时,似乎得做如下几步操作:

  1. 定义子类
  2. 重写接口中的方法
  3. 创建子类对象
  4. 调用重写后的方法
  1. interface Swim {
  2. public abstract void swimming();
  3. }
  4. // 1. 定义接口的实现类
  5. class Student implements Swim {
  6. // 2. 重写抽象方法
  7. @Override
  8. public void swimming() {
  9. System.out.println("狗刨式...");
  10. }
  11. }
  12. public class Demo07 {
  13. public static void main(String[] args) {
  14. // 3. 创建实现类对象
  15. Student s = new Student();
  16. // 4. 调用方法
  17. s.swimming();
  18. }
  19. }

我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快捷方式。

2.7.3 匿名内部类前提和格式

匿名内部类必须继承一个父类或者实现一个父接口

匿名内部类格式

  1. new 父类名或者接口名(){
  2. // 方法重写
  3. @Override
  4. public void method() {
  5. // 执行语句
  6. }
  7. };

2.7.4 使用方式

以接口为例,匿名内部类的使用,代码如下:

创建匿名内部类,并调用:GUI做界面

  1. interface Swim {
  2. public abstract void swimming();
  3. }
  4. public class Demo07 {
  5. public static void main(String[] args) {
  6. // 使用匿名内部类
  7. new Swim() {
  8. @Override
  9. public void swimming() {
  10. System.out.println("自由泳...");
  11. }
  12. }.swimming();
  13. // 接口 变量 = new 实现类(); // 多态,走子类的重写方法
  14. Swim s2 = new Swim() {
  15. @Override
  16. public void swimming() {
  17. System.out.println("蛙泳...");
  18. }
  19. };
  20. s2.swimming();
  21. s2.swimming();
  22. }
  23. }

2.7.5 匿名内部类的特点

  1. 定义一个没有名字的内部类
  2. 这个类实现了父类,或者父类接口
  3. 匿名内部类会创建这个没有名字的类的对象

2.7.6 匿名内部类的使用场景

通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。代码如下:

  1. interface Swim {
  2. public abstract void swimming();
  3. }
  4. public class Demo07 {
  5. public static void main(String[] args) {
  6. // 普通方式传入对象
  7. // 创建实现类对象
  8. Student s = new Student();
  9. goSwimming(s);
  10. // 匿名内部类使用场景:作为方法参数传递
  11. Swim s3 = new Swim() {
  12. @Override
  13. public void swimming() {
  14. System.out.println("蝶泳...");
  15. }
  16. };
  17. // 传入匿名内部类
  18. goSwimming(s3);
  19. // 完美方案: 一步到位
  20. goSwimming(new Swim() {
  21. public void swimming() {
  22. System.out.println("大学生, 蛙泳...");
  23. }
  24. });
  25. goSwimming(new Swim() {
  26. public void swimming() {
  27. System.out.println("小学生, 自由泳...");
  28. }
  29. });
  30. }
  31. // 定义一个方法,模拟请一些人去游泳
  32. public static void goSwimming(Swim s) {
  33. s.swimming();
  34. }
  35. }

第三章 包和权限修饰符

3.1 包

包我们每天建的项目就是在一个目录下,我们每次都会建立一个包,这个包在磁盘下其实就是一个目录。包是用来分门别类的管理技术,不同的技术类放在不同的包下,方便管理和维护。

在IDEA项目中,建包的操作如下:

多态、包、权限修饰符、内部类,Object类,Date类 - 图1

包名的命名规范

  1. 路径名.路径名.xxx.xxx
  2. // 例如:com.zsy.oa
  • 包名一般是公司域名的倒写。例如:黑马是www.itheima.com,包名就可以定义成com.itheima.技术名称。
  • 包名必须用”.“连接。
  • 包名的每个路径名必须是一个合法的标识符,而且不能是Java的关键字。

3.2 权限修饰符

在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,我们之前已经学习过了public 和 private,接下来我们研究一下protected和缺省(default默认)修饰符的作用。

  • public:公共的,所有地方都可以访问。
  • protected:当前类 ,当前包,当前类的子类可以访问。
  • 缺省(没有修饰符):当前类 ,当前包可以访问。
  • private:私有的,当前类可以访问。
    public > protected > 缺省 > private

3.3 不同权限的访问能力

public protected 缺省(空的) private
同一类中
同一包中的类
不同包的子类
不同包中的无关类

可见,public具有最大权限。private则是最小权限。

编写代码时,如果没有特殊的考虑,建议这样使用权限:

  • 成员变量使用private ,隐藏细节。
  • 构造方法使用public ,方便创建对象。
  • 成员方法使用public ,方便调用方法。

小贴士:不加权限修饰符,就是default权限

第四章 Object类

4.1 概述

java.lang.Object类是Java语言中的根类,即所有类的父类。它中描述的所有方法子类都可以使用。在对象实例化的时候,最终找的父类就是Object。

如果一个类没有特别指定父类, 那么默认则继承自Object类。例如:

  1. public class MyClass /*extends Object*/ {
  2. // ...
  3. }

根据JDK源代码及Object类的API文档,Object类当中包含的方法有11个。今天我们主要学习其中的2个:

  • public String toString():返回该对象的字符串表示。
  • public boolean equals(Object obj):指示其他某个对象是否与此对象“相等”。

4.2 toString方法

方法摘要

  • public String toString():返回该对象的字符串表示。

toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值。

由于toString方法返回的结果是内存地址,而在开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此也需要重写它。

覆盖重写

如果不希望使用toString方法的默认行为,则可以对它进行覆盖重写。例如自定义的Person类:

  1. public class Person {
  2. private String name;
  3. private int age;
  4. @Override
  5. public String toString() {
  6. return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
  7. }
  8. // 省略构造器与Getter Setter
  9. }

在IntelliJ IDEA中,可以点击Code菜单中的Generate...,也可以使用快捷键alt+insert,点击toString()选项。选择需要包含的成员变量并确定。如下图所示:

多态、包、权限修饰符、内部类,Object类,Date类 - 图2

小贴士: 在我们直接使用输出语句输出对象名的时候,其实通过该对象调用了其toString()方法。

4.3 equals方法

方法摘要

  • public boolean equals(Object obj):指示其他某个对象是否与此对象“相等”。

调用成员方法equals并指定参数为另一个对象,则可以判断这两个对象是否是相同的。这里的“相同”有默认和自定义两种方式。

默认地址比较

如果没有覆盖重写equals方法,那么Object类中默认进行==运算符的对象地址比较,只要不是同一个对象,结果必然为false。

对象内容比较

如果希望进行对象的内容比较,即所有或指定的部分成员变量相同就判定两个对象相同,则可以覆盖重写equals方法。例如:

  1. import java.util.Objects;
  2. public class Person {
  3. private String name;
  4. private int age;
  5. @Override
  6. public boolean equals(Object o) {
  7. // 如果对象地址一样,则认为相同
  8. if (this == o)
  9. return true;
  10. // 如果参数为空,或者类型信息不一样,则认为不同
  11. if (o == null || getClass() != o.getClass())
  12. return false;
  13. // 转换为当前类型
  14. Person person = (Person) o;
  15. // 要求基本类型相等,并且将引用类型交给java.util.Objects类的equals静态方法取用结果
  16. return age == person.age && Objects.equals(name, person.name);
  17. }
  18. }

这段代码充分考虑了对象为空、类型一致等问题,但方法内容并不唯一。大多数IDE都可以自动生成equals方法的代码内容。在IntelliJ IDEA中,可以使用Code菜单中的Generate…选项,也可以使用快捷键alt+insert,并选择equals() and hashCode()进行自动代码生成。如下图所示:

多态、包、权限修饰符、内部类,Object类,Date类 - 图3

多态、包、权限修饰符、内部类,Object类,Date类 - 图4

多态、包、权限修饰符、内部类,Object类,Date类 - 图5

tips:Object类当中的hashCode等其他方法,今后学习。

第五章 Objects类

Objects类是对象工具类,它里面的的方法都是用来操作对象的。

5.1 equals方法

在刚才IDEA自动重写equals代码中,使用到了java.util.Objects类,那么这个类是什么呢?

JDK7添加了一个Objects工具类,它提供了一些方法来操作对象,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象。

在比较两个对象的时候,Object的equals方法容易抛出空指针异常,而Objects类中的equals方法就优化了这个问题。方法如下:

  • public static boolean equals(Object a, Object b):判断两个对象是否相等。

我们可以查看一下源码,学习一下:

  1. public static boolean equals(Object a, Object b) {
  2. return (a == b) || (a != null && a.equals(b));

5.2 isNull

static boolean isNull(Object obj) 判断对象是否为null,如果为null返回true。

  1. Student s1 = null;
  2. Student s2 = new Student("蔡徐坤", 22);
  3. // static boolean isNull(Object obj) 判断对象是否为null,如果为null返回true
  4. System.out.println(Objects.isNull(s1)); // true
  5. System.out.println(Objects.isNull(s2)); // false

第六章 Date类

6.1 Date概述

java.util.Date`类 表示特定的瞬间,精确到毫秒。

继续查阅Date类的描述,发现Date拥有多个构造函数,只是部分已经过时,我们重点看以下两个构造函数

  • public Date():从运行程序的此时此刻到时间原点经历的毫秒值,转换成Date对象,分配Date对象并初始化此对象,以表示分配它的时间(精确到毫秒)。
  • public Date(long date):将指定参数的毫秒值date,转换成Date对象,分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即1970年1月1日00:00:00 GMT)以来的指定毫秒数。

tips: 由于中国处于东八区(GMT+08:00)是比世界协调时间/格林尼治时间(GMT)快8小时的时区,当格林尼治标准时间为0:00时,东八区的标准时间为08:00。

简单来说:使用无参构造,可以自动设置当前系统时间的毫秒时刻;指定long类型的构造参数,可以自定义毫秒时刻。例如:

  1. import java.util.Date;
  2. public class Demo01Date {
  3. public static void main(String[] args) {
  4. // 创建日期对象,把当前的时间
  5. System.out.println(new Date()); // Tue Jan 16 14:37:35 CST 2020
  6. // 创建日期对象,把当前的毫秒值转成日期对象
  7. System.out.println(new Date(0L)); // Thu Jan 01 08:00:00 CST 1970
  8. }
  9. }

tips:在使用println方法时,会自动调用Date类中的toString方法。Date类对Object类中的toString方法进行了覆盖重写,所以结果为指定格式的字符串。

6.2 Date常用方法

Date类中的多数方法已经过时,常用的方法有:

  • public long getTime() 把日期对象转换成对应的时间毫秒值。
  • public void setTime(long time) 把方法参数给定的毫秒值设置给日期对象

示例代码

  1. public class DateDemo02 {
  2. public static void main(String[] args) {
  3. //创建日期对象
  4. Date d = new Date();
  5. //public long getTime():获取的是日期对象从1970年1月1日 00:00:00到现在的毫秒值
  6. //System.out.println(d.getTime());
  7. //System.out.println(d.getTime() * 1.0 / 1000 / 60 / 60 / 24 / 365 + "年");
  8. //public void setTime(long time):设置时间,给的是毫秒值
  9. //long time = 1000*60*60;
  10. long time = System.currentTimeMillis();
  11. d.setTime(time);
  12. System.out.println(d);
  13. }
  14. }

小结:Date表示特定的时间瞬间,我们可以使用Date对象对时间进行操作。