当前位置: 首页 > article >正文

Java——继承实现的基本原理

继承实现的基本原理1、示例2、类加载过程3、对象创建的过程4、方法调用的过程5、变量访问的过程6、继承是把双刃剑6.1、继承破坏封装6.2、封装是如何被破坏的6.3、继承没有反映is-a关系6.4、如何应对继承的双面性1、示例Base类publicclassBase{publicstaticints;privateinta;static{System.out.println(基类静态代码块ss);s1;}{System.out.println(基类实例代码块aa);a1;}publicBase(){System.out.println(基类构造方法aa);a2;}protectedvoidstep(){System.out.println(base s: s, a: a);}publicvoidaction(){System.out.println(start);step();System.out.println(end);}}Base包括一个静态变量s一个实例变量a一段静态初始化代码块一段实例初始化代码块一个构造方法两个方法step和action。子类Child如代码清单所示。publicclassChildextendsBase{publicstaticints;privateinta;static{System.out.println(子类静态代码块ss);s10;}{System.out.println(子类实例代码块aa);a10;}publicChild(){System.out.println(子类构造方法aa);a20;}protectedvoidstep(){System.out.println(child s: s, a: a);}}Child继承了Base也定义了和基类同名的静态变量s和实例变量a静态初始化代码块实例初始化代码块构造方法重写了方法step。使用的例子如代码清单所示。publicstaticvoidmain(String[]args){System.out.println(---- new Child());ChildcnewChild();System.out.println(---- c.action());c.action();Basebc;System.out.println(---- b.action());b.action();System.out.println(---- b.s: b.s);System.out.println(---- c.s: c.s);}上面的代码创建了Child类型的对象赋值给了Child类型的引用变量c通过c调用action方法又赋值给了Base类型的引用变量b通过b也调用了action最后通过b和c访问静态变量s并输出。这是屏幕的输出结果----newChild()基类静态代码块 s:0子类静态代码块 s:0基类实例代码块 a:0基类构造方法 a:1子类实例代码块 a:0子类构造方法 a:10----c.action()start child s:10,a:20end----b.action()start child s:10,a:20end----b.s:1----c.s:102、类加载过程在Java中所谓类的加载是指将类的相关信息加载到内存。在Java中类是动态加载的当第一次使用这个类的时候才会加载加载一个类时会查看其父类是否已加载如果没有则会加载其父类。一个类的信息主要包括以下部分类变量静态变量​类初始化代码类方法静态方法​实例变量实例初始化代码实例方法父类信息引用。类初始化代码包括定义静态变量时的赋值语句静态初始化代码块。实例初始化代码包括定义实例变量时的赋值语句实例初始化代码块构造方法。类加载过程包括分配内存保存类的信息给类变量赋默认值加载父类设置父子关系执行类初始化代码。注意类初始化代码是先执行父类的再执行子类的。不过父类执行时子类静态变量的值也是有的是默认值。对于默认值我们之前说过数字型变量都是0,boolean是false, char是’\u0000’引用型变量是null。之前我们说过内存分为栈和堆栈存放函数的局部变量而堆存放动态分配的对象还有一个内存区存放类的信息这个区在Java中称为方法区。加载后Java方法区就有了一份这个类的信息。以我们的例子来说有3份类信息分别是Child、Base、Object内存布局如图所示。我们用class_init()来表示类初始化代码用instance_init()表示实例初始化代码实例初始化代码包括了实例初始化代码块和构造方法。例子中只有一个构造方法实际情况则可能有多个实例初始化方法。本例中类的加载大致就是在内存中形成了类似上面的布局然后分别执行了Base和Child的类初始化代码。接下来我们看对象创建的过程。3、对象创建的过程在类加载之后new Child()就是创建Child对象创建对象过程包括分配内存对所有实例变量赋默认值执行实例初始化代码。分配的内存包括本类和所有父类的实例变量但不包括任何静态变量。实例初始化代码的执行从父类开始再执行子类的。但在任何类执行初始化代码之前所有实例变量都已设置完默认值。每个对象除了保存类的实例变量之外还保存着实际类信息的引用。Child c new Child()会将新创建的Child对象引用赋给变量c而Base b c会让b也引用这个Child对象。创建和赋值后内存布局如图所示。引用型变量c和b分配在栈中它们指向相同的堆中的Child对象。Child对象存储着方法区中Child类型的地址还有Base中的实例变量a和Child中的实例变量a。创建了对象接下来来看方法调用的过程。4、方法调用的过程我们先来看c.action(); 这句代码的执行过程查看c的对象类型找到Child类型在Child类型中找action方法发现没有到父类中寻找在父类Base中找到了方法action开始执行action方法action先输出了start然后发现需要调用step()方法就从Child类型开始寻找step()方法在Child类型中找到了step()方法执行Child中的step()方法执行完后返回action方法继续执行action方法输出end。寻找要执行的实例方法的时候是从对象的实际类型信息开始查找的找不到的时候再查找父类类型信息。我们来看b.action()这句代码的输出和c.action()是一样的这称为动态绑定而动态绑定实现的机制就是根据对象的实际类型查找要执行的方法子类型中找不到的时候再查找父类。这里因为b和c指向相同的对象所以执行结果是一样的。如果继承的层次比较深要调用的方法位于比较上层的父类则调用的效率是比较低的因为每次调用都要进行很多次查找。大多数系统使用一种称为虚方法表的方法来优化调用的效率。所谓虚方法表就是在类加载的时候为每个类创建一个表记录该类的对象所有动态绑定的方法包括父类的方法及其地址但一个方法只有一条记录子类重写了父类方法后只会保留子类的。对于本例来说Child和Base的虚方法表如图所示。对Child类型来说action方法指向Base中的代码toString方法指向Object中的代码而step()指向本类中的代码。当通过对象动态绑定方法的时候只需要查找这个表就可以了而不需要挨个查找每个父类。接下来我们介绍变量访问的过程。5、变量访问的过程对变量的访问是静态绑定的无论是类变量还是实例变量。代码中演示的是类变量b.s和c.s通过对象访问类变量系统会转换为直接访问类变量Base.s和Child.s。例子中的实例变量都是private的不能直接访问如果是public的则b.a访问的是对象中Base类定义的实例变量a而c.a访问的是对象中Child类定义的实例变量a。6、继承是把双刃剑继承其实是把双刃剑一方面继承是非常强大的另一方面继承的破坏力也是很强的。继承广泛应用于各种Java API、框架和类库之中一方面它们内部大量使用继承另一方面它们设计了良好的框架结构提供了大量基类和基础公共代码。使用者可以使用继承重写适当方法进行定制就可以简单方便地实现强大的功能。但继承为什么会有破坏力呢主要是因为继承可能破坏封装而封装可以说是程序设计的第一原则另外继承可能没有反映出is-a关系。下面我们详细来说明。6.1、继承破坏封装什么是封装呢封装就是隐藏实现细节提供简化接口。使用者只需要关注怎么用而不需要关注内部是怎么实现的。实现细节可以随时修改而不影响使用者。函数是封装类也是封装。通过封装才能在更高的层次上考虑和解决问题。可以说封装是程序设计的第一原则没有封装代码之间会到处存在着实现细节的依赖则构建和维护复杂的程序是难以想象的。继承可能破坏封装是因为子类和父类之间可能存在着实现细节的依赖。子类在继承父类的时候往往不得不关注父类的实现细节而父类在修改其内部实现的时候如果不考虑子类也往往会影响到子类。我们通过一些例子来说明。这些例子主要用于演示可以基本忽略其实际意义。6.2、封装是如何被破坏的我们来看一个简单的例子基类Base如代码清单所示。publicclassBase{privatestaticfinalintMAX_NUM1000;privateint[]arrnewint[MAX_NUM];privateintcount;publicvoidadd(intnumber){if(countMAX_NUM){arr[count]number;}}publicvoidaddAll(int[]numbers){for(intnum:numbers){add(num);}}}Base提供了两个方法add和addAll将输入数字添加到内部数组中。对使用者来说 add和addAll就是能够添加数字具体是怎么添加的不用关心。子类代码Child如代码清单所示。publicclassChildextendsBase{privatelongsum;Overridepublicvoidadd(intnumber){super.add(number);sumnumber;}OverridepublicvoidaddAll(int[]numbers){super.addAll(numbers);for(inti0;inumbers.length;i){sumnumbers[i];}}publiclonggetSum(){returnsum;}}子类重写了基类的add和addAll方法在添加数字的同时汇总数字存储数字的和到实例变量sum中并提供了方法getSum获取sum的值。使用Child的代码如下所示publicstaticvoidmain(String[]args){ChildcnewChild();c.addAll(newint[]{1,2,3});System.out.println(c.getSum());//12}使用addAll添加1、2、3期望的输出是1236实际输出为12为什么是12呢查看代码不难看出同一个数字被汇总了两次。子类的addAll方法首先调用了父类的add-All方法而父类的addAll方法通过add方法添加由于动态绑定子类的add方法会执行子类的add也会做汇总操作。可以看出如果子类不知道基类方法的实现细节它就不能正确地进行扩展。知道了错误现在我们修改子类实现修改addAll方法为OverridepublicvoidaddAll(int[]numbers){super.addAll(numbers);}也就是说addAll方法不再进行重复汇总。这次程序就可以输出正确结果6了。但是基类Base决定修改addAll方法的实现改为下面代码publicvoidaddAll(int[]numbers){for(intnum:numbers){if(countMAX_NUM){arr[count]num;}}}也就是说它不再通过调用add方法添加这是Base类的实现细节。但是修改了基类的内部细节后上面使用子类的程序却错了输出由正确值6变为了0。从这个例子可以看出子类和父类之间是细节依赖子类扩展父类仅仅知道父类能做什么是不够的还需要知道父类是怎么做的而父类的实现细节也不能随意修改否则可能影响子类。更具体地说子类需要知道父类的可重写方法之间的依赖关系具体到上例中就是add和addAll方法之间的关系而且这个依赖关系父类不能随意改变。但即使这个依赖关系不变封装还是可能被破坏。还是上面的例子我们先将addAll方法改回去这次我们在基类Base中添加一个方法clear这个方法的作用是将所有添加的数字清空代码如下publicvoidclear(){for(inti0;icount;i){arr[i]0;}count0;}基类添加一个方法不需要告诉子类Child类不知道Base类添加了这么一个方法但因为继承关系Child类却自动拥有了这么一个方法。因此Child类的使用者可能会这么使用Child类publicstaticvoidmain(String[]args){ChildcnewChild();c.addAll(newint[]{1,2,3});c.clear();c.addAll(newint[]{1,2,3});System.out.println(c.getSum());//12}先添加一次之后调用clear清空又添加一次最后输出sum期望结果是6但实际输出是12。因为Child没有重写clear方法它需要增加如下代码重置其内部的sum值Overridepublicvoidclear(){super.clear();this.sum0;}可以看出父类不能随意增加公开方法因为给父类增加就是给所有子类增加而子类可能必须要重写该方法才能确保方法的正确性。总结一下对于子类而言通过继承实现是没有安全保障的因为父类修改内部实现细节它的功能就可能会被破坏而对于基类而言让子类继承和重写方法就可能丧失随意修改内部实现的自由。6.3、继承没有反映is-a关系继承关系是设计用来反映is-a关系的子类是父类的一种子类对象也属于父类父类的属性和行为也适用于子类。就像橙子是水果一样水果有的属性和行为橙子也必然都有。但现实中设计完全符合is-a关系的继承关系是困难的。比如绝大部分鸟都会飞可能就想给鸟类增加一个方法fly()表示飞但有一些鸟就不会飞比如企鹅。在is-a关系中重写方法时子类不应该改变父类预期的行为但是这是没有办法约束的。还是以鸟为例你可能给父类增加了fly()方法对企鹅你可能想企鹅不会飞但可以走和游泳就在企鹅的fly()方法中实现了有关走或游泳的逻辑。继承是应该被当作is-a关系使用的但是Java并没有办法约束父类有的属性和行为子类并不一定都适用子类还可以重写方法实现与父类预期完全不一样的行为。但对于通过父类引用操作子类对象的程序而言它是把对象当作父类对象来看待的期望对象符合父类中声明的属性和行为。如果不符合结果是什么呢混乱。6.4、如何应对继承的双面性继承既强大又有破坏性那怎么办呢避免使用继承正确使用继承。我们先来看怎么避免继承有三种方法使用final关键字优先使用组合而非继承使用接口。1. 使用final避免继承给方法加final修饰符父类就保留了随意修改这个方法内部实现的自由使用这个方法的程序也可以确保其行为是符合父类声明的。给类加final修饰符父类就保留了随意修改这个类实现的自由使用者也可以放心地使用它而不用担心一个父类引用的变量实际指向的却是一个完全不符合预期行为的子类对象。2. 优先使用组合而非继承使用组合可以抵挡父类变化对子类的影响从而保护子类应该优先使用组合。还是上面的例子我们使用组合来重写一下子类如代码清单所示。publicclassChild{privateBasebase;privatelongsum;publicChild(){basenewBase();}publicvoidadd(intnumber){base.add(number);sumnumber;}publicvoidaddAll(int[]numbers){base.addAll(numbers);for(inti0;inumbers.length;i){sumnumbers[i];}}publiclonggetSum(){returnsum;}}这样子类就不需要关注基类是如何实现的了基类修改实现细节增加公开方法也不会影响到子类了。但组合的问题是子类对象不能当作基类对象来统一处理了。解决方法是使用接口。3. 正确使用继承如果要使用继承怎么正确使用呢使用继承大概主要有三种场景基类是别人写的我们写子类我们写基类别人可能写子类基类、子类都是我们写的。第1种场景中基类主要是Java API、其他框架或类库中的类在这种情况下我们主要通过扩展基类实现自定义行为这种情况下需要注意的是重写方法不要改变预期的行为阅读文档说明理解可重写方法的实现机制尤其是方法之间的依赖关系在基类修改的情况下阅读其修改说明相应修改子类。第2种场景中我们写基类给别人用在这种情况下需要注意的是使用继承反映真正的is-a关系只将真正公共的部分放到基类对不希望被重写的公开方法添加final修饰符写文档说明可重写方法的实现机制为子类提供指导告诉子类应该如何重写在基类修改可能影响子类时写修改说明。第3种场景我们既写基类也写子类关于基类注意事项和第2种场景类似关于子类注意事项和第1种场景类似不过程序都由我们控制要求可以适当放松一些。

相关文章:

Java——继承实现的基本原理

继承实现的基本原理1、示例2、类加载过程3、对象创建的过程4、方法调用的过程5、变量访问的过程6、继承是把双刃剑6.1、继承破坏封装6.2、封装是如何被破坏的6.3、继承没有反映is-a关系6.4、如何应对继承的双面性1、示例 Base类: public class Base {public stati…...

不止于VWF:用Modelsim SE-64 10.4 为你的Quartus 18.1 Verilog项目做高效前仿真

超越基础工具链:Modelsim SE-64与Quartus 18.1深度协同仿真指南 当Verilog代码通过Quartus编译后,许多开发者会止步于基础功能验证。但真正的设计可靠性往往隐藏在时序边界条件和复杂状态机交互中——这正是专业仿真工具的价值所在。本文将带您突破VWF的…...

书匠策AI写毕业论文到底靠不靠谱?我把它的功能拆成“论文通关游戏“讲给你听

各位同学好,我是专门教人写论文的教育博主。 最近后台私信里出现频率最高的一句话是:"老师,书匠策AI到底怎么用?"今天我换个讲法——不列功能清单,而是把写毕业论文想象成一款五关游戏,每一关告…...

别再手动K帧了!Maya路径动画保姆级教程,5分钟让模型丝滑走位

Maya路径动画终极指南:告别手动K帧,实现模型智能运动 在三维动画制作中,让角色或物体沿着预设路径运动是一个常见需求。无论是制作蛇形移动的生物、赛车漂移轨迹,还是摄像机漫游动画,路径动画都能大幅提升工作效率。传…...

论文小白别哭了!书匠策AI把毕业论文变成了“填空题“,官网www.shujiangce.com亲测能用

先聊个扎心的事实 你有没有发现,身边总有那么一两个同学,论文写得又快又好,答辩的时候导师还夸?你以为人家天赋异禀?不,人家只是比你早发现了一个叫书匠策AI的工具。 今天这篇文章,我不讲什么…...

香港電動車普及化路線圖(繁) 2026

香港环境及生态局 2026 年 2 月发布《香港电动车普及化路线图(更新版)》,坚定维持2035 年或之前停止新登记燃油及混合动力私家车、2050 年前实现车辆零排放的核心目标,不受海外部分地区放缓电动化政策的影响,持续朝着零…...

S32K144 Lin组件实战:告别官方LinStack,手把手教你用底层驱动搞定超声波雷达

S32K144 Lin组件实战:从高级封装到底层驱动的技术跃迁 在嵌入式开发领域,协议栈选择往往决定了项目的灵活性与开发效率。当我们使用NXP S32K144微控制器进行LIN总线通信时,官方提供的LinStack组件确实能快速搭建基础通信框架。但真正投入工业…...

Wireshark抓包实战:手把手教你解析IEC61850 GOOSE报文(附ASN.1解码技巧)

Wireshark实战解析IEC61850 GOOSE报文:从抓包到ASN.1解码全流程 在智能变电站自动化系统中,GOOSE(Generic Object Oriented Substation Event)报文作为IEC61850标准的核心通信机制,承担着保护跳闸、断路器位置信号等关…...

终极GTA5线上小助手:完全免费的游戏体验增强工具完整指南

终极GTA5线上小助手:完全免费的游戏体验增强工具完整指南 【免费下载链接】GTA5OnlineTools GTA5线上小助手 项目地址: https://gitcode.com/gh_mirrors/gt/GTA5OnlineTools GTA5线上小助手是一款专为《侠盗猎车手5》线上模式设计的综合性游戏体验增强工具&a…...

BLheli电调硬件避坑指南:搞懂MOS驱动逻辑,别让固件和电路“打架”

BLheli电调硬件设计深度解析:从MOS驱动逻辑到实战排错 在无刷电机控制领域,BLheli固件因其出色的性能和开源特性成为众多开发者的首选。但当你兴奋地将精心设计的硬件与下载的固件结合时,电机却纹丝不动,甚至冒出缕缕青烟——这种…...

10分钟为Royal TSX打造完美中文界面:从英文迷茫到母语掌控

10分钟为Royal TSX打造完美中文界面:从英文迷茫到母语掌控 【免费下载链接】Royal_TSX_Chinese_Language_Pack Royal_TSX的简体中文汉化包 项目地址: https://gitcode.com/gh_mirrors/ro/Royal_TSX_Chinese_Language_Pack 当您第一次打开Royal TSX这款强大的…...

在自动化视频剪辑脚本中调用AI进行智能片段筛选与拼接

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 在自动化视频剪辑脚本中调用AI进行智能片段筛选与拼接 自动化视频生产正成为内容创作者和运营团队提升效率的关键路径。面对海量的…...

三步构建专业级抖音内容管理系统:douyin-downloader架构解析与实践指南

三步构建专业级抖音内容管理系统:douyin-downloader架构解析与实践指南 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browse…...

MouseClick终极指南:简单免费的鼠标自动化工具完全教程

MouseClick终极指南:简单免费的鼠标自动化工具完全教程 【免费下载链接】MouseClick 🖱️ MouseClick 🖱️ 是一款功能强大的鼠标连点器和管理工具,采用 QT Widget 开发 ,具备跨平台兼容性 。软件界面美观 &#xff0c…...

Taotoken多模型聚合平台为开发者提供稳定高效的大模型调用服务

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Taotoken多模型聚合平台为开发者提供稳定高效的大模型调用服务 对于需要集成多种大模型能力的AI应用开发者而言,管理不…...

终极Word转LaTeX神器:5分钟搞定专业文档格式转换

终极Word转LaTeX神器:5分钟搞定专业文档格式转换 【免费下载链接】docx2tex Converts Microsoft Word docx to LaTeX 项目地址: https://gitcode.com/gh_mirrors/do/docx2tex 还在为Word文档转换为LaTeX格式而烦恼吗?每次手动调整公式、表格和图片…...

在Python项目中下载OpenAI兼容SDK并接入Taotoken聚合API

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 在Python项目中下载OpenAI兼容SDK并接入Taotoken聚合API 对于使用Python的开发者而言,通过OpenAI官方风格的SDK接入大模…...

从开发者视角感受 Taotoken 文档与示例代码的易用性

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 从开发者视角感受 Taotoken 文档与示例代码的易用性 作为一名经常需要对接不同大模型服务的开发者,我习惯于在评估一个…...

LRCGET:一键为本地音乐库下载同步歌词的终极方案

LRCGET:一键为本地音乐库下载同步歌词的终极方案 【免费下载链接】lrcget Utility for mass-downloading LRC synced lyrics for your offline music library. 项目地址: https://gitcode.com/gh_mirrors/lr/lrcget 你是否厌倦了每次听歌都要手动搜索歌词&am…...

终极指南:3步解决Mac NTFS读写难题,Nigate免费工具完整教程

终极指南:3步解决Mac NTFS读写难题,Nigate免费工具完整教程 【免费下载链接】Free-NTFS-for-Mac Nigate: An open-source NTFS utility for Mac. It supports all Mac models (Intel and Apple Silicon), providing full read-write access, mounting, a…...

对比直接使用厂商API,通过Taotoken调用大模型的延迟体感差异

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 对比直接使用厂商API,通过Taotoken调用大模型的延迟体感差异 1. 关于延迟体感的说明 在接入大模型服务时,…...

终极指南:3步解锁碧蓝航线全皮肤功能的Perseus补丁配置

终极指南:3步解锁碧蓝航线全皮肤功能的Perseus补丁配置 【免费下载链接】Perseus Azur Lane scripts patcher. 项目地址: https://gitcode.com/gh_mirrors/pers/Perseus 还在为碧蓝航线中那些精美的限定皮肤无法使用而烦恼吗?Perseus原生库补丁为…...

ImageGlass架构深度剖析:Windows平台高性能图像浏览引擎的技术实现与优化

ImageGlass架构深度剖析:Windows平台高性能图像浏览引擎的技术实现与优化 【免费下载链接】ImageGlass 🏞 A lightweight, versatile image viewer 项目地址: https://gitcode.com/gh_mirrors/im/ImageGlass ImageGlass作为Windows平台上一款轻量…...

TrollInstallerX终极指南:iOS 14-16.6.1系统一键安装TrollStore的完整教程

TrollInstallerX终极指南:iOS 14-16.6.1系统一键安装TrollStore的完整教程 【免费下载链接】TrollInstallerX A TrollStore installer for iOS 14.0 - 16.6.1 项目地址: https://gitcode.com/gh_mirrors/tr/TrollInstallerX TrollInstallerX是一款专为iOS 14…...

PlayCover完整指南:在Apple Silicon Mac上运行iOS应用与游戏的终极解决方案

PlayCover完整指南:在Apple Silicon Mac上运行iOS应用与游戏的终极解决方案 【免费下载链接】PlayCover Community fork of PlayCover 项目地址: https://gitcode.com/gh_mirrors/pl/PlayCover PlayCover是一个革命性的开源工具,专门为Apple Sili…...

第四部分-Docker网络与存储——20. 数据持久化

20. 数据持久化 1. 数据持久化概述 容器默认情况下数据是临时的,当容器删除时数据也会丢失。数据持久化是生产环境中必须解决的问题,Docker 提供了多种数据持久化方案。 ┌──────────────────────────────────────…...

终极指南:3分钟快速找回Navicat数据库连接密码的免费工具

终极指南:3分钟快速找回Navicat数据库连接密码的免费工具 【免费下载链接】navicat_password_decrypt 忘记navicat密码时,此工具可以帮您查看密码 项目地址: https://gitcode.com/gh_mirrors/na/navicat_password_decrypt 你是否曾经因为忘记Navicat保存的数…...

第四部分-Docker网络与存储——19. 容器间通信

19. 容器间通信 1. 容器间通信概述 容器间通信是 Docker 编排的核心,理解容器如何相互通信对于构建微服务架构至关重要。Docker 提供了多种容器间通信方式,每种方式适用于不同场景。 ┌────────────────────────────────…...

从Word到LaTeX的完美转换:3种方案对比与docx2tex终极指南

从Word到LaTeX的完美转换:3种方案对比与docx2tex终极指南 【免费下载链接】docx2tex Converts Microsoft Word docx to LaTeX 项目地址: https://gitcode.com/gh_mirrors/do/docx2tex 深夜三点,李博士盯着电脑屏幕,手指在键盘上机械地…...

BUUCTF:[极客大挑战 2019]RCE ME 深度解析:从正则绕开到LD_PRELOAD的完整利用链

1. 题目背景与初步分析 BUUCTF的[极客大挑战 2019]RCE ME是一道典型的PHP代码审计与绕过题目。题目给出了一个简单的PHP页面&#xff0c;核心代码如下&#xff1a; <?php error_reporting(0); if(isset($_GET[code])){$code$_GET[code];if(strlen($code)>40){die(&quo…...