🔥你不知道的Java内部类

前言

文本已收录至个人GitHub仓库,欢迎Star: https://github.com/bin3923282...
种一棵树最好的时间是十年前,其次是如今
我知道不少人不玩 qq了,可是怀旧一下,欢迎加入六脉神剑Java菜鸟学习群,群聊号码: 549684836 鼓励你们在技术的路上写博客

絮叨

看了不少源码,都有用到内部类,可是本身之前在生产环境上,用的确实少,也有用过可是不多,因此今天就打算好好的把它从头至尾的过一遍。
可能我写文章有点乱,可是我是发现本身少了啥,我就补啥,若是是写系列的话,就确定是从头至尾的

定义

能够将一个类的定义放在里另外一个类的内部,这就是内部类,所谓的内部类的概念只是出如今编译阶段,对于jvm层是没有内部类这个概念的。咱们能够利用内部类来解决git

  • 类的单继承问题,外部类不能再继承的类能够交给内部类继承
  • 咱们能够经过定义内部类来实现一个类私属于一个类,实现更好的封装性
  • 代码优化:它须要更少的代码

分类

内部类能够分为:github

  • 静态内部类。
  • 非静态内部类。

非静态内部类又能够分为:编程

  • 成员内部类。
  • 方法内部类。
  • 匿名内部类。

静态内部类

我感受这个是用的最多的,你好比说Redis的key的设计, 由于咱们要中间拼接:号,因此用静态内部类去组成不一样的key是很是好的,这样可让相同类型的key再同一个文件目录下jvm

静态内部类的定义和普通的静态变量或者静态方法的定义方法是同样的,使用static关键字,只不过此次static是修饰在class上的,通常而言,只有静态内部类才容许使用static关键字修饰,普通类的定义是不能用static关键字修饰的,这一点须要注意一下。下面定义一个静态内部类:ide

public class Out {
    private static String name;
    private int age;

    public static class In{
        private int age;
        public void sayHello(){
            
            System.out.println("my name is : "+name);
            //--编译报错---
            //System.out.println("my age is :"+ age);
        }
    }
}

在上述代码中,In这个类就是一个静态内部类。咱们说内部类是能够访问外部类的私有字段和私有方法的,对于静态内部类,它遵循一致的原则,只能访问外部类的静态成员。上述代码中,外部类的非静态私有字段age在静态内部类中使不容许访问的,而静态字段name则是可访问的。下面咱们看,如何建立一个静态内部类的实例对象。函数式编程

public static void main(String [] args){
    Out.In innerClass = new Out.In();
    innerClass.sayHello();
}

使用场景,通常来讲,对于和外部类联系紧密可是并不依赖于外部类实例的状况下,能够考虑定义成静态内部类。下面咱们看稍显复杂的成员内部类。函数

成员内部类

咱们说了,四种不一样类型的内部类都各自有各自的使用场景,静态内部类适合于那种和外部类关系密切可是并不依赖外部类实例的状况。可是对于须要和外部类实例相关联的状况下,能够选择将内部类定义成成员内部类。如下代码定义了一个简单的成员内部类:学习

public class Out {
    private String name;

    public void showName(){
        System.out.println("my name is : "+name);
    }

    public class In{
        public void sayHello(){
            System.out.println(name);
            Out.this.showName();
        }
    }
}

以上定义了一个简单的内部类In,咱们的成员内部类能够直接访问外部类的成员字段和成员方法,由于它是关联着一个外部类实例的。下面咱们看看在外部是如何建立该内部类实例的。优化

public static void main(String [] args){
    Out out = new Out();
    out.setName("六脉神剑")
    Out.In in = out.new In();
    in.sayHello();
}

由于成员内部类是关联着一个具体的外部类实例的,因此它的实例建立必然是由外部类实例来建立的。对于实例的建立,咱们只须要记住便可,成员内部类的实例建立须要关联外部类实例对象,静态内部类实例建立相对简单。下面咱们主要看看在编译阶段编译器是如何保持内部类对外部类成员信息可访问的。this

使用场景,对于那种要高度依赖外部类实例的状况下,定义一个成员内部类则会显的更加明智。

方法内部类

方法内部类,顾名思义,定义在一个方法内部的类。方法内部类相对而言要复杂一些,下面定义一个方法内部类:

public class Out {
    private String name;

    public void sayHello(){
        class In{
            public void showName(){
                System.out.println("my name is : "+name);
            }
        }

        In in = new In();
        in.showName();
    }
}

咱们定义了一个类,在该类中又定义了一个方法sayHello,然而在该方法中咱们定义了一个内部类,类In就是一个方法内部类。咱们的方法内部类的生命周期不超过包含它的方法的生命周期,也就是说,方法内部类只能在方法中使用。因此在声明的时候,任何的访问修饰符都是没有意义的,因而Java干脆不容许使用任何的访问修饰符修饰方法内部类。其中还须要注意一点的是,定义和使用时两回事,别看那一大串定义类的代码,你实际想要使用该类,就必须new对象,而对于方法内部类而言,只能在方法内部new对象。这就是方法内部类的简单介绍,下面咱们看看其实现原理。

有关方法内部类的实现原理实际上是和成员内部类差不太多的,也是在内部类初始化的时候为其传入一个外部类实例,区别在哪呢?就在于方法内部类是定义在具体方法的内部的,因此该类除了能够经过传入的外部实例访问外部类中的字段和方法,对于包含它的方法中被传入的参数也会随着外部类实例一块儿初始化给内部类。

毋庸置疑的是,方法内部类的封装性比以前介绍的两种都要完善。因此通常只有在须要高度封装的时候才会将类定义成方法内部类。

匿名内部类

可能内部类的全部分类中,匿名内部类的名号是最大的,也是咱们最经常使用到的,多见于函数式编程,lambda表达式等。下面咱们重点看看这个匿名内部类。

匿名内部类就是没有名字的内部类,在定义完成同时,实例也建立好了,经常和new关键字紧密结合。固然,它也不局限于类,也能够是接口
,能够出如今任何位置。下面咱们定义一个匿名内部类:

若是您必须重写类或接口的方法,则应该使用它。能够经过两种方式建立Java匿名内部类

//首先定义一个普通类
public class Out {
    private String name;

    public void sayHello(){
        System.out.println("my name is :" + name);
    }
}
//定义和使用一个匿名内部类
public static void main(String [] args){
    Out out = new Out(){
        @Override
        public void sayHello(){
            System.out.println("my name is cyy");
        }
        public void showName(){
            System.out.println("hello single");
        }
    };
    out.sayHello();
}

从上述代码中能够很显然的让咱们看出来,咱们的匿名内部类一定是要依托一个父类的,由于它是没有名字的,没法用一个具体的类型来表示。因此匿名内部类每每都是经过继承一个父类,重写或者从新声明一些成员来实现一个匿名内部类的定义。实际上仍是利用了里式转换原理。

其实在看了上述三种内部类的原理以后,反而以为匿名内部类的实现较为简单了。主要思路仍是将内部类抽离出来,经过初始化传入外部类的实例以达到对外部类全部成员的访问。只是在匿名内部类中,被依托的父类不是他的外部类。匿名内部类的主要特色在于,没有名字,对象只能被使用一次,能够出如今任意位置。因此它的使用场景也是呼之欲出,对于一些对代码简洁度有所要求的状况下,可首选匿名内部类。

总结

以上完成了对四种内部类的简单介绍,对于他们各自实现的原理也都已经介绍过了。其实大体相同,因为jvm对每一个类都要求一个单独的源码文件,因此编译阶段就完成了分离的操做,可是在分离的过程当中又要保持内部类和外部类之间的这种联系,因而编译器添加了一些接口保持这种信息共享的结构。使用内部类能够大大增长程序的封装性,使得代码总体简洁度较高。

讲完这个后面的函数式接口 引用就好讲一点了

结尾

内部类就讲那么多,但愿你们之后看源码会轻松点,哈哈

平常求赞

好了各位,以上就是这篇文章的所有内容了,能看到这里的人呀,都是 人才

创做不易,各位的支持和承认,就是我创做的最大动力,咱们下篇文章见

六脉神剑 | 文 【原创】若是本篇博客有任何错误,请批评指教,不胜感激 !

相关文章
相关标签/搜索