Object Oriented 面向对象

一个简单的例子说清楚 继承,封装 与 多态。也同时介绍final,abstrac, public, privated, protected,super 关键字的使用场景。也介绍重写 euqals() 方法 和 toString() 方法。也介绍 instanceof 方法,和 注解 的使用。

假设一个场景:

  1. 有一个 Animal 类, 我们不需要它被实例化, 因为一个动物过于抽象,实例化除了没有意义。所以将这个类设置为 abstract 抽象类,希望被子类继承。

  2. Animal 类 有3个私有的成员变量,分别是 姓名 name, 年龄 month 和 品种 species,我们不希望这个三个成员变量被外部直接访问, 所以设置为 private。同时为了, 为了方便给上面三个属性赋值 和 获取他们的值,我们给三个私有成员 设置 setter 和 getter 方法, 并且访问限制符 设置为 protected。三个私有变量的初始化通过构造函数完成。 由于 Animal 是抽象类,不希望被实例化, 所以 构造函数 访问限制符设置为 protected, 方便给子类继承

  3. Animal 类型都有一个属性:睡觉。 我们用一个 void sleep() 方法来表示。这个属性是不随子类变化而变化的,只要是一个动物, 都会睡觉。所以我们将这个函数设置为 static,静态方法 。 另外我们希望对外展示这个属性,所以访问修饰符设置为public。综上,方法申明应该是: public static void sleep()

  4. Animal 类型还有一个属性:吃东西。我们用一个 void eat() 方法来表示。这个属性在不同的子类中表现形式不同, 比如 猫吃鱼, 狗吃骨头, 兔子吃草。所以我们设置这个方法为protected, 让子类去继承和重写。

  5. 为了区分两只动物是否相等, 我们需要比较两个动物的 name, month 和 species 。若两只动物的这三个属性都相同,我们确定这是同一个动物。重写equals()方法,在函数内部比较3个属性是否相同,若三个属性相等返回true, 否则返回false, 能够满足我们的要求。完成以后,我们对两个对象用等于号 = 相连接,如: a = b 就可以自动调用 equals()方法判断两个动物是否为同一个个体。

  6. 我们希望将 animal 子类放入System.out.println()方法时能够直接打印出动物的三个属性。对此我们需要重写toString()方法。

public abstract class Animal {

	private String name;
	private int month;
	private String species;

	protected Animal(String name, int month,String species) {
		this.setName(name);
		this.setMonth(month);
		this.setSpecies(species);
	}

	protected String getName() {
		return name;
	}

	protected void setName(String name) {
		this.name = name;
	}

	protected int getMonth() {
		return month;
	}

	protected void setMonth(int month) {
		this.month = month;
	}

	protected String getSpecies() {
		return species;
	}

	protected void setSpecies(String species) {
		this.species = species;
	}

	// 睡觉函数
	public static void sleep() {
		System.out.println("动物都会睡觉觉!");
	}

	protected abstract void eat() ;

	// 重写Object类的equals方法,用来判断"两个"动物是否是一个动物
	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}
		try {
			Animal temp = (Animal) obj;// 要保证obj 可以转换为Animal 类型
			if (this.getName() == (temp.getName()) && this.getMonth() == temp.getMonth()
					&& this.getSpecies() == temp.getSpecies()) {
				return true;
			} else {
				return false;
			}
		} catch (java.lang.ClassCastException e) {
			e.printStackTrace();
			return false;
		}

	}

	// 重写Animal类的equals方法
	public boolean equals(Animal obj) {
	 
		if (this.getName() == (obj.getName()) && this.getMonth() == obj.getMonth()
				&& this.getSpecies() == obj.getSpecies()) {
			return true;
		} else {
			return false;
		}

	}

	// 重写toString方法
	@Override
	public String toString() {
		return "昵称 : " + this.getName() + " , 品种 : "+this.getSpecies()+" , 年龄 : " + this.getMonth() + "个月";
	}

}

继续上面的场景:

  1. Cat 类继承 Animal 类, 我们不希望 Cat 类再往下细分品种, 不希望被别的类继续继承, 所以 设置为 final 类。

  2. Cat 类继承 Animal 类,多了一个私有变量 food。需要为这个变量设置 getter 和 setter 函数。我们为 food 变量设置了 setter 和 getter 方法, 并且在构造函数内对其进行赋值。构造函数 访问限制符 设置为 public 。

  3. Cat 继承了 Animal 类, 需要实现 Animal 的抽象方法 void eat(), 为了方便外部调用, 访问修饰符应设置为 public。

  4. Cat 继承了 Animal 类, 也继承了私有变量 name, food, species 和他们的 getter 和 setter 方法。我们需要对 setter 方法和 getter 方法进行重写, 并且修改 访问限制符为 public。这3个属性初始化通过构造方法完成, 构造方法 访问限制符设置为 public。 setter, getter,构造方法可以通过 super 关键字 调用其父类的 setter, getter 和构造方法来实现。

  5. Cat 类继承了 Animal 类的 void eat() 方法, 并且需要重写该方法,将访问限制符 设置为 public。

public final class Cat extends  Animal{

    private String food;

    public Cat(String name, int month, String species,String food ) {
        //子类构造默认调用父类的无参构造方法
        //可以通过super()调用父类允许被访问的带参构造
        //super()必须放在第一行
        super(name,month,species);
        this.setFood(food);
    }

    public String getFood() {
        return food;
    }

    public void setFood(String food) {
        this.food = food;
    }


    @Override
    public void eat() {
        System.out.println(this.getName() + "喜欢吃"+this.getFood()+"!");
    }


    @Override
    public String getName() {
        return super.getName();
    }

    @Override
    public  int getMonth() {
        return super.getMonth();
    }

}

我们通过两个对象,来测试我们实现的 Animal 类 和 Cat 类。我们额外用到了 instance 方法, 用来判断一个对象是不是一个类的实例化。

public static void main(String[] args) {

    Animal.sleep();//输出: 动物都会睡觉觉!

    Cat cat1= new Cat("鸡腿",3, "金渐层","小鱼干");
    Cat cat2= new Cat("泰哥",4, "美短","鸡肉罐头");

    cat1.eat();//输出: 鸡腿喜欢吃小鱼干!
    cat2.eat();//输出: 泰哥喜欢吃鸡肉罐头!

    System.out.println(cat1);//输出: 泰哥喜欢吃鸡肉罐头!
    System.out.println(cat2);//输出: 昵称 : 泰哥 , 品种 : 美短 , 年龄 : 4个月

    System.out.println("cat1 和 cat2 是同一个动物吗? "+ cat1.equals(cat2));//false

    System.out.println("cat1 是 Animal的实例化吗 ? "+ (cat1 instanceof  Animal));//true

}

总结一下

封装:

  • 我们不希望 类的成员变量被直接访问,而是通过变量的 setter 和 getter 函数进行修改和访问, 避免误操作修改成员变量的可能性。

继承:

  • 一个类可以被另一个类继承。被继承的类称为 父类,继承父类的类称为子类。 子类一旦继承父类,也就继承了

  • Java 中不允许多继承, 也就是一个类只能继承一个父类。

多态:

  • 父类中的成员函数可能在子类中有多种表现形式。比如我们起那么提到的 Animal 类 有一个 eat() 函数, 但是他的子类具体 吃 什么是多张多样的, 子类的 eat() 函数表现形式多种多样。

abstract 和 protected 的用法:

  • 当我们希望一个类不希望被实例化,而希望被继承时, 设置这个类为抽象类 abstract, 同时内部的所有成员函数都设置为 protected。

final 的用法:

  • 当某一个类,我们不希望它被其他类继承时,我们这个类为final。

  • 当一个变量,从一开始初始化以后不允许被修改,也可以设置为final。如:

final int a=10;

a 的 值一旦被设置为10,以后不允许被修改。

super 的用法:

  • 当想要调用父类的成员方法或属性时,可直接通过 super 关键字访问。

private 和 public 用法:

  • 从封装性的角度来说,应该将类的所有成员变量设置为 private, 然后通过 setter 和 getter 函数 进行修改和访问。若这个类是虚类,不希望被实例化,而希望被继承, setter 和 getter 应该设置为 protected。 否则 setter 和 getter 应该设置为 public 。

  • 所有希望能被外部访问的方法, 都应该设置为 public 。 

static 的用法:

一个类的固有属性,不随类的实例化,或者其子类的实例化变化而变化,那么我们可以把这个固有属性用一个静态成员变量或者一个静态方法表现出来。比如,我们上面说到的 Animal 类,从语义上理解动物都会睡觉,是不随动物的种类变化而变化的。所以 我们设置 sleep() 方法为 静态 static 类型。

toString() 方法:

这是一个 返回值为 String 类型的方法。我们把想要展示这个对象的信息返回,比如 Animal 类型,我们想要对外展示这个对象的 name, month, species 属性。这样当我们调用 System.out.println(对象时), 自动调用对象的 toString() 方法,将对象信息输出到 控制台。

如果不重写 toString() 方法, 当我们调用 System.out.println(对象时), 会返回这个 "对象的位置@哈希码" 。

Last updated

Was this helpful?