写的不是很长,尽可能简洁清晰把知识串联起来理解吧。
提纲:
1、instanceof
2、声明类型的强制转换
3、多态、向上转型和向下转型
1、instanceof
用于判断两个类之间是否存在父子关系。
是双目运算符,作用是测试它左边的对象是否是它右边的类的实例。比较的时候我们只看实际类型——就是new后面的对象所属类——和instanceof右边类之间的关系。
代码示例:
Object object = new Student();
System.out.println(object instanceof Student); //ture
System.out.println(object instanceof Person); //ture
System.out.println(object instanceof Object); //ture
System.out.println(object instanceof Teacher); //False
System.out.println(object instanceof String); //False
Person person = new Student();
System.out.println(person instanceof Student); //ture
System.out.println(person instanceof Person); //ture,
System.out.println(person instanceof Object); //Ture
System.out.println(person instanceof Teacher); //False
System.out.println(person instanceof String); //编译报错
Student student = new Student();
System.out.println(student instanceof Student); //ture
System.out.println(student instanceof Person); //ture,
System.out.println(student instanceof Object); //Ture
System.out.println(student instanceof Teacher); //编译报错
System.out.println(student instanceof String); //编译报错
2、声明类型的强制转换
在基本类型里,高容量数据类型转低容量数据类型是强制转换。在对象里也一样,转换的是声明类型,父类代表高的一方,子类代表低的一方。将声明类型从父类转换为子类,就是强制转换(向下转型)。
被父类引用指向的子类对象(Father son)不能使用子类独有的方法,需强转后才可使用:
public class Father {
public void run(){
System.out.println("father run");
}
}
=================================================
public class Son extends Father{
public void go(){
System.out.println("son go");
}
}
=================================================
@Test
public void test(){
Father son = new Son(); 子类可以直接转父类。
son.run();
// son.go(); 编译不通过!
Son son2 = (Son) son;
son2.go(); 通过强转后可以使用了
}
输出:father run
son go
注意,如果实际类型是父类的,声明类型不能转换为子类,编译不会报错但运行会报错 :
Father father = new Father();
Son father1 = (Son) father;//编译通过了
father1.go(); //类型转换异常ClassCastException: class Father cannot be cast to class Son
总结:
- 实际类型是子类、声明类型是子类的,可以转换为父类,也可以再从父类转换为子类(强转)。
- 实际类型是父类、声明类型是父类的,不能转换为子类。
简而言之,实际类型是子类的,声明类型可以是父类或子类;
实际类型是父类的,声明类型只能是父类。
3、多态、向上转型和向下转型
令父类引用指向子类对象就是 向上转型,父类代表高的一方,子类代表低的一方。
3.1 父类引用指向子类对象的好处?
1.如果这个父类是抽象类,抽象类本身不能实例化,就必须通过子类来达到目的。
2.限定了调用方法的范围,达到封装的安全性。 此种情况下,不能调用子类定义的(特有的)方法,只能调用父类定义过的方法,且运行时实际用的是子类覆写父类后的。我们在编译期只能调用父类中声明的方法,但执行期/运行期,我们实际执行的是子类重写父类的方法——虚拟方法调用。【即:编译看左边(类),运行看右边(类)】
只有声明类型是自己时,才可以调用自己定义的属性和方法。之所以这么做是为了规范,因为类里可能有很多东西并非用户需要的,用户只需要通过某个“外接口”来获取对象并操作它就可以了,这就是封装。缺点却也是优点。
3. 方便修改,降低了修改程序的成本和风险。比如
HashMap<Integer,String> map = new HashMap<>();
这种情况,如果要用其他Map(这是一个接口)的其他实现类(比如能力更为强大的SuperMap),注意你有可能在后续代码里用到了HashMap的独有方法,这时改成SuperMap就需要改一系列被波及的代码,成本极大,也有很大的风险隐患。如果改成下面这种
Map<Integer,String> map = new HashMap<>();
由于使用的是接口的方法,由于所有实现类都对它进行了重写,需要调整为其他类的时候就非常灵活,最好的情况是,只需把new HashMap<>()这行替换掉即可。
3.2 什么是多态?条件?作用?
通过子类重写父类方法表现不同特性就是多态。
多态的条件?
1.继承关系 2.子类重写父类方法 3.通过父类引用调用重写后的方法(向上转型)
多态的作用?
1.通过子类重写父类方法,表现不同的特性 2.减少(子类之间的)方法重载,不用子类特别去造方法,避免代码冗余,比如在没有多态性的情况下,一个方法声明什么类型的参数,就只能用这个类型的对象去传,这就需要写很多同种功能的方法,造成了方法冗余。
对象的多态性只适用于方法,不适用于属性,Person p2 = new Man();此时打印父类和子类同名的属性时,输出的是父类的属性值。【属性 编译看左边】
Father son = new Son();
如上,不能调用子类所特有的方法、属性,编译时,p2是Person类。
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但由于变量声明为父类类型,导致编译时只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用(声明了,但p2不能调用!)。所谓【编译看左边】。
问如何才能调用子类特有的属性和方法? 答:向下转型,Son son2 = (Son) son; 也就是强制转换。
3.3 向下转型及其条件:
令声明类型从父类转换为子类,就是向下转型。
向下转型的对象其实际类型须是本类或其子类,转换到本类或父类。反之,因为父类没有子类特定的功能,因此不能转、不合法! 要想向下转型,new的一定不是父类,而是本类或子类,然后强转回来。总结一句话:功能少的不可以转换为功能多的。
(根据自己的理解整理,如有错误欢迎指正!)