Java Inner Class 内部类

内部类  Inner Class

一个内部类可以定义在另一个类里,可以定义在函数里,甚至可以作为一个表达式的一部分。

Java中的内部类共分为四种

  静态内部类static inner class (also called nested class)

  成员内部类member inner class

  局部内部类local inner class

  匿名内部类anonymous inner class

 

1 成员内部类  member inner class

 

1.1 形式

成员内部类也是定义在另一个类中,但是定义时不用static修饰。形如:

1 class Outer {
2     class Inner{
3     
4     }
5 }

编译之后会产生如下2个class文件:

 

---->这可以将相关的类组织在一起,从而降低了命名空间的混乱。

 

成员内部类的修饰符:
对于普通的类,可用的修饰符有final、abstract、strictfp、public和默认的包访问。
但是成员内部类更像一个成员变量和方法。
可用的修饰符有:final、abstract、public、private、protected、strictfp和static。
一旦用static修饰内部类,它就变成静态内部类了。

 

1.2 创建内部类实例

成员内部类就像一个实例变量,他依赖于外部类的实例存在 --> 必须先有外部类的实例  才能创建成员内部类对象

在外部类里面创建成员内部类的实例:this.new Innerclass(); 或可以用 Inner inner = new Inner(); 方法直接创建

在外部类之外创建内部类的实例:(new Outerclass()).new Innerclass();  

                                    或   Inner inner = new Outer().new Inner()

                                    或   Outer outer = new Outer(); Inner inner = outer.new Inner();

案例:从外部类的非静态方法中实例化内部类对象。

 1 class Outer {
 2   private int i = 10;
 3   public void makeInner(){
 4    Inner in = new Inner();
 5    in.seeOuter();
 6   }
 7   class Inner{
 8    public void seeOuter(){
 9    System.out.print(i);
10   }
11  }
12 }

表面上,我们并没有创建外部类的对象就实例化了内部类对象,和上面的话矛盾。
事实上,如果不创建外部类对象也就不可能调用makeInner()方法,所以到头来还是要创建外部类对象的。
你可能试图把makeInner()方法修饰为静态方法,即static public void makeInner()。
这样不创建外部类就可以实例化外部类了!但是在一个静态方法里能访问非静态成员和方法吗?显然不能。

--> 必须先有外部类的实例  才能创建成员内部类对象

案例:从外部类的静态方法中实例化内部类对象

 1     class Outer {
 2         private int i = 10;
 3         class Inner{
 4             public void seeOuter(){
 5                 System.out.print(i);
 6             }
 7         }    
 8         public static void main(String[] args) {
 9             Outer out = new Outer();
10             Outer.Inner in = out.new Inner();
11             //Outer.Inner in = new Outer().new Inner();
12             in.seeOuter();
13         }
14     }

被注释掉的那行是它上面两行的合并形式,一条简洁的语句。
对比一下:在外部类的非静态方法中实例化内部类对象是普通的new方式:Inner in = new Inner();
在外部类的静态方法中实例化内部类对象,必须先创建外部类对象:Outer.Inner in = new Outer().new Inner();

 

1.3 成员内部类操作外部类

 

成员内部类可以访问它的外部类的所有成员变量和方法,不管是静态的还是非静态的都可以

内部类就像一个实例成员一样存在于外部类,所以内部类可以访问外部类的所有成员就想访问自己的成员一样没有限制。

 

内部类中的this指的是内部类的实例对象本身,如果要用外部类的实例对象就可以用类名.this的方式获得。

普通的类可以用this引用当前的对象,内部类也是如此。

但是假若内部类想引用外部类当前的对象呢?用“外部类名”.this;的形式,如下例的Outer.this。

 1 class Outer {
 2    class Inner{
 3       public void seeOuter(){
 4         System.out.println(this);
 5         System.out.println(Outer.this);
 6       }
 7    }
 8 
 9    public static void main(String[] strs){
10      new Outer().new Inner().seeOuter();
11    }
12 }
13 
14 输出:
15 Outer$Inner@61de33
16 Outer@14318bb

1.4 内部类对象中不能有静态成员

原因很简单,内部类的实例对象是外部类实例对象的一个成员,若没有外部类对象,内部类就不会存在,何谈静态成员呢。

 1 class Outer {
 2    class Inner{
 3       static int i = 0;
 4       public void seeOuter(){
 5         System.out.println(this);
 6         System.out.println(Outer.this);
 7       }
 8    }
 9 
10    public static void main(String[] strs){
11      new Outer().new Inner().seeOuter();
12    }
13 }

我们编译这个类:

1  E:\>javac Outer.java
2  Outer.java:3: 内部类不能有静态声明
3            static int i = 0;
4                      ^
5  1 错误

 

2 局部内部类local inner class

 

局部内部类local inner class 也可以成为方法内部类

顾名思义,就是把类放在方法内。局部内部类定义在方法中,比方法的范围还小。是内部类中最少用到的一种类型。

像局部变量一样,不能被public, protected, private和static修饰。

局部内部类在方法中定义,所以只能在方法中使用,即只能在方法当中生成局部内部类的实例并且调用其方法。

 1  class Outer {
 2         public void doSomething(){
 3             class Inner{
 4                 public void seeOuter(){
 5                     System.out.println("inner class");
 6                 }
 7             } 
 8             
 9             Inner inner = new Inner();
10             inner.seeOuter();
11         }
12 
13         public static void main(String ... args){
14           new  Outer().doSomething();
15         }
16     }

输出:

inner class

局部内部类只能在声明的方法内是可见的,因此定义局部内部类之后,想用的话就要在方法内直接实例化,
记住这里顺序不能反了,一定是要先声明后使用,否则编译器会说找不到。

方法内部类的修饰符:
  与成员内部类不同,方法内部类更像一个局部变量。
  可以用于修饰方法内部类的只有final和abstract。


注意事项:

A: 方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化
B: 方法内部类对象不能使用该内部类所在方法的非final局部变量。

原因:

     因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。
    但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!例如,如果对它的引用被传递到其他某些代码,并存储在一个成员变量内。
    正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。下面是完整的例子:

 1 class Outer {
 2 public void doSomething(){
 3   final int a =10;
 4   class Inner{
 5     public void seeOuter(){
 6       System.out.println(a);
 7     }
 8   } 
 9   Inner in = new Inner();
10   in.seeOuter(); 
11    }
12   public static void main(String[] args) {
13     Outer out = new Outer();
14     out.doSomething();
15    }
16 }

  C:静态方法内的方法内部类。

     静态方法是没有this引用的,因此在静态方法内的内部类遭受同样的待遇,即:只能访问外部类的静态成员。

 

3 匿名内部类Anonymous Inner Class

顾名思义,没有名字的内部类。

匿名内部类就是没有名字的局部内部类,不使用关键字class, extends, implements, 没有构造方法。

匿名内部类隐式地继承了一个父类或者实现了一个接口

匿名内部类使用得比较多,通常是作为一个方法参数。

 

A、继承式的匿名内部类。

 1 class Car {
 2      public void drive(){
 3          System.out.println("Driving a car!");
 4      }
 5  }
 6 
 7 
 8     class Test{
 9         public static void main(String[] args) {
10             Car car = new Car(){
11                 public void drive(){
12                     System.out.println("Driving another car!");
13                 }
14             };
15             car.drive();
16         }
17     }

 

结果输出了:Driving another car!


建立匿名内部类的关键点是重写父类的一个或多个方法。再强调一下,是重写父类的方法,而不是创建新的方法。
因为用父类的引用不可能调用父类本身没有的方法!创建新的方法是多余的。简言之,参考多态。

 

B、接口式的匿名内部类。

 1  interface  Vehicle {
 2         public void drive();
 3  }
 4 
 5 
 6  class Test{
 7         public static void main(String[] args) {
 8             Vehicle v = new Vehicle(){
 9                 public void drive(){
10                     System.out.println("Driving a car!");
11                 }
12             };
13             v.drive();
14  }

这种形式的代码我们一定写过,这也是内部类的存在的一个重要作用:便于编写 线程和事件驱动的代码

 1 public class ThreadDemo {
 2     public static void main(String[] args) {
 3         
 4         new Thread(new Runnable(){
 5             public void run(){
 6                 System.out.println("Hello World!");
 7             }
 8         }).start();
 9     }
10 }
11 
12 
13 E:\>javac ThreadDemo.java
14 E:\>java ThreadDemo
15 Hello World!
 1 public class SwingTest
 2 {
 3     public static void main(String[] args)
 4     {
 5         JFrame frame = new JFrame("JFrame");
 6         JButton button = new JButton("JButton");
 7 
 8         button.addActionListener(new ActionListener(){
 9 
10             @Override
11             public void actionPerformed(ActionEvent arg0){
12                 System.out.println("Hello World");
13 
14             }
15         });
16 
17         frame.getContentPane().add(button);
18         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
19         frame.setSize(200, 200);
20   
21         frame.addWindowListener(new WindowAdapter() {
22             
23             @Override
24             public void windowClosing(WindowEvent e){
25                 System.out.println("Closing");
26                 System.exit(0);
27             }
28         });
29 
30         frame.setVisible(true);
31     }
32 }

若你了解安卓的话  会发现这样的代码编写方式有着很多的应用

好处就是简化我们的代码。

 

C、参数式的匿名内部类。

 1 class Bar{
 2     void doStuff(Foo f){}
 3 }
 4 
 5 inteface Foo{
 6     void foo();
 7 }
 8 
 9 class Test{
10     static void go(){
11         Bar b = new Bar();
12         b.doStuff(new Foo(){
13             public void foo(){
14                 System.out.println("foofy");
15             }
16         });
17     }
18 }
19                             

 

4 静态内部类static inner class 

 

在定义成员内部类的时候,可以在其前面加上一个权限修饰符static。此时这个内部类就变为了静态内部类。

同样会被编译成一个完全独立的.class文件,名称为OuterClass$InnerClass.class的形式。

只可以访问外部类的静态成员和静态方法,包括了私有的静态成员和方法。

生成静态内部类对象的方式为:OuterClass.InnerClass inner = new OuterClass.InnerClass();

 

静态内部类使用代码:

 1 package com.learnjava.innerclass;
 2 
 3 class StaticInner
 4 {
 5     private static int a = 4;
 6 
 7     // 静态内部类
 8     public static class Inner
 9     {
10         public void test()
11         {
12             // 静态内部类可以访问外部类的静态成员
13             // 并且它只能访问静态的
14             System.out.println(a);
15         }
16 
17     }
18 }
19 
20 public class StaticInnerClassTest
21 {
22 
23     public static void main(String[] args)
24     {
25         StaticInner.Inner inner = new StaticInner.Inner();
26         inner.test();
27     }
28 }

 

与一般内部类不同,在静态代码中不能够使用this操作,所以在静态内部类中只可以访问外部类的静态变量和静态方法。
使用静态内部类的目的和使用内部类相同。如果一个内部类不依赖于其外部类的实例变量,或与实例变量无关,则选择应用静态内部类。

可以在静态内部类的方法中,直接访问外部类的静态变量和调用静态方法。但不允许访问外部类的实例变量以及实例方法。
静态内部类的实例方法中亦只允许访问外部类的静态成员。

 

静态内部类不同于其他3种内部类,他有着自己特殊的特性,参看:解析静态内部类的使用目的与限制

 

5 小结

 

5.1 几种内部类的共性:

A、内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类命和$符号。
B、内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的。

 

5.2 java中为什么要引入内部类?还有匿名内部类?

 

1)可以是单继承的一种补充解决方案 inner classes能有效实际地允许“多重实现继承(multiple implementation)”

   Java中一个类只能继承一个类 可以通过内部类达到继承多个类的效果

   :每个inner class都能够各自继承某一实现类(implementation),因此,inner class不受限于outer class是否已继承自某一实现类。 

2)针对具体的问题提供具体的解决方案,同时又能对外隐藏实现细节    看具体的案例

 

案例1

在集合中可以使用Iterator遍历 但每一种集合的数据结构不同  导致遍历的方法必然也不同
所以Java在每个具体的集合里定义了一个内部类Itr  他实现了Iterator接口   从而根据所在类的具体情况进行遍历

 1 public interface Iterator {//迭代器的功能
 2     boolean hasNext();
 3     Object next(); 
 4 }
 5 
 6 public interface Iterable {//返回迭代器的能力
 7     Iterator iterator();
 8 }
 9 
10 public interface Collection extends Iterable {
11     Iterator iterator();
12 }
13 
14 public interface List extends Collection {
15     Iterator iterator();
16 }
17 
18 public class ArrayList implements List {
19     public Iterator iterator() {
20        return new Itr();
21     }
22     
23 private class Itr implements Iterator {
24     //每种集合的具体实现采用了不同的数据结构
25         public boolean hasNext() {......}
26         public Object next(){......} 
27     }
28 }
29 
30 Collection c = new ArrayList();
31 c.add("hello");
32 c.add("world");
33 c.add("java");
34 Iterator it = c.iterator();     //new Itr();
35 while(it.hasNext()) {
36     String s = (String)it.next();
37     System.out.println(s);
38 }

Itr是private的类 对外并不可见 因为我的遍历方法我自己知道就可以了 别人并不需要了解

集合类有实现了Tterable接口 通过里面的iterator方法获取该内部类实例 外部不能直接创建该内部类的实例

 

案例2

为某个类提供特定的数据结构 :  为了共同解决一个具体的问题 但又可以对外部保持透明

如HashMap中有Entry   ConcurrentHashMap有HashEntry 和Segment  

看HashMap中的内部类:

 1 public class HashMap<K,V>
 2     extends AbstractMap<K,V>
 3     implements Map<K,V>, Cloneable, Serializable
 4 {
 5 
 6     static final Entry<?,?>[] EMPTY_TABLE = {};
 7     transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
 8 
 9     static class Entry<K,V> implements Map.Entry<K,V> {
10         final K key;
11         V value;
12         Entry<K,V> next;
13         int hash;
14         // ... ...
15     } 
16 }

关于HashMap 可以参考:HashMap源码解析

关于ConcurrentHashMap:ConcurrentHashMap  ConcurrentHashMap原理分析

转载于:https://www.cnblogs.com/wihainan/p/4773090.html

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

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

相关文章

现代制造工程笔记01:课程安排

电子教材&#xff1a;http://www.bookask.com/read/4588.html

22个值得收藏的android开源码-UI篇

本文介绍了android开发人员中比較热门的开源码&#xff0c;这些代码绝大多数能够直接应用到项目中。FileBrowserView 一个强大的文件选择控件。界面比較美丽&#xff0c;使用也非常easy。 特点&#xff1a;能够自己定义UI&#xff1b;支持复制、剪切、删除、移动文件&#xff1…

现代制造工程02:第一部分——刀具(含2个易考点)

一、金属切削原理 可以看出哪些性能参数是同向性得&#xff0c;并且知道性能参数与三要素有什么关系 易考点&#xff1a;三个变形区 易考点&#xff1a;磨损种类以及磨损阶段、磨顿标准

iOS开发之share第三方登录以及分享

&#xff08;1&#xff09;官方下载ShareSDK iOS 2.8.8&#xff0c;地址&#xff1a;http://sharesdk.cn/ &#xff08;2&#xff09;根据实际情况&#xff0c;引入相关的库&#xff0c;参考官方文档。 &#xff08;3&#xff09;在项目的AppDelegate中一般情况下有三个操作&am…

有限元课堂笔记03:钢架(Frame)

1.平面钢架(Frame)&#xff1a;是桁架(Truss)和梁(Beam)的合成&#xff0c;两节点六自由度 2.空间钢架&#xff1a;两节点12自由度 相对于平面钢架来说每一个节点增加了z轴线性变形、绕x轴扭矩&#xff0c;绕y轴扭矩 刚度矩阵

Docker新手入门:基本用法

Docker新手入门&#xff1a;基本用法 1.Docker简介 1.1 第一本Docker书 工作中不断碰到Docker&#xff0c;今天终于算是正式开始学习了。在挑选系统学习Docker以及虚拟化技术的书籍时还碰到了不少麻烦&#xff0c;主要就是没有特别经典的书&#xff01;Docker的《第一版Docker书…

有限元笔记04:二维实体单元

1.二维实体即平面问题 创建单元的步骤&#xff1a; 型函数&#xff08;插值函数&#xff09;>>>应变矩阵>>>刚度矩阵>>>质量矩阵>>>力的分量 1&#xff09;三角形单元 2&#xff09;面坐标 3&#xff09;线性矩形单元 4)高斯积分 6)任意…

线程与内核对象的同步-2

等待定时器内核事件 CreateWaitableTimer( PSECURITY_ATTRIBUTES psa, BOOL fManualReset, PCTSTR pszName); 进程可以获得它自己的与进程相关的现有等待定时器的句柄。 HANDLE OpenWaitableTimer( DWORD dwDesiredAccess, BOOL bInheritHandle, PCTSTR pszName); 等待定时器对…

【Win10 应用开发】自定义应用标题栏

Win 10 app对窗口标题栏的自定义包括两个层面&#xff1a;一是只定义标题中各部分的颜色&#xff0c;如标题栏上文本的颜色、三个系统按钮&#xff08;最大化&#xff0c;最小化&#xff0c;关闭&#xff09;的背景颜色等&#xff1b;另一层是把窗口的可视区域直接扩展到标题栏…

学习笔记(59):Python实战编程-Graphics

立即学习:https://edu.csdn.net/course/play/19711/343123?utm_sourceblogtoedu 1.graphics&#xff1a;图形界面组件的绘制&#xff0c;利用的是坐标的定位来对各个组件进行相对地位置布局 2.graphics与thinkter的区别 1&#xff09;窗口的创建上&#xff1a; win graphics…

IIS6配置Asp.net MVC运行环境

Windows server 2003 IIS6 搭建Asp.net MVC运行环境 1、安装.Net Framework4.0。 下载地址&#xff1a; http://www.microsoft.com/zh-cn/download/details.aspx?id17718 2、安装WindowsServer2003-KB968930-x86-CHS.exe&#xff0c;PowerShell 2.0的补丁 下载地址&#xff1…

最优化课堂笔记04:非线性规划(考点4-5例题)

目录 4.1 多元函数的泰勒展开 4.2方向导数与梯度 4.2.1方向导数 n元函数在点沿特定方向的方向导数 4.2.2梯度 4.3二次函数及正定矩阵 4.4凸函数与凸规划 4.4.1凸函数 4.4.2凸规划 4.4无约束优化问题的极值条件 4.5约束优化问题的极值条件&#xff08;重点考点&#x…

orb-slam2在PC和ARM上运行

ORBSLAM2的编译与运行 环境&#xff1a;Ubuntu16.04 ORBSLAM2 &#xff08;1&#xff09;安装工具 sudo apt-get install cmake sudo apt-get install git sudo apt-get install gcc g (2) 安装pangolin 安装依赖项&#xff1a; sudo apt-get install libglew-dev sudo ap…

烂泥:智能DNS使用与配置

公司的业务现在已经扩展到海外&#xff0c;对外提供的统一接口都是通过域名来解析的&#xff0c;但是海外用户访问国内接口的话&#xff0c;你懂的&#xff0c;很慢的。为了提高域名解析的速度&#xff0c;打算使用智能DNS功能&#xff0c;来解决海外用户域名解析慢的问题。 PS…

现代制造工程——考试复习01

第一部分 金属切削原理 1.切削过程中工件上的加工表面分类 2.不同工艺的工件和刀具的相对关系 3.不同工艺的主运动和进给运动的方向 4.思考&#xff1a;主运动一般只有一个&#xff0c;但是进给运动一个也可以是多个 5.切削三要素&#xff08;必考&#xff09; 6.思考&#x…

最优化课堂笔记05——一维最优化方法(含重点:黄金分割法)

5-1 一维搜索区间的确定 搜索区间只是适用于单峰区间 、 例子 5.2 黄金分割法&#xff08;重点&#xff09; 上面的a与b都会跟着计算的推进而变化的 例子重点 5.3二次插值法 总结&#xff1a; 5.4 切线法&#xff08;牛顿法&#xff09; 5.5 割线法&#xff08;不需要计算导数&…

C++中静态成员数据初始化问题

C中静态成员数据初始化问题 1、静态成员变量&#xff1a;定义为静态成员意味着它能被所有的实例化对象所共有&#xff0c;其修改值为该类的其它所有实例所见。 下面看一个例子 class people { public:people(int i):id(i){num;} private:static int num;int id; }; num为静…

现代制造工程笔记04-精密超精密加工和特种加工(主要掌握加工原理加工条件)

一、精密加工与超精密加工 不同时期对精密加工的定义以及要求不一样 1.1金刚石超精密加工&#xff08;&#xff09; 1.2精密磨料加工——精密砂带抛光加工 1.3超声波加工 1.4 电解加工&#xff08;加工材料必须是金属&#xff09;——工件失去电子成型 1.5电铸加工——工件得到…