关于Object类的简介

2.Object

2.1.概述
1. Object类是所有类继承层次的祖先类,所有类(包括数组)都直接或者间接的继承自该类,都实现了该类的方法。
2. 我们在自定义类时,并不需要特别标注extends Object。
3. 如果一个类没有明确的指出它的父类,Object类就默认被认为是这个类的父类,extends Object则被省略了。
2.2.默认的无参构造
  1. 当一个类没有定义构造方法的时候,就会自动添加默认构造方法
  2. 一旦有默认构造方法,在创建子类对象的时候,就会执行子类对象的隐式初始化
  3. 隐式初始化,默认调用父类的无参构造
  4. 所以最终,一定能保证,调用到Object类的无参构造方法,先初始化Object这个父类
2.3.一些常用成员方法
2.3.1.public final Class getClass()
  • 其作用是返回调用此方法的Object的运行时类的Class对象。
  • 需要注意的是Class首字母大写,这是一个类的对象,和String一样
  • JVM每加载一个类,都会在内存中创建唯一一个和该类对应的Class对象
    • 这个Class对象包含了这个类的全部信息
    • 帮助程序员在运行时期,了解该对象的属性和行为
    • Class对象处于堆上
    • 在运行时期程序员可以通过这个类,获取该类型的所有信息
  • 由于Class对象和类是一一对应的,所有Class对象很多时候被称为类对象
    • Class对象和方法区中加载的字节码文件,都是在触发类加载的时候生成的
    • 类加载只会触发一次,Class对象也只独一份
  • 类的对象和类对象的区别
    • 一个类的Class对象叫做类对象,也称之为运行时类对象,整个程序运行期间独一份
    • 类的对象是类的一个实例,程序运行期间可以创建多个
  • Class类对象是反射的基础
  • Class的常用API
    • getName() 获取类的全限定类名
    • getSimpleName() 获取类名
2.3.2.public String toString()
  • 返回该对象的字符串(String)表示
  • 通常,toString 方法会返回一个“以文本方式表示”此对象的字符串
  • 结果应是一个简明但易于读懂的信息表达式
  • 建议所有子类都重写此方法
  • 直接打印数组名或者对象名,默认调用 toString() 方法,然后打印该方法返回的字符串
  • 用一个字符串和一个对象直接拼接,默认拼接该类的 toString() 方法字符串
  • debug时下一步会打印 toString() 返回的字符串
    • 不要在 toString() 方法里对对象进行操作,避免造成奇怪的bug
  • 如果类中有别的引用类型,可以在返回语句中调用该引用类型的 toString() 方法
2.3.3.public boolean equals(Object obj)
  • 指示其他某个对象是否与此对象“相等”

    • 此对象:调用 equals() 方法的对象
    • 其他某个对象:equals() 方法括号中的对象
  • 对象相等的含义

    • 我们理想状态下的对象相等:
      • 首先两个对象的类型要相同,如果类型都不想同,那就没有意义了
      • 类型相同的情况下,比较成员变量的取值是否相等,都相等则认为两个对象相等
    • Object 类中的相等:
      • 只有两个对象的内存地址相等,才叫相等
      • Object类中的 equals() 方法等价于 ==
      • 对象存在堆上没有办法直接拿出来比,于是 == 比较的是两个对象的引用
      • 两个引用变量是否相等,取决于它们是否指向了同一对象,也就是比较对象的内存地址
    • Object 类中的 equal() 方法不能满足我们需求,需要自己手动重写该方法
  • 设计 equals() 方法需遵循的原则

    • 自反性:对于任何非空引用值xx.equals(x) 都应返回 true
    • 排他性:当比对的不是同种类型的对象或者是一个null时,默认返回 false
    • 对称性:对于任何非空引用值 xy ,当且仅当 y.equals(x) 返回 true 时, x.equals(y) 才应返回 true
    • 传递性:对于任何非空引用值 xyz ,如果 x.equals(y) 返回 true ,并且 y.equals(z) 返回 true
      • 那么 x.equals(z) 应返回 true
    • 一致性:对于任何非空引用值 xy ,多次调用 x.equals(y) 始终返回 true 或始终返回 false
      • 前提是对象上 equals 比较中所用的信息没有被修改
    • 其实只要按照下述原则重写,就能够满足上面的常规协定
      • 若是不同类型的对象,直接认定不相等
      • 若是相同类型的对象,认为具有相同的成员变量的两个对象,才是相等的
  • equals 方法使用注意事项:

    1. 对于任何非空引用值 xx.equals(null) 都应返回 false
    2. 不要使用一个 null 常量,调用方法,会引发程序错误
      • 在方法中,我们只能对方法的参数进行校验,没办法校验调用者
      • 应该在外部写代码,防止使用一个 null 去调用方法
    3. 如果类中有引用类型的成员变量,继续调用该引用类型的 equal() 方法判断
2.3.4.public int hashCode()
  • 通过一个哈希(散列)函数,返回该对象的哈希(码)值
  • 支持此方法是为了提高哈希表的性能(主要是集合体系中用)
  • hashCode 的常规协定:
    1. 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将象进行 equals 比较时所用的信息没有被修改(哈希值是根据成员变量来计算的)。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
    2. 如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
    3. 如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
  • hashCode() 方法是会返回一个对应于当前对象的一个整数,为了将来在集合中更快捷的查找对象
    • 集合类似于数组,但不同的是集合中只能装对象
    • Java中的集合类 hashMaphashSet 底层的实现都是哈希表,之所以采用哈希表
      • 是因为他能克服链表和二叉树等数据结构在查询上的低效率问题
      • 哈希表的存储,就是根据对象的哈希值,以此确定他的存储位置
2.3.5.protected Object clone()
  • 创建并返回此对象的一个副本(对象的引用)

  • 对于任何对象 x,表达式:

    1. x.clone() != xtrue(这一条是必须的)
    2. x.clone().getClass() == x.getClass()true(一般情况下满足,非必须)
    3. x.clone().equals(x)true(一般情况下满足,非必须)
  • clone() 方法的使用规范:

    ​ 1,要想使用一个类的 clone() 方法,这个类都必须实现 Cloneable 接口,否则抛出CloneNotSupportedException 异常

    ​ 2,在一个类中创建,自身类对象的拷贝,无需重写 clone() 方法

    ​ 3,Object 类中的 clone()protected 方法,除开2中的情况外,都必须重写 clone() 方法,用来改变其访问权限

  • Cloneable 接口:

    1. 该接口是一个标记接口,没有任何方法
    2. 空(标记)接口的意义:
      • 实现空接口后,从成员角度,该类没有任何变化
      • 但是在内存中,该类的数据类型已然发生了改变,成为了这个接口的一个实现类
    3. 空接口的作用:
      • 空接口给该实现类打上了标记
      • 使用 instanceOf 运算符,可以判断一个类是否是该空接口的实现类
      • 如果判定是,可以进行一系列操作

    使用clone() 方法创建对象:使用clone() 方法创建对象和new创建对象属于同等级别

  • 浅拷贝和深拷贝:

    • 浅拷贝:
      • 被复制对象的,所有基本类型成员变量值,都与原来对象的相同,且独立
      • 被复制对象的,所有引用类型的引用,仍然指向原来的对象,相当于复制了对象引用,而没有复制对象
    • 深拷贝:
      • 在浅拷贝的基础上,复制对象引用的同时,也复制了对象,并让复制的引用指向了复制的对象
      • Object 类当中的 clone() 方法只是浅拷贝
      • 深拷贝基于浅拷贝实现,需要我们手动重写 clone() 方法实现
  • 案例:

/**
 * 对于创建深拷贝的副本,应该按如下步骤进行
 * 1.每个子类及其父类实现Clonable接口
 * 2.重写clone()方法并抛出CloneNotSupportedException异常,因为clone()方法的protected访问权限导致子类无法直接调用
 * 3.对于每个涉及到的引用类型重新克隆
 * 4.克隆完毕的对象指向新克隆的引用类型
 * 5.返回克隆完毕的对象
 *
 */

public class Star implements Cloneable{
    int age;
    String name;

    public Star() {
    }

    public Star(int age, String name) {
        this.age = age;
        this.name = name;
    }
    
    //对于无引用类型的类,直接克隆即可,因为String的不变形,所以不必克隆
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //浅克隆
        return super.clone();
    }
}

public class Student implements Cloneable{
    int age;
    String name;
    Star s;

    public Student() {
    }

    public Student(int age, String name, Star s) {
        this.age = age;
        this.name = name;
        this.s = s;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //调用父类Object的clone()方法,返回Object对象,应强制类型转化为Student类型
        Student cloneStudent = (Student) super.clone();
        
        //对于Student对象,里边的Star引用类型应重新克隆,继续调用Clone()方法,并返回Star类型
        Star cloneStar = (Star) this.s.clone();
        
        //Student的对象里的Star引用类型指向新克隆的对象
        cloneStudent.s = cloneStar;
        
        //返回克隆完毕的Student类型
        return cloneStudent;
    }
}

public class Teacher implements Cloneable{
    int age;
    String name;
    Student s;

    public Teacher() {
    }

    public Teacher(int age, String name, Student s) {
        this.age = age;
        this.name = name;
        this.s = s;
    }
    
    //与上一个克隆同理
    //对于克隆的对象,每个被克隆对象只需管好自己的引用成员即可,不需要管引用成员里嵌套的引用成员变量,所以只需克隆两层
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Teacher cloneTeacher = (Teacher) super.clone();
        Student cloneStudent = (Student) this.s.clone();
        cloneTeacher.s = cloneStudent;
        return cloneTeacher;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", s.name='" + s.name +'\''+
                ",s.age="+s.age +
                ",star.name='" + s.s.name +'\''+
                ",star.age="+ s.s.age +
                '}';
    }
}

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        //Test
        Teacher t = new Teacher(18, "马云", new Student(10, "马化腾", new Star(99, "张三")));
        Teacher cloneTeacher = (Teacher) t.clone();
        System.out.println(cloneTeacher);
        System.out.println(t);
        System.out.println("-----------------------");
        System.out.println(cloneTeacher.s);
        System.out.println(t.s);
        System.out.println("-----------------------");
        System.out.println(cloneTeacher.s.s);
        System.out.println(t.s.s);
        cloneTeacher.name = "新垣结衣";
        System.out.println(cloneTeacher.name);
        System.out.println(t.name);
    }
}

//输出结果
Teacher@1b6d3586
Teacher@4554617c
-----------------------
Student@74a14482
Student@1540e19d
-----------------------
Star@677327b6
Star@14ae5a5
    
新垣结衣
马云

你可能感兴趣的:(Java)