面向对象的三大特征:
1. 封装 2. 继承 3. 多态
多态:
什么叫多态:一个对象具备多种形态,也可以理解为事物存在的多种体现形态(父类的引用类型变量指向了子类对象,或者是接口的引用类型变量指向了接口实现类的对象)
多态的前提:必须存在继承或者实现关系。
多态要注意的细节:
1. 多态情况下,子父类存在同名的成员变量时,访问的是父类的成员变量。
2. 多态情况下,子父类存在同名的非静态的成员函数时,访问的是子类的成员函数。
3. 多态情况下,子父类存在同名的静态的成员函数时,访问的是父类的成员函数。
4. 多态情况下,不能访问子类特有的成员。
总结:多态情况下,子父类存在同名的成员时,访问的都是父类成员,除了在同名非静态函数时才是访问子类的。
abstract class Animal{
String name;
static String color="动物色";
public Animal(String name){
this.name=name;
}
public abstract void run();
public void eat(){
System.out.println("动物在吃东西");
}
}
//老鼠
class Mouse extends Animal{
static String color="老鼠色";
public Mouse(String name){
super(name);
}
public void run(){
System.out.println(name+"四条腿,灵活的走位");
}
public void eat(){
System.out.println("老鼠在吃东西");
}
public void dig(){//老鼠特有的方法
System.out.println("老鼠在打洞");
}
}
class Fish extends Animal{
public Fish(String name){
super(name);
}
public void run(){
System.out.println(name+"在海中快速的游行");
}
}
public class Demo4 {//多态
public static void main(String[] args) {
Animal a=new Mouse("老鼠" );
System.out.println(a.color);
a.eat();
}
}
编译看左边,运行不一定看右边
编译看左边:java编译器在编译的时候,会检查引用类型变量所属的类是否具备指定的成员,如果不具备,马上编译报错。
普通的情况都是看左边,只有非静态函数的时候才是看右边的
凡是引用类型变量,都是记录了一个内存地址而已,指向那边。
多态的应用场景:
1. 多态用于形式参数类型的时候,可以接受更多类型的数据 。
2. 多态用于返回值类型的时候,可以返回更多类型的数据。
多态的好处:提高了代码的拓展性。
需求1:
定义一个函数,可以接受任意类型的图形对象,并且打印图形面积与周长
abstract class MyShape{
public abstract void getArea();
public abstract void getLength();
}
class Circle extends MyShape{
public static final double PI=3.14;
double r;
public Circle(double r){
this.r=r;
}
public void getArea(){
System.out.println("圆形的面积"+PI*r*r);
}
public void getLength(){
System.out.println("圆的周长"+2*PI*r);
}
}
class Rect extends MyShape{
int width;
int height;
public Rect(int width,int height){
this.width=width;
this.height=height;
}
public void getArea(){
System.out.println("矩形的面积是"+width*height);
}
public void getLength(){
System.out.println("矩形的周长是"+(width+height)*2);
}
}
public class Demo5 {
public static void main(String[] args) {
Circle c=new Circle(2.0);
print(c);
}
public static void print(MyShape s){ //MyShape s=new Circle(4.0);
s.getArea();
s.getLength();
}
}
需求2:定义一个函数可以返回任意类型的图形对象。
public class Demo5 {
public static void main(String[] args) {
MyShape m=getShape(0);//调用了使用多态,定义的变量类型要以返回值类型一致。
m.getArea();
m.getLength();
}
public static MyShape getShape(int i){
if(i==0){
return new Circle(4.0);
}
else{
return new Rect(3,4);
}
}
}
需求3:
创建三个类,三个类分别是Pet,Cat,TestPetCat,父类为Pet,定义name,age属性并封装取来,Cat继承Pet,然添加一个color属性,在主函数TestPetCat输出。(利用多态)
public class Pet {
private String name;
private int age;
public Pet(){
super();
}
public Pet(String name,int age){
super();
setName(name);
setAge(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 0 && age < 10) {
this.age = age;
} else {
System.out.println("年龄不合法");
}
}
public void show(){
System.out.println("名字:"+getName()+",年龄:"+getAge());
}
}
public class Cat extends Pet{
private String color;
public Cat(){
super();
}
public Cat(String name,int age,String color){
super(name,age);
setColor(color);
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public void show(){
System.out.println("花色:"+getColor());
}
}
public static void main(String[] args) {
//使用父类类型的引用指向父类自己的对象,没有多态
Pet p1=new Pet();
//无论是否重写show()方法,都调用Pet类中的show方法
p1.show();
System.out.println("----------------------");
//使用子类类型的引用指向父类自己的对象,没有多态
//当子类重写show()方法后,则调用子类中重写以后的方法
Cat c1=new Cat("小花",2,"白色");
//调用Pet类中的show()方法
c1.show();
System.out.println("----------------------");
//父类类型的引用指向了子类的对象,形成了多态
Pet pc=new Cat("喵咪",3,"咖啡色");
//调用Pet类的show方法
//当子类重写show()方法后,在运行阶段最终调用了子
类重写以后
的版本
//在编译阶段还是调用父类的方法,运行阶段调子类
pc.show();
}
Pet pc=new Cat();
pc.show();
解析:
对于上述代码来说,采用父类的引用指向子类的对象后:在编译阶段 pc还是父类类型的,因此只可以调用父类的show()方法:在运行阶段pc是子类类型的,因此最终调用了子类重写以后的show()方法。
多态的效果:
1.当父类的引用指向子类的对象时,该引用可以直接调用父类的成员方法,但是不可以直接
调用子类的成员变量
2.对于父子类都拥有的非静态成员方法来说,编译阶段调用父类的,
运行阶段调用子类的
3.对于父子类都拥有的静态成员方法来说,编译和运行阶段调用父类的版本,与对象无关。
引用数据类型之间的转换
1.引用数据类型之间的转换分为俩种,自动类转换和强制类型转换;
- 自动类型转换主要指从小范围到大范围之间的转换,也就是子类向父类的转换。(Pet pc=new Cat() 这是自动类型转换)
- ((Cat)pc).getColor();父类到子类的转换,强制类型转换主要指从大范围到小范围之间的转换,也就是父类向子类的转换。
2.引用数据类型之间的转换必须发生在父子类之间,否则编译报错。
3.拥有父子类关系后可以发生强制类型转换,若该引用真正指向的对象并不是目标类型的对象时,编译阶段ok,运行阶段发生类型转换异常。
4.为了避免上述错误的发生,每次强转之前,应该使用instanceof进行判断,格式如下:
if(引用变量名 instanceof 目标类型)— 判断引用变量指向的对象是否为目标类型
如果用多态,就是父类的引用指向子类,而父类的引用能直接访问父类的方法,若想访问子类的,需要加个强转
多态的实际意义:
多态的实际意义在于屏蔽不同子类的差异性实现通用的编程
public class TestShape {
//自定义成员方法实现将参数指定矩形对象的特征打印出来
//Recr rt=new Rect(1,2,3,4);
//矩形类型的引用指向矩形类型的对象,没有多态
//矩形类型的引用指向矩形类型的对象,没有多态
public static void drowRect(Rect rt){
//调用矩形类中的show方法
rt.show();
}
//圆形类型的引用指向圆形类型的对象,没有多态
public static void drowCircle(Circle ce){
ce.show();
}
//可能一会传圆形一会传矩形
//父类类型的引用指向了子类的对象,形成了多态
public static void draw(Shape s){
//编译阶段调用父类的方法,运行阶段调用子类重写以后的方法
//Shape s=new Rect(1,2,3,4)
//Shape s=new Circle(4,4,6)
s.show();
}
public static void main(String[] args){
TestShape.drowRect(new Rect(1,2,3,4));//Rect rt = new rect(1,2,3,4);
System.out.println("---------------");
TestShape.drowCircle(new Circle(5,6,2.0));
System.out.println("---------------");
TestShape.draw(new Rect(1,2,3,4));
System.out.println("---------------");
TestShape.draw(new Circle(4,4,6));//shape s = new circle();
}
}
主板运行网卡声卡的案例:
interface PCI{
public void open();
public void close();
}
class MainBpard {
public void run(){
System.out.println("主板运行成功-------");
}
public void userPCI(PCI p){
if (p!=null){
p.open();
p.close();
}
}
}
class NetCard implements PCI{
public void open(){
System.out.println("netcard open");
}
public void close(){
System.out.println("nedcard close");
}
}
class SoundCard implements PCI{
public void open(){
System.out.println("SoundCard open");
}
public void close(){
System.out.println("SoundCard close");
}
}
public class Demo {
public static void main(String[] args) {
MainBpard m=new MainBpard();
m.run();
m.userPCI(new NetCard());
m.userPCI(new SoundCard());
}
}