Java编程常见问题汇总.docx
- 文档编号:3639475
- 上传时间:2022-11-24
- 格式:DOCX
- 页数:29
- 大小:32.69KB
Java编程常见问题汇总.docx
《Java编程常见问题汇总.docx》由会员分享,可在线阅读,更多相关《Java编程常见问题汇总.docx(29页珍藏版)》请在冰豆网上搜索。
Java编程常见问题汇总
Java编程:
常见问题汇总
每天在写Java程序,其实里面有一些细节大家可能没怎么注意,这不,有人总结了一个我们编程中常见的问题。
虽然一般没有什么大问题,但是最好别这样做。
另外这里提到的很多问题其实可以通过Findbugs()来帮我们进行检查出来。
字符串连接误用
错误的写法:
1Strings="";
2for(Personp:
persons){
3s+=","+p.getName();
4}
5s=s.substring
(2);//removefirstcomma
正确的写法:
6StringBuildersb=newStringBuilder(persons.size()*16);//wellestimatedbuffer
7for(Personp:
persons){
8if(sb.length()>0)sb.append(",");
9sb.append(p.getName);
10}
错误的使用StringBuffer
错误的写法:
11StringBuffersb=newStringBuffer();
12sb.append("Name:
");
13sb.append(name+'\n');
14sb.append("!
");
15...
16Strings=sb.toString();
问题在第三行,appendchar比String性能要好,另外就是初始化StringBuffer没有指定size,导致中间append时可能重新调整内部数组大小。
如果是JDK1.5最好用StringBuilder取代StringBuffer,除非有线程安全的要求。
还有一种方式就是可以直接连接字符串。
缺点就是无法初始化时指定长度。
正确的写法:
17StringBuildersb=newStringBuilder(100);
18sb.append("Name:
");
19sb.append(name);
20sb.append("\n!
");
21Strings=sb.toString();
或者这样写:
22Strings="Name:
"+name+"\n!
";
测试字符串相等性
错误的写法:
23if(pareTo("John")==0)...
24if(name=="John")...
25if(name.equals("John"))...
26if("".equals(name))...
上面的代码没有错,但是不够好。
compareTo不够简洁,==原义是比较两个对象是否一样。
另外比较字符是否为空,最好判断它的长度。
正确的写法:
27if("John".equals(name))...
28if(name.length()==0)...
29if(name.isEmpty())...
数字转换成字符串
错误的写法:
30""+set.size()
31newInteger(set.size()).toString()
正确的写法:
32String.valueOf(set.size())
利用不可变对象(Immutable)
错误的写法:
33zero=newInteger(0);
34returnBoolean.valueOf("true");
正确的写法:
35zero=Integer.valueOf(0);
36returnBoolean.TRUE;
请使用XML解析器
错误的写法:
37intstart=xml.indexOf("
38intend=xml.indexOf("");
39Stringname=xml.substring(start,end);
正确的写法:
40SAXBuilderbuilder=newSAXBuilder(false);
41Documentdoc=doc=builder.build(newStringReader(xml));
42Stringname=doc.getRootElement().getChild("name").getText();
请使用JDom组装XML
错误的写法:
43Stringname=...
44Stringattribute=...
45Stringxml="
46+"
47+"";
正确的写法:
48Elementroot=newElement("root");
49root.setAttribute("att",attribute);
50root.setText(name);
51Documentdoc=newDocumet();
52doc.setRootElement(root);
53XmlOutputterout=newXmlOutputter(Format.getPrettyFormat());
54Stringxml=out.outputString(root);
XML编码陷阱
错误的写法:
55Stringxml=FileUtils.readTextFile("my.xml");
因为xml的编码在文件中指定的,而在读文件的时候必须指定编码。
另外一个问题不能一次就将一个xml文件用String保存,这样对内存会造成不必要的浪费,正确的做法用InputStream来边读取边处理。
为了解决编码的问题,最好使用XML解析器来处理。
未指定字符编码
错误的写法:
56Readerr=newFileReader(file);
57Writerw=newFileWriter(file);
58Readerr=newInputStreamReader(inputStream);
59Writerw=newOutputStreamWriter(outputStream);
60Strings=newString(byteArray);//byteArrayisabyte[]
61byte[]a=string.getBytes();
这样的代码主要不具有跨平台可移植性。
因为不同的平台可能使用的是不同的默认字符编码。
正确的写法:
62Readerr=newInputStreamReader(newFileInputStream(file),"ISO-8859-1");
63Writerw=newOutputStreamWriter(newFileOutputStream(file),"ISO-8859-1");
64Readerr=newInputStreamReader(inputStream,"UTF-8");
65Writerw=newOutputStreamWriter(outputStream,"UTF-8");
66Strings=newString(byteArray,"ASCII");
67byte[]a=string.getBytes("ASCII");
未对数据流进行缓存
错误的写法:
68InputStreamin=newFileInputStream(file);
69intb;
70while((b=in.read())!
=-1){
71...
72}
上面的代码是一个byte一个byte的读取,导致频繁的本地JNI文件系统访问,非常低效,因为调用本地方法是非常耗时的。
最好用BufferedInputStream包装一下。
曾经做过一个测试,从/dev/zero下读取1MB,大概花了1s,而用BufferedInputStream包装之后只需要60ms,性能提高了94%!
这个也适用于outputstream操作以及socket操作。
正确的写法:
73InputStreamin=newBufferedInputStream(newFileInputStream(file));
无限使用heap内存
错误的写法:
74byte[]pdf=toPdf(file);
这里有一个前提,就是文件大小不能讲JVM的heap撑爆。
否则就等着OOM吧,尤其是在高并发的服务器端代码。
最好的做法是采用Stream的方式边读取边存储(本地文件或database)。
正确的写法:
75Filepdf=toPdf(file);
另外,对于服务器端代码来说,为了系统的安全,至少需要对文件的大小进行限制。
不指定超时时间
错误的代码:
76Socketsocket=...
77socket.connect(remote);
78InputStreamin=socket.getInputStream();
79inti=in.read();
这种情况在工作中已经碰到不止一次了。
个人经验一般超时不要超过20s。
这里有一个问题,connect可以指定超时时间,但是read无法指定超时时间。
但是可以设置阻塞(block)时间。
正确的写法:
80Socketsocket=...
81socket.connect(remote,20000);//failafter20s
82InputStreamin=socket.getInputStream();
83socket.setSoTimeout(15000);
84inti=in.read();
另外,文件的读取(FileInputStream,FileChannel,FileDescriptor,File)没法指定超时时间,而且IO操作均涉及到本地方法调用,这个更操作了JVM的控制范围,在分布式文件系统中,对IO的操作内部实际上是网络调用。
一般情况下操作60s的操作都可以认为已经超时了。
为了解决这些问题,一般采用缓存和异步/消息队列处理。
频繁使用计时器
错误代码:
85for(...){
86longt=System.currentTimeMillis();
87longt=System.nanoTime();
88Dated=newDate();
89Calendarc=newGregorianCalendar();
90}
每次new一个Date或Calendar都会涉及一次本地调用来获取当前时间(尽管这个本地调用相对其他本地方法调用要快)。
如果对时间不是特别敏感,这里使用了clone方法来新建一个Date实例。
这样相对直接new要高效一些。
正确的写法:
91Dated=newDate();
92for(Eentity:
entities){
93entity.doSomething();
94entity.setUpdated((Date)d.clone());
95}
如果循环操作耗时较长(超过几ms),那么可以采用下面的方法,立即创建一个Timer,然后定期根据当前时间更新时间戳,在我的系统上比直接new一个时间对象快200倍:
96privatevolatilelongtime;
97Timertimer=newTimer(true);
98try{
99time=System.currentTimeMillis();
100timer.scheduleAtFixedRate(newTimerTask(){
101publicvoidrun(){
102time=System.currentTimeMillis();
103}
104},0L,10L);//granularity10ms
105for(Eentity:
entities){
106entity.doSomething();
107entity.setUpdated(newDate(time));
108}
109}finally{
110timer.cancel();
111}
捕获所有的异常
错误的写法:
112Queryq=...
113Personp;
114try{
115p=(Person)q.getSingleResult();
116}catch(Exceptione){
117p=null;
118}
这是EJB3的一个查询操作,可能出现异常的原因是:
结果不唯一;没有结果;数据库无法访问,而捕获所有的异常,设置为null将掩盖各种异常情况。
正确的写法:
119Queryq=...
120Personp;
121try{
122p=(Person)q.getSingleResult();
123}catch(NoResultExceptione){
124p=null;
125}
忽略所有异常
错误的写法:
126try{
127doStuff();
128}catch(Exceptione){
129log.fatal("Couldnotdostuff");
130}
131doMoreStuff();
这个代码有两个问题,一个是没有告诉调用者,系统调用出错了.第二个是日志没有出错原因,很难跟踪定位问题。
正确的写法:
132try{
133doStuff();
134}catch(Exceptione){
135thrownewMyRuntimeException("Couldnotdostuffbecause:
"+e.getMessage,e);
136}
重复包装RuntimeException
错误的写法:
137try{
138doStuff();
139}catch(Exceptione){
140thrownewRuntimeException(e);
141}
正确的写法:
142try{
143doStuff();
144}catch(RuntimeExceptione){
145throwe;
146}catch(Exceptione){
147thrownewRuntimeException(e.getMessage(),e);
148}
149try{
150doStuff();
151}catch(IOExceptione){
152thrownewRuntimeException(e.getMessage(),e);
153}catch(NamingExceptione){
154thrownewRuntimeException(e.getMessage(),e);
155}
不正确的传播异常
错误的写法:
156try{
157}catch(ParseExceptione){
158thrownewRuntimeException();
159thrownewRuntimeException(e.toString());
160thrownewRuntimeException(e.getMessage());
161thrownewRuntimeException(e);
162}
主要是没有正确的将内部的错误信息传递给调用者.第一个完全丢掉了内部错误信息,第二个错误信息依赖toString方法,如果没有包含最终的嵌套错误信息,也会出现丢失,而且可读性差.第三个稍微好一些,第四个跟第二个一样。
正确的写法:
163try{
164}catch(ParseExceptione){
165thrownewRuntimeException(e.getMessage(),e);
166}
用日志记录异常
错误的写法:
167try{
168...
169}catch(ExceptionAe){
170log.error(e.getMessage(),e);
171throwe;
172}catch(ExceptionBe){
173log.error(e.getMessage(),e);
174throwe;
175}
一般情况下在日志中记录异常是不必要的,除非调用方没有记录日志。
异常处理不彻底
错误的写法:
176try{
177is=newFileInputStream(inFile);
178os=newFileOutputStream(outFile);
179}finally{
180try{
181is.close();
182os.close();
183}catch(IOExceptione){
184/*wecan'tdoanything*/
185}
186}
is可能close失败,导致os没有close
正确的写法:
187try{
188is=newFileInputStream(inFile);
189os=newFileOutputStream(outFile);
190}finally{
191try{if(is!
=null)is.close();}catch(IOExceptione){/*wecan'tdoanything*/}
192try{if(os!
=null)os.close();}catch(IOExceptione){/*wecan'tdoanything*/}
193}
捕获不可能出现的异常
错误的写法:
194try{
195...doriskystuff...
196}catch(SomeExceptione){
197//neverhappens
198}
199...dosomemore...
正确的写法:
200try{
201...doriskystuff...
202}catch(SomeExceptione){
203//neverhappenshopefully
204thrownewIllegalStateException(e.getMessage(),e);//crashearly,passingallinformation
205}
206...dosomemore...
transient的误用
错误的写法:
207publicclassAimplementsSerializable{
208privateStringsomeState;
209privatetransientLoglog=LogFactory.getLog(getClass());
210
211publicvoidf(){
212log.debug("enterf");
213...
214}
215}
这里的本意是不希望Log对象被序列化.不过这里在反序列化时,会因为log未初始化,导致f()方法抛空指针,正确的做法是将log定义为静态变量或者定位为具备变量。
正确的写法:
216publicclassAimplementsSerializable{
217privateStringsomeState;
218privatestaticfinalLoglog=LogFactory.getLog(A.class);
219
220publicvoidf(){
221log.debug("enterf");
222...
223}
224}
225publicclassAimplementsSerializable{
226privateStringsomeState;
227
228publicvoidf(){
229Loglog=LogFactory.getLog(getClass());
230log.debug("enterf");
231...
232}
233}
不必要的初始化
错误的写法:
234publicclassB{
235privateintcount=0;
236privateStringname=null;
237privatebooleanimportant=false;
238}
这里的变量会在初始化时使用默认值:
0,null,false,因此上面的写法有些多此一举。
正确的写法:
239publicclassB{
240privateintcount;
241privateStringname;
242privatebooleanimportant;
243}
最好用静态final定义Log变量
244privatestaticfinalLoglog=LogFactory.getLog(MyClass.class);
这样做的好处有三:
可以保证线程安全
静态或非静态代码都可用
不会影响对象序列化
选择错误的类加载器
错误的代码:
245Classclazz=Class.forName(name);
246Classclazz=getClass().getClassLoader().loadClass(name
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 编程 常见问题 汇总
![提示](https://static.bdocx.com/images/bang_tan.gif)