酷游 发布的文章 - 第 110 页 - 酷游博客
首页
关于
友链
Search
1
阿里的简历多久可以投递一次?次数多了有没有影响?可以同时进行吗?
45 阅读
2
Java中泛型的理解
40 阅读
3
Java 14 发布了,再也不怕 NullPointerException 了!
38 阅读
4
Java中的可变参数
37 阅读
5
该如何创建字符串,使用" "还是构造函数?
29 阅读
技术
登录
/
注册
找到
556
篇与
酷游
相关的结果
- 第 110 页
2025-01-22
[译]空引用真的有那么糟糕吗?
本文翻译自国外编程问答网站Programmers Stack Exchange中的一个热门问答:Are null references really a bad thing? 问题 我曾听说有人说过,包含空引用的编程语言是一个价值十亿美元的错误(译者注:图灵奖得主Tony Hoare说过)。但是为什么呢?当然,他们可能会导致NullReferenceException,但那又怎样?只要使用不当,一个语言的任何一个元素都可导致错误啊。 还有其他的选择吗?我一般在代码中这么写: Customer c = Customer.GetByLastName("Goodman"); // returns null if not found if (c != null) { Console.WriteLine(c.FirstName + " " + c.LastName + " is awesome!"); } else { Console.WriteLine("There was no customer named Goodman. How lame!"); } 你可能这么用: if (Customer.ExistsWithLastName("Goodman")) { Customer c = Customer.GetByLastName("Goodman") // throws error if not found Console.WriteLine(c.FirstName + " " + c.LastName + " is awesome!"); } else { Console.WriteLine("There was no customer named Goodman. How lame!"); } 但你的做法又比我的好在哪里呢?无论哪种方式,如果你忘了检查Customer是否存在,你会得到一个异常。我猜测可能是因为调试CustomerNotFoundException可能比NullReferenceException更容易,因为他描述的更清楚。这就是全部原因吗? 最佳答案 null是邪恶的(译者注:回答者在回答提问者的关于为什么空引用能导致十亿美金的损失的问题) 在infoQ上有一篇关于这个的演讲:Null References: The Billion Dollar Mistake by Tony Hoare Option类型(译者注:回答者告诉提问者可以使用什么方式避免使用空引用) 在函数式编程中有一个选择就是使用Option类型,他可以包含某个值和NONE The “Option” Pattern讨论了Option类型,并介绍了java中的实现。(译者注:在java8中提供了Optional类型) 我还发现了一个Java中关于这个问题的错误报告:Add Nice Option types to Java to prevent NullPointerExceptions. 另一个高分回答 问题在于,理论上任何一个对象都有可能是null,并且当你试图使用他的时候就会抛出异常。你的面向对象代码基本上就是一个定时炸弹。 你是对的,使用if语句进行非空检查是一种优雅的处理方式。但是如果一个你确信不可能为null的对象的值为null了会怎么样?敢肯定的有两件事。1、这绝对不是一件好事儿。2、你肯定不希望他发生。 还有,千万不要忽视”容易debug”这个好处,成熟的代码都是庞然大物。一个好的错误提示会节省你几个小时的时间。
技术
# Java
酷游
1月22日
0
14
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
2025-01-22
Java Web应用的代码分层最佳实践。
代码分层,对于任何一个Java Web开发来说应该都不陌生。一个好的层次划分不仅可以能使代码结构更加清楚,还可以使项目分工更加明确,可读性大大提升,更加有利于后期的维护和升级。 从另外一个角度来看,好的代码分层架构,应该是可以很好的匹配上单一职责原则的。这样就可以降低层与层之间的依赖,还能最大程度的复用各层的逻辑。本文就来介绍下Java Web项目的代码到底应该如何分层。 三层架构 在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层、业务逻辑层(又或称为领域层)、表示层。这也是Java Web中重要的三层架构中的三个层次。区分层次的目的即为了“高内聚低耦合”的思想。 所谓三层体系结构,是在客户端与数据库之间加入了一个“中间层”,也叫组件层。这里所说的三层体系,不是指物理上的三层,不是简单地放置三台机器就是三层体系结构,也不仅仅有B/S应用才是三层体系结构,三层是指逻辑上的三层,即把这三个层放置到一台机器上。 数据访问层 主要是对非原始数据(数据库或者文本文件等存放数据的形式)的操作层,而不是指原始数据,也就是说,是对数据库的操作,而不是数据,具体为业务逻辑层或表示层提供数据服务。 业务逻辑层 主要是针对具体的问题的操作,也可以理解成对数据层的操作,对数据业务逻辑处理,如果说数据层是积木,那逻辑层就是对这些积木的搭建。 界面层 主要表示WEB方式。如果逻辑层相当强大和完善,无论表现层如何定义和更改,逻辑层都能完善地提供服务。 三层架构与MVC的区别 MVC(模型Model-视图View-控制器Controller)是一种架构模式,可以用它来创建在域对象和UI表示层对象之间的区分。 同样是架构级别的,相同的地方在于他们都有一个表现层,但是他们不同的地方在于其他的两个层。 在三层架构中没有定义Controller的概念。这是最不同的地方。而MVC也没有把业务的逻辑访问看成两个层,这是采用三层架构或MVC搭建程序最主要的区别。 更加细致的分层 随着网站的用户量的不断提升,系统架构也在不断的调整。有时候,随着业务越来越复杂,有时候三层架构好像不够用了。比如,我们的应用除了要给用户提供页面访问以外,还需要提供一些开放接口,供外部系统调用。这个接口既不属于界面层,也不应该属于业务逻辑层,因为他还可能包含一些和业务逻辑无关的处理,如权限控制、流量控制等。 还有,随着微服务的盛行,我们应用中可能要依赖很多外部接口或第三方平台。这部分代码放下业务逻辑层和数据访问层也都不合适。 所以,渐渐的,在三层架构的基础上,系统架构的分层变得更加复杂了。也正是因为复杂,就非常考验架构设计能力,因为层次划分的不好,很可能会影响后面的开发,给代码维护带来很大的困难。 下图,是阿里巴巴(参考《阿里巴巴Java开发手册》)提倡的应用分层结构: 开放接口层 可直接封装 Service 方法暴露成 RPC 接口;通过 Web 封装成 http 接口;进行网关安全控制、流量控制等。 终端显示层 各个端的模板渲染并执行显示的层。当前主要是 velocity 渲染,JS 渲染,JSP 渲染,移动端展示等。 Web 层 主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。 Service 层 相对具体的业务逻辑服务层。 Manager 层 通用业务处理层,它有如下特征: 1) 对第三方平台封装的层,预处理返回结果及转化异常信息; 2) 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理; 3) 与 DAO 层交互,对多个 DAO 的组合复用。 DAO 层 数据访问层,与底层 MySQL、Oracle、Hbase 等进行数据交互。 外部接口或第三方平台 包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口。 事务处理 在了解了分层之后,我们再来看一下写Java Web代码的时候,大家比较关心的一个问题,那就是涉及到数据库操作的时候,事务处理应该在哪一层控制呢? 关于这个问题,仁者见仁,智者见智。作者认为,事务处理应该放在Service层和Manager层。 DAO层不应该有事务,应该只是很纯的 CRUD 等比较通用的数据访问方法。一个DAO应该只处理和自己相关的操作,不要有任何组合。组合的事情交给上层。 Service层和Manager层一般会组合多个DAO的CRUD操作,例如:在注册一个用户的时候需要往日志表里 INSERT 日志,那么就在 Service 层构造事务,在该事务中调用 Dao 层的 User.Insert () 与 Log.Insert ()。 异常处理 异常处理是Java中比较重要的一个话题,在《Effective Java》中有很多关于异常处理的最佳实践,这里不详细介绍了,本文主要简单说一下在应用代码分层之后,各个层次之间的异常应该如何处理,是自己捕获,还是向上一层抛出。 首先,每一层都是可能发生异常的。由于每一层的职责都不通,处理方式也可能有差别。 DAO层 在 DAO 层,产生的异常类型可能有很多,可能是SQL相关的异常,也可能是数据库连接相关的异常。 这一层的处理方式可以简单一点,直接try-catch(Exception),然后封装成DAOException抛给上一层。这一层一般不需要打印日志,交给Service或者Manager层来打印。 try{ CRUD }catch(Exception e){ throw new DAOException(e); } Manager/Service 首先,对于DAO层抛上来的异常一定要捕获的,并且记录日志打印现场。 但是值得注意的是,如果是需要事务控制的方法,要注意捕获到异常之后再向上抛一个新的异常,如 TransactionRolledbackException,否则事务无法回滚。 这两层发生的异常可以根据情况决定是继续向上抛还是自己处理掉。如果是自己可以处理的异常,就捕获,打日志,然后通过ErrorCode等方式返回给上一层。如果是自己无法处理或者不知道该如何处理的异常,就直接抛给上一层来处理。 Web 首先,可以明确的一点:Web层不应该再往外抛异常,因为这一层一旦抛异常,就可能会导致用户跳转到不友好的错误页面甚至看到错误信息等。 如果意识到这个异常将导致页面无法正常渲染,那么就应该直接跳转到友好错误页面,加上用户容易理解的错误提示信息。 开放接口层 这一层和Web层一样,不可以抛出异常。一般通过ErrorCode和ErrorMessage反馈给外部调用方。 这一层,要自己处理好所有的异常,定义好ErrorCode,并记录好日志,便于日后排查问题。 总结 本文主要介绍了Java Web项目中代码分层的方案,通过分层之后可以使没一层更加专注,解除耦合。并简单介绍了一下分层之后的事务处理和异常处理的逻辑。
技术
# Java
酷游
1月22日
0
16
0
2025-01-22
使用Dozer优雅的将DO转换成VO
在Web开发中,我们会接触到很多领域模型中的概念,其中大部分和实体相关的概念都有缩写,一般以O(Object)结尾。其中比较常见的由DO、DTO、VO、DAO等。我们也经常有把一个实体对象转换为另外一个实体对象的操作。本文主要是介绍一种作者在实践中总结的一种自认为比较优雅的转换方式。欢迎拍砖。 什么是DO、DTO和VO 在Java中 VO、 PO、DO、DTO、 BO、 QO、DAO、POJO的概念中介绍过Java中的各种模型概念。在这里简单再总结一下: 在日常的项目开发中,VO对应于页面上需要显示的数据(表单),DO对应于数据库中存储的数据(数据表),DTO对应于除二者之外需要进行传递的数据。 很多人可能对VO和DTO并不是那么熟悉,相反对DO却比较熟悉,那是因为在很多项目中由于种种原因我们只使用了DO,原因可能有以下几种: 1、项目太小,对于一种业务实体,封装成一个DO就够了。 2、并不熟悉DTO、VO,更不知道他们之间的区别。 3、了解DO\DTO\VO之间的区别,但是懒得用。 那么,这里,博主再啰嗦一下为什么要引入这么多概念,为什么我要建议大家在自己的项目中使用这些实体对象。 为什么不能只用一个DO 我们来看这样一个例子。假如我们的项目中由User这样一个实体。我们在创建User表的时候一般包含一下属性: 针对这个实体,我们通常需要创建一个DO类,用来封装这个User实体。 public class UserDO { private Integer id; //唯一主键 private Date createdTime; //创建时间 private Date updatedTime; //最后更新时间 private String name; //姓名 private Integer age; //年龄 private String gender; //性别 private String address; //住址 private String password; //密码 private String nickName; //昵称 private Date birthday; //生日 private String politicalStatus; //政治面貌,1表示群众,2表示团员,3表示党员,4表示其他,100表示未知 private Integer companyId; //公司的ID private Integer status; //数据状态,1表示可用,0表示不可用 //setter and getter } 然后,在代码中,从DAO一直到前端展示,我们都通过这个UserDO类的对象来进行数据传输。这样做会有什么问题嘛? 不需要的字段也会传递到前端页面。 如password、createdTime、updatedTime和status这几个字段我们可能在前端根本不需要展示,但是这些字段有可能也会被传递到前端(除非我们在SQL查询的时候没有查询出对应的字段)。这不仅使数据的传输量增大,还可能有安全性问题。 某些字段需要转换,但是无法支持。 对于上面例子中的政治面貌字段,我们在数据库中存储的是数字,但是在前端页面我要展示的是中文描述。这种情况只能在前端通过if/else的方式来分情况展示。 某些字段要展示,但是并不希望出现在数据库中 在User表中我们只保存了这个用户的companyId,需要同时查询company表来查询出该公司的更多详细信息。对于User对象,如果我们想在前端同时展示他所属的公司,希望通过一次查询全都查出来怎么办?有几个简单的方案,第一个是让UserDO中包含一个Company类的属性,通过这个属性来传递。另外一种是把我们希望传到前端的Company的属性也写到UserDO中。但是,如果真的这么做了,那UserDO还能被称作DO了吗? 还有很多问题,这这里就不详细介绍了。 可见,使用一个DO从头用到尾(从数据库到前端页面)并不是一种好的设计。 如何正确的使用DO、DTO、VO 对于上面的例子,我们可以将他设计成以下类。由于模型并不复杂,这里只需要再引入VO就可以了。 UserDO已经和数据库字段一一对应了,这里不需要修改。 public class UserDO { private Integer id; //唯一主键 private Date createdTime; //创建时间 private Date updatedTime; //最后更新时间 private String name; //姓名 private Integer age; //年龄 private String gender; //性别 private String address; //住址 private String password; //密码 private String nickName; //昵称 private Date birthday; //生日 private String education; //学历 private String politicalStatus; //政治面貌,1表示群众,2表示团员,3表示党员,4表示其他,100表示未知 private Integer companyId; //公司的ID private Integer status; //数据状态,1表示可用,0表示不可用 //setter and getter } 引入UserVO,用于封装传递到前端需要展示的字段。 public class UserVO { private Integer id; //唯一主键 private String name; //姓名 private Integer age; //年龄 private String gender; //性别 private String address; //住址 private String nickName; //昵称 private Date birthday; //生日 private String education; //学历 private String politicalStatus; //政治面貌,群众、团员、党员等 private Company company; //公司 //setter and getter } UserVO中只包含了展示所需要的字段,并不需要展示的字段在这里不需要包含。 在引入了这个类之后,我们就可在进行数据库查询的时候使用UserDO,然后再需要传递到前端的时候把DO转换成VO。 总结 看到这里,你可能已经发现,UserDO和UserVO中包含了大量的相同字段。难道真的有必要再单独设计个VO嘛?我可以明确告诉你的是,当你的系统越来越大,表中的字段越来越多的时候,使用DO\DTO\VO等概念进行分层处理是绝对有好处的。至于如何进行有效的在不同的实体类间进行转换是我接下来要介绍的。 优雅的将DO转换成VO Dozer 是一个对象转换工具。 Dozer可以在JavaBean到JavaBean之间进行递归数据复制,并且这些JavaBean可以是不同的复杂的类型。所有的mapping,Dozer将会很直接的将名称相同的fields进行复制,如果field名不同,或者有特别的对应要求,则可以在xml中进行定义。 前面我们介绍的DO\DTO\VO不就是JavaBean嘛。正好可以使用Dozer进行转换呀。除了使用Dozer,当然你还由其他选择: 典型的解决方案就是手动拷贝,弊端很明显,代码中充斥大量Set 和Get方法,真正的业务被埋藏值与值的拷贝之中。 另一种方案就是使用BeanUtil,但BeanUtil不够很好的灵活性,又时候还不得不手动拷贝。Dozer可以灵活的对对象进行转换,且使用简单。 Dozer 支持的转换类型 Primitive 基本数据类型 , 后面带 Wrapper 是包装类 Complex Type 是复杂类型 • Primitive to Primitive Wrapper • Primitive to Custom Wrapper • Primitive Wrapper to Primitive Wrapper • Primitive to Primitive • Complex Type to Complex Type • String to Primitive • String to Primitive Wrapper • String to Complex Type if the Complex Type contains a String constructor • String 到复杂类型 , 如果复杂类型包含一个 String 类型的构造器 • String to Map • Collection to Collection • Collection to Array • Map to Complex Type • Map to Custom Map Type • Enum to Enum • Each of these can be mapped to one another: java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp, java.util.Calendar, java.util.GregorianCalendar • String to any of the supported Date/Calendar Objects. • Objects containing a toString() method that produces a long representing time in (ms) to any supported Date/Calendar object. 在普通Java项目中使用Dozer 在pom.xml中增加依赖 net.sf.dozer dozer 5.5.1 使用Dozer进行类转换 public class Main { public static void main(String[] args) { DozerBeanMapper mapper = new DozerBeanMapper(); UserDO userDO = new UserDO(); userDO.setName("hollis"); userDO.setAddress("hz"); userDO.setAge(25); userDO.setCompanyId(1); userDO.setBirthday(new Date()); userDO.setGender("male"); userDO.setEducation("1"); userDO.setNickName("hollis"); userDO.setPoliticalStatus("3"); UserVO userVO = (UserVO) mapper.map(userDO, UserVO.class); System.out.println(userVO); } } 特殊的字段转换在使用mapper进行转换前,设置一个或多个mapping文件 List myMappingFiles = new ArrayList(); myMappingFiles.add("dozer-mapping.xml"); mapper.setMappingFiles(myMappingFiles); 配置dozer-mapping.xml文件 数据类型不一致,或者名称不相同或者有级联关系等情况下,可以通过文件dozer-mapping.xml来进行定制Bean的转换 com.hollis.lab.UserDO com.hollis.lab.UserVO 在JavaWeb项目中使用Dozer 在pom.xml中增加依赖 net.sf.dozer dozer 5.5.1 使用Spring集成dozer classpath:mapping/dozer-mapping.xml 使用baseMapper进行Bean的转换 @Autowired private Mapper baseMapper; private UserVO doToVo(UserDO userDO){ if(userDO == null) return null; UserVO vo = baseMapper.map(userDO, UserVO.getClass()); if(userDO.getCompanyId != null) getCompany(vo); return vo; } 通过以上的代码加配置,我们就实现了从DO转换到VO的部分操作,之所以说是部分操作,是因为我们在dozer-mapping.xml并没有做多余的配置,只是使用dozer将DO中和VO中共有的属性转换了过来。对于其他的类型不同或者名称不同等的转换可以参考官网例子通过设置dozer-mapping.xml文件来实现。 上面还有一个getCompany()没有实现。这个方法其实就是通过companyId查询出company实体然后在赋值给UserVO中的company属性。 在使用了dozer之后,我们可以把UserDO中的部分属性赋值到UserVO中,其实,转化的过程是通过调用UserDO中的getter方法和UserVO中的setter方法来实现的。读者可以做个实验,对于UserVO中的部分属性不写Setter方法看看还能不能把属性值转换过来,博主已经测试过了,是不能的。
技术
# 工具类
酷游
1月22日
0
6
0
2025-01-22
你离BAT之间,只差这一套Java面试题。
最近,各大公司开始了春招,很多人已经开始在准备面试了,特地来总结下初中级程序员应该掌握的面试题目。这篇面试指南,只适用于初中级程序员,其中不涉及分布式等问题。关于中高级的程序员问题,我后面可能再出一篇文章。 对于一个初中级程序员来说,面试问题不仅仅涉及到Java语言,还会包括很多其他知识,比如计算机基础知识(数据结构、计算机网络、操作系统等)、C语言基础、Java底层知识以及一些框架相关知识等。本文几乎覆盖到了所有领域。 计算机基础知识 C语言基础 Java基础 Java高级 Java Web 设计模式 知识的综合能力 工具使用 项目相关 技术热情 表达能力 思考方式 其他 推荐阅读 还有,我知道很多人会问关于这些题目的答案问题。答案我都有,由于篇幅有限没办法直接贴上来。这些题目的答案我会在我的公众号及知识星球中给出,感谢关注。 为了方便,我把他们分了类,有一些是必看的,我用!标注,有一些进阶型的我用%标注,有一些需要了解的,我用?标注。 必会关键字 void byte int long char short float double String StringBuffer StringBuilder Array Collection Collections List ArrayList LinkedList Vector Set HashMap TreeMap LinkedHashMap ConcerrentHashMap Set TreeMap HashMap synchronized volatile transient implements extends public private protected this super static final const run start thread enmu stack queue list heap throw throws try catch finally break continue instanceof 计算机基础知识 数据结构 !1、什么是队列、栈、链表 !2、什么是树(平衡树,排序树,B树,B+树,R树,红黑树)、堆(大根堆、小根堆)、图(有向图、无向图、拓扑) !3、栈和队列的相同和不同之处 ?4、栈通常采用的两种存储结构 %5、两个栈实现队列,和两个队列实现栈 算法 !1、排序都有哪几种方法? !2、会写常用的排序算法,如快排,归并等。 %3、各种排序算法的时间复杂度和稳定性 ,重点快排。 !4、单链表的遍历和逆序 !5、深度优先搜索和广度优先搜索 ?6、最小生成树 !7、常见Hash算法,哈希的原理和代价 %8、全排列、贪心算法、KMP算法、hash算法 ?9、一致性Hash算法 操作系统 ?1、虚拟内存管理 ?2、换页算法 !3、进程间通信 ?4、进程同步:生产者消费者问题、哲学家就餐问题、读者写者问题 !5、死锁的四个必要条件,避免方法 !6、Linux的一些基本命令,如ls、tail、chmod等 计算机网络 !1、tcp,udp区别 !2、HTTP请求和响应的全过程 !3、HTTP常见响应码:200、301、302、404、500 !4、get和post的区别 !5、forward和redirect的区别 !6、osi七层模型 !7、tcp/ip四层模型及原理 !8、TCP和UDP区别 !9、TCP的三次握手,四次关闭 %10、丢包,粘包, ?11、容量控制,拥塞控制 ?12、子网划分 %13、IPV4和IPV6 ?14、HTTPS和HTTP/2 数据库: !1、范式 !2、数据库事务和隔离级别 !3、为什么需要锁,锁定分类,锁粒度 %4、乐观锁,悲观锁的概念及实现方式 !5、分页如何实现(Oracle,MySql) !6、Mysql引擎 ?7、MYSQL语句优化 %8、从一张大表读取数据,如何解决性能问题 !9、内连接,左连接,右连接作用及区别 !10、Statement和PreparedStatement之间的区别 %11、索引以及索引的实现(B+树介绍、和B树、R树区别 ?12、什么是数据库连接池 海量数据处理 %1、海量日志数据,如何提取出某日访问淘宝次数最多的IP %2、上亿数据,统计其中出现次数最多的前N个数据 %3、5亿个int,找出他们的中位数 %4、两个文件,各存放50亿条URL,每个URL占64字节。内存限制是4G,找出两个文件中相同的URL %5、有40亿个不重复的unsigned int的整数,没排过序,现在给一个数,如何快速判断这个数是否在这40亿个数当中。 ?6、提示:分治、Hash映射、堆排序、双层桶划分、Bloom filter、bitmap、数据库索引、mapreduce C语言基础 构造函数、析构函数 !1、构造函数和析构函数 %2、为什么不要在构造器中调用虚函数 %3、为什么不要在析构函数中抛出异常 c++相关 !1、面向对象的三大基本特征,五大基本原则 %2、C++继承的内存布局 !3、C++多态的实现机制 !4、new/deletr和malloc/free的区别 其他 !1、为什么使用补码 %2、C语言中的内存泄漏 !3、进制转换 % 4、自己编写strlen/strcpy/strcmp ! 5、C、C++以及Java之间的区别和各自优缺点 Java基础 封装、继承、多态 !1、Java中实现多态的机制是什么,动态多态和静态多态的区别 !2、接口和抽象类的区别,如何选择 !3、Java能不能多继承,可不可以多实现 %4、Static Nested Class 和 Inner Class的不同 !5、重载和重写的区别。 !6、是否可以继承String类 !7、构造器是否可被override? !8、public,protected,private的区别? 集合相关 !1、列举几个Java中Collection类库中的常用类 !2、List、Set、Map是否都继承自Collection接口?存储特点分别是什么? !3、ArrayList、LinkedList和Vector之间的区别与联系 !4、HashMap和Hashtable、TreeMap以及ConcurrentHashMap的区别 !5、Collection 和 Collections的区别 %6、其他的集合类:treeset,linkedhashmap等。 异常相关 !1、Error和Exception的区别 !2、异常的类型,什么是运行时异常 !3、final、finally和finalize的区别 %4、try-catch-finally中,如果在catch中return了,finally中的代码还会执行么,原理是什么? !5、列举3个以上的RuntimeException !6、Java中的异常处理机制的简单原理和应用 其它 !1、String和StringBuffer、StringBuilder的区别 !2、==和equals的区别 %3、hashCode的作用,和equals方法的关系 !4、Input/OutputStream和Reader/Writer有什么区别 !5、如何在字符流和字节流之间转换? !6、switch可以使用那些数据类型 %7、Java的四种引用 !8、序列化与反序列化 !9、正则表达式 !10、int和Integer的区别,什么是自动装箱和自动拆箱 Java高级 多线程 !1、进程和线程的区别 !2、并行和并发的区别和联系 !3、同步与异步 !4、多线程的实现方式,有什么区别 !5、什么叫守护线程 %6、如何停止一个线程? !7、什么是线程安全? !8、synchronized 和 lock的区别 !9、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? !10、启动一个线程是用run()还是start()? !12、wait和sleep的区别 %13、notify和notifyAll的区别 %14、线程池的作用 %15、Java中线程池相关的类 JVM底层技术 !1、gc的概念,如果A和B对象循环引用,是否可以被GC? %2、jvm gc如何判断对象是否需要回收,有哪几种方式? !3、Java中能不能主动触发GC !4、JVM的内存结构,堆和栈的区别 !5、JVM堆的分代 %6、Java中的内存溢出是什么,和内存泄露有什么关系 !7、Java的类加载机制,什么是双亲委派 !8、ClassLoader的类加载方式 IO ! 1、NIO、AIO和BIO 之间的区别 ?2、IO和NIO常用用法 其它 ?1、hashcode 有哪些算法 !2、反射的基本概念,反射是否可以调用私有方法 !3、Java中范型的概念 ?4、JVM启动参数,-Xms和 -Xmx %5、代理机制的实现 !6、String s = new String("s"),创建了几个对象。 Java Web Servlet !1、JSP和Servlet的区别,Servelt的概念。 !2、Servlet的生命周期 !3、Servlet中的session工作原理 ,以及设置过期时间的方式 !4、Servlet中,filter的应用场景有哪些? ?5、JSP的动态include和静态include %6、web.xml中常用配置及作用 %7、Servlet的线程安全问题 MVC框架 !1、介绍几个常用的MVC框架 !2、什么是MVC !3、Struts中请求的实现过程 %4、Spring mvc与Struts mvc的区别 ?5、Service嵌套事务处理,如何回滚 !6、struts2 中拦截器与过滤器的区别及执行顺序 %7、struts2拦截器的实现原理 http相关 !1、session和cookie的区别 !2、HTTP请求中session实现原理? %3、如果客户端禁止Cookie能实现Session吗? !4、http中 get和post区别 !5、redirect与forward的区别 !6、常见的web请求返回的状态码。404、302、301、500分别代表什么 SSH相关 ?1、Hibernate/Ibatis/MyBatis之间的区别 ?2、什么是OR Mapping %3、hibernate的缓存机制、一级和二级缓存 !4、使用Spring的好处是什么,Spring的核心理念 !5、什么是AOP和IOC,实现原理是什么 !6、spring bean的初始化过程 !7、Spring的事务管理 ,Spring bean注入的几种方式 %8、spring四种依赖注入方式 容器相关 !1、什么是web服务器、什么是应用服务器 !2、常用的web服务器有哪些? ?3、Tomcat和weblogic的区别 web安全 !1、什么是SQL注入 ,如何避免。 %2、什么是XSS攻击,如何避免 %3、什么是CSRF攻击,如何避免 动态代理 !1、Java的动态代理的概念 %2、Java的动态代理的实现 编码问题 !1、常用的字符编码 !2、如何解决中文乱码问题 其它 %1、XML的解析方式,以及优缺点。 %2、什么是ajax,Ajax如何解决跨域问题 设计模式 %1、谈一下自己了解或者熟悉的设计模式 !2、Singleton的几种实现方式,实现一个线程安全的单例。 ?3、工厂模式和抽象工厂模式之间的区别 知识的综合能力 !1、请介绍一下一个http请求的全过程,描述的越全面越好 !2、当你在浏览器地址栏输入www.taobao.com,敲下回车之后都发生了什么 工具使用 !1、知道git/svn是干什么的吗?用过吗 !2、知道maven/gradle是干什么的吗?用过吗 !3、平常使用什么IDE,为什么 !4、平常使用什么浏览器,为什么 !5、平常开发机器是什么操作系统的 !6、会在Linux上开发吗。Linux常用命令会吗 项目相关 !1、请简单介绍一下你的这个项目 !2、你在这个项目中充当什么角色 !3、这个项目的技术选型有做过么。 !4、选择某项技术做过哪些调研和对比 !5、这个项目中遇到的最大的问题是什么?你是如何解决的。 !6、项目中是否考虑过性能、安全性等问题 技术热情 !1、当前Java的最新版本 !2、Java8的lambda表达式 %3、Java8的stream API %4、Java9的模块化 %5、Java10的局部变量类型推断 %6、Spring Boot2.0 %7、HTTP/2 %8、会翻墙么,知道翻墙的原理吗 !9、你最近在读什么书 表达能力 !1、能不能简单做一个自我介绍。 !2、能不能描述一下杭州给你的印象。用三句话概括一下。 思考方式 !1、如何估算杭州有多少软件工程师 !2、你最近读过的印象最深的文章是什么 !3、这篇文章中有几个观点,你最赞成哪一个,最不赞成哪一个 其他 !1、你对加班怎么看 !2、你还有什么问题要问我(面试官)的么 推荐阅读 程序员面试笔试宝典 程序员面试金典 Java编程思想 Effective Java 深入理解Java虚拟机 大话数据结构 剑指Offer !Hollis技术博客(http://www.hollischuang.com)及个人公众号(Hollis)
技术
# Java
酷游
1月22日
0
6
0
上一页
1
...
109
110
111
112
下一页
易航博客