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,一经查实,立即删除!

相关文章

SLAM系统工程,常用数据集下载链接(TUM KITTI DSO Mono EuRoC)

TUM 链接&#xff1a;https://pan.baidu.com/s/1nwXtGqH 密码&#xff1a;lsgr KITTI 链接&#xff1a;https://pan.baidu.com/s/1htFmXDE 密码&#xff1a;uu20 DSO 链接&#xff1a;https://pan.baidu.com/s/1eSRmeZK 密码&#xff1a;6x5b Mono 链接&#xff1a;http…

uva1331三角剖分

题意&#xff1a;输入一个简单m&#xff08;2<m<50)边形&#xff0c;找到一个最大三角形最小的三角剖分&#xff08;用不相交的对角线把一个多边形分成若干个三角形&#xff09;。输出最大的三角形面积。 分析&#xff1a;每条对角线都是无序的&#xff0c;因此&#xff…

Halcon算子翻译——default

名称 default - switch段中的备用分支。 用法 default( : : : ) 描述 default在switch段中开放备用分支。 如果switch语句的控制表达式的计算结果不匹配前面的case语句的任何整数常量&#xff0c;则访问该分支。 结果 default&#xff08;作为算子&#xff09;总是返回2&#x…

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

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

(转).gitignore详解

本文转自http://sentsin.com/web/666.html 今天讲讲Git中非常重要的一个文件——.gitignore。 首先要强调一点&#xff0c;这个文件的完整文件名就是“.gitignore”&#xff0c;注意最前面有个“.”。这样没有扩展名的文件在Windows下不太好创建&#xff0c;这里给出win7的创建…

Effective Java 英文 第二版 读书笔记 Item 14:In public classes,use accessor methods,not public fields...

本章主要分析 公开属性与私有属性提供公开get、set方法两种方式对比 // Degenerate classes like this should not be public! class Point { public double x; public double y; } // Public class with exposed immutable fields - questionable public final class Time { …

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

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

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

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

Fortran向C传递NULL值

在很多C或C的头文件定义中&#xff0c;NULL被指定定义为0&#xff0c;这里不再具体展开 gfortran的手册关于iso c binding的章节&#xff0c;定义NULL如下 Moreover, the following two named constants are defined: NameType C_NULL_PTRC_PTRC_NULL_FUNPTRC_FUNPTRBoth are e…

视觉slam重点知识笔记

1、除了基本矩阵和本质矩阵&#xff0c;我们还有一种称为单应矩阵&#xff08;Homography&#xff09;H 的东西&#xff0c;它 描述了两个平面之间的映射关系。若场景中的特征点都落在同一平面上&#xff08;比如墙&#xff0c;地面等&#xff09;&#xff0c;则可以通过单应性…

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…

Linux磁盘的划分

磁盘的组成&#xff1a; 磁道&#xff1a;track 扇区&#xff1a;sector (512字节) 磁头&#xff1a;head 柱面&#xff1a;cylinder MBR/msdos 分区模式 1--4个主分区&#xff0c;或者0--3个主分区加1个扩展分区&#xff08;n个逻辑分区&#xff09; 最大支持容量为2.2TB的磁…

opencv的pnp()算法接口是相对于3D点,输出的是相机与3D点之间的R和T

1、情况一&#xff1a; 两帧图像 -》 提取特征-》特征匹配-》通过2d-2d计算 F基础矩阵、E 本质矩阵 、H 单一性矩阵 -》解析出 相机自身的运动R和T -》再通过三角化&#xff0c;将2d点转为相机的3d点&#xff08;每个空间点在两个相机坐标系下的投影3D坐标与像素2D坐标&#…

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

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

关于系统性能检测的一些使用

1.安装sysstat&#xff1a;yum install sysstat---------- iostat -x 1 10 如果 %util 接近 100%&#xff0c;说明产生的I/O请求太多&#xff0c;I/O系统已经满负荷&#xff0c;该磁盘可能存在瓶颈。 idle小于70% IO压力就较大了,一般读取速度有较多的wait. 2.如果想对硬盘…

Python tab 补全

1. 先准备一个tab.py的脚本 shell> cat tab.py 12345678910111213141516171819#!/usr/bin/python# python tab fileimport sys import readline import rlcompleter import atexit import os # tab completionreadline.parse_and_bind(tab: complete) # history filehistfil…

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)任意…

oracle中的常用函数

一、运算符算术运算符&#xff1a; - * / 可以在select 语句中使用连接运算符&#xff1a;|| select deptno|| dname from dept; 比较运算符&#xff1a;> > ! < < like between is null in逻辑运算符&#xff1a;not and or 集合运算符&#xff1a; 集合操作不适…