本节索引
6.10 实例分析: 宠物商店
6.11 Object类
6.12 包装类
6.13 匿名内部类
6.14 本章要点
6.10 实例分析: 宠物商店
page 219
6.11 Object类
1. Java中, 类都是以继承的关系存在的。java中所有的类都有一个公共的父类Object,一个类只要没有明显地继承另外一个类,则它默认的就是继承自Object类。以下两种声明类的含义都是一样的:
class Person{}
class Person extends Object{}
2. Object类的主要方法
既然Object类是所有类的父类,那么Object类中所定义的方法也可以被公用和覆写。 一个比较良好的类,一般要覆写Object类中的以下三个方法: toString()、equals(Object obj)、hashCode()。
序号 | 方法名称 | 类型 | 描述 |
1 | public Object() | 构造方法 | 构造方法 |
2 | public boolean equals(Object obj) | 普通 | 对象比较 |
3 | public int hashCode() | 普通 | 取得对象的Hash码 |
4 | public String toString() | 普通 | 对象打印时调用 |
3. toString()方法: 对象打印时自动被调用
class Person{
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
public String toString(){ //覆写Object的toString方法,在打印Person对象时,自动被调用。修饰符一定不能被父类Object有更严格的访问限制,必须是public
return "Person infomation: name " + this.name + ", age " + this.age;
}
}
public class ObjectDemo01{
public static void main(String args[]){
Person per1 = new Person("forfan06", 28);
System.out.println(per1); //调用的始终是被子类所覆写过的方法toString
}
}
4. equals()方法 public Boolean equals(Object obj)
equals()方法的功能就是进行对象的比价。String类也是Object类的子类,所以在String类中已经覆写了此方法。
如果现在一个类需要实现对象的比较操作,则直接在类中覆写此方法即可。
*******Object类提供的equals()方法默认是比较地址的,默认是按地址进行比较,并不能对内容进行比较
范例: 对象的比较操作
class Person{
private String name;
private int age;
public Person(String name, int age){
this.setName(name);
this.setAge(age);
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
public boolean equals(Object obj){ //覆写Object类中的equals()方法
if (this == obj){ //如果两个对象的地址相等,则肯定是同一个对象
return true;
}
if (!(obj instanceof Person)){ //判断obj是否是Person类的实例化对象,如果不属于,则肯定不是同一个对象
return false;
}
Person per = (Person) obj; //将传进来的对象向下转型。问题,前面讲过,要向下转型,首先必须要有向上转型的操作。此时向上转型操作隐藏在哪里??实际上向上转型的操作隐藏处在于此方法被调用时,是将obj已经自动向上转型为Object类对象了!!!!
if ((this.getName()).equals(per.getName()) && (this.getAge() == per.getAge())){
return true; //将属性进行逐个比较,看是否相等, 对象内容相等,则返回ture;否则返回false
}else {
return false;
}
}
public String toString(){ //覆写Object的toString方法,在打印Person对象时,自动被调用。
return "Person infomation: name " + this.name + ", age " + this.age;
}
}
public class ObjectDemo01{
public static void main(String args[]){
Person per1 = new Person("forfan06", 28);
Person per2 = new Person("forfan07", 17);
Person per3 = new Person("forfan06", 28);
System.out.println(per1.equals(per2)?"是同一个对象":"不是同一个对象");
System.out.println(per1.equals(per3)?"是同一个对象":"不是同一个对象");
System.out.println(per1.equals("forfan06")?"是同一个对象":"不是同一个对象");
//System.out.println(per1); //调用的始终是被子类所覆写过的方法toString
}
}
在Person类中覆写了equals()方法,首先判断传递进来的对象是否与当前对象的地址是否相等?如果相等,则表示是同一个对象,因为在equals()方法处传递的参数是Object类型,所以任何对象都可以接受,这样在对象进行向下转型前必须判断进来的对象是否是Person类的实例,如果不是,则直接返回false;如果是,则需要进行对象内容的比较,将对象的各个属性以此进行判断。 这也是判断两个对象是否相等的一般思路!!!
5. Object类可以接收任意引用类型的对象
Object类是所有类的父类,则所有类的对象都可以用Object接收,但是Object不管可以接受对象,还可以接收任意的引用数据类型(类、接口、数组)!!!!
!!!!Object可以接收所有的引用类型的对象!!!!
实例1: 使用Object接收接口实例
interface A{ //定义一个接口
public String getInfo();
}
class B implements A{ //类B实现接口A
public String getInfo(){ //覆写接口A中的抽象方法
return "Hello World!!!";
}
}
public class ObjectDemo02{
public static void main(String args[]){
A a = new B(); //通过对象的多态性(向上转型),创建接口A的实例化对象a
Object obj = a; //因为Object类是所有的类的父类,所以可以进行向上转型操作
A aa = (A)obj; //向下转型,前提是已经发生过向上转型操作 Object obj = a;
System.out.println(aa.getInfo()); //对象的多态性,调用的始终是被子类所覆写过的方法!!!
}
}
实例2: 使用Object接收数组
public class ObjectDemo05{
public static void main(String args[]){
int temp[] = {1, 3, 5, 7, 9}; //定义一个数组
Object obj = temp; //使用Object接收数足
print(obj);
}
public static void print(Object obj){
if (obj instanceof int[]){ //判断该对象是否是整型数组
int x[] = (int[])obj; //如果对象是整型数组,将对象向下转型(向上转型操作已经在此方法调用处用过)
for(int i = 0; i < x.length; i++){ //输出数组
System.out.println(x[i] + "\t");
}
}else{ //若该对象不是整型数组
System.out.println("内部错误");
System.exit(1);
}
}
}
- Object类是所有类的父类,只要是引用数据类型的对象,都可以使用Object进行接收
- 对象在进行向下转型之前,一定要先发生向上转型,要使用instanceof关键字判断。
- toString(),对象打印时调用
- equals(),对象比较时使用
- String类也是Object类的子类。
******因为Object类可以接收任意的引用数据类型,所以在很多的类库设计上都采用Object作为方法的参数,这样操作会比较方便*******
6.12 包装类
问题的提出,java中提倡“一切皆对象”的思想,但是java中数据类型又分为基本数据类型和引用数据类型。那么,基本数据类型又怎么能称为对象呢??
此时就需要对基本数据类型进行包装,将8种基本数据类型变为一个类的形式。这就是包装类的作用!!!
序号 | 基本数据类型 | 包装类 |
1 | int | Integer |
2 | char | Character |
3 | short | Short |
4 | long | Long |
5 | float | Float |
6 | double | Double |
7 | boolean | Boolean |
8 | byte | Byte |
(1) 包装类Integer、Short、Long、Float、Double、Byte都属于Number类的子类,Number类本身提供了一系列的返回以上6种基本数据类型的操作。
(2) 类Character、类Boolean属于Object的直接子类!!
(3) Number类是一个抽象类,主要是将数字包装类中的内容变为基本数据类型,其中定义的方法如下表:
序号 | 方法 | 类型 | 描述 |
1 | public byte byteValue() | 普通 | 以byte形式返回指定的数值 |
2 | public abstract double doubleValue() | 普通 | 以double形式返回指定的数值 |
3 | public abstract float floatValue() | 普通 | 以float形式返回指定的数值 |
4 | public abstract int intValue() | 普通 | 以int形式返回指定的数值 |
5 | public abstract long longValue() | 普通 | 以long形式返回指定的数值 |
6 | public short shortValue() | 普通 | 以short形式返回指定的数值 |
装箱与拆箱:
装箱: 将基本数据类型变为包装类的过程称为装箱操作。
拆箱: 将包装类变为基本数据类型的过程称为拆箱操作。
public class WrapperDemo01{
public static void main(String args[]){
int i = 30; //声明一个基本数据类型
Integer ii = new Integer(i); //装箱操作: 将基本数据类型变为包装类。用到了Number类中的带一个参数的构造方法
int temp = ii.intValue(); //拆箱操作: 将一个包装类变为基本数据类型。使用Number中定义到的方法来完成拆箱操作。
System.out.println(temp);
}
}
exam2:
public class WrapperDemo02{
public static void main(String args[]){
float f = 28.8f; //声明一个基本数据类型
Float ff = new Float(f); //装箱操作: 将基本数据类型变为包装类。用到了Number类中的带一个参数的构造方法
float temp = ff.floatValue(); //拆箱操作: 将一个包装类变为基本数据类型。使用Number中定义到的方法来完成拆箱操作。
System.out.println(temp);
}
}
exam3: jdk1.5过后,提供了自动的装箱及拆箱操作
public class WrapperDemo03{
public static void main(String args[]){
Integer i = 28; //自动装箱成Integer
Float f = 30.8f; //自动装箱成Float
int j = i; //自动拆箱成int
float g = f; //自动拆箱成float
System.out.println(j + ", " + g);
}
}
包装类的应用
包装类在实际应用中用得最多的还是字符串变为基本数据类型的操作, 例如将一个全由数字组成的字符串变为一个int或float类型的数据。在Integer和Float类中分别定义了以下两种方法:
(1) Integer类 (字符串转int型)
public static int parseInt(String s) throws NumberFormatException
(2) Float类(字符串转float型)
public static float parseFloat(String s) throws NumberFormatException
在使用以上两种操作时,一定要注意字符串必须全部由数字组成!!!
范例如下
public class WrapperDemo04 {
public static void main(String[] args) {
String str1 = "67"; //全由数字组成的字符串
String str2 = "6.7";
int i = Integer.parseInt(str1); //将字符串转换成基本数据类型的数据
float f = Float.parseFloat(str2);
System.out.println("整数乘方: " + i + "*" + i + "=" + (i*i));
System.out.println("小数乘方: " + f + "*" + f + "=" + (f*f));
}
}
6.13 匿名内部类
匿名内部类就是指没有一个具体名称的类,且是在接口和抽象类的应用上发展起来的。
interface A{
public void printInfo();
}
class B implements A{
public void printInfo(){ //覆写父类中的抽象方法
System.out.println("Hello World!!!");
}
}
class X{
public void fun1(){
this.fun2(new B()); //传递子类实例化对象
}
public void fun2(A a){ //接收接口实例化对象
a.printInfo(); //调用接口方法,调用的始终是被子类所覆写的方法
}
}
public class NoNameInnerClassDemo01{
public static void main(String args[]){
new X().fun1(); //实例化X类对象,并调用fun1()方法
}
}
如果接口的实现类(子类)只使用一次,那么没有必要单独的再定义一个子类B。此时就可以用匿名内部类来修改上面代码如下
interface A{
public void printInfo();
}
class X{
public void fun1(){
<span style="color:#ff0000;">this.fun2(new A(){ //匿名内部类
public void printInfo(){ //实现接口中的抽象方法
System.out.println("Hello World!!!");
}
});</span>
}
public void fun2(A a){ //接收接口实例化对象
a.printInfo(); //调用接口方法,调用的始终是被子类所覆写的方法
}
}
public class NoNameInnerClassDemo01{
public static void main(String args[]){
new X().fun1(); //实例化X类对象,并调用fun1()方法
}
}
以上程序定义了一个匿名内部类,
(1) 直接实例化接口对象,代码如下
new A() {}
此时是直接实例化接口对象,但是接口本身是无法直接进行实例化的。所以在接口实例化后面要有一个大括号,然后在大括号中编写具体的实现方法
(2) 实现抽象方法,代码如下
new A(){
public void printInfo(){
System.out.println("Hello World!!!");
}
}
整个代码编写完成后,实际上此时就是一个接口的实例化对象了。其中的抽象方法也得到了实现。
6.14 本章要点
- 继承可以扩充已有类的功能,通过关键字extends实现,可将父类的成员(包含数据成员和方法)继承到子类。
- java在执行子类的构造方法前会先调用父类的无参构造方法,其目的是为了对继承自父类的成员做初始化的操作。
- 父类有多个构造方法时,如要调用特定的构造方法,则可在子类的构造方法中通过super()关键字来实现。
- this()用于在同一个类中调用其它的构造方法,而super()则用于从子类的构造方法中调用其父类的构造方法。
- 使用this调用属性或方法时,会先从本类中查找,如果在本类中没有查找到,再从父类中查找; 而super则会直接从父类中查找需要的属性或方法。
- this()与super()相似之处: (1) 当构造方法有重载时,两者均会根据所给予的参数类型和个数正确的执行相对应的构造方法;(2) 两者均必须编写在构造方法的首行,正是因为如此,二者不能同时处想在同一个构造方法里面。
- 重载overloading是指在相同类内定义名称完全相同,但是参数的类型或个数不同的方法,因此java可依据参数的个数或参数类型调用相应的方法
- 覆写overriding是在子类中定义名称、参数个数和参数类型完全与父类相同的方法,用以覆写父类中的方法。
- 如果父类的方法不希望被覆写,可在父类的方法前加上final关键字,这样该方法便不会被覆写。 如果一个类不希望被继承,也可以在该类的定义前加上final关键字。
- final关键字如果来修饰数据成员变量,这样该变量就变成了一个常量,因此便无法在程序代码中再做修改了。使用public static final可以声明一个全局常量。
- 所有的类均继承自Object类。 一个好的类应该覆写Object类中的toString()、boolean equals(Object obj)、hashCode()3个方法。并且被覆写后该方法的访问限制不能低于public。。所有的引用对象均可以向Object类进行向上转型操作。
- JAVA中可以创建抽象类,专门用来当作父类。抽象类的作用类似于“模板”,其目的是依据其格式来修改并创建新的类
- 抽象类的方法可以分为两种: 一种是一般的方法; 另外一种是以abstract关键字修饰的抽象方法。 抽象方法没有定义方法体,而是要保留给由抽象类派生出来的新类来定义实现的。
- 抽象类不能直接被实例化,必须通过对象的多态性进行实例化操作。
- 接口是抽象方法和全局常量的集合。 接口必须被子类所实现,一个接口可以同时继承多个接口,一个子类可以同时实现多个接口。
- Java并不允许类的多重继承,但是允许实现多个接口
- 接口与一般类一样,均可通过扩展的技术来派生出新的接口。 原来的接口称为基本接口或父接口;派生出的接口称为派生接口或子接口。通过这种机制,派生接口不仅可以保留父接口的成员,同时可以加入新的成员以满足实际的需要。
- java中,对象的多态性分为向上转型(程序自动完成)和向下转型(强制方法)
- 通过instanceof关键字可以判断对象属于哪个类
- 匿名内部类的作用是可利用内部类创建不具有名称的对象!!并利用它访问类中的成员。