一个有趣的程序问题,我没搞清楚,欢迎大家猜一下执行结果,一定要先猜再去测试

老紫竹 2008-01-10 03:50:24
大家猜一下结果,再去测试,很奇怪的事情。也许有些内幕没有搞清楚
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
out.println("12121212");
out.clearBuffer();
out.close();
OutputStream os = response.getOutputStream();
os.write("this is a test".getBytes());
os.close();
%>
a test line


屏幕输出啥?
...全文
363 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
cangyue87 2008-01-14
  • 打赏
  • 举报
回复
受教了...顺便口水下星星...
老紫竹 2008-01-14
  • 打赏
  • 举报
回复
火龙果辛苦了。呵呵,这个代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
out.println("12121212");
out.clearBuffer();
out.close();
OutputStream os = response.getOutputStream();
os.write("this is a test".getBytes());
os.close();
%>
a test line


在我这里的输出结果为
this is a test


但是控制台有异常输出
java.io.IOException: Stream closed
at org.apache.jasper.runtime.JspWriterImpl.ensureOpen(JspWriterImpl.java:203)
at org.apache.jasper.runtime.JspWriterImpl.clearBuffer(JspWriterImpl.java:159)
at org.apache.jsp.test.test_jsp._jspService(test_jsp.java:59)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:97)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:334)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:314)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:264)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869)
at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664)
at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684)
at java.lang.Thread.run(Thread.java:619)


报错的地点在下面这个地方
  OutputStream os = response.getOutputStream();
os.write("this is a test".getBytes());
os.close();

out.write("\r\n");
out.write("a test line\r\n");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
out.clearBuffer(); // 这个地方就是59行
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
}
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
}
}
}


我得到的结论是
out.close();只是关闭了内部的PrintWrite的操作,并没有关闭输出流,
因为jsp在页面代码执行后,还要有后续的操作,所以OutputStream还是可以使用的。
而异常是因为out被关掉了,后面的2个out.write(); 虽然存在于缓冲区,但不能输出了,因为前面我已经os.close()掉了。
  • 打赏
  • 举报
回复
嘻嘻~~
cangyue87 2008-01-14
  • 打赏
  • 举报
回复
和16楼手拉手扎草人去...
flybird 2008-01-14
  • 打赏
  • 举报
回复
学习了
excignjord 2008-01-14
  • 打赏
  • 举报
回复
guanzhu
老紫竹 2008-01-14
  • 打赏
  • 举报
回复
看大家这么热心,特别是果果,特意给帖子追加了所有家当。 可用分还有10分了。
  • 打赏
  • 举报
回复
火龙果很不经摔的,夹在一起扔下来的话,会变成“火龙果饼”的。
  • 打赏
  • 举报
回复
9494,难道CSDN有随机给帖子加分?
cangyue87 2008-01-14
  • 打赏
  • 举报
回复
天上掉下来的......好多好多分啊.....

啊~~~``谁啊,把火龙果夹在分里一起扔下来了...太没道德了...
bushuang 2008-01-14
  • 打赏
  • 举报
回复
..
  • 打赏
  • 举报
回复
PS:我记得我上次来时,这个帖是没分的,怎么今天变成100分了呢?
  • 打赏
  • 举报
回复
这个代码为什么会报错,原因就在于(见下面的注释)

  OutputStream os = response.getOutputStream();
os.write("this is a test".getBytes());
os.close();

// 前面的out已经close掉了。下面还有两个输出,见后面的说明
// 异常的产生点实际上在这一句上。
out.write("\r\n");
out.write("a test line\r\n");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
}
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
}
}
}


说明A

org.apache.jasper.runtime.JspWriterImpl类,这个类实际上就是out对象,我们来看一下,它的write方法:

// out.write仅是调用了这个方法
public void write(String s) throws IOException {
write(s, 0, s.length()); // 这个方法又调用了下面的一个
}

public void write(String s, int off, int len) throws IOException {
ensureOpen(); // 这一句正是抛出异常的关键,方法见下
// 以下省略
....
}

// 这个就是抛出异常的所在,在显式调用out.close(),而在close()中会将closed置为true
// 所以在接下来的out.write中就可抛异常
private void ensureOpen() throws IOException {
if(response == null || closed)
throw new IOException("Stream closed"); // 这句就是抛出异常点
else
return;
}



**********************************************************************
out.close();只是关闭了内部的PrintWrite的操作,并没有关闭输出流,
因为jsp在页面代码执行后,还要有后续的操作,所以OutputStream还是可以使用的。
—————————————————————————————————————————
out是一个JspWriter,是从response.getWriter()中产生的(具体的可看10楼的分析),而response.getOutputStream与JspWriter完全是不同的东西,一个是OutputStream,一个JspWriter。
所以它们任何一个的关闭与另一个是无关的。而有关的仅是 out 和 response.getWriter()。

  • 打赏
  • 举报
回复
13楼 我哭...怎么我7楼的回复被无视了....绑个草人扎针去~~~``
________________________________

我也要哭了,为了这个帖子,我今天看了近一个下午的Tomcat源代码,
把看的结果都贴在10楼了,也被无视了....555~~躲在角落里绑个草人扎针去`````
cursor_wang 2008-01-11
  • 打赏
  • 举报
回复
同一个buffer看来是肯定的,但上面的out不关闭,下面的os就不能得到,为什么呢?
cursor_wang 2008-01-11
  • 打赏
  • 举报
回复
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
out.println("12121212");
out.flush();
out.clearBuffer();
out.close();
OutputStream os = response.getOutputStream();
os.write("this is a test".getBytes());
os.close();
%>
a test line

输出:12121212

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
out.println("12121212");
//out.flush();
out.clearBuffer();
out.close();
OutputStream os = response.getOutputStream();
os.write("this is a test".getBytes());
os.close();
%>
a test line

输出:this is a test

所以感觉是一个对象.
cangyue87 2008-01-11
  • 打赏
  • 举报
回复
回12楼,不是一个对象,而是两个对象用了同一个buffer来放东西...

我哭...怎么我7楼的回复被无视了....绑个草人扎针去~~~``
cursor_wang 2008-01-11
  • 打赏
  • 举报
回复
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
out.println("12121212");
out.flush();
out.clearBuffer();
//out.close();
//OutputStream os = response.getOutputStream();
//os.write("this is a test".getBytes());
//os.close();
%>
a test line


输出:12121212 a test line

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
out.println("12121212");
//out.flush();
out.clearBuffer();
//out.close();
//OutputStream os = response.getOutputStream();
//os.write("this is a test".getBytes());
//os.close();
%>
a test line


输出:a test line

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
out.println("12121212");
out.flush();
out.clearBuffer();
out.close();
//OutputStream os = response.getOutputStream();
//os.write("this is a test".getBytes());
//os.close();
%>
a test line

输出:12121212
说明这个out.println("12121212");当时不马上输出的.out不具有自动行刷新的.你要out.flush();刷新该流的缓冲,才能马上输出.你后面再怎么操作它已经输出了.如果你不刷新缓冲流,后面又out.clearBuffer();清除了.所以也不输出了.也就被吃掉了.哈哈!如果out.close();它上面还是输出,就是下面不输出了,因为out已经关闭.我个人感觉out和os是一个对象.蛮有意思的.
cangyue87 2008-01-11
  • 打赏
  • 举报
回复
也许是java在编译Jsp时做了小动作吧...

我在断点里看见它们用一个outputBuffer...
  • 打赏
  • 举报
回复
呵呵,抱歉~~上面有许多不对的地方。请管理员将8楼的帖子删除。
更正一下:out对象也是在请求/响应阶段产生的。


但是out对象属于JspWriter,是从pageContext.getOut(),而getOut通过传入一个
response.getWriter对象而构建出来的。

下面的是org.apache.jasper.runtime.JspWriterImpl中initOut的源代码:

private void initOut() throws IOException {
if(out == null)
out = response.getWriter();
}


从上面的代中可以看出,out其实就是一个response.getWriter,只不过对其进行了再次的封装。

显式的调用out.close()时,做了以下的事情:

public void close() throws IOException {
if(response == null || closed)
return;
flush();
if(out != null)
out.close();
out = null; // 在关闭的同时设为null了。
closed = true;
}


在out.close()关闭后,实际上也把response中的out对象设为null。

再看一下response中getWriter的源代码,org.apache.catalina.connector.Response

public PrintWriter getWriter() throws IOException {
if(usingOutputStream)
throw new IllegalStateException(sm.getString("coyoteResponse.getWriter.ise"));
setCharacterEncoding(getCharacterEncoding());
usingWriter = true;
outputBuffer.checkConverter();
if(writer == null)
writer = new CoyoteWriter(outputBuffer);
return writer;
}


在response获得writer对象时,先判断过了其writer是否为null,若为null时,则重新生成一个。

这也就是说在页面上 out.close()后,再使用response.getWriter()不会出错的原因。

而楼主所用的response.getOutputStream与out就是两个不同的东西这是互不影响的。

如果将页面代码改为:

<%
Writer os = response.getWriter();
os.write(0x4e01);
os.close();
out.println("12121212");
%>


可以输出一个“丁”字,而后面的东西是死活都出不来了,其也不会报错,这是因为out.println
在输出之前会检查一下是否关闭了,其检查的代码如下:

private void ensureOpen() throws IOException {
if(response == null || closed)
throw new IOException("Stream closed");
else
return;
}


response是不可能为空的,而closed只有在close的方法中才设为true,所以这个检查形同虚设。
而out.println("12121212");中的那些字符也许就被Tomcat吃掉了吧 :)
加载更多回复(9)

62,623

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧