面向对象之多态
面向对象三大特性
1.多态
-
什么是多态
- 即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
- 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)
- 好处:动态编译,类型可扩展性更强
-
多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
-
不能重写的方法:
- 1.static 方法,属于类,它不属于实例:静态的方法随着类加载,不属于实例化的对象,不在堆中
- 2.final 常量 ,被final修饰的无法修改,属于常量池
- 3.private 私有方法,不能被重写
-
注意事项:
- 1.多态是方法的多态,属性没有多态性
- 2.父类和子类,有联系才能转换,不然会异常!类型转换异常:ClassCastException
- 3.存在条件:继承关系,方法需要重写,父类引用指向子类对象!Father f1 = new son();
- 4.父类型的引用可以指向new出的子类型的对象,但不能调用子类中的方法,如果需要会进行强制类型转换,父类转子类后调用。
- 5.具有继承关系的父子类,父类中的public非静态方法在子类中被重写,则用父类型的引用和子类型的引用去调用都是输出子类中重写后的方法。
- 6.对象能执行哪些方法,主要看对象左边的类型,和右边关系不大!
1.1 多态代码演示:
package oopdemo01.demo07;
public class Person {
public void run(){
System.out.println("老子跑了起来");
}
}
package oopdemo01.demo07;
public class Student extends Person{
@Override //子类重写
public void run() {
System.out.println("小子跑了起来");
}
// 子类独有的方法,父类不能调用
public void eat(){
System.out.println("吃饭");
}
}
package oopdemo01.demo07;
public class Application {
public static void main(String[] args) {
// 一个对象的实际类型是确定的,new出的是什么对象,就是什么对象
// 但可以指向的引用类型就不确定了,父类的引用指向子类
//new Student();
//new Person();
//student 子类能调用的方法都是自己的或继承父类的
Student s1 = new Student();
//父类可以指向子类,但是不能调用子类独有的方法
Person s2 = new Student();
Object s3 = new Student();
//对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
s1.run(); //父类有,子类没有,子类继承父类方法
s2.run(); //子类重写后,执行子类方法
s1.eat(); //子类独有方法
((Student)s2).eat(); //父类不能调用子类独有方法,但可以强制转换为子类后使用
}
}
运行结果
小子跑了起来
小子跑了起来
吃饭
吃饭
2.instanceof和类型转换
-
instanceof:判断一个对象是什么类型。(类型转换—引用类型之间的转换)
- System.out.println(x instanceof y) :true or false (能不能编译通过,看x所指向的实际类型是不是y的子类型)
- 1.能否编译通过,看类,X与Y的类(实际 X与Y本身就是 类) 若存在父子或子父关系则可编译通过
- 2.若X与Y没有父子关系如teacher 与student ,就不可编译
- 3.编译后 是true或flase 看引用指向对象,X指向的对象如果是后面y的子类,既是true
-
类型转换:父类可以指向子类,但是不能调用子类独有的方法,如果需要调用子类独有的方法,则需要强制类型转换(向下转型:高->低)
-
类型转换的特点
-
1.父类引用指向子类的对象
-
2.把子类转换为父类,向上转型; 可能会丢失某些方法
-
3.把父类转换为子类,向下转换: 强制转换。
-
4.好处:方便方法的调用,减少重复的代码!简洁代码
-
-
注意: 子转父自动向上转换会丢失某些子类中的方法,子列中的某些方法可能无法通过转换后的引用名来调用,需要向下再强制转换为子类才能调用子类中丢失的某些方法。
2.1 instanceof和类型转换代码演示:
package oopdemo01.demo8;
public class Person {
public void run(){
System.out.println("老子跑了起来");
}
}
package oopdemo01.demo8;
public class Student extends Person{
public void go(){
System.out.println("小子跑了起来");
}
}
package oopdemo01.demo8;
public class Teacher extends Person {
}
package oopdemo01.demo8;
public class Application {
public static void main(String[] args) {
//object>String
//object>Person>Teacher
//object>Person>Student
// X instanceof Y 比较之间是否存在父子关系
Object object = new Student();
System.out.println(object instanceof Student); //true
System.out.println(object instanceof Person);//true
System.out.println(object instanceof Object);//true
System.out.println(object instanceof Teacher);//false
System.out.println(object instanceof String);//false
System.out.println("======================================");
Person person = new Student();
System.out.println(person instanceof Student); //true
System.out.println(person instanceof Person);//true
System.out.println(person instanceof Object);//true
System.out.println(person instanceof Teacher);//false
//System.out.println(person instanceof String);//编译报错
System.out.println("======================================");
Student student = new Student();
System.out.println(student instanceof Student); //true
System.out.println(student instanceof Person);//true
System.out.println(student instanceof Object);//true
// System.out.println(student instanceof Teacher);//编译报错
// System.out.println(student instanceof String);//编译报错
//类型之间的转化 : 父---子
//高 低
Person s1 = new Student();
//高转低可以直接转;低转高,需要强制转
Student student1 = (Student) s1;
student1.go();
((Student) s1).go();//上述简便写法
Student student2 = new Student();
Person person1=student2;
person1.run();//子转父自动向上转换会丢失某些子类中的方法,子列中的某些方法可能无法通过转换后的引用名来调用,
// 需要向下再强制转换为子类才能调用子类中丢失的某些方法。
/*
类型转换的特点:
1.父类引用指向子类的对象
2.把子类转换为父类,向上转型; 可能会丢失某些方法
3.把父类转换为子类,向下转换: 强制转换
4.方便方法的调用,减少重复的代码!简洁代码
* */
/*
1.能否编译通过,看类,X与Y的类(实际 X与Y本身就是 类) 若存在父子或子父关系则可编译通过
2.若X与Y没有父子关系如teacher 与student ,就不可编译
3.编译后 是true或flase 看引用指向对象,X指向的对象如果是后面y的子类,既是true
*/
}
}
运行结果
true
true
true
false
false
======================================
true
true
true
false
======================================
true
true
true
小子跑了起来
小子跑了起来
老子跑了起来
————————————————————————————————————————————————————————————————————————————
3.static关键字详解
-
static:Static加上方式上叫静态方式,加上属性上叫静态属性。
- 1.static 跟类一起产生:静态方法在类的时候就已经加载了
- 2.静态方法不能调用非静态方法,非静态方法可以调用静态方法
- 3.类可以直接调用静态static变量和static方法
- 4.类的对象可以调用static和非static方法和变量
-
代码块和静态代码块加载顺序
-
1. 静态代码块,且仅执行一次,可用来赋初值。跟类一起产生,永久只执行一次
-
2. 匿名代码块跟对象同时产生,在构造器之前
-
3. 构造方法
-
-
特别注意:final之后,断子绝孙(被final修饰的类不可再被继承)
3.1 关于static代码演示:
package oopdemo01.demo09;
//静态代码块
public class Person {
{
//2.第二加载代码块(匿名代码块) 跟对象同时产生,在构造方法之前
System.out.println("匿名代码块");
}
static {
//1.第一加载静态代码块,仅执行一次,可用来赋初始值
System.out.println("静态代码块");
}
public Person() {
//3.第三加载
System.out.println("构造方法");
}
public static void main(String[] args) {
Person person = new Person();
System.out.println("====================");
Person person1 = new Person();
}
}
运行结果
静态代码块
匿名代码块
构造方法
====================
匿名代码块
构造方法
package oopdemo01.demo09;
//静态方法
public class Student {
private static String name;
private double score;
public void run(){
System.out.println("跑起来");
go();//静态的随着类一起加载
}
public static void go(){
System.out.println("走起来");
//run(); 报错
// 静态可以调用静态
// 非静态可以调用静态
// 静态不可以调用非静态
}
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(Student.name);
//System.out.println(Student.score); // 报错
System.out.println(s1.score);
s1.go();
Student.go();
go();//和student.go() 效果一样
System.out.println(Student.name);
}
}
运行结果
null
0.0
走起来
走起来
走起来
null
package oopdemo01.demo09;
//静态导入包
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(Math.random()); //没有静态导入包时
System.out.println(Math.PI);
System.out.println("=============");
System.out.println(random());//省略Math 类直接用方法
System.out.println(PI);
}
}
运行结果
0.5071520544914102
3.141592653589793
=============
0.7416491333763838
3.141592653589793