java提高篇之详解内部类

转载自 java提高篇之详解内部类

内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二)。

第一次见面

内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
publicclass OuterClass {
    privateString name ;
    privateint age;
    publicString getName() {
        returnname;
    }
    publicvoid setName(String name) {
        this.name = name;
    }
    publicint getAge() {
        returnage;
    }
    publicvoid setAge(intage) {
        this.age = age;
    }
    classInnerClass{
        publicInnerClass(){
            name = "chenssy";
            age = 23;
        }
    }
}

在这里InnerClass就是内部类,对于初学者来说内部类实在是使用的不多,鄙人菜鸟一个同样没有怎么使用过(貌似仅仅只在做swing 注册事件中使用过),但是随着编程能力的提高,我们会领悟到它的魅力所在,它可以使用能够更加优雅的设计我们的程序结构。在使用内部类之间我们需要明白为什么要使用内部类,内部类能够为我们带来什么样的好处。

一、为什么要使用内部类

为什么要使用内部类?在《Think in java》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
publicinterface Father {
}
publicinterface Mother {
}
publicclass Son implementsFather, Mother {
}
publicclass Daughter implementsFather{
    classMother_ implementsMother{
    }
}

其实对于这个实例我们确实是看不出来使用内部类存在何种优点,但是如果Father、Mother不是接口,而是抽象类或者具体类呢?这个时候我们就只能使用内部类才能实现多重继承了。

其实使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,但是如果我们不需要解决多重继承问题,那么我们自然可以使用其他的编码方式,但是使用内部类还能够为我们带来如下特性(摘自《Think in java》):

  1、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。

2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。

3、创建内部类对象的时刻并不依赖于外围类对象的创建。

4、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。

5、内部类提供了更好的封装,除了该外围类,其他类都不能访问。

二、内部类基础

在这个部分主要介绍内部类如何使用外部类的属性和方法,以及使用.this与.new。

当我们在创建一个内部类的时候,它无形中就与外围类有了一种联系,依赖于这种联系,它可以无限制地访问外围类的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
publicclass OuterClass {
    privateString name ;
    privateint age;
    /**省略getter和setter方法**/
    publicclass InnerClass{
        publicInnerClass(){
            name = "chenssy";
            age = 23;
        }
        publicvoid display(){
            System.out.println("name:"+ getName() +"   ;age:" + getAge());
        }
    }
    publicstatic void main(String[] args) {
        OuterClass outerClass = newOuterClass();
        OuterClass.InnerClass innerClass = outerClass.newInnerClass();
        innerClass.display();
    }
}
--------------
Output:
name:chenssy   ;age:23

在这个应用程序中,我们可以看到内部了InnerClass可以对外围类OuterClass的属性进行无缝的访问,尽管它是private修饰的。这是因为当我们在创建某个外围类的内部类对象时,此时内部类对象必定会捕获一个指向那个外围类对象的引用,只要我们在访问外围类的成员时,就会用这个引用来选择外围类的成员。

其实在这个应用程序中我们还看到了如何来引用内部类:引用内部类我们需要指明这个对象的类型:OuterClasName.InnerClassName。同时如果我们需要创建某个内部类对象,必须要利用外部类的对象通过.new来创建内部类: OuterClass.InnerClass innerClass = outerClass.new InnerClass();。

同时如果我们需要生成对外部类对象的引用,可以使用OuterClassName.this,这样就能够产生一个正确引用外部类的引用了。当然这点实在编译期就知晓了,没有任何运行时的成本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
publicclass OuterClass {
    publicvoid display(){
        System.out.println("OuterClass...");
    }
    publicclass InnerClass{
        publicOuterClass getOuterClass(){
            returnOuterClass.this;
        }
    }
    publicstatic void main(String[] args) {
        OuterClass outerClass = newOuterClass();
        OuterClass.InnerClass innerClass = outerClass.newInnerClass();
        innerClass.getOuterClass().display();
    }
}
-------------
Output:
OuterClass...

到这里了我们需要明确一点,内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)。对于一个名为OuterClass的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.class和OuterClass$InnerClass.class。

在Java中内部类主要分为成员内部类、局部内部类、匿名内部类、静态内部类。

三、成员内部类

成员内部类也是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有 成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。

在成员内部类中要注意两点,第一:成员内部类中不能存在任何static的变量和方法;第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
publicclass OuterClass {
    privateString str;
    publicvoid outerDisplay(){
        System.out.println("outerClass...");
    }
    publicclass InnerClass{
        publicvoid innerDisplay(){
            //使用外围内的属性
            str = "chenssy...";
            System.out.println(str);
            //使用外围内的方法
            outerDisplay();
        }
    }
    /*推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 */
    publicInnerClass getInnerClass(){
        returnnew InnerClass();
    }
    publicstatic void main(String[] args) {
        OuterClass outer = newOuterClass();
        OuterClass.InnerClass inner = outer.getInnerClass();
        inner.innerDisplay();
    }
}
--------------------
chenssy...
outerClass...

      推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 。

四、局部内部类

有这样一种内部类,它是嵌套在方法和作用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。

对于局部内部类实在是想不出什么好例子,所以就引用《Think in java》中的经典例子了。

定义在方法里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
publicclass Parcel5 {
    publicDestionation destionation(String str){
        classPDestionation implementsDestionation{
            privateString label;
            privatePDestionation(String whereTo){
                label = whereTo;
            }
            publicString readLabel(){
                returnlabel;
            }
        }
        returnnew PDestionation(str);
    }
    publicstatic void main(String[] args) {
        Parcel5 parcel5 = newParcel5();
        Destionation d = parcel5.destionation("chenssy");
    }
}

定义在作用域内:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
publicclass Parcel6 {
    privatevoid internalTracking(booleanb){
        if(b){
            classTrackingSlip{
                privateString id;
                TrackingSlip(String s) {
                    id = s;
                }
                String getSlip(){
                    returnid;
                }
            }
            TrackingSlip ts = newTrackingSlip("chenssy");
            String string = ts.getSlip();
        }
    }
    publicvoid track(){
        internalTracking(true);
    }
    publicstatic void main(String[] args) {
        Parcel6 parcel6 = newParcel6();
        parcel6.track();
    }
}

五、匿名内部类

在做Swing编程中,我们经常使用这种方式来绑定事件

1
2
3
4
5
6
button2.addActionListener( 
                newActionListener(){ 
                    publicvoid actionPerformed(ActionEvent e) { 
                        System.out.println("你按了按钮二"); 
                    
                });

我们咋一看可能觉得非常奇怪,因为这个内部类是没有名字的,在看如下这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
publicclass OuterClass {
    publicInnerClass getInnerClass(finalint num,String str2){
        returnnew InnerClass(){
            intnumber = num + 3;
            publicint getNumber(){
                returnnumber;
            }
        };       /* 注意:分号不能省 */
    }
    publicstatic void main(String[] args) {
        OuterClass out = newOuterClass();
        InnerClass inner = out.getInnerClass(2,"chenssy");
        System.out.println(inner.getNumber());
    }
}
interfaceInnerClass {
    intgetNumber();
}
----------------
Output:
5

这里我们就需要看清几个地方

1、 匿名内部类是没有访问修饰符的。

  2、 new 匿名内部类,这个类首先是要存在的。如果我们将那个InnerClass接口注释掉,就会出现编译出错。

3、 注意getInnerClass()方法的形参,第一个形参是用final修饰的,而第二个却没有。同时我们也发现第二个形参在匿名内部类中没有使用过,所以当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final。

4、 匿名内部类是没有构造方法的。因为它连名字都没有何来构造方法。

PS:由于篇幅有限,对匿名内部类就介绍到这里,有关更多关于匿名内部类的知识,我就会在下篇博客(java提高篇—–详解匿名内部类)做详细的介绍,包括为何形参要定义成final,怎么对匿名内部类进行初始化等等,敬请期待……

六、静态内部类

在java提高篇—–关键字static中提到Static可以修饰成员变量、方法、代码块,其他它还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,不过我们更喜欢称之为嵌套内部类。静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:

1、 它的创建是不需要依赖于外围类的。

2、 它不能使用任何外围类的非static成员变量和方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
publicclass OuterClass {
    privateString sex;
    publicstatic String name = "chenssy";
    /**
     *静态内部类
     */
    staticclass InnerClass1{
        /* 在静态内部类中可以存在静态成员 */
        public static String _name1 = "chenssy_static";
        public void display(){
            /*
             * 静态内部类只能访问外围类的静态成员变量和方法
             * 不能访问外围类的非静态成员变量和方法
             */
            System.out.println("OutClass name :" + name);
        }
    }
    /**
     * 非静态内部类
     */
    class InnerClass2{
        /* 非静态内部类中不能存在静态成员 */
        public String _name2 = "chenssy_inner";
        /* 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的 */
        public void display(){
            System.out.println("OuterClass name:" + name);
        }
    }
    /**
     * @desc 外围类方法
     * @author chenssy
     * @data 2013-10-25
     * @return void
     */
    public void display(){
        /* 外围类访问静态内部类:内部类. */
        System.out.println(InnerClass1._name1);
        /* 静态内部类 可以直接创建实例不需要依赖于外围类 */
        new InnerClass1().display();
        /* 非静态内部的创建需要依赖于外围类 */
        OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
        /* 方位非静态内部类的成员需要使用非静态内部类的实例 */
        System.out.println(inner2._name2);
        inner2.display();
    }
    publicstatic void main(String[] args) {
        OuterClass outer = newOuterClass();
        outer.display();
    }
}
----------------
Output:
chenssy_static
OutClass name :chenssy
chenssy_inner
OuterClass name:chenssy

上面这个例子充分展现了静态内部类和非静态内部类的区别。

到这里内部类的介绍就基本结束了!对于内部类其实本人认识也只是皮毛,逼近菜鸟一枚,认知有限!我会利用这几天时间好好研究内部类!



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/330121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

rabbitmq-通配符模式

【README】 本文介绍 通配符模式,及代码示例 【1】intro to rabbitmq通配符模式 0)通配符模式-交换机类型为 Topic; 1)与路由模式相比,相同点是 两者都可以通过 routingkey 把消息转发到不同的队列; 不同…

中海达手部链接电脑安装软件_山东水文局:较大含沙量条件下中海达ADCP外接测深仪测流系统试验成功...

近日,黄委山东水文水资源局与中海达海洋公司联合,在黄河泺口水文站较大含沙量环境下进行了ADCP外接测深仪、GNSS测流试验,试验取得了圆满成功。泺口水文站简介1泺口水文站概况泺口水文站位于山东省济南市天桥区黄河泺口浮桥南岸,隶…

一文搞懂ThreadLocal及相关的内存泄露问题

首先,看一张整体的结构图,来帮助理解 什么是ThreadLocal ThreadLocal用于创建线程局部变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,…

resnet50加入fpn_FPN+SSD同时兼顾速度和精度的检测器(二)

本文首发于知乎专栏“人工智能从入门到逆天杀神”,本文以及本专栏所有算法源代码都可以在神力AI平台获取,如果你没有GPU但需要预训练模型或者你想获取更多开箱即用的AI算法,欢迎加入我们的会员,一杯咖啡即可带你入门AI&#xff0c…

Java秒杀系统实战系列~RabbitMQ死信队列处理超时未支付的订单(转)

转自: https://juejin.cn/post/6844903903130042376 文末有源代码,非常棒 摘要: 本篇博文是“Java秒杀系统实战系列文章”的第十篇,本篇博文我们将采用RabbitMQ的死信队列的方式处理“用户秒杀成功生成订单后,却迟…

主流Java数据库连接池比较及前瞻

转载自 主流Java数据库连接池比较及前瞻主流数据库连接池 常用的主流开源数据库连接池有C3P0、DBCP、Tomcat Jdbc Pool、BoneCP、Druid等 C3p0: 开源的JDBC连接池,实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibern…

有序数组中查找第一个比target大的数

思路&#xff1a;二分法 时间复杂度&#xff1a;O(logn) 空间复杂度&#xff1a;O(1) 代码&#xff1a; public class Solution{public int search(int[] nums, int target){int lf0, rtn.length-1;while(lf<rt){int mid lf(rt-lf)/2;if(n[mid]>t){rt mid-1;}else{lf…

2020最新Java线程池入门(超详细)

转 https://blog.csdn.net/weixin_43893397/article/details/104361154 【1】代码示例 /*** 线程池测试-自定义线程池创建方式* since 2021/03/23 */ public class ThreadPoolMain2 {public static void main(String[] args) throws Exception {newMethod();}public static…

HDFS的诞生

转载自 HDFS的诞生 1牛刀小试 张大胖找了个实习的工作&#xff0c; 第一天上班Bill师傅给他分了个活儿&#xff1a;日志分析。张大胖拿到了师傅给的日志文件&#xff0c;大概有几十兆&#xff0c;打开一看&#xff0c; 每一行都长得差不多&#xff0c;类似这样&#xff1a;212.…

项目背景怎么描述_产品经理写简历,如何让「项目经验」更出众?

项目经验怎么写更出众&#xff1f;时间长但效果一般的项目经验要不要写&#xff1f;没有项目经验怎么办&#xff1f;本文凭借作者自己长期招聘产品、阅读大量简历所积累的经验解答了这三个问题&#xff0c;希望对你有所帮助。产品经理写简历时&#xff0c;都会通过项目案例来证…

一致性Hash算法原理

前言 当在需要将数据分发到多个数据库/缓存&#xff0c;或将请求分发给多个服务节点时&#xff0c;不可避免的会遇到以下问题&#xff1a; 如何将数据均匀的分散到各个节点中&#xff0c;并且尽量的在加减节点时能使受影响的数据最少。 选择节点的方法 随机放置 从多个节点…

转: java多线程-ThreadPoolExecutor的拒绝策略RejectedExecutionHandler

转自&#xff1a; https://blog.csdn.net/qq_25806863/article/details/71172823 概述 原文地址 http://blog.csdn.net/qq_25806863/article/details/71172823 在分析ThreadPoolExecutor的构造参数时&#xff0c;有一个RejectedExecutionHandler参数。 RejectedExecutionH…

已知两点坐标如何快速增加其他坐标_「测绘精选」坐标转换概述

引言&#xff1a;这篇“坐标转换概述”献给各位&#xff0c;可以对坐标转换有一个大致地、整体地了解。文中有些名词是为了便于表达而自创的&#xff0c;大家不用考据、较真。一、静态坐标和动态坐标(1)静态坐标传统大地测量没有考虑板块运动对坐标的影响。虽然板块运动客观存在…

什么是G1垃圾回收算法

转载自 什么是G1垃圾回收算法为解决CMS算法产生空间碎片和其它一系列的问题缺陷&#xff0c;HotSpot提供了另外一种垃圾回收策略&#xff0c;G1&#xff08;Garbage First&#xff09;算法&#xff0c;通过参数 -XX:UseG1GC来启用&#xff0c;该算法在JDK 7u4版本被正式推出&am…

一文理清RocketMQ顺序消费、重复消费、消息丢失问题

前言 在使用消息队列时不可避免的会遇到顺序消费、重复消费、消息丢失三个问题。在一次面试字节的时候&#xff0c;面试官问到如何保证顺序消费&#xff0c;当时回答不太准确&#xff0c;特意此文回顾如何解决顺序消费、重复消费、消息丢失三个问题。 重复消费 解决重复消费…

一道丧心病狂的java面试题

转载自 一道丧心病狂的java面试题无意中了解到如下题目&#xff0c;觉得蛮好。 题目如下&#xff1a; public class TestSync2 implements Runnable {int b 100; synchronized void m1() throws InterruptedException {b 1000;Thread.sleep(500); //6System.out.pri…

水晶报表图形位置_看了我用Excel做的年度报表,老板直夸好

2020年前5个月&#xff0c;最火爆的莫过于口罩。口罩的整条产业链都变得炙手可热&#xff0c;口罩、口罩机、炒熔喷布、聚丙烯等等相关企业的业务数据往往都是去年的几倍。那我们现在作为一家“表姐牌”的口罩厂的员工&#xff0c;老板叫我用Excel做一个既酷炫又简洁的年度报表…

Mysql优化(三):优化order by

MySQL中的两种排序方式 .通过有序索引顺序扫描直接返回有序数据 因为索引的结构是B树&#xff0c;索引中的数据是按照一定顺序进行排列的&#xff0c;所以在排序查询中如果能利用索引&#xff0c;就能避免额外的排序操作。EXPLAIN分析查询时&#xff0c;Extra显示为Using inde…

漫画:什么是服务熔断

转载自 漫画&#xff1a;什么是服务熔断什么是服务熔断&#xff1f;熔断这一概念来源于电子工程中的断路器&#xff08;Circuit Breaker&#xff09;。在互联网系统中&#xff0c;当下游服务因访问压力过大而响应变慢或失败&#xff0c;上游服务为了保护系统整体的可用性&#…

rabbitmq手动确认ack

【README】 参考 https://blog.csdn.net/u012943767/article/details/79300673 &#xff1b; 【0】声明交换机&#xff0c;队列 与绑定 /*** 交换机&#xff0c;队列声明与绑定 */ public class AckDeclarer {/** 确认交换机 */public static final String ACK_EXCHANGE2 &q…