Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 447|回复: 0

[Java基础知识]Java的终极父类Object(一)

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-9-30 17:38:19 | 显示全部楼层 |阅读模式
    java的一些特性会让初学者感到困惑,但在有经验的开发者眼中,却是合情合理的。例如,新手可能不会理解Object类。这篇文章分成三个部分讲跟Object类及其方法有关的问题。
    上帝类
    问:什么是Object类?
    答:Object类存储在java.lang包中,是所有java类(Object类除外)的终极父类。当然,数组也继承了Object类。然而,接口是不继承Object类的,原因在这里指出:Section 9.6.3.4 of the Java Language Specification:“Object类不作为接口的父类”。
    Object类中声明了以下函数,我会在下文中作详细说明。  protected Object clone()
      boolean equals(Object obj)
      protected void finalize()
      Class<?> getClass()
      int hashCode()
      void notify()
      void notifyAll()
      String toString()
      void wait()
      void wait(long timeout)
      void wait(long timeout, int nanos)
      
       
       

         
       

         
       
      
        java的任何类都继承了这些函数,并且可以覆盖不被final修饰的函数。例如,没有final修饰的toString()函数可以被覆盖,但是final wait()函数就不行。 问:可以声明要“继承Object类”吗?
    答:可以。在代码中明确地写出继承Object类没有语法错误。参考代码清单1。 代码清单1:明确的继承Object类
    1. import java.lang.Object;

    2. public class Employee extends Object {
    3.     private String name;

    4.     public Employee(String name) {
    5.         this.name = name;
    6.     }

    7.     public String getName() {
    8.         return name;
    9.     }

    10.     public static void main(String[] args) {
    11.         Employee emp = new Employee("John Doe");
    12.         System.out.println(emp.getName());
    13.     }
    14. }
    复制代码
    你可以试着编译代码1(javac Employee.java),然后运行Employee.class(java Employee),可以看到John Doe 成功的输出了。    因为编译器会自动引入java.lang包中的类型,即 import java.lang.Object; 没必要声明出来。Java也没有强制声明“继承Object类”。如果这样的话,就不能继承除Object类之外别的类了,因为java不支持多继承。然而,即使不声明出来,也会默认继承了Object类,参考代码清单2。 代码清单2:默认继承Object类
    1. public class Employee
    2. {
    3.    private String name;

    4.    public Employee(String name)
    5.    {
    6.       this.name = name;
    7.    }

    8.    public String getName()
    9.    {
    10.       return name;
    11.    }

    12.    public static void main(String[] args)
    13.    {
    14.       Employee emp = new Employee("John Doe");
    15.       System.out.println(emp.getName());
    16.    }
    17. }
    复制代码
      就像代码清单1一样,这里的Employee类继承了Object,所以可以使用它的函数。 克隆Object类
    问:clone()函数是用来做什么的? 答:clone()可以产生一个相同的类并且返回给调用者。 问:clone()是如何工作的?
    答:Object将clone()作为一个本地方法来实现,这意味着它的代码存放在本地的库中。当代码执行的时候,将会检查调用对象的类(或者父类)是否实现了java.lang.Cloneable接口(Object类不实现Cloneable)。如果没有实现这个接口,clone()将会抛出一个检查异常()――java.lang.CloneNotSupportedException,如果实现了这个接口,clone()会创建一个新的对象,并将原来对象的内容复制到新对象,最后返回这个新对象的引用。 问:怎样调用clone()来克隆一个对象?
    答:用想要克隆的对象来调用clone(),将返回的对象从Object类转换到克隆的对象所属的类,赋给对象的引用。这里用代码清单3作一个示例。 代码清单3:克隆一个对象
      public class CloneDemo implements Cloneable {
        int x;
        public static void main(String[] args) throws CloneNotSupportedException {
          CloneDemo cd = new CloneDemo();
          cd.x = 5;
         System.out.printf("cd.x = %d%n", cd.x);
         CloneDemo cd2 = (CloneDemo) cd.clone();
         System.out.printf("cd2.x = %d%n", cd2.x);
        }
       }
    代码清单3声明了一个继承Cloneable接口的CloneDemo类。这个接口必须实现,否则,调用Object的clone()时将会导致抛出异常CloneNotSupportedException。 CloneDemo声明了一个int型变量x和主函数main()来演示这个类。其中,main()声明可能会向外抛出CloneNotSupportedException异常。 Main()先实例化CloneDemo并将x的值初始化为5。然后输出x的值,紧接着调用clone() ,将克隆的对象传回CloneDemo。最后,输出了克隆的x的值。 编译代码清单3(javac CloneDemo.java)然后运行(java CloneDemo)。你可以看到以下运行结果:
      cd.x = 5
      cd2.x = 5

    问:什么情况下需要覆盖clone()方法呢?
    答:上面的例子中,调用clone()的代码是位于被克隆的类(即CloneDemo类)里面的,所以就不需要覆盖clone()了。但是,如果调用别的类中的clone(),就需要覆盖clone()了。否则,将会看到“clone在Object中是被保护的”提示,因为clone()在Object中的权限是protected。(译者注:protected权限的成员在不同的包中,只有子类对象可以访问。代码清单3的CloneDemo类和代码清单4的Data类是Object类的子类,所以可以调用clone(),但是代码清单4中的CloneDemo类就不能直接调用Data父类的clone())。代码清单4在代码清单3上稍作修改来演示覆盖clone()。 代码清单4:从别的类中克隆对象
    1. class Data implements Cloneable {
    2.     int x;

    3.     @Override
    4.     public Object clone() throws CloneNotSupportedException {
    5.         return super.clone();
    6.     }
    7. }

    8. public class CloneDemo {
    9.     public static void main(String[] args) throws CloneNotSupportedException {
    10.         Data data = new Data();
    11.         data.x = 5;
    12.         System.out.printf("data.x = %d%n", data.x);
    13.         Data data2 = (Data) data.clone();
    14.         System.out.printf("data2.x = %d%n", data2.x);
    15.     }
    16. }
    复制代码
    代码清单4声明了一个待克隆的Data类。这个类实现了Cloneable接口来防止调用clone()的时候抛出异常CloneNotSupportedException,声明了int型变量x,覆盖了clone()方法。这个方法通过执行super.clone()来调用父类的clone()(这个例子中是Object的)。通过覆盖来避免抛出CloneNotSupportedException异常。 代码清单4也声明了一个CloneDemo类来实例化Data,并将其初始化,输出示例的值。然后克隆Data的对象,同样将其值输出。 编译代码清单4(javac CloneDemo.java)并运行(java CloneDemo),你将看到以下运行结果:
      data.x = 5
      data2.x = 5

    问:什么是浅克隆?
    A:浅克隆(也叫做浅拷贝)仅仅复制了这个对象本身的成员变量,该对象如果引用了其他对象的话,也不对其复制。代码清单3和代码清单4演示了浅克隆。新的对象中的数据包含在了这个对象本身中,不涉及对别的对象的引用。 如果一个对象中的所有成员变量都是原始类型,并且其引用了的对象都是不可改变的(大多情况下都是)时,使用浅克隆效果很好!但是,如果其引用了可变的对象,那么这些变化将会影响到该对象和它克隆出的所有对象!代码清单5给出一个示例。 代码清单5:演示浅克隆在复制引用了可变对象的对象时存在的问题
    1. class Employee implements Cloneable {
    2.     private String name;
    3.     private int age;
    4.     private Address address;

    5.     Employee(String name, int age, Address address) {
    6.         this.name = name;
    7.         this.age = age;
    8.         this.address = address;
    9.     }

    10.     @Override
    11.     public Object clone() throws CloneNotSupportedException {
    12.         return super.clone();
    13.     }

    14.     Address getAddress() {
    15.         return address;
    16.     }

    17.     String getName() {
    18.         return name;
    19.     }

    20.     int getAge() {
    21.         return age;
    22.     }
    23. }

    24. class Address {
    25.     private String city;

    26.     Address(String city) {
    27.         this.city = city;
    28.     }

    29.     String getCity() {
    30.         return city;
    31.     }

    32.     void setCity(String city) {
    33.         this.city = city;
    34.     }
    35. }

    36. public class CloneDemo {
    37.     public static void main(String[] args) throws CloneNotSupportedException {
    38.         Employee e = new Employee("John Doe", 49, new Address("Denver"));
    39.         System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
    40.                           e.getAddress().getCity());
    41.         Employee e2 = (Employee) e.clone();
    42.         System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
    43.                           e2.getAddress().getCity());
    44.         e.getAddress().setCity("Chicago");
    45.         System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
    46.                           e.getAddress().getCity());
    47.         System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
    48.                           e2.getAddress().getCity());
    49.     }
    50. }
    复制代码
    代码清单5给出了Employee、Address和CloneDemo类。Employee声明了name、age、address成员变量,是可以被克隆的类;Address声明了一个城市的地址并且其值是可变的。CloneDemo类驱动这个程序。   CloneDemo的主函数main()创建了一个Employee对象并且对其进行克隆,然后,改变了原来的Employee对象中address值城市的名字。因为原来的Employee对象和其克隆出来的对象引用了相同的Address对象,所以两者都会提现出这个变化。 编译 (javac CloneDemo.java) 并运行 (java CloneDemo)代码清单5,你将会看到如下输出结果:
      John Doe: 49: Denver
      John Doe: 49: Denver
      John Doe: 49: Chicago
      John Doe: 49: Chicago

    问:什么是深克隆?
    答:深克隆(也叫做深复制)会复制这个对象和它所引用的对象的成员变量,如果该对象引用了其他对象,深克隆也会对其复制。例如,代码清单6在代码清单5上稍作修改演示深克隆。同时,这段代码也演示了协变返回类型和一种更为灵活的克隆方式。 代码清单6:深克隆成员变量address
    1. class Employee implements Cloneable
    2. {
    3.    private String name;
    4.    private int age;
    5.    private Address address;

    6.    Employee(String name, int age, Address address)
    7.    {
    8.       this.name = name;
    9.       this.age = age;
    10.       this.address = address;
    11.    }

    12.    @Override
    13.    public Employee clone() throws CloneNotSupportedException
    14.    {
    15.       Employee e = (Employee) super.clone();
    16.       e.address = address.clone();
    17.       return e;
    18.    }

    19.    Address getAddress()
    20.    {
    21.       return address;
    22.    }

    23.    String getName()
    24.    {
    25.       return name;
    26.    }

    27.    int getAge()
    28.    {
    29.       return age;
    30.    }
    31. }

    32. class Address
    33. {
    34.    private String city;

    35.    Address(String city)
    36.    {
    37.       this.city = city;
    38.    }

    39.    @Override
    40.    public Address clone()
    41.    {
    42.       return new Address(new String(city));
    43.    }

    44.    String getCity()
    45.    {
    46.       return city;
    47.    }

    48.    void setCity(String city)
    49.    {
    50.       this.city = city;
    51.    }
    52. }

    53. public class CloneDemo
    54. {
    55.    public static void main(String[] args) throws CloneNotSupportedException
    56.    {
    57.       Employee e = new Employee("John Doe", 49, new Address("Denver"));
    58.       System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
    59.                         e.getAddress().getCity());
    60.       Employee e2 = (Employee) e.clone();
    61.       System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
    62.                         e2.getAddress().getCity());
    63.       e.getAddress().setCity("Chicago");
    64.       System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
    65.                         e.getAddress().getCity());
    66.       System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
    67.                         e2.getAddress().getCity());
    68.    }
    69. }
    复制代码
    Java支持协变返回类型,代码清单6利用这个特性,在Employee类中覆盖父类clone()方法时,将返回类型从Object类的对象改为Employee类型。这样做的好处就是,Employee类之外的代码可以不用将这个类转换为Employee类型就可以对其进行复制。   Employee类的clone()方法首先调用super().clone(),对name,age,address这些成员变量进行浅克隆。然后,调用成员变量Address对象的clone()来对其引用Address对象进行克隆。 从Address类中的clone()函数可以看出,这个clone()和我们之前写的clone()有些不同:
       Address类没有实现Cloneable接口。因为只有在Object类中的clone()被调用时才需要实现,而Address是不会调用clone()的,所以没有实现Cloneable()的必要。
    这个clone()函数没有声明抛出CloneNotSupportedException。这个检查异常只可能在调用Object类clone()的时候抛出。clone()是不会被调用的,因此这个异常也就没有被处理或者传回调用处的必要了。
    Object类的clone()没有被调用(这里没有调用super.clone())。因为这不是对Address的对象进行浅克隆――只是一个成员变量复制而已。
    为了克隆Address的对象,需要创建一个新的Address对象并对其成员进行初始化操作。最后将新创建的Address对象返回。 编译(javac CloneDemo.java)代码清单6并且运行这个程序,你将会看到如下输出结果(java CloneDemo):
      John Doe: 49: Denver
      John Doe: 49: Denver
      John Doe: 49: Chicago
      John Doe: 49: Denver

    Q:如何克隆一个数组?
    A:对数组类型进行浅克隆可以利用clone()方法。对数组使用clone()时,不必将clone()的返回值类型转换为数组类型,代码清单7示范了数组克隆。 代码清单7:对两个数组进行浅克隆
    1. class City {
    2.     private String name;

    3.     City(String name) {
    4.         this.name = name;
    5.     }

    6.     String getName() {
    7.         return name;
    8.     }

    9.     void setName(String name) {
    10.         this.name = name;
    11.     }
    12. }

    13. public class CloneDemo {
    14.     public static void main(String[] args) {
    15.         double[] temps = { 98.6, 32.0, 100.0, 212.0, 53.5 };
    16.         for (double temp : temps)
    17.             System.out.printf("%.1f ", temp);
    18.         System.out.println();
    19.         double[] temps2 = temps.clone();
    20.         for (double temp : temps2)
    21.             System.out.printf("%.1f ", temp);
    22.         System.out.println();

    23.         System.out.println();

    24.         City[] cities = { new City("Denver"), new City("Chicago") };
    25.         for (City city : cities)
    26.             System.out.printf("%s ", city.getName());
    27.         System.out.println();
    28.         City[] cities2 = cities.clone();
    29.         for (City city : cities2)
    30.             System.out.printf("%s ", city.getName());
    31.         System.out.println();

    32.         cities[0].setName("Dallas");
    33.         for (City city : cities2)
    34.             System.out.printf("%s ", city.getName());
    35.         System.out.println();
    36.     }
    37. }
    复制代码
    代码清单7声明了一个City类存储名字,还有一些有关城市的数据(比如人口)。CloneDemo类提供了主函数main()来演示数组克隆。
        main()函数首先声明了一个双精度浮点型数组来表示温度。在输出数组的值之后,克隆这个数组――注意没有运算符。之后,输出克隆的完全相同的数据。    紧接着,main()声明了一个City对象的数组,输出城市的名字,克隆这个数组,输出克隆的这个数组中城市的名字。为了证明浅克隆完成(比如,这两个数组引用了相同的City对象),main()最后改变了原来的数组中第一个城市的名字,输出第二个数组中所有城市的名字。我们马上就可以看到,第二个数组中的名字也改变了。 编译 (javac CloneDemo.java)并运行 (java CloneDemo)代码清单7,你将会看到如下输出结果:
      98.6 32.0 100.0 212.0 53.5
      98.6 32.0 100.0 212.0 53.5

      Denver Chicago
      Denver Chicago
      Dallas Chicago

      
      
       
       

         
       

         
       
      
    复制代码
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-20 19:12 , Processed in 0.398170 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表