内部类,一帖足矣

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

 

目录

前言

一、内部类是什么?

1,原理

2,特性

3,用途

4,内部类创建方式:

二,内部类种类

1,普通内部类

2,局部内部类(在方法和作用域内的内部类)

 

3,匿名内部类

4,静态内部类

总结



前言

因为内部类语法较为复杂,接触少的难以理解,网上的文章描述内部类的较少,虽然内部类平时用的地方不多,但是不能否认它的重要性,看过java核心类库源码的应该知道,内部类在里面大有作为。于是有了此文章,此文章描述了内部类的基本语法以及底层原理实现,以后要是忘记了,自己也可以看看。

 


提示:以下是本篇文章正文内容,下面案例可供参考

一、内部类是什么?

1,原理

简单来说就是一个类定义在一个类的内部。

首先要明确,内部类和外部类都是独立的个体,内部类在内存中跟类一样,也有存储空间。

想法一:既然内部类也有存储空间,在虚拟机中和平常的类没什么俩样,用普通类不就成了吗,何必用内部类?它存在的意义是什么。

答:1,因为内部类可以访问外围类所有的成员变量,不论访问权限类型,包括私有类型。

2,内部类可以对同一个包中的其他类隐藏。可以在外围类中设置内部类的访问权限。

想法二:既然内部类在内存中和外围类没什么俩样,都有自己的存储空间,那么,内部类为什么可以访问外围类私有类型?

答:因为在内部类中,在构造内部类的时候,外围类通过内部类构造器把自己的引用传到内部类中,内部类中会存在一个隐式的引用字段指向外围类,根据此引用可以访问外围类的一切。

请看下面的例子:

public class Duck {
    class Duck_in{

    }
}

一个非常简单的内部类例子,接下来我们去探究一下它的底层,对内部类Duck-in进行反射。

在字段文件Fields中,有一个没有定义过的字段this¥0,final类型,指向其外围类Duck。

再看构造器<init>

系统默认的构造器,在Object构成器调用之前就把外围类的引用传到了内部类中,以上就是内部类可以访问外部类私有变量的真正原因。

内部类和外部类在内存中都有其自己的空间,但是在内存中却有关联,看下面例子,把Duck与Duck-in实例化

public class Duck {
    class Duck_in{

    }
    public static void main(String... args){
        Duck duck = new Duck();
        Duck_in duck_in = duck.new Duck_in();
        System.out.println("Duck="+duck+"  Duck_in"+duck_in);
    }
}

可以看到,内部类在格式是在外围类后面 加一个美元符号在加自己的类名。

2,特性

1,因为内部类对于外部类来说类似于字段,尽管它在内存中和类一样,在外围类中可以定义它的访问权限,比如公共,保护,私有。还可以设置为静态类型。

2,创建内部类前首先要创建其外围类对象(静态内部类除外,静态内部类中没有外围类引用,只能使用静态类型),因为内部类的构造器中要拿到外围类的引用。这一点很容易理解,创建内部类的语法下面在介绍。

3,普通内部类不能拥有static型数据和字段(静态内部类除外),这个也好理解,既然你是普通型内部类,要是可以拥有静态字段的话,内部类的访问权限设置就没有什么意义了。

3,用途

内部类的用途有很多,因为它强大的隐藏机制和对外围类的访问权限。当你想某个类来实现某项功能,它就是你的强大辅助,可以帮助外围类完成相应的行为,“低调的工具人类”。因为它可以继承接口和类,就跟外围类一样。它的出现完善了java的多重继承变得完整。

打个比方,有一个鸭子类,有一个走路接口。

方案一:鸭子类直接实现此接口。

方案二:在鸭子类中定义内部类实现此接口。

既然鸭子类也可以实现接口,为什么要多此一举的用内部类实现?

当方案一可以满足需求的时候,确实应该用方案一。

什么情况不能满足?比如这个鸭子不一般,它想走,但是只有一种走路方式满足不了它,它既想它可以蹦蹦跳跳,又可以慢慢悠悠,还可以嘻嘻哈哈的走,对这个走路接口,鸭子类想有多种实现的时候,内部类就派上了用场。

内部类最吸引人的原因是,每个内部类都能独立的继承自一个接口的实现,所以无论外围类是否已经继承了某个类(接口)的实现,对于内部类都没有影响“       ——《java编程思想》

private内部类给类的设计的设计者提供了一种途径,通过这种方式可以完全阻止任何依赖与类型的编码,并且完全隐藏了实现了细节“  ——《java编程思想》

注:内部类的访问权限体系跟类的成员变量一样

这俩句话实在是太精辟了,必须贴上来。

鄙人浅薄见解:一个内部类就相当于隐藏的自己在偷偷摸摸学神功,我可以有无数个隐藏的我学不同的神功,我还可以对同一种神功有不同的方向。

其实内部类的作用不仅仅于此,它更多的作用是构建类体系,完善java多重继承体系。

4,内部类创建方式:

前问我们提到了内部类中有一个指向外围类的引用,它的表达式

OuterClass.this

编写内部类对象构造器

outerObject.new InnerClass(构造器参数) //系统默认构造器没有参数

public class Duck {
    private int x;
    class Duck_in{
        void f(){
            x=1;
            Duck.this.x=2;//这是补全了引用,效果一样
        }
    }
    public static void main(String... args){
        Duck duck = new Duck();
        Duck_in duck_in = duck.new Duck_in();
    }
}

二,内部类种类

1,普通内部类

普通内部类就是正正经经的在外围类中自己定义的类就像上面的Duck-in。很好理解

格式:

public class Duck {
    class Duck_in{

    }
}

2,局部内部类(在方法和作用域内的内部类)

这个范围就很广了,可以在方法中直接定义一个内部类,注意是任意的作用域。

比如在接口中定义内部类,接口就用之前的走路接口。

interface Walk{
    void walkmethod();
    class A{

    }
}

那么它有什么用途呢?

接口中的任何成员都是public static类型的,当你想让接口还提供别的公开资源的时候,在接口中定义内部类,它就可以向接口实现类提供-接口额外公开的资源。

还可以在方法中定义内部类:

    public Walk getwalk(){
        class Duck_Walk implements Walk{
            @Override
            public void walkmethod() {
                print("走路");
            }
        }
        Duck_Walk duck_walk = new Duck_Walk();
        return duck_walk;
    }

此方法是像外围类返回一个实现走路接口的内部类。注意并不意味着方法运行完毕就不可用了,它返回的是一个Walk引用,仍然可以调用Walk接口中的方法,实现多态。

还可以这样

 if(1==1){
            class If{
                
            }
        }

是不是觉得很离谱???这样都行!

没错,注意是任意作用域!任意作用域!任意作用域!不过在if中定义的并不是说明它有创造条件,其实它和别的类一起编译过了,不过出了if{}外,它就不可用了,别的和正常类一样,此用途非常罕见。

这个用途也很好理解,当满足条件的时候,就对它进行实例化操作。

细节

注:局部内部类不仅可以访问外部类变量,还可以访问变量,但是它必须是final型的变量

这是因为final型变量在编译的时侯就已经别确认了值,这样是为了保证安全性,这也说明,他们一旦赋值就绝对不会改变。

可以通过向方法传递值来初始化构造器,还是那个例子。

public Walk getwalk(final String walkname){
        class Duck_Walk implements Walk{
            private String walkname1;
            Duck_Walk(String name){this.walkname1 = name;}
            @Override
            public void walkmethod() {
                print("走路");
            }
        }
        Duck_Walk duck_walk = new Duck_Walk(walkname);
        return duck_walk;
    }

 

 

 

3,匿名内部类

这个种类的内部类不配拥有姓名,深藏功与名,因为没有姓名,所以没有构造器。他就是局部内部类它弟弟!它的存在就是为了简化代码!当然还有别的作用。可以把它简单的理解为局部内部类的简易版。但是它和局部内部类还是有差异,先看几个例子,具体细节往下看:

//注意这个方法是原来的局部内部类,贴过来对比更直观
public Walk getwalk(){
        class Duck_Walk implements Walk{
            @Override
            public void walkmethod() {
                print("走路");
            }
        }
        Duck_Walk duck_walk = new Duck_Walk();
        return duck_walk;
    }


//分界线

 public Walk getwalk1(){
        return new Walk() {
            @Override
            public void walkmethod() {
                    print("走路");
            }
        };
    }

可以很直观的看到,代码少了点(咳咳),我认为当在方法中如果只返回内部类引用,而不用在方法中用这个引用做些别的事情,那么用匿名内部类就更符合我们的编程想法和意图

问题:匿名内部类的父类如果需要用有参构造器怎么办?可以这样

 public Walk getwalk1(){
        return new Walk() {
            {
                //在代码块进行初始化
            }
            @Override
            public void walkmethod() {
                    print("走路");
            }
        };

以上就是匿名内部类了

问题:既然匿名内部类和局部内部类很相似,那么什么时候用匿名内部类?什么时候用局部内部类?

当需要对内部类进行构造器重载的时候,只能用局部内部类,因为匿名内部类只能在代码块进行成员初始化,不能完成构造器重载。

其他具体情况具体分析,看你想要这个内部类完成什么行为。

4,静态内部类

静态内部类就是在类名前面加上static

细节:

用法类似于static方法,里面只能访问静态变量。

里面没有外围类的引用。

而且和其他内部类不同的是里面可以拥有静态方法和静态成员。

因为没有外围类引用,要创建静态内部类对象并不需要外围类的对象。

public class Duck {
    
    static  class Duck_Static{
        static int x=0;
        static void p(){
            print("p()被调用");
        }
    }
   
    public static void main(String... args){
        Duck.Duck_Static.p();
        Duck_Static.x=2;
    }
}

什么时候用静态内部类?

当不需要用到外围类引用的时候就应该使用静态内部类,这样更节省空间

总结

以上就是内部类的相关语法。

内部类用法较复杂,内部类也可以被继承,里面也能嵌套内部类,套娃大法。内部类对于构建类体系有重要的作用,用多了就理解了。

 

-优雅结束-

 

 


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值