内部类的反编译分析和总结

内部类:是指在类的内部又定义了一个类。根据位置的不同可以分为成员内部类和局部内部类。

成员内部类

在成员变量的位置定义一个类。
1. 依赖外部类对象存在
2. 可以访问外部类的成员变量和成员方法。

访问方式:
1. 直接访问,直接使用变量名和方法名
2. 通过外部类名.this.变量名/方法名

package test;

public class Outer {
    private int num = 1;
    public void before() {
        System.out.println("before");
    }
    // 成员变量,成员方法,成员内部类是一样
    public class Inner{
        private int count = 2;
        private int num = 2;
        public void speak() {
            System.out.println(num);                // 2
            System.out.println(Outer.this.num);     // 1
            before();                               // before
            System.out.println(count);              // 2
        }
        public Inner() {
            super();
        }
    }
}

编译生成Outer.class和Outer$Inner.class两个class文件
用javap -c Outer$Inner.class 命令反编译查看

public class test.Outer$Inner {
  final test.Outer this$0;

  public void speak();
    Code:
       0: getstatic     #13                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: getfield      #19                 // Field num:I
       7: invokevirtual #21                 // Method java/io/PrintStream.println:(I)V
      10: getstatic     #13                 // Field java/lang/System.out:Ljava/io/PrintStream;
      13: aload_0
      14: getfield      #27                 // Field this$0:Ltest/Outer;
      17: invokestatic  #29                 // Method test/Outer.access$0:(Ltest/Outer;)I
      20: invokevirtual #21                 // Method java/io/PrintStream.println:(I)V
      23: aload_0
      24: getfield      #27                 // Field this$0:Ltest/Outer;
      27: invokevirtual #35                 // Method test/Outer.before:()V
      30: getstatic     #13                 // Field java/lang/System.out:Ljava/io/PrintStream;
      33: aload_0
      34: getfield      #38                 // Field count:I
      37: invokevirtual #21                 // Method java/io/PrintStream.println:(I)V
      40: return

  public test.Outer$Inner(test.Outer);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #27                 // Field this$0:Ltest/Outer;
       5: aload_0
       6: invokespecial #46                 // Method java/lang/Object."<init>":()V
       9: aload_0
      10: iconst_2
      11: putfield      #38                 // Field count:I
      14: aload_0
      15: iconst_2
      16: putfield      #19                 // Field num:I
      19: return
}

在编译成class文件的过程,编译器会自己在Inner类中生成一个指向父类对象的引用,并且只含一个有外部类类型的参数的构造方法。
所以看到这就明白了为何内部类能访问外部类对象的成员变量和方法,并且依赖外部类对象存在。

静态成员内部类

在成员内部类的基础上加上了static关键字修饰
1.不依赖外部类对象存在
2.只能访问外部类的静态成员变量和静态方法。
访问方式:
1. 直接访问,直接使用变量名和方法名
2. 通过外部类名.变量名/方法名

package test;

public class Outer {

    private static int num = 1;
    public void before() {
        System.out.println("before");
    }
    // 静态成员内部类
    public static class Inner{
        private int count = 2;
        private int num = 2;
        public void speak() {
            System.out.println(num);                // 2
            System.out.println(Outer.num);          // 通过类名的方式访问
            System.out.println(count);              // 2
        }
        public Inner() {
            super();
        }
    }
}

创建内部类对象: Outer.Inner inner = new Outer().new Inner();
编译生成Outer.class和Outer$Inner.class两个class文件
用javap -c Outer$Inner.class 命令反编译查看

public class test.Outer$Inner {
  public void speak();
    Code:
       0: getstatic     #11                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: getfield      #17                 // Field num:I
       7: invokevirtual #19                 // Method java/io/PrintStream.println:(I)V
      10: getstatic     #11                 // Field java/lang/System.out:Ljava/io/PrintStream;
      13: invokestatic  #25                 // Method test/Outer.access$0:()I
      16: invokevirtual #19                 // Method java/io/PrintStream.println:(I)V
      19: getstatic     #11                 // Field java/lang/System.out:Ljava/io/PrintStream;
      22: aload_0
      23: getfield      #31                 // Field count:I
      26: invokevirtual #19                 // Method java/io/PrintStream.println:(I)V
      29: return

  public test.Outer$Inner();
    Code:
       0: aload_0
       1: invokespecial #38                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: iconst_2
       6: putfield      #31                 // Field count:I
       9: aload_0
      10: iconst_2
      11: putfield      #17                 // Field num:I
      14: return
}

发现静态成员内部类没有外部类对象的引用,所以不能访问外部类对象的非静态的成员变量和方法。它是相当一个独立的类,但限制了访问方式,要通过外部类访问内部类。此时内部类相当于外部类的静态成员.
创建内部类对象:Outer.Inner inner = new Outer.Inner();

成员内部类

在方法中声明的一个类

package test;

public class Outer2 {
    private int num = 1;

    public void print() {
        System.out.println("Outer的print");
    }

    public void fun() {

        // 局部内部类
        class Inner{
            public void print() {
                System.out.println("Inner的print");
            }
            public void test() {
                Outer2.this.print();    // 调用外部类对象的方法
            }
        }
        new Inner().print();
        new Inner().test();
    }
    public static void main(String[] args) {
        Outer2 outer2 = new Outer2();
        outer2.fun();
    }
}

运行结果如下

Inner的print
Outer的print

打开反编译工具查看下代码

class test.Outer2$1Inner {
  final test.Outer2 this$0;

  test.Outer2$1Inner(test.Outer2);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #10                 // Field this$0:Ltest/Outer2;
       5: aload_0
       6: invokespecial #12                 // Method java/lang/Object."<init>":()V
       9: return

  public void print();
    Code:
       0: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #26                 // String Inner的print
       5: invokevirtual #28                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return

  public void test();
    Code:
       0: aload_0
       1: getfield      #10                 // Field this$0:Ltest/Outer2;
       4: invokevirtual #35                 // Method test/Outer2.print:()V
       7: return
}

同样局部内部类含有外部类对象的引用并且有且仅有一个含有外部类类型的参数的构造方法,和成员内部类是一样的。

和成员内部类的区别就是,成员内部类对象可以在其他的类中创建,而局部内部类只能在方法声明并创建,作用域在一个方法中。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
APK Editor Studio 是一个功能强大但易于使用的 APK 向工程工具。提取,编辑或替换 APK 资源,图像和图标;添加翻译,创建自己的 APK mod 或了解其内部结构。多种便捷工具将帮助您轻松更改应用程序图标,应用程序标题,自动签名 APK 并将其安装在设备上。对于所有想要修改其 APK 文件各种参数的用户,APK Editor Studio 免费版是一种用户友好的解决方案。 APK 编译工具 APK Editor Studio 中文版APK 编译工具 APK Editor Studio 中文版 APK Editor Studio 特色: 图标编辑器可轻松更改 Android 应用程序图标。 标题编辑器可快速编辑包括翻译在内的 Android 应用标题。 具有自动格式交叉转换的图像编辑器以替换 Android 图像。 代码编辑器,高亮显示XML和 YAML 的语法。 Android Explorer 可以管理 Android 设备上的文件并拍摄屏幕截图。 权限编辑器可轻松添加或删除 Android 权限。 清单编辑器,包括一个方便的 API 级别选择器。 资源检查器可轻松浏览方便分组的资源。 APK 签名器可自动为您轻松地对 APK 进行签名。 APK 优化对齐APK以减少 RAM 消耗。 APK 安装器可立即在您的设备上安装应用程序。 简单实用的 GUI 对于希望对其软件包进行一些调整的所有开发人员,APK Editor Studio 的主窗口非常简单明了。 您不仅可以修改应用名称和图标,还可以编辑 APK 资源,签名或安装 APK。 此外,您可以篡改多个 Android 清单属性,而无需分析内部结构。 选择感兴趣的 APK 第一步是选择需要处理的包装。它可以位于计算机或 Android 设备上。 您只需要通过专用USB电缆将其连接到PC,即可立即获得有关它的一些详细信息:别名,序列号,型号或产品类型。 建立连接后,您可以浏览设备的内容并加载任何选定的APK。您甚至可以单击鼠标来拍摄智能手机屏幕的屏幕截图。 使用自定义键签名APK APK Editor Studio 的一个不错的功能是您可以设置一组密钥,这些密钥以后可以用来对软件包进行签名。 您可以为新密钥分配密码和别名,以及定义其有效期(以年为单位)。您还需要指定开发人员的名字和姓氏,组织单位和位置详细信息。 总结 APK Editor Studio 中文版可以轻松成为您快速执行 APK 文件调整的首选解决方案,因此您不再需要检查软件包的代码和基础结构。
C语言教程(原书第4版) 《c语言教程(原书第4版)》是一本优秀的c程序设计语言教材,完整描述了ansi c语言及其语法特性,并对c语言的高级特性和应用作了深入阐述,介绍了从c到c++和java过渡的相关知识。《c语言教程(原书第4版)》的一个鲜明特色就是结合大量示例描述c语言的重要特征,并对很多工作代码给出了逐步的分析,以这种独特的教学方法向读者解释新接触的编程元素及一些惯用法。   《c语言教程(原书第4版)》系统、完整,可作为c语言的参考手册,也非常适合作为学习c语言的入门和高级课程教材。 前言 第0章 从零开始 0.1 为什么要用c 0.2 ansi c标准 0.3 从c到c++ 0.4 从c和c++到java 第1章 c语言概述 1.1 编程和预备知识 1.2 程序输出 1.3 变量、表达式和赋值 1.4 使用#define和#include 1.5 使用printf()和scanf() 1.6 控制流 1.7 函数 1.8 数组、字符串和指针 1.8.1 数组 1.8.2 字符串 1.8.3 指针 1.9 文件 1.10 与操作系统有关的内容 1.10.1 编写和运行c程序 1.10.2 中断程序 1.10.3 输入文件尾标志 1.10.4 输入和输出的重定向 1.11 总结 1.12 练习 第2章 词法元素、操作符和c系统 2.1 字符和词法元素 2.2 语法规则 2.3 注释 2.4 关键字 2.5 标识符 2.6 常量 2.7 字符串常量 2.8 操作符和标点符号 2.9 操作符的优先级和结合性 2.10 增值操作符和减值操作符 2.11 赋值操作符 2.12 例子:计算2的乘方 2.13 c系统 2.13.1 预处理器 2.13.2 标准函数库 2.14 总结 2.15 练习 第3章 基本数据类型 3.1 声明、表达式和赋值 3.2 基本数据类型 3.3 字符和char数据类型 3.4 int数据类型 3.5 整数类型short、long和unsigned 3.6 浮点类型 3.7 typedef的用法 3.8 sizeof操作符 3.9 使用getchar()和putchar() 3.10 数学函数 3.10.1 使用abs()和fabs() 3.10.2 unix和数学函数库 3.11 隐式类型转换和强制类型转换 3.11.1 整型提升 3.11.2 寻常算术转换 3.11.3 强制类型转换 3.12 十六进制和八进制常量 3.13 总结 3.14 练习 第4章 控制流 4.1 关系操作符、相等操作符和逻辑操作符 4.2 关系操作符和表达式 4.3 相等操作符和表达式 4.4 逻辑操作符和表达式 4.5 复合语句 4.6 表达式和空语句 4.7 if和if-else语句 4.8 while语句 4.9 for语句 4.10 例子:布尔变量 4.11 逗号操作符 4.12 do语句 4.13 例子:斐波那契数 4.14 goto语句 4.15 break和continue语句 4.16 switch语句 4.17 条件操作符 4.18 总结 4.19 练习 第5章 函数 5.1 函数定义 5.2 return语句 5.3 函数原型 5.4 例子:创建乘方表 5.5 从编译器的角度观察函数原型 5.6 函数定义顺序的另一种风格 5.7 函数调用和传值调用 5.8 开发大型程序 5.9 使用断言 5.10 作用域规则 5.10.1 平行和嵌套代码块 5.10.2 以调试为目的使用代码块 5.11 存储类型 5.11.1 auto存储类型 5.11.2 extern存储类型 5.11.3 register存储类型 5.11.4 static存储类型 5.12 静态外部变量 5.13 默认初始化 5.14 递归 5.15 例子:汉诺塔 5.16 总结 5.17 练习 第6章 数组、指针和字符串 6.1 一维数组 6.1.1 初始化 6.1.2 下标 6.2 指针 6.3 传引用调用 6.4 数组和指针之间的关系 6.5 指针运算和元素的大小 6.6 数组作为函数的实参 6.7 例子:冒泡排序 6.8 用calloc()和malloc()进行动态内存分配 6.9 例子:归并和归并排序 6.10 字符串 6.11 标准函数库中的字符串处理函数 6.12 多维数组 6.12.1 二维数组 6.12.2 存储映射函数 6.12.3 形式参数声明 6.12.4 三维数组 6.12.5 初始化 6.12.6 使用typedef 6.13 指针数组 6.14 main()函数的参数 6.15 不规则数组 6.16 函数作为参数 6.17 例子:使用二分法寻找函数的根 6.18 函数指针数组 6.19 类型限定符const和volatile 6.20 总结 6.21 练习 第7章 位操作符和枚举类型 7.1 位操作符和表达式 7.1.1 按位求 7.1.2 补码 7.1.3 位逻辑操作符 7.1.4 左移位和右移位操作符 7.2 掩码 7.3 软件工具:打印int值的二进制形式 7.4 包装和解包 7.5 枚举类型 7.6 例子:“石头、剪刀、布”游戏 7.7 总结 7.8 练习 第8章 预处理器 8.1 #include的使用 8.2 使用#define 8.3 带参数的宏 8.4 stddef.h中的类型定义和宏 8.5 例子:用qsort()进行排序 8.6 例子:带参数的宏 8.7 stdio.h和ctype.h中的宏 8.8 条件编译 8.9 预定义的宏 8.10 “#”和“##”操作符 8.11 assert()宏 8.12 使用#error和#pragma 8.13 行号 8.14 对应的函数 8.15 例子:快速排序 8.16 总结 8.17 练习 第9章 结构和联合 9.1 结构 9.2 访问结构成员 9.3 操作符的优先级和结合性的总结 9.4 在函数中使用结构 9.5 结构的初始化 9.6 例子:玩扑克牌 9.7 联合 9.8 位字段 9.9 例子:访问位和字节 9.10 adt堆栈 9.11 总结 9.12 练习 第10章 结构和列表处理 10.1 自引用的结构 10.2 线性链表 10.3 链表操作 10.4 一些链表处理函数 10.4.1 插入 10.4.2 删除 10.5 堆栈 10.6 例子:波兰记法和堆栈求值 10.7 队列 10.8 二叉树 10.8.1 二叉树的遍历 10.8.2 创建树 10.9 普通的树 10.9.1 遍历 10.9.2 calloc()的用法以及树的创建 10.10 总结 10.11 练习 第11章 输入/输出和操作系统 11.1 输出函数printf() 11.2 输入函数scanf() 11.3 fprintf()、fscanf()、sprintf() 和sscanf()函数 11.4 fopen()和fclose()函数 11.5 例子:对文件进行空间加倍 11.6 使用临时文件和优雅函数 11.7 随机访问文件 11.8 文件描述符输入/输出 11.9 文件访问权限 11.10 在c程序内部执行命令 11.11 在c程序内部使用管道 11.12 环境变量 11.13 c编译器 11.14 使用性能评估程序 11.15 函数库 11.16 对c代码进行计时 11.17 使用make 11.18 使用touch 11.19 其他有用的工具 11.20 总结 11.21 练习 第12章 高级应用 12.1 用fork()创建并发进程 12.2 进程的叠加:exec...()函数族系 12.3 使用pipe()实现进程间的通信 12.4 信号 12.5 例子:哲学家用餐问题 12.6 矩阵的动态分配 12.6.1 为什么二维数组无法满足要求 12.6.2 用指针数组创建矩阵 12.6.3 调整下标范围 12.6.4 一次分配所有内存 12.7 返回状态 12.8 总结 12.9 练习 第13章 从c到c++ 13.1 输出 13.2 输入 13.3 函数 13.4 类和抽象数据类型 13.5 重载 13.6 构造函数和析构函数 13.7 面向对象编程和继承 13.8 多态 13.9 模板 13.10 c++的异常 13.11 面向对象编程的优点 13.12 总结 13.13 练习 第14章 从c到java 14.1 输出 14.2 变量和类型 14.3 类和抽象数据类型 14.4 重载 14.5 类的创建和销毁 14.6 面向对象编程和继承 14.7 多态和重写方法 14.8 applet 14.9 java的异常 14.10 java和oop的优势 14.11 总结 14.12 练习 附录a 标准函数库 附录b c的语法 附录c ansi c与传统c的比较 附录d ascii字符码 附录e 操作符的优先级和结合性

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值