JAVA笔记

JAVA多态

2018-11-04  本文已影响0人  Young_Allen

每天一篇系列:
强化知识体系,查漏补缺。
欢迎指正,共同学习!

多态

继承只能单继承,不能同时继承多个父类(extends),但是可以有多个接口(implements)。
接口源于抽象的设计,首先看看什么是抽象:
抽象方法必须用abstract关键字进行修饰

abstract class TEST2 {
    public abstract void fun();
}

如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰,否则JAVA会提示编译报错。
抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
有了抽象类,子类就可以继承并且实现抽象方法来实现多态。

抽象类是对一种事物的抽象,接口是对一类行为的抽象:
接口关键字是interface:

public interface TEST3 {
    public int param1 = 0;
    //private int param2 = 0; // erro : Illegal modifier for the interface field TEST3.param2; only public, static & final are permitted
    public static final int param3 = 0;
    
    public abstract void fun();
    //private abstract void fun2();// erro : Illegal modifier for the interface method fun2; only public & abstract are permitted
}

接口中可以含有变量和方法。但是要注意,接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误,Illegal modifier for the interface field TEST3.param3; only public, static & final are permitted,注意public是没有编译错误的),而方法会被隐式地指定为public abstract方法且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误),并且接口中所有的方法不能有具体的实现,也就是说,接口中的方法必须都是抽象方法。从这里可以隐约看出接口和抽象类的区别,接口是一种极度抽象的类型,它比抽象类更加“抽象”,并且一般情况下不在接口中定义变量。

另外内部类也是实现多态的一种方式:
内部类是定义在另一个类里面或者方法里面的类定义。包括成员内部类、局部内部类、匿名内部类和静态内部类。
1.成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
如果外部类要访问成员内部类的方法和属性,必须先创建外部类对象后创建内部类对象。(对于这点的理解可以参考:https://www.cnblogs.com/dolphin0520/p/3811445.html)

2.局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。

class People{
    public People() {
         
    }
}
 
class Man{
    public Man(){
         
    }
     
    public People getWoman(){
        class Woman extends People{   //局部内部类
            int age =0;
        }
        return new Woman();
    }
}

3.匿名内部类没有方法名的内部类(匿名内部类是唯一一种没有构造器的类),在Android中有很多匿名类的使用,比如clinklistenner的事件注册,广播事件的注册等。但是匿名内部类在某些情况下不能很好的管理,比如广播注册后的销毁动作就不能完成,这样就可能导致内存溢出问题。
4.静态内部类
可能是和成员内部类一起考察的最多的知识点。
静态内部类可以看成是加了static关键字的成员内部类,静态内部类可以不依赖外部类,并且不能调用外部类的非static成员变量或方法(因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象)。

因为类只可以单继承,但每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整。

从静态内部类来了解下static关键字的作用。
在《Java编程思想》P86页有这样一段话:

“static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。”

因为它不依附于任何对象,既然都没有对象,就谈不上this了,简而言之,一句话来描述就是:方便在没有创建对象的情况下来进行调用(方法/变量)。

public class TEST5 {
    private static int param1 = 0;
    private int param2 = 0;
    
    public TEST5(){
        
    }
    
    public void fun1(){
        System.out.println(param1);
        System.out.println(param2);
        fun2();
    }
    
    public static void fun2(){
        System.out.println(param1);
        //System.out.println(param2);   // erro : Cannot make a static reference to the non-static field param2
        //fun1();   // erro : Multiple markers at this line
                // - Occurrence of 'fun1'
                // - Cannot make a static reference to the non-static method fun1() from the type 
    }
}

假如说可以在静态方法中访问非静态方法/变量的话,那么如果在main方法中有下面一条语句:

TEST5.fun2();

此时TEST5对象都没有创建和初始化,param2根本就不存在,所以就会产生矛盾了。同样对于方法也是一样,由于你无法预知在fun1方法中是否访问了非静态成员变量,所以也禁止在静态成员方法中访问非静态成员方法。

public class Test {
    Person person = new Person("Test");
    static{
        System.out.println("test static");
    }
     
    public Test() {
        System.out.println("test constructor");
    }
     
    public static void main(String[] args) {
        new MyClass();
    }
}
 
class Person{
    static{
        System.out.println("person static");
    }
    public Person(String str) {
        System.out.println("person "+str);
    }
}
 
 
class MyClass extends Test {
    Person person = new Person("MyClass");
    static{
        System.out.println("myclass static");
    }
     
    public MyClass() {
        System.out.println("myclass constructor");
    }
}

注意这段代码的输出:

test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor

说明了对象的成员变量是在创建对象时(调用构造函数前)初始化的。注意父类和子类成员变量和构造函数的调用时序,父类构造完后,子类才能开始成员变量的初始化和构造。

上一篇 下一篇

猜你喜欢

热点阅读