第 20 节 Object类


1、Object类的作用;

2、Object类的常用方法;

3、Object类接收引用数据类型;

4、利用Object类修改链表。

Object类的基本定义

Object类是所有类的父类,也就是说任何一个类在定义的手如果没有明确的继承一个父类的话,那么它就是Object类的子类,也就是说以下两种类定义的效果是完全相同的;

class Book {}
class Book extends Object {}

在整个Java里面类的继承关系一直存在(除了Object类)。

既然Object类是所有类的父类,那么就有了一个最大的好处:利用Object类可以接受全部类的对象,因为所有类都可以向上转型成Object类。

范例: 利用Object类接收对象

class Book extends Object {}
public class MainClass {
	public static void main(String[] args) {
		Object obja = new Book(); // 向上转型
		Object objb = "hello"; // 向上转型
		Book b = (Book) obja;
		String s = (String) objb;
	}
}

既然这样,在不确定参数类型的时候,使用Object类应该是最好的选择。

**问题:**为什么要在Object类里面定义一个无参构造方法?

Object类是所有类的父类,所有子类进行实例化时都一定要调用父类的构造方法。

从严格意义上来讲(一般不遵守),任何一个简单Java类都应该覆写Object类中的如下三个方法:

  • 取得对象信息:public String toString();

  • 对象比较:public boolean equals(Object obj);

  • 取得对象Hash码:public int hashCode()。

取得对象信息:toString()

如果说现在是一个String类对象,直接输出这个对象可以取得字符串的内容,但是如果是一个自定义的对象,那么就会出现一个对象编码。

范例: 观察代码

class Book extends Object {}
public class MainClass {
	public static void main(String[] args) {
		Book b = new Book();
		String s = "Hello";
		System.out.println(b);
		System.out.println(b.toString());
		System.out.println(s);
	}
}

发现现在如果直接输出对象与调用toString()方法后输出对象的功能是完全一样的。

在进行对象输出的时候,输出操作会自动调用对象中的toString()方法将对象变为字符串后再输出,而默认情况下Object类中的toString()为了适应于所有对象的输出,所以只输出了对象的编码。

如果现在有需要,也可以自己根据实际情况来覆写此方法。

范例: 覆写toString()方法

class Book {
	private String title;
	private double price;
	public Book(String title, double price) {
		this.title = title;
		this.price = price;
	}
	public String toString() {
		return "title=" + this.title + " price=" + this.price;
	}
}
public class MainClass {
	public static void main(String[] args) {
		Book b = new Book("java", 11.1);
		System.out.println(b);
	}
}

直接输出对象就调用了toString(),等于节约了输出对象时的代码。

对象比较:equals()

对象比较在很多开发之中都是一定要使用到的核心概念,而在之前使用了一个自定义的compare()方法作为比较方法的名称,但是这个不标准,标准的做法是使用equals()方法完成。

范例: 实现对象比较

class Book {
	private String title;
	private double price;
	public Book(String title, double price) {
		this.title = title;
		this.price = price;
	}
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Book))
			return false;
		Book book = (Book) obj;
		if (!this.title.equals(book.title))
			return false;
		if (this.price != book.price)
			return false;
		return true;
	}
	public String toString() {
		return "title=" + this.title + " price=" + this.price;
	}
}
public class MainClass {
	public static void main(String[] args) {
		Book b1 = new Book("java", 11.1);
		Book b2 = new Book("java", 11.1);
		System.out.println(b1.equals(b2));
	}
}

Object类可以接受一切引用数据类型

Object类是所有类的父类,所以Object类的对象可以接受所有类的对象,可是除了类的对象之外,Object还可以接受数组和接口对象。

范例: 接受数组数据

public class MainClass {
	public static void main(String[] args) {
		Object obj = new int[] {1, 2, 3};
		System.out.println(obj); // [I@7852e922
		if (obj instanceof int[]) {
			int data[] = (int[]) obj;
			for (int i = 0; i < data.length; i ++) {
				System.out.println(data[i]);
			}
		}
	}
}

除了数组外,接口也同样可以。

范例: Object类接收接口对象

interface A {
	public void fun();
}
class B implements A {
	public void fun() {
		System.out.println("Hello World !");
	}
}
public class MainClass {
	public static void main(String[] args) {
		Object obj = new B();
		A a = (A) obj;
		a.fun();
	}
}

整个程序的参数都可以使用Object进行统一。

修改链表

长期一直困扰我们的是事情就是链表问题,因为在设计链表的时候一直遗留下的问题:链表不能统一数据,所以就造成了每一次使用链表的时候都要进行重复的开发,但是有了Object之后,这一切就彻底解决了,因为Object可以接受任何类型的对象,同时在链表之中需要对象比较的动能,这一功能Object也提供有容器equals()方法。

范例: 修改可用链表

class Link { // 链表类,外部能够看见的只有这一个类
    // 让其为Link类服务
    private class Node { // 定义的节点类
        private Object data;
        private Node next;
        public Node(Object data) {
            this.data = data;
        }
        public void addNode(Node newNode) {
            if (this.next == null) { // 当前的下一个节点为空
                this.next = newNode;
            } else { // 向后继续保存
                this.next.addNode(newNode);
            }
        }
        // 第一次调用(Link):this = Link.root
        // 第二次调用(Node):this = Link.root.next
        // 第三次调用(Node):this = Link.root.next.next
        public boolean containsNode(Object data) {
            if (data.equals(this.data)) { // 当前节点数据为要查询的数据
                return true; // 不再向后查询
            } else { // 当前节点数据不满足查询要求
                if (this.next != null ) { // 有后续节点
                    return this.next.containsNode(data);
                } else {
                    return false;
                }
            }
        }
        public Object getNode(int index) {
            // 使用当前的foot与要查询的索引进行比较
            // 随后将foot的内容自增,目的是为了进行下一步查询
            if (Link.this.foot ++ == index) { // 当前节点是要查询的索引
                return this.data; // 返回当前节点数据
            } else { // 继续向后查询
                return this.next.getNode(index);
            }
        }
        public void setNode(int index, Object data) {
            if (Link.this.foot ++ == index) {
                this.data = data; // 进行内容的修改
            } else {
                this.next.setNode(index, data);
            }
        }
        // 要传递上一个节点以及要删除的数据
        public void removeNode(Node previous, Object data) {
            if (data.equals(this.data)) { // 当前节点为要删除节点
                previous.next = this.next; // 空出当前节点
            } else { // 应该向后继续查询
                this.next.removeNode(this, data);
            }
        }
        // 第一次调用(Link):this = Link.root
        // 第二次调用(Node):this = Link.root.next
        public void toArrayNode() {
            Link.this.retArray[Link.this.foot ++] = this.data;
            if (this.next != null) { // 有后续元素
                this.next.toArrayNode();
            }
        }
    }
    // ========================= 以上为内部类 =========================
    private Node root; // 需要根节点
    private int count = 0; // 保存元素的个数
    private int foot = 0;
    private Object[] retArray; // 返回的数组
    public void add(Object data) { // 假设不允许有null
        if (data == null) {
            return ;
        }
        Node newNode = new Node(data); // 要保存的数据
        if (this.root == null) { // 当前没有根节点
            this.root = newNode; // 保存根节点
        } else { // 根节点存在,其他节点交给Node类处理
            this.root.addNode(newNode);
        }
        this.count ++; // 每一次保存完成后数据量加一
    }
    public int size() { // 取得保存的数据量
        return this.count;
    }
    public boolean isEmpty() {
        return this.count == 0;
    }
    public boolean contains(Object data) {
        // 没有要查询的数据,也不存在根节点
        if (data == null || this.root == null) {
            return false; // 没有查询结果
        }
        return this.root.containsNode(data);
    }
    public Object get(int index) {
        if (index >= this.count) { // 操过了查询范围(索引从0开始)
            return null; // 没有数据
        }
        this.foot = 0; // 表示从前向后查询
        return this.root.getNode(index); // 将查询过程交给Node类处理
    }
    public void set(int index, Object data) {
        if (index >= this.count) {
            return ; // 结束方法调用
        }
        this.foot = 0; // 重新设置foot属性的内容,作为索引出现
        this.root.setNode(index, data); // 交给Node类设置数据内容
    }
    public void remove(Object data) {
        if (this.contains(data)) { // 判断数据是否存在
            // 判断要删除的数据是否是根节点的数据
            if (data.equals(this.root.data)) {
                this.root = this.root.next; // 空出当前根节点
            } else { // 不是根元素
                this.root.next.removeNode(this.root, data);
            }
            this.count -- ; // 统计个数减少
        }
    }
    public Object[] toArray() {
        if (this.root == null) {
            return null;
        }
        this.foot = 0; // 需要索引控制
        this.retArray = new Object[this.count]; // 根据保存内容个开辟数组
        this.root.toArrayNode(); // 交给Node类处理
        return this.retArray;
    }
}

范例: 测试

public class MainClass {
	public static void main(String[] args) {
		Link all = new Link();
		all.add("A");
		all.add("B");
		all.add("C");
		all.remove("A");
		Object[] data = all.toArray();
		for (int i = 0; i < data.length; i ++) {
			System.out.println(data[i]);
		}
	}
}

总结

1、Object类对象可以接受一切的数据类型(引用数据类型),包括数组和接口,解决了数据统一问题;

2、toString()在对象输出时调用,equals()在对象比较时调用。

综合实战:宠物商店

整合链表应用,同时进一步巩固接口的作用(标准)。

**思考:**要求以程序结构为主

实现一个宠物商店的模型,一个宠物商店可以保存多个宠物的信息(名字、年龄),可以实现宠物的上架、下架、模糊查询的功能。

现在已经有了一个可用链表,并且这两个链表里面还提供有Object统一参数。

范例: 定义宠物的标准

interface Pet { // 定义一个宠物的标准
	public String getName(); // 得到宠物的名字
	public int getAge(); // 得到宠物的年龄
}

宠物商店与具体的宠物没有任何的关系,它只和宠物这个接口的标准有关。

范例: 定义宠物商店

class PetShop { // 一个宠物商店要保存有多个宠物信息
	private Link pets = new Link(); // 保存的宠物信息
	public void add(Pet pet) { // 上架
		this.pets.add(pet);
	}
	public void delete(Pet pet) { // 下架
		this.pets.remove(pet);
	}
	public Link search(String keyWork) {
		Link result = new Link();
		Object[] obj = this.pets.toArray();
		for (int i = 0; i < obj.length; i ++) {
			Pet p = (Pet) obj[i];
			if (p.getName().contains(keyWork)) {
				result.add(p);
			}
		}
		return result;
	}
}

下面就可以根据宠物的标准定义各个子类。

范例: 定义猫

class Cat implements Pet { // 如果不实现接口无法保存宠物信息
	private String name;
	private int age;
	public Cat(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return this.name;
	}
	public int getAge() {
		return this.age;
	}
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Cat))
			return false;
		Cat other = (Cat) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	public String toString() {
		return "Cat [name=" + name + ", age=" + age + "]";
	}
}

范例: 定义狗

class Dog implements Pet { // 如果不实现接口无法保存宠物信息
	private String name;
	private int age;
	public Dog(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return this.name;
	}
	public int getAge() {
		return this.age;
	}
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Cat))
			return false;
		Dog other = (Dog) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	public String toString() {
		return "Dog [name=" + name + ", age=" + age + "]";
	}
}

可以发现,有了接口以后子类的形式都非常的类似,这属于接口的特点。

范例: 测试类

public class MainClass {
	public static void main(String[] args) {
		PetShop shop = new PetShop();
		shop.add(new Cat("小黑", 20));
		shop.add(new Cat("皇受", 18));
		shop.add(new Cat("吾皇", 14));
		shop.add(new Dog("二哈", 6));
		shop.add(new Dog("泰迪", 16));
		shop.add(new Dog("阿拉斯加", 18));
		shop.delete(new Dog("泰迪", 16));
		Link all = shop.search("皇");
		Object[] objs = all.toArray();
		for (int i = 0; i < objs.length; i ++) {
			System.out.println(objs[i]);
		}
	}
}

此时实现了现实的关系的模拟。

最后更新于