继承、封装、多态的理解
封装
将对象的属性和行为封装起来,其载体就是类,类通常对客户隐藏其实现细节,无须知道类内部是如何工作的,这就是封装。
避免了外部操作对内部数据的影响,提高了程序的可维护性。
继承
继承性主要利用特定对象之间的共有属性。如儿童是人,男士和女士也是人,他们具有共同特性,一对眼睛一个鼻子一个嘴巴,儿童可以复用人的属性和行为,同时添加了儿童自身独有的属性和行为。
在Java语言中将类似于儿童的类称为子类,将类似于人的类称为超类或父类。子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。
注意:子类的实例都是父类的实例,但不能说父类的实例是子类的实例。
多态
有了继承,才能有多态。
父类的对象变量可以引用本类的对象,也可以引用子类的对象。
由于Object类是超根类(祖先类),因此,它的对象变量可以引用所有类的对象。
特殊运算符: instanceof 属性
- 注意:子类的对象变量可以引用父类的对象吗?
答案: 不可以 若要进行,则必须强制转换。
即对象类型的转换分为以下两种:
1) 向上转型: 子类的对象可以直接赋给父类的对象变量。
这种现象也称之为类型的自动转换。
2) 向下转型: 将父类的引用强制转换成子类的引用。
注意: 它必须强制转换。
格式: (类型名) 对象变量;
当父类对象变量引用了子类的对象时,则问: 父类的对象变量是否可以直接调用子类的特有方法?
答案: 否定的。
例如: Animal 是父类, Dog是子类, 而getName()方法是Dog子类特有的方法。
因此, 当有 Anaimal a = new Dog(); 时,
则 a.getName(); 是否正确?
答: 错误。
问: 那如何访问?
答: 需要先强制转换(向下转型)
子类类名 对象变量 = (子类类名)父类的对象变量;
即: Dog d = (Dog)a;
之后, d.getName(); 此时,正确。
什么情况下需要将对象的引用实现强制转换(还原)(向下转型)?
- 一定是发生多态: 父类的对象变量引用了子类的对象。
Object obj = new Student(); - 一定是想要去访问(调用)子类对象的特有属性或方法。
父类的对象变量.子类对象的特有方法(属性); //则错了。
Stringstr = obj.getSchool(); //错了。
((子类类名)父类的对象变量).子类对象的特有方法(属性); //则对了。
Stringstr = ((Student)obj).getSchool(); //对了。
//多态: 父类的对象变量引用了子类的对象。
<span style="font-size:18px;"><span style="font-size:18px;">public class AnimalsTest {
public static void main(String[] args) {
Animal a = new Animal("动物");
Dog d1 = new Dog("狗", "旺财", true, 2);
Cat c1 = new Cat("猫", "豆豆", "blue");
System.out.println( a );
System.out.println( d1 );
System.out.println( c1 );
boolean flag = a instanceof Animal ; //判断a属于Animal类型吗
System.out.println( flag );
flag = a instanceof Dog;
System.out.println( flag );
flag = d1 instanceof Dog;
System.out.println( flag );
flag = d1 instanceof Animal; //当结果为true时,说明它们之间存在继承关系。
System.out.println( flag );
//多态: 父类的对象变量引用了子类的对象。
Animal a1 = new Dog("狗", "ABC", false, 3);
//System.out.println( a1.getName() ); //调用a1的 getName()方法. 此时,它会去Animal类中找 getName()方法。若找不到,则报错。
//判断a1是否拥有Dog类的特性,若有,则还原。
if( a1 instanceof Dog ){
Dog dd = (Dog)a1; //将a1强制转换为Dog类型。
System.out.println( dd.getName() );
}
System.out.println("程序结束。");
}
}
</span></span>
多态有编译时多态 和运行时多态。
第一个是通过方法重载实现;第二个是通过方法覆盖实现(子类覆盖父类方法)。
- 第一种就是我们调用方法是不用区分参数类型,程序会自动执行相应方法,如:
加法运算,可以使int相加,可以是double相加,都是同一个方法名。 - 第二种就是动态绑定,使用父类引用指向子类对象,再调用某一父类中的方法时,不同子类会表现出不同结果。
这样的作用就是扩展性极好,玩过网游的话应该知道
游戏中有不同的角色,它们都有一个父类,它们做相同动作时表现出来的效果就会不一样,比如跑,魔法师的跑跟战士的跑就不会一样,这就是俩者都覆盖了父类中的跑方法,各自有自己的现实,表现出来多态。
如果有一天你想再加个角色,只用再写一个类继承该父类,覆盖其中的跑方法就行了,其他代码不用怎么改,所以可维护性也很好。
比如 你没用多态的话 ,你没实例化一个对象 就要new一下,那假如你那天改变了需求了呢?那是不是又要改里面的?这样不好,所以 你可以通过多态,把需要相似的给提出来,然后继承它,以后需要扩展你仅仅只是继承而已。
这个需要多看代码 才可以更深刻理解。假设有一个类 叫 鸟类,它拥有属性翅膀,拥有方法鸣叫,如下
public class Bird{
private Wing wing;
public void moo(){
System.out.println("鸟叫声");
}
}
鸟类封装了 翅膀类和moo方法;另外有两个类都继承鸟类并重写了moo方法,分别是鹦鹉和麻雀如下:
//鹦鹉类
public class Parrot extends Bird{
public void moo(){
System.out.println("鹦鹉的叫声");
}
}
//麻雀类
public class Sparrow extends Bird{
public void moo(){
System.out.println("麻雀的叫声");
}
}
方法重写应该懂吧,不懂自己找书看吧;然后你有个妻子她想听鸟叫,就有个妻子类
public class Wife{
public void listen(Bird bird){
bird.moo();
}
}
这时多态就很好的体现了,你妻子想听鸟叫,无论什么鸟都可以给她,但是你想让她和鹦鹉说话,你就买了一只鹦鹉传给listen方法,结果你妻子听到了鹦鹉的叫声,程序输出:鹦鹉的叫声
public static void main(String[] args) {
new Wife().listen(new Parrot());
}
多态实现了动态绑定,让程序有了很好的扩展性,比如你以后想买一只燕子送给你妻子,就只需要写个燕子类Swallow继承Bird方法就可以了,而不需要再在妻子类里添加一个方法listen(Swallow swallow)。