多态 / 面向接口编程

一、对象与多态

1. 重载与重写

1.1 重载

(1)发生在一个类中,方法名相同,参数列表不同;

(2)遵循"编译器"绑定,看引用的类型绑定方法;

(3)方法的重载可以看成完全不同的方法,只不过是方法名恰好相同;

遵循:

(1)方法名相同,参数列表(类型和个数)不同;  

(2)不能通过访问权限,返回值类型和抛出的异常来实现重载;

(3)可以有不同的访问修饰符、返回值类型、抛出不同的异常;

1.2 重写

(1)发生在父子类中,方法名称相同,参数列表相同,方法体不同;

(2)重写方法被调用时,看对象的类型(遵循"运行期"绑定,看对象的类型绑定方法);

遵循(两同两小一大原则):

两同:方法名相同,参数类型相同;

两小:子类返回类型小于等于父类方法返回类型;

     子类抛出异常小于等于父类方法抛出异常;

一同:子类访问权限大于等于父类方法访问权限;

 强制类型转换: 

 子类对象可以声明为父类类型,父类对象不可以声明为子类类型;

 在子类对象声明为父类类型后,可以通过强制转型,转型回来;

 而父类对象声明为父类类型之后,并不能执行强制类型转化;

   因为在子类对象声明为父类类型后,其实对象的真实意义还是子类对象;  参考[类型实例]

1.3 多态

重载(overload)和重写(override)是实现多态的两种主要方式。

实现多态: 接口多态性。 继承多态性。 通过抽象类实现的多态性

多态的三个条件:

a. 继承的存在(继承是多态的基础,没有继承就没有多态).

b. 子类重写父类的方法(多态下调用子类重写的方法).

c. 父类引用变量指向子类对象(子类到父类的类型转换).

  当父类的引用指向子类对象时,通过这个引用能调用什么方法,看引用的类型,而调用的是子类对象里面的方法。

/**
 * 向上造型:能点出什么来,看引用的类型,调用的方法是对象的
 */
public class Test {
    public static void main(String[] args) {
        A o = new B();
        o.f1(); //B.f1()

        B b= (B)o;
        b.f1(); //B.f1()
        b.f3(); //B.f3()
    }
}

class A{
    void f1(){
        System.out.println("A.f1()");
    }
}
class B extends A{
    @Override
    void f1(){
        System.out.println("B.f1()");
    }
    void f3(){
        System.out.println("B.f3()");
    }
}
View Code

2. 面向接口编程 

为什么要面向接口编程?

减少耦合性,令各个成员依赖于抽象, 而不是依赖于具体,方便维护和扩展[封闭-开放原则]。

用法:传递的参数为接口/抽象类  

public class Test {
    public static void main(String[] args) {
        //面向接口编程
        Test.test01(new B()); // B.f1()
        Test.test01(new C()); // C.f1()
    }

    public static void test01(A a) {
        a.f1();
    }
}

class A{
    void f1(){
        System.out.println("A.f1()");
    }
}
class B extends A{
    @Override
    void f1(){
        System.out.println("B.f1()");
    }
}

class C extends A{
    @Override
    void f1(){
        System.out.println("C.f1()");
    }
}
View Code

 3、对象创建

方式一:new Person()

普通的对象实例化语法,调用的是类的默认构造方法(如果没有显式定义构造方法的话,默认会有一个无参构造方法)。这个对象的类型是 Person 类的一个实例。

使用反射获取 new Person() 创建的对象的类类型时,将直接得到 Person 类的类型。

方式二:new Person(){}

匿名内部类的语法,创建一个匿名子类的实例,它继承了 Person 类并且可以覆写父类的方法或添加新的方法。它实际上是在定义一个继承了 Person 类的匿名子类,并且在创建子类对象时执行一些代码块。这种方式通常用于创建临时的、仅在特定地方使用的对象。Person可以是接口,也可以是类。

使用反射获取 new Person(){} 创建的对象的类类型时,将得到一个匿名子类的类型。因为 new Person(){} 创建的是一个继承了 Person 类的匿名子类的对象,而匿名子类是动态生成的,没有显式的类名,因此反射获取的类型将是这个匿名子类的类型。

class Person {
    public void display() {
        System.out.println("Person class");
    }
}

public class Main {
    public static void main(String[] args) {
        Person person = new Person() {
            @Override
            public void display() {
                System.out.println("Anonymous subclass");
            }
        };
        Class<? extends Person> classType = person.getClass();
        System.out.println(classType.getName()); // 输出:Main$1
    }
}

在上面的例子中,new Person(){} 创建了一个匿名子类的对象,并重写了 display() 方法。当使用反射获取类型时,返回的类型是 Main$1,其中 $1 表示这是外部类 Main 的第一个匿名内部类。

总结:

  • new Person()用于实例化一个具体的类(对象)。
  • new Person(){}用于创建一个匿名内部类的实例,可以用来实现接口或扩展类的行为。
posted @ 2019-03-07 11:05  一帘幽梦&nn  阅读(143)  评论(1编辑  收藏  举报
点击查看具体代码内容