Web相关知识和技巧概括

  • JavaEE三层架构
  • MVC 概念
  • 前后台的介绍
  • 分页
  • 模版引擎(后端)
  • JavaWeb优化技巧
  • DAO层的优化
  • Web层的优化
  • 数据的封装和抽取 BeanUtils 的使用
  • 为什么dao层和service层要用接口
  • BO、DO、VO、PO、DTO、POJO如何理解和区分
  • 开发环境、测试环境、生产环境 的区别
  • HTTP协议详解
  • $ 和 # 的运用
  • 什么是集群、分布式、集中式、伪分布式
  • 回调的含义和用途
  • 文件的上传和下载
  • Java Bean 使用包装类型 还是基本类型


JavaEE三层架构

java 通过路径创建文件写入数据_MVC

分层的目的是为了解耦。解耦就是为了降低代码的耦合度。方便项目后期的维护和升级。

MVC 概念

MVC简介:

  • MVC 全称:Model 模型、 View 视图、 Controller 控制器。
  • MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。
  • MVC 是一种思想 :MVC 的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了降低耦合度)。

MVC介绍:

  • View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作—— JSP/HTML。
  • Controller 控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet,转到某个页面。或者是重定向到某个页面。
  • Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码——JavaBean/domain/entity/pojo,简单来说就是“数据+ 业务逻辑”,也就是Service和Dao和Entity这些代码,

MVC与MVMM详解:

  • 对于 View 来说,你如果抽象得好,那么一个 App 的动画效果可以很方便地移植到别的 App 上,而 Github 上也有很多 UI控件,这些控件都是在 View 层做了很好的封装设计,使得它能够方便地开源给大家复用。
  • 对于 Model 来说,它其实是用来存储业务的数据的,如果做得好,它也可以方便地复用。比如我当时在做有道云笔记 iPad版的时候,我们就直接和 iOS 版复用了所有的 Model 层的代码。在创业做猿题库客户端时,iOS 和 iPad 版的 Model层代码再次被复用上了。当然,因为和业务本身的数据意义相关,Model 层的复用大多数是在一个产品内部,不太可能像 View层那样开源给社区。
  • 说完 View 和 Model 了,那我们想想 Controller,Controller 有多少可以复用的?我们写完了一个 Controller 之后,可以很方便地复用它吗?结论是:非常难复用。在某些场景下,我们可能可以用 addSubViewController 之类的方式复用 Controller,但它的复用场景还是非常非常少的。
  • 如果我们能够意识到 Controller 里面的代码不便于复用,我们就能知道什么代码应该写在 Controller 里面了,那就是那些不能复用的代码。在我看来,Controller 里面就只应该存放这些不能复用的代码,这些代码包括: ①在初始化时,构造相应的 View 和 Model。 ②监听 Model 层的事件,将 Model 层的数据传递到 View 层。 ③监听 View 层的事件,并且将 View 层的事件转发到 Model 层。 ④如果 Controller 只有以上的这些代码,那么它的逻辑将非常简单,而且也会非常短。
  • 如果 Controller 只有以上的这些代码,那么它的逻辑将非常简单,而且也会非常短。但是我们却很难做到这一点,因为还是有很多逻辑我们不知道写在哪里,于是就都写到了 Controller 中了,那我们接下来就看看其它逻辑应该写在哪里。
  • objc.io 是一个非常有名的 iOS 开发博客,它上面的第一课 《Lighter View Controllers》 上就讲了很多对 ViewController 瘦身的技巧,我们先总结一下它里面的观点: ①将 UITableView 的 Data Source 分离到另外一个类中。 ②将数据获取和转换的逻辑分别到另外一个类中。 ③将拼装控件的逻辑,分离到另外一个类中。
  • 构造 ViewModel,MVC 也可以用 ViewModel 。MVVM 的优点我们一样可以借鉴。具体做法就是将 ViewController 给 View 传递数据这个过程,抽象成构造 ViewModel 的过程。这样抽象之后,View 只接受 ViewModel,而 Controller 只需要传递 ViewModel 这么一行代码。而另外构造 ViewModel 的过程,我们就可以移动到另外的类中了。
  • 总结来说就是MVC模式中M和V可以复用而C不可以复用,因为C中有许多逻辑是针对业务来写的,因此我们可以将一些常用的业务逻辑代码再次抽象出来(相当于写成一个个方法),这样Controller就变成了ViewModel,只需要几行代码就可以搞定。
  • 链接详解:MVC详谈

MVC 架构程序的工作流程:

  • 用户通过 View 页面向服务端提出请求,可以是表单请求、超链接请求、AJAX 请求等
  • 服务端 Controller 控制器接收到请求后对请求进行解析,找到相应的 Model 对用户请求进行处理
  • Model处理后,将处理结果再交给 Controller
  • Controller 在接到处理结果后,根据处理结果找到要作为向客户端发回的响应 View 页面。页面经渲染(数据填充)后,再发送给客户端。

前后台的介绍

java 通过路径创建文件写入数据_web_02

分页

当查询的结果过多时那么这时网页就需要分页,因此我们可以把分页抽成一个对象模型。

@param <T> 是具体的模块的 javaBean 类 */ 
*/
public class Page<T> {
public static final Integer PAGE_SIZE = 4;
// 当前页码 
private Integer pageNo; 
// 总页码 
private Integer pageTotal; 
// 当前页显示数量 
private Integer pageSize = PAGE_SIZE; 
// 总记录数 
private Integer pageTotalCount; 
// 当前页数据 
private List<T> items;

java 通过路径创建文件写入数据_复用_03

模版引擎(后端)

模版引擎(后端)定义:

  • 模板的诞生是为了将显示与数据分离,模板技术多种多样,但其本质是将模板文件和数据通过模板引擎生成最终的HTML代码。
  • 模板引擎的实现方式有很多,最简单的是“置换型”模板引擎,这类模板引擎只是将指定模板内容(字符串)中的特定标记(子字符串)替换一下便生成了最终需要的业务数据(比如网页)。 ①置换型模板引擎实现简单,但其效率低下,无法满足高负载的应用需求(比如有海量访问的网站),因此还出现了“解释型”模板引擎和“编译型”模板引擎等。

模版引擎分类(后端):

  • jsp是大家最熟悉的技术 ①优点: 1、功能强大,可以写java代码 2、支持jsp标签(jsp tag) 3、支持表达式语言(el) 4、官方标准,用户群广,丰富的第三方jsp标签库 5、性能良好。jsp编译成class文件执行,有很好的性能表现 ②缺点: jsp没有明显缺点,非要挑点骨头那就是,由于可以编写java代码,如使用不当容易破坏mvc结构。
  • velocity是较早出现的用于代替jsp的模板语言 ①优点: 1、不能编写java代码,可以实现严格的mvc分离 2、性能良好,据说比jsp性能还要好些 3、使用表达式语言,据说jsp的表达式语言就是学velocity的 ②缺点: 1、不是官方标准 2、用户群体和第三方标签库没有jsp多。 3、对jsp标签支持不够好 4、已经很久很久没有维护了。
  • freemarker ①优点: 1、不能编写java代码,可以实现严格的mvc分离 2、性能非常不错 3、对jsp标签支持良好 4、内置大量常用功能,使用非常方便 5、宏定义(类似jsp标签)非常方便 6、使用表达式语言 ②缺点: 1、不是官方标准 2、用户群体和第三方标签库没有jsp多
  • thymeleaf ①Thymeleaf是个XML/XHTML/HTML5模板引擎,可以用于Web与非Web应用。 ②Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。相对于编写逻辑或代码,开发者只需将标签属性添加到模板中即可。接下来,这些标签属性就会在DOM(文档对象模型)上执行预先制定好的逻辑。Thymeleaf的可扩展性也非常棒。你可以使用它定义自己的模板属性集合,这样就可以计算自定义表达式并使用自定义逻辑。这意味着Thymeleaf还可以作为模板引擎框架。 ③thymeleaf优点:静态html嵌入标签属性,浏览器可以直接打开模板文件,便于前后端联调。springboot官方推荐方案。 ④thymeleaf缺点:模板必须符合xml规范,就这一点就可以判死刑!太不方便了!js脚本必须加入

选择freemarker的原因:

  • 性能。velocity应该是最好的,其次是jsp,普通的页面freemarker性能最差(虽然只是几毫秒到十几毫秒的差距)。但是在复杂页面上(包含大量判断、日期金额格式化)的页面上,freemarker的性能比使用tag和el的jsp好。
  • 宏定义比jsp tag方便
  • 内置大量常用功能。比如html过滤,日期金额格式化等等,使用非常方便
  • 支持jsp标签
  • 可以实现严格的mvc分离

为什么不使用模版引擎了?

  • J2EE中传统的前端依靠JSP Tag技术封装,依靠服务器解析,完成页面预编译之后,通过HTTP发送到用户浏览器端。那些数不清的JSP标签库/TAG库,由于缺乏必要的技术规范各大厂商完成的千差万别。很多时候已经成为影响J2EE开发效率的一大死穴。JSP等模板引擎作为伪前端技术(实际上是服务器端技术)有如下弊端: <1>标签库没有统一标准,各大厂商完成不一,常使开发者晕头转向 <2> 本身不是一种前端与后端分离的技术,不能实现前端与后端各自的语言独立 <3> 不是好的富客户端技术,GWT虽然可以用为富客户端前端技术,但是也是基于Java <4> 前端与后端数据交换XML/JSON支持不够灵活,不是天生支持Ajax
  • 反观Angular、React、Vue等前端技术,主要有以下显著特点: <1>前台与后台语言独立, <2>内嵌支持Ajax <3>灵活的数据交换支持XML/JSON <4>前后端独立语言的好处,理论上后端可以支持任何语言
  • 现在很多新开发的WEB应用,Java作用主要在后台,但浏览器端越来越与Java无关是无关的,这个就是WEB2.0的威力,Div + CSS+ Json的数据交换以及Javascript库(ExtJS, JQuery)的应用,足以已经使前端开发与后端开发完全分离,这些完全得益于ajax/Bayeux(Comet)等技术助推。伴随着HTML5技术的进一步推广,独自在后端闭门造车的行为变得尤其愚蠢。而且近些年发展迅猛的虚拟Dom技术类框架Angular、React、Vue,配合Redux、dva等数据流方案,使得大前端的时代已然真正的降临。
  • 链接:关于前后端分离和模版引擎

JavaWeb优化技巧

DAO层的优化

简介:

  • DAO:Data Access Object访问数据信息的类和接口,包括了对数据的CRUD(Create、Retrival、Update、Delete),而不包含任何业务相关的信息。有时也称作:BaseDAO
  • 作用:为了实现功能的模块化,更有利于代码的维护和升级。

Web层的优化

简介:

  • 在实际的项目开发中,一个模块,一般只使用一个 Servlet 程序。
  • 在进行反射的时候获取方法时的方法参数类型必须使用类名.class ,因为使用实例.getClass()获取到的类是一个Tomcat使用Facade门面模式对Request对象和Response对象进行包装的类。

java 通过路径创建文件写入数据_web_04

java 通过路径创建文件写入数据_web_05

数据的封装和抽取 BeanUtils 的使用

简介:

  • BeanUtils 工具类,它可以一次性的把所有请求的参数注入到 JavaBean 中。 BeanUtils 工具类,经常用于把Map 中的值注入到 JavaBean 中,或者是对象属性值的拷贝操作。 BeanUtils 它不是 Jdk的类。而是第三方的工具类。所以需要导包。
  • 步骤: 1、导入需要的 jar 包: ①commons-beanutils-1.8.0.jar ②commons-logging-1.1.1.jar 2、编写 WebUtils 工具类使用

为什么dao层和service层要用接口

为每个DAO声明接口的好处在于:

  • 可以在尚未实现具体DAO的时候编写上层代码,如Service里对DAO的调用
  • 可以为DAO进行多实现,例如有JDBCDAO实现,MyBatisDAO实现,而不需要更改上层代码,只需要简单的在Spring的IoC配置里修改一下注入的DAO实现

为每个SERVICE声明接口的好处在于:

  • 可以在尚未实现具体Service情况下编写上层改代码,如Controller对Service的调用
  • Spring无论是AOP还是事务管理的实现都是基于动态代理的,而动态代理的实现依赖于接口,所以必须有接口的定义才能使用这些功能
  • 可以对Service进行多实现

详解:

  • 总的来说,接口的优势就在于规范方法参数,返回值,另外可以实现多态,结合Spring来说接口对于使用Spring的各种功能也是不可或缺的
  • 另外,使用接口对于测试代码也是有好处的,对于mock一个方法来说,我们不需要关注方法的具体实现,因为本来mock就会将方法内部实现置空,我们的关注点集中于方法参数以及返回值,所以使用接口对于快速实现流程上的测试是有好处的.
  • 使用接口是为了调用与实现解耦,带来的好处是可以各干各的了,带来的坏处是从一个概念变成了两个概念,增加了系统的复杂度。衡量一下在具体场景中是弊大于利还是利大于弊,就可以做选择了。当然,在大部分场景下,还要考虑一个因素,就是你会不会写接口。没有良好接口设计能力的人,写出来的接口抽象不合理,等于没写,什么好处都得不到,只有坏处,这种情况下干脆别写。那怎么衡量你会不会写接口呢,我的经验是,至少见过一次写了接口后得到明确好处的例子。
  • 什么情况下需要各干各的? ①最简单的场景,写接口的是你,写实现的是你小弟。当然大多数类似情况没必要真的建一个interface然后再让人家去implements,把函数的第一行写好,注释写好,代码提交上,里面的内容让小弟去填就行了。 ②另一种情况,调用代码先于实现代码编写。比如你开发的是struts这种东西,那你指定得搞个Action接口。 ③再一种情况,多种业务的模式类似。此时这个接口类实际上相当于某一层的抽象。定义出一个层后,有多种实现,然后通过向调用端注入不同的实现类,实现不同的逻辑。如果这种注入不能在编译期完成的话,也就需要用接口抽象一下。 ④上面这几种情况写得有点绕,没办法,太难表述了并且好多事我自己也没想明白……
  • 说到题目中的场景: ①先说dao,这玩意儿是做数据库读写的。对应一下上面那几种情况:你作为项目架构师想写两行代码就让苦逼小弟加班干活然则自己去泡妹子的话,可能需要写个interface里面几个抽象的insert、delete之类的函数;项目在快速原型阶段如果客户满意就掏钱买oracle如果客户不满意就得免费MySQL的话,你可能需要定义个dao接口然后先用内存数据库写点能让原型跑起来的实现,等一切有定论了再说;每个类都有一个dao,每个dao都有crud基本方法的话你可能需要定义一个通用Dao接口然后搞点代码技巧不用一个个的去写体力代码从此登上人生巅峰。所以dao接口还是有用的。 ②再说service,这玩意儿更得具体问题具体分析。不去抠理论的话,什么是service,我的理解就是一段段实现了某个逻辑的代码的组合。所以service是个比dao更抽象的概念,严格来讲dao就是一种service。只不过在java web开发中,dao是个人人都得写的东西,所以都拿出来单说了。因此,后面说的service跟dao没有本质分别。
  • 还是上面说的那几种情况: ①先从工序上说,你在写上一层的时候,会用到下一层提供的逻辑,具体表现形式就是各种各样的service类和里面的方法。上一层开搞的时候,一定会知道的一个事是下一层会干什么事,比如“将传入编号对应的人员信息设置为离职”,但下一层的代码不一定已经一行一行写出来了。所以这会儿需要有个接口,让写上层代码的人先能把代码写下去。有各种理由可以支持这种工序的合理性,比如一般来说,上一层的一行代码会对应下一层的好多行代码,那先让写上层代码的人写一遍,解决高端层面的bug,会提高很多效率。 ②再从抽象角度说,不同业务模块之间的共用,不一定是共用某段代码,也可能是共用某段逻辑框架,这时候就需要抽象一个接口层出来,再通过不同的注入逻辑实现。比如模块1是登记学生信息,模块2是新闻发布,看上去风马牛不相及。但分析下来如果两个模块都有共同点,顺序都是1、验证是否有权限 2、验证输入参数是否合法 3、将输入参数转化为业务数据 4、数据库存取 5、写log,那就可以写一个service接口,里面有上述5个函数,再分别写两个service实现。具体执行的时候,通过各种注入方法,直接new也好,用spring注入也好,实现不同的效果。 ③当然上面说的这种情况很少有人这么干,因为已经普遍到这个程度,再精化精化就是struts了,java web的各种mvc框架都提供机制给你干这个事。但是每个项目或产品,都应该可以用类似的思路抽象出一些东西,如果抽象合理,会很大程度的提高项目架构的合理性。 ④这些要是能搞定,那什么写个接口然后实现个mock用于测试这种事,信手拈来。
  • JavaWeb开发中,服务器端通常分为表示层、业务层、持久层,这就是所谓的三层架构。三层架构的每一层都有自己的开发模式,即架构模式,如下图。
  • java 通过路径创建文件写入数据_web_06

  • 其中,表示层一般是采用 MVC架构模式,业务层有事务脚本模式、领域模型模式等,持久层有数据映射器模式(Hibernate即是典型的)、入口模式(iBatis、JDBC)。
  • 事务脚本模式和领域模型模式: ①Domain Model:很难用语言说清楚。简单的说就是领域模型象征着真实世界里项目中的各个参与者。“万物皆对象”的原则在此体现得淋漓尽致。在其他地方对象总是带着种种具体的责任,而在领域模式中,它们常常描述为一组属性及附加的代理。它们是做某些事的某些东西。适合简单项目。 ②Transaction Script:使用过程来组织业务逻辑,每个过程处理来自表现层的单个请求。貌似有点太抽象了。大多数业务应用都可以被看作是一系列事务,有时候,事务可能就显示下数据库的信息,有时候,也可能涉及许多校验和计算的步骤。事务脚本则将所有这些逻辑组织成单个过程,而且每个事务都有自己的事务脚本,就是都有自己的执行过程,但注意的是事务间的公共子任务可以被分解成多个子程序。适合复杂项目。
  • 企业应用中最关键的显然是业务层。而对于初学者来说,事务脚本模式是最为简单,最容易掌握的。如果开发团队面向对象设计能力一般,而且业务逻辑相对简单,业务层一般都会采用事务脚本模式。为嘛?简单呀,是个人都能很快学会!(当然,如果业务逻辑复杂,用事务脚本模式就很不明智了。嗯,简单点讲,就是违背了单一职责设计原则,Service类成为万能的上帝,承担了太多职责。。。)
  • 那么什么是事务脚本模式呢? ①所谓事务,就是表示层的一个请求;所谓脚本就是一个方法或者一个函数;所谓事务脚本就是将一次请求封装为一个方法或者一个函数。所谓事务脚本模式,就是将业务层的对象分为三类,按照下图的方式组织起来(下图是用EA画的UML类图,一个简单的学生管理的业务层设计)。
  • java 通过路径创建文件写入数据_web_07

  • 如上图所示,在事务脚本模式中,有三类对象。其中 ①Service类封装业务流程(或者说是界面上的业务流程), ②DAO类封装对持久层的访问, ③DTO类封装业务实体对象。 ④各个对象之间的关系如上图所示,这就是所谓业务逻辑的组织方式。
  • 为什么要用Service接口和DAO接口? ①我们还得回到最基本的面向对象设计原则上去。 ②面向对象设计原则中有三条与此相关:开闭原则、依赖倒转原则、理氏替换原则。还记得依赖倒转原则吧?高层不依赖于低层,二者都依赖于抽象,也就是面向接口编程。
  • 为什么要用Service接口? ①是让表示层不依赖于业务层的具体实现。
  • 为什么要用DAO接口? ①是让业务层不依赖于持久层的具体实现。
  • 有了这两个接口,Spring IOC容器才能发挥作用。 ①举个例子,用DAO接口,那么持久层用Hibernate,还是用iBatis,还是 JDBC,随时可以替换,不用修改业务层Service类的代码。 ②使用接口的意义就在此。

那么写接口到底有什么好处呢?

  • 一些框架可以利用接口,简化开发。比如mybatis可以利用动态代理直接帮助我们实现具体的类,我们只需要写好接口即可,当然这个接口就值得写。
  • 方便协调开发,比如一个比较牛的人搞定一些接口约束,然后交给手下人去实现具体代码。
  • 为了隔离变化,比如你数据库可能会从mysql变成mssql,或者实现思路会有改变。那么接口帮助你依赖抽象而非具体。类似的一些具体的设计模式也会用到接口。大型项目建议使用。
  • 为了方便单元测试。或者测试驱动开发的情况下。
  • 一些古老的框架做ioc和aop的时候,只能依赖接口生成代理类,没得办法。

BO、DO、VO、PO、DTO、POJO如何理解和区分

简介:

  • DO( Data Object数据对象):与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
  • PO(persistant object 持久化对象): 持久对象,一个 PO 的数据结构对应着库中表的结构,表中的一条记录就是一个 PO 对象。
  • DTO(Data Transfer Object 数据传输对象): 数据传输对象,Service 或 Manager 向外传输的对象。
  • BO(business object 业务对象) : 业务对象。和上面那些 O 不同的是,由 Service 层输出的封装业务逻辑的对象。
  • VO(view object 视图对象) : 显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
  • POJO(plian ordinary java object 简单无规则java对象):POJO 专指只有 setter/getter/toString 的简单类,包括 DO/DTO/BO/VO 等。
  • DAO(Data Access Objects数据访问对象):数据访问对象,和上面那些 O 不同的是,其功能是用于进行数据操作的。通常不会用于描述数据实体。

流程:

  • 用户发出请求(可能是填写表单),表单的数据在展示层被匹配为VO。
  • 展示层把VO转换为服务层对应方法所要求的DTO,传送给服务层。
  • 服务层首先根据DTO的数据构造(或重建)一个DO,调用DO的业务方法完成具体业务。
  • 服务层把DO转换为持久层对应的PO(可以使用ORM工具,也可以不用),调用持久层的持久化方法,把PO传递给它,完成持久化操作。
  • 对于一个逆向操作,如读取数据,也是用类似的方式转换和传递,略。
  • 浅析VO、DTO、DO、PO的概念、区别和用处

java 通过路径创建文件写入数据_MVC_08

java 通过路径创建文件写入数据_MVC_09

既然DTO是展示层与服务层之间传递数据的对象,为什么还需要一个VO呢?

  • 对!对于绝大部分的应用场景来说,DTO和VO的属性值基本是一致的,而且他们通常都是POJO,因此没必要多此一举,但不要忘记这是实现层面的思维,对于设计层面来说,概念上还是应该存在VO和DTO,因为两者有着本质的区别,DTO代表服务层需要接收的数据和返回的数据,而VO代表展示层需要显示的数据。
  • 用一个例子来说明可能会比较容易理解:例如服务层有一个getUser的方法返回一个系统用户,其中有一个属性是gender(性别),对于服务层来说,它只从语义上定义:1-男性,2-女性,0-未指定,而对于展示层来说,它可能需要用“帅哥”代表男性,用“美女”代表女性,用“秘密”代表未指定。说到这里,可能你还会反驳,在服务层直接就返回“帅哥美女”不就行了吗?
  • 对于大部分应用来说,这不是问题,但设想一下,如果需求允许客户可以定制风格,而不同风格对于“性别”的表现方式不一样,又或者这个服务同时供多个客户端使用(不同门户),而不同的客户端对于表现层的要求有所不同,那么,问题就来了。再者,回到设计层面上分析,从职责单一原则来看,服务层只负责业务,与具体的表现形式无关,因此,它返回的DTO,不应该出现与表现形式的耦合。

VO与DTO的应用:

  • 上面只是用了一个简单的例子来说明VO与DTO在概念上的区别,本节将会告诉你如何在应用中做出正确的选择。
  • 在以下才场景中,我们可以考虑把VO与DTO二合为一(注意:是实现层面): ①需求非常清晰稳定,而且客户端很明确只有一个的时候,没有必要把VO和DTO区分开来,这时候VO可以退隐,用一个DTO即可,为什么是VO退隐而不是DTO?回到设计层面,服务层的职责依然不应该与展示层耦合,所以,对于前面的例子,你很容易理解,DTO对于“性别”来说,依然不能用“帅哥美女”,这个转换应该依赖于页面的脚本(如JavaScript)或其他机制(JSTL、EL、CSS) ②即使客户端可以进行定制,或者存在多个不同的客户端,如果客户端能够用某种技术(脚本或其他机制)实现转换,同样可以让VO退隐
  • 以下场景需要优先考虑VO、DTO并存: ①上述场景的反面场景 因为某种技术原因,比如某个框架(如Flex)提供自动把POJO转换为UI中某些Field时,可以考虑在实现层面定义出VO,这个权衡完全取决于使用框架的自动转换能力带来的开发和维护效率提升与设计多一个VO所多做的事情带来的开发和维护效率的下降之间的比对。 ②如果页面出现一个“大视图”,而组成这个大视图的所有数据需要调用多个服务,返回多个DTO来组装(当然,这同样可以通过服务层提供一次性返回一个大视图的DTO来取代,但在服务层提供一个这样的方法是否合适,需要在设计层面进行权衡)。

DO与PO的区别:

  • DO和PO在绝大部分情况下是一一对应的,PO是只含有get/set方法的POJO,但某些场景还是能反映出两者在概念上存在本质的区别:
  • DO在某些场景下不需要进行显式的持久化,例如利用策略模式设计的商品折扣策略,会衍生出折扣策略的接口和不同折扣策略实现类,这些折扣策略实现类可以算是DO,但它们只驻留在静态内存,不需要持久化到持久层,因此,这类DO是不存在对应的PO的。
  • 同样的道理,某些场景下,PO也没有对应的DO,例如老师Teacher和学生Student存在多对多的关系,在关系数据库中,这种关系需要表现为一个中间表,也就对应有一个TeacherAndStudentPO的PO,但这个PO在业务领域没有任何现实的意义,它完全不能与任何DO对应上。这里要特别声明,并不是所有多对多关系都没有业务含义,这跟具体业务场景有关,例如:两个PO之间的关系会影响具体业务,并且这种关系存在多种类型,那么这种多对多关系也应该表现为一个DO,又如:“角色”与“资源”之间存在多对多关系,而这种关系很明显会表现为一个DO——“权限”。
  • 某些情况下,为了某种持久化策略或者性能的考虑,一个PO可能对应多个DO,反之亦然。例如客户Customer有其联系信息Contacts,这里是两个一对一关系的DO,但可能出于性能的考虑(极端情况,权作举例),为了减少数据库的连接查询操作,把Customer和Contacts两个DO数据合并到一张数据表中。反过来,如果一本图书Book,有一个属性是封面cover,但该属性是一副图片的二进制数据,而某些查询操作不希望把cover一并加载,从而减轻磁盘IO开销,同时假设ORM框架不支持属性级别的延迟加载,那么就需要考虑把cover独立到一张数据表中去,这样就形成一个DO对应对个PO的情况。
  • PO的某些属性值对于DO没有任何意义,这些属性值可能是为了解决某些持久化策略而存在的数据,例如为了实现“乐观锁”,PO存在一个version的属性,这个version对于DO来说是没有任何业务意义的,它不应该在DO中存在。同理,DO中也可能存在不需要持久化的属性。

DO与PO的应用:

  • 由于ORM框架的功能非常强大而大行其道,而且JavaEE也推出了JPA规范,现在的业务应用开发,基本上不需要区分DO与PO,PO完全可以通过JPA,HibernateAnnotations/hbm隐藏在DO之中。虽然如此,但有些问题我们还必须注意:
  • 对于DO中不需要持久化的属性,需要通过ORM显式的声明,如:在JPA中,可以利用@Transient声明。
  • 对于PO中为了某种持久化策略而存在的属性,例如version,由于DO、PO合并了,必须在DO中声明,但由于这个属性对DO是没有任何业务意义的,需要让该属性对外隐藏起来,最常见的做法是把该属性的get/set方法私有化,甚至不提供get/set方法,但对于Hibernate来说,这需要特别注意,由于Hibernate从数据库读取数据转换为DO时,是利用反射机制先调用DO的空参数构造函数构造DO实例,然后再利用JavaBean的规范反射出set方法来为每个属性设值,如果不显式声明set方法,或把set方法设置为private,都会导致Hibernate无法初始化DO,从而出现运行时异常,可行的做法是把属性的set方法设置为protected。
  • 对于一个DO对应多个PO,或者一个PO对应多个DO的场景,以及属性级别的延迟加载,Hibernate都提供了很好的支持,请参考Hibnate的相关资料。

开发环境、测试环境、生产环境 的区别

简述:

  • 开发环境(development):开发环境是程序猿们专门用于开发的服务器,配置可以比较随意,为了开发调试方便,一般打开全部错误报告。(程序员接到需求后,开始写代码,开发,运行程序,看看程序有没有达到预期的功能;)
  • 测试环境(testing):一般是克隆一份生产环境的配置,一个程序在测试环境工作不正常,那么肯定不能把它发布到生产机上。(程序员开发完成后,交给测试部门全面的测试,看看所实现的功能有没有bug,测试人员会模拟各种操作情况;)
  • 生产环境(production):是指正式提供对外服务的,一般会关掉错误报告,打开错误日志。(就是线上环境,发布到对外环境上,正式提供给客户使用的环境。)
  • 三个环境也可以说是系统开发的三个阶段:开发->测试->上线,其中生产环境也就是通常说的真实环境。

HTTP协议详解

简述:

  • http是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。
  • 请求和响应消息的头以ASCII码形式给出;而消息内容则具有一个类似MIME的格式。这个简单模型是早期Web成功的有功之臣,因为它使开发和部署非常地直截了当。

浏览器输入url按回车背后经历了哪些?

  • 1、首先,在浏览器地址栏中输入url,先解析url,检测url地址是否合法
  • 2、浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则跳到第三步操作。 ①浏览器缓存:浏览器会记录DNS一段时间,因此,只是第一个地方解析DNS请求; ②操作系统缓存:如果在浏览器缓存中不包含这个记录,则会使系统调用操作系统,获取操作系统的记录(保存最近的DNS查询缓存); ③路由器缓存:如果上述两个步骤均不能成功获取DNS记录,继续搜索路由器缓存; ④ISP缓存:若上述均失败,继续向ISP搜索。
  • 3、在发送http请求前,需要域名解析(DNS解析),解析获取相应的IP地址。
  • 4、浏览器向服务器发起tcp连接,与浏览器建立tcp三次握手。
  • 5、握手成功后,浏览器向服务器发送http请求,请求数据包。
  • 6、服务器处理收到的请求,将数据返回至浏览器
  • 7、浏览器收到HTTP响应
  • 8、浏览器解码响应,如果响应可以缓存,则存入缓存。
  • 9、 浏览器发送请求获取嵌入在HTML中的资源(html,css,javascript,图片,音乐······),对于未知类型,会弹出对话框。
  • 10、 浏览器发送异步请求。
  • 11、页面全部渲染结束。

HTTP状态码:

  • 200 请求已成功,请求所希望的响应头或数据体将随此响应返回。
  • 201 请求已经被实现,而且有一个新的资源已经依据请求的需要而建立,且其 URI 已经随Location 头信息返回
  • 202 服务器已接受请求,但尚未处理
  • 301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
  • 302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
  • 303 (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
  • 304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
  • 305 (使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。
  • 307 (临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
  • 401 当前请求需要用户验证。如果当前请求已经包含了 Authorization 证书,那么401响应代表着服务器验证已经拒绝了那些证书
  • 403 服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交
  • 404 请求失败,请求所希望得到的资源未被在服务器上发现
  • 500 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。
  • 501 服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。
  • 502 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
  • 503 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。

http协议有哪几种请求方式?

  • GET
  • POST
  • HEAD
  • OPTIONS
  • PUT
  • DELETE
  • TRACE
  • CONNECT

get和post请求区别:

  • 以下这些都是百度答案,但其实不是标准答案: ①GET在浏览器回退时是无害的,而POST会再次提交请求。 ②GET产生的URL地址可以被Bookmark,而POST不可以。 ③GET请求会被浏览器主动cache,而POST不会,除非手动设置。 ④GET请求只能进行url编码,而POST支持多种编码方式。 ⑤GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。 ⑥GET请求在URL中传送的参数是有长度限制的,而POST么有。 ⑦对参数的数据类型,GET只接受ASCII字符,而POST没有限制。 ⑧GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。 ⑨GET参数通过URL传递,POST放在Request body中。
  • 首先从安全性讲,get和post都一样,没啥所谓的哪个更安全。get请求参数在url地址上,直接暴露,post请求的参数放body部分,按F12也直接暴露了,所以没啥安全性可言
  • “GET参数通过URL传递,POST放在Request body中”这个其实也不准,post请求也可以没body,也可以在url传递。
  • 标准答案是get请求和post请求本质上没区别
  • GET和POST有一个重大区别: ①简单的说:GET产生一个TCP数据包;POST产生两个TCP数据包。 ②长的说: 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
  • 详情可以参考这篇,写的挺好的《GET和POST两种基本请求方法的区别》

为什么说:"对参数的数据类型,GET只接受ASCII字符,而POST没有限制?"

  • 在 HTTP 协议中,URL 是 HTTP 的一个首部。既然作为一个首部,那么根据约定,一定是 ASCII字符的(毕竟计算机是美帝发明的嘛),
  • 而 GET 参数取的正是 URL 中的 QUERY STRING。最初 HTTP也和计算机一样只是应用的很少,美帝也没想到会覆盖全球,于是他们就很 naive 地使用非 ASCII 的头部 URL 来传输 GET。
  • 后来有了 UTF-8,浏览器开发公司发现,编码都是十六进制的嘛。于是开始使用 % 加编码的一个字节来组成新编码,这就是 URL code的由来,其实本质上传输的还是 ASCII,只不过对它硬转码了。
  • HTTP 是直接使用 MIME 的,它是为了收发非 ASCII 邮件而存在的一种扩展。最初电子邮件也是只支持ASCII,可是由于邮件越来越复杂,美帝才对协议进行扩展,让它能够传输小电影、小图片或者其它的文件,然后就有了 MIME,它有一个MIME type,来说明具体是什么类型的文件。
  • 后来,其他国家的人一想:诶,卧槽,我直接用 MIME 来传输我们国家的字符不就完了么。然后 MIME 就能传非 ASCII 字符了。
  • 我们说的 POST,它其实就是 HTTP 的实体,通过 MIME,也就可以传输非 ASCII 字符了。

$ 和 # 的运用

JSP(EL表达式): EL 表达式主要是代替 jsp 页面中的表达式脚本在 jsp 页面中进行数据的输出。 语法:${ 运算表达式 } 。 EL 表达式支持如下运算符:

  • 关系运算,逻辑运算,算数运算,三元运算还有empty 运算
  • .点运算 和 [] 中括号运算符 .点运算,可以输出 Bean 对象中某个属性的值。 []中括号运算,可以输出有序集合中某个元素的值。

Spring(SpEL表达式): SpEL表达式语言是一个支持运行时查询和操作对象图的强大的表达式语言。 语法类似于EL:SpEL使用#{表达式}作为定界符,所有在大框号中的字符都将被认为是SpEL。

通过SpEL可以实现:

  • 字面量的表示:整数,小数,科学计数法,String可以使用单引号或者双引号作为字符串的定界符号。
  • 引用其他对象
  • 引用其他对象的属性
  • 调用其他方法,还可以链式操作
  • 算数运算符:+, -, *, /, %, ^:
  • 加号还可以用作字符串连接:
  • 比较运算符: <, >, ==, <=, >=, lt, gt, eq, le, ge
  • 逻辑运算符号: and, or, not, |
  • if-else 运算符:?: (ternary), ?: (Elvis)
  • if-else 的变体
  • 正则表达式:matches
  • 调用静态方法或静态属性:通过 T() 调用一个类的静态方法,它将返回一个 Class Object,然后再调用相应的方法或属性。

Spring(properties 取值 字符串替换): properties 如果在 spring 中通过 PropertyPlaceholderConfigurer 加载,当spring中需要用到 properties 中的一些 key 和value 值时可以利用 PorpertyPlaceholderConfiger 提供的$ 直接取得。从org.springframework.beans.factory.config.PropertyPlaceholderConfigurer 的 JavaDoc API 可知,它不光能从属性文件里取值,也能从系统属性,甚至是环境变量中取值。

PorpertyPlaceholderConfiger 有一些常用的属性,在一些高级应用中可能会用到:

  • placeholderPrefix 默认前缀占位符是"${"。可以根据需要改为其它的前缀符。 这个特点估计就是 spring 中能用${xxx} 取得 properties 文件中的内容的理由吧,即估计是spring只要看到 是 ${ 占位符 就会 到 PropertyPlaceholderConfigurer 中去找其加载的 properties文件,从而 spring能用${}取得 properties 中属性值。

详细链接:Spring 中 用 ${xxx} 读取properties文件的说明spring 配置文件属性设置默认值以及读取环境变量值

maven内置属性(${} properties): Maven属性事实上有六种类型的Maven属性:

  • 内置属性:主要有两个常用内置属性——${basedir}表示项目根目录,即包含pom.xml文件的目录;${version}表示项目版本。
  • POM属性:pom中对应元素的值。例如${project.artifactId}对应了元素的值,常用的POM属性包括: ${project.build.sourceDirectory}:项目的主源码目录,默认为src/main/java/. ${project.build.testSourceDirectory}:项目的测试源码目录,默认为/src/test/java/. ${project.build.directory}:项目构建输出目录,默认为target/. ${project.build.outputDirectory}:项目主代码编译输出目录,默认为target/classes/. ${project.build.testOutputDirectory}:项目测试代码编译输出目录,默认为target/testclasses/. ${project.groupId}:项目的groupId. ${project.artifactId}:项目的artifactId. ${project.version}:项目的version,于${version}等价 ${project.build.finalName}:项目打包输出文件的名称,默认为${project.artifactId}${project.version}.
  • 自定义属性:在pom中元素下自定义的Maven属性。例如
<project>  
    <properties>  
        <my.prop>hello</my.prop>  
    </properties>  
</project>
  • Settings属性:与POM属性同理。如${settings.localRepository}指向用户本地仓库的地址。
  • Java系统属性:所有Java系统属性都可以使用Maven属性引用,例如${user.home}指向了用户目录。可以通过命令行mvn help:system查看所有的Java系统属性
  • 环境变量属性:所有环境变量都可以使用以env.开头的Maven属性引用。例如${env.JAVA_HOME}指代了JAVA_HOME环境变量的值。也可以通过命令行mvn help:system查看所有环境变量。

详细链接:Maven内置了三大特性:属性、Profile和资源过滤来支持构建的灵活性。

PreparedStatement中#{}与${}的区别:

  • #{}: 表示一个占位符号,实现向PreparedStatement占位符中设置值(#{}表示一个占位符?),自动进行Java类型到JDBC类型的转换(因此#{}可以有效防止SQL注入).#{}可以接收简单类型或PO属性值,如果parameterType传输的是单个简单类型值,#{}花括号中可以是value或其它名称.
  • $ {}: 表示替换SQL字符串,通过${}可将parameterType内容拼接在SQL中而不进行JDBC类型转换,${}可以接收简单类型或PO属性值,如果parameterType传输的是单个简单类型值,${}花括号中只能是value.

详细链接:井号和美元号的区别Mybatis中#{}与${}的区别:

  • #{}: mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值
  • ${}: mybatis在处理 ${ } 时,使用的方式为OGNL方式获取值。

什么是集群、分布式、集中式、伪分布式

  1. 集中式 将项目等部署到同一台机器上,对机器性能要求比较高,一般会用多台机器备份,否则,如果机器出现死机等状况,整个项目将不能运行。 (举例:就好比你要盖一座房子,你房子就给一个人盖,如果这个人生病或者有事,你还没有合适的人来代替这个人,你的房子就不能盖了。
  2. 分布式 将一个项目分成几块,分别在不同的机器上运行,相比较与集中式,对机器要求有所下降。
  3. 集群 与集中式、分布式是完全不同的概念。 分布式一定是集群,集群不一定是分布式(eg:集中式的多机备份)集群只是相对于机器数量的一个概念
  4. 伪分布式 从字面理解:不是真正的分布式伪分布式是将多台机器的任务放到一台机器运行 eg: 将淘宝分多模块后,一个模块放到一台机器中运行;多台机器多个模块的时候是同时运行,速度快。 一台机器中运行多个模块,多个模块不能并行处理,速度慢,必须得一个任务一个任务的 完成,其他任务只能等待。

分布式和集中式对比:

集中式架构,就是将主机资源集中在大型主机或小型机上,操作系统,中间件,数据库等“基础软件”均为闭源商用系统,简单来说就是将资源集中存储在中央服务器中,工作时只需要从中央服务器中调取需要的资源,然后工作完成后再交还给中央服务器即可。

java 通过路径创建文件写入数据_数据_10

分布式架构,就是由多台服务器、分布式数据库和大量内存闪存组成的集群,简单来说所有服务器都有单独处理保存工作的功能,有一台服务器作为版本集中存放的服务器,用来方便“交换”大家的修改就够了。

java 通过路径创建文件写入数据_java 通过路径创建文件写入数据_11

很多人应该都知道集中式架构普遍应用于银行、电信等行业,面对的是庞大的结构化的数据量,需要更高性能的服务器和联网,这一点决定了集中式架构的部署结构简单。集中式系统往往基于底层性能卓越的大型主机,或者性能优势明显的小机,因此无须考虑如何对服务进行多个节点的部署,也就不用考虑多个节点之间的分布式协作问题,但其对稳定性要求极其严格,因为系统故障直接影影响着业务开展。

分布式架构则更多的在Google、Amazon、Facebook、阿里巴巴、腾讯等互联网公司及金融行业上大展手脚,鉴于这些公司的业务范围不难推出分布式架构的特点就是分布性、对等性、高并发性、缺乏全局时钟以及故障随时发生。分布式系统中的多台服务器在空间上自由度较大,没有主/从之分,并且能够并发地操作一些共享资源,诸如数据库或分布式存储等,难以定义事件的先后顺序,能够应对随时出现的任何形式的故障。

回调的含义和用途

什么是回调?

  • 一般来说,模块之间都存在一定的调用关系,从调用方式上看,可以分为三类同步调用、异步调用和回调。同步调用是一种阻塞式调用,即在函数A的函数体里通过书写函数B的函数名来调用之,使内存中对应函数B的代码得以执行。异步调用是一种类似消息或事件的机制解决了同步阻塞的问题,例如A通知B后,他们各走各的路,互不影响,不用像同步调用那样,A通知B后,非得等到B走完后,A才继续走。回调是一种双向的调用模式,也就是说,被调用的接口被调用时也会调用对方的接口,例如A要调用B,B在执行完又要调用A。

回调的用途:

  • 回调一般用于层间协作,上层将本层函数安装在下层,这个函数就是回调,而下层在一定条件下触发回调。例如作为一个驱动,是一个底层,他在收到一个数据时,除了完成本层的处理工作外,还将进行回调,将这个数据交给上层应用层来做进一步处理,这在分层的数据通信中很普遍。

Java实现接口回调:

  • 在C/C++中,要实现回调函数,被调用函数要告诉调用者自己的指针地址。但是Java没有指针地址,不能传递方法的地址,一般采用接口回调的方法来实现:把实现某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被调用类实现的接口的方法。

原理:

  • 首先创建一个回调对象,然后再创建一个控制器对象,将回调对象需要被调用的方法告诉控制器对象,控制器对象负责检查某个场景是否出现或某个条件是否满足,当满足时,自动调用回调对象的方法。

例如:

  • 老板A对员工B说,我现在交给你一个任务,并且我把我的电话号码给你,你一旦完成任务就给我打电话。

文件的上传和下载

文件的上传和下载简介:

  • 文件的上传和下载是非常常见的功能。很多的系统中或者软件中都经常使用文件的上传和下载。
  • 比如: ①QQ 头像就使用了上传。 ②邮箱中也有附件的上传和下载功能。 ③OA 系统中审批有附件材料的上传。

文件上传:

  • 要有一个 form 标签,method=post 请求
  • form 标签的 encType 属性值必须为 multipart/form-data 值
  • 在 form 标签中使用 input type=file 添加上传的文件
  • 编写服务器代码(Servlet 程序)接收,处理上传的数据。 encType=multipart/form-data表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼 接,然后以二进制流的形式发送给服务器
<form action="http://192.168.31.74:8080/09_EL_JSTL/uploadServlet" 
method="post" enctype="multipart/form-data"> 
	用户名:<input type="text" name="username" /> <br> 
	头像:<input type="file" name="photo" > <br> 
	<input type="submit" value="上传"> 
</form>

文件上传相关jar包:

  • commons-fileupload.jar 需要依赖 commons-io.jar 这个包,所以两个包我们都要引入。 ①commons-fileupload-1.2.1.jar ②commons-io-1.4.jar
  • commons-fileupload.jar 和 commons-io.jar 包中常用的类: ①ServletFileUpload 类,用于解析上传的数据。 ②FileItem 类,表示每一个表单项。
  • commons-fileupload.jar 和 commons-io.jar 包中常用的方法: ①boolean ServletFileUpload.isMultipartContent(HttpServletRequestrequest) <1>判断当前上传的数据格式是否是多段的格式。 ②public List<FileItem> parseRequest(HttpServletRequest request) <1>解析上传的数据 ③ boolean FileItem.isFormField() <1>判断当前这个表单项是否是普通的表单项还是上传的文件类型。 <2>true表示普通类型的表单项 false 表示上传的文件类型 ④ String FileItem.getFieldName() <1>获取表单项的 name 属性值 ⑤ String FileItem.getString() <1>获取当前表单项的值。 ⑥ String FileItem.getName(); <1>获取上传的文件名 ⑦ void FileItem.write( file ); <1>将上传的文件写到 参数 file 所指向抽硬盘位置 。

文件下载常用 API 说明:

  • response.getOutputStream(); <1>得到一个输出流
  • servletContext.getResourceAsStream(); <1>在服务器端获取一个输入流
  • servletContext.getMimeType(); <1>得到指定路径的文件类型
  • response.setContentType(); <1>设置相应的类型
  • response.setHeader(); <1>设置相应的响应头 <2>response.setHeader(“Content-Disposition”, “attachment; fileName=1.jpg”); 1、这个响应头告诉浏览器这是需要下载的。而 attachment 表示附件,也就是下载的一个文件。fileName="XXX" 表示下载的文件名。 2、完成上面的两个步骤下载文件是没问题了。但是如果我们要下载的文件是中文名的话还需要设置编码。 3、因为在响应头中,不能包含有中文字符,只能包含 ASCII 码。

中文名乱码问题解决方案:

  • 方案一:URLEncoder 解决 IE 和谷歌浏览器的附件中文名问题。 ①如果客户端浏览器是IE浏览器或者是谷歌浏览器。我们需要使用 URLEncoder 类先对中文名进行 UTF-8 的编码操作。 ②因为 IE 浏览器和谷歌浏览器收到含有编码后的字符串后会以 UTF-8 字符集进行解码显示。
// 把中文名进行 UTF-8 编码操作。 
String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");
// 然后把编码后的字符串设置到响应头中 
response.setHeader("Content-Disposition", str);
  • 方案二:BASE64 编解码解决火狐浏览器的附件中文名问题 ①如果客户端浏览器是火狐浏览器,那么我们需要对中文名进行 BASE64 的编码操作。
这时候需要把请求头 Content-Disposition: attachment; filename=中文名 
编码成为:Content-Disposition: attachment; filename==?charset?B?xxxxx?= 

=?charset?B?xxxxx?= 现在我们对这段内容进行一下说明:
=? 表示编码内容的开始 
charset 表示字符集 
B 表示 BASE64编码 
xxxx 表示文件名BASE64编码后的内容 
?= 表示编码内容的结束
  • 那么我们如何解决上面两种不同编解码方式呢? ①我们只需要通过判断请求头中 User-Agent 这个请求头携带过来的浏览器信息即可判断出是什么浏览器。

Java Bean 使用包装类型 还是基本类型

int优缺点:

  • 优点: ①用于Bean的时候,有默认值。比如自己拼接sql增加一个User时,会方便很多,不过现在都用ORM框架,所以这也不算是优点啦。 ②两个值比较方便,使用 == 就可以了。
  • 缺点:
//错误
  int a1 = (Integer) null;
  //错误
  boolean x1 = (Boolean)null;
  //正确
  Integer a2 = (Integer) null;
  Boolean x2 = (Boolean)null;

Integer优缺点:

  • 优点:可以存放null,从数据库中查出值时可能会有null
  • 缺点:Intege不能使用 == 比较相等。
Integer i1 = 127;
        Integer i2 = 127;
        Integer i3 = 128;
        Integer i4 = 128;
        /**
         *   public static Integer valueOf(int i) {
         if (i >= IntegerCache.low && i <= IntegerCache.high)
         return IntegerCache.cache[i + (-IntegerCache.low)];
         return new Integer(i);
         }
         */
        System.out.println("  i1 == i2 "+(i1 == i2));//true
        System.out.println("  i3 == i4 "+(i3 == i4));//false
        int i6 = 127;
        int i7 = 127;
        int i8 = 128;
        int i9 = 128;

        System.out.println("  i6 == i7 "+(i6 == i7));//true  
        System.out.println("  i8 == i9 "+(i8 == i9));//true

        System.out.println("  i1 == i6 "+(i6 == i1));//true 与 int 类型的比较都是值比较
        System.out.println("  i8 == i3 "+(i8 == i3));//true

        int i10 = new Integer(128);
        int i11 = new Integer(128);
        System.out.println("  i10 == i11 "+(i10 == i11));//true


        Integer i12 = new Integer(127);
        Integer i13 = new Integer(127);
        System.out.println("  i12 == i13 "+(i12 == i11));//false 对象地址比较
  • 把int类型赋值给Integer类型。此时,int类型变量的值会自动装箱成Integer类型,然后赋给Integer类型的引用,这里底层就是通过调用valueOf()这个方法来实现所谓的装箱的。
  • 把Integer类型赋值给int类型。此时,Integer类型变量的值会自动拆箱成int类型,然后赋给int类型的变量,这里底层则是通过调用intValue()方法来实现所谓的拆箱的。
  • Integer 和 int 进行比较分三情况: ①Integer与int类型的比较:这里就无所谓是谁与谁比较了,Integer == int与int == Integer的效果是一样的,都会把Integer类型变量拆箱成int类型,然后进行比较,相等则返回true,否则返回false。同样,拆箱调用的还是intValue()方法。 ②Integer之间的比较: <1>这个就相对简单了,直接把两个引用的值(即是存储目标数据的那个地址)进行比较就行了,不用再拆箱、装箱什么的。 <2>值得注意的是:如果为Integer=某个数值这种情况的, Integer之间的比较,JVM会自动缓存-128~127范围内的值,所以所有在这个范围内的值相等的Integer对象都会共用一块内存,而不会开辟多个;超出这个范围内的值对应的Integer对象有多少个就开辟多少个内存。 ③int之间的比较:这个也一样,直接把两个变量的值进行比较。

阿里巴巴开发手册:

  • 链接:Java Bean 使用包装类型还是基本类型

java 通过路径创建文件写入数据_复用_12

java中为何short,byte用得少?

  • 所有的short参与运算时都会自动转化成int,并且在一个short在Java虚拟机里面仍然占用四个字节的空间。
  • 相反的,short和int赋值时还要经常进行类型转换。
  • 所以很少使用short(byte也是一样),一般情况下都int.