前端开发入门到精通的在线学习网站

网站首页 > 资源文章 正文

JAVA-从一个故事认识类(Class)(java从一个类调用另一个类中的一个方法)

qiguaw 2024-09-05 19:36:00 资源文章 20 ℃ 0 评论

小咖(Java)有一天陪女朋友(Object)去逛街,逛吃逛吃...遇见了科特林(Kotlin)。打起了招呼,"嗨,科特林好久不见,最近怎么样呢?"
科特林:"也就那样,还在安卓拧螺丝。你呢,这你女朋友吧,我怎么看着眼熟。"
小咖:"我也那样,离开安卓换了个工厂拧螺丝。没事,一起逛逛"。
此时,小咖的女朋友(Object)使劲的掐了小咖,然后道:"小咖,我朋友找我了,我先回去了。"
....
"喂,小鸥哇。你在哪呀?我刚去找你,你不在家"。小咖打着电话问道。
"你管我在哪,你好好陪你朋友,不需要我陪着,反正你也不懂我,哼~"。小鸥直接挂掉电话。
小咖然后重新拨打,话筒里传来"您拨打的电话,已经关机,请稍后再拨。Sorry, the number you dailed"。
小咖很懵圈,不明白自己的女朋友为什么生气,自己明明很了解她,姓甚名谁,身高体重,家庭成员,爱好..都很清楚。
小咖不能就此放弃,根据小鸥喜好开始推测在哪,去找小鸥好好哄哄..

言归正传:
一个
Java经过编译,生成字节码文件(.class)。经过类加载器加载后,程序可以通过反射获取类所有的信息,就像小咖知道女朋友所有的信息,就可以想办法哄好女朋友。
Java程序使用过程中,通过反射获取类所有的信息,就可以去实例化,增强,切面编程(AOP)等。

此处,我们思考一个问题:
为什么反射能获取类所有的信息?

在解答问题前,先看 程序=数据结构+算法 这个公式,作为程序员开发的Java 代码(当然也可以是Kotlin等)先看做是原始数据,经过编译(词法分析、语法分析、语义分析、代码生成)生成字节码文件是加工后的数据。既然是数据就需数据结构承载数据,算法是数据进行运算的处理过程暂时不管。那么,需要思考什么结构可以承载字节码信息
如果只是单纯地去思考结构就没意义了,所以还需
思考字节码有哪些信息。

通过抽丝剥茧,核心问题是字节码有哪些信息。因为我们不?是重新设计,最简单解决方式?是编译生成一个class,然后看class。
来,上菜,不对上代码:

/**
 * 字节码有什么信息呢
 */
public class HelloByteCode {
    
    /**定义个私有属性**/
    private int a;
    
    /**定义个私有属性**/
    private static int aStatic;
    
    /**定义个私有属性**/
    private final static int aFinalStatic = 1;
    
    
    
    public HelloByteCode() {
        
    }

    public void testMethod() {
        
    }

    public void testMethod(int a) {
        this.a = a;
    }

    public static void main(String[] args) {
        
    }
}

打开命令行执行:

javac -encoding utf-8 HelloByteCode.java

可以看到生成HelloByteCode.class文件,该文件记录了什么信息呢?
NodePad打开是什么样呢?
应该是乱码,一探究竟:



(O_o)??,不认识,果然是乱码。那么怎么打开呢?
别着急,我们可以用下面的命令查看

javap -verbose HelloByteCode.class

结果如下:

Classfile /HelloByteCode.class
  Last modified 2022-10-19; size 478 bytes
  MD5 checksum dc8978d076ad0c0217b845692932bc77
  Compiled from "HelloByteCode.java"
public class HelloByteCode
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#21         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#22         // HelloByteCode.a:I
   #3 = Class              #23            // HelloByteCode
   #4 = Class              #24            // java/lang/Object
   #5 = Utf8               a
   #6 = Utf8               I
   #7 = Utf8               aStatic
   #8 = Utf8               aFinalStatic
   #9 = Utf8               ConstantValue
  #10 = Integer            1
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               testMethod
  #16 = Utf8               (I)V
  #17 = Utf8               main
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               SourceFile
  #20 = Utf8               HelloByteCode.java
  #21 = NameAndType        #11:#12        // "<init>":()V
  #22 = NameAndType        #5:#6          // a:I
  #23 = Utf8               HelloByteCode
  #24 = Utf8               java/lang/Object
{
  public HelloByteCode();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 17: 0
        line 19: 4

  public void testMethod();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 23: 0

  public void testMethod(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: putfield      #2                  // Field a:I
         5: return
      LineNumberTable:
        line 26: 0
        line 27: 5

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 31: 0
}
SourceFile: "HelloByteCode.java"

看上面的内容,可以看到一些熟悉的内容,定义变量名?,方法名,return,Integer,main方法?和常量值1,其他的信息就有些陌生了。不过这信息还是给我们看的,计算机看到的应该是01编码,因此通过编辑器打开16进制的信息如下:

可以通过:vi HelloByteCode.class
进入交互模式(通过:):%!xxd
如果您用的是windows,可以通过Git Bash查看

00000000: cafe babe 0000 0034 0019 0a00 0400 1509  .......4........
00000010: 0003 0016 0700 1707 0018 0100 0161 0100  .............a..
00000020: 0149 0100 0761 5374 6174 6963 0100 0c61  .I...aStatic...a
00000030: 4669 6e61 6c53 7461 7469 6301 000d 436f  FinalStatic...Co
00000040: 6e73 7461 6e74 5661 6c75 6503 0000 0001  nstantValue.....
00000050: 0100 063c 696e 6974 3e01 0003 2829 5601  ...<init>...()V.
00000060: 0004 436f 6465 0100 0f4c 696e 654e 756d  ..Code...LineNum
00000070: 6265 7254 6162 6c65 0100 0a74 6573 744d  berTable...testM
00000080: 6574 686f 6401 0004 2849 2956 0100 046d  ethod...(I)V...m
00000090: 6169 6e01 0016 285b 4c6a 6176 612f 6c61  ain...([Ljava/la
000000a0: 6e67 2f53 7472 696e 673b 2956 0100 0a53  ng/String;)V...S
000000b0: 6f75 7263 6546 696c 6501 0012 4865 6c6c  ourceFile...Hell
000000c0: 6f42 7974 6543 6f64 652e 6a61 7661 0c00  oByteCode.java..
000000d0: 0b00 0c0c 0005 0006 0100 0d48 656c 6c6f  ...........Hello
000000e0: 4279 7465 436f 6465 0100 106a 6176 612f  ByteCode...java/
000000f0: 6c61 6e67 2f4f 626a 6563 7400 2100 0300  lang/Object.!...
00000100: 0400 0000 0300 0200 0500 0600 0000 0a00  ................
00000110: 0700 0600 0000 1a00 0800 0600 0100 0900  ................
00000120: 0000 0200 0a00 0400 0100 0b00 0c00 0100  ................
00000130: 0d00 0000 2100 0100 0100 0000 052a b700  ....!........*..
00000140: 01b1 0000 0001 000e 0000 000a 0002 0000  ................
00000150: 0011 0004 0013 0001 000f 000c 0001 000d  ................
00000160: 0000 0019 0000 0001 0000 0001 b100 0000  ................
00000170: 0100 0e00 0000 0600 0100 0000 1700 0100  ................
00000180: 0f00 1000 0100 0d00 0000 2200 0200 0200  ..........".....
00000190: 0000 062a 1bb5 0002 b100 0000 0100 0e00  ...*............
000001a0: 0000 0a00 0200 0000 1a00 0500 1b00 0900  ................
000001b0: 1100 1200 0100 0d00 0000 1900 0000 0100  ................
000001c0: 0000 01b1 0000 0001 000e 0000 0006 0001  ................
000001d0: 0000 001f 0001 0013 0000 0002 0014 0a    ...............

第一行,看到cafe babe 这个我们应该熟系一个故事Java命名的来历。这个表示该文件是Class文件,基于安全方面的考虑,判断一个文件是什么文件,不能简单的通过后缀名判断(后缀名可改),应该通过魔数(Magic Number)。
字节码规定 Class 文件开头的 4 个字节的无符号整数称为魔数,它的唯一作用是确定这个文件是否为一个能被虚拟机接受的
有效合法的 Class 文件。魔数值固定为 0xCAFEBABE,不会改变。一个 Class 文件不以 0xCAFEBABE 开头,虚拟机在进行文件校验的时候就会直接抛出以下错误:

Error: A JNI error has occurred, please check your installation and try again

Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1685430635 in class file HelloByteCodeTest

其他信息,暂时看不出来,到此发现了字节码真实的数据。
接下来我们就来分析字节码,来看字节码怎么记录我们的程序,请看下回分解。

关注点赞转发三重操作,防止走丢(^U^)ノ~YO

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表