java 内部类匿名内部类

Posted myseries

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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);
        }
    }
    
}
    在程序清单2-1中,test()方法内部有一个线程对象thread,是通过new Thread()创建的。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

除此之外,内部类最引人注意的原因是:

 内部类可以独立地继承一个抽象类或者实现一个接口,无论外部类是否也这样做了,对内部类都没有影响。

 

以上是关于java 内部类匿名内部类的主要内容,如果未能解决你的问题,请参考以下文章

java匿名内部类

java内部类之成员内部类之匿名内部类

理解java匿名内部类

对 Java 的匿名内部类理解

java基础学习——29匿名内部类

50java 匿名内部类剖析