接口之美,内部之妙:深入解析Java的接口与内部类

简介: 接口之美,内部之妙:深入解析Java的接口与内部类

接口

接口不是类,而是对类的一组需求描述,这些类要遵从在接口中定义的统一格式。

接口中的所有方法自动地属于 public。因此,在接口中声明方法时,不必提供关键字 public。但是在方法的实现时,需要将方法根据需求进行作用域声明。

接口可以包含多个方法声明,Java8 以后可以在接口中添加默认方法。

接口中可以定义常量,但是不可以定义实例域(属性)。实例域和方法的实现应该由实现此接口的那个类来提供。

接口的特性

不能使用 new 进行实例化,不能创建接口对象,但是可以声明接口变量,接口变量必须引用实现了接口的类对象。

使用 instanceof 检查一个对象是否实现了某个接口,一个类可以实现多个接口。

接口可以使用 extends 进行继承

Java 的早期版本中,接口中不可以包含实例域和静态方法,后面 Java8 允许在接口中增加静态方法,但是这样做其实有些违背当初接口设计的初衷。之前想要为接口设计静态方法,通常是为接口设计一个伴随类,成对存在,比如:Collection/Collections。

接口中可以定义常量,接口中的常量默认为 public static final。有些接口专门只用来定义常量,那么所有实现此接口的类都自动的继承,可以直接使用而不需要使用类名.常量名的方式。

接口与抽象类

由于 Java 的单继承机制,一个类最多可继承自一个抽象类,但是可以实现多个接口。

相比 C++ 是支持多继承,但是多继承会让语言本身变得更加复杂,而且效率也会降低。那么接口可以提供多重继承的大多数好处,同时还能避免多重继承的复杂性和低效性。

接口的默认方法

Java8 后可以为接口方法提供一个默认的实现,需要使用 default 关键字标记。

声明了默认方法之后,类想要实现此接口就可以不一定重写此方法了。

默认方法的冲突问题
多个接口有相同的默认方法【手动解决】

父类(protected)和接口中有相同的方法【手动解决】

父类(public)和接口中有相同的方法【优先父类】

内部类

内部类式定义在另一个类中的类。

  1. 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
  2. 内部类可以对同一个包中的其他类隐藏起来。
  3. 当想要定义一个回调函数且不想编写大量代码时,使用匿名 (anonymous )内部类比较便捷。

内部类是一种编译器现象,与虚拟机无关。编译器将会把内部类翻译成用 $ (美元符号)分隔外部类名与内部类名的常规类文件,而虚拟机则对此一无所知。

例如:在 User 类内部的 Contact 类会被编译成类文件 User$Contact.class

使用内部类访问对象状态

内部类可以直接访问所在类的所有内容,比如私有的属性和方法。内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。

内部类的分类

成员内部类

成员内部类:类比成员方法,不能拥有静态域但是可以访问外部类的静态域。

  • 成员内部类的创建需要依赖于外部类对象(成员方法必须通过对象调用),在没有外部类实例之前无法创建成员内部类对象。
  • 内部类与外部类相对独立,不是 is a 的关系。
  • 私有属性的互相访问,内部类可以直接访问外部类,而外部类访问内部类需要内部类的对象来访问。
  • 创建内部类的语法
  • 在外部类内部创建内部类对象 Inner inner = new Inner();
  • 在外部类外部创建内部类对象,外部类.内部类 inner = new Outter().new Inner();
  • 在内部类内部使用隐藏的外部类对象(隐藏的this),外部类名.this

静态内部类

定义在外部类的内部,使用static修饰,类比静态方法,静态内部类不需要外部类对象产生就能使用,不能访问外部类的成员域,但能访问静态域。

静态内部类的创建语法:

  • 外部类内部:Inner inner = new Inner();
  • 外部类外部:StaticInnerClass.Inner inner = new StaticInnerClass.Inner();

方法内部类/局部内部类

局部内部类不仅能够访问包含它们的外部类,还可以访问局部变量。不过,那些局部变量必须事实上为 final。这说明,它们一旦赋值就绝不

会改变。

局部类不能用 public 或 private 访问说明符进行声明。它的作用域被限定在声明这个局部类的块中。

局部类的优点是对外部世界完全隐藏。

匿名内部类

只创建这个类的一个对象,不需要明确定义类名。不过必须继承一个抽象类或者实现一个接口。而且匿名内部类是没有构造方法的。

内部类是否有用、必要和安全

代理

给一个对象提供一种代理对象以控制对该对象的访问。代理对象在不改变原对象代码的基础上对原对象的功能进行扩展。

使用代理降低了系统的耦合度,扩展性也会更好,还可以起到保护目标对象的作用。

代理模式在 Java 开发中是广泛应用的,特别是在框架中底层原理经常涉及到代理模式(尤其是动态代理)。

静态代理和动态代理,实际使用时还是动态代理使用的比较多。原因就是静态代理需要自行手写代码,维护、修改非常繁琐,会额外引入很多工作量。也不能很好的使用配置完成逻辑的指定,所以使用较少。

基于 JDK 和基于 CGLIB,实际使用时两个都会用到。

在 Spring 中,默认情况下它就支持了两种动态代理方式。如果你指定的目标类实现了接口,Spring 就会自动选择 JDK 的动态代理。而如果目标类没有实现接口,则 Spring 会使用 CGLIB。

我们在开发时,由于基于 JDK 的动态代理要求比较多,更不容易实现,所以很多人习惯于统一配置为使用 CGLIB 进行代理。也就是 CGLIB 更通用。

静态代理

静态代理维护复杂,一旦接口或父类发生改变,所有相关的类或接口就都得进行维护。

通过接口

让目标对象和代理对象都实现一个共同接口。那么这两个类就有了公共的方法,就可以在代理对象中实现对目标对象功能的扩展。

通过继承

让代理对象继承目标对象,那么代理对象就拥有目标对象的方法,同时又可以对其功能进行扩展。

动态代理

无需手写代理类,也不会存在代码编译的过程。运用在内存中生产代理类的技术在JVM的运行区造一个代理对象,只需对需要修改的部分进行编辑。

JDK 动态代理【接口】

CGLIB 动态代理【继承】

笔记大部分摘录自《Java核心技术卷I》,含有少数本人修改补充痕迹。

参考文章:http://gg.gg/12h2s6

相关文章
|
1天前
|
Java
java面试基础 -- 普通类 & 抽象类 & 接口
java面试基础 -- 普通类 & 抽象类 & 接口
7 0
|
1天前
|
人工智能 Java 编译器
Java接口详解
Java接口详解
5 0
|
1天前
|
Java
Java接口
Java接口
6 0
|
1天前
|
设计模式 Java 开发者
Java的内部类
Java的内部类
5 0
|
2天前
|
存储 Java
【JAVA学习之路 | 进阶篇】ArrayList,Vector,LinkedList内存解析
【JAVA学习之路 | 进阶篇】ArrayList,Vector,LinkedList内存解析
|
2天前
|
存储 Java
【JAVA学习之路 | 进阶篇】Map接口及其实现类及常用方法
【JAVA学习之路 | 进阶篇】Map接口及其实现类及常用方法
|
2天前
|
Java 索引
【JAVA学习之路 | 进阶篇】List接口常用方法
【JAVA学习之路 | 进阶篇】List接口常用方法
|
2天前
|
存储 Java API
【JAVA学习之路 | 提高篇】[内部类与常见API]String类
【JAVA学习之路 | 提高篇】[内部类与常见API]String类
|
2天前
|
Java
【JAVA学习之路 | 提高篇】创建与启动线程之二(继承Thread类)(实现Runnable接口)
【JAVA学习之路 | 提高篇】创建与启动线程之二(继承Thread类)(实现Runnable接口)
|
2天前
|
Java 编译器
【JAVA学习之路 | 提高篇】接口(interface)
【JAVA学习之路 | 提高篇】接口(interface)

推荐镜像

更多