编译后的成果物层面

同样的C\C++源文件文件,经过不同的计算机硬件(x86平台、arm、AMD)、不同的操作系统(Linux\mac\windows etc.)上的编译器编译后,生成了不同的机器码,是互不通用的。

而Java源码(.java)经过编译后,生成了class字节码文件,通过不同平台上的JVM(Java 虚拟机)都可以解释执行。JVM掩盖了计算机硬件和操作系统的差异,对class提供了统一的执行接口,这就是java为啥能跨平台的原因。

在面向对象设计原则中,有个原则叫“开闭原则”(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。java的跨平台性就是遵守了这一原则。它所使用的设计模式,是适配器模式。

#TODO: 如果深入理解这个问题,可能涉及到很多层面(编译原理、操作系统提供的API、操作系统内核提供的系统调用、CPU指令集),不同的操作系统中的函数库提供了不同的系统API(应用程序接口);不同的编译器和操作系统定义了不同的ABI(应用二进制接口);不同的CPU架构,有着不同的指令集。机器码就是指令集上指令对应的二进制(0和1组成的序列)表示。

以openjdk8为例

openjdk 是如何屏蔽操作系统和硬件的不同的呢?可以到jdk的源码中找到答案:

源码网址:jdk8u/jdk8u/hotspot: 00df30073cfa /

在源码目录的os和cpu中对应的代码,就是用来分别对操作系统和计算机硬件做屏蔽的

在学jvm的时候,我们很多人可能都见过下面这个图。下图中的Native Method Interface部分,就对应了上图中src目录下一部分的源码,所以如果想学好jvm,读源码是一个好方法。

首先我们来看看上面这个图的“Class File”这块,class文件是以什么样的形态被Class Loader SubSystem加载的。

Java的class文件是什么

Class文件是jvm认识的一种字节码文件,里面的地址都是逻辑的地址。最后需要运行在操作系统中,操作系统只能识别真实的物理地址。此时需要动态链接(这个过程就是将逻辑地址变成物理地址),就是在运行时动态地绑定对象、对象地址。

此外,它还是一组以8位字节为基础单位的二进制流(容错性低,错一个字节则整个class文件不可用;节省空间\可以不用定义传输的格式,比如json,xml,而直接用二进制流传输数据),各个数据项目严格按照顺序紧凑地排列在Class文件中,中间没有添加任何的分隔符,这使得整个Class文件中存储的内容几乎全部是程序运行的必需数据。

Class字节码文件只有两种数据类型:无符号数(u)和表(info)。

Class文件的规范可以在oracle的 jvm规格说明书 的第4节(4. The class File Format)中查看:

u4:4字节无符号数,u2类似

将一个.class文件用sublim打开,查看16进制形式,这个16进制形式按上面的ClassFile Structure对应解析例如:

package main.test;
public class classtest {public static void main(String[] args) {int a = 11;int b = 22;int c = a+b;System.out.println(c);}
}

生成class字节码后用sublim打开

cafe babe 0000 0034 0024 0a00 0500 1709
0018 0019 0a00 1a00 1b07 001c 0700 1d01
0006 3c69 6e69 743e 0100 0328 2956 0100
0443 6f64 6501 000f 4c69 6e65 4e75 6d62
6572 5461 626c 6501 0012 4c6f 6361 6c56
6172 6961 626c 6554 6162 6c65 0100 0474
6869 7301 0015 4c6d 6169 6e2f 7465 7374
2f63 6c61 7373 7465 7374 3b01 0004 6d61
696e 0100 1628 5b4c 6a61 7661 2f6c 616e
672f 5374 7269 6e67 3b29 5601 0004 6172
6773 0100 135b 4c6a 6176 612f 6c61 6e67
2f53 7472 696e 673b 0100 0161 0100 0149
0100 0162 0100 0163 0100 0a53 6f75 7263
6546 696c 6501 000e 636c 6173 7374 6573
742e 6a61 7661 0c00 0600 0707 001e 0c00
1f00 2007 0021 0c00 2200 2301 0013 6d61
696e 2f74 6573 742f 636c 6173 7374 6573
7401 0010 6a61 7661 2f6c 616e 672f 4f62
6a65 6374 0100 106a 6176 612f 6c61 6e67
2f53 7973 7465 6d01 0003 6f75 7401 0015
4c6a 6176 612f 696f 2f50 7269 6e74 5374
7265 616d 3b01 0013 6a61 7661 2f69 6f2f
5072 696e 7453 7472 6561 6d01 0007 7072
696e 746c 6e01 0004 2849 2956 0021 0004
0005 0000 0000 0002 0001 0006 0007 0001
0008 0000 002f 0001 0001 0000 0005 2ab7
0001 b100 0000 0200 0900 0000 0600 0100
0000 0200 0a00 0000 0c00 0100 0000 0500
0b00 0c00 0000 0900 0d00 0e00 0100 0800
0000 6a00 0200 0400 0000 1210 0b3c 1016
3d1b 1c60 3eb2 0002 1db6 0003 b100 0000
0200 0900 0000 1600 0500 0000 0400 0300
0500 0600 0600 0a00 0700 1100 0800 0a00
0000 2a00 0400 0000 1200 0f00 1000 0000
0300 0f00 1100 1200 0100 0600 0c00 1300
1200 0200 0a00 0800 1400 1200 0300 0100
1500 0000 0200 16

则,

cafe babe  (u4   magic;)

0000(u2             minor_version;)

0034(u2             major_version;)

0024(u2             constant_pool_count;)

......

其余类似。

思考题:string和stringbuffer的效率孰高孰低?

答:第一个层面:string有常量池,每次改变都会重新创建一个对象,stringbuffer是只创建一个对象,作出的改变都是在原来对象基础上进行append,大量的字符串拼接操作下,stringbuffer的效率要高于string。

补充知识:
String:字符串常量,字符串长度不可变。Java中String 是immutable(不可变)的。
用于存放字符的数组被声明为final的,因此只能赋值一次,不可再更改。String str = "hello";存放在常量池中,String str=new String("hello");存放在堆上。StringBuffer:字符串变量(Synchronized,即线程安全)。如果要频繁对字符串内容进行修改,
出于效率考虑最好使用 StringBuffer,如果想转成 String 类型,
可以调用 StringBuffer 的 toString() 方法。
Java.lang.StringBuffer 线程安全的可变字符序列。
在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。StringBuilder:字符串变量(非线程安全)。在内部 StringBuilder 对象被当作是一个
包含字符序列的变长数组。基本原则:
如果要操作少量的数据用 String ;
单线程操作大量数据用StringBuilder ;
多线程操作大量数据,用StringBuffer。

第二个层面:查看字节码

查看Class字节码文件命令:

javap -verbose Test.class

格式

  • 存储:操作码+操作数 ,如istore_1
  • iload_x: 从局部变量表中加载int类型的数据到操作数栈,lload fload dload aload
  • 常量加载到操作数栈:ldc ldc2 ldc_w bipush...
  • 对象创建、访问指令:new newarray XXXarray getfield pupfild getstatic putstatic
  • 控制转移指令:ifeq iflt ifle ifgt goto goto_w
  • 方法调用指令:invokevirtual(调用实例方法) invokestatic(调用静态方法) invokeintface invokeXXX

以下面的类为例(只看class1,和class2,main的部分只是为了程序运行时有个显示):

package main.test;class StringTest {public static void class1(){String str = "";for (int i=0;i<10;i++){str = str+"love,";}System.out.println(str);}public static void class2(){StringBuffer str = new StringBuffer();for (int i=0;i<10;i++){str.append("love,");}System.out.println(str);}public static void main(String[] args) {System.out.println("basket" + "ball");}}

执行#javap -verbose StringTest.class命令,查看class的字节码,如下面代码所示,其实真正的字节码是上一节用sublim打开看到的十六进制的样子,用javap命令打开看到的其实翻译后的字节码,方便人类查看的,下面这段翻译后的字节码文本比较长,我们只关注其中的class1,和class2,分析一下在循环使用时,string和stringbuffer的底层执行逻辑,然后判断孰优孰劣:


#javap -verbose StringTest.classLast modified 2021年11月29日; size 1248 bytesMD5 checksum ab20ccd626637e7b03278358905d205eCompiled from "classtest.java"
class main.test.StringTestminor version: 0major version: 52flags: (0x0020) ACC_SUPERthis_class: #15                         // main/test/StringTestsuper_class: #16                        // java/lang/Objectinterfaces: 0, fields: 0, methods: 4, attributes: 1
Constant pool:#1 = Methodref          #16.#40        // java/lang/Object."<init>":()V#2 = String             #41            //#3 = Class              #42            // java/lang/StringBuilder#4 = Methodref          #3.#40         // java/lang/StringBuilder."<init>":()V#5 = Methodref          #3.#43         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#6 = String             #44            // love,#7 = Methodref          #3.#45         // java/lang/StringBuilder.toString:()Ljava/lang/String;#8 = Fieldref           #46.#47        // java/lang/System.out:Ljava/io/PrintStream;#9 = Methodref          #48.#49        // java/io/PrintStream.println:(Ljava/lang/String;)V#10 = Class              #50            // java/lang/StringBuffer#11 = Methodref          #10.#40        // java/lang/StringBuffer."<init>":()V#12 = Methodref          #10.#51        // java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;#13 = Methodref          #48.#52        // java/io/PrintStream.println:(Ljava/lang/Object;)V#14 = String             #53            // basketball#15 = Class              #54            // main/test/StringTest#16 = Class              #55            // java/lang/Object#17 = Utf8               <init>#18 = Utf8               ()V#19 = Utf8               Code#20 = Utf8               LineNumberTable#21 = Utf8               LocalVariableTable#22 = Utf8               this#23 = Utf8               Lmain/test/StringTest;#24 = Utf8               class1#25 = Utf8               i#26 = Utf8               I#27 = Utf8               str#28 = Utf8               Ljava/lang/String;#29 = Utf8               StackMapTable#30 = Class              #56            // java/lang/String#31 = Utf8               class2#32 = Utf8               Ljava/lang/StringBuffer;#33 = Class              #50            // java/lang/StringBuffer#34 = Utf8               main#35 = Utf8               ([Ljava/lang/String;)V#36 = Utf8               args#37 = Utf8               [Ljava/lang/String;#38 = Utf8               SourceFile#39 = Utf8               classtest.java#40 = NameAndType        #17:#18        // "<init>":()V#41 = Utf8#42 = Utf8               java/lang/StringBuilder#43 = NameAndType        #57:#58        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#44 = Utf8               love,#45 = NameAndType        #59:#60        // toString:()Ljava/lang/String;#46 = Class              #61            // java/lang/System#47 = NameAndType        #62:#63        // out:Ljava/io/PrintStream;#48 = Class              #64            // java/io/PrintStream#49 = NameAndType        #65:#66        // println:(Ljava/lang/String;)V#50 = Utf8               java/lang/StringBuffer#51 = NameAndType        #57:#67        // append:(Ljava/lang/String;)Ljava/lang/StringBuffer;#52 = NameAndType        #65:#68        // println:(Ljava/lang/Object;)V#53 = Utf8               basketball#54 = Utf8               main/test/StringTest#55 = Utf8               java/lang/Object#56 = Utf8               java/lang/String#57 = Utf8               append#58 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;#59 = Utf8               toString#60 = Utf8               ()Ljava/lang/String;#61 = Utf8               java/lang/System#62 = Utf8               out#63 = Utf8               Ljava/io/PrintStream;#64 = Utf8               java/io/PrintStream#65 = Utf8               println#66 = Utf8               (Ljava/lang/String;)V#67 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuffer;#68 = Utf8               (Ljava/lang/Object;)V
{main.test.StringTest();descriptor: ()Vflags: (0x0000)Code:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 4: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0  this   Lmain/test/StringTest;public static void class1();descriptor: ()Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=2, locals=2, args_size=00: ldc           #2                  // String2: astore_03: iconst_04: istore_15: iload_16: bipush        108: if_icmpge     3711: new           #3                  // class java/lang/StringBuilder14: dup15: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V18: aload_019: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;22: ldc           #6                  // String love,24: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;27: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;30: astore_031: iinc          1, 134: goto          537: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;40: aload_041: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V44: returnLineNumberTable:line 7: 0line 8: 3line 9: 11line 8: 31line 11: 37line 13: 44LocalVariableTable:Start  Length  Slot  Name   Signature5      32     1     i   I3      42     0   str   Ljava/lang/String;StackMapTable: number_of_entries = 2frame_type = 253 /* append */offset_delta = 5locals = [ class java/lang/String, int ]frame_type = 250 /* chop */offset_delta = 31public static void class2();descriptor: ()Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=2, locals=2, args_size=00: new           #10                 // class java/lang/StringBuffer3: dup4: invokespecial #11                 // Method java/lang/StringBuffer."<init>":()V7: astore_08: iconst_09: istore_110: iload_111: bipush        1013: if_icmpge     2916: aload_017: ldc           #6                  // String love,19: invokevirtual #12                 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;22: pop23: iinc          1, 126: goto          1029: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;32: aload_033: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V36: returnLineNumberTable:line 16: 0line 17: 8line 18: 16line 17: 23line 20: 29line 22: 36LocalVariableTable:Start  Length  Slot  Name   Signature10      19     1     i   I8      29     0   str   Ljava/lang/StringBuffer;StackMapTable: number_of_entries = 2frame_type = 253 /* append */offset_delta = 10locals = [ class java/lang/StringBuffer, int ]frame_type = 250 /* chop */offset_delta = 18public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=2, locals=1, args_size=10: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #14                 // String basketball5: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 25: 0line 26: 8LocalVariableTable:Start  Length  Slot  Name   Signature0       9     0  args   [Ljava/lang/String;
}
SourceFile: "classtest.java"
  • 在class1中if_icmpge 和goto之间的就是源码的for循环部分,这里我们可以看到,每循环一次,中间都会调用一下new,new出一个stringBuilder的对象(string底层借助的是stringBuilder来实现的),循环多少次就要new多少次,可见开销是很大的
         8:if_icmpge     3711: new           #3                  // class java/lang/StringBuilder14: dup15: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V18: aload_019: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;22: ldc           #6                  // String love,24: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;27: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;30: astore_031: iinc          1, 134: goto          5
  • 在class2中,if_icmpge 和goto之间,只是调用的append操作,复用的是类开始的new的那个对象,由此可见,频繁操作时,stringBuffer要比string高效
        13: if_icmpge     2916: aload_017: ldc           #6                  // String love,19: invokevirtual #12                 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;22: pop23: iinc          1, 126: goto          10

[中级01]java为什么能跨平台,而C\C++语言不能跨平台相关推荐

  1. java 语言是跨平台的吗_java语言的跨平台特点是由什么保证的?有什么用吗?

    作为最火的编程语言之一,Java与其他语言的最大区别就在于它的跨平台性,这种特性使得Java在任何平台上都可以运行,问题来了,那你们知道Java它的跨平台性到底是由什么保证的呢?下面跟我一起来了解一下 ...

  2. Java企业实训 - 01 - Java前奏

    前言: 虽然个人专攻.NET方向,不过由于个人是干教育行业的,方方面面的东西,不能说都必须精通,但肯定多少都会涉及到. 一个菜鸟学员,从啥都不会,经过一步步学习,最后到企业上手掌管一个模块甚至一个项目 ...

  3. 01.Java基础语法

    目录 01.Java基础语法 一,环境搭建 1,Java体系与特点 2,Java跨平台原理 2.1 Java虚拟机(JVM) 2.2 垃圾回收器(GC) 2.3 Java SE组成概念图 2.4 JD ...

  4. Java 基础-01 Java语言入门

    文章目录 Java 基础-01 Java语言入门 1.计算机基本概念 1.1 计算机概述 1.2 计算机组成 1.3 CPU.内存与硬盘 2.软件基本概念 2.1 软件概述 2.2 人机交互方式 2. ...

  5. java学习-01 java历程以及java学习环境配置

    01 Java的历程 前言: ​ 最近在学习Java,之前学习过挺久的Python,现在想来,对很多基础地方都不是很清晰,原因就是没有养成良好的笔记记录习惯,因此,这次学习Java准备从基础开始记录笔 ...

  6. Java-Runoob-高级教程-实例-数组:01. Java 实例 – 数组排序及元素查找

    ylbtech-Java-Runoob-高级教程-实例-数组:01. Java 实例 – 数组排序及元素查找 1.返回顶部 1. Java 实例 - 数组排序及元素查找  Java 实例 以下实例演示 ...

  7. java/01/java简介,java基本概念,java基本类型的划分

    厚积薄发 java笔记 java/01/java简介,java基本概念,java基本类型的划分 java :一门面向对象的计算机编程语言,java称得上是行业开发标准结构,1990年代初开发,1995 ...

  8. 01 | Java入门级学习指南

    01 | Java入门级学习指南 前言 第一部分:JAVA 设计和编程基础 1.1Java 语言基础 1.2Java 面向对象 1.3Java 核心类库 1.4Java SE实战项目 第二部分:数据库 ...

  9. Java基础01 Java的安装和配置

    超详细的Java知识点路线图 前言 一直想写一个系列文章,包含:Java基础.JavaWeb.Java企业级框架.分布式.就业等,希望能帮助到想学Java的朋友,自己也能获得提升,这是个大工程,千里之 ...

最新文章

  1. win7 桌面右下角音量图标消失的解决办法 参考
  2. 关于autoupgader的狗屎问题
  3. html5教学案例撰写,怎样撰写教育教学案例
  4. axios 使用步骤很简单,首先在前端项目中,引入 axios:
  5. Vue 项目结构介绍
  6. GCD(Grand Central Dispatch)和Block 使用-浅析
  7. mysql 前沿表设计_史上最简单MySQL教程详解(基础篇)之表的维护和改造
  8. 从Face ID说起,浅析人脸识别之刷脸技术
  9. 一文搞懂什么是禁忌搜索算法Tabu Search【附应用举例】
  10. 非华为电脑安装华为电脑管家教程(支持一碰传,多屏协同等)
  11. 5S管理活动的实施和运行方案
  12. LWC 71: 780. Reaching Points
  13. 邓俊辉 数据结构 习题4-18 Fermat-Lagrange定理代码实现
  14. Error:A problem occurred configuring project ':app'. failed to find target with hash string 'andro
  15. Ubuntu WPS 字体缺失问题解决
  16. JavaScript 递归算法
  17. 预计一季度国内手机市场下滑逾30%;美国或取诺基亚爱立信控
  18. AI智能电话机器人错误码大全解析
  19. java excel模板 下载_Java从服务端下载Excel模板文件
  20. 软考发的是职业证书,还是执业证书?

热门文章

  1. 第十二章_网络搭建及训练
  2. optparse命令解析模块
  3. sklearn库的学习入门
  4. 通过Docker进程pid获取容器id
  5. ActiveMQ持久化方式
  6. android PopupWindow实现从底部弹出或滑出选择菜单或窗口
  7. 科技公司钟爱的50款开源工具
  8. springmvc整合mysql_SpringMVC+Spring+Mybatis+Maven+mysql整合
  9. 【ROS】机器人编程实践
  10. cmake使用教程(四)调用外部共享库和头文件