第十五章 Object类与对象类型判断
提纲
- 15.1 Object类
- 15.1.1 什么是Object类
- 15.1.2 为什么没有extends Object关键字
- 15.1.3 详解toString()和equals()方法
- 15.1.4 clone()方法
- 15.2 对象类型判断(instanceof)
- 15.2.1 为什么要判断对象类型
- 15.2.2 语法
- 15.2.3 举例
- 15.2.4 进阶题
15.1 Object类
-
15.1.1 什么是Object类:在Java中,所有的类都直接或者间接的继承了java.lang.Object类。Object是一个比较特殊的类,它是所有类的父类,是Java类层中的最高层。当创建一个类时,总是在继承,除非这个类已经指定要从其他类继承,否则它就是从java.lang.Object类继承而来。
-
15.1.2 为什么没有extends Object关键字呢?
//创建一个Book类 public class Book { } 等价于 public class Book extends Object{ } //因为每个类都直接或者间接的继承了Object类,所以省略了关键字
-
15.1.3 详解toString()和equals()方法
-
toString()方法
-
含义:toString()方法的功能是将一个对象返回为字符串形式,它会返回一个String实例。实际应用中通常重写toString()方法。
-
举例:
//这个是Object中的toString()方法 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } //将2中的Book调用toString()方法并打印 public class Test { public static void main(String[] args) { Book book = new Book(); System.out.println(book); System.out.println(book.toString()); } } //执行结果 com.zxjy.day1010.Book@1bc4459 com.zxjy.day1010.Book@1bc4459
结论:当打印这个对象时会默认调用Object类中的toString()方法。
3. 重写toString()方法//创建一个Book类,类中有属性,并且重写toString()方法 public class Book{ String name; @Override public String toString() { return "这是一本叫:"+name+"的书。"; } } //创建测试类,打印对象 public class Test { public static void main(String[] args) { Book book1 = new Book(); book1.name = "星辰变"; System.out.println(book1.toString()); Book book2 = new Book(); book2.name = "诛仙"; System.out.println(book2.toString()); } } //执行结果 这是一本叫:星辰变的书。 这是一本叫:诛仙的书。
结论:当重写toString()方法后,调用对象toString()方法时会调用重写的toString()方法。
-
-
equals()方法与==的区别
-
创建一个字符串的两种形式
//形式1: String str1 = "abc"; String str2 = "abc"; //形式2: String str3 = new String("abc"); String str4 = new String("abc");
区别(见图解:创建一个字符串的两种形式.jpg):1.如果“abc”这个字符串在内存中不存在,会在内存中创建一个创建一个String对象(“abc”),即new一个String对象。然后str1指向这个内存地址,无论以后用这种方式创建多少个值为”abc”的字符串对象,始终只有一个内存地址被分配,之后的都是String的拷贝,Java中称为“字符串驻留”。形式2.不论内容是否相等,都会在内存中开辟一个新的空间,来存放这个字符串。
-
给同一个字符串引用赋值
String str = "abc"; str = "def";
过程解释(见图解:创建一个字符串的两种形式.jpg):首先创建了一个str字符串引用,对象为“abc”,当将“def”再次赋值给str引用时,str引用不再指向“abc”对象,而是重新指向“def”对象,原来的对象则没有引用指向,会在不确定的时候被垃圾回收机制回收掉。
-
两种比较的区别
String str1 = "abc"; String str2 = "abc"; String str3 = new String("abc"); String str4 = new String("abc"); System.out.println("str1==str2的比较结果:"+(str1 == str2)); System.out.println("str1.equals(str2))的比较结果:"+(str1.equals(str2))); System.out.println("str3==str4的比较结果:"+(str3 == str4)); System.out.println("str3.equals(str4))的比较结果:"+(str3.equals(str4))); //执行结果 str1==str2的比较结果:false str1.equals(str2))的比较结果:true str3==str4的比较结果:true str3.equals(str4))的比较结果:true
结论:equals()比较的是内容是否相等,==比较的是内存地址是否相等,即是否是同一个对象。
-
-
-
15.1.4 clone()方法
- clone()方法的作用:clone()可以产生一个相同的类并且返回给调用者。
- clone()是如何工作的:Object将clone()作为一个本地方法来实现,这意味着它的代码存放在本地的库中。当代码执行的时候,将会检查调用对象的类(或者父类)是否实现了java.lang.Cloneable接口(Object类不实现Cloneable)。如果没有实现这个接口,clone()将会抛出一个检查异常()——java.lang.CloneNotSupportedException,如果实现了这个接口,clone()会创建一个新的对象,并将原来对象的内容复制到新对象,最后返回这个新对象的引用。
- 怎样调用clone()来克隆一个对象:用想要克隆的对象来调用clone(),将返回的对象从Object类转换到克隆的对象所属的类,赋给对象的引用。
-
本类中克隆对象
public class CloneDemo implements Cloneable{ String name; int age; @Override public String toString() { return "CloneDemo [age=" + age + ", name=" + name + "]"; } public static void main(String[] args) throws CloneNotSupportedException { CloneDemo cd = new CloneDemo(); cd.name = "尼古拉斯.赵四"; cd.age = 100; System.out.println("cd:"+cd); CloneDemo cd2 = (CloneDemo) cd.clone(); System.out.println("cd2:"+cd2); } } //执行结果 cd:CloneDemo [age=100, name=尼古拉斯.赵四] cd2:CloneDemo [age=100, name=尼古拉斯.赵四]
-
从别的类中克隆对象:上面的例子中,调用clone()的代码是位于被克隆的类(即CloneDemo类)里面的,所以就不需要覆盖clone()了。但是,如果调用别的类中的clone(),就需要覆盖clone()了。否则,将会看到“clone在Object中是被保护的”提示,因为clone()在Object中的权限是protected。(译者注:protected权限的成员在不同的包中,只有子类对象可以访问。代码清单3的CloneDemo类和代码清单4的Data类是Object类的子类,所以可以调用clone(),但是代码清单4中的CloneDemo类就不能直接调用Data父类的clone())。代码清单4在代码清单3上稍作修改来演示覆盖clone()。
//创建CloneDemo类 public class CloneDemo{ String name; int age; public CloneDemo() { } public CloneDemo(String name, int age) { this.name = name; this.age = age; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "CloneDemo [age=" + age + ", name=" + name + "]"; } } //测试类 public class Test implements Cloneable{ public static void main(String[] args) throws CloneNotSupportedException { CloneDemo cd = new CloneDemo(); cd.name = "尼古拉斯.凯奇"; cd.age = 888; System.out.println("cd:"+cd); CloneDemo cd2 = (CloneDemo) cd.clone(); System.out.println("cd2:"+cd2); } } //执行结果 cd:CloneDemo [age=888, name=尼古拉斯.凯奇] cd2:CloneDemo [age=888, name=尼古拉斯.凯奇]
-
克隆基本类型数组和对象数组
public class Test{ public static void main(String[] args) throws CloneNotSupportedException { //克隆基本类型数组 double[] temps = {98.6, 32.0, 100.0, 212.0, 53.5}; System.out.print("temps:"); for (int i = 0; i < temps.length; i++) { System.out.print("temps:"+temps[i]+" "); } System.out.println(); double[] temps2 = temps.clone(); System.out.print("temps2:"); for (int i = 0; i < temps2.length; i++) { System.out.print("temps2:"+temps2[i]+" "); } System.out.println(); //克隆对象数组 CloneDemo[] cloneDemos = {new CloneDemo("AAAA", 18), new CloneDemo("BBBB", 10)}; System.out.print("cloneDemos:"); for (int i = 0; i < cloneDemos.length; i++) { System.out.print("cloneDemos:"+cloneDemos[i]+" "); } System.out.println(); CloneDemo[] cloneDemos2 = cloneDemos.clone(); System.out.print("cloneDemos2:"); for (int i = 0; i < cloneDemos2.length; i++) { System.out.print("cloneDemos2:"+cloneDemos2[i]+" "); } } } //执行结果 temps:temps:98.6 temps:32.0 temps:100.0 temps:212.0 temps:53.5 temps2:temps2:98.6 temps2:32.0 temps2:100.0 temps2:212.0 temps2:53.5 cloneDemos:cloneDemos:CloneDemo [age=18, name=AAAA] cloneDemos:CloneDemo [age=10, name=BBBB] cloneDemos2:cloneDemos2:CloneDemo [age=18, name=AAAA] cloneDemos2:CloneDemo [age=10, name=BBBB]
-
15.2 对象类型判断(instanceof)
-
15.2.1 为什么要判断对象类型:当向下转型时,如果父类的引用不是子类的实例,即两个对象之间不存在继承关系时就会出现异常。所以在转型时要事先判断父类是否为子类对象的实例。
-
15.2.2 语法:某类对象的引用 instanceof 某个类
-
15.2.3 举例:
//创建动物类 public class Animal { } //创建猫类继承动物类并重写toString()方法 public class Cat extends Animal{ @Override public String toString() { return "这是一只猫"; } } //创建狗类继承动物类并重写toString()方法 public class Dog extends Animal{ @Override public String toString() { return "这是一条狗"; } } //创建测试类测试 public class Test { public static void main(String[] args) { Animal animal = new Cat(); //判断这个父类animal引用对象是否为子类Cat的对象 if (animal instanceof Cat) { Cat cat = (Cat) animal; System.out.println(cat); } if (animal instanceof Dog) { Dog dog = (Dog) animal; System.out.println(dog); } } } //执行结果 这是一只猫
-
15.2.4 进阶题:有动物类Animal,动物类有子类猫Cat和狗Dog。猫中有方法抓老鼠catchMice(),狗中有方法看门lookDoor()。使用随机数Random类,随机五次,如果随机出1,则创建一只猫,并存在对象数组中,如果随机出2,则创建一条狗,也存在同一个对象数组中。最后打印出对象数组的所有对象,并要求调用猫类和狗类中自己的方法。提示:1.每次随机的动物是未知的,所以数组中有猫还是狗是未知的。2.如果要调用子类中的方法必须创建子类的实例才可以,即向下转型,父类的引用指向子类的实例是无法调用子类自己的方法的。
//创建动物类 public class Animal { } //创建猫类继承动物类并有catchMice()方法 public class Cat extends Animal{ public void catchMice(){ System.out.println("抓老鼠"); } } //创建狗类继承动物类并有lookDoor()方法 public class Dog extends Animal{ public void lookDoor(){ System.out.println("狗看门"); } } //创建测试类测试 public class Test { public static void main(String[] args) { Random random = new Random(); int num; Animal[] animals = new Animal[5]; for (int i = 0; i < 5; i++) { num = random.nextInt(2)+1; if (num == 1) { animals[i] = new Cat(); System.out.println("发现了一只猫!"); } else if (num == 2) { animals[i] = new Dog(); System.out.println("发现了一条狗!"); } } for (int i = 0; i < animals.length; i++) { if (animals[i] instanceof Cat) { Cat cat = (Cat) animals[i]; cat.catchMice(); } if (animals[i] instanceof Dog) { Dog dog = (Dog) animals[i]; dog.lookDoor(); } } } } //执行结果 发现了一只猫! 发现了一只猫! 发现了一条狗! 发现了一条狗! 发现了一条狗! 抓老鼠 抓老鼠 狗看门 狗看门 狗看门
nstanceof Cat) {
Cat cat = (Cat) animals[i];
cat.catchMice();
}
if (animals[i] instanceof Dog) {
Dog dog = (Dog) animals[i];
dog.lookDoor();
}
}
}
}
//执行结果
发现了一只猫!
发现了一只猫!
发现了一条狗!
发现了一条狗!
发现了一条狗!
抓老鼠
抓老鼠
狗看门
狗看门
狗看门