标签 翻译 下的文章 - 酷游博客
首页
关于
友链
Search
1
阿里的简历多久可以投递一次?次数多了有没有影响?可以同时进行吗?
45 阅读
2
Java中泛型的理解
40 阅读
3
Java 14 发布了,再也不怕 NullPointerException 了!
38 阅读
4
Java中的可变参数
37 阅读
5
该如何创建字符串,使用" "还是构造函数?
29 阅读
技术
登录
/
注册
找到
5
篇与
翻译
相关的结果
2025-01-22
[译]Java中的this关键字
英文原文:Java This翻译地址:Java中的this关键字原文作者:Java Papers翻译作者:Hollis 转载请注明出处。 java中关于this这个关键字的定义: java中的this关键字用于在方法中引用当前实例。 以下是在java中使用this的方法: 1、明确表示使用的是成员变量(instance)而不是静态变量(static)或者局部变量(local)。 private String javaFAQ; void methodName(String javaFAQ) { this.javaFAQ = javaFAQ; } this在这里代表成员变量(译者注:this.javaFAQ表示成员变量,javaFAQ表示局部变量)。在这个方法里,局部变量的优先级更高。因此,如果没有用this.表示的话则指定的是局部变量。在这个方法里面,如果局部变量的名字和成员变量的名字并不一样的话,那么用不用这个this其实就没有关系了。 2、this用来表示构造函数 public JavaQuestions(String javapapers) { this(javapapers, true); } 这里使用this调用同一个类中的另外一个包含两个参数的构造方法。 3、用于将当前Java实例作为参数传递 obj.itIsMe(this); 4、和上一个类似,this还可以用于返回当前java实例 CurrentClassName startMethod() { return this; } 注意:上面这两种用法如果在内部类中使用,可能会导致结果与预期不一致。因为this将代表内部类的实例而不是外部类的实例。 5、this也可以表示当前类的句柄 Class className = this.getClass(); // this methodology is preferable in java 也可以通过 Class className = ABC.class;实现,这里的ABC指的是java类的类名。 通常,java中的this都与他的实例相关联,不能在静态方法中使用。
技术
# 翻译
酷游
1月22日
0
18
0
2025-01-22
[译]Java中整型的缓存机制
英文原文:Java Integer Cache 翻译地址:Java中整型的缓存机制 原文作者:Java Papers 翻译作者:Hollis 转载请注明出处。 本文将介绍Java中Integer的缓存相关知识。这是在Java 5中引入的一个有助于节省内存、提高性能的功能。首先看一个使用Integer的示例代码,从中学习其缓存行为。接着我们将为什么这么实现以及他到底是如何实现的。你能猜出下面的Java程序的输出结果吗。如果你的结果和真正结果不一样,那么你就要好好看看本文了。 package com.javapapers.java; public class JavaIntegerCache { public static void main(String... strings) { Integer integer1 = 3; Integer integer2 = 3; if (integer1 == integer2) System.out.println("integer1 == integer2"); else System.out.println("integer1 != integer2"); Integer integer3 = 300; Integer integer4 = 300; if (integer3 == integer4) System.out.println("integer3 == integer4"); else System.out.println("integer3 != integer4"); } } 我们普遍认为上面的两个判断的结果都是false。虽然比较的值是相等的,但是由于比较的是对象,而对象的引用不一样,所以会认为两个if判断都是false的。在Java中,==比较的是对象应用,而equals比较的是值。所以,在这个例子中,不同的对象有不同的引用,所以在进行比较的时候都将返回false。奇怪的是,这里两个类似的if条件判断返回不同的布尔值。 上面这段代码真正的输出结果: integer1 == integer2 integer3 != integer4 Java中Integer的缓存实现 在Java 5中,在Integer的操作上引入了一个新功能来节省内存和提高性能。整型对象通过使用相同的对象引用实现了缓存和重用。 适用于整数值区间-128 至 +127。 只适用于自动装箱。使用构造函数创建对象不适用。 Java的编译器把基本数据类型自动转换成封装类对象的过程叫做自动装箱,相当于使用valueOf方法: Integer a = 10; //this is autoboxing Integer b = Integer.valueOf(10); //under the hood 现在我们知道了这种机制在源码中哪里使用了,那么接下来我们就看看JDK中的valueOf方法。下面是JDK 1.8.0 build 25的实现: /** * Returns an {@code Integer} instance representing the specified * {@code int} value. If a new {@code Integer} instance is not * required, this method should generally be used in preference to * the constructor {@link #Integer(int)}, as this method is likely * to yield significantly better space and time performance by * caching frequently requested values. * * This method will always cache values in the range -128 to 127, * inclusive, and may cache other values outside of this range. * * @param i an {@code int} value. * @return an {@code Integer} instance representing {@code i}. * @since 1.5 */ public static Integer valueOf(int i) { if (i >= IntegerCache.low && i = 127; } private IntegerCache() {} } 其中的javadoc详细的说明了缓存支持-128到127之间的自动装箱过程。最大值127可以通过-XX:AutoBoxCacheMax=size修改。 缓存通过一个for循环实现。从低到高并创建尽可能多的整数并存储在一个整数数组中。这个缓存会在Integer类第一次被使用的时候被初始化出来。以后,就可以使用缓存中包含的实例对象,而不是创建一个新的实例(在自动装箱的情况下)。 实际上这个功能在Java 5中引入的时候,范围是固定的-128 至 +127。后来在Java 6中,可以通过java.lang.Integer.IntegerCache.high设置最大值。这使我们可以根据应用程序的实际情况灵活地调整来提高性能。到底是什么原因选择这个-128到127范围呢?因为这个范围的数字是最被广泛使用的。 在程序中,第一次使用Integer的时候也需要一定的额外时间来初始化这个缓存。 Java语言规范中的缓存行为 在Boxing Conversion部分的Java语言规范(JLS)规定如下: 如果一个变量p的值是: -128至127之间的整数(§3.10.1) true 和 false的布尔值 (§3.10.3) ‘\u0000’至 ‘\u007f’之间的字符(§3.10.4) 中时,将p包装成a和b两个对象时,可以直接使用a==b判断a和b的值是否相等。 其他缓存的对象 这种缓存行为不仅适用于Integer对象。我们针对所有的整数类型的类都有类似的缓存机制。 有ByteCache用于缓存Byte对象 有ShortCache用于缓存Short对象 有LongCache用于缓存Long对象 有CharacterCache用于缓存Character对象 Byte, Short, Long有固定范围: -128 到 127。对于Character, 范围是 0 到 127。除了Integer以外,这个范围都不能改变。
技术
# 翻译
酷游
1月22日
0
5
0
2025-01-22
高效学习新技术的10种方法——总结自StackOverflow 2017开发者调查
对于一个软件工程师来说,最大的挑战其实就是不断学习。不用怀疑,因为要想成为一个优秀的开发者,你需要经常学习新东西。我相信,每一个开发人员在C,Java,Linux或者SQL等方面都有一些基础足够你日常工作使用,但是,这些技术本身也一直在发生着变化。比如,在2017年,Java 9问世,虽然有很多人还在学习Java 8应该怎么用。还有,2017年发布了Spring 5.0,Spring Security 5.0 和 Spring Boot 2.0 等,相信这些你都已经列入了2018的学习计划。 简而言之,程序员应该能够快速学习。这种快速学习能力也是面试官在挑选候选人时所要寻找的。 如果你不是一个能够快速学习的人,那么你真的应该思考一下,编程这个行业到时适不适合你。但是学习新技术的秘诀是什么呢?程序员如何每年学习新的框架、库和新技术呢?StackOverFlow’s Developer Survey of 2017也许可以给你一些答案。 下面是我列出的几条关于程序员如何快速学习新知识的方法,你可以用他们学习机器学习、大数据、移动开发。也可以用来学习一种新的编程语言,比如Rust, Groovy, 或者 Kotlin。也可用来学习一个新的框架,比如Spring Security 5, Angular JS 或者 React JS 。或者你只想学习一个简单的类库,比如Jackson。 一、参加在线课程 根据StackOverFlow 2017开发者调查显示,有64.5%的人通过在线课程学习新技术。本人其实也参与过一些在线课程,免费的付费的都有,效果还可以。国内也有类似的在线课程的网站,为了避免广告嫌疑,我就不在这里列举了。 既然这一项在所有开发者投票中排名最高,你不妨试一试,万一适合你呢。在线课程最大的好处就是有人给讲解,最大的缺点并不是所有的东西都有对应的课程,还是需要结合其他的学习方法。 二、看书 这是另一种自己学习新技术或编程语言的最好方法。事实上,这是我所遵循的最古老的也是及其有效的方法。我比较喜欢通过书籍的方式学习新知识,这样可以有足够的时间来思考。在我的博客中也有一个单独的页面记录着我在看的以及看过的书籍。 当我不想阅读的时候我也会选择听书。另外,听书软件默认速度非常慢,很容易让人睡着。我一般以1.5倍或2.0倍的速度听。 最近这一年来,国内的很多听书软件也渐渐的火起来了。我用过很多,一般会使用这些软件听一些文学类的或者杂谈类的内容。也是上周开始用软件听技术类内容,感受还不错。听书的好处就是不需要自己总结,讲书的人一般会给整理好,念给我们听。坏处就是没有太多的思考过程。 如果你正在寻找在2018应该读什么书,我建议从《Effective Java 第三版》开始,1.7日刚刚出版,目前国内好像还没有中文版。 线下课程 说实话,没有任何东西能比得上课堂上的学习,甚至虚拟课堂或在线培训也不行。为什么?因为当你参加一个真正的课程时,你遇到了真正的人,你进入了有利于学习的环境。你和真实的人讨论事情和问题。这意味着你正在做一个积极的学习,你的所有感官,例如眼睛、耳朵、嘴巴、身体和头脑都是活跃的。 这有助于快速学习,你也可以保持较长时间的学习知识。总之,如果你可以的话,你应该参加一个学院或培训学院,利用业余时间或晚上学习一项新技术。 参与开源项目 这是有经验的程序员保持自我进步的最好方法之一。例如,如果您需要学习像Apache Spark或Hadoop这样的大数据框架,那么您也可以作为开源贡献者加入这个项目。 你不仅可以根据你的经验来帮助开源社区,而且也是学习这种新技术的很好的方式。通过和其他几个优秀的程序员交谈也是一个很好增加你的经验的方式。 我强烈推荐中级和有经验的程序员在2018加入开源项目。你也可以用同样的方法在2018学习Rust, Groovy 或者 Kotlin等语言。 编码训练营 这听起来像是一个新鲜事物,但在线课程和编码训练营已经比较普遍了。这更加适合没有经验的人。有很多人没有编程经验,现在可以更容易地过渡到职业生涯,成为一个开发者。 许多初创公司,事实上大的技术公司如谷歌和亚马逊也愿意接受训练营出来的学生。训练营的学生其实比应届高校毕业生更具备必要的技术知识。他们对数据结构以及算法等了解的更深入。对于开发工具,如IntelliJ、版本控制工具如Git,svn等掌握的更好。 总之,如果你想学习新的东西,或者改变你的职业生涯而成为一个程序员,编码训练营是最好的启动方式。 参与在线编码竞赛 现在编码竞赛也比较流程,他也存在一些好处。通过他,你走出了你的舒适区,加速了你的学习。 举个例子,如果你想在2018年学习Python,那么不妨尝试使用Python语言参加有些编码竞赛。你不仅能快速学习所有的基本概念,还能提高你的编码意识,并获得一个非常你需要的真实体验。 参加线下会议 是的,参加会议的帮助很大,这就是为什么我鼓励java开发者加入当地的java社群。这些会议是你发现你附近的优秀的开发者的很好的地方。 这不仅为你创造了一个好的人际关系,而且你从与他们交谈中学到了很多东西。它们还为虚拟的编程世界添加了一个非常需要的真实元素。 如果你不是某个java编程社区的会员,我强烈建议你2018参加一个。 关于会议,我觉得有两种方式,第一种是线下的,如QCon等。第二种也可以是在线的,如微信群等。只要是大家志同道合,可以交流,就是个好的方式。 参与黑客马拉松 这种形式的比赛是另一个快速一个新的框架或库的好方法。在黑客马拉松中,你需要在24到48小时内搞出些有用的东西。 有时候看起来很艰难,但是你一直在前进。黑客马拉松的好处,在于你可以提前为他们准备的,这就是你学习的开始。例如,如果你想使用机器学习算法建立一个原型,如图像处理程序,那么你需要在参加马拉松之前就了解机器学习。单纯的参与,可以给你一个理由去学习和掌握新的技术。并且可以在马拉松这个战场去实践他们。 创建博客 是的,一个博客也可以帮助你学习新的编程语言或框架。因为在写博客之前,你要开始你的研究,而这正是你学习的起点。 例如,我之前没有任何JUnit 5的知识,,但是当我要写关于他的文章的时候,我必须首先了解JUnit 5,这样我就会做一些研究。当我向其他人解释时,我往往学得更好,这就是为什么当我写博客文章时,我的大部分概念都会变得清晰,知识也会被填补。它还帮助我把知识保持的时间更长。 其他 每一个程序员都是不同的,对我有用的可能对你不起作用。有些程序员发现从书中学习很容易,有些人觉得网上课程更好。有些人喜欢创建真实的项目并在途中学习。 简言之,使用适合你的方法。说到学习,没有硬性规定。选一个你喜欢的。如果你在听音乐的时候学习得更好,那么你可以在学习的时候听你最喜欢的歌曲。 10种方法学习一项新技术,编程语言,框架或库 附录:StackOverflow 2017开发者调查
技术
# 翻译
酷游
1月22日
0
6
0
2025-01-22
[译]Java Servlet初级知识
本文由HollisChuang 翻译自 Java Servlet Tutorial with Examples for Beginners . 原作者:Pankaj Kumar 在上一篇文章中,我们了解了Java Web应用程序和Web应用程序中的核心概念,如Web服务器,Web客户端,HTML,HTTP和Web容器以及如何使用Servlet和Jsp创建web应用程序。同时分别使用servlet和jsp创建了可以在tomcat中运行的web应用程序。 这篇文章将主要介绍更多关于setvlet的细节,如servlet中的核心api、servlet3.0中的注解、servlet的生命周期。最后再创建一个简单的servlet应用程序实现登录功能。 Servlet概述 Servlet是在Java中创建web应用程序的J2ee 服务端技术。javax.servlet 和 javax.servlet.http包中提供了编写servlet的接口和类。 所有的servlet都要实现javax.servlet.Servlet接口。该接口中定义了一个Servlet生命周期中的所有方法。如果要实现一个通用的Servlet,可以通过继承Java Servlet API中提供的GenericServlet类。HttpServlet类中提供了用于处理http请求的doGet() 和 doPost()等方法。 多数情况下,web应用程序都使用http协议,所以,我们多数时候都通过继承HttpServlet类来实现自己的Servlet。 通用网关接口(CGI) 在Servlet API产生之前,CGI技术用于创建动态web应用程序。CGI技术有许多缺点,如为每个请求创建单独的进程、依赖于平台代码(C、C++)、内存消耗大和性能低等。 CGI 和 Servlet 对比 Servlet技术的诞生克服了很多CGI技术的缺点: Servlet在处理时间、内存利用率等方面表现更好。因为servlet使用多线程技术,为每个请求创建一个新线程。这自然就比为每个请求创建新进程的CGI技术要快很多,并且节省内存资源。 Servlet是平台无关的,使用Servlet开发的web应用程序可以运行在任何标准的web容器中,如Tomcat、JBoss、Glassfish服务器。同样可以在任何操作系统中,如Windows、Linux、Unix、Solaris、Mac等。 Servlet是健壮的,因为servlet容器负责管理servlet的生命周期,我们不需要担心内存泄漏、安全、垃圾收集等问题。 Servlet易维护的,并且学习曲线小。因为在使用Servlet的时候我们只需要关注业务逻辑就可以了。 Servlet API的层次结构 javax.servlet.Servlet是Servlet Api的最上层接口。还有一些其他的接口和类是我们在使用servlet的时候需要关注的。在Servlet 3.0规范中,建议使用的注解我们也需要了解。 在本节中,我们将学习重要Servlet API接口,类和注释。下面的图显示了servlet API层次结构。 Servlet 接口 javax.servlet.Servlet 是Servlet Api的最上层接口,Servlet接口定义了一系列servlet的生命周期方法(init、service、destory等)。所有的Servlet类都需要继承这个接口。该接口中定义了以下方法: public abstract void init(ServletConfig paramServletConfig) throws ServletException – 该方法由servlet容器调用,用于初始化servlet以及servlet配置参数。在init()方法执行之前,servlet是无法处理用户请求的。在servlet生命周期中该方法只会被调用一次,他会使servlet类不同区别于普通的java对象。我们可以扩展该方法来初始化资源,如数据库连接、socket连接等。 public abstract ServletConfig getServletConfig() – 该方法返回一个servlet配置对象,其中包含servlet中所有初始化参数和启动配置。我们可以用这个方法来获取servlet的初始化参数,这些参数一般被定义在web.xml或servlet 3的注解中。后面会介绍ServletConfig接口。 public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException – 该方法负责处理客户端请求。当servlet容器收到客户端请求时,它会创建一个新线程并执行service()方法,并把request 和 response作为参数传递给该方法。servlet通常运行在多线程环境中,所以开发人员应该使用同步来保证访问共享资源的线程安全性问题。 public abstract String getServletInfo() – 这个方法返回包含servlet信息的字符串,比如它的作者、版本和版权。返回的字符串应该是纯文本,不能有标记符号。 public abstract void destroy() – 这个方法在整个servlet生命周期中只会被调用一次来关闭所有资源。有点像Java中的finalize方法。 ServletConfig 接口 javax.servlet.ServletConfig用于给servlet传递配置信息(译者注:描述Servlet本身的相关配置信息)。每个servlet都有属于它自己的ServletConfig对象,该对象由servlet容器负责实例化。可以在web.xml中提供初始化参数,当然在servlet3.0中可以使用注解。我们可以使用getServletConfig()方法来获取ServletConfig的对象。 ServletConfig接口中主要方法: public abstract ServletContext getServletContext() – 该方法返回servlet的ServletContext对象。在下一节中我们将介绍ServletContext接口。 public abstract Enumeration getInitParameterNames() – 该方法返回servlet中所有初始化参数的名字的枚举。如果没有初始化参数定义,该方法将返回空枚举。 public abstract String getInitParameter(String paramString) – 这种方法可以通过名字来获取特定的初始化参数值。如果参数的名称不存在,则返回null。 ServletContext 接口 javax.servlet.ServletContext接口用于描述应用程序的相关信息。ServletContext是一个独立的对象,可用于web应用程序中所有的servlet。当我们想要一些初始化的参数可用于web应用程序中多个或全部servlet时,我们可以使用ServletContext对象并且在web.xml中使用标签定义参数。我们可以通过ServletConfig 中的 getServletContext()方法得到ServletContext对象。 ServletContext接口中的主要方法: public abstract ServletContext getContext(String uripath) – 这个方法返回指定的uripath的ServletContext对象,如果uripath不可用或不可见则返回null。 public abstract URL getResource(String path) throws MalformedURLException – 返回的一个代表某个资源的URL对象。资源可以是本地文件系统、远程文件系统、数据库,甚至是不知道如何获取资源的具体细节的远程网络站点。 public abstract InputStream getResourceAsStream(String path) – 这个方法返回给定的资源路径的一个输入流对象。如果没有找到返回null。 public abstract RequestDispatcher getRequestDispatcher(String urlpath) – 这个方法一般被用于获得对于另外一个servlet的引用。获取到RequestDispatcher对象之后,就可以通过他把一个请求转发出去(forward或者include)。 public abstract void log(String msg) – 该方法用于把指定的消息字符串写入servlet日志文件中。 public abstract Object getAttribute(String name) – 按照指定的name返回对象属性。可以使用public abstract Enumeration getAttributeNames()活的所有对象属性的枚举。 public abstract void setAttribute(String paramString, Object paramObject) – 该方法用于在应用的范围内设置属性。该属性可以被可以访问当前ServletContext的所有servle获取到。可以使用public abstract void removeAttribute(String paramString)删除一个属性。 String getInitParameter(String name) – 该方法用于返回在web.xml中定义的初始化参数的值。如果指定的name在web.xml中并没有匹配到,则返回null。可以使用Enumeration getInitParameterNames()得到所有初始化参数的名称的枚举。 boolean setInitParameter(String paramString1, String paramString2) – 可以使用该方法设置应用中的初始化参数。 ServletRequest 接口 ServletRequest接口是用来向servlet提供客户端请求信息。每一个客户端请求到达Servlet容器的时候,他都会创建一个ServletRequest对象,并将其传递对应的servlet的service()方法。 ServletRequest接口中的主要方法: Object getAttribute(String name) – 返回指定的参数名对应的属性值。如果对应的参数不存在则返回null。我们可以使用getAttributeNames()方法来获取请求中的所有属性名称的枚举。接口中同样提供了设置值和删除值的方法。 String getParameter(String name) – 以字符串的形式返回请求参数值。我们可以使用getParameterNames()方法来获取请求参数名称的枚举。 String getServerName() – 返回服务器的主机名 int getServerPort() – 返回服务器监听的端口号。 ServletRequest的子接口HttpServletRequest中还包含了一些和session、cookies等相关的方法。 译者注:该接口中提供了getAttribute和getParameter两个方法,都是用于获取参数(属性)值的,那么这两个方法有什么区别呢?或者说Attribute和Parameter的区别是什么呢? 答: 来源不同 参数(parameter)是从客户端(浏览器)中由用户提供的,若是GET方法是从URL中 提供的,若是POST方法是从请求体(request body)中提供的; 属性(attribute)是服务器端的组件(JSP或者Servlet)利用requst.setAttribute()设置的. 操作不同 参数(parameter)的值只能读取不能修改,读取可以使用request.getParameter()读取; 属性(attribute)的值既可以读取亦可以修改,读取可以使用request.setAttribute(),设置可使用request.getAttribute() 数据类型不同 参数(parameter)不管前台传来的值语义是什么,在服务器获取时都以String类型看待,并且客户端的参数值只能是简单类型的值,不能是复杂类型,比如一个对象。 属性(attribute)的值可以是任意一个Object类型。 ServletResponse 接口 servlet使用ServletResponse向客户端发送响应。和每ServletRequest类似,一个客户端请求到达Servlet容器的时候,他都会创建一个ServletResponse对象,并将其传递对应的servlet的service()方法。最终,该response对象用于给客户端生成html响应。 ServletResponse接口中的主要方法: void addCookie(Cookie cookie) – 向响应中添加cookie void addHeader(String name, String value) – 设置响应头 String encodeURL(java.lang.String url) – 通过重写Url的方式支持session,在Url中增加sessionId,如果不需要重写,直接返回该url String getHeader(String name) – 返回指定的头信息。 void sendRedirect(String location)–重定向到指定的地址 void setStatus(int sc) – 设置响应的状态码 RequestDispatcher 接口 RequestDispatcher 接口用于把一个请求转发给同一个servlet上下文中的其他的资源(Html、jsp、servlet)来处理。也可以用它来把另一个资源的内容包含到响应中。此接口用于同一个servlet上下文中的servlet相互沟通。 RequestDispatcher 接口的主要方法: void forward(ServletRequest request, ServletResponse response) – 把一个servlet的请求转发到服务器上的其他资源中(Html、jsp、servlet)。 void include(ServletRequest request, ServletResponse response) – 把另一个资源的内容包含到当前响应中。 译者注:forward和include的区别: 如果使用forward跳转,forward语句后面的response输出则不会执行,会跳转到forward指定的servlet中去执行。 用include来跳转,则include的servlet执行完后,再返回到原来的servlet执行forward语句后面的response的输出。 在servlet中可以使用getRequestDispatcher(String path)来获取一个RequestDispatcher。路径必须以/开头,并且是针对于当前context的根路径的相对地址。 GenericServlet 类 GenericServlet是一个实现类Servlet, ServletConfig 和 Serializable 的抽象类。他提供了Servlet生命周期中的主要方法以及ServletConfig中的方法的默认实现。当我们定义自己的servlet的时候,只要继承了该方法,我们只需要重写我们关注的方法就可以了,其他的不关注的方法都可以使用其默认实现。该类中定义的大部分方法都是让用户更放方便的使用Servlet和ServletConfig接口中定义的常用方法。 GenericServlet 类中有一个重要的方法——无参数的init方法。如果我们必须在处理请求之前初始化一些资源,那么可以重写该方法。 HTTPServlet 类 HTTPServlet 类是GenericServlet类的子类,主要为基于HTTP创建的web应用程序提供了一些支持。其中定义了一些可重写HTTP方法。 doGet(), 用于处理get请求 doPost(), 用于处理post请求 doPut(), 用于处理put请求 doDelete(), 用于处理delete请求 Servlets属性(Attributes) Servlet属性用于servlet之间的沟通,可以在web应用程序中设置、获取甚至删除属性值。servlet属性有三种范围:request、session、application ServletRequest, HttpSession 和 ServletContext接口为request、session和application范围提供了get/set/remove的方法。 Servlet 3中的注解 在servlet 3之前,所以的servlet映射和初始化参数都是定义在web.xml文件中的,随着应用中的servlet数量增多,这种方式就很难维护。 servlet 3中使用支持使用java注解来定义servlet、filter、listener以及初始化参数。 servlet 3 中主要的注解: WebServlet – 可以在servlet类中使用该注解来定义初始化参数、loadOnStartup的值、description信息和url匹配模式(pattern)等。该注解的属性中 vlaue 或者 urlPatterns 通常是必需的,且二者不能共存。该注释声明的类必须继承HttpServlet。 WebInitParam – 该注解用于给servlet 或者 filter定义初始化参数(包括name,value和description)。可以在 WebFilter 或者 WebServlet中使用该注解。 WebFilter – 该注解用于声明一个servlet过滤器。使用该注解声明的类必须实现javax.servlet.Filter接口。 WebListener – 该注解用于声明一个事件监听器。 后续文章会更多的介绍servlet 监听器和过滤器。在本文中,我们的学习重点是Servlet API的接口和类。 使用Servlet实现登录的例子 现在我们准备创建一个有登录功能的servlet实例,在这个例子中,我会使用简单的HTML、jsp和servlet来进行用户的权限校验。我们将用到ServletContext的初始化参数、属性,ServletConfig的初始化参数、RequestDispatcher的include()方法和response的sendRedirect()等方法。 这个Web项目的代码结构应该如下图所示。这里使用Eclipse+Tomcat进行开发及运行。创建项目的过程参见Java Web应用程序初级知识 这是登录页面的代码。我们会把它配置在web.xml 的中标签下。这样,我们一访问这个应用就能跳转到该页面。 login.html Login Page Username: Password: 如果用户登录成功,用户将会在一个新的jsp页面中看到登录成功的提示信息。该jsp页面的代码如下: LoginSuccess.jsp Login Success Page Hi Pankaj, Login successful. Login Page 下面是web.xml文件的配置,其中包含了servlet的初始化、参数、以及欢迎页的配置。 web.xml LoginExample login.html dbURL jdbc:mysql://localhost/mysql_db dbUser mysql_user dbUserPwd mysql_pwd 下面则是Servlet类,他的主要功能就是做用户登录校验。主要关注其中关于Servlet以及ServletConfig的注解的使用。 LoginServlet.java package com.journaldev.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class LoginServlet */ @WebServlet( description = "Login Servlet", urlPatterns = { "/LoginServlet" }, initParams = { @WebInitParam(name = "user", value = "Pankaj"), @WebInitParam(name = "password", value = "journaldev") }) public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void init() throws ServletException { //we can create DB connection resource here and set it to Servlet context if(getServletContext().getInitParameter("dbURL").equals("jdbc:mysql://localhost/mysql_db") && getServletContext().getInitParameter("dbUser").equals("mysql_user") && getServletContext().getInitParameter("dbUserPwd").equals("mysql_pwd")) getServletContext().setAttribute("DB_Success", "True"); else throw new ServletException("DB Connection error"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //get request parameters for userID and password String user = request.getParameter("user"); String pwd = request.getParameter("pwd"); //get servlet config init params String userID = getServletConfig().getInitParameter("user"); String password = getServletConfig().getInitParameter("password"); //logging example log("User="+user+"::password="+pwd); if(userID.equals(user) && password.equals(pwd)){ response.sendRedirect("LoginSuccess.jsp"); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("Either user name or password is wrong."); rd.include(request, response); } } } 下面是页面展示,包括登录成功及失败两种情况。 Servlet的初级教程就介绍到这里了,下一篇文章会介绍session管理、Servlet过滤器和监听器。 [wpdm_package id=’1214′]
技术
# 翻译
酷游
1月22日
0
15
0
2025-01-22
设计模式(十)——建造者模式的实践
新专题:设计模式,我会在博客(http://www.hollischuang.com)及微信公众号(hollischuang)同步更新,欢迎共同学习。 本文由HollisChuang 翻译自 The builder pattern in practice . 原作者:Jose Luis 我不打算深入介绍设计模式的细节内容,因为有很多这方面的文章和书籍可供参考。本文主要关注于告诉你为什么以及在什么情况下你应该考虑使用建造者模式。然而,值得一提的是本文中的模式和GOF中的提出的有点不一样。那种原生的模式主要侧重于抽象构造的过程以达到通过修改builder的实现来得到不同的结果的目的。本文中主要介绍的这种模式并没有那么复杂,因为我删除了不必要的多个构造函数、多个可选参数以及大量的setter/getter方法。 假设你有一个类,其中包含大量属性。就像下面的User类一样。假设你想让这个类是不可变的。 public class User { private final String firstName; //required private final String lastName; //required private final int age; //optional private final String phone; //optional private final String address; //optional ... } 在这样的类中,有一些属性是必须的(required)而另外一些是可选的(optional)。如果你想要构造这个类的实例,你会怎么做?把所有属性都设置成final类型,然后使用构造函数初始化他们嘛?但是,如果你想让这个类的调用者可以从众多的可选参数中选择自己想要的进行设置怎么办? 第一个可想到的方案可能是重载多个构造函数,其中有一个只初始化必要的参数,还有一个会在初始化必要的参数同时初始化所有的可选参数,还有一些其他的构造函数介于两者之间,就是一次多初始化一个可选参数。就像下面的代码: public User(String firstName, String lastName) { this(firstName, lastName, 0); } public User(String firstName, String lastName, int age) { this(firstName, lastName, age, ""); } public User(String firstName, String lastName, int age, String phone) { this(firstName, lastName, age, phone, ""); } public User(String firstName, String lastName, int age, String phone, String address) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.phone = phone; this.address = address; } 首先可以肯定的是,这样做是可以满足要求的。当然,这种方式的缺点也是很明显的。当一个类中只有几个参数的时候还好,如果一旦类中的参数逐渐增大,那么这个类就会变得很难阅读和维护。更重要的是,这样的一个类,调用者会很难使用。我到底应该使用哪个构造方法?是包含两个参数的还是包含三个参数的?如果我没有传递值的话那些属性的默认值是什么?如果我只想对address赋值而不对age和phone赋值怎么办?遇到这种情况可能我只能调用那个参数最全的构造函数,然后对于我不想要的参数值传递一个默认值。此外,如果多个参数的类型都相同那就很容易让人困惑,第一个String类型的参数到底是number还是address呢? 还有没有其他方案可选择呢?我们可以遵循JaveBean规范,定义一个只包含无参数的构造方法和getter、setter方法的JavaBean。 public class User { private String firstName; // required private String lastName; // required private int age; // optional private String phone; // optional private String address; //optional public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } 这种方式看上去很容易阅读和维护。对于调用者来说,我只需要创建一个空的对象,然后对于我想设置的参数调用setter方法设置就好了。这难道还有什么问题吗?其实存在两个问题。第一个问题是该类的实例状态不固定。如果你想创建一个User对象,该对象的5个属性都要赋值,那么直到所有的setXX方法都被调用之前,该对象都没有一个完整的状态。这意味着在该对象状态还不完整的时候,一部分客户端程序可能看见这个对象并且以为该对象已经构造完成。这种方法的第二个不足是User类是易变的(因为没有属性是final的)。你将会失去不可变对象带来的所有优点。 幸运的是应对这种场景我们有第三种选择,建造者模式。解决方案类似如下所示: public class User { private final String firstName; // required private final String lastName; // required private final int age; // optional private final String phone; // optional private final String address; // optional private User(UserBuilder builder) { this.firstName = builder.firstName; this.lastName = builder.lastName; this.age = builder.age; this.phone = builder.phone; this.address = builder.address; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public int getAge() { return age; } public String getPhone() { return phone; } public String getAddress() { return address; } public static class UserBuilder { private final String firstName; private final String lastName; private int age; private String phone; private String address; public UserBuilder(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public UserBuilder age(int age) { this.age = age; return this; } public UserBuilder phone(String phone) { this.phone = phone; return this; } public UserBuilder address(String address) { this.address = address; return this; } public User build() { return new User(this); } } } 值得注意的几个要点: User类的构造函数是私有的,这意味着你不能在外面直接创建这个类的对象。 该类是不可变的。所有属性都是final类型的,在构造方法里面被赋值。另外,我们只为它们提供了getter方法。 builder类使用流式接口风格,让客户端代码阅读起来更容易(我们马上就会看到一个它的例子) builder的构造方法只接收必要的参数,为了确保这些属性在构造方法里赋值,只有这些属性被定义成final类型。 使用建造者模式有在本文开始时提到的两种方法的所有优点,并且没有它们的缺点。客户端代码写起来更简单,更重要的是,更易读。我听过的关于该模式的唯一批判是你必须在builder类里面复制类的属性。然而,考虑到这个事实,builder类通常是需要建造的类的一个静态类成员,它们一起扩展起来相当容易。(译者表示没明白为设定为静态成员扩展起来就容易了。设为静态成员我认为有一个好处就是可以避免出现is not an enclosing class的编译问题,创建对象时候更加方便) 现在,试图创建一个新的User对象的客户端代码看起来如何那?让我们来看一下: public User getUser() { return new User.UserBuilder("Jhon", "Doe") .age(30) .phone("1234567") .address("Fake address 1234") .build(); } 译者注:如果UserBuilder没有设置为static的,以上代码会有编译错误。错误提示:User is not an enclosing class 以上代码看上去相当整洁。我们可以只通过一行代码就可以创建一个User对象,并且这行代码也很容易读懂。除此之外,这样还能确保无论何时你想获取该类的对象都不会是不完整的(译者注:因为创建对象的过程是一气呵成的,一旦对象创建完成之后就不可修改了)。 这种模式非常灵活,一个单独的builder类可以通过在调用build方法之前改变builder的属性来创建多个对象。builder类甚至可以在每次调用之间自动补全一些生成的字段,例如一个id或者序列号。 值得注意的是,像构造函数一样,builder可以对参数的合法性进行检查,一旦发现参数不合法可以抛出IllegalStateException异常。 但是,很重要的一点是,如果要检查参数的合法性,一定要先把参数传递给对象,然后在检查对象中的参数是否合法。其原因是因为builder并不是线程安全的。如果我们在创建真正的对象之前验证参数,参数值可能被另一个线程在参数验证完和参数被拷贝完成之间的时间修改。这段时间周期被称作“脆弱之窗”。我们的例子中情况如下: public User build() { User user = new user(this); if (user.getAge() > 120) { throw new IllegalStateException(“Age out of range”); // thread-safe } return user; } 上一个代码版本是线程安全的因为我们首先创建user对象,然后在不可变对象上验证条件约束。下面的代码在功能上看起来一样但是它不是线程安全的,你应该避免这么做: public User build() { if (age > 120) { throw new IllegalStateException(“Age out of range”); // bad, not thread-safe } // This is the window of opportunity for a second thread to modify the value of age return new User(this); } 建造者模式最后的一个优点是builder可以作为参数传递给一个方法,让该方法有为客户端创建一个或者多个对象的能力,而不需要知道创建对象的任何细节。为了这么做你可能通常需要一个如下所示的简单接口: public interface Builder { T build(); } 借用之前的User例子,UserBuilder类可以实现Builder。如此,我们可以有如下的代码: UserCollection buildUserCollection(Builder userBuilder){...} 译者注:关于这这最后一个优点的部分内容并没太看懂,希望有理解的人能过不吝赐教。 好吧,这确实是一篇很长的文章。总而言之,建造者模式在多于几个参数(虽然不是很科学准确,但是我通常把四个参数作为使用建造者模式的一个很好的指示器),特别是当大部分参数都是可选的时候。你可以让客户端代码在阅读,写和维护方面更容易。另外,你的类可以保持不可变特性,让你的代码更安全。 UPDATE:如果你使用eclipse开发,你有很多插件来避免编写建造者模式大部分的重复代码。已知的有下面三个: http://code.google.com/p/bpep/http://code.google.com/a/eclipselabs.org/p/bob-the-builder/http://code.google.com/p/fluent-builders-generator-eclipse-plugin/ 这几个插件我都没有使用过,所以关于哪个更好,我无法给出好的建议。我估计其他IDEs也会存在类型的插件。
技术
# 翻译
酷游
1月22日
0
20
0
易航博客