Java基础-- 继承 多态 泛型 接口 动态绑定 动态代理

Posted danfengw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础-- 继承 多态 泛型 接口 动态绑定 动态代理相关的知识,希望对你有一定的参考价值。

继承

1 概念:

之所以叫继承,是因为子类继承了父类的行为与属性,父类有的行为和属性子类也都有,子类可以增加子类特有的属性,子类的实现方式也可能与父类不同

优点:

一方面,可以复用代码,公共的行为与属性可以放到父类中处理,子类只需要关注子类特有的。另一方面,不同的子类可以统一管理

多态

1 概念:

多态:就是一种类型的变量,可以引用多种类型的实际变量
动态绑定:比如我们有个父类Shape 实现了draw方法, Shape就是静态类型,而Shape的子类 Rectangle Circle 之类的就是动态类型,而我们调用shape.draw的时候可调用对应类型的draw方法,这就叫动态绑定

2 多态与动态绑定的好处

使得操作对象的程序不需要关注对象的实际类型,而可以进行统一的处理,但又能实现不同对象的特有行为

3 输出

public class Base 
    protected static int s;
    protected  int a;

    static 
        System.out.println("基类静态代码块---s=" + s);
        s = 1;
    

    
        System.out.println("基类实例代码块---a=" + a);
        a = 1;
    

    public Base() 
        System.out.println("基类构造器初始化---a=" + a);
        a = 2;
    

    protected void step() 
        System.out.println("base s: " + s + ", a: " + a);
    

    public void action() 
        System.out.println("start");
        step();
        System.out.println("end");
    




public class Child extends Base 
    protected static int s;
    protected   int a;

    static 
        System.out.println("子类静态代码块---s=" + s);
        s = 10;
    

    
        System.out.println("子类实例代码块---a=" + a);
        a = 10;
    

    public Child() 
        System.out.println("子类构造器初始化---a=" + a);
        a = 20;
    

    protected void step() 
        System.out.println("Child s: " + s + ", a: " + a);
    



public class Test 
    public static void main(String[] args) 
        System.out.println("---- new Child()");
        Child c = new Child();
        System.out.println("\\n---- c.action()");
        c.action();
        Base b = c;
        System.out.println("\\n---- b.action()");
        b.action();
        System.out.println("\\n---- b.s: " + b.s);
        System.out.println("\\n---- c.s: " + c.s);
    

输出结果:

---- new Child()
基类静态代码块---s=0
子类静态代码块---s=0
基类实例代码块---a=0
基类构造器初始化---a=1
子类实例代码块---a=0
子类构造器初始化---a=10

---- c.action()
start
Child s: 10, a: 20
end

---- b.action()
start
Child s: 10, a: 20
end

---- b.s: 1

---- c.s: 10

4 重写与重载

重写(Override)
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

接口

interface是一种特殊形式的abstract

  1. 抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所 有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
  2. 抽象类要被子类继承,接口要被类实现。
  3. 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
  4. 抽象类里可以没有抽象方法。
  5. 接口可以被类多实现(被其他接口多继承),抽象类只能被单继承。
  6. 接口中没有 this 指针,没有构造函数,不能拥有实例字段(实例变量)或
    实例方法。
  7. 抽象类不能在Java 8 的 lambda 表达式中使用。

接口 int integer

泛型

1 泛型引入的目的

通过泛型使得在编译阶段完成一些类型转换的工作,避免在运行时强制类型转换而出现 ClassCastException

2 泛型的好处

1 更好的安全性:类型错误现在在编译期间就被捕获到了,而不是在运行时当作 java.lang.ClassCastException展示出来,将类型检查从运行时挪到编译时有助于开 发者更容易找到错误,并提高程序的可靠性。
2 更好的可读性:消除了代码中许多的强制类型转换,增强了代码的可读性。

3 上/下限通配符

上限通配符: 使用extends关键字指定这个类型必须是继承某个类, 或者实现某个接口,也可以是这个类或接口本身
下限通配符:使用super关键字指定这个类型必须是是某个类的父 类,或者是某个接口的父接口,也可以是这个类或接口本身

<? extend E>  灵活读取
<? supter E> 灵活写入

?extend E 场景

<? extend E> 很少用于下图这样的声明,因为这样的声明是不能添加内容的。

比较多的用于给外部提供某种方法,比如下面的代码

  public float getTotalPrice( List<? extends Fruit> fruits)
        float price=0f;
        for (int i = 0; i < fruits.size(); i++) 
            price+= fruits.get(i).price();
        
        return price;
    

?super E 场景

适合写入的场景,如果读取的话,会如下图报错。

适用场景跟 ? extend E 类似,也是提供方法

public class Banana extends Fruit
    @Override
    public float price() 
        return 2f;
    

    public void addList(List<? super Banana> list) 
        list.add(this);
    

如何获取泛型的具体类型

 public static void main(String[] args) 
       // 这里因为bananas1 和 bananas2 打印出来的会有所不同
        List<? super Banana> bananas1 = new ArrayList<Fruit>();
        List<? super Banana> bananas2 = new ArrayList<Fruit>();
        
		//获取泛型具体类型
        Type type1 = bananas1.getClass().getGenericSuperclass();
        Type[] types1 = ((ParameterizedType) type1).getActualTypeArguments();
        System.out.println("泛型的具体类名"+types1[0]);

        Type type2 = bananas2.getClass().getGenericSuperclass();
        Type[] types2 = ((ParameterizedType) type2).getActualTypeArguments();
        System.out.println("泛型的具体类名"+types2[0]);
    

打印:

泛型的具体类名E
泛型的具体类名class com.example.demo.Fruit

这里获取具体类名的方式没有问题,至于打印的不一致的原因在于,ArrayList 的创建上,可以点进源码看到,public class ArrayList<E>,默认构造方法创建的底层类型确实是E,但是如果我们使用加的方式创建,在编译的时候会自动生成一个子类,因此我们就能获取到Fruit了。

4 题目

Class c1=new ArrayList<Integer>().getClass(); 
Class c2=new ArrayList<String>().getClass(); 
System.out.println(c1==c2);

输出为true

原因:
泛型是通过泛型擦除来实现的,类型参数会在编译时被替换为object

5 为什么泛型类的类型不能是基本数据类型

因为泛型在出现的时候是为了解决将一个对象放入集合中,但是集合不会记住对象的类型的问题,因此泛型必须是对象,而基本数据类型就不会被允许了。

动态代理

1 概念

动态代理:在运行时动态创建一个类,实现一个或者多个接口,可以在不修改原有类的基础上动态的为通过该类获取的对象添加方法,修改行为。

内部类

成员内部类

以上是关于Java基础-- 继承 多态 泛型 接口 动态绑定 动态代理的主要内容,如果未能解决你的问题,请参考以下文章

Java基础---泛型

Java 三大特性:封装继承多态

多态的支撑系统:面向多态编程

Java基础09—接口继承与多态

Java基础09—接口继承与多态

java基础讲解09-----接口,继承,多态