6——多态
面向对象编程一共由三大特性,分别是:继承、封装、多态,前两种特性我们已经学习并且总结过了,多于多态我们今天来看看它是怎么让我们的面向对象编程变得多姿多彩的。
一、多态的实现
首先我们来看一下多态是通过什么来实现的:
1、方法的多态性
(1)方法重载:在同一个类中,方法名字相同,参数个数不同、参数类型不同、参数顺序不同就叫做方法重载;方法重载将同一个参数名可以同时实现不同参数类型、参数个数的功能。比如我们在main方法中使用的System.out.println();这个方法在源程序中是将所有的参数类型、参数个数都考虑进去了的,这样我们在使用的时候不管输入什么才可以正确的输出结果。
(2)方法重写:子类通过继承将父类的所有结构都继承过来了,但是对于父类的一些方法有些不满意,所以对它进行了改进 ,对于方法名字相同、参数个数相同、参数类型相同但是方法实现的功能不同就叫做方法覆写;方法覆写同样也实现了多态。
2.对象的多态:对象的多态性前提是有方法覆写,分为两种
(1)对象向上转型:父类 父类对象=new 子类;
情况一:通过向上转型以后这个方法子类是经过子类覆写的,这个时候是可以调用成功的。
//(1)向上转型
class Person{
private String name;
public void print(){
System.out.println("这是父类的方法");
}
}
class Student extends Person{
public void print(){
System.out.print("这是子类经过覆写以后的方法");
}
}
public class Fuxie{
public static void main(String[] args){
Person per=new Student();//向上转型
per.print();//观察向上转型以后父类调用的方法到底是经过覆写以后的方法还是原来的方法
}
}
情况二 :父类的对象调用的方法是没有经过子类覆写的,这个时候调用会出现错误,具体错误下面代码的运行结果可见
//(1)向上转型
class Person{
private String name;
public void print(){
System.out.println("这是父类的方法");
}
public void Print2(){
System.out.println("这是被父类的第二个方法,子类不对它进行覆写");
}
}
class Student extends Person{
public void print(){
System.out.print("这是子类经过覆写以后的方法");
}
}
public class Fuxie{
public static void main(String[] args){
Person per=new Student();//向上转型
per.print();//观察向上转型以后父类调用的方法到底是经过覆写以后的方法还是原来的方法
//在这个程序里面,类型是Person类的,但是它对应开辟的空间是Student的,所以最后在父类对象调用方法的时候,调用的是被覆写过以后的方法
//对一个子类没有覆写的方法用父类的对象调用,看它是否会有结果输出。
per.print2();
}
}
总结:对于对象的向上转型,我们的java是支持的,两种类型是兼容的,编译、运行均可以通过,但是前提是转型后这个对象调用的方法是被覆写过的,否则还是会出错。
(2)对象向下转型:子类 子类对象=new 父类;
通过下面的代码我们来看向下转型我们需要注意什么,它的一些知识:
//(2)向下转型
class Person{
public void print(){
System.out.println("这是父类的方法");
}
public void print2(){
System.out.println("这是父类的第二个方法");
}
}
class Student extends Person{
public void print(){
System.out.println("这是子类覆写以后的方法");
}
}
public class Fuxie{
public static void main(String[] args){
Student stu=new Person();//向下转型:将小类型转化为大类型
stu.print();
}
}
通过上面的栗子我们可以看出向下转型会报错:类型不兼容,因为子类相当于是小类型,父类相当于是大类型,大类型转小类型是不可以的,所以会提示类型不兼容。下面我们来看一下强转可以不。
(3)强转
//(3)强转
class Person{
public void print(){
System.out.println("这是父类的方法");
}
public void print2(){
System.out.println("这是父类的第二个方法");
}
}
class Student extends Person{
public void print(){
System.out.println("这是子类覆写以后的方法");
}
}
public class Fuxie{
public static void main(String[] args){
Student stu=(Student)new Person();//向下转型:将小类型转化为大类型——》强转
stu.print();
}
}
通过上面的栗子我们可以看出来经过强转了以后我们的编译是可以通过的,我们的逻辑是没有问题的,但是运行的时候仍然会出错,是因为要向下转型必须先经过向上转型以后才是可行的。
(4)看起来像是对的,其实是错的
class Person{
public void print(){
System.out.println("这是父类的方法");
}
public void print2(){
System.out.println("这是父类的第二个方法");
}
}
class Student extends Person{
public void print(){
System.out.println("这是子类覆写以后的方法");
}
}
public class Fuxie{
public static void main(String[] args){
person per = new Student();//向上转型
Student stu=per;
}
}
上面的程序看起来像是自己创建自己的程序,但其实刚开始是经过了一次向上转型,转型完以后,我们的程序还没有执行,那么这是执行下面的 Student stu=per;这条语句的时候,per的类型还不是Student这个类型,还是原来修饰它的那个类型Person,所以下面的这个运行会出错。类型不兼容。
对于上面的情况我们也可以举一个基本数据类型的栗子来看看:
//举一个基本数据类型看看
public class Fuxie{
public static void main(String[] args){
//第一种方法:将一个整数直接赋给byte变量
byte b=20;
System.out.println(b);
//第二种方法:将一个整数先赋给int型变量,再利用这个变量给byte型变量赋值
int a=10;
byte b=a;
System.out.println(a);
}
}
对于我们的基本数据类型也是一样的,如果上面的代码中我们先将一个整数赋值给int变量,然后再通过这个整形变量给byte型变量赋值的时候编译时会提示我们说类型不兼容,其实这个和我们的对象向下转型的强转类型是一个道理,看起来我们是将一个整数付给了byte,但是由于编写后还没有分配空间,在编译的时候只是按照前面提示的类型进行判断,并不会分配空间(注意:只要程序还没有运行就还没有分配空间),所以在编译的时候认为这个a就是int型的,所以我们的编译都不可以通过;但是基本数据类型经过强转以后是可以正常执行的,但是对于类是不行的。
//经过强转
public class Fuxie{
public static void main(String[] args){
//第一种方法:将一个整数直接赋给byte变量
byte b=20;
System.out.println(b);
//第二种方法:将一个整数先赋给int型变量,再利用这个变量给byte型变量赋值
int a=10;
byte c=(byte)a;
System.out.println(c);
}
}
二、解决向下转型存在的安全隐患
我们在生活中做一个事情的时候会先判断它的好坏,在向下转型的时候我们先对它进行判断,然后再进行转型,这个实现的过程通过instanceof来实现,语法如下:
子类对象 instanceof 类,返回boolean类型
观察instanceof操作:
class Person{
public void print(){
System.out.println("这是父类的方法");
}
}
class Student extends Person{
public void print(){
System.out.println("这是子类经过覆写以后的方法");
}
}
public class Fuxie{
public static void main(String[] args){
Person per=new Student();//实现了向上转型
System.out.println(per instanceof Person);//判断是真还是假
System.out.println(per instanceof Student);
if(per instanceof Student){//避免ClassCastException
Student stu=(Student)per;//实现向下转型,因为在向下转型之前已经有了向上转型,所以
//经过了向上转型,所以才可以成功。
stu.print();//最后经过调用的方法是经过子类覆写以后的方法
}
}
}
通过instanceof这个关键字可以让我们在进行向下转型的时候保证这个一定是经过了向上转型的。这样转型才可以成功。
对于向上转型、向下转型有什么意义:
//向上转型的应用
class Person{
public void print(){
System.out.println("我是人类");
}
}
class Student extends Person{
public void print(){
System.out.println("我是学生");
}
}
class Worker extends Person{
public void print(){
System.out.println("我是工人");
}
}
public class Fuxie{
public static void main(String[] args){
whoyu(new Student());
whoyu(new Worker());
}
public static void whoyu(Person per){//传参:Person per=new Student();实现向上转型
per.print();
}
}
通过上面的实例,可以看到向上转型可以实现对象的传参,可以实现传参后参数的统一。
三、多态的总结:
1.多态的核心是在于方法覆写这一块。
2.向上转型可以实现传参参数的统一化。
3.两个没有关系的类是不可能实现转型的,会报错:ClassCastException。