java 内部类、匿名内部类
一:内部类
1:什么是内部类?
大部分时候,类被定义成一个独立的程序单元。在某些情况下,也会把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类(有些地方也叫做嵌套类),包含内部类的类也被称为外部类(有些地方也叫做宿主类)
我们先创建一个基本的内部类结构:
class Outer{//外部类 //内部类 class Inner{ } }
2:内部类的划分
内部类分为成员内部类和局部内部类。内部类也会生成.class文件。
2.1: 成员内部类
定义在外部类中的成员位置,与类中的成员变量相似,可通过外部类对象进行访问。
内部类可以使用外部类的成员,包括私有成员。但是外部类要使用内部类的成员,必须建立内部类变量。
2.2: 局部内部类(比较少用)
定义:在方法里面有一个内部类。
只有在内部类所属的方法中创建内部类对象,方可访问局部内部类。而测试类中只需要创建外部类对象,然后调用外部类方法即可。
3:例子
import java.util.HashMap; public class Parcell { private HashMap<String, String> testMap = new HashMap<String, String>(); class Contents { // 返回一个外部类的引用. public Parcell ParcellRef = Parcell.this; } class Destination { public void putSomethingInMap() { testMap.put("hello", "world"); System.out.println(testMap.get("hello")); } } public Destination to() { return new Destination(); } public Contents contents() { return new Contents(); } public void ship(String dest) { Contents c = new Contents(); Destination d = new Destination(); } public static void main(String[] args) { Parcell p = new Parcell(); Parcell.Contents c = p.contents(); Parcell.Destination d = p.to(); d.putSomethingInMap(); Parcell.Contents c1 = p.new Contents(); } }
内部类的语法介绍
(1)普通内部类持有一个指向外部类的引用。要创建普通内部类,一定要先创建外部类。
(2)普通内部类就像人体的心脏一样,能够随意访问外部类的任意成员变量。
(3)在内部类中可以通过“外部类类名.this”的方式返回一个指向外部类实例的引用.如Parcell.this
(4)在外部类的static方法中若要创建内部类对象,则需要通过“外部类类名.new XXX()”的方式来创建。
(5)普通内部类中不能拥有静态成员变量。静态内部类中可以拥有静态成员变量。也可以拥有非静态成员变量。但是静态内部类不能访问外部类中非静态的成员变量。而普通内部类可以访问外部类的静态成员变量。
为什么static方法中需要p.new XXX()的方式而非static方法中我们直接new 内部类名 就可以创建一个对象了呢?
如果你有这样的疑问请再看看第一条,一定可以想明白的。
4.作用
1)更好的封装性
2)内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,但外部类不能访问内部类的实现细节,例如内部类的成员变量
3)匿名内部类适合用于创建那些仅需要一次使用的类
体外话:静态内部类
Java里面static一般用来修饰成员变量或函数。但有一种特殊用法是用static修饰内部类,普通类是不允许声明为静态的,只有内部类才可以。被static修饰的内部类可以直接作为一个普通类来使用,而不需实例一个外部类。
静态内部类的特点:
1.非静态内部类中不允许定义静态成员
2.外部类的静态成员不可以直接使用非静态内部类
3.静态内部类,不能访问外部类的实例成员,只能访问外部类的类成员
二:匿名内部类
匿名内部类使用最频繁的场合就是在创建线程的时候。
程序清单2-1:
public class Demo { public void test(String title) { Thread thread = new Thread(new Runnable() { @Override public void run() { // title = "我不要吃鸡"; // 改变时会提示错误 // 在封闭范围中定义的局部变量必须是final的。 System.out.println(title); } }); thread.start(); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { Demo demo = new Demo(); demo.test("我要吃鸡" + i); } } }
new Thread()
可以接收一个实现了Runnable接口类型的对象,这个对象要怎么创建呢?可以通过匿名内部类的形式来创建——new Runnable() {public void run(){......}}
——这段简短的代码等同于:// 实现Runnable接口 class MyRunnable implements Runnable { @Override public void run() { } } // 向上转型 Runnable myRunnable = new MyRunnable();
匿名内部类的好处就在于不仅节省了定义实现类的过程,还能够自动向上转型。
在程序清单2-1中,test()方法还有一个参数title,JDK1.8之前,编译器要求它必须是final类型的。但JDK1.8之后,如果我们在匿名内部类中需要访问局部变量,那么这个局部变量不再需要用final
关键字修饰了。
但如果想要在匿名内部类中改变局部变量的值,编译器就会提醒你不能这样做,它会提示:“在封闭范围中定义的局部变量必须是final的。”
另一个关于匿名内部类的例子:
开发中,最常用到的内部类就是匿名内部类了。以接口举例,当你使用一个接口时,似乎得做如下几步操作。
1、定义子类
2、重写接口中的方法
3、创建子类对象
4、调用重写后的方法
我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快捷方式。
条件
匿名内部类必须继承一个父类或者实现一个父接口。
new 父类名或者接口名(){ // 方法重写 @Override public void method() { // 执行语句 } };
以接口为例,匿名内部类的使用,代码如下:
定义接口:
public abstract class FlyAble{ public abstract void fly(); }
创建匿名内部类,并调用:
public class InnerDemo { public static void main(String[] args) { /* 1.等号右边:是匿名内部类,定义并创建该接口的子类对象 2.等号左边:是多态赋值,接口类型引用指向子类对象 */ FlyAble f = new FlyAble(){ public void fly() { System.out.println("我飞了~~~"); } }; //调用 fly方法,执行重写后的方法 f.fly(); } }
通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。代码如下:
public class InnerDemo2 { public static void main(String[] args) { /* 1.等号右边:定义并创建该接口的子类对象 2.等号左边:是多态,接口类型引用指向子类对象 */ FlyAble f = new FlyAble(){ public void fly() { System.out.println("我飞了~~~"); } }; // 将f传递给showFly方法中 showFly(f); } public static void showFly(FlyAble f) { f.fly(); } }
以上两步,也可以简化为一步,代码如下:
public class InnerDemo3 { public static void main(String[] args) { /* 创建匿名内部类,直接传递给showFly(FlyAble f) */ showFly( new FlyAble(){ public void fly() { System.out.println("我飞了~~~"); } }); } public static void showFly(FlyAble f) { f.fly(); } }
为什么需要内部类?
Java的内部类让我很容易的想起来JavaScript的闭包,闭包就是定义在一个函数内部的函数——这听起来和Java的内部类定义一样一样的。本质上,闭包是将函数内部与函数外部连接起来的桥梁。内部类一样,它是将内部类与外部类连接起来的桥梁。
来看看什么是闭包吧:
function wanger() { var age = 30; function know() { console.log(age); } } wanger(); // 控制台输出30
除此之外,内部类最引人注意的原因是:
内部类可以独立地继承一个抽象类或者实现一个接口,无论外部类是否也这样做了,对内部类都没有影响。