首页 文章 精选 留言 我的

精选列表

搜索[手游社交组件],共10010篇文章
优秀的个人博客,低调大师

android用户界面-组件Widget-常用组件

用户会员注册实例 介绍控件 文本框TextView 编辑框EditText 密码文本框EditText 单选按钮RadioButton 复选框CheckBox 开关按钮ToggleButton 下拉列表Spinner 实例: 注册页面 /Chapter04_UI_CommonWidget/src/com/amaker/test/MainActivity.java 代码 packagecom.amaker.test; importandroid.app.Activity; importandroid.content.Intent; importandroid.os.Bundle; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.ArrayAdapter; importandroid.widget.Button; importandroid.widget.CheckBox; importandroid.widget.EditText; importandroid.widget.RadioButton; importandroid.widget.Spinner; importandroid.widget.ToggleButton; publicclassMainActivityextendsActivity{ privateButtonregister,cancel; privateToggleButtonmarriged; privateRadioButtonmale,female; privateEditTextusername,password; privateSpinnerposition; privateCheckBoxreading,swimming; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); username=(EditText)findViewById(R.id.username); password=(EditText)findViewById(R.id.password); male=(RadioButton)findViewById(R.id.male); female=(RadioButton)findViewById(R.id.female); reading=(CheckBox)findViewById(R.id.reading); swimming=(CheckBox)findViewById(R.id.swimming); marriged=(ToggleButton)findViewById(R.id.marriged); position=(Spinner)findViewById(R.id.position); String[]str={"CEO","CFO","PM"}; ArrayAdapteraa=newArrayAdapter(this,android.R.layout.simple_spinner_dropdown_item,str); position.setAdapter(aa); register=(Button)findViewById(R.id.register); cancel=(Button)findViewById(R.id.cancel); register.setOnClickListener(newOnClickListener(){ publicvoidonClick(Viewv){ Bundleb=newBundle(); b.putString("username","用户名称:"+username.getText().toString()); b.putString("password","用户密码:"+password.getText().toString()); if(male.isChecked()){ b.putString("gender","性别:男"); }else{ b.putString("gender","性别:女"); } Stringtemp="爱好:"; if(reading.isChecked()){ temp+="阅读"; } if(swimming.isChecked()){ temp+=""; temp+="游泳"; } b.putString("hobby",temp); if(marriged.isChecked()){ b.putString("marriged","婚否:已婚"); }else{ b.putString("marriged","婚否:未婚"); } b.putString("position","职位:"+position.getSelectedItem().toString()); Intentintent=newIntent(MainActivity.this,ResultActivity.class); intent.putExtra("data",b); startActivity(intent); } }); } } 注册结果页面 /Chapter04_UI_CommonWidget/src/com/amaker/test/ResultActivity.java 代码 packagecom.amaker.test; importjava.util.ArrayList; importjava.util.List; importandroid.app.Activity; importandroid.content.Intent; importandroid.os.Bundle; importandroid.widget.ArrayAdapter; importandroid.widget.ListView; publicclassResultActivityextendsActivity{ privateListViewlistView; protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.result); listView=(ListView)findViewById(R.id.ListView01); Intentintent=this.getIntent(); Bundleb=intent.getBundleExtra("data"); System.out.println(b.getString("username")); Listlist=newArrayList(); list.add(b.getString("username")); list.add(b.getString("password")); list.add(b.getString("position")); list.add(b.getString("gender")); list.add(b.getString("hobby")); list.add(b.getString("marriged")); ArrayAdapteradapter=newArrayAdapter(this,android.R.layout.simple_list_item_1,list); listView.setAdapter(adapter); } } 布局文件 /Chapter04_UI_CommonWidget/res/layout/main.xml 代码 <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TableLayout android:id="@+id/TableLayout01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:stretchColumns="1" > <TableRow android:id="@+id/TableRow01" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:text="用户名称" android:id="@+id/TextView01" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> <EditText android:text="" android:id="@+id/username" android:layout_width="wrap_content" android:layout_height="wrap_content" ></EditText> </TableRow> <TableRow android:id="@+id/TableRow02" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:text="用户密码" android:id="@+id/TextView02" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> <EditText android:text="" android:id="@+id/password" android:layout_width="wrap_content" android:layout_height="wrap_content" android:password="true" ></EditText> </TableRow> <TableRow android:id="@+id/TableRow03" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:text="性别" android:id="@+id/TextView03" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> <RadioGroup android:id="@+id/gender_g" android:layout_width="wrap_content" android:layout_height="wrap_content"> <RadioButton android:text="男" android:id="@+id/male" android:layout_width="wrap_content" android:layout_height="wrap_content"></RadioButton> <RadioButton android:text="女" android:id="@+id/female" android:layout_width="wrap_content" android:layout_height="wrap_content"></RadioButton> </RadioGroup> </TableRow> <TableRow android:id="@+id/TableRow04" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:text="婚否" android:id="@+id/TextView04" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> <ToggleButton android:text="@+id/ToggleButton01" android:id="@+id/marriged" android:layout_width="wrap_content" android:layout_height="wrap_content"></ToggleButton> </TableRow> <TableRow android:id="@+id/TableRow05" android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView android:text="爱好" android:id="@+id/hobby" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> <CheckBox android:text="阅读" android:id="@+id/reading" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_column="1" ></CheckBox> <CheckBox android:text="游泳" android:id="@+id/swimming" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_column="1" ></CheckBox> </TableRow> <TableRow android:id="@+id/TableRow06" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:text="职务" android:id="@+id/TextView05" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> <Spinner android:id="@+id/position" android:layout_width="wrap_content" android:layout_height="wrap_content"></Spinner> </TableRow> <TableRow android:id="@+id/TableRow07" android:layout_width="wrap_content" android:layout_height="wrap_content"> <Button android:text="取消" android:id="@+id/cancel" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="注册" android:id="@+id/register" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </TableRow> </TableLayout> </LinearLayout> /Chapter04_UI_CommonWidget/res/layout/result.xml 代码 <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ListView android:id="@+id/ListView01" android:layout_width="wrap_content" android:layout_height="wrap_content"></ListView> </LinearLayout> 本文转自linzheng 51CTO博客,原文链接:http://blog.51cto.com/linzheng/1080698

优秀的个人博客,低调大师

为了搞清楚类加载,竟然撸JVM!

持续坚持原创输出,点击蓝字关注我吧 作者:小傅哥博客:https://bugstack.cn ❝ 沉淀、分享、成长,让自己和他人都能有所收获!😜 ❞ 目录 一、前言 二、面试题 三、类加载过程描述 四、写个代码加载下 1. 案例工程 2. 代码讲解 五、解析字节码文件 1. 提取部分字节码 2. 解析魔数并校验 3. 解析版本号信息 4. 解析全部内容对照 六、总结 七、系列推荐 一、前言 学习,不知道从哪下手? 当学习一个新知识不知道从哪下手的时候,最有效的办法是梳理这个知识结构的脉络信息,汇总出一整张的思维导出。接下来就是按照思维导图的知识结构,一个个学习相应的知识点,并汇总记录。 就像JVM的学习,可以说它包括了非常多的内容,也是一个庞大的知识体系。例如:类加载、加载器、生命周期、性能优化、调优参数、调优工具、优化方案、内存区域、虚拟机栈、直接内存、内存溢出、元空间、垃圾回收、可达性分析、标记清除、回收过程等等。如果没有梳理的一头扎进去,东一榔头西一棒子,很容易造成学习恐惧感。 如图 24-1 是 JVM 知识框架梳理,后续我们会按照这个结构陆续讲解每一块内容。 图 24-1 JVM 知识框架 二、面试题 谢飞机,小记!,很多知识根本就是背背背,也没法操作,难学! 「谢飞机」:大哥,你问我两个JVM问题,我看看我自己还行不! 「面试官」:啊?嗯!往死了问还是? 「谢飞机」:就就就,都行!你看着来! 「面试官」:啊,那 JVM 加载过程都是什么步骤? 「谢飞机」:巴拉巴拉,加载、验证、准备、解析、初始化、使用、卸载! 「面试官」:嗯,背的挺好!我怀疑你没操作过! 那加载的时候,JVM 规范规定从第几位开始是解析常量池,以及数据类型是如何定义的,u1、u2、u4,是怎么个玩意? 「谢飞机」:握草!算了,告诉我看啥吧! 三、类加载过程描述 图 24-2 JVM 类加载过程 「JVM 类加载过程分为」,加载、链接、初始化、使用和卸载这四个阶段,在链接中又包括:验证、准备、解析。 「加载」:Java 虚拟机规范对 class 文件格式进行了严格的规则,但对于从哪里加载 class 文件,却非常自由。Java 虚拟机实现可以从文件系统读取、从JAR(或ZIP)压缩包中提取 class 文件。除此之外也可以通过网络下载、数据库加载,甚至是运行时直接生成的 class 文件。 「链接」:包括了三个阶段; 验证,确保被加载类的正确性,验证字节流是否符合 class 文件规范,例魔数 0xCAFEBABE,以及版本号等。 准备,为类的静态变量分配内存并设置变量初始值等 解析,解析包括解析出常量池数据和属性表信息,这里会包括 ConstantPool 结构体以及 AttributeInfo 接口等。 「初始化」:类加载完成的最后一步就是初始化,目的就是为标记常量值的字段赋值,以及执行 <clinit> 方法的过程。 JVM虚拟机通过锁的方式确保 clinit 仅被执行一次 「使用」:程序代码执行使用阶段。 「卸载」:程序代码退出、异常、结束等。 四、写个代码加载下 JVM 之所以不好掌握,主要是因为不好实操。虚拟机是 C++ 写的,很多 Java 程序员根本就不会去读,或者读不懂。那么,也就没办法实实在在的体会到,到底是怎么加载的,加载的时候都干了啥。只有看到代码,我才觉得自己学会了! 所以,我们这里要手动写一下,JVM 虚拟机的部分代码,也就是类加载的过程。通过 Java 代码来实现 Java 虚拟机的部分功能,让开发 Java 代码的程序员更容易理解虚拟机的执行过程。 1. 案例工程 interview-24├──pom.xml└──src└──main│└──java│└──org.itstack.interview.jvm│├──classpath││├──impl│││├──CompositeEntry.java│││├──DirEntry.java│││├──WildcardEntry.java│││└──ZipEntry.java││├──Classpath.java││└──Entry.java│├──Cmd.java│└──Main.java└──test└──java└──org.itstack.interview.jvm.test└──HelloWorld.java 「以上」,工程结构就是按照 JVM 虚拟机规范,使用 Java 代码实现 JVM 中加载 class 文件部分内容。当然这部分还不包括解析,因为解析部分的代码非常庞大,我们先从把 .class 文件加载读取开始了解。 2. 代码讲解 2.1 定义类路径接口(Entry) publicinterfaceEntry{byte[]readClass(StringclassName)throwsIOException;staticEntrycreate(Stringpath){//File.pathSeparator;路径分隔符(win\linux)if(path.contains(File.pathSeparator)){returnnewCompositeEntry(path);}if(path.endsWith("*")){returnnewWildcardEntry(path);}if(path.endsWith(".jar")||path.endsWith(".JAR")||path.endsWith(".zip")||path.endsWith(".ZIP")){returnnewZipEntry(path);}returnnewDirEntry(path);}} 接口中提供了接口方法 readClass 和静态方法 create(String path)。 jdk1.8 是可以在接口中编写静态方法的,在设计上属于补全了抽象类的类似功能。这个静态方法主要是按照不同的路径地址类型,提供不同的解析方法。包括:CompositeEntry、WildcardEntry、ZipEntry、DirEntry,这四种。 接下来分别看每一种的具体实现 2.2 目录形式路径(DirEntry) publicclassDirEntryimplementsEntry{privatePathabsolutePath;publicDirEntry(Stringpath){//获取绝对路径this.absolutePath=Paths.get(path).toAbsolutePath();}@Overridepublicbyte[]readClass(StringclassName)throwsIOException{returnFiles.readAllBytes(absolutePath.resolve(className));}@OverridepublicStringtoString(){returnthis.absolutePath.toString();}} 目录形式的通过读取绝对路径下的文件,通过 Files.readAllBytes 方式获取字节码。 2.3 压缩包形式路径(ZipEntry) publicclassZipEntryimplementsEntry{privatePathabsolutePath;publicZipEntry(Stringpath){//获取绝对路径this.absolutePath=Paths.get(path).toAbsolutePath();}@Overridepublicbyte[]readClass(StringclassName)throwsIOException{try(FileSystemzipFs=FileSystems.newFileSystem(absolutePath,null)){returnFiles.readAllBytes(zipFs.getPath(className));}}@OverridepublicStringtoString(){returnthis.absolutePath.toString();}} 其实压缩包形式与目录形式,只有在文件读取上有包装差别而已。 FileSystems.newFileSystem 2.4 混合形式路径(CompositeEntry) publicclassCompositeEntryimplementsEntry{privatefinalList<Entry>entryList=newArrayList<>();publicCompositeEntry(StringpathList){String[]paths=pathList.split(File.pathSeparator);for(Stringpath:paths){entryList.add(Entry.create(path));}}@Overridepublicbyte[]readClass(StringclassName)throwsIOException{for(Entryentry:entryList){try{returnentry.readClass(className);}catch(Exceptionignored){//ignored}}thrownewIOException("classnotfound"+className);}@OverridepublicStringtoString(){String[]strs=newString[entryList.size()];for(inti=0;i<entryList.size();i++){strs[i]=entryList.get(i).toString();}returnString.join(File.pathSeparator,strs);}} File.pathSeparator,是一个分隔符属性,win/linux 有不同的类型,所以使用这个方法进行分割路径。 分割后的路径装到 List 集合中,这个过程属于拆分路径。 2.5 通配符类型路径(WildcardEntry) publicclassWildcardEntryextendsCompositeEntry{publicWildcardEntry(Stringpath){super(toPathList(path));}privatestaticStringtoPathList(StringwildcardPath){StringbaseDir=wildcardPath.replace("*","");//remove*try{returnFiles.walk(Paths.get(baseDir)).filter(Files::isRegularFile).map(Path::toString).filter(p->p.endsWith(".jar")||p.endsWith(".JAR")).collect(Collectors.joining(File.pathSeparator));}catch(IOExceptione){return"";}}} 这个类属于混合形式路径处理类的子类,唯一提供的方法就是把类路径解析出来。 2.6 类路径解析(Classpath) 启动类路径、扩展类路径、用户类路径,熟悉吗?是不经常看到这几句话,那么时候怎么实现的呢? 有了上面我们做的一些基础类的工作,接下来就是类解析的实际调用过程。代码如下: publicclassClasspath{privateEntrybootstrapClasspath;//启动类路径privateEntryextensionClasspath;//扩展类路径privateEntryuserClasspath;//用户类路径publicClasspath(StringjreOption,StringcpOption){//启动类&扩展类"C:\ProgramFiles\Java\jdk1.8.0_161\jre"bootstrapAndExtensionClasspath(jreOption);//用户类F:\..\org\itstack\demo\test\HelloWorldparseUserClasspath(cpOption);}privatevoidbootstrapAndExtensionClasspath(StringjreOption){StringjreDir=getJreDir(jreOption);//..jre/lib/*StringjreLibPath=Paths.get(jreDir,"lib")+File.separator+"*";bootstrapClasspath=newWildcardEntry(jreLibPath);//..jre/lib/ext/*StringjreExtPath=Paths.get(jreDir,"lib","ext")+File.separator+"*";extensionClasspath=newWildcardEntry(jreExtPath);}privatestaticStringgetJreDir(StringjreOption){if(jreOption!=null&&Files.exists(Paths.get(jreOption))){returnjreOption;}if(Files.exists(Paths.get("./jre"))){return"./jre";}Stringjh=System.getenv("JAVA_HOME");if(jh!=null){returnPaths.get(jh,"jre").toString();}thrownewRuntimeException("CannotfindJREfolder!");}privatevoidparseUserClasspath(StringcpOption){if(cpOption==null){cpOption=".";}userClasspath=Entry.create(cpOption);}publicbyte[]readClass(StringclassName)throwsException{className=className+".class";//[readClass]启动类路径try{returnbootstrapClasspath.readClass(className);}catch(Exceptionignored){//ignored}//[readClass]扩展类路径try{returnextensionClasspath.readClass(className);}catch(Exceptionignored){//ignored}//[readClass]用户类路径returnuserClasspath.readClass(className);}} 启动类路径,bootstrapClasspath.readClass(className); 扩展类路径,extensionClasspath.readClass(className); 用户类路径,userClasspath.readClass(className); 这回就看到它们具体在哪使用了吧!有了具体的代码也就方便理解了 2.7 加载类测试验证 privatestaticvoidstartJVM(Cmdcmd){Classpathcp=newClasspath(cmd.jre,cmd.classpath);System.out.printf("classpath:%s class:%s args:%s\n",cp,cmd.getMainClass(),cmd.getAppArgs());//获取classNameStringclassName=cmd.getMainClass().replace(".","/");try{byte[]classData=cp.readClass(className);System.out.println(Arrays.toString(classData));}catch(Exceptione){System.out.println("Couldnotfindorloadmainclass"+cmd.getMainClass());e.printStackTrace();}} 这段就是使用 Classpath 类进行类路径加载,这里我们测试加载 java.lang.String 类。你可以加载其他的类,或者自己写的类 配置IDEA,program arguments 参数: -Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" java.lang.String 另外这里读取出的 class 文件信息,打印的是 byte 类型信息。 「测试结果」 [-54,-2,-70,-66,0,0,0,52,2,28,3,0,0,-40,0,3,0,0,-37,-1,3,0,0,-33,-1,3,0,1,0,0,8,0,15,8,0,61,8,0,85,8,0,88,8,0,89,8,0,112,8,0,-81,8,0,-75,8,0,-47,8,0,-45,1,0,0,1,0,3,40,41,73,1,0,20,40,41,76,106,97,118,97,47,108,97,110,103,47,79,98,106,101,99,116,59,1,0,20,40,41,76,106,97,118,97,47,108,97,110,103,47,83,116,114,105,110,103,59,1,0,3,40,41,86,1,0,3,40,41,90,1,0,4,40,41,91,...] 这块部分截取的程序运行打印结果,就是读取的 class 文件信息,只不过暂时还不能看出什么。接下来我们再把它翻译过来! 五、解析字节码文件 JVM 在把 class 文件加载完成后,接下来就进入链接的过程,这个过程包括了内容的校验、准备和解析,其实就是把 byte 类型 class 翻译过来,做相应的操作。 整个这个过程内容相对较多,这里只做部分逻辑的实现和讲解。如果读者感兴趣可以阅读小傅哥的《用Java实现JVM》专栏。 1. 提取部分字节码 //取部分字节码:java.lang.Stringprivatestaticbyte[]classData={-54,-2,-70,-66,0,0,0,52,2,26,3,0,0,-40,0,3,0,0,-37,-1,3,0,0,-33,-1,3,0,1,0,0,8,0,59,8,0,83,8,0,86,8,0,87,8,0,110,8,0,-83,8,0,-77,8,0,-49,8,0,-47,1,0,3,40,41,73,1,0,20,40,41,76,106,97,118,97,47,108,97,110,103,47,79,98,106,101,99,116,59,1,0,20,40,41,76,106,97,118,97,47,108,97,110,103,47,83,116,114,105,110,103,59,1,0,3,40,41,86,1,0,3,40,41,90,1,0,4,40,41,91,66,1,0,4,40,41,91,67,1,0,4,40,67,41,67,1,0,21,40,68,41,76,106,97,118,97,47,108,97,110,103,47,83,116,114,105,110,103,59,1,0,4,40,73,41,67,1,0,4}; java.lang.String 解析出来的字节码内容较多,当然包括的内容也多,比如魔数、版本、类、常量、方法等等。所以我们这里只截取部分进行进行解析。 2. 解析魔数并校验 很多文件格式都会规定满足该格式的文件必须以某几个固定字节开头,这几个字节主要起到标识作用,叫作魔数(magic number)。 例如; PDF文件以4字节“%PDF”(0x25、0x50、0x44、0x46)开头, ZIP文件以2字节“PK”(0x50、0x4B)开头 class文件以4字节“0xCAFEBABE”开头 privatestaticvoidreadAndCheckMagic(){System.out.println("\r\n------------校验魔数------------");//从class字节码中读取前四位byte[]magic_byte=newbyte[4];System.arraycopy(classData,0,magic_byte,0,4);//将4位byte字节转成16进制字符串Stringmagic_hex_str=newBigInteger(1,magic_byte).toString(16);System.out.println("magic_hex_str:"+magic_hex_str);//byte_magic_str是16进制的字符串,cafebabe,因为java中没有无符号整型,所以如果想要无符号只能放到更高位中longmagic_unsigned_int32=Long.parseLong(magic_hex_str,16);System.out.println("magic_unsigned_int32:"+magic_unsigned_int32);//魔数比对,一种通过字符串比对,另外一种使用假设的无符号16进制比较。如果使用无符号比较需要将0xCAFEBABE &0x0FFFFFFFFL与运算System.out.println("0xCAFEBABE &0x0FFFFFFFFL:"+(0xCAFEBABE&0x0FFFFFFFFL));if(magic_unsigned_int32==(0xCAFEBABE&0x0FFFFFFFFL)){System.out.println("class字节码魔数无符号16进制数值一致校验通过");}else{System.out.println("class字节码魔数无符号16进制数值一致校验拒绝");}} 读取字节码中的前四位, -54, -2, -70, -66,将这四位转换为16进制。 因为 java 中是没有无符号整型的,所以只能用更高位存放。 解析后就是魔数的对比,看是否与 CAFEBABE 一致。 「测试结果」 ------------校验魔数------------magic_hex_str:cafebabemagic_unsigned_int32:34056915820xCAFEBABE&0x0FFFFFFFFL:3405691582class字节码魔数无符号16进制数值一致校验通过 3. 解析版本号信息 刚才我们已经读取了4位魔数信息,接下来再读取2位,是版本信息。 魔数之后是class文件的次版本号和主版本号,都是u2类型。假设某class文件的主版本号是M,次版本号是m,那么完整的版本号可以表示成“M.m”的形式。次版本号只在J2SE 1.2之前用过,从1.2开始基本上就没有什么用了(都是0)。主版本号在J2SE 1.2之前是45,从1.2开始,每次有大版本的Java版本发布,都会加1{45、46、47、48、49、50、51、52} privatestaticvoidreadAndCheckVersion(){System.out.println("\r\n------------校验版本号------------");//从class字节码第4位开始读取,读取2位byte[]minor_byte=newbyte[2];System.arraycopy(classData,4,minor_byte,0,2);//将2位byte字节转成16进制字符串Stringminor_hex_str=newBigInteger(1,minor_byte).toString(16);System.out.println("minor_hex_str:"+minor_hex_str);//minor_unsigned_int32转成无符号16进制intminor_unsigned_int32=Integer.parseInt(minor_hex_str,16);System.out.println("minor_unsigned_int32:"+minor_unsigned_int32);//从class字节码第6位开始读取,读取2位byte[]major_byte=newbyte[2];System.arraycopy(classData,6,major_byte,0,2);//将2位byte字节转成16进制字符串Stringmajor_hex_str=newBigInteger(1,major_byte).toString(16);System.out.println("major_hex_str:"+major_hex_str);//major_unsigned_int32转成无符号16进制intmajor_unsigned_int32=Integer.parseInt(major_hex_str,16);System.out.println("major_unsigned_int32:"+major_unsigned_int32);System.out.println("版本号:"+major_unsigned_int32+"."+minor_unsigned_int32);} 这里有一个小技巧,class 文件解析出来是一整片的内容,JVM 需要按照虚拟机规范,一段一段的解析出所有的信息。 同样这里我们需要把2位byte转换为16进制信息,并继续从第6位继续读取2位信息。组合出来的才是版本信息。 「测试结果」 ------------校验版本号------------minor_hex_str:0minor_unsigned_int32:0major_hex_str:34major_unsigned_int32:52版本号:52.0 4. 解析全部内容对照 按照 JVM 的加载过程,其实远不止魔数和版本号信息,还有很多其他内容,这里我们可以把测试结果展示出来,方便大家有一个学习结果的比对印象。 classpath:org.itstack.demo.jvm.classpath.Classpath@4bf558aaclass:java.lang.Stringargs:nullversion:52.0constantscount:540accessflags:0x31thisclass:java/lang/Stringsuperclass:java/lang/Objectinterfaces:[java/io/Serializable,java/lang/Comparable,java/lang/CharSequence]fieldscount:5value[ChashIserialVersionUIDJserialPersistentFields[Ljava/io/ObjectStreamField;CASE_INSENSITIVE_ORDERLjava/util/Comparator;methodscount:94<init>()V<init>(Ljava/lang/String;)V<init>([C)V<init>([CII)V<init>([III)V<init>([BIII)V<init>([BI)VcheckBounds([BII)V<init>([BIILjava/lang/String;)V<init>([BIILjava/nio/charset/Charset;)V<init>([BLjava/lang/String;)V<init>([BLjava/nio/charset/Charset;)V<init>([BII)V<init>([B)V<init>(Ljava/lang/StringBuffer;)V<init>(Ljava/lang/StringBuilder;)V<init>([CZ)Vlength()IisEmpty()ZcharAt(I)CcodePointAt(I)IcodePointBefore(I)IcodePointCount(II)IoffsetByCodePoints(II)IgetChars([CI)VgetChars(II[CI)VgetBytes(II[BI)VgetBytes(Ljava/lang/String;)[BgetBytes(Ljava/nio/charset/Charset;)[BgetBytes()[Bequals(Ljava/lang/Object;)ZcontentEquals(Ljava/lang/StringBuffer;)ZnonSyncContentEquals(Ljava/lang/AbstractStringBuilder;)ZcontentEquals(Ljava/lang/CharSequence;)ZequalsIgnoreCase(Ljava/lang/String;)ZcompareTo(Ljava/lang/String;)IcompareToIgnoreCase(Ljava/lang/String;)IregionMatches(ILjava/lang/String;II)ZregionMatches(ZILjava/lang/String;II)ZstartsWith(Ljava/lang/String;I)ZstartsWith(Ljava/lang/String;)ZendsWith(Ljava/lang/String;)ZhashCode()IindexOf(I)IindexOf(II)IindexOfSupplementary(II)IlastIndexOf(I)IlastIndexOf(II)IlastIndexOfSupplementary(II)IindexOf(Ljava/lang/String;)IindexOf(Ljava/lang/String;I)IindexOf([CIILjava/lang/String;I)IindexOf([CII[CIII)IlastIndexOf(Ljava/lang/String;)IlastIndexOf(Ljava/lang/String;I)IlastIndexOf([CIILjava/lang/String;I)IlastIndexOf([CII[CIII)Isubstring(I)Ljava/lang/String;substring(II)Ljava/lang/String;subSequence(II)Ljava/lang/CharSequence;concat(Ljava/lang/String;)Ljava/lang/String;replace(CC)Ljava/lang/String;matches(Ljava/lang/String;)Zcontains(Ljava/lang/CharSequence;)ZreplaceFirst(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;replaceAll(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;split(Ljava/lang/String;I)[Ljava/lang/String;split(Ljava/lang/String;)[Ljava/lang/String;join(Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String;join(Ljava/lang/CharSequence;Ljava/lang/Iterable;)Ljava/lang/String;toLowerCase(Ljava/util/Locale;)Ljava/lang/String;toLowerCase()Ljava/lang/String;toUpperCase(Ljava/util/Locale;)Ljava/lang/String;toUpperCase()Ljava/lang/String;trim()Ljava/lang/String;toString()Ljava/lang/String;toCharArray()[Cformat(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;format(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;valueOf(Ljava/lang/Object;)Ljava/lang/String;valueOf([C)Ljava/lang/String;valueOf([CII)Ljava/lang/String;copyValueOf([CII)Ljava/lang/String;copyValueOf([C)Ljava/lang/String;valueOf(Z)Ljava/lang/String;valueOf(C)Ljava/lang/String;valueOf(I)Ljava/lang/String;valueOf(J)Ljava/lang/String;valueOf(F)Ljava/lang/String;valueOf(D)Ljava/lang/String;intern()Ljava/lang/String;compareTo(Ljava/lang/Object;)I<clinit>()VProcessfinishedwithexitcode0 如果大家对这部分验证、准备、解析,的实现过程感兴趣,可以参照这部分用Java实现的JVM源码:https://github.com/fuzhengwei/itstack-demo-jvm 六、总结 学习 JVM 最大的问题是不好实践,所以本文以案例实操的方式,学习 JVM 的加载解析过程。也让更多的对 JVM 感兴趣的研发,能更好的接触到 JVM 并深入的学习。 有了以上这段代码,大家可以参照 JVM 虚拟机规范,在调试Java版本的JVM,这样就可以非常容易理解整个JVM的加载过程,都做了什么。 如果大家需要文章中一些原图 xmind 或者源码,可以添加作者小傅哥(fustack),或者关注公众号:bugstack虫洞栈进行获取。好了,本章节就扯到这,后续还有很多努力,持续原创,感谢大家的支持! 七、系列推荐 ReentrantLock之公平锁讲解和实现 除了JDK、CGLIB,还有3种类代理方式?面试又卡住! 面试官,ThreadLocal 你要这么问,我就挂了! 手写线程池,对照学习ThreadPoolExecutor线程池实现原理! 一次代码评审,差点过不了试用期! bugstack虫洞栈 沉淀、分享、成长,让自己和他人都能有所收获! 作者小傅哥多年从事一线互联网Java开发,从19年开始编写工作和学习历程的技术汇总,旨在为大家提供一个较清晰详细的核心技能学习文档。如果本文能为您提供帮助,请给予支持(关注、点赞、分享)! 感谢支持小傅哥原创,欢迎点击在看和转发 本文分享自微信公众号 - bugstack虫洞栈(bugstack)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册