“内部类” 大总结(Java)
(本文整理自很久以前收集的资料(我只是做了排版修改),作者小明,链接地址没有找到,总之感谢,小明)
(后面也对“静态内部类”专门做了补充)
内部类的位置:
内部类可以作用在方法里以及外部类里,作用在方法里称为局部内部类,作用在外部类里分为实例内部类和静态内部类。
外部类和内部类的类访问修饰符:
外部类可以是public和默认的,内部类可以是public,private,protected类型的
内部类和外部类的关系:
*在【实例内部类中】
A:内部类可以访问外部类中所有的任何访问修饰符的成员。
B:而一个外部类实例对应0个或n个内部类实例。在外部类中不能直接访问内部类的成员,必须通过内部类的实例去访问才可以
*在【静态内部类中】
A:静态内部类可以直接访问外部类的静态成员,如果访问外部类的实例成员,就必须通过外部类的实例去访问。在创建内部类的实例的时候,不必创建外部类的实例.。
B:外部类可以通过完整的类名来直接访问静态内部类的静态成员,如果要访问静态内部类的非静态成员则需要创建静态内部类的一个实例才行
- public class StaticInnerTest
- {
- public static class A
- {
- public int a = 10;
- static int b = 100;
- }
- public static void main(String[] args)
- {
- StaticInnerTest.A mya = new StaticInnerTest.A();
- mya.a = 1234;
- System.out.println(""+ mya.a);
- }
- }
以上是可以运行成功的,已经经过测试
*在【局部内部类中】
A:局部内部类和实例内部类一样,可以访问外部类的所有成员
B:外部类根本不知道局部内部类的存在,所以访问不了。
内部类的分类:
1. 成员内部类(实例内部类,静态内部类)
2. 局部内部类
(一) 成员内部类(类的成员)
* (1)实例内部类:
a. 在创建实例内部类的实例的时候,外部类的实例必须已经存在
Outer.InnerTools its = new Out().new InnerTools();
以上代码等价于:
Outer out = new Outer();
Outer.InnerTools its = out.new InnerTools();
(2) 实例内部类的实例【自动】持有对外部类的实例的引用,所以在实例内部类中
可以直接访问外部类的[所有]成员(这里指的是外部类当中所有的public,protected,private,static的方法和成员变量)
之所以能访问,是因为在实例内部类中需要现有外部类的实例,才能有内部类的实例,参见(1).
Outer.InnerTools its = new Outer().newInnerTools();
【注意:】在多重嵌套中,实例内部类可以访问所有外部类的成员
- Class A
- {
- Private void methodA(){}
- Class B
- {
- Private void methodB(){}
- Class C
- {
- Private void MethodC()
- {
- methodA();
- methodB();
- }
- }
- }
- }
(3) 在实例内部类中,外部类的实例与内部类的实例是一对多的关系,一个内部类实例
只会引用一个外部类实例,而一个外部类实例对应0个或n个内部类实例,在外部类中【不能】直接访问内部类的成员,必须通过内部类的实例去访问才可以。
注意: 在实例内部类中不能定义静态成员
* (2)静态内部类
- 静态内部类的实例【不会自动持有】外部类的实例的引用,在创建内部类的实例时,不必创建外部类的实例
- Class A
- {
- Public static class B
- {
- Int v;
- }
- }
- Class Test
- {
- Public void test()
- {
- A.B b = new A.B();
- b.v = 1;
- }
- }
- 静态内部类可以直接访问外部类的静态成员,如果访问外部类的实例成员,就必须通过外部类的实例去访问
- class A
- {
- private int a1;
- private static int a2;
- public static class B
- {
- int b1 = a1;
- int b2 = a2;
- int b3 = new A().a1;
- }
- }
- 在静态内部类中可以定义静态成员和实例成员
- class A
- {
- public static class B
- {
- int v1;
- static int v2;
- public static class C
- {
- static int v3;
- }
- }
- }
- 测试类可以通过完整的类名来直接访问静态内部类的静态成员
- class A
- {
- public static class B
- {
- int v1;
- static int v2;
- public static class C
- {
- static int v3;
- int v4;
- }
- }
- }
- public class Client
- {
- public void test()
- {
- A.B b = new A.B();
- A.B.C c = new A.B.C();
- b.v1 = 1;
- b.v2 = 1;
- A.B.v1 = 1//error
- A.B.v2 = 1;
- A.B.C.v3 = 1;
- }
- }
- 局部内部类 (在一个方法中定义的内部类)
局部内部类是在一个方法当中定义的内部类,它的可见范围是当前方法。和局部变量一样,局部内部类不能使用访问修饰符(public , private , protected) 以及 static 来修饰,局部内部类的特点是:
局部内部类只能在当前方法中使用.
- class A
- {
- B b = new B();//error
- public void method()
- {
- class B
- {
- int v1;
- int v2;
- class C
- {
- int v3;
- }
- }
- B b = new B();
- B.C c = b.new C();
- }
- }
局部内部类和实例内部类一样,不能包含静态成员。
- class A
- {
- public void method()
- {
- class B
- {
- static int v1;//error
- int v2;
- static class C //error
- {
- int v3;
- }
- }
- }
- }
在局部内部类中定义的内部类同样也不能被public protected, private这些访问修饰符修饰(局部内部类不能被public protected private访问修饰符修饰)
局部内部类和实例内部类一样,可以访问外部类的所有成员,此外,局部内部类还可以访问所在方法中的final类型的参数和变量
- class A
- {
- int a;
- public void method(final int p1,int p2)
- {
- int localV1 = 1;
- final int localV2 = 2;
- class B
- {
- int b1 = a;
- int b2 = p1;
- int b3 = p2;//error
- int b4 = localV1//error
- int b5 = localV2;
- }
- }
- }
- 匿名内部类(匿名类是一种特殊的内部类,这种内部类没有自己的名字)
匿名内部类本身没有构造器,但是会调用父类中的构造器。
- public class A
- {
- A(int y)
- {
- System.out.println("不是默认构造器");
- }
- A()
- {
- System.out.println("默认构造器");
- }
- void method()
- {
- System.out.println("from A");
- }
- public static void main(String[] args)
- {
- //new A().method();//默认构造器,from A
- A a = new A()
- {
- void method()
- {
- System.out.println("我是匿名内部类中的method方法");
- }
- }
- /*也就是说匿名构造器会自动调用父类中的构造器*/
- a.method();//默认构造器,我是匿名内部类中的method方法
- }
- }
(2) 匿名内部类尽管没有构造方法,但是可以在匿名内部类中提供一段实例初始化代码,Java虚拟机会在调用了父类的构造方法之后,执行这段代码。
- public static void main(String [] args)
- {
- int v = 1;
- A a = new A(v)
- {
- {
- System.out.println("init instance");
- }
- void method()
- {
- System.out.println("from anonymous");
- }
- };
- a.method();
- }
程序执行结果如下:
another constructer//父类当中的构造器执行的结果
init instance
from anonymous
补充:
- 除了可以在外部类的方法内定义匿名类外,还可以在声明一个成员变量时定义匿名内部类。
- abstract class A
- {
- A a = new A()
- {
- void method()
- {
- System.out.println("inner");
- }
- }
- abstract void method();
- }
- 匿名内部类除了可以继承类外,还可以实现接口【重点】
- Ccass Sample
- {
- public static void main(String [] args)
- {
- Thread t = new Thread(new Runnable()
- {
- public void run()
- {
- for(int i = 0; i < 100; i++)
- {
- System.out.print(i);
- }
- }
- });
- }
- }
- 匿名内部类同局部内部类一样,可以访问外部类的所有成员,如果匿名内部类位于一个方法中,还能访问所在方法的final 类型的变量和参数
- 局部内部类和匿名内部类一样,名字在方法外都是不可见的,因此都可以起到封装类型名字的作用。
【匿名内部类和局部内部类的区别】
1. 匿名内部类的程序代码量少
2. 一个局部内部类可以有多个重载构造方法,并且测试类可以多次创建局部内部类的实例。而匿名内部类没有重载构造方法之说,并且只能创建一次实例。
因此,如果只需要创建内部类的一个实例,可以使用匿名内部类,它能使程序代码简洁明了,如果需要多次创建内部类的实例,那么使用局部内部类。
内部类小结:
作者: 小明
参考资料: <<Core Java>>
静态内部类补充:
原来连接:http://www.2cto.com/kf/201304/206692.html
整理如下:(链接的作者原文中有很多错别字,但是大体意思很清楚,讲解也清楚)
一般情况下是不可以用static修饰类的。如果一定要用static修饰类的话,通常static修饰的是匿名内部类。 在一个类中创建另外一个类,叫做成员内部类。这个成员内部类可以静态的(利用static关键字修饰),也可以是非静态的。由于静态的内部类在定义、使用的时候会有种种的限制。所以在实际工作中用到的并不多。 在开发过程中,内部类中使用的最多的还是非静态地成员内部类。不过在特定的情况下,静态内部类也能够发挥其独特的作用。 一、静态内部类的使用目的。 在定义内部类的时候,可以在其前面加上一个权限修饰符static。此时这个内部类就变为了静态内部类。不过由于种种的原因,如使用上的限制等等因素(具体的使用限制,笔者在下面的内容中会详细阐述),在实际工作中用的并不是很多。但是并不是说其没有价值。在某些特殊的情况下,少了这个静态内部类还真是不行。如在进行代码程序测试的时候,如果在每一个Java源文件中都设置一个主方法(主方法是某个应用程序的入口,必须具有),那么会出现很多额外的代码。而且最主要的时这段主程序的代码对于Java文件来说,只是一个形式,其本身并不需要这种主方法。但是少了这个主方法又是万万不行的。在这种情况下,就可以将主方法写入到静态内部类中,从而不用为每个Java源文件都设置一个类似的主方法。这对于代码测试是非常有用的。在一些中大型的应用程序开发中,则是一个常用的技术手段。为此,这个静态内部类虽然不怎么常用,但是程序开发人员还必须要掌握它。也许在某个关键的时刻,其还可以发挥巨大的作用也说不定。 二、静态内部类的使用限制。 将某个内部类定义为静态类,跟将其他类定义为静态类的方法基本相同,引用规则也基本一致。不过其细节方面仍然有很大的不同。具体来说,主要有如下几个地方要引起各位程序开发人员的注意。 一是静态成员(包括静态变量与静态成员)的定义。一般情况下,如果一个内部类不是被定义成静态内部类,那么在定义成员变量或者成员方法的时候,是不能够被定义成静态成员变量与静态成员方法的。也就是说,在非静态内部类中不可以声明静态成员。如现在在一个student类中定义了一个内部类age,如果没有将这个类利用static关键字修饰,即没有定义为静态类,那么在这个内部类中如果要利用static关键字来修饰某个成员方法或者成员变量是不允许的。在编译的时候就通不过。故程序开发人员需要注意,只有将某个内部类修饰为静态类,然后才能够在这个类中定义静态的成员变量与成员方法。这是静态内部类都有的一个特性。也正是因为这个原因,有时候少了这个静态的内部类,很多工作就无法完成。或者说要绕一个大圈才能够实现某个用户的需求。这也是静态的内部类之所以要存在的一个重要原因。 二是在成员的引用上,有比较大的限制。一般的非静态内部类,可以随意的访问外部类中的成员变量与成员方法。即使这些成员方法被修饰为private(私有的成员变量或者方法),其非静态内部类都可以随意的访问。则是非静态内部类的特权。因为在其他类中是无法访问被定义为私有的成员变量或则方法。但是如果一个内部类被定义为静态的,那么在银用外部类的成员方法或则成员变量的时候,就会有诸多的限制。如不能够从静态内部类的对象中访问外部类的非静态成员(包括成员变量与成员方法)。这是什么意思呢?如果在外部类中定义了两个变量,一个是非静态的变量,一个是静态的变量。那么在静态内部类中,无论在成员方法内部还是在其他地方,都只能够引用外部类中的静态的变量,而不能够访问非静态的变量。在静态内部类中,可以定义静态的方法(也只有在静态的内部类中可以定义静态的方法),在静态方法中引用外部类的成员。但是无论在内部类的什么地方引用,有一个共同点,即都只能够引用外部类中的静态成员方法或者成员变量。对于那些非静态的成员变量与成员方法,在静态内部类中是无法访问的。这就是静态内部类的最大使用限制。在普通的非静态内部类中是没有这个限制的。也正是这个原因,决定了静态内部类只应用在一些特定的场合。其应用范围远远没有像非静态的内部类那样广泛。 三是在创建静态内部类时不需要将静态内部类的实例绑定在外部类的实例上。 通常情况下,在一个类中创建成员内部类的时候,有一个强制性的规定,即内部类的实例一定要绑定在外部类的实例中。也就是说,在创建内部类之前要先在外部类中要利用new关键字来创建这个内部类的对象。如此的话如果从外部类中初始化一个内部类对象,那么内部类对象就会绑定在外部类对象上。也就是说,普通非静态内部类的对象是依附在外部类对象之中的。但是,如果成员开发人员创建的时静态内部类,那么这就又另当别论了。通常情况下,程序员在定义静态内部类的时候,是不需要定义绑定在外部类的实例上的。也就是说,要在一个外部类中定义一个静态的内部类,不需要利用关键字new来创建内部类的实例。即在创建静态类内部对象时,不需要其外部类的对象。 具体为什么会这样,一般程序开发人员不需要了解这么深入,只需要记住有这个规则即可。在定义静态内部类的时候,千万不要犯画蛇添足的错误。 从以上的分析中可以看出,静态内部类与非静态的内部类还是有很大的不同的。一般程序开发人员可以这么理解,非静态的内部类对象隐式地在外部类中保存了一个引用,指向创建它的外部类对象。不管这么理解,程序开发人员都需要牢记静态内部类与非静态内部类的差异。如是否可以创建静态的成员方法与成员变量(静态内部类可以创建静态的成员而非静态的内部类不可以)、对于访问外部类的成员的限制(静态内部类只可以访问外部类中的静态成员变量与成员方法而非静态的内部类即可以访问静态的也可以访问非静态的外部类成员方法与成员变量)。这两个差异是静态内部类与非静态外部类最大的差异,也是静态内部类之所以存在的原因。了解了这个差异之后,程序开发人员还需要知道,在什么情况下该使用静态内部类。如在程序测试的时候,为了避免在各个Java源文件中书写主方法的代码,可以将主方法写入到静态内部类中,以减少代码的书写量,让代码更加的简洁。 总之,静态内部类在Java语言中是一个很特殊的类,跟普通的静态类以及非静态的内部类都有很大的差异。作为程序开发人员,必须要知道他们之间的差异,并在实际工作中在合适的地方采用合适的类。不过总的来说,静态内部类的使用频率并不是很高。但是在有一些场合,如果没有这个内部静态类的话,可能会起到事倍功半的反面效果 看了以上内容后,下面我整体测试了一下: 首先大家需要明白,静态内部类只是不依附与外部类,它里面的变量和方法也没有说一定得是静态的,下面拿代码说事:
再看一代码:(不创建静态内部类对象,而直接访问静态内部类中的元素,看看是否是默认静态的成员)
代码中的
替换为:
提示没有初始化,然后我就将其附为null,结果eclipse在inner.a=10处提示 Null pointer access: The variable inner can only be null at this location 这充分说明了静态内部类中的变量不是默认是静态的,运行结果是 java.lang.NullPointerException 下面一个代码更能说明问题
这个代码运行就正确了,说明静态内部类其实和定义在外面的单独一个普通类区别不大,但是大家还是需要理解上面引用的那篇文章里说的,毕竟他是内部类,还是有一些和外部类不同的 虽然这个代码运行成功了,但是eclipse提示了一个警告 The static field Test1.MyInnerStaticClass.a should be accessed in a static way 这个大家都懂得,类中的静态方法和变量不属于单独一个实例,所以访问时应该直接classname.var方式访问,当前上面方法也不错,但是不好,改正后代码
|
参考2: http://kenby.iteye.com/blog/1603803
整理如下:
两种内部类
Java的内部类有两种,一种是静态内部类,另一种是普通内部类,普通内部类可以获得外部对象的引用,
所以在普通内部类能够访问外部对象的成员变量 ,也就能够使用外部类的资源,可以说普通内部类依赖
于外部类,普通内部类与外部类是共生共死的,创建普通内部类的对象之前,必须先创建外部类的对象。
创建普通内部类的代码如下:
Outer o = new Outer();
Outer.Inner inner = o.new Inner();
Outer.Inner inner = new o.Inner(); /* 错误 */
Outer.Inner inner = new Outer.Inner(); /* 错误 */
静态内部类没有外部对象的引用,所以它无法获得外部对象的资源,当然好处是,静态内部类无需依赖于
外部类,它可以独立于外部对象而存在。创建静态内部类的代码如下:
Outer.Inner inner = new Outer.Inner();
静态类的使用场景
在以下场合可以使用静态内部类:
(1) 外部类需要使用内部类,而内部类无需使用外部类的资源
(2) 内部类可以独立外部类创建对象
使用静态内部类的好处是加强了代码的封装性以及提高了代码的可读性,举个例子:
- public class Person{
- //姓名
- private String name;
- //家庭
- private Home home;
- //构造函数设置属性值
- public Person(String _name){
- name = _name;
- }
- /* home、name的getter/setter方法省略 */
- public static class Home{
- //家庭地址
- private String address;
- //家庭电话
- private String tel;
- public Home(String _address,String _tel){
- address = _address;
- tel = _tel;
- }
- /* address、tel的getter/setter方法省略 */
- }
- }
把Home放在Person内部,一看便知道Home是Person的一个属性。使用的时候也很方便,如下代码:
- public static void main(String[] args) {
- Home home = new Person.Home("上海", "021");
- Person p1 = new Person("张三");
- Person p2 = new Person("李四");
- p1.setHome(home);
- p2.setHome(home);
- }
这里创建一个home对象,p1和p2都使用这个home对象,p1和p2共享同一个home对象。
如果把Home换成普通内部类,情况就不同了,这时代码变成:
- public static void main(String[] args) {
- Person p1 = new Person("张三");
- Home home = p1.new Home("上海", "021");
- p1.setHome(home);
- Person p2 = new Person("李四");
- p2.setHome(home);
- }
这里p1和p2依然共享同一个home对象,但此时home对象和p1是同生共死的,如果p1对象消亡,那么p2就没有
家了,这对p2来说实在不公平,而如果为p1和p2都创建一个Home对象,又浪费资源。所以在这个例子中,
使用静态内部类比普通内部类要合适。
静态内部类和普通内部类的区别
(1)普通内部类不能声明static的方法和变量
普通内部类不能声明static的方法和变量,注意这里说的是变量,常量(也就是final static修饰的属性)
还是可以的,而静态内部类形似外部类,没有任何限制。
(2)使用静态内部类,多个外部类的对象可以共享同一个内部类的对象。
使用普通内部类,每个外部类的对象都有自己的内部类对象,外部对象之间不能共享内部类的对象
待续…
“内部类” 大总结(Java)的更多相关文章
- 【全集】大数据Java基础
课程介绍 本课程是由猎豹移动大数据架构师,根据Java在公司大数据开发中的实际应用,精心设计和打磨的大数据必备Java课程.通过本课程的学习大数据新手能够少走弯路,以较短的时间系统掌握大数据开发必备语 ...
- 各大公司java后端开发面试题
各大公司Java后端开发面试题总结 ThreadLocal(线程变量副本)Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量.采用空间换时间,它用于线程间的数据隔离 ...
- 大数据学习--day04(选择结构、循环结构、大数据java基础面试题)
选择结构.循环结构.大数据java基础面试题 switch: 注意: byte short int char String(jdk1.7支持) 不能是 long float double boolea ...
- 2020年9大顶级Java框架
诞生于1995年的Java,目前已在134,861个网站上广泛使用,包括ESPN.SnapDeal等.在其24年的成长史中,Java已经证明了自己是用于自定义软件开发的顶级通用编程语言. Java广泛 ...
- 面试题:各大公司Java后端开发面试题总结 已看1 背1 有用 链接有必要看看
ThreadLocal(线程变量副本) --整理 Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量. 采用空间换时间,它用于线程间的数据隔离,为每一个 ...
- java继承内部类问题(java编程思想笔记)
普通内部类默认持有指向所属外部类的引用.如果新定义一个类来继承内部类,那“秘密”的引用该如何初始化? java提供了特殊的语法: class Egg2 { public class Yolk{ pub ...
- CSU 1114 平方根大搜索 java大数
1114: 平方根大搜索 Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 49 Solved: 23[Submit][Status][Web Board ...
- 各大公司Java后端开发面试题总结
ThreadLocal(线程变量副本)Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量.采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副 ...
- 入我新美大的Java后台开发面试题总结
静儿最近在总结一些面试题,那是因为做什么事情都要认真.面试也一样,静儿作为新美大金融部门的面试官,负责任的告诉大家,下面的问题回答不上来,面试是过不了的.不过以下绝不是原题,你会发现自己实力不过硬,最 ...
随机推荐
- Spark 1.6.1分布式集群环境搭建
一.软件准备 scala-2.11.8.tgz spark-1.6.1-bin-hadoop2.6.tgz 二.Scala 安装 1.master 机器 (1)下载 scala-2.11.8.tgz, ...
- http 服务
今天把一个功能模块做成了http服务,这还是第一次写http服务,纪录下来. package com.chuntent.itemsearch; import java.io.BufferedReade ...
- angularjs ngRoute demo
<!doctype html> <html lang="en" ng-app="AMail"> <head> <met ...
- HDU 4662 MU Puzzle 简单找规律
没有任何变换(III变U和删UU操作)之前,I 的个数一定是2^x个(也就是2的整数次幂) 若仅考虑III变U,那么设U的个数为k,I 的个数变为2^x-3*k 再加上删除UU操作,假设我们删除了2* ...
- 运用CodeSmith Studio实现C#项目构架
http://www.cnblogs.com/iCaca/category/80950.html http://www.cnblogs.com/BlueBreeze/archive/2011/07/1 ...
- SQLServer2008 行转列3
with a as ( select 日期,学号,名字, '语文' as 科目,语文 as 分数 from tsco union all select 日期,学号,名字, '数学' as 科目,数学 ...
- SQLServer2008 行转列
with a as( select *,row_number() over(partition by hyid order by jp desc) rowid from rtc) select a.h ...
- 在单机Hadoop上面增加Slave
之前的文章已经介绍了搭建单机Hadoop, HBase, Hive, Spark的方式:link 现在希望在单机的基础上,加一个slave. 首先需要加上信任关系,加信任关系的方式,见前一篇文章:li ...
- HNOI2008题目总结
呜呼..NOI前一个月正式开始切BZOJ了……以后的题解可能不会像之前的零散风格了,一套题我会集中起来发,遇到一些需要展开总结的东西我会另开文章详细介绍. 用了一天的时间把HNOI2008这套题切了… ...
- 51nod1175 区间中第K大的数
裸的主席树. #include<cstdio> #include<cstring> #include<cctype> #include<algorithm&g ...