Java入门(二十四)
多态
- 多态可以实现动态变量,程序的最终状态只有在执行的过程中可以决定。动态编译:可扩展性更强,多态可以让我们不用管写的程序是什么状态。
- 新建一个包,继续Student继承Person类,相当于学生现在也是一个人了。
- 如果new Person,那么他出来的东西一定是Person类的。如果new Student,那他肯定是Student类的,不会再有其他东西,一个对象的实际类型他是确定的。类型确定,但是他可以指向引用类型就不确定的了。
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
Person s2 = new Student();
}
}
- 为什么new 了一个Student,他还是Person呢?因为有父子关系,这叫父类的引用指向子类。同样都是学生,但他表现出来的有多种状态。除此之外,Student还可以变成Object类。
- 一个类的实际对象是一定的,但是可以指向这个引用对象的类型却是任意的父类型。
- 比如去Person类中写一个方法
public class Person {
public void run(){
System.out.println("run");
}
}
- 再去看看能不能调用它
- 说明正常执行,虽然new的是Student,但是他run走了父类的方法,因为子类继承了父类全部的方法。
- 我们来重写一下父类的方法
public class Student extends Person{
@Override
public void run() {
System.out.println("son");
}
}
- 可以发现他两个都走了son,因为他一旦子类重写了父类的方法,那就是执行子类的方法。因为现在调用到的方法是Student,虽然类型是Person。我们再Student类加个eat方法,而父类并没有这个方法。
public class Student extends Person{
@Override
public void run() {
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
- 现在s2可以调eat方法吗?可以看到idea直接报错了。因为s2他的类型是Person,Person里面没有eat方法所以调不了。但是如果Person里面跟Student都有的情况下,只要子类没有重写父类,他就调用父类的。如果子类重写了,就调用子类的。对象能执行哪些方法主要看左边的类型,右边关系不大。
- 子类的引用和父类的引用指向是不一样的,Student是子类指向引用,Person是父类指向引用,他们之间有区别。父类虽然可以指向子类,但是不能调用子类独有的方法。
- 类型转换,把s2.eat()的选项选中,然后idea自动帮你转换。强制转换Person成Student,高类型转换到低类型需要强制转换。
((Student)s2).eat();
- 多态注意事项:多态是方法的多态,属性没有多态。父类和子类需要有父子关系,假如没有关系的转换,会报类型转换异常(ClassCastException)。多态存在的条件,得有继承关系,方法需要重写,两个类型都有的时候他要执行子类的。父类的引用指向子类对象,father指向son----father f1 = new son();
- static方法属于类,不属于实例,不能重写。final是常量,被final修饰的无法重写,在常量池的。
- private,私有的也不能重写。不能实现多态。
- 多态就是统一方法可以根据发送对象的不同而采用多种不同的行为方式。同样都是run,通过s1、s2调用出来的可能产生不同的结果。子类重写父类,s1 跟 s2都走的子类了,只有运行期间才能动态地去确定。多态使程序变得更灵活。
instanceof
- instanceof关键词,java判断一个对象是什么类型,可以通过此关键词判断两个类之间是否存在父子关系。有父子关系就ok,没有就不行
- 写Teacher类和Student类继承Person类。
public class Application {
public static void main(String[] args) {
Object object = new Student();
System.out.println(object instanceof Student);
System.out.println(object instanceof Person);
System.out.println(object instanceof Object);
System.out.println(object instanceof Teacher);
System.out.println(object instanceof String);
}
}
- 假设再来new,把Object转换为Person
public class Application {
public static void main(String[] args) {
Person person = new Student();
System.out.println(person instanceof Student);
System.out.println(person instanceof Person);
System.out.println(person instanceof Object);
System.out.println(person instanceof Teacher);
System.out.println(person instanceof String);
}
}
- 发现上面代码从编译开始就报错了,Person和String没有关系,是不能进行比较和转换的。instanceof是有联系才可以
- 注释掉报错那一行之后,结果如下
- 假设我们再把他变成一个单纯的Student,学生跟老师是同级的,编译报错的就有Teacher和String,他是一个比较转换的过程,肯定是两边有关系的才会返回true跟false的结果,如果没有关系就直接报错。下面都是true。
Student student = new Student();
System.out.println(student instanceof Student);
System.out.println(student instanceof Person);
System.out.println(student instanceof Object);
-
公式:System.out.println(X instanceof Y) 这个代码是否能编译通过,取决于X和Y之间是否存在父子关系,如果没有父子关系编译就会报错。true和false主要看X所指向的类型是否为Y的子类型。X和Y有关系他也会通过。
-
现在我想给他进行转换,现在Student类有自己的go方法和继承了父的run方法,我想进行操作。
-
现在这两个之间我想给他做一个类型的转换,基本类型高转低需要强转,低转高则不需要。这边的引用类型存在一个父跟子的类型。父就是代表高的,子代表低的。
-
我们new 一个Student类,这个Student类可以直接变成Person类,因为是低转高。那现在Student可以执行go方法吗?
-
是不行的,Person类里面是run,没有go这个方法,Student里面才有这个go。为了执行go,需要想办法把这个student转换为Student类型,就可以使用go方法。
-
Person往Student转是高往低转,要强制转换。
Person obj = new Student();
Student student = (Student)obj;
student.go();
- 把两句话写成一句,使用子类的方法把他强转成子类
Person obj = new Student();
((Student) obj).go();
- 假如我重新new 一个Student的对象,我想把他变成Person类型那怎么办?可以直接低转高,但是他的go方法就执行不了了。
- 如果你要把子类型转换成一个高类型,子类转换成父类,他有可能丢失一些方法。
- 父类引用指向子类对象,子类引用不能指向父类对象。
- 把子类转换为父类就要向上转型,不用强制转换。
- 如果想父类转换成子类,向下转型,就要强制转换。可能会丢失方法。并不是很好
- 方便方法的调用,减少重复的代码,提升利用率,代码简洁。