JAVA基础知识|内部类

一、什么是内部类?

内部类(inner class)是定义在另一个类中的类

为什么使用内部类?

1)内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据

2)内部类可以对同一个包中的其他类隐藏起来

3)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷

二、内部类有几种?

内部类分为:成员内部类、局部内部类、静态内部类、匿名内部类

2.1、成员内部类

成员内部类是最普通的内部类,我们先从它开始说起:

package com.my.po;

/**
 * description:{description}
 * author:jyy
 * date:2018-02-08 16:53
 * modify:{modify}
 */
public class Outer {

    private String id;
    private String name;

    public class Inner {

        public void print() {
            id = "1001";
            name = "李明";
            System.out.println(id);
            System.out.println(name);
        }
    }

    public void start() {
        Inner inner = new Inner();
        inner.print();
    }
}

执行结果:

1001
李明

Inner是一个内部类,内部类可以访问外部类定义的所有属性和方法,包括私有属性

内部类的对象总有一个隐式的引用,它指向创建它的外部类对象,也是通过这个隐式引用,内部类可以访问外部类的属性和方法,图示如下:

所以System.out.println(name)等价于System.out.println(Outer.this.name),格式为:外部类.this.属性/方法名,下面一个例子,可以加深我们的理解

package com.my.po;

/**
 * description:{description}
 * author:jyy
 * date:2018-02-08 16:53
 * modify:{modify}
 */
public class Outer {

    private String id = "1";
    private String name = "张三";

    public class Inner {

        private String name = "李四";

        public void print() {
            String name = "王五";
            System.out.println(id);
            System.out.println(name);
            System.out.println(this.name);
            System.out.println(Outer.this.name);
        }
    }

    public void start() {
        Inner inner = new Inner();
        inner.print();
    }
}

执行结果:

1
王五
李四
张三

内部类可以拥有private、protected、public、包访问权限(默认),而外部类只有public和包访问权限(protected)

注意:成员内部类不能含有static修饰的变量和方法,因为成员内部类需要先创建外部类,才能创建自己

2.2、局部内部类

局部内部类是定义在一个方法或者作用域中的类

package com.my.po;

/**
 * description:{description}
 * author:jyy
 * date:2018-02-08 16:53
 * modify:{modify}
 */
public class Outer {

    public void print(final String name) {
        class Inner {
            public void innerPrint() {
                System.out.println(name);
            }
        }
        Inner inner = new Inner();
        inner.innerPrint();
    }
}

如果需要从外部类的print方法传参到内部类中,形参必须为final类型。因为在方法print执行结束以后,变量会被释放,然而内部类的对象可能仍然在使用这个变量,只有声明为final常量,才不会导致程序报错。所以传入的参数,不可以修改值

局部内部类不可以使用public或private访问修饰符

2.3、静态内部类

 静态内部类,修饰符为static的内部类

package com.my.po;

/**
 * description:{description}
 * author:jyy
 * date:2018-02-08 16:53
 * modify:{modify}
 */
public class Outer {

    private static String name = "李明";

    public static class Inner {

        public void print() {
            System.out.println(name);
        }
    }
}

可以直接使用Outer.Inner的方式进行调用,静态类中只可以访问外部类的静态属性和方法

2.4、匿名内部类

以上三种内部类,我们在平常编程中使用较少。但是匿名内部类我们会经常遇到,在各种框架中也经常出现它的身影。同时后面的篇章中会说到的lambda表达式,很大的一个作用就是为了简便匿名内部类的写法。所以我们要着重讲一下匿名内部类,上面三种内部类,有所了解即可

在2.2中我们说到了局部内部类,如果我们再深入一步。假如,在方法中内部类,我们只创建这个类的一个对象,我们就可以不用声明了,这种类被称为匿名内部类。前提是这个内部类要有父类或者实现某个接口

package com.my.po;

/**
 * description:{description}
 * author:jyy
 * date:2018-02-09 15:03
 * modify:{modify}
 */
public interface Printable {

    void print();
}
package com.my.po;

/**
 * description:{description}
 * author:jyy
 * date:2018-02-08 16:53
 * modify:{modify}
 */
public class Outer {

    public void start() {
        class Inner implements Printable {
            @Override
            public void print() {
                System.out.println("实现接口的局部内部类");
            }
        }
        Inner inner = new Inner();
        inner.print();

        System.out.println("======================");

        Printable printable = new Printable() {
            @Override
            public void print() {
                System.out.println("匿名内部类,重写了接口的方法");
            }
        };
        printable.print();
    }
}
package com.my.controller;

import com.my.po.Outer;
import junit.framework.TestCase;
import org.junit.Test;

/**
 * description:{description}
 * author:jyy
 * date:2018-01-09 16:43
 * modify:{modify}
 */
public class AppTest extends TestCase {

    @Test
    public void test() {

        Outer outer = new Outer();
        outer.start();
    }
}

执行结果:

实现接口的局部内部类
======================
匿名内部类,重写了接口的方法
package com.my.controller;

import com.my.po.Outer;
import junit.framework.TestCase;
import org.junit.Test;

/**
 * description:{description}
 * author:jyy
 * date:2018-01-09 16:43
 * modify:{modify}
 */
public class AppTest extends TestCase {

    @Test
    public void test() {

        Object obj1 = new Object();
        System.out.println(obj1.toString());

        Object obj2 = new Object() {
            public String toString() {
                return "ok";
            }
        };
        System.out.println(obj2.toString());
    }
}

执行结果:

java.lang.Object@306a30c7
ok

匿名内部类同样不可以使用public或private访问修饰符,同样也不可以被static修饰

匿名内部类是唯一一个没有构造器的类,因为构造器的名字必须和类名相同,而匿名内部类连类名都没有

同样父类方法start的传参也是默认final修饰的,也就是说匿名内部类不能修改传入参数的值

三、匿名内部类的使用

匿名内部类的主要使用场景:1、事件监听2、回调

请看下面的示例

        try {
            /**
             * 代码
             */
        } catch (Exception e) {
            logger.error("This is error message.Exception:" + e);
            e.printStackTrace();
        } finally {
            System.out.println("退出");
        }

上面这种格式,相信大家都很熟悉,当我们需要监控代码异常的时候,会在程序大量使用这种代码。然而这段代码中只有try中的部分是需要经常变动的,其他的部分基本不变,我们试着用匿名内部类的方式,重写这部分代码

首先定义一个接口

package com.my.controller;

/**
 * description:{description}
 * author:jyy
 * date:2018-02-09 15:42
 * modify:{modify}
 */
public interface CatchExceptionable {
    void catchException();
}

再定义一个异常处理的模板类

package com.my.controller;


import org.apache.log4j.Logger;

/**
 * description:{description}
 * author:jyy
 * date:2018-02-09 15:40
 * modify:{modify}
 */
public class ExceptionTemplate {

    private static Logger logger = Logger.getLogger(ExceptionTemplate.class);

    public void execute(CatchExceptionable catchExceptionable) {
        try {
            catchExceptionable.catchException();
        } catch (Exception e) {
            logger.error("This is error message.Exception:" + e);
            e.printStackTrace();
        } finally {
            System.out.println("退出");
        }
    }
}

当我们写代码的时候,可以像如下方式进行调用,这样我们就不必重复的写try catch这类代码了,提高了复用性

        new ExceptionTemplate().execute(new CatchExceptionable() {
            @Override
            public void catchException() {
                /**
                 * 代码
                 */
            }
        });
原文地址:https://www.cnblogs.com/maikucha/p/8431503.html