拿捏Java:多态

51 阅读8分钟

1、多态

   表现形式:父类类型 对象名称=子类名称;
   前提:有继承关系;
        有父类引用指向子类对象;Fu f=new Zi();
        有方法重写;
   调用成员的特点:
       变量调用:编译看左边:javac编译代码时,会看左边的父类有没有这个变量,如果有,编译成功,如果没有,编译失败;
                运行也看左边:java运行代码时,实际获取的是左边父类中成员变量的值       
       方法调用:编译看左边:javac编译代码时,会看左边的父类有没有这个方法,如果有,编译成功,如果没有,编译失败;
                运行看右边:java运行代码时,实际运行的是子类中的方法
       因为:成员变量:在子类的对象中,会把父类的成员变量也继承下来,父:name 子:name
            成员方法:如果子类对父类的成员方法进行了重写,虚方法表中父类的方法就会被覆盖

2、弊端

不能调用子类的特有功能(父类中没有的方法):因为编译的时候会先检查左边的父类中没有该方法,如果没有直接报错。
解决方案:变回子类型,强制类型转换
例如:Animal a=new Dog();
    a.lookHome();//不能运行,因为lookHome是属于Dog类的特殊方法
    强制类型转换:
    Dog d=(Dog) a;
    a.looHome();//可以正常运行
    Cat d=(Cat) a;//不能正常运行,因为a是Dog类型的,不能瞎转

3、instanceof:判断类型

普通版:
    变量名 instanceof 类名:如果是该变量是该类型,结果是true;如果不是,结果是false
升级版:
    变量名1 instanceof 类名 变量名2:如果是变量1是该类型,则强转为该类型,并且重新命名为变量2,结果是true;如果不是,则不进行强转,结果是false

4、包 package

包名规则:公司域名反写+包的作用,全部英文小写  com.itheima.domain
使用同一个包中的类,不需要导包
使用java.lang包中的类的时候,不需要导包
其他情况都需要导包
同时使用两个包中的同名类,需要用全类名

5、final可修饰

1、方法:该方法是最终方法,不能被重写
2、类:该类是最终类,不能被继承
3、变量:叫做常量,只能被赋值一次,
        常量命名规范:单个单词需全部大写;
                     多个单词需全部大写,单词之间用下划线隔开
4、若final修饰的变量是基本数据类型,那么变量存储的数据值是不变的
   若final修饰的变量是引用类型,那么变量存储的地址值是不变的,对象内部可以改变

6、权限修饰符

private(自己用)<空着不写,默认(只能本包使用)<protected(受保护的,同一个类+不同包的子类+同一个包下的其他类 可用)<public(公共的)

7、代码块

1、局部代码块:在方法内的代码块,加大括号,提前结束变量的生命周期,变量只在所属的大括号内有效
2、构造代码块:写在成员位置,将构造函数中的公共部分提取出来,放入大括号中;创建本类的对象时,先执行构造代码块再执行构造方法
3、静态代码块:static{},随着类的加载而加载,自动触发,只执行一次
              在类加载的时候,可以做一些数据初始化

8、抽象类和抽象方法

1、抽象方法:将共性的方法抽取到父类时,每一个子类执行的方法是不同的,因此需要在子类中重写该方法,在父类中不能确定该方法的具体内容,该方法可以定义为抽象方法;
2、抽象类:如果一个类中有抽象方法,该类必须声明为抽象类
定义格式:public abstract 返回值类型 函数名(参数列表);
         public abstract class 类名{}
注意事项:1、抽象类不能创建对象
         2、抽象类中不一定有抽象方法,有抽象方法的一定是抽象类
         3、有构造方法:该方法的作用是当创建子类对象时,给属性进行赋值的
         4、抽象类的子类:要么重写所有的抽象方法,要么该类是抽象类

9、接口:对行为的抽象

public **interface** 接口名{}
接口不能实例化
public class 类名 **implements** 接口名{}
接口的实现类:要么实现接口的所有方法,要么抽象类

一个类可以实现多个接口:
public class 类名 implements 接口1 接口2 {}
public class 类名 extends 类名 implements 接口1 接口2 {}

成员变量:只能是常量,默认修饰符为public static final
构造方法:没有
成员方法:只能是抽象方法,默认修饰符为public abstract

接口与接口之间的关系:可以单继承,也可以多继承,类实现最后一个接口时,必须实现子接口和父接口中的所有抽象类

JDK8开始接口中新增的方法:
    JDK7前,接口中只能定义抽象方法
    JDK8添加的方法:接口中可以定义有方法体的方法
        1、默认方法(必须使用default修饰)
        默认方法:public default 返回值类型 方法名(参数列表){}
        默认方法不是抽象方法,不会被强制重写,但若要重写,需要去掉default
        如果实现了多个接口,多个接口中存在相同名字的默认方法,那么子类就必须重写该方法
        2、静态方法(必须使用static修饰)
        public static 返回值类型 方法名(参数列表){}
        静态方法只能通过接口名调用,不能通过实现类名和对象名调用
        静态方法不能被重写
        
    JDK9添加的方法:接口中可以定义私有方法,该方法只为这个接口使用,不被其他的类使用
        给默认方法服务:private 返回值类型 方法(){}
        给静态方法服务:private static 返回值类型 方法(){}
        
 当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称为接口多态

10、适配器设计模式:解决接口和接口实现类之间的矛盾问题

设计模式就是解决各种问题的套路
当一个接口中抽象方法比较多的时候,但我只要使用其中一个方法的时候,可以使用适配器模式
实现方法:1、编写中间类XXXAdapter实现接口
         2、对接口中的抽象方法进行空实现
         3、让真正的实现类继承中间类,并重写需要用的方法
         4、为了防止其他类创建适配器类的对象,要用abstract修饰适配器类

public interface inter{
    public void method1(){}
    public void method2(){}
    public void method3(){}
    public void method4(){}
    public void method5(){}
    public void method6(){}
    public void method7(){}
}

适配器:
public abstract class interAdapter implements inter{
//不让外界创建该类的对象
    public void method1(){}
    public void method2(){}
    public void method3(){}
    public void method4(){}
    public void method5(){}
    public void method6(){}
    public void method7(){}
    }
    
public class interImpl extends interAdapter{
    //要用到哪个方法就实现哪个方法
    public void method5(){
        system.out.println("具体实现method5");
    }
}

11、内部类

分为4种:成员内部类,静态内部类,局部内部类,匿名内部类
在一个类的内部定义一个类,称为内部类;
内部类表示的事物是外部类的一部分;
内部类单独出现没有意义;    

public class car{
//外部类
    String carName;
    String carColor;
    int carAge;
    
    class engine{
        //内部类
        String engineName;
        int engineAge;
    }
}

内部类访问特点:
1、内部类可以直接访问外部类的成员,包括私有
2、外部类要访问内部类,必须创建内部类的对象

public class outer{

    private int num=10;
    class inner{
        private int num=20;
        public void show(){
            int num=30;
            sout(num);//30
            sout(this.num);//20
            sout(outer.this.num);//10
        }
    }
    
    public inner getIntance{
        return new inner();
    }
}

创建成员内部类的对象:
1、直接创建:outer.inner oi=new outer().new inner();
2、若成员内部类是private,外界要访问成员内部类,需要在外部类创建方法,向外界提供内部类的对象
3、JDK16之前不可以在内部类里面定义静态变量,JDK16之前可以在内部类里面定义静态变量

静态内部类
创建内部类对象的方法:外部类.内部类 对象名=new 外部类.内部类();
若要访问外部类的静态成员和静态方法,则外部类.内部类.方法名;
若要访问外部类的非静态成员和方法,则需要提前创建对象,用对象访问

局部内部类
1、将内部类定义在方法里面
2、外界无法直接使用,需要在内部类里面创建对象
3、该类可以直接访问外部类的成员,也可以访问方法内的变量

匿名内部类:没有名字的类,可以写在成员位置,也可以写在局部位置
new 类名或者接口名(){
    重写接口中的所有方法;
};
包含继承或实现,方法重写,创建对象
整体就是一个类的子类对象或者接口的实现类对象
当方法的参数是接口或者类时,可以使用匿名内部类的对象作为参数传递