精华内容
下载资源
问答
  • 获取printwriter对象的是
    千次阅读
    2013-01-06 10:49:10

    在servlet 处理请求后,需要返回一些数据给浏览器客户端。这时候就需要用到PrintWriter对象用来写数据。
    一般的使用方法是:
    首先获得PrinterWriter内置对象,然后将内容写入

    PrinterWriter out;
    out = response.getWriter();
    
    
    out.println("<HTML><HEAD><TITLE>");
    
    
    out.println(title); 
    out.println("</TITLE></HEAD><BODY>");//写入的内容,将要发送给客户端浏览器

    有时候写入的内容过多,会导致浏览器长时间等待接收文本,这里可以使用flush()函数强制将以写入的内容发送到浏览器,再接着写入内容
    out.flush();



    Tip:
    我们web容器(Tomcat为例),都是查找对应.jsp页面的class文件来响应请求的。
    在我们写完jsp文件后,会在WEB-INF目录下找到类似的../../_*JSP.CLASS 文件。(class文件中使用的是out对象的write方法)
    Servlet教程

    更多相关内容
  • 解决response获取PrintWriter输出的中文乱码

    出现乱码代码

    @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            
            //1.获取字符输出流
            PrintWriter pw = response.getWriter();
            pw.write("你好 response");
        }
    

    在这里插入图片描述

    问题分析

    出现问题这样的问题的原因是浏览器默认的为我们系统使用的字符集,一般默认为GBK,而PrintWriter对象是我们从response获取而来的,而服务器默认的字符集为utf-8,所以就会出现乱码情况,如果是自己创建的PrintWriter对象的话,就不会出现这种情况.所以只需要让浏览器知道服务器使用的是什么编码,并且对浏览器的请求头content-type进行修改就可以解决编码不一致的问题了

    修改后的代码

    @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            //在获取流对象之前告诉浏览器使用什么字符集
            response.setCharacterEncoding("utf-8");
    
            //告诉浏览器,服务器发送的消息体的数据的编码
            response.setHeader("content-type","text/html;charset=utf-8");
            
            //1.获取字符输出流
            PrintWriter pw = response.getWriter();
            pw.write("你好 response");
        }
    

    在这里插入图片描述

    补充:
    除了以上的方法,还有更简单的方法,就是直接设置 response.setContentType(“text/html;charset=utf-8”);
    其效果也是一样的

    //简单的设置编码的方法
            response.setContentType("text/html;charset=utf-8");
    
    展开全文
  • // 获取URLConnection对象对应的输出流 PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream()); // 发送请求参数 printWriter.write(post);//post的参数 xx=xx&yy=yy // flush输出流的...

    话不多说,先看代码!

    /**

    * Created by david on 2017-7-5.

    */

    import com.google.gson.JsonObject;

    import com.google.gson.JsonParser;

    import java.io.BufferedReader;

    import java.io.IOException;

    import java.io.InputStream;

    import java.io.InputStreamReader;

    import java.net.HttpURLConnection;

    import java.net.URL;

    public class HttpRequestUtil {

    /**

    * 发起http请求并获取结果

    * @param requestUrl 请求地址

    */

    public static JsonObject getXpath(String requestUrl){

    String res="";

    JsonObject object = null;

    StringBuffer buffer = new StringBuffer();

    try{

    URL url = new URL(requestUrl);

    HttpURLConnection urlCon= (HttpURLConnection)url.openConnection();

    if(200==urlCon.getResponseCode()){

    InputStream is = urlCon.getInputStream();

    InputStreamReader isr = new InputStreamReader(is,"utf-8");

    BufferedReader br = new BufferedReader(isr);

    String str = null;

    while((str = br.readLine())!=null){

    buffer.append(str);

    }

    br.close();

    isr.close();

    is.close();

    res = buffer.toString();

    JsonParser parse =new JsonParser();

    object = (JsonObject) parse.parse(res);

    }

    }catch(IOException e){

    e.printStackTrace();

    }

    return object;

    }

    public static JsonObject postDownloadJson(String path,String post){

    URL url = null;

    try {

    url = new URL(path);

    HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();

    httpURLConnection.setRequestMethod("POST");// 提交模式

    // conn.setConnectTimeout(10000);//连接超时 单位毫秒

    // conn.setReadTimeout(2000);//读取超时 单位毫秒

    // 发送POST请求必须设置如下两行

    httpURLConnection.setDoOutput(true);

    httpURLConnection.setDoInput(true);

    // 获取URLConnection对象对应的输出流

    PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream());

    // 发送请求参数

    printWriter.write(post);//post的参数 xx=xx&yy=yy

    // flush输出流的缓冲

    printWriter.flush();

    //开始获取数据

    BufferedInputStream bis = new BufferedInputStream(httpURLConnection.getInputStream());

    ByteArrayOutputStream bos = new ByteArrayOutputStream();

    int len;

    byte[] arr = new byte[1024];

    while((len=bis.read(arr))!= -1){

    bos.write(arr,0,len);

    bos.flush();

    }

    bos.close();

    JsonParser parse = new JsonParser();

    return (JsonObject)parse.parse(bos.toString("utf-8"));

    } catch (Exception e) {

    e.printStackTrace();

    }

    return null;

    }

    //测试

    public static void main(String args [] ) {

    JsonObject res = null;

    // res = getXpath("http://ip.taobao.com/service/getIpInfo.php?ip=63.223.108.42");

    res = postDownloadJson("http://ip.taobao.com/service/getIpInfo.php?ip=63.223.108.42","123");

    System.out.println(res);

    System.out.println(res.get("code"));

    System.out.println(res.get("data"));

    }

    }

    看第一个方法,发送get请求获取后台数据,其中,将返回回来的字符串解析成json对象用到了google的Gson.jar包,用到了其中JsonParser的parse方法。

    第二个方法,发送post数据到后台并获取后台数据。

    以上这篇java发起http请求获取返回的Json对象方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

    展开全文
  • PrintWriter 缓冲 Java默认的缓冲区大小是8kb的字节缓冲。也就是8192个字节。 缓冲的作用 应用程序每一次进行I/O操作都需要和设备进行通信,而且通信效率非常低。因此缓冲区的存在,大大提高了I/O效率。 例如写入...

    PrintWriter

    缓冲

    Java默认的缓冲区大小是8kb的字节缓冲。也就是8192个字节。

    缓冲的作用

    应用程序每一次进行I/O操作都需要和设备进行通信,而且通信效率非常低。因此缓冲区的存在,大大提高了I/O效率。

    例如写入设备时,每一次通信时,尽可能多的将字节流写入缓冲区,然后等到缓冲区满了(使用flush()或者close()),将数据一次性写入设备。这样一来,避免每写入一个字节都需要与设备进行一次通信的操作。大大提高了效率。

    flush()&close()

    代码示例1

    package flush;
    import java.io.PrintWriter;
    public class TestFlush{
        public static void main(String[] args) {
            String s = "Hello World!\n";
            PrintWriter p = null;
            try {
                p = new PrintWriter(System.out);
                p.write(s);
                //p.flush();    //1
            }catch(Exception e) {
                e.printStackTrace();
            }finally{
                //p.close();    //2
            }
        }
    }
    

    flush()

    当缓冲区没有满时,无法将字节流输出到目的地。flush()的主要作用就是强制将缓冲区存储的剩余字节清出,输出到目的地。

    如果目的地是另一个字节流或者字符流,它也会被flush,输出给下一个目的地。所以一旦flush()调用,整个输出链都会被flush,输出给最后的目的地。

    如果最终目的地是底层系统提供的抽象的目的地(例如file),flush()只能够保证之前写入缓冲的字节可以作为底层系统去写入的数据源,但是不能保证写入硬盘。

    以PrintWriter为例,它的flush()实际上调用的是成员变量out的方法,类型是Writer。

    不使用flush()

    在代码示例1中,注释了代码行12,Console并不会输出Hello World!。这是因为缓冲区没有满,不会自动输出。

    使用flush()

    将代码中示例1中注释1去掉,执行,最终Console会输出:Hello World!。这是因为flush()强制清空缓冲区,输出到目的地。

    close()

    由于这里以PrintWriter为例,带有缓冲区,所以调用close()后首先回去flush缓冲区。而如果是OutputStream,则只会关闭流。

    关闭流同时会释放所有与该流关联的系统资源。一个被关闭的流无法在进行相应的I/O操作,而且无法被重新打开。

    一旦流最外层包装类调用了close(),整个链中的流对象都会被close。

    将代码示例1中注释2去掉,执行,最终Console一样输出了:Hello World!

    参考

    那些你一直没有搞明白的Java缓冲流细节!

    PrintWriter中的缓冲区

    构造器

    1. PrintWriter(File file)
    2. PrintWriter(File file, String csn)
    3. PrintWriter(OutputStream out)
    4. PrintWriter(OutputStream out, boolean autoFlush)
    5. PrintWriter(String fileName)
    6. PrintWriter(String fileName, String csn)
    7. PrintWriter(Writer out)
    8. PrintWriter(Writer out, boolean autoFlush)

    有无缓冲区

    前六种构造器都是通过BufferedWriter包装后作为参数使用最后一种构造器创建PrintWriter对象。所以使用前六种构造器创建的PrintWriter对象一定有缓冲区。

    而使用后两种构造器创建的PrintWriter对象有无缓冲区取决于传递的Writer参数自身有无缓冲区。PrintWriter类并不会自动提供缓冲区。

    实际上在使用BufferedWriter包装之前,还会使用OutputStreamWriter进行包装。其作用是用指定或者默认的字符集对字节进行编码,转换成字符。而OutputStreamWriter内部通过StreamEncoder实现。在StreamEncoder中内置了8kb的字节缓冲区。而前六种构造器都是这样包装,生成流。

    BufferedWriter作用

    既然有无BufferedWriter的存在,PrintWriter的前六种创建的对象必定有缓冲区,那么BufferedWriter对于PrintWriter存在的必要性是什么?BufferedWriter主要作用是减少方法调用的次数。这种优化在大量数据输出时尤为明显。

        /**
         * Flushes the stream.
         * @see #checkError()
         */
        public void flush() {
            try {
                synchronized (lock) {
                    ensureOpen();
                    out.flush();
                }
            }
            catch (IOException x) {
                trouble = true;
            }
        }
    

    举个例子,在源代码中可以PrintWriter.flush()实现是去调用成员变量outflush()。而out是通过构造器初始化的,对于前六种构造器out变量的实际类型就是BufferedWriter。所以会直接去调用BufferedWriter.flush()。

    而如果使用PrintWriter(OutputStreamWriter(new FileOutputStream(fileName)))来创建对象,保证了对象拥有缓冲区,但是out实际类型就是OutputStreamWriter。一旦调用PrintWriter.flush()后回去调用OutputStreamWriter.flush(),而它内部回去调用StreamEncoder.flush()

    构造器参数csn和autoFlush

    参数csn用于指定字符集(Charset),然后通过toCharset()转换为Charset对象。

        /**
         * Returns a charset object for the given charset name.
         * @throws NullPointerException          is csn is null
         * @throws UnsupportedEncodingException  if the charset is not supported
         */
        private static Charset toCharset(String csn)
            throws UnsupportedEncodingException
        {
            Objects.requireNonNull(csn, "charsetName");
            try {
                return Charset.forName(csn);
            } catch (IllegalCharsetNameException|UnsupportedCharsetException unused) {
                // UnsupportedEncodingException should be thrown
                throw new UnsupportedEncodingException(csn);
            }
        }
    

    最后使用私有构造器

    private PrintWriter(Charset charset, File file)
            throws FileNotFoundException
        {
            this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)),
                 false);
        }
    

    创建对象。可以看到最后还是用了最后一种构造器。

    参数autoFlush如果为true,println, printf, or format方法会触发缓冲区flush操作。

    参考

    java.io.PrintWriter

    作用

    PrintWriter是字符型打印输出流,继承于Writer。可以对字符或者字符序列进行格式化。属于处理流。

    PrintWriter优势

    除了构造器,PrintWriter其他方法都不会抛出I/O异常。

    如果要检查I/O操作过程中是否有错误,可以调用checkError()进行查询。如果流还没有关闭,会进行flush,同时检查有无错误。如果有错误,就会返回true。出错可能在底层流输出时或者格式化时。

    常用方法

    write()

        public void write(int c) {
            try {
                synchronized (lock) {
                    ensureOpen();
                    out.write(c);
                }
            }
            catch (InterruptedIOException x) {
                Thread.currentThread().interrupt();
            }
            catch (IOException x) {
                trouble = true;
            }
        }
    

    write()的实现主要是利用成员变量out.write(),而且文档中说明:write()并不会直接从Writer基类中继承,因为PrintWriter需要压制异常。一旦发生IOException就会将trouble至为true,通过checkError()可以检测有无错误。

    append()

    该系列重载方法,将指定的字符或者字符序列追加到PrintWriter流中。其内部实现主要是利用了write()方法。

        public PrintWriter append(CharSequence csq) {
            if (csq == null)
                write("null");
            else
                write(csq.toString());
            return this;
        }
    

    print()

    该系列重载方法,将指定类型的数值转换为String、字符、字符数组、字符序列等参数通过内部write()方法添加到PrintWriter流中。

        public void print(boolean b) {
            write(b ? "true" : "false");
        }
    

    println()

    该系列重载方法和print()类似,但是其内部是用newLine()print()方法实现的。

        public void println(boolean x) {
            synchronized (lock) {
                print(x);
                println();
            }
        }
        
        private void newLine() {
            try {
                synchronized (lock) {
                    ensureOpen();
                    out.write(lineSeparator);
                    if (autoFlush)
                        out.flush();
                }
            }
            catch (InterruptedIOException x) {
                Thread.currentThread().interrupt();
            }
            catch (IOException x) {
                trouble = true;
            }
        }
    

    可以看到当autoFlush为true时,自动清理缓冲区。

    format()

    根据Locale来格式化数据,将其添加到PrintWriter流中。

        public PrintWriter format(String format, Object ... args) {
            try {
                synchronized (lock) {
                    ensureOpen();
                    if ((formatter == null)
                        || (formatter.locale() != Locale.getDefault()))
                        formatter = new Formatter(this);
                    formatter.format(Locale.getDefault(), format, args);
                    if (autoFlush)
                        out.flush();
                }
            } catch (InterruptedIOException x) {
                Thread.currentThread().interrupt();
            } catch (IOException x) {
                trouble = true;
            }
            return this;
        }
    

    和println()一样根据autoFlush成员变量决定是否清理缓冲区。代码中涉及到了Formatter类,之后学习。

    Println()

    其内部是通过format()来实现的。

    public PrintWriter printf(String format, Object ... args) {
            return format(format, args);
        }
    

    由于通过format()实现,一样会根据autoFlush清理缓冲区。

    checkError()

    由于PrintWriter压制了异常,调用者不会catch任何异常,可以通过checkError()来判断I/O操作是否有异常。这种异常主要有两种一种是I/O操作,另一种是格式化错误。

        public boolean checkError() {
            if (out != null) {
                flush();
            }
            if (out instanceof java.io.PrintWriter) {
                PrintWriter pw = (PrintWriter) out;
                return pw.checkError();
            } else if (psOut != null) {
                return psOut.checkError();
            }
            return trouble;
        }
    
    1. 首先判断流对象是否被回收,不为null就flush缓冲区。
    2. 根据成员变量out来决定调用哪一种checkError()的实现。
    3. 不满足判断,直接返回trouble。

    close()

    close()对于PrintWriter的作用上面已经分析了。还有一点需要注意,close()并不会造成引用变量的值为null。

    Charset折腾

    I/O流从处理数据类型可以分为字节流和字符流。一般字节流处理二进制文件而字符流处理文本,那么字节流可以处理文本吗?

    Charset作用

    查看文档介绍类的第一句话,Charset是用来实现UTF-16的code unit序列和其他编码方式字节序列之间的映射关系。

    例如指定Charset是UTF-8,也就是采用了UTF-8编码与UTF-16编码之间的映射,实现转换。

    为什么要一定转换成UTF-16,而不采用原始字符集?一旦在内存中统一字符集,向外界输出时,再不受限于编码集,可以完全按照输出对象的编码集来输出。例如输出目的地采用UTF-8,那么使用PrintWriter的构造函数时就可以指定字符集(参数csn)。

    字节流处理文本

    public class TestCharset {
        public static void main(String[] args) {
            Charset charset = Charset.forName("UTF-8");
            Charset charset2 = Charset.forName("GBK");
            InputStream input = null;
            File file = new File("..", "/file/TestFile.txt");
            try {
                input = new FileInputStream(file);
                int count = input.available();
                byte[] b = null;
                if(count > 0) {
                    b = new byte[count];
                    input.read(b);
                }
                // for(byte i : b) {
                //     System.out.print(i + " ");
                // }
                // System.out.println();
                String s = new String(b, charset);
                int[] codePoints = s.codePoints().toArray();
                System.out.println("Charset is UTF-8: ");
                for(int i : codePoints) {
                    System.out.print(i + " ");
                }
                System.out.println();
    
                s = new String(b, charset2);
                codePoints = s.codePoints().toArray();
                System.out.println("Charset is GBK: ");
                for(int i : codePoints) {
                    System.out.print(i + " ");
                }
                System.out.println();
    
    
                String h = new String("h");
                codePoints = h.codePoints().toArray();
                System.out.println("The correct \"h\"'s encode: ");
                for(int i : codePoints) {
                    System.out.print(i + " ");
                }
                System.out.println();
    
                System.out.println("The correct character: ");
                System.out.println(s);
            }catch (FileNotFoundException e) {
                e.printStackTrace();
            }catch(IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在执行之前,先创建一个采用GBK字符集编码的文件TestFile.txt。里面输入了一个中文“一”。由于采用Mac系统保存后默认使用UTF-8字符集编码,变成“h”。这里看似是一个正确显示的字符,其实不然。对于ASCII表中的所有字符,在每一个字符集编码都是相同的,这种现象称为类ASCII字符集。但是在创建TestFile.txt时输入的是“一”,所以编码绝对不会是104,这绝笔是一个乱码。

    文本切换编码方式,其本质是对于同一个编码在不同的字符集中去解码。所以可以这么认为一个文本文件存储的并非是字符序列,而是创建文本时根据输入的字符和设定好的字符集来encoding得到的编码序列。

    设定Charset为GBK的流程:

    1. 使用字节流,从文本文件中获取所有字节,构成一个字节数组。文本默认采用的是UTF-8字符集去解码字符,而字节流获得了文本中原始编码(即,输入文本字符时采用了GBK编码的字节)。
    2. 根据字节数组,并且指定Charset为GBK,转换成Java内存中的Unicode字符串。采用GBK是因为文件在创建时采用的是GBK字符集显示正确的字符“一”,所以在原始编码不变的情况下只有采用正确的字符集才能获取到正确的字符,以转换为正确的Unicode(UTF-16)编码。
    3. 最后通过采用默认的字符集(UTF-8)向控制台输出。注意控制台默认字符集是UTF-8。

    在代码中看到,还打印了获取到的字符串的code point。其作用是,一是看采用不同Charset映射UTF-16后获取到字符串后code point;二是证实文件采用UTF-8字符集显示时,确实是一个乱码(如果是正确的,必定是104)。

    执行结果:

    Charset is UTF-8: 
    1211 10 
    Charset is GBK: 
    19968 10 
    The correct "h"'s encode: 
    104 
    The correct character:

    参考

    Java 使用Charset类解决读入字符乱码问题和控制输出字符编码

    Java nio 学习笔记(二) Charset(字符集)与Selector(异步IO)的知识

    Java字符串

    JVM内存中,字符串存储形式是UTF-16编码。有一个概念必须理解,Java字符串就是char值序列。char只是表示UTF-16的一个code unit,就是UTF-16字符集的一个子集。而字符串为了表示所有字符,可以使用两个char来表示补充字符。

    最直接证明,String对象实际上维护了一个char数组类型变量value。

    UTF-16

    为了用好Java中的字符串,那么有必要了解一下UTF-16字符集。UTF-16实际上是对Unicode编码的一种转换,在存储空间和效率上进行了一定的权衡。

    Unicode的code point可以分为17个代码级别,第一个代码级别称为基本的多语言级别,code point范围U+0000~U+FFFF;其余的16个级别code point从U+10000~U+10FFFF,包括补充字符。

    UTF-16编码采用不同长度的编码表示所有Unicode code point。在第一个级别,基本的多语言级别中,每个字符使用16位表示(也就是char),通常称为code unit;针对其余级别的补充字符,采用一对连续的code unit进行编码(两个char)。采用一组char来编码补充字符,就构成了所谓的surrogate pair(底层是用一个int来表示):第一个char称为high-surrogate,高代理部分,范围是从"uD800到"uDBFF,共1024个码位;第二个char称为low-surrogate,低代理部分,范围是从"uDC00到"uDFFF,共1024个码位。

    这样的构成难道不会造成第一级别和其余级别编码重复吗?实际上并不会,在第一级别中有2048字节空闲,而它们的编码范围正好是surrogate pair组合中的高低代理部分的编码。这样一来可以很方便的去判断一个code unit到底是一个字符,还是一个补充字符的高或者低代理部分。

    code unit是指每一种编码中最小的编码单元。

    实战

    正确使用Unicode转义字符

    public class TestCharacterSequence {
        public static void main(String[] args) {
            // 1D546 是Unicode编码,而且超过了16位,所以无法赋值给char类型变量。
            //char c = '\u1D546';
            String s = null;
            // \u1D546在字符串字面量中出现时分为两部分:\u1D54作为一个UTF-16第一级字符;‘6’作为一个字符
            s = "\u1D546" + " ";
            System.out.println("The character sequence is: " + s);
            System.out.println("each character in this character sequence is:");
            for(int i = 0; i < s.length(); i++) {
                char c = s.charAt(i);
                System.out.println(c);
            }
            int cuCount = s.length();
            System.out.println("the character sequence's code unit count is " + cuCount); //Console 3
            int cpCount = s.codePointCount(0, cuCount);
            System.out.println("the character sequence's code point counte is " + cpCount); //Console 3
            //从打印日志可以看到,确实将\u1D546分成两部分,如果没有,code unit的数量应该比code point数量多一个。
    
            System.out.println("===================");
            String correctStr = "\uD835\uDD46" + " ";
            System.out.println("The correct character sequence is: " + correctStr);
            cuCount = correctStr.length();
            System.out.println("the correct character sequence's code unit count is " + cuCount);
            cpCount = correctStr.codePointCount(0, cuCount);
            System.out.println("the correct character sequence's code point counte is " + cpCount);
    
            System.out.println("each correct character in this character sequence is:");
            for(int i = 0; i < cuCount;) {
                int index = correctStr.offsetByCodePoints(i, 0);
                int cp = correctStr.codePointAt(index);
                if(Character.isSupplementaryCodePoint(cp)) {
                    i += 2;
                    System.out.println(cp);
                }
                else {
                    i++;
                    System.out.println((char)cp);
                }
            }
        }
    }
    

    代码分析:
    String实际上是char序列,那么对于补充字符如何正确的编写。由于Java在内存中使用UTF-16编码形式存储字符串,所以使用补充字符是应该按照UTF-16中规定的surrogate pair依次分别编写high-surrogate和low-surrogate两部分。

    Console输出:

    img

    正确使用Unicode转义字符.png

    正确遍历含有补充字符的字符串

    public class TestUnicode{
        public static void main(String[] args) {
            //char c = '\u1D546';
            char c = '\u2122';
            System.out.println("转义字符: " + c);
    
    
            String s3 = "\u0041\u00DF\u6771\ud801\uDC00\u2122";
            System.out.println(s3 + "\'s code unit count is " + s3.length());
            System.out.println(s3 + "\'s character is: ");
            for(int i = 0; i < s3.length(); i++) {
                System.out.println(s3.charAt(i));
            }
            int cpCount3 = s3.codePointCount(0, s3.length());
            System.out.println(s3 + "\'s code point count is " + cpCount3);
            //第一种遍历方式
            for (int i = 0; i < cpCount3; i++) {
                int index = s3.offsetByCodePoints(0, i);
                //System.out.println("i = " + i + ", index = " + index);
                int cp = s3.codePointAt(index);
                //System.out.println("code point is ");
                if (!Character.isSupplementaryCodePoint(cp)) {
                    System.out.println((char) cp);
                } else {
                    System.out.println(cp);
                }
            }
            System.out.println("另一种遍历方法: ");
            for(int i = 0; i < s3.length();) {
                int index = s3.offsetByCodePoints(i, 0);
                int cp = s3.codePointAt(index);
                if(Character.isSupplementaryCodePoint(cp)) {
                    i += 2;
                    System.out.println(cp);
                }else {
                    i++;
                    System.out.println((char)cp);
                }
            }
            System.out.println("回退遍历模式: ");
            for(int i = s3.length() - 1; i >= 0;) {
                char cu = s3.charAt(i);
                int cp = 0;
                if(Character.isSurrogate(cu)) {
                    i--; //因为是回退操作,如果是代理字符,那么一定是low-surrogate,所以先减一。
                    int index = s3.offsetByCodePoints(i, 0);
                    cp = s3.codePointAt(index);
                    i--;//获取到high-surrogate后,再减一,可以遍历下一个code unit。
                    System.out.println(cp);
                }else {
                    cp = s3.charAt(i);
                    System.out.println((char) cp);
                    i--;
                }
            }
        }
    }
    

    代码分析:
    首先看关键方法int offsetByCodePoints(int index, int codePointOffset)。文档解释说返回根据给定索引配合给定偏移量计算得到的code point索引,如果未匹配在surrogate范围内,算作为一个code point。感觉完全没说一样,那么来看看源码怎么实现的。

        public int offsetByCodePoints(int index, int codePointOffset) {
            if (index < 0 || index > value.length) {
                throw new IndexOutOfBoundsException();
            }
            return Character.offsetByCodePointsImpl(value, 0, value.length,
                    index, codePointOffset);
        }
    

    个人理解,源码提供的唯一信息就是参数index范围是String对象的code unit个数(也就是char[] value的长度)。由此有一种思路,通过指定当前遍历到的code unit来获取code point索引。于是,有了第二种遍历思路。

    第一种方法借鉴于Java 正确遍历字符串,既然是按照字符串code point个数来遍历为何还要通过offsetByCodePoint()来获取索引,多此一举。

    第三种回退遍历模式。

    从代码中可以看到codePointAt()方法返回的事int类型,这应该是为了适配surrogate pair模式。其值为Unicode字符集编码。

    Console输出:

    img

    遍历含有补充字符的String对象.png

    String.getBytes()使用

    官方文档解释,该方法主要根据平台的默认字符集,对String对象中的char sequence进行编码然后以字节序列的形式存储在一个字节数组中。

    MacOs默认字符集是UTF-8。

    当然可以使用它的重载方法进行指定Charset来实现编码。

    public class NewString {
        public static void main(String[] args) throws UnsupportedEncodingException{
            String str = "中";
            //获取指定字符集UTF-16的字节数组。
            byte[] b1 = str.getBytes("UTF-16"); //getByte()方法采用指定的字符集来保存编码
            for(byte b : b1) {
                System.out.println(Hex2Decimal.toHexString(b));
            }
            System.out.println();
            System.out.println("=====================");
            //获取默认字符集(UTF-8)的字节数组。
            byte[] b2 = str.getBytes();
            for(byte b : b2) {
                int i = b;
                //System.out.println(i);//输出是负数
                System.out.println(b & 0xff);//高24位置0,输出正确值
            }
            System.out.println();
            System.out.println("=====================");
            Charset c = Charset.forName("UTF-16");
            System.out.println(new String(b1, c));//在是用String(byte[])构造String对象时,需要使用字符集去解码,默认是UTF-8
            System.out.println(new String(b2));//其实就是先使用UTF-8Charset解码成Unicode编码,然后在内存中使用char[]来保存
        }
    }
    
    
    public class Hex2Decimal {
        public static String toHexString(byte b) {
            String tmp = Integer.toHexString(b & 0xff);
            if(tmp.length() == 1) {
                tmp = '0' + tmp;
            }
            return "0x" + tmp.toUpperCase();
        }
    }
    

    Console输出:

    img

    getBytes方法使用.png

    误区

    这里遇到两个坑:

    一、打印getBytes()返回的字节数组出现负数

    首先需要说明UTF-16字符集有两种,大尾序和小尾序,即 UTF-16BE 和 UTF-16LE,在编码前会放置一个U+FEFFU+FFFE(UTF-16BE 以 FEFF 代表,UTF-16LE 以 FFFE 代表)。所以打印指定UTF-16字符集获取的byte array前两个元素是FEFF

    但是直接打印分别是-2,-1,78,45。

    调用System.out.println()实际上会先对byte进行一个转型,变成int后再去打印。问题的关键就出在转型。由于FE最高位为1,而byte类型范围是-128~127,所以JVM认为它是负数。例如:

    “中”在UTF-8字符集中的编码由三个字节组成,第一个是228(二进制:1110 0100),如果直接转换为int为-28(前三个1作为符号位,表示负数)。

    值为FE的byte类型直接转换为int是-2,其实质就是把前6位作为符号位,最后两位作为-2的二进制码。

    byte转换成int类型是符号位扩展,所以直接转换就出现了误差。

    为了避免这种方法,在代码中应该进行高位置0操作,能够这样做是因为字符集编码绝对不会有负数。

    二、使用字节数组构造String对象乱码
    在代码中看到最后由前两个获取到的字节数组重新创建String对象时,第一个构造器中制定了字符集为UTF-16。这是因为默认使用UTF-8,如果使用默认的Charset映射UTF-16编码,存储在内存的是乱码。

    在内存实际上是通过编码的形式存储字符,如文本、String对象。而在流中实际上是以字节形式处理。

    展开全文
  • 1. 这两个对象的类型是... 获取方式不同:JspWriter是JSP的内置对象,直接使用即可,对象名out是保留字,也只能通过out来调用其相关方法。此外还可以通过内置对象pageContext.getOut();获得;PrintWriter则是在用的...
  • PrintWriter 是字符类型的打印输出流,它继承于Writer。接下来通过本文给大家介绍java中的 PrintWriter 相关知识,感兴趣的朋友一起学习吧
  • response.getWriter()返回的是PrintWriter,这是一个打印输出流 response.getWriter().write()和 response.getWriter().print()是响应给客户端的东西,如果不用ajax接收将数据放在合适的位...
  • 获取标准输出流 PrintWriter out = resp.getWriter(); 向标准输出流中写入数据,那么客户端浏览器就会直接看到这写数据 out.write()是字节输出流的方法 输出数字,显示的是其ASCII对应字符 out.write(97); 输出a ...
  • java解析数据接口获取json对象

    千次阅读 2019-07-07 14:18:49
    使用了Postman这个工具来解析,也获取了json对象,但后也发现,它没法直接连接数据库,也就是说这些数据不能直接存入数据库,经过查询,使用node.js作为中介可以解决这个问题,后又发现,连接后一次只能向数据库post...
  • PrintWriter对象的print和write方法的区别? PrintWriter out = response.getWriter(); out.print("数据"); out.write("数据"); 我在servlet中用print输出的东西,jsp页面能接受到,而用write输出就接受不到东西...
  • HttpServletRequest对象 1. HttpServletRequest接口获取请求行的相关方法 方法声明 功能描述 String getMethod( ) 该方法用于获取HTTP请求消息中的请求方式(如GET、POST等) String getRequestURI( ) 该...
  • 在学习AJAX获取servlet中JSON对象时遇到获取不到数据的问题。 问题情境:在jsp页面请求servlet 获取json串数据,将数据显示在jsp页面上的表格中。 如: 问题:无法将数据显示在表格中 解决如下: 代码: AJAX...
  • Java 打印流PrintWriter

    2017-10-15 15:38:25
    * 2 File对象 * 3 字节输出流 * 4 字符输出流 */ public static void main(String[] args) throws IOException { BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in)); ...
  • 于是...",如果这个说法是对的话,为什么我定义的PrintWriter对象会调用SocketOutputStream里面的flush()?如果这个说法不对,那么是什么导致调用write()之后flush()无效?求解惑,万分感谢!
  • 文章目录1、打印流-PrintStream 和 PrintWriter1.1、一览图1.2、代码实现2、Properties 类2.1、需求引出2.2、基本介绍2.3、应用案例3、练习 1、打印流-PrintStream 和 PrintWriter 1.1、一览图 打印流只有输出流,...
  • Servlet--获取Session对象

    千次阅读 2017-07-23 11:23:45
    Servlet--获取Session对象 一、获取Session对象 1、创建一个servlet类,实现获取session对象 package servlet08; import java.io.IOException; import java.io.PrintWriter; import javax.servlet....
  • 在JSP中,所有内容都写入PrintWriter out = response.getWriter();在页面的末尾,在向客户端发送响应之前,我想要保存此页面,无论是在文件中还是在缓冲区中作为字符串,以便以后处理。如何保存Printwriter内容或...
  • 通过request对象获取客户端请求信息

    千次阅读 2021-04-22 22:20:44
     HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。 二、Request常用方法 2.1、...
  • 说明writerToBeRead这个对象是 org.apache.catalina.connector.CoyoteWriter 类型的。如果您了解tomcat源代码,可以知道该类在TOMCAT_BASE/lib/catalina.jar中,这个jar由tomcat主程序调用,不被程序员显式使用,...
  • 主要介绍了java获取服务器基本信息,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 获取servletContext对象的两种方式

    千次阅读 2016-07-14 11:51:56
    获取servletContext对象 的两种方式 package com.cdsxt.action; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletContext; import javax.servlet.ServletException; ...
  • BufferedWriter:将文本写入字符输出流,缓冲各个字符从而提供单个字符,数组和字符串的高效写入。通过write()方法可以将获取到的...PrintWriter:向文本输出流打印对象的格式化表示形式(Prints formatted representat
  • 1调用PrintWriter的构造方法会创建一个新文件,如果文件已经存在,那么文件的当前内容将在不和用户确认的情况下被废弃... out是控制台的标准JAVA对象 4close()如果没有调用该方法,数据就不能正确地保存在文件中 ...
  • PrintStream,Printwriter提供了一系列重载的print()方法和println()方法用于输出,可以在System.out时设置其对象,使得其不输出在控制台上,输出到文件中 使用 使用打印流对sout重定向 public static void main...
  • 打印流:输出流 ...Web阶段学习时,从服务器端往客户端返回消息时用到response,response.getWriter()可以返回PrintWriter对象。 即 Web服务器往客户端(例如:浏览器)返回html网页时,用的是PrintWri
  • 一.HttpSession对象的特点 二.HttpSession对象的创建 三.HttpSession对象的使用 四.HttpSession的销毁方式 五.通过HttpSession实现客户端与服务端会话的维持 六.HttpSession生命周期 七.HttpSession对象总结 ...
  • Spring Boot当中获取request的三种方式

    千次阅读 2022-03-27 15:30:05
    获取字符输入流,只能操作字符数据 BufferedReader getReader() // 获取对象-获取字节输入流,既能操作字节也能操作字符 ServletInputStream getInputStream() 3.4、获取参数 // 获取请求参数通用方式(根据参数...
  • 有这样一种情况,如果我们需要在一些非Spring管理的对象中引用Spring管理的bean(这些场景还是挺多的,比如在web servlet中使用Spring管理的bean),其实这种情况下,就是如何获取ApplicationContext对象的问题,这...
  • HttpServletRequest对象

    2022-03-19 16:59:35
    1. HttpServletRequest接口获取请求行的相关方法 2. 获取请求头的相关方法 3.请求转发 4.获取请求参数 5. 通过Request对象传递数据

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 60,361
精华内容 24,144
关键字:

获取printwriter对象的是

友情链接: pascal.rar