• 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() {
                    /**
                     * 代码
                     */
                }
            });
  • 相关阅读:
    转换上午下午 时间
    Canvas
    DOS Card (线段树)(hud杭电多校)
    ShuanQ (取模的小知识)(杭电多校)
    Painting Game (博弈论)
    Two Permutations (DP搜索的方式) (2022杭电多校3)
    Slayers Come (区间覆盖种类数(dp+线段树)+ st表+二分,或者线段树) (2022杭电多校2)
    copy (倒叙离线+bitset+^特性) (hud 2022多校 2)
    Static Query on Tree (述链剖分+线段树)(2022杭电多校)
    Fast Bubble Sort (单调zai+倍增) (2022杭电多校10)
  • 原文地址:https://www.cnblogs.com/maikucha/p/8431503.html
Copyright © 2020-2023  润新知