JAVA常用类之Object类

JAVA常用类:

API概述:
  • 概念:
    API (Application Programming Interface) 代指应用程序编程接口。
  • JAVA API:
    就是Java提供给我们使用的类,这些类将底层的实现封装了起来,
    我们不需要关心这些类是如何实现的,只需要学习这些类如何使用。
Object 类:
  • 概念:Object:顶层父类,所有的类,都是直接或间接继承自它。
  • 构造方法:
    public Object()
    
    回想面向对象中为什么说:
    子类的构造方法默认访问的是父类的无参构造方法,所以JAVA中所有对象都会调用Object的构造方法,同时它们都是Object的子类(那么如果Object中有一些成员方法,则这些子类都会继承这些方法或者改写这些方法,以达到自己想要的效果!)
Object的hashCode()方法:
  • 示例:
    Object obj = new Object();
    int hashCode = obj.hashCode();
    ---------------
    输出:
    460141958
    
  • 特点:
    a. 返回该对象的哈希码值。默认情况下,该方法会根据对象的地址来计算。
    b. 不同对象的,hashCode()一般来说不会相同。 但是,同一个对象的hashCode()值肯定相同。
    c. 不是对象的实际地址值,可以理解为逻辑地址值。
Object类的getClass()方法:
  • 概念:返回对象对应的类的字节码文件对象!
  • 示例:
    Object obj = new Object();
    Class aClass = obj.getClass();  //Object.class---->JVM为这个文件创建的对象,
    								//aClass就是获取出来的代表Object.class这个文件(类)的字节码对象!
    System.out.println(aClass.getName());//可以通过Class类中的一个方法,获取对象的真实类的全名称。
    ------------------
    输出:
    java.lang.Object
    
  • 特点:
    a. 返回此 Object 的运行时类。
    b. 可以通过返回的文件字节码对象中的一个方法,获取对象的真实类的全名称。
Object类的toString()方法:
  • 概念:返回该对象的字符串表示。
  • 示例:
    • 源码追溯:
      System.out.println(obj);
      -------------------------
      输出:
      java.lang.Object@1b6d3586
      
      查看println源码可得到:
      public void println(Object x) {
          String s = String.valueOf(x);
          synchronized (this) {
              print(s);
              newLine();
          }
      }
      
      查看String.valueOf(x)的源码可得到:(看出来其执行的实际是obj.toString()方法返回的字符串!)
      public static String valueOf(Object obj) {
          return (obj == null) ? "null" : obj.toString();
      }
      
      继续追踪查看obj.toString()方法的源码可得到:
      public String toString() {
          return getClass().getName() + "@" + Integer.toHexString(hashCode());
      }
      
    • 结论:我们通过追踪System.out.println(obj)的源码发现,其打印的是obj.toString()方法返回的字符串!,由此可以通过prinln(obj)来得到一些有用的反馈,如果在改写了相应类的toString()方法后!
  • 问题:
    由于默认情况下的数据对我们来说没有意义,一般建议重写该方法。
    那么怎么重写呢? 一般是将该类的所有的成员变量组成返回即可
    示例:
    public class MyTest extends Object{
        public static void main(String[] args) {
            //常用类:Java给我们提供好的类,我们学习他里面封装的功能,灵活去用,完成我们的业务需求。
            //Object :顶层父类,所有的类,都是直接或间接继承自他
            Object obj = new Object();
            System.out.println(obj);
            String string = obj.toString();
            System.out.println(string);
    
         /*   public String toString () {
                return getClass().getName() + "@" + Integer.toHexString(hashCode());
            }
    	*/
    	     //我们认为老是打印地址值,不是我想要的。我想打印一些我们认为有用的数据,那么子类可以重新toString()
            Student student = new Student();
            System.out.println(student);
    	  }
    }
    
    
    class Student{
        int a=200;
    
        @Override
        public String toString() {
            return "Student{" +
                    "a=" + a +
                    '}';
        }
    }
    ----------------
    输出:
    java.lang.Object@1b6d3586
    java.lang.Object@1b6d3586
    Student{a=200}
    
Object类的equals()方法:
  • 概念:比较两个对象的地址值是否相同
  • 用法:
    boolean b = obj.equals(obj2);
    
  • 查看equals()方法的源码,发现与"=="一致。
    示例:(这是Object源码中对equals方法的定义!)
    public boolean equals(Object obj) {
        return (this == obj);
    }
    
  • == 和 equals()有什么区别?
    a. == 是一个比较运算符,他可以比较基本数据类,也可以比较引用数据类型
    b. == 比较基本数据类型,比较的是两个值是否相同,比较引用数据类型,比较的是地址值是否相同
    c. equals() 是Object类中的方法,只能比较引用数据类型,比较的是两个对象的地址值。
  • 问题:
    默认equals()方法比较两个对象的地址值,是否相同,认为意义不大。
    如果两个对象的成员变量值一模一样就认为两对象一样。
    (可以通过子类可以重写父类的equals()方法来实现!)
  • 示例:
    public class MyTest {
        public static void main(String[] args) {
            Student s1 = new Student("张三", 23);
            Student s2 = new Student("张三", 24);
            // System.out.println(s1==s2);
            //默认 equals(s2);方法比较两个对象的地址值,是否相同,我认为意义不是很大。
            //我认为两个对象的成员变量的值,一模一样,就认为两个对象一样。
            //子类就可以重新父类equals(s2)按照我定义的比较方式来比较
            boolean b = s1.equals(s2);//
            System.out.println(b);
    
        }
    }
    
    class Student{
        private String name;
        private int age;
    
        public Student() {
        }
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        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;
        }
    
        @Override //重写部分!
        public boolean equals(Object obj) {
            // 我认为两个对象的成员变量的值,一模一样,就认为两个对象一样。
            // 非空判断:
            if(obj == null){
                return false;
            }
            // 从效率方面考虑:
            if (this == obj){
                return true;
            }
            // 从健壮性考虑:
            // 判断他传过来的这个引用(对象)是不是该类的引用(对象)
            // instancedof()判断一个对象(引用)是不是该类型的一个引用
            if (!(obj instanceof Student)){ //obj 是否是 Student 的实例对象?
                return false;
            }
            Student transform_obj =  ((Student)(obj)); //向下转型!
    		// String 类是引用类型,你用 == 来比较两个字符串,比较的是两个字符串的地址值是否一样,如果想要比较两个字符串的字面值内容是否一样,则调用equals()方法来比较!
            // String 类也是继承于Object方法的!
            // String 类也重写了equals()方法来比较两个字符串的内容是否相同(很多类都重写类Object的equals()方法!)
            //旧版:
            //if (transform_obj.age == this.age && transform_obj.name == this.name){
            //    return true;
            //}
            //改进版本
    		if (transform_obj.age == this.age && transform_obj.name.equals(this.name)){
                return true;
            }
            return false;
        }
    }
    --------------------------------------------
    输出:
    true
    
  • 改进版:
    示例:
    public class MyTest {
        public static void main(String[] args) {
            Student s1 = new Student("张三", 23);
            Student s2 = new Student("张三", 23);
            Student s3 = new Student("张三",25);
            Dog d1 = new Dog("二哈",15);
            // System.out.println(s1==s2);
            //默认 equals(s2);方法比较两个对象的地址值,是否相同,我认为意义不是很大。
            //我认为两个对象的成员变量的值,一模一样,就认为两个对象一样。
            //子类就可以重新父类equals(s2)按照我定义的比较方式来比较
            System.out.println(s1.equals(s2));
            System.out.println(s1.equals(s3));
            System.out.println(s1.equals(s1));
            System.out.println(s1.equals(d1));
            System.out.println(s1.equals(null));
    
        }
    }
    
    class Student{
        private String name;
        private int age;
    
        public Student() {
        }
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        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;
        }
    
        @Override
        public boolean equals(Object obj) {
            // 我认为两个对象的成员变量的值,一模一样,就认为两个对象一样。
            // 非空判断;
            if(obj == null){
                System.out.println("obj对象为空!,不能参与比较!");
                return false;
            }
            //从效率方面考虑:
            if (this == obj){
                System.out.println("两个对象是同一个对象!,无需比较!");
                return true;
            }
            // 从健壮性方面考虑:
            // 判断他传过来的这个引用(对象)是不是该类的引用(对象)
            // instancedof()判断一个对象(引用)是不是该类型的一个引用
            if (!(obj instanceof Student)){
                System.out.println("obj不是Student类型的对象!,不能进行比较!");
                return false;
            }
    
            Student transform_obj =  ((Student)(obj)); //向下转型
    
            if (transform_obj.age == this.age && transform_obj.name.equals(this.name)){
                System.out.println("这两个对象同属于一个类,且成员属性的值都相同!");
                return true;
            }
    
            System.out.println("这两个对象同属于一个类,但成员属性的值不相同!");
            return false;
        }
    }
    
    class Dog{
        private String name;
        private int age;
    
        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;
        }
    
    
        public Dog() {
        }
    
        public Dog(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    -----------------------------------------------
    输出:
    这两个对象同属于一个类,且成员属性的值都相同!
    true
    这两个对象同属于一个类,但成员属性的值不相同!
    false
    两个对象是同一个对象!,无需比较!
    true
    obj不是Student类型的对象!,不能进行比较!
    false
    obj对象为空!,不能参与比较!
    false
    
    • 注意:下面两者的效果是一样的!
    if (!(obj.getClass() == this.getClass())){
                System.out.println("obj不是Student类型的对象!,不能进行比较!");
                return false;
            }
    ------------------------------------------------
    if (!(obj instanceof Student)){
                System.out.println("obj不是Student类型的对象!,不能进行比较!");
                return false;
            }
    
    原因:.java 文件被 javac 编译了以后会生成 .java 文件中所写的所有类的 .class 文件,然后,在主函数运行的过程中,如果用到哪个类的 .class 文件,就要将 .class 文件加载入内存从而生成它的文件字节码对象。(只加载一次!, 所以该对象在JVM的方法区中应该只存在一个)。 且 obj.getClass() 方法返回就是这个obj对应类的文件字节码对象。所以可以利用这个方法判断两个对象是否属于同一个类,进而再进行向下转型!
Object类的clone() 方法(浅克隆):
  • 概念:创建并返回此对象的副本(也是一个对象)。
  • 使用方法:
    示例:
public class MyTest {
    public static void main(String[] args) throws CloneNotSupportedException {
       // protected Object clone () 创建并返回此对象的副本。
        //clone()方法是受保护的 你让子类重写一些,把方法的修饰符改为public 重写逻辑还是使用父类的
        Dog dog = new Dog("旺财", 3);
        System.out.println(dog.name);
        System.out.println(dog.age);
        Dog dog2 = (Dog) dog.clone();//这里需要进行向下转换!(dog.clone()返回的是java.lang.Object对象!)
        dog2.name="小白";
        System.out.println(dog2.name);
        System.out.println(dog2.age);


       // CloneNotSupportedException

    }
}
//Cloneable 这个借口什么方法都没实现,只是一个空壳子!
//像这种接口中没有任何的抽象方法的接口,我们称之为标记接口
//标记接口,该该类打一个标记,是告诉JVM我要完成某种操作
class Dog extends Object implements Cloneable{
    String name;
    int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override //这里重写的应该是超类(Object)的clone()方法!
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
----------------------
输出:
旺财
3
小白
3
  • 对象浅克隆要注意的细节:
    1. 如果一个对象需要调用clone的方法克隆,那么该对象所属的类必须要实现Cloneable接口。
    2. Cloneable接口只不过是一个标识接口而已,没有任何方法。
    3. 对象的浅克隆就是克隆一个对象的时候,如果被克隆的对象中维护了另外一个类的对象,这时候只是克隆另外一个对象的地址,而没有把另外一个对象也克隆一份。
    4. 对象的浅克隆也不会调用到构造方法的。

  • 问题:为啥protected的clone方法不能访问?(这个参考老师上课给的权限修饰符的作用范围是有歧义的,解释不通!)

深浅拷贝的问题:
  • 从一个代码的示例来引出深浅拷贝的问题:
    示例:
public class MyTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        //创建一个狗粮类对象
        DogFood dogFood = new DogFood("双汇王中王");
        Dog dog = new Dog("小白", 23, dogFood);
        dog.dogFood.name="金锣火腿肠";
        //克隆狗对象
        Dog dog2= (Dog) dog.clone();
         dog2.name="旺财";
         dog2.age=4;
         dog2.dogFood.name = "泡面搭档火腿肠";

        System.out.println("--------------------");
        System.out.println(dog.name);
        System.out.println(dog.age);
        System.out.println(dog.dogFood.name); // 金锣火腿肠 or 泡面搭档火腿肠 ?
        System.out.println("--------------------");
        System.out.println(dog2.name);
        System.out.println(dog2.age);
        System.out.println(dog2.dogFood.name); // 泡面搭档火腿肠


    }
}



class Dog implements Cloneable{
    String name;
    int age;
    //在这个Dog类里面维护着另一个类的对象
    DogFood dogFood;

    public Dog(String name, int age, DogFood dogFood) {
        this.name = name;
        this.age = age;
        this.dogFood = dogFood;
    }

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class DogFood{
    String name;

    public DogFood(String name) {
        this.name = name;
    }
}
------------------------
输出:
--------------------
小白
23
泡面搭档火腿肠
--------------------
旺财
4
泡面搭档火腿肠
  • 结论:从上述的输出结果中可以看到,dog和dog2的dogFood指向的应该是同一个对象!,所以clone()是一个浅克隆!
  • 上述代码执行过程中的内存图示:(可以更清楚地看到上面的结论!)
    在这里插入图片描述
  • 解决办法:
    a. 那如果遇到这种问题,该如何解决呢?就得使用深克隆的办法。(因为浅克隆不会克隆嵌套的子对象!)
    b. 对象的深克隆(后面讲):采用IO流来实现,使用 ObjectOutputStream 将对象写入文件中,然后再用ObjectInputStream读取回来便可得到另一个对象,这个对象相当于是克隆得到的一样(同时也会构造它的嵌套子对象,所以称为深克隆(拷贝))
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值