-
sourceMap的作用 通常,js代码出错,控制台会提示第几行第几列代码出错。但是webpack打包压缩后的代码,都被压缩到了一行,变量也变成了a,b,c,d。代码出错,控制台就没法正确的提示错误位置。 sourceMap就可以解决这个问题。sourceMap就是一个信息文件,里面储存着打包前的位置信息。也就是说,转换后的代码的每一个位置,所对应的转换前的位置。具体如何编码,可以看下阮一峰的文章。 有了它,出错的时候,浏览器控制台将直接显示原始代码出错的位置,而不是转换后的代码,点击出错信息将直接跳转到原始代码位置。 sourceMap的调试 Console直接点击错误提示跳转到源码(如上图所示) 一般浏览器会默认开启sourceMap调试,如果控制台不能显示源码错误位置,则需要打开配置。 打开浏览器的这个配置后,控制台的Console面板的错误提示,直接点击就会跳转到对应的源文件出错位置。 Sources面板找到源文件,进行断点调试 如果没Console没报错,但是页面显示不正确。可以点击控制台的Sources面板,源文件都在 webpack:// 目录下,或者直接搜索文件,打开源文件后进行断点调试。 按需加载的路由,只有页面加载了,源文件才会在这个目录下显示。 webpack配置 vue-cli3的vue.config.js文件: module.exports = { productionSourceMap: false, //默认是true } 打包后的文件: 对于打包后的sourceMap,webpack提供多种类型的配置。 其中,开发环境使用: eval:会把每个模块封装到 eval 里包裹起来执行,并且会在末尾追加map文件的地址。map文件映射到转换后的代码(可执行的js文件),而不是映射到原始代码(vue文件),所以不能正确的显示错误行数。 eval-source-map: 和 eval 类似,为每个模块生成原始的sourceMap,map文件会以dataURL的形式添加到js中(类似于图片的base64形式)。原始的sourceMap可以正确提示错误行数。 eval-cheap-source-map: 跟eval-source-map相同,唯一不同的就是增加了cheap,cheap是指忽略了列信息(绝大部分时候列信息对于错误提示没啥用,只需要提示行数就行)。 cheap-module-eval-source-map: 与eval-cheap-source-map相同,但是包含了不同loader模块之间的sourceMap。例如借助babel编译ES6,如果生成不包含loader的sourcemap,此时debug到的将是编译后的代码,而非原始代码。 生产环境使用: source-map: map文件包含完整的原始代码,但是打包会很慢。打包后的js最后一行是map文件地址的注释 hidden-source-map:与 sourceMap 相同,也生成map文件,但是打包后的js最后没有map文件地址的引用。浏览器不会主动去请求map文件,一般用于网站错误分析,需要让错误分析工具按名称匹配到map文件。 nosources-source-map:生成的map文件不包含源码,但是会正确提示错误的行数。另外项目的目录结构和文件名称会暴露在Sources面板 特定环境用(针对一些第三方工具,用的很少,暂不详细讨论): inline-source-map inline-cheap-source-map inline-cheap-module-source-map cheap-source-map cheap-module-source-map 其实就是inline,cheap,module,source-map的自由组合。 inline 是以dateURL的形式添加map,不额外生成map文件 cheap 是没有列信息 module 是包含了loader的sourcemap source-map 则是映射到源文件 不同的参数,生成的sourceMap不同,打包速度、体积、错误提示的效果也不同。而vue-cli3帮我们预选了一种模式: 开发环境,配置文件位置:node_modules\\@vue\cli-service\lib\config\dev.js,第5行 生产环境,配置文件位置: \node_modules\\@vue\cli-service\lib\config\prod.js,第11行 另外:css同样有sourceMap,不过vue-cli3是默认关闭的: module.exports = { css: { sourceMap: false, //默认false } } sourceMap的安全问题 事情都是具有两面性的,方便调试的同时,也将源码暴露在控制台,可能会导致代码泄露的安全问题。 虽然说前端代码是公开的,但是代码的压缩混淆也一定程度上提高了安全性。 代码泄露可能导致的问题: 代码被抄袭 场景:给A客户写了一个地图功能,A客户展示给B客户,B客户想要写地图,但是不会,查看源码后,抄走了地图功能 业务流失 场景:竞争对手拿到了源码,挑出其中的漏洞或者缺陷,大肆宣传。或者直接写一份后台,成本少,价格低导致业务被抢走 系统被攻击 场景:由于项目急,鉴权是通过前端动态生成路由控制的,后台没做判断,导致修改源码后,可以获得更多的权限(数据被删除) 例如常见的视频播放网站,如果不加密,直接一个标签,那么就很容易被人拿到视频资源,自己的会员业务也会损失。 sourceMap的处理 如果无特殊需求,生产环境是需要关闭这个选项的,vue-cli3直接配置productionSourceMap: false即可。或者不关闭但是在测试环境迁移到正式环境时删掉map文件。也可以通过服务器配置,特殊账号(调试专用)能访问到map文件,其他用户则不行。 如果需要控制台能正确提示报错的位置而不暴露源码,推荐用nosources-source-map模式,但是这个模式会暴露源码的目录结构与文件命名。一般测试环境用这个比较好,QA测试出来的问题能正确提示错误,即使运维忘了删除sourceMap文件,也不会暴露源码。 // vue.config.js module.exports = { configureWebpack: config => { if (process.env.NODE_ENV === 'production') {//只修改生产环境配置 return { devtool: 'nosources-source-map', }; } } } 补充:本地sourceMap调试 办法1 : 网络拦截 webpack打包仍然生成sourceMap,但是将map文件挑出放到本地服务器,将不含有map文件的部署到服务器,借助第三方软件(例如fiddler),将浏览器对map文件的请求拦截到本地服务器,就可以实现本地sourceMap调试。 办法2 : 修改webpack打包,测试环境不放map文件,修改sourceMap的引用到本地服务器 可行性:本地调试专用,由于测试环境和本机属于同一个局域网,所以测试环境可以请求到本地服务器上的sourceMap文件,部署之后,客户访问则不能访问到本机的局域网IP,也就无法拿到sourceMap文件。 缺点:其他人打包需要修改电脑IP和本地服务器文件地址,无法直接复用,优点是绝对安全。或者可以提供另一台专门放map文件的服务器,打包之后自动上传map文件到这个服务器。 借助SourceMapDevToolPlugin修改打包后的文件对map文件的引用地址,同时借助filemanager-webpack-plugin将map文件移到到本地服务器上。代码如下(vue.config.js): const webpack = require('webpack')const FileManagerPlugin = require('filemanager-webpack-plugin');module.exports = {configureWebpack: config => {if (process.env.NODE_ENV === 'production') {return {devtool: false, // 注意SourceMapDevToolPlugin和devtool不可共存plugins: [new webpack.SourceMapDevToolPlugin({append: '\n//# sourceMappingURL=http://192.168.23.230/sourceMap/', // 192.168.23.230是本机局域网IPfilename: '[file].map',}),new FileManagerPlugin({onEnd: {copy: [{source: './dist/js/*.map',destination: 'D:/xampp/htdocs/sourceMap', // D:/xampp/htdocs/是本地服务器的文件地址}],delete: ['./dist/js/*.map'],archive: [{ //顺便把网站文件压缩一下source: './dist',destination: './dist/dist.zip',}]}})]};}}}这个插件主要是实现了对sourceMap的更精细控制,比如打包之后的位置,以及修改js/css文件最后面对sourceMap的引用地址。 这里主要用到修改sourceMap引用地址的功能。 使用这个必须关闭devtool 这个插件的主要功能是打包之后文件的移动,删除,拷贝,压缩等功能,使用之前需要先安装: npm install filemanager-webpack-plugin --save-dev 注意: 这个插件有bug,移动操作不支持模糊匹配,只能改成复制+删除 ———————————————— 原文链接:https://blog.csdn.net/weixin_31047023/article/details/113687529
-
1. XSS是什么XSS就是跨站脚本攻击,全称是Cross-Site Scripting,简称不是CSS是因为和层叠样式表重名了所以改称为XSS,其中X是代表未知并且有扩展的含义。XSS攻击涉及到三个方面:攻击者、用户、web server。其中用户是通过浏览器来访问web server上的网页,XSS攻击就是攻击者通过各种办法,在用户访问的网页中插入自己的脚本,让其在用户访问网页时在其浏览器中进行执行。攻击者通过插入的脚本的执行,来获得用户的信息,比如cookie,发送到攻击者自己的网站(跨站了),所以这种攻击被称为跨站脚本攻击,即在用户的浏览器中执行攻击者自己定制的脚本。2.XSS的类型XSS可以分为反射型XSS、持久性XSS、DOM Based XSS。2.1 反射型XSS反射性XSS也称为非持久性XSS,它是用户点击攻击链接,服务器解析后响应,在返回的响应内容中出现攻击者的XSS代码,被浏览器去执行。XSS攻击脚本被web server反射回来给浏览器执行,所以称为反射型XSS。它没有保存在web server中,而是出现在URL地址中,一般是攻击者通过邮件,聊天软件等等方式发送攻击URL,然后用户点击来达到攻击的;2.2 持久型XSS和反射型不同的是这种XSS攻击是将恶意代码存储在web server中,这样每个访问特定网页的用户都会被攻击到,一般是通过网站的留言、评论、博客、日志等等所有能够向web server输入内容的地方,将攻击代码存储到web server上的;有时持久性XSS和反射型XSS是被同时使用的,比如先通过对一个攻击url进行编码来绕过xss filter,然后提交该web server并存储在web server中,最后在用户浏览特定页面如果点击该url,就会触发一个XSS攻击,同时当用户点击该url的时候,也可能会触发一个CSRF(Cross site request forgery)的攻击。2.3 DOM based XSS这种XSS攻击是基于DOM的XSS攻击,是只涉及到浏览器的XSS攻击。比如根据用户的输入来动态构造一个DOM节点,如果没有对用户的输入进行过滤,那么也就导致XSS攻击的产生,过滤可以考虑采用esapi4js进行过滤。3. XSS 攻击的防御XSS防御的总体思路是:对输入(和URL参数)进行过滤,对输出进行编码,也就是对提交的所有内容进行过滤,对url中的参数进行过滤,过滤掉会导致脚本执行的相关内容,对动态输出到页面的内容进行html编码,使脚本无法在浏览器中执行。采取这些方法虽然对输入过滤可以被绕过,但是也还是会拦截很大一部分的XSS攻击。对于输入的防御处理使用XSS filter之外,需要对于在客户端和服务器端进行各种入参验证,需要验证字符的合法性、长度、格式等等,即分为黑名单和白名单,黑名单表示验证中不能出现某些字符,白名单表示可以出现的字符;对于输出数据的防御,可以对存在潜在威胁的字符进行编码、转义。总的来说输入防御可以采用AntiSamy提供的XSS Filter来实现,对输出进行防御可以采用ESAPI提供的编码来实现。
-
1.如何生成随机数在Java编程过程中,我们常常会需要实现随机数,那么如果需要生成一个指定范围之内的随机数字要怎么来实现呢?答案是有两种实现的方法:一种是调用Math类的random() 方法,一种是使用Random随机类。2.Math 类Math 类是在java.lang包下的类,它是数学相关的工具类,里面提供了大量的静态方法,可以直接进行调用用来完成与数学运算相关的操作。如果要通过Math类来实现随机数的生成的,需要借助random()方法来的进行实现,random()方法没有参数,它会默认返回大于等于 0.0小于1.0 double类型随机数,即 0<=随机数<1.0。对 random() 方法返回的数字进行乘法就可以实现产生任意范围随机数的功能。比如要生产[0,100),即生成大于等于0,小于100的随机整数:int i = (int) (Math.random() * 100);2.Random类Random类是java.util包下的类,它提供了丰富的随机数生成方法(顾名思义嘛,名字就是随机数,当然专门进行随机数操作的),可以产生boolean、int、long、float、byte数组以及double类型的随机数,这是它与 random() 方法最大的不同之处,因为random()只能产生double类型的大于等于0小于1的随机数。Random类两个构造方法:一个是无参的Random():该构造方法使用一个和当前系统时间对应的数字作为种子数,然后使用这个种子数构造 Random 对象。一个是有参的Random(long seed),random里其实是一个数列,这个数列每一位的数字接近随机分布,seed是种子数,设置用于随机数生成器的初始化值。因为Random类提供的所有方法生成的随机数字都是均匀分布的,所以说区间内部的数字生成的概率都是均等的。下面是Random类生成int、double、long、boolean、float类型的随机数常用方法:Random r = new Random(); double d1 = r.nextDouble(); // 生成[0,1.0]区间的小数 double d2 = r.nextDouble() * 10; // 生成[0,10.0]区间的小数 int i1 = r.nextInt(10); // 生成[0,10]区间的整数 int i2 = r.nextInt(10) - 3; // 生成[-1,9)区间的整数 long l1 = r.nextLong(); // 生成一个随机长整型值 boolean b1 = r.nextBoolean(); // 生成一个随机布尔型值 float f1 = r.nextFloat(); // 生成一个随机浮点型值以上就是随机数的简单应用和实现方法的小知识的汇总。
-
1.#{}的用法在Java编程过程中,我们肯定是少不了和SQL打交道的,无论的是查询的SQL还是新增、修改、删除的SQL我们都会传对应数据的不断变化的参数值,我们不能每次传值就去修改SQL,需要一个占位符来接收这个可变的参数值,这样在Mapper.xml映射文件中,我们经常会使用#{属性名} 来作为SQL语句的占位符,来映射Sql需要的实际参数值。1.1 一个参数的占位符使用如果只有一个参数我们只需要在对应的字段后面跟上#{属性值}就可以接收传递过来的参数值,也就是说#{}起到的作用就是一个预编译的占位符的作用,运行代码的时候会编译成 ?,但这只适用于只有一个参数的情况,而且这种一个参数的情况#{id}中的id可以写成任何字符串,比如#{aaa} select * from test where id=#{id}1.2 多个参数的占位符使用1.2.1 按照参数的顺序,通过使用自然序号对应所传递的参数来对应标注出传递的参数位置,这种方法需要对代码和业务逻辑比较熟悉,因为是使用自然序号来进行标注参数的位置的,所以稍微错位就会造成整个语句的混乱,所以不太推荐这种形式。 select * from test where id=#{0} and name=#{1}1.2.2 跟第一种比较类似,只是改为按照顺序通过param1、param2来进行标注参数的位置,这种形式跟第一种比较相似,如果标注的稍微有错位的情况也会造成整个语句的混乱,所以也不是很推荐这种形式的编码。 select * from test where id=#{param1} and name=#{param2}1.2.3 利用接口Mapper中对应的参数,必须保证xml和接口Mper中的参数名称保持一致,这样对应起来代码的可读性就增加了很多,而且也不容易出现错位造成整个语句混乱这种错位,因为是接口Mapper中的参数每个都必须一一对应到sql中才能使用,所以准确率肯定会提高很多,因此推荐这种使用占位符#{}的方法来进行使用。public interface TestMapper { public Test getUserByNameAndAge(@Param("id") Integer id, @Param("name") String name);} select * from test where id=#{id} and name=#{name}2.${}的用法${}的用法和#{}的用法都是占位符的作用,但是它们的不同在于#{}会被编译成?,而${}则会被原样输出,并且用在参数上的时候,需要使用param注解。3.#{}和${}的区别#{}会对SQL进行预编译会将占位符的位置编译成?,${}是做sql拼接会原样输出,会有SQL注入的隐患。#{}不需要关注数据类型,MyBatis会自动实现数据类型的转换; ${} 必须自己判断数据类型(需要再接口Mapper中使用@param注解进行标注),两者相同之处都是占位符的作用,都是支持@param注解。因为${}有SQL注入隐患,所以我们一般在Sql中使用#{}来进行参数的占位。
-
1.为什么要使用Java链式编程为什么要使用Java链式编程呢?我们在Java语言编程的过程中,肯定少不了创建对象的,但是创建对象后,我们设置属性需要不停的使用对象.setXXX( )方法,然后再换行继续使用对象.setXXX( )方法,这样创建出来的对象需要被我们重复的写很多遍,效率低而且代码可读性也很差,怎么能让我们代码能不用重复书写对象而更优雅的编写出来呢,链式编程就能帮我们实现这个效果,我们可以在创建对象后直接在后面.setXXX().setXXX()...这样链式的设置完各个属性。2.Java链式编程的优缺点链式编程可以使得代码可读性变得更高,链式编程也比较好实现,它的原理就是返回一个this对象,即返回对象本身,这样就可以达到链式的效果。2.1 链式编程的优点有:编程性更强、可读性更强、代码更简洁。2.2 链式编程的缺点有:对程序员的业务能力要求更高、不太利于代码调试。3.Java链式编程的实现3.1 之前我们创建对象后设置属性是这样的,需要多次重复的写出对象的名字.set去设置:public class Student{ private Integer id; private String name; private String email; private String phone; /** setter/getter方法省略 */}...Student s = new Student();s.setId(1);s.setEmail("xxxx@123.com");s.setName("zhangsan");user.setPhone("1135654635");3.2 通过简单改造,我们将各个属性中的set方法的返回值设置为对象本身,并且返回类型设置为对象本身的类型这样就可以实现链式编程了,很明显代码简捷了很多,如果遇到很多属性的情况这会显得更加明细。 public Student setId(Integer id) { this.id = id; return this; } public Student setName(String name) { this.name = name; return this; } public Student setEmail(String email) { this.email = email; return this; } public Student setPhone(String phone) { this.phoneNum = phone; return this; }...Student s = new Student();s.setId(1).setName("zhangsan").setPhone("113334343")setEmail("xxxxx@123.com");3.3 lombok实现的方式项目中我们常常引入lombok来简化编程,只需要@Data就可以省略有参、无参构造方法以及属性中的set和get方法,那么get和set方法已经省略了,我们要如何设置set方法中的返回值和返回类型呢?其实lombok已经帮我们实现了,只需增加@Accessors(chain = true) 注解就可开启链式编程了。@Accessors(chain = true)@Data@AllArgsConstructor@NoArgsConstructorpublic class Student{ private Integer id; private String name; private String email; private String phone; }以上就是Java链式编程的简单实现和使用,让我们的代码变得更优雅简捷起来吧~
-
1、Content-Type 的简介Content-Type即互联网媒体类型,也叫MIME类型。在互联网HTTP传输数据对象中我们会为他们打上这个Content-Type的数据格式标签,用于区分数据类型。最初Content-Type只是用于电子邮件系统,随着慢慢的使用推广,我们在HTTP中也采用了这一套规范方案。现在在HTTP协议消息头中,我们使用Content-Type来表示请求和响应中的媒体类型信息,以此用来告诉服务端如何处理请求的数据,以及告诉客户端(一般是浏览器)如何解析响应的数据,比如显示图片,解析并展示html等等。2、Content-Type的常见格式类型Content-Type格式规范是这样的(Content-Type:type/subtype ;parameter) ,其中参数如下:type:主类型,任意的字符串,如text,如果是*号代表所有;subtype:子类型,任意的字符串,如html,如果是*号代表所有,用“/”与主类型隔开;parameter:可选参数,如charset,boundary等。常见的栗子有:HTML文档标记:text/html;普通ASCII文档标记:text/html;JPEG图片标记:image/jpeg;GIF图片标记:image/gif;js文档标记:application/javascript;xml文件标记:application/xml;json字符串并且使用utf-8编码:application/json;charset:utf-8;3.常用类型简介3.1 application/x-www-form-urlencodedHTTP会将请求参数用key1=val1&key2=val2的方式进行拼接然后放到请求实体里面,需要注意的是,如果是中文或特殊字符如"/"、","、“:" 等会自动进行URL转码,并且这种类型不支持文件,一般是用于表单提交的。3.2 multipart/form-data它是一个多部分多媒体类型,首先生成了一个 boundary 用于分割不同的字段,在请求实体里每个参数以boundary开始,然后是附加信息和参数名,然后是空行,最后是参数内容。多个参数将会有多个boundary块。如果参数是文件会有特别的文件域。最后以boundary为结束标识。multipart/form-data是支持文件上传的格式的,一般用于上传文件的表单。3.3 application/json顾名思义是以JSON这种轻量级的数据格式,以“键-值”对的方式拼接数据进行传输的。使用时需要参数本身就是json格式的数据,参数会被直接放到请求实体里,不进行任何处理。服务端/客户端会按json格式解析数据,一般我们在服务端创建实体类进行接收解析。3.4 application/xml 和 text/xml与application/json类似,是传输xml格式的数据,如果使用text/xml类型会忽略xml数据里的编码格式。4、Content-Type的使用 4.1 request中的Content-Type客户端发出的Request请求中的的Content-Type设置必须要准确,如果不准确的话可能会导致请求失败。在spring中,如果接口使用了@RequestBody,spring的自动解析功能会将请求实体的内容自动转换为对应的Bean,但前提是请求的Content-Type必须设置为application/json,否正就会返回415错误(415 错误是 Unsupported media type,即不支持的媒体类型)。在使用的时候可以这样来配置:如果是一个restful接口(json格式),一般将Content-Type设置为application/json; charset=UTF-8;如果是文件上传,一般Content-Type设置为multipart/form-data如果普通表单提交,一般Content-Type设置为application/x-www-form-urlencoded4.2 response的Content-Type服务端Response响应的Content-Type也必须准确,虽然前端解析响应的数据不会根据Content-Type,并且服务端一般能自动设置准确的Content-Type,但是如果乱设置某些情况下可能会有问题,比如导出文件,打开图片等。如果在spring项目里使用@ResponseBody,spring会将响应的Content-Type设置为application/json;charset=UTF-8;,可能会导致文件无法导出,需要注意下。在使用的时候可以这样来配置:如果是文件导出,Content-Type 设置为 multipart/form-data,并且添加一个Content-Disposition设置为attachment;fileName=文件.后缀。其中Content-Disposition是Content-Type的扩展,告诉浏览器弹窗下载框,而不是直接在浏览器里展示文件。因为一般浏览器对于它能够处理的文件类型,如txt,pdf 等,它都是直接打开展示,而不是弹窗下载框。以上就是Content-Type的使用和小知识点的总结。
-
rt,公司没有git托管,需要自己写pom,但没接触过FI平台,不知道是不是跟CDH一样有对应CDH版本的依赖包,所以求一个
匿名用户
发表于2022-08-26 16:58:00
2022-08-26 16:58:00
最后回复
匿名用户
2022-08-26 17:54:11
184 3 -
比较原始的一种调用做法,这里把get请求和post请求都统一放在一个方法里面。实现过程:GET:1、创建远程连接2、设置连接方式(get、post、put。。。)3、设置连接超时时间4、设置响应读取时间5、发起请求6、获取请求数据7、关闭连接POST:1、创建远程连接2、设置连接方式(get、post、put。。。)3、设置连接超时时间4、设置响应读取时间5、当向远程服务器传送数据/写数据时,需要设置为true(setDoOutput)6、当前向远程服务读取数据时,设置为true,该参数可有可无(setDoInput)7、设置传入参数的格式:(setRequestProperty)8、设置鉴权信息:Authorization:(setRequestProperty)9、设置参数10、发起请求11、获取请求数据12、关闭连接直接上代码:package com.riemann.springbootdemo.util.common.httpConnectionUtil; import org.springframework.lang.Nullable; import java.io.*;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection; /** * @author riemann * @date 2019/05/24 23:42 */public class HttpURLConnectionUtil { /** * Http get请求 * @param httpUrl 连接 * @return 响应数据 */ public static String doGet(String httpUrl){ //链接 HttpURLConnection connection = null; InputStream is = null; BufferedReader br = null; StringBuffer result = new StringBuffer(); try { //创建连接 URL url = new URL(httpUrl); connection = (HttpURLConnection) url.openConnection(); //设置请求方式 connection.setRequestMethod("GET"); //设置连接超时时间 connection.setReadTimeout(15000); //开始连接 connection.connect(); //获取响应数据 if (connection.getResponseCode() == 200) { //获取返回的数据 is = connection.getInputStream(); if (null != is) { br = new BufferedReader(new InputStreamReader(is, "UTF-8")); String temp = null; while (null != (temp = br.readLine())) { result.append(temp); } } } } catch (IOException e) { e.printStackTrace(); } finally { if (null != br) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } if (null != is) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } //关闭远程连接 connection.disconnect(); } return result.toString(); } /** * Http post请求 * @param httpUrl 连接 * @param param 参数 * @return */ public static String doPost(String httpUrl, @Nullable String param) { StringBuffer result = new StringBuffer(); //连接 HttpURLConnection connection = null; OutputStream os = null; InputStream is = null; BufferedReader br = null; try { //创建连接对象 URL url = new URL(httpUrl); //创建连接 connection = (HttpURLConnection) url.openConnection(); //设置请求方法 connection.setRequestMethod("POST"); //设置连接超时时间 connection.setConnectTimeout(15000); //设置读取超时时间 connection.setReadTimeout(15000); //DoOutput设置是否向httpUrlConnection输出,DoInput设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个 //设置是否可读取 connection.setDoOutput(true); connection.setDoInput(true); //设置通用的请求属性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"); connection.setRequestProperty("Content-Type", "application/json;charset=utf-8"); //拼装参数 if (null != param && param.equals("")) { //设置参数 os = connection.getOutputStream(); //拼装参数 os.write(param.getBytes("UTF-8")); } //设置权限 //设置请求头等 //开启连接 //connection.connect(); //读取响应 if (connection.getResponseCode() == 200) { is = connection.getInputStream(); if (null != is) { br = new BufferedReader(new InputStreamReader(is, "GBK")); String temp = null; while (null != (temp = br.readLine())) { result.append(temp); result.append("\r\n"); } } } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { //关闭连接 if(br!=null){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } if(os!=null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if(is!=null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } //关闭连接 connection.disconnect(); } return result.toString(); } public static void main(String[] args) { String message = doPost("https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=13026194071", ""); System.out.println(message); }}运行结果:
-
需求:使用 SSH 框架的 Java web 项目想要和第三方公司集成 CAS 单点登录,接入到第三方的 OA 系统中,(在网上查阅一些资料,基本都是 CSDN 的帖子,尝试添加过滤器的方案未能实现(问题在过滤后跳转这块未能解决))向社区大佬们求助,有做过这方面的,或者总结过这方面的给个链接什么的非常感谢
-
StringBuffer类String类是在所有开发项目开发之中一定会使用的一个功能类,并且这个类拥有如下功能每个字符串的常量都属于一个String类匿名对象,并且不能修改;String类有两个常量池:静态常量池、运行常量池;String类对象实例化建议直接使用赋值的形式完成,这样可以直接将对象保存在对象池中方便下次重用;虽然String类很好用,但是我们发现它也有弊端,就是内容不允许频繁修改,所以为了解决问题,我们提供了StringBuffer类StringBuffer 并不像String类那样拥有两种实例化方法,必须要想普通方法一样调用方法,这个时候可以考虑一下方法构造方法:public StringBuffer();数据追加:public StringBuffer append(数据类型、变量),相当于字符串中的“+”操作;构造方法:public StringBuffer(String str),相当于初始化范例:观察String与StringBuffer对比 String类对象引用传递public class JavaAPIDemo { public static void main(String[] args) { // TODO Auto-generated method stub String str ="hello"; change(str); System.out.println(str); } public static void change(String temp) { temp+="word"; } }str内的值没有发生改变StringBuffer类对象引用传递public class JavaAPIDemo { public static void main(String[] args) { // TODO Auto-generated method stub StringBuffer buf =new StringBuffer("Hello "); change(buf); System.out.println(buf); } public static void change(StringBuffer temp) { temp.append("World"); } }buf内的值发生了改变 由此可见String内的值不能改变,StringBuffer内的值可以改变,这就是两这最主要的区别实际上大部分的情况下,很少会出现有字符串内容的改变,这种改变指的并不是针对静态常量池的改变; 范例:分析一下已有问题public class JavaAPIDemo { public static void main(String[] args) { String strA="www.baidu.com"; String strB="www."+"baidu."+"com"; System.out.println(strA==strB); } }在这个时候strB对象里面的内容并不是真正改变,或者更严格的将,对于现在的strB当程序编译的时候就相当于下面形式buf.append("www.").append("baidu.").append("com");也就是说所有的”+“在我们编译后都变为了StringBuffer中的append()方法,并且在程序中String类对象和StringBuffer类对象本来就可以互相转换String类对象变为StringBuferr可以依靠StringBuffer类构造方法或者使用append()方法所以类的对象都可以通过toString()方法将其变为String类型在StringBuffer类里面除了可以支持字符串内容修改之外,实际上也添加了很多String所不具有的方法插入数据:public StringBuffer insert(int offest,boolean b);public class JavaAPIDemo { public static void main(String[] args) { StringBuffer buf=new StringBuffer(); buf.append(".com").insert(0,"www.").insert(4, "baidu"); System.out.println(buf); } }删除指定范围的数据:public StringBuffer delete(int start,int end);public class JavaAPIDemo { public static void main(String[] args) { StringBuffer buf=new StringBuffer(); buf.append("Hello World!").delete(6, 12); System.out.println(buf); } }-字符串内容反转:public StringBuffer reverse();public class JavaAPIDemo { public static void main(String[] args) { StringBuffer buf=new StringBuffer(); buf.append("Hello World!"); System.out.println(buf.reverse()); } }实际上与StringBuffer类还有一个类似的功能类:StringBuilder类,这个类是在JDK1.5之后提供的,该类中提供的方法与StringBuffer类相同,区别在于StringBuffer类中的方法属于线程安全的,全都使用了synchronized关键字进行标注,而StringBuilder类属于非线程安全的面试题:请解释String、StringBuffer、StringBuilder的主要区别?String类是字符串的首选类型,其最大的特点是内容不允许修改;StringBuffer与StringBuilder类的内容允许修改;StringBuffer是在JDK1.0的时候提供的,属于线程安全操作,而StringBuilder是在JDK1.5的时候提供的不是线程安全操作
-
是否是因为gsjbcd4.jar中的 org.postgresql.ds.common.BaseDataSource getUrl为private 导致与springboot2.6.3中org.springframework.boot.jdbc.DataSourceBuilderPostgresDataSourceProperties BaseDataSource::getUrl 报错
-
1、UserAgent的简介 User Agent即用户代理,简称为UA,是一种特殊的字符串头,会使得服务器能够识别客户使用的操作系统、版本、CPU 类型、浏览器版本、浏览器渲染引擎、浏览器语言、浏览器插件等等,是不感觉很厉害的样子,通过这个串头,我们就可以进行获得很多的信息,从而进行相关业务的处理。 在server抓包的时候,会经常碰到直接使用wget或者curl被服务器拒绝的状况,这时候只需要通过增加一个user-agent串头进行模拟就可以通过了。2、查看User Agent的方法2.1 通过js获取 在浏览器地址栏中输入以下代码navigator.userAgent 或者window.navigator.userAgent 进行获取相关信息,获取后的信息会是这个样子的:'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/xxx (KHTML, like Gecko) Chrome/xxx Safari/xxx'2.2 通过Chrome开发者模式获取 打开chrome开发者模式(快捷键是F12),或者在网页上右键选择检查(快捷键是Ctrl+Shift+I),在Network栏中找到需要查询的请求,在Headers的最后可以看到User-Agent的相关信息了。这种方法可以通过模拟机型,在选择模拟的设备机型后,找到相应的请求就可以找到这类机型所对应的User-Agent信息了。3、User-Agent的应用3.1 User-Agent在js中的应用 其中判断是否是微信环境,通常我们会通过window.navigator.userAgent获取User-Agent串头后进行转小写,然后通过正则匹配/MicroMessenger/i是否为'micromessenger'来判断是否是微信内部浏览器从而判断是否是微信环境,这里如果不加window的话Android机可能会判断不了是否是微信,所以还是加上比较保险。function isWX() { var ua = window.navigator.userAgent.toLowerCase(); if (ua.match(/MicroMessenger/i) == 'micromessenger') { return true; } else { return false; }} 3.2 User-Agent在java中的应用 通常我们会通过reques请求中获取header中的user-Agent元素,然后进行转小写,在获取到信息之后,我们就可以通过这个User-Agent串头信息来进行环境判断了,其中包含micromessenger则为微信内部浏览器标识,即微信内部环境,在微信内部环境又包括小程序和企微,我们通过判断User-Agent串头信息是否包含wxwork元素来判断是否为企微环境,通过判断User-Agent串头信息是否包含miniprogram来判断是否为小程序环境下。String userAgent = request.getHeader("user-agent").toLowerCase();if (userAgent.indexOf("micromessenger") < 0) {// 非微信 return true;} else{ if (userAgent.indexOf("wxwork") > 0) { // 企业微信客户端 return true; }else if(userAgent.indexOf("miniprogram") > 0){//小程序下 return true; }}以上就是User-Agent的相关应用和总结。
-
1、pom的简介 pom是Project Object Model的简称,即项目对象模型。它是Maven项目中的XML配置文件。张什么样呢?就是这样的:pom.xml。主要作用是管理源代码、版本信息、配置文件信息、开发者的信息和角色信息、项目授权、url、继承和项目使用的依赖管理等等,一个Maven项目必须包含pom.xml配置文件。 其中各个pom依赖只是为了聚合工程或传递依赖用的。2、Maven项目打包类型 maven项目的打包类型有三种:pom、jar、war。会通过packing标签来进行标识,默认是jar类型,具体如下: pom表示父类型都为pom类型。 jar表示内部调用或者是作服务使用。 war表示需要部署的项目。2.1 jar类型介绍 2.1.1 扩展名为.jar(Java Application Archive简写)的文件,其中包含Java类的普通库、资源(resources)、辅助文件(auxiliary files)等。 2.1.2 jar包是java打的包,一般只包括一些编译后class文件和一些部署文件。 2.1.3 jar包通常是开发时要引用通用类,打成包便于存放管理。 2.1.4 常用于内部、接口、服务部署等。 2.2 war类型 2.2.1 扩展名为.war(Web Application Archive)的文件,包含全部Web应用程序。 2.2.2 war包可以理解为javaweb打的包,是一个web模块,包括写的代码编译成的class文件,依赖的包,配置文件,所有的网站页面,包括html,jsp等等。一个war包即为一个web项目,里面是项目的所有东西。 2.2.3 war包需要发布到一个容器里面,比如Tomcat需要将war包放置到它的webapps目录下,启动Tomcat就可以自动解压到你的web目录。 2.2.4 war类型是Sun公司提出的一种Web应用程序格式,与jar类型类似,也是许多文件的一个压缩包。这个包中的文件按一定目录结构来组织:通常其根目录下包含有Html和Jsp文件或者包含这两种文件的目录,另外还会有一个WEB-INF目录,这个目录很重要。通常在WEB-INF目录下有一个web.xml文件和一个classes目录,web.xml是这个应用的配置文件,而classes目录下则包含编译好的Servlet类和Jsp或Servlet所依赖的其它类(JavaBean)。 2.3 pom类型2.3.1 项目中使用maven进行模块管理时,每个模块下对应都会有一个pom文件,因为pom文件中维护了各模块之间的依赖和继承关系。 2.3.2 使用maven进行模块划分管理时,一般都会有一个父级项目,pom文件除了GAV(groupId, artifactId, version)是必须要配置的,另一个重要的属性就是packaging打包类型,所有的父级项目的packaging都为pom(可以到pom.xml文件里面进行手动配置),packaging默认是jar类型,如果不作配置,maven会将该项目打成jar包。 3.Maven项目的多模块 Maven项目的多模块(multi-modules)项目,也被称为聚合项目,通常由一个父模块和若干个子模块构成。其中,父模块必须以pom打包类型,通过标签标识出所有下属的子模块。其中的每个module,都是另外一个Maven项目,并且子类项目的packaging值只能是war或者jar。父模块的POM示例如下:pom test-one test-two test-three 以上就是Maven项目的知识点的简单归纳汇总。
-
一、 关键字和保留字关键字(keyword)的定义和特点定义:被Java语言赋予了特殊含义,用做专门用途的字符串(单词)特点:关键字中所有字母都为小写根据分类:数据类型:Boolean、int、long、short、byte、float、double、char、class、interface流程控制:if、else、do、while、for、Switch、case、default、break、continue、return、try、catch、finally修饰符: public、protected、private、final、void、static、abstract、transient、synchronized、volatile、native动作:package、import、throw 、throws、extends、implements、this、supper、instanceof、new保留字:Java保留字:现有Java版本尚未使用,以后可能会作为关键字使用。命名【标识符】时要避免使用这些保留字goto、const二、标识符什么是标识符Java对各种变量,方法和类等要素命名时使用的字符串序列称为标识符技巧:凡是自己可以 起名字的地方都叫标识符定义合法标识符规则由26个英文字母大小写,0-9,_或$组成数字不能包含空格不可以使用关键字和保留字,但能包含关键字个保留字Java中严格区分大小写,长度无限制Java 中名称命名规范包名:多单词组成时所有字母都小写:xxxxyyyy类名:接口名:多单词组成时,所有单词首字母读大写 : XxxYyy变量名:方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz常量名:所有字母都大写,多单词时每个单词用下划线连接:XXX_YYY_AAA注意点:注意1:在起名字时,为了提高阅读性,要尽量有意义注意2:Java采用Unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用三、变量变量的声明与使用变量的概念: 内存中的一个存储区域该区域的数据可以在同一类型范围内不断变化变量是程序中最基本的存储的单位。包含变量类型 变量名和存储的值变量的作用:用于在内存中保存的数使用变量的注意:Java中每个变量必须先声明,后使用使用变量名来访问这块区域的数据变量的作用域:其定义所在的一对{}内变量只有在其作用域才有效同一个作用域内,不能定义重名的变量声明变量:语法:数据类型 变量名称 int var;变量的赋值:var =10;声明和赋值变量语法:数据类型 变量名 =初始值int var =10;补充:变量的分类 按声明的位置的不同在方法体外,类体内声明的变量称为成员变量在方法体内,声明的变量称为局部变量所有变量:成员变量、局部变量成员变量和局部变量的区别:成员变量:实例变量(不以static修饰);类变量(以static修饰)局部变量:形参(方法、构造器中定义的变量);方法局部变量(在方法内定*义);代码块局部变量(在代码块内定义)注意:二者在初始值方面的异同异:局部变量除形参外,都需要显式初始化同: 都有生命周期基本数据类型变量的分类 按数据类型对于每一种数据都定义了明确的具体数据类型(强类型语言),在内存中分配了不同大小的内存空间。数据类型包括:基本数据类型和引用数据类型基本数据类型:数值型(整数类型:byte、short、int、long;浮点类型:float、double)、字符型char、布尔型boolean引用数据类型:类class、接口interface、数组 【】整数类型:byte、short、int、longJava各整数类型有固定的表数范围和字段长度,不受OS的影响,以保证Java程序的可移植性。Java的整型常量默认为int型,声明long型常量须后加“l”或‘L’Java程序中变量通常声明为int 型,除非不足以表示较大的数,才使用longbit 计算机中的最小存储单位,byte计算机中基本存储单位下面一个小练习:class VariableTest1 { public static void main(String[] args) { //整型:byte(1字节=8bit) short(2字节) int(4字节) long(8字节) //byte 范围: -128~127 byte b1 = 12; byte b2 = -128; //b2=128 编译不通过 System.out.println(b1); System.out.println(b2);// ② 声明long型变量,必须以“1”或“L”结尾 short s1 = 128; int i1 = 12345; long l1 = 345678586; System.out.println(l1); }}浮点类型:float 、double 与整数类型类似,Java浮点类型也有固定的表数范围和字段长度,不受具体的操作系统影响。浮点型常量有两种表示形式:十进制数形式:如:5.12 512.0f 0.512 (必须有小数点)科学计数法形式:如:5.12e2 512E2 100E-2float :单精度,尾数可以精确到7有效数字。很多情况下,精度很难满足需求。double :双精度,精度是float 的两倍。通常采用此类型。Java的浮点型常量默认为double 型,声明float 型常量,须后加“ f ”或“ F ” 字符类型:charchar 型数据用来表示通常意义上的“字符”(2字节)Java中所有字符都使用Unicode编码,故一个字符可以存储一个字母,一个汉字,或其他书面语的一个字符。字符型变量的三种形式:字符常量是用单引号(‘’)括起来的单个字符、例如:char c1 = ‘a’; char c2 = ‘中’; char c3 = ‘9’;Java中还可以使用转义字符‘ / ’ 来将其后字符转变为特殊字符型常量 。例如:char c3 = ‘\n’;‘\n’ 表示换行符直接使用Unicode 值来表示字符型常量:‘\uXXXX’。其中,XXXX代表一个十六进制整数。如: \u000a 表示\n。char类型是可以进行运算的。因为它都对应有Unicode码。小练习:编程就需要多动手,看会了理解了还需要多多动手class VariableTest1 { public static void main(String[] args) { double d1 = 12.3; System.out.println(d1 + 1); //定义float类型变量时,变量要以"f" 或"F"结尾 float f1 = 12.3f; System.out.println(f1); //② 通常,定义浮点型变量时,使用double变量 //3. 字符型:char(1字符=2字节) //① 定义char型变量,通常使用一对'' char c1='a'; System.out.println(c1); char c2 = '1'; char c3 = '中'; char c4 = '&'; System.out.println(c2); System.out.println(c3); System.out.println(c4); //② 表示方式:1.声明一个字符;2.转义字符;3.直接使用Unicode值来表示字符型常量 char c5='\n';//换行符 c5='\t';//制表符 System.out.println("hello"+c5); System.out.println("world"); char c6= '\u0123'; System.out.println(c6); char c7 = '\u0043'; System.out.println(c7); }}布尔类型: Boolean Boolean 类型用来判断逻辑条件,一般用于程序流程控制:if 条件控制语句while 循环控制语句do-while 循环控制语句for 循环控制语句boolean类型数据只允许取值true 和 false ,这点和C语言 不同。Java虚拟机中没有任何供Boolean值专用的字节码指令,Java语言表达所操作的Boolean 值,在编译之后都使用Java虚拟机中的int 类型数据类型代替:true 用1 表示,false 用0表示。代码示例:class VariableTest2 { public static void main(String[] args) {//4. 布尔型:boolean//① 只能取两个值之一:true . false//② 常常在条件判断. 循环结构中使用 boolean bb1 = true; System.out.println(bb1); boolean isMarried=false; if(isMarried){ System.out.println("禁止入内!"); }else { System.out.println("可以参观!"); } }}基本数据类型转换自动类型转换:容量小的类型自动转换为容量大的数据类型,数据类型按容量大小排序为:有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再 进行计算。byte、short、char之间不会相互转换,他们三者在计算时首先转换为int类型。boolean类型不能与其它数据类型运算。当把任何基本数据类型的值和字符串(String)进行连接运算时(+),基本数据类型的值将自动转化为 字符串(String)类型。 代码示例:class VariableTest3{ public static void main(String[] args) { byte b1=2; int i1=129; int i2=b1+i1; long l3=b1+i1; System.out.println(i2); System.out.println(l3); float f = b1 + i1; System.out.println(f);//***************特别的************************** char c1 = 'a'; //字符’a‘对应的数字是97 int i3=10; int i4 = c1 + i3; System.out.println(i4); }}字符串类型:StringString不是基本数据类型,属于引用数据类型使用方式与基本数据类型一致。例如:String str= “abcd”;一个字符串可以串接另一个字符串,也可以直接串接其它类型的数据。代码示例:String类型变量的使用1. String属于引用数据类型2. 声明String类型变量时,使用一对""3. String可以和8种基本数据类型变量做运算,且运算只能是连接运算;+4. 运算的结果依然是String类型class StringTest{ public static void main(String[] args) { String s1="hello"; System.out.println(s1); String s2 = "a"; String s3 = "";// char c = ''; //编译不通过//******************************* int number = 1001; String numberStr = "学号:"; String info = numberStr + number; //连接运算 boolean b1 = true; String info1 = info + true; System.out.println(info1); }}练习:String str1 = 4; //判断对错:noString str2 = 3.5f + “”; //判断str2对错:yesSystem.out.println(str2); //输出:”3.5”System.out.println(3+4+“Hello!”); //输出:7Hello!System.out.println(“Hello!”+3+4); //输出:Hello!34System.out.println(‘a’+1+“Hello!”); //输出:98Hello!System.out.println(“Hello”+‘a’+1); //输出:Helloa1强制类型转换自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换 符:(),但可能造成精度降低或溢出,格外要注意。通常,字符串不能直接转换为基本类型,但通过基本类型对应的包装类则可以实现把字符串转换成 基本类型。如:String a = “43”; inti= Integer.parseInt(a);boolean 类型不可以转换为其它的数据类型。练习:判断是否能通过编译short s = 5;s = s-2; //判断:nobyte b = 3;b = b + 4;//判断:nob = (byte)(b+4);//判断:yeschar c = ‘a’;int i = 5;float d = .314F;double result = c+i+d; //判断:yesbyte b = 5;short s = 3;short t = s + b;//判断:no————————————————版权声明:本文为CSDN博主「Serendipity的人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/m0_57448314/article/details/126239827
-
一、栈相关基础概念① 先进先入。不是所有进完才可以出,入的同时也可以出。②用数组实现栈:入栈和出栈的时间复杂度是O(1)③用单链表实现栈:头插法: 入栈和出栈 O(1)尾插法:入栈和出栈 O(n)所以用单链表实现栈时一定要头入头出。④方法 push、peek、pop、size、empty二、队列相关基础概念① 队尾进,队头出。底层是个双向链表。②Queue是个接口,在实例化时必须实例化LinkedList的对象。 Queue<Integer> q = new LinkedList<>();③用单链表实现队列 ok④ 用数组实现队列 。必须使用循环数组。后面3.6有代码实现逻辑。④方法 offer、poll、peek、size、isEmpty三、OJ题3.1 不可能的出栈顺序核心:入的时候同时可以出3.2 中缀表达式中缀转后缀:按照自己的理解,从左往右加括号,先乘除后加减。再逐步将运算符移到括号后,移 掉所有的(),就是rug计算中缀表达式的值:遇见数字入栈,遇到运算符就从栈出两个元素。先出的放在运算符的后面,后出栈的放在运算符前面。将计算结果再入栈。3.3 括号匹配 OJ链接:20. 有效的括号 - 力扣(LeetCode)实现代码:class Solution { public boolean isValid(String s) { Stack<Character> stack = new Stack<>(); for(int i = 0 ;i<s.length();i++){ char ch = s.charAt(i); // 遇见左括号就入栈,注意是单引号 if(ch == '[' || ch == '{' || ch == '('){ stack.push(ch); }else{ // 否则,就与栈顶元素进行比较 // 如果栈是空的,并且遇见的是右括号,直接返回false // 判断栈空 不能使用stack==null if(stack.isEmpty()){ return false; }else{ char peek = stack.pop(); if( (peek == '[' && ch == ']') || (peek == '{' && ch == '}') || (peek == '(' && ch ==')')){ continue; }else{ return false; } } } } if(stack.isEmpty()){ return true; }else{ return false; } }}3.4 逆波兰表达式求值 OJ链接:150. 逆波兰表达式求值 - 力扣(LeetCode)实现代码:class Solution { public int evalRPN(String[] tokens) { Stack<Integer> stack = new Stack<>(); HashSet<String> set = new HashSet<>(); set.add("+"); set.add("-"); set.add("*"); set.add("/"); for(String str:tokens){ // 如果遇见的是不是运算符,是数字,就入栈 if(!set.contains(str)){ stack.push(Integer.parseInt(str)); }else{ // 遇见运算符,就弹出两个数字 int back = stack.pop(); int front = stack.pop(); HashMap<String,Integer> hashMap = new HashMap<>(); hashMap.put("+",front+back); hashMap.put("-",front-back); hashMap.put("*",front*back); // 注意:back有可能为 0 if(back != 0){ hashMap.put("/",front/back); }//计算结果,将结果入栈 int result = hashMap.get(str); stack.push(result); } } return stack.peek(); }}3.5 出栈入栈次序匹配 OJ链接:栈的压入、弹出序列_牛客题霸_牛客网 (nowcoder.com) 解题思路:要点1:定义一个pos,记录popA数组中元素的位置。要点2:遍历pushA,依次将pushA中的元素入栈,判断栈顶元素和popA中的元素是否相同,相同就出栈,并观察popA中的下一个元素是否和新栈顶元素相等。如果不相等,入栈pushA的下一个元素实现代码:import java.util.*;import java.util.ArrayList; public class Solution { public boolean IsPopOrder(int [] pushA,int [] popA) { Stack<Integer> stack1 = new Stack(); int pos = 0; for(int i=0;i<pushA.length;i++){ stack1.push(pushA[i]); while(!stack1.isEmpty() && stack1.peek() == popA[pos]){ stack1.pop(); pos++; } } if(stack1.empty()){ return true; } return false; }}3.6 循环队列 OJ链接:622. 设计循环队列 - 力扣(LeetCode) 解题思路:要点1:为了区分队列空和满,约定: rear指向数组最后一个元素的下一个位置,下一次添加数据,可以直接往rear位置上放 front == rear 队列空 rear的下一个元素是front,队列满了。(rear+1)%elem.length要点2:返回队列顶部元素,返回front所在位置元素,front向前移。为了保证front可以从3移到1,front=(front+1)%elem.length要点3:返回队列最后一个元素。如果rear=0,返回elem[elem.length-1].如果rear不为0,就返回elem[rear-1]实现代码:class MyCircularQueue { private int[] elem; private int front; private int rear; public MyCircularQueue(int k) { elem = new int[k+1]; } public boolean enQueue(int value) { if(isFull()){ return false; } if(isEmpty()){ elem[0] = value; front = 0; rear = 1; return true; } elem[rear] = value; rear = (rear+1) % elem.length; return true; } public boolean deQueue() { if(isEmpty()){ return false; } front = (front+1) % elem.length; return true; } public int Front() { if(isEmpty()){ return -1; } return elem[front]; } public int Rear() { if(isEmpty()){ return -1; } if(rear == 0){ return elem[elem.length-1]; } return elem[rear-1]; } public boolean isEmpty() { return rear == front; } public boolean isFull() { if((rear+1)%elem.length == front){ return true; } return false; }}3.7 用队列实现栈 OJ链接:225. 用队列实现栈 - 力扣(LeetCode)解题思路:要点1:有两个队列要点2:第一次放push数据时,默认放到队列1中要点3:添加元素时,添加到不为空的队列中,这样才能保证顺序要点4:出栈—》获得栈顶元素—》栈后入先出—》弹出最后一个入栈的元素因此,需要找出最后一个入队列的元素。将队列的前n-1个元素,放到另外一个队列中,剩下的就是最后一个入队列的元素实现代码:class MyStack { private Queue<Integer> qu1; private Queue<Integer> qu2; public MyStack() { qu1 = new LinkedList<>(); qu2 = new LinkedList<>(); } public void push(int x) { // 如果队列2是空的,就往队列1里面放。原因:只有放在不为空的队列中顺序才不会乱 if(qu2.isEmpty()){ qu1.offer(x); }else{ qu2.offer(x); } } public int pop() { if(empty()){ return -1; } if(qu1.isEmpty()){ // 队列2不为空,队列先进先出,将队列前size-1个元素放到队列1中,剩下的最后一个元素就是最后入队列的,也就是栈的第一个元素,将其返回 int size = qu2.size(); while(size > 1){ qu1.offer(qu2.poll()); size--; } int result = qu2.poll(); return result; } if(qu2.isEmpty()){ int size = qu1.size(); while(size > 1){ qu2.offer(qu1.poll()); size--; } int result = qu1.poll(); return result; } return -1; } public int top() { if(empty()){ return -1; } if(qu1.isEmpty()){ int size = qu2.size(); while(size > 1){ qu1.offer(qu2.poll()); size--; } int result = qu2.poll(); qu1.offer(result); return result; } if(qu2.isEmpty()){ int size = qu1.size(); while(size > 1){ qu2.offer(qu1.poll()); size--; } int result = qu1.poll(); qu2.offer(result); return result; } return -1; } public boolean empty() { return qu1.isEmpty()&&qu2.isEmpty(); }}3.8 用栈实现队列 OJ链接:232. 用栈实现队列 - 力扣(LeetCode) 解题思路:要点1:有两个栈。存数据都存到栈1中,取数据都从栈2中取。要点2:取数据时,如果栈2是空的,就把栈1中的元素全放到栈2中。再从栈2中取。实现代码:class MyQueue { private Stack<Integer> stack1; private Stack<Integer> stack2; public MyQueue() { stack1 = new Stack(); stack2 = new Stack(); } public void push(int x) { stack1.push(x); } public int pop() { if(empty()){ return -1; } if(stack2.empty()){ while(!stack1.empty()){ stack2.push(stack1.pop()); } } return stack2.pop(); } public int peek() { if(empty()){ return -1; } if(stack2.empty()){ while(!stack1.empty()){ stack2.push(stack1.pop()); } } return stack2.peek(); } public boolean empty() { return stack1.empty()&&stack2.empty(); }}3.9 最小栈 OJ链接:155. 最小栈 - 力扣(LeetCode) 解题思路:要点1:创建两个栈。一个栈存放所有的元素,另外一个栈存放栈中最小的元素要点2:第一次push数据,数据存放在两个栈中要点3:之后存放数据,该数据小于等于最小栈的栈顶元素,才能存放到最小栈和stack中。否则,就只能存放到stack中实现代码:class MinStack { public Stack<Integer> stack; public Stack<Integer> minStack; public MinStack() { stack = new Stack(); minStack = new Stack(); } public void push(int val) { // 如果第一次放元素,往stack和minStack里都放 if(stack.empty() && minStack.empty()){ stack.push(val); minStack.push(val); return; } // 如果存放的数据比minStack元素小,该元素同时放到两个栈中 if(val <= minStack.peek()){ stack.push(val); minStack.push(val); return; }else{ // 否则,就只往stack中放 stack.push(val); return; } } public void pop() { int peek = stack.pop(); if(peek == minStack.peek()){ minStack.pop(); } } public int top() { if(stack.empty()){ return -1; } return stack.peek(); } public int getMin() { if(minStack.empty()){ return -1; } return minStack.peek(); }}————————————————版权声明:本文为CSDN博主「刘减减」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/weixin_44258092/article/details/126240946
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签