3.4 继承(下)



在继承体系中,有个远古的老祖宗:Object类。

拜拜山头~

一、Object类

1.1 概念

Object类是所有类的父类,也叫根类。

一个类,如果没有使用extends关键字明确标识继承关系,那么默认继承Object类(包括数组)。

Java中的每个类都可以使用Object类中定义的方法。

1.2 查看Object类及相关成员

在Oracle官网,查看认识Object类及相关成员:

equals()方法为例:

其中,有两个方法:equals( )方法、toString( )方法,是在子类当中,重写几率非常高的方法。

1.3 重写equals( )方法

1.系统提供的两种equals( )方法的区别:

上述的Object类中的equals()方法,判断的是对象的引用是否一致。

下面的子类String中的equals()方法,其实是方法的重写,判断的是字符串的值是否一致。

2.代码验证系统提供的两种equals( )方法的区别

(1)一般实例化对象,默认继承和调用Object类中的equals()方法。

这比较的是两个引用是否指向同一个对象,即是否指向同一个内存空间。

(2)如果是系统内置的子类String类的实例化对象,那么调用的是该系统内置子类String中,重写过的equals()方法。

这比较的是值。

  • Eclipse的智能提示验证:

  • Animal.java中:
package com.imooc.animal;

public class Animal {
    private String name="妮妮";//昵称
    protected int month=2;//月份
    String species="动物";//品种
    
    public Animal() {//无参构造
        System.out.println("我是父类的无参构造方法");
    }
    
    public Animal(String name,int month) {//带参构造
        this.name=name;
        this.month=month;
        System.out.println("我是父类的带参构造方法");
    }
    
    ...
    
    public void eat() {
        System.out.println("这是父类的方法,"+this.getName()+"这个动物在吃东西");    
    }
}
  • TestThree.java中:
package test;

import com.imooc.animal.Animal;

public class TestThree {
    public static void main(String[] args) {
        //equals方法
        Animal one=new Animal("花花",2);
        Animal two=new Animal("花花",2);
        boolean flag=one.equals(two);        //默认情况下,调用的是Object类中的equals()方法
        System.out.println("One和two的引用比较:"+flag);
        System.out.println("One和two的引用比较:"+(one==two));    
        
        System.out.println("======================================");
        String str1=new String("hello");
        String str2=new String("hello");
        flag=str1.equals(str2);        //调用的是系统内置的子类String中,重写的equals()方法                
        System.out.println("str1和str2的引用比较:"+flag);
        System.out.println("str1和str2的引用比较:"+(str1==str2));
    }

}

--->
One和two的引用比较:false            //比较的是引用,new两个对象一定是两个不同的空间
One和two的引用比较:false
======================================
str1和str2的引用比较:true            //比较的是值,值一样啊
str1和str2的引用比较:false

3.在自定义类中自己重写的equals( )方法

在自定义类Animal类中重写equals方法,用于进行值的比较。

  • Animal.java中:
package com.imooc.animal;

public class Animal {
    private String name="妮妮";//昵称
    protected int month=2;//月份
    String species="动物";//品种    
    ...
    
    //在Animal类中重写equals方法
    public boolean equals(Object obj) {
        if(obj==null) //如果没有这个判空操作,就会报出空指针异常
            return false;//如果为空,不需要做任何判断,直接返回false
        
        Animal temp=(Animal)obj;//将Object类型,强转为Animal类型
        //只有不为空,才会将待比较对象与传入的对象进行比较        
        if(this.getName().equals(temp.getName())&&(this.getMonth()==temp.getMonth()))
            return true;
        else
            return false;
    }

注意:

(1)对象之间相互操作,一定要先判空,避免空指针异常。

(2)比较运算符==的作用:

  • 对于基本数据类型,比较的是变量中的值是否相同;
  • 对于引用数据类型(例如String、自定义类等),比较的是对象的内存地址。

4.总结

所以,一般情况下:

  • 对于比较的是基本数据类型,推荐用==;
  • 对于比较的是数据类型(例如String、自定义类等),推荐用equals()方法

5.优化

在上述自定义类Animal类中重写equals方法时,需要强转对象类型,这有可能会发生类型转换带来的异常。

因此,可以通过一个方法重载,限定传入的对象类型:优化如下:

  • Animal.java中:
package com.imooc.animal;

public class Animal {
    private String name="妮妮";//昵称
    protected int month=2;//月份
    String species="动物";//品种
    ...
    
    public boolean equals(Object obj) {
        if(obj==null) 
            return false;
     
        Animal temp=(Animal)obj;
        if(this.getName().equals(temp.getName())&&(this.getMonth()==temp.getMonth()))
            return true;
        else
            return false;
    }
    
    //针对上面,是方法重载
    public boolean equals(Animal obj) {//限定传入的对象类型,只能是Animal类
        if(obj==null) 
            return false;
        if(this.getName().equals(obj.getName())&&(this.getMonth()==obj.getMonth()))
            return true;
        else
            return false;
}

问题:

不是如果为空,就要实例化对象?

1.4 重写toString( )方法

用途:用于输出对象的字符串表现形式。

1.默认调用远古老祖宗Object类中的toString( )方法。

输出形式:包名+类名@内存位置

TestThree.java中:

package test;

import com.imooc.animal.Animal;

public class TestThree {
    public static void main(String[] args) {
        Animal one=new Animal("花花",2);
        Animal two=new Animal("花花",2);
        
        System.out.println(one.toString());//都是默认调用远古之父Object类的toString()方法
        System.out.println(one);//当语句中输出对象名时,也是如此
    }

}

--->
com.imooc.animal.Animal@626b2d4a
com.imooc.animal.Animal@626b2d4a

2.在自定义类中自己重写toString( )方法

  • Animal.java中:
package com.imooc.animal;

public class Animal {
    private String name="妮妮";//昵称
    protected int month=2;//月份
    String species="动物";//品种
    ...
        
    //改写
     public String toString() {
        return "昵称:"+this.getName()+";年龄:"+this.getMonth();
    }
       
}
  • TestThree.java中:
package test;

import com.imooc.animal.Animal;

public class TestThree {
    public static void main(String[] args) {
        Animal one=new Animal("花花",2);
        Animal two=new Animal("花花",2);
        
        System.out.println(one.toString());//调用的是自定义类中已经改写过的方法
        System.out.println(one);//调用的也是自定义类中已经改写过的方法
    }

}

--->
昵称:花花;年龄:2
昵称:花花;年龄:2

至于其他的方法,可以查阅前面提及的文档。

养成查阅官方技术文档的习惯:

https://docs.oracle.com/javase/8/docs/api/index.html

二、final关键字

继承有利于提高代码的复用性和灵活性。但是,有时候我们并不想这个类被继承、这个方法被重写、这个变量的值被修改。

此时,final关键字应运而生。

总体上,final关键字可以提高性能,但会降低可扩展性。

2.1 让类不允许被继承

即,绝种了、不能有后代了。

1.书写格式

只要final关键字总体上写在class的前面即可,所以有两种写法:

public final class Animal(){};
final public class Animal(){};

2.代码演示

在Java的系统api中,也有很多是用final关键字来修饰的类。比如:

2.2 让方法不允许被子类重写

即,让方法不允许被子类重写,但是子类不能改但是可以调用

1.final与方法

让方法不允许被子类重写:

2.既然final关键字可以让方法不允许被子类重写,但是子类是否可以直接使用呢?

可以。

注意:final不能修饰构造方法。

2.3 让变量不允许被修改

1.final关键字与局部变量、成员变量:

根据变量的作用域不同,变量可以分为两大类:

  • 类中的成员属性:范围受限于四大访问修饰符。
  • 方法中的局部变量:范围仅限于方法中的大括号内。

(1)final关键字修饰的局部变量,一旦被赋值,不允许重新修改赋值。

此时,一般局部变量temp,什么时候使用,什么时候赋值即可。

(2)final关键字修饰类中的成员属性,也是一旦被赋值,不允许重新修改赋值

当final关键字修饰类中的成员属性,必须进行赋值。只有三种方式可以进行初始化,即赋值:

  • 定义的时候,直接初始化。
  • 定义的时候只声明,然后构造方法中进行初始化。
  • 定义的时候只声明,然后在构造代码块中进行初始化。

其他位置,不允许对final关键字修饰的类成员属性赋值。

2.final关键字与基本数据类型、引用数据类型

其中,对于引用数据类型:

final Animal ani=new Animal();

ani=new Animal();                //对象不能再重新实例化❌
ani.month=12;                    //对象的属性可以根据需要,进行修改!

2.4 可配合static关键字使用

如果对于类中的成员变量,既不希望在程序中被修改,同时,又希望作为全局变量来存在,那么此时就可以使用staticfinal关键字,来共同限定。

两者之间位置随意。

1.含义

表示全局的不允许被修订的内容:

public class Animal {
    public static final int temp=12;
alt+/-----------提示菜单

2.使用场景

只需要加载一次,不需要后期修改的内容,比如:

  • 配置信息
public static final String URL="www.imooc.com";

三、注解

本质:就是更有的辅助程序员、编译器,更好的理解代码。

3.1 Eclipse中子类重写父类的快速方法:

alt+/快捷键

其中,@Override的作用是:用于标注下面的方法,是否是一个合法的重写方法。

其实,这个符号,就是注解。

3.2 注解概念

(1)JDK1.5版本引入的一个i新特性;

(2)可以声明在包、类、属性、方法、、局部变量、方法参数等前面,对这个元素进行说明、注释。

本质:一种标记、标签,一种注释、说明。

3.3 注解分类

1.按照运行机制分:

(1)源码注解---------注解只在源码阶段保留,在编译阶段会被丢弃。例如,@override

(2)编译时注解------注解只要编译阶段保留,在虚拟机加载class文件时会被丢弃。例如,@NotNull表示不可为空

(3)运行时注解------注解可以保留到程序运行阶段时,任然起作用,甚至影响运行逻辑。例如,Spring注解:@Autowired

2.按照来源分

(1)来自JDK的注解--------------例如,@override

(2)来自第三方的注解----------例如,Spring注解:@Autowired

(3)我们自己定义的注解

3.特殊的注解

元注解:用于定义注解的注解。就是对注解进行标注。

例如,@Target用于限定注解可以用于什么地方

3.4 注解 @Override的使用

  • 注意大小写

当然,如果父类加个final关键字,那么子类是不能重写这个方法的。

特殊的情况是:

声明:Jerry's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 3.4 继承(下)


Follow excellence, and success will chase you.