java 内部类与闭包

内部类

首先,让我们看看什么是内部类(Java 内部类,成员类,局部类,匿名类等):
根据内部类的位置不同,可将内部类分为

  1. 成员内部类
  2. 局部内部类
class C{
	//成员内部类
	class B{
	}
	
	public void show1() {
	//局部内部类
		class D{
		}
	}
}

####成员内部类
成员内部类的访问格式:

外部类名.内部类名 对象名=外部类对象.内部类对象

class C{
	//成员内部类
	class B{
		public void show() {
			System.out.println("你好");
		}
	}
}

public class niming {
	public static void main(String[]args) {
		//创建对象访问对应内部类函数成员
		C.B b=new C().new B();
		b.show();
	}
}

虽然内部类可以以上述方式访问,但实际开发中,常将内部设为私有成员,以保护数据的安全,不让外部直接使用
1. 将内部类设为私有成员

class C{
	//成员内部类,设为私有
	private class B{
		public void show() {
			System.out.println("你好");
		}
	}
	public void show1() {
		B b=new B();
		b.show();
	}
}
 
public class niming {
	public static void main(String[]args) {
		//创建对象访问对应内部类函数成员
		C c=new C();
		c.show1();
	}
}

2. 将内部类设为静态类

特定:静态内部类访问外部类成员时,只能访问外部类的静态成员

class C{
	//静态成员变量
	private static int num=20;
	//静态成员方法
	public static void show2() {
		System.out.println("hello world");
	}
	//成员内部类,设为私有静态
	private static class B{
		public void show() {
			System.out.println(num);
			show2();
	}
}
	public void show1() {
		B b=new B();
		b.show();
	}
}

public class niming {
	public static void main(String[]args) {
		//创建对象访问对应内部类函数成员
		C c=new C();
		c.show1();
	}
}

Private 保证了数据的安全
Static 让数据访问更方便

class Outer {
	public int num = 10;
	class Inner {
		public int num = 20;
		public void show() {
			int num = 30;
			//三个变量对应的变量名相同
			//访问的是本方法中的num变量,满足就近原则
			System.out.println(num);
			//通过this变量,访问方法外,本类中的变量,this代表Inner类对象
			System.out.println(this.num);
			//通过外部类名加this,返回外部类对象,访问对象外部类的num
			System.out.println(Outer.this.num);
			//通过创建对象的方式访问外部成员变量不推荐
			//System.out.println(new Outer().num); 
		}
	}
}

public class niming {
	public static void main(String[]args) {
		Outer.Inner inner=new Outer().new Inner();
		inner.show();
	}
}

####局部内部类

可以直接访问外部类的成员

内部类访问的外部变量必须定义为 final 或 static 类型

public class Outer{
	public void inner(){
		final int num = 5;
		class InnerClass{
			private int num = 4;
			public void testNum(){
				System.out.println(this.num);
            }
        }
     }
}

首先,内部类和外部类其实是处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁。问题就来了,如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就被GC了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中,这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。


###匿名内部类

  1. 概念:即内部类的简化写法
  2. 前提:存在一个类(可以是具体类也可以是抽象类)或接口
  3. 格式:new 类名或接口名{重写的方法}
  4. 本质:创建的是继承了类或实现了接口的子类匿名对 象。

Java语言规范上是这么描述匿名类的:

匿名类的声明:

  • 匿名类的声明是由java编译器自动派生自一个类实例创建表达式。
  • 匿名类永远不能是抽象的。
  • 匿名类总是隐式的final。
  • 匿名类总是一个内部类;并且不能是static的。

匿名构造函数:

匿名类不能有显式声明的构造函数。相反的,Java编译器必须为这个匿名类自动提供一个匿名构造函数。匿名类C继承于父类S那么匿名构造函数是下面的形式:

  • 如果S不是一个内部类或者S是一个静态上下文中的局部类,那么这个匿名构造函数有一个形参为每一个在C的类实例创建表达式中的实际参数。
    类的实例创建表达式的实际参数用来确定S的一个构造函数cs,使用方法调用相同的规则。
    每一个匿名构造函数的形参必须和cs中的相关形参一致。
    这个构造函数中显式的包括super(…)构造函数调用,这个实际参数是构造函数的形参,按它们声明的顺序。

  • 否则,C的构造函数第一个形参描述了这个值,直接包围实例i相对于S。参数的类型是S的class类型。
    构造函数有一个额外的形参为每一个类实例创建语句声明在匿名类中的实际参数。第n个形参和第n-1个实际参数相关。
    类的实例创建表达式的实际参数用来确定S的一个构造函数cs,使用方法调用相同的规则。
    每一个匿名构造函数的形参必须和cs中的相关形参一致。
    这个构造函数中显式的包括super(…)构造函数调用,这个实际参数是构造函数的形参,按它们声明的顺序。

在所有的情况下,匿名构造函数的throws语句必须列出所有的检查异常,包括父类构造函数显式调用语句抛出的和匿名类的实例初始化或者变量初始化抛出的异常。

注意:匿名构造函数的签名涉及到了一个不可达类型是可能的(例如,出现在父类构造函数中的类型),这不会导致任何编译时和运行时错误。

实际使用中我们只需注意这几点儿:
1. 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2. 匿名内部类中是不能定义构造函数的。
3. 匿名内部类中不能存在任何的静态成员变量和静态方法。
4. 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5. 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

编译时的命名规则:

  1. 内部类的class文件命名是:主类+$+内部类名
  1. 匿名类的class文件命名是:主类+$+(1,2,3…)

匿名类的语法

Runnable hello = new Runnable() {  
    public void run() {  
        System.out.println("hello");  
    }  
};

一个匿名类由以下几个部分组成:

  1. new操作符
  2. Runnable:接口名称。这里还可以填写抽象类、普通类的名称。
  3. ():这个括号表示构造函数的参数列表。由于Runnable是一个接口,没有构造函数,所以这里填一个空的括号表示没有参数。
  4. {…}:大括号中间的代码表示这个类内部的一些结构。在这里可以定义变量名称、方法。跟普通的类一样。

访问权限

  1. 访问外层Class里面的字段。
  2. 不能访问外层方法中的本地变量。除非变量是final。
  3. 如果内部类的名称和外面能访问的名称相同,则会把名称覆盖掉。
  4. 不能定义静态初始化代码块
  5. 不能在匿名类里面定义接口
  6. 不能在匿名类中定义构造函数

因为匿名类没有名字,而构造函数需要把类名作为方法名才能看成构造函数。
匿名类中可以包含的东西有:

  1. 字段
  1. 方法
  2. 实例初始化代码
  3. 本地类

匿名内部类方法的访问:

//方法1:直接在new A内部类后边加点加方法,这样访问,如果方法多了,调用太麻烦
new D(){
	@Override
	public void ShowContext() {
		System.out.println("hello");
	}
}.ShowContext();

//方法2:通过创建对象来访问,多态思想
D a=new D(){
	@Override
	public void ShowContext() {
		System.out.println("hello");
	}
};
a.ShowContext();

匿名内部类在开发中的使用,一般是方法参数为接口的情况

interface D{
	void ShowContext();
}
class B{
	//参数为接口对象
	public void show(D d) {
		d.ShowContext();
	}
}

public class niming {
	public static void main(String[]args) {
		B b=new B();
		//调用时使用匿名类创建匿名对象
		b.show(new D(){
			@Override
			public void ShowContext() {
				System.out.println("hello");
			}
		});
	}
}

闭包

闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境(局部变量)),浅谈闭包

闭包函数的出现条件

  • 被嵌套的函数引用到非本函数的外部变量,而且这外部变量不是“全局变量”
  • 嵌套的函数被独立了出来(被父函数返回或赋值 变成了独立的个体), 而被引用的变量所在的父函数已结束

闭包与内部类

  • Java 内部类与闭包

闭包 = 引用了非内部的局部变量的内部类

闭包与外部隔离,通过“引用环境(局部变量)”与外部沟通

你可能感兴趣的:(java基础)