连接与通信,作为桥接中间件存在。

内部类和主体类可以无障碍通信;

1、通过继承连接实现;

2、通过接口连接通信;

形式:

1、命名空间;

2、运行上下文;

其它:

信息隐藏是次要功能。

内部类

Java的内部类也是一个语法糖,它仅仅是一个编译时的概念,outer.java里面定义了一个内部类inner,一旦编译成功,就会生成两个完全不同的.class文件了,分别是outer.class和outer$inner.class。所以内部类的名字完全可以和它的外部类名字相同。

内部类分为四种:成员内部类、局部内部类、匿名内部类、静态内部类。但本篇不是谈论四种内部类的用法的,只讲内部类一些值得注意的地方。

为什么要使用内部类

在《Think in java》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

因为Java不支持多继承,支持实现多个接口。但有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。

成员内部类

成员内部类也是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有 成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。在成员内部类中要注意两点

  • 成员内部类中不能存在任何static的变量和方法;

  • 成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。例子略。

局部内部类和匿名内部类

局部内部类是嵌套在方法和作用域内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。

而匿名内部类也可以说是局部内部类的一种,有时候一个类只使用一次,就可以用匿名内部类,告诉GC只用一次就可以回收了,同时也可以简化代码和方便地定义回调

需要注意的是局部内部类和匿名内部类引用外部变量时,外部的变量需要是final 的:

abstract class InnerClass {
public abstract void print();
}
public class Outer {
public void test1(final String s1) {// 参数必须是final
//成员内部类
InnerClass c = new InnerClass() {
public void print() {
System.out.println(s1);
}
};
c.print();
} public void test2(final String s2) {// 参数必须是final
//匿名内部类
new Outer() { //名字可以跟外部类一样
public void print() {
System.out.println(s2);
}
}.print();
}
public static void main(String[] args) {
Outer o=new Outer();
o.test1("inner1");
o.test2("inner2"); }
}

为什么匿名内部类和局部内部类引用外部的变量必要是final的呢?
直接看编译出来的源码吧

InnerClass:

    abstract class InnerClass
{
public abstract void print();
}

Outer.class:

`import java.io.PrintStream;

public class Outer
{
public void test1(String paramString)
{
Outer.1 local1 = new InnerClass(this, paramString) {
public void print() {
System.out.println(this.val$s1);//引用了s1变量
} };
local1.print(); } public void test2(String paramString) {
new Outer(this, paramString) {//名字可以一样
public void print() {
System.out.println(this.val$s2);
}
}
.print();
} public static void main(String[] paramArrayOfString)
{
Outer localOuter = new Outer();
localOuter.test1("inner1");
localOuter.test2("inner2");
}
}

局部内部类Outer$1.class:

import java.io.PrintStream;
class 1 extends InnerClass
{
public void print()
{
System.out.println(this.val$s1);//引用了s1变量
}
}

匿名内部类Outer$2.class:

import java.io.PrintStream;
class 2 extends Outer
{
public void print()
{
System.out.println(this.val$s2);//引用了s2变量
}
}

看源码两个内部类各编译出了一个独立的class文件,也就是说Outer$1和Outer$2的生命周期是对象级别的,而变量s1、s2是方法级别的,方法运行完了变量就销毁了,而局部内部类对象Outer$1和Outer$2还可能一直存在(只能没有人再引用该对象时,它才会被GC回收),它不会随着方法运行结束就马上死亡。这时可能会出现了一种"荒唐"的结果:局部内部类对象inner_object要访问一个已不存在的局部变量s1、s2!

也有人说:当方法调用完了,内部类也不可能再被访问到了,照理内部类对象也应该成为了垃圾。
别忘了Java还有反射,而且在多线程的情况下完全有可能主线程的方法运行结束,而内部类还在运行,例如:

public void execute() {
final int s = 10;
class InnerClass {
public void execute() {
new Thread() {
@Override
public void run() {
try {
Thread.currentThread().sleep(2000);
System.out.println(s);
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
new InnerClass().execute();
System.out.println("主方法已经over");
}

为什么把变量定义为final就能避免上述问题?
看stackoverflow上的一个讨论
http://stackoverflow.com/questions/3910324/why-java-inner-classes-require-final-outer-instance-variables

stackoverflow里最高票的答案说到,当主方法结束时,局部变量会被cleaned up 而内部类可能还在运行。当局部变量声明为final时,当使用已被cleaned up的局部变量时会把局部变量替换成常量:

The compiler can then just replace the use of lastPrice and price in the anonymous class with the values of the constants (at compile time, ofcourse)

也就是说当变量是final时,编译器会将final局部变量"复制"一份,复制品直接作为局部内部中的数据成员.这样,当局部内部类访问局部变量时,其实真正访问的是这个局部变量的"复制品"。因此:当运行栈中的真正的局部变量死亡时,局部内部类对象仍可以访问局部变量(其实访问的是"复制品")。而且,由于被final修饰的变量赋值后不能再修改,所以就保证了复制品与原始变量的一致。给人的感觉:好像是局部变量的"生命期"延长了。
这就是java的闭包。

最后贴两段关于闭包的笔记,来源于网络:

闭包是个什么东西呢?
Ruby之父松本行弘在《代码的未来》一书中解释的最好:闭包就是把函数以及变量包起来,使得变量的生存周期延长。闭包跟面向对象是一棵树上的两条枝,实现的功能是等价的。

Java中闭包带来的问题
  在Java的经典著作《Effective Java》、《Java Concurrency in Practice》里,都提到:匿名函数里的变量引用,也叫做变量引用泄露,会导致线程安全问题,因此在Java8之前,如果在匿名类内部引用函数局部变量,必须将其声明为final,即不可变对象。(Python和Javascript从一开始就是为单线程而生的语言,一般也不会考虑这样的问题,所以它的外部变量是可以任意修改的)。
  而java8的lambda 表达式之所以不用写final,是因为Java8这里加了一个语法糖:在lambda表达式以及匿名类内部,如果引用某局部变量,则直接将其视为final。本质并没有改变。

静态内部类

略。

作者:Eric新之助
链接:https://www.jianshu.com/p/f55b11a4cec2
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

java内部类的本质的更多相关文章

  1. Java 内部类详解

    什么 定义在一个类内部的类,称为内部类(累不累),如下: public class A { private int c = 1; public class C { public void test() ...

  2. Java内部类final语义实现

    本文描述在java内部类中,经常会引用外部类的变量信息.但是这些变量信息是如何传递给内部类的,在表面上并没有相应的线索.本文从字节码层描述在内部类中是如何实现这些语义的. 本地临时变量 基本类型 fi ...

  3. Java内部类详解

    Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...

  4. 黑马----JAVA内部类

    黑马程序员:Java培训.Android培训.iOS培训..Net培训 黑马程序员--JAVA内部类 一.内部类分为显式内部类和匿名内部类. 二.显式内部类 1.即显式声明的内部类,它有类名. 2.显 ...

  5. java 内部类 *** 最爱那水货

    注: 转载于http://blog.csdn.net/jiangxinyu/article/details/8177326 Java语言允许在类中再定义类,这种在其它类内部定义的类就叫内部类.内部类又 ...

  6. java内部类和匿名内部类

    内部类即是包含在类里面的又一个类. java内部类分为: 成员内部类.静态嵌套类.方法内部类.匿名内部类 . 内部类的共性 (1).内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.clas ...

  7. attilax.java 注解的本质and 使用最佳实践(3)O7

    attilax.java 注解的本质and 使用最佳实践(3)O7 1. 定义pojo 1 2. 建立注解By eclipse tps 1 3. 注解参数的可支持数据类型: 2 4. 注解处理器 2 ...

  8. Java内部类小程序(成员内部类,静态内部类,匿名内部类)

    /** * 测试java内部类(成员内部类,静态内部类,匿名内部类) * 局部内部类不常用,就不写了. * @package :java05 * @author shaobn * @Describe ...

  9. [转] Java内部类详解

    作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...

随机推荐

  1. CloseableHttpClient方式配置代理服务器访问外网

    小编最近在负责银行内部项目.其中有模块需要访问天眼查API接口,但由于公司全部内网,所以需要配置代理服务器才可以访问外网接口. 又到了激动人心的上码时刻! public void Connect(Ht ...

  2. JMeter扩展插件实现对自定义协议进行支持 转

    本文版权归xmeter.net 所有.欢迎转载,转载请注明出处. 摘要## JMeter本身提供了插件机制,允许第三方扩展JMeter以支持JMeter不支持的协议的测试.本文以扩展一个简单的Apac ...

  3. ZYNQ笔记(4):PL触发中断

    一.ZYNQ中断框图 PL到PS部分的中断经过ICD控制器分发器后同时进入CPU1 和CPU0.从下面的表格中可以看到中断向量的具体值.PL到PS部分一共有20个中断可以使用.其中4个是快速中断.剩余 ...

  4. 百度前端技术学院task14源代码

    刚开始理解错误,以为是读取对象,结果后面才发现是二维数组. 另外对于数组排序,创建新的节点啊,输入到doM中啊,都不是很熟悉. <!DOCTYPE html> <html> & ...

  5. SQL Server 事务日志截断、回绕与收缩(转载)

    每个 SQL Server 数据库都具有事务日志,用于记录所有事务以及每个事务对数据库所做的修改. 必须定期截断事务日志以避免它被填满. 但是,一些因素可能延迟日志截断,因此监视日志大小很重要. 某些 ...

  6. NET 实例化泛形对象并赋值

    1.泛形方法:具体实例点击查看BuilderResultList /// <summary> /// 实例化泛形对象并赋值 /// </summary> /// <typ ...

  7. 在excel实现多级联动

    最近做了一个Excel的多级联动的功能,具体是将全国所有的气象局按一二三四级单位做成四列,实现各级的联动下拉选择,这和省市县乡的各级联动的功能基本一样,下面记录下具体的操作步骤. 1.首先需要从数据库 ...

  8. 使用GitHub搜索技巧

    in:name example 名字中有"example"in:readme example readme中有"example"in:description e ...

  9. python基础08--迭代器,生成器

    1.1 迭代器 1.可迭代对象:str,list,tuple,set,dict, 迭代器:f(文件),range() 可迭代对象和迭代器都可以for循环 可迭代对象不会计数, 迭代器会计数,上面操作到 ...

  10. JavaScript 之 Array 对象

    Array 对象 之前已经了解了 Array(数组)的定义和基本操作.数组的基本操作. 下面来学习更多的方法. 检测一个对象是否是数组 instanceof // 看看该变量是否是该对象的实例 Arr ...