在翻阅servlet-src时看到ServletOutputStream的某个实现方式,一知半解的看不明白,代码如下:
public void print(String s) throws IOException {
if (s == null)
s = "null";
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
//
// XXX NOTE: This is clearly incorrect for many strings,
// but is the only consistent approach within the current
// servlet framework. It must suffice until servlet output
// streams properly encode their output.
//
if ((c & 0xff00) != 0) { // high order byte must be zero
String errMsg = lStrings.getString("err.not_iso8859_1");
Object[] errArgs = new Object[1];
errArgs[0] = Character.valueOf(c);
errMsg = MessageFormat.format(errMsg, errArgs);
throw new CharConversionException(errMsg);
}
write(c);
}
}
其中”if ((c & 0xff00) != 0) { // high order byte must be zero”这句话是什么意思?我们知道在java中,一个字符占两个字节,共16位,0xff00不就是11111111 11111111 00000000 00000000.假如字符串是纯英文,经测试能正常显示。但假如是汉字呢?
测试代码:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class OutputStreamServlet extends HttpServlet
{
public void service(ServletRequest req,
ServletResponse res)
throws ServletException,
java.io.IOException{
res.setCharacterEncoding("utf-8"); //该语句并不能解决编码问题
ServletOutputStream outputStream = res.getOutputStream();
outputStream.println("简体中文");
}
}
用前天配置虚拟路径的方法测试了下:
http://blog.csdn.net/nanphonfy/article/details/48749877
javac编译好的.class文件拷到D:\myWeb\WEB-INF\classes目录下,在浏览器中访问:
http://localhost:8080/it315/test.html
如上出现了编码问题。
而源代码中public abstract class ServletOutputStream extends OutputStream{…}可知javax.servlet.ServletOutputStream是个抽象类。
在以前早期的版本,不会出现这个编码问题,因为我们运行用的类的方法不是它本身,而是它的子类,所以这个子类一定是最终被tomcat实现了。
即这个println(String s)函数以前是被子类重写,覆盖了。
经过分析tomcat代码最终找到了真正的实现类,路径如下apache-tomcat-7.0.28-src\java\org\apache\catalina\connector\CoyoteOutputStream.java,在里面并没有重写println(String s)
当然tomcat7不再覆盖println(String s)方法也是有道理的,就是让我们使用Writer而不再使用OutputStream来输出HTML或XML之类的内容。
用PrintWriter来测试:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class OutputStreamServlet extends HttpServlet
{
public void service(ServletRequest req,
ServletResponse res)
throws ServletException,
java.io.IOException{
res.setCharacterEncoding("utf-8");
PrintWriter writer = res.getWriter();
writer.println("简体中文");
}
}
正常显示。
分析源代码
apache-tomcat-7.0.28-src\java\org\apache\catalina\connector\CoyoteWriter.java
@Override
public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
@Override
public void write(String s) {
write(s, 0, s.length());
}
@Override
public void write(String s, int off, int len) {
if (error) {
return;
}
try {
ob.write(s, off, len);//这个ob在前面有定义:protected OutputBuffer ob;
} catch (IOException e) {
error = true;
}
}
继续查看OutputBuffer的代码,发现如下方法:
/**
* Append a string to the buffer
*/
@Override
public void write(String s, int off, int len)
throws IOException {
if (suspended) {
return;
}
charsWritten += len;
if (s == null) {
s = "null";
}
conv.convert(s, off, len);
//这里进行编码转换,conv的声明:protected C2BConverter conv;
//在调试过程中可以看到C2BConverter中的存放的正是utf-8编码。
}
总结:
OutputStream输出的是二进制流(字节流),涉及到汉字的要用char来存储处理,此时要考虑用Writer。
参考:
http://blog.csdn.net/hengyunabc/article/details/17056237
http://gongsy.iteye.com/blog/516740