文档库 最新最全的文档下载
当前位置:文档库 › 深入分析 Java 中的中文编码问题

深入分析 Java 中的中文编码问题

深入分析 Java 中的中文编码问题
深入分析 Java 中的中文编码问题

几种常见的编码格式

为什么要编码

不知道大家有没有想过一个问题,那就是为什么要编码?我们能不能不编码?要回答这个问题必须要回到计算机是如何表示我们人类能够理解的符号的,这些符号也就是我们人类使用的语言。由于人类的语言有太多,因而表示这些语言的符号太多,无法用计算机中一个基本的存储单元—— byte 来表示,因而必须要经过拆分或一些翻译工作,才能让计算机能理解。我们可以把计算机能够理解的语言假定为英语,其它语言要能够在计算机中使用必须经过一次翻译,把它翻译成英语。这个翻译的过程就是编码。所以可以想象只要不是说英语的国家要能够使用计算机就必须要经过编码。这看起来有些霸道,但是这就是现状,这也和我们国家现在在大力推广汉语一样,希望其它国家都会说汉语,以后其它的语言都翻译成汉语,我们可以把计算机中存储信息的最小单位改成汉字,这样我们就不存在编码问题了。

所以总的来说,编码的原因可以总结为:

1. 计算机中存储信息的最小单元是一个字节即8 个bit,所以能表示的字符范围是0~255 个

2. 人类要表示的符号太多,无法用一个字节来完全表示

3. 要解决这个矛盾必须需要一个新的数据结构char,从char 到byte 必须编码

如何“翻译”

明白了各种语言需要交流,经过翻译是必要的,那又如何来翻译呢?计算中提拱了多种翻译方式,常见的有ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等。它们都可以被看作为字典,它们规定了转化的规则,按照这个规则就可以让计算机正确的表示我们的字符。目前的编码格式很多,例如GB2312、GBK、UTF-8、UTF-16 这几种格式都可以表示一个汉字,那我们到底选择哪种编码格式来存储汉字呢?这就要考虑到其它因素了,是存储空间重要还是编码的效率重要。根据这些因素来正确选择编码格式,下面简要介绍一下这几种编码格式。

?ASCII 码

学过计算机的人都知道ASCII 码,总共有128 个,用一个字节的低7 位表示,0~31 是控制字符如换行回车删除等;32~126 是打印字符,可以通过键盘输入并且能够显示出来。

?ISO-8859-1

128 个字符显然是不够用的,于是ISO 组织在ASCII 码基础上又制定了一些列标准用来扩展ASCII 编码,它们是ISO-8859-1~ISO-8859-15,其中ISO-8859-1 涵盖了大多数西欧语言字符,所有应用的最广泛。ISO-8859-1 仍然是单字节编码,它总共能表示256 个字符。

?GB2312

它的全称是《信息交换用汉字编码字符集基本集》,它是双字节编码,总的编码范围是A1-F7,其中从A1-A9 是符号区,总共包含682 个符号,从B0-F7 是汉字区,包含6763 个汉字。

?GBK

全称叫《汉字内码扩展规范》,是国家技术监督局为windows95 所制定的新的汉字内码规范,它的出现是为了扩展GB2312,加入更多的汉字,它的编码范围是8140~FEFE(去掉XX7F)总共有23940 个码位,它能表示21003 个汉字,它的编码是和GB2312 兼容的,也就是说用GB2312 编码的汉字可以用GBK 来解码,并且不会有乱码。

?GB18030

全称是《信息交换用汉字编码字符集》,是我国的强制标准,它可能是单字节、双字节或者四字节编码,它的编码与GB2312 编码兼容,这个虽然是国家标准,但是实际应用系统中使用的并不广泛。

?UTF-16

说到UTF 必须要提到Unicode(Universal Code 统一码),ISO 试图想创建一个全新的超语言字典,世界上所有的语言都可以通过这本字典来相互翻译。可想而知这个字典是多么的复杂,关于Unicode 的

详细规范可以参考相应文档。Unicode 是Java 和XML 的基础,下面详细介绍Unicode 在计算机中的存储形式。

UTF-16 具体定义了Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是16 个bit,所以叫UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是Java 以UTF-16 作为内存的字符存储格式的一个很重要的原因。

UTF-8

UTF-16 统一采用两个字节表示一个字符,虽然在表示上非常简单方便,但是也有其缺点,有很大一部分字符用一个字节就可以表示的现在要两个字节表示,存储空间放大了一倍,在现在的网络带宽还非常有限的今天,这样会增大网络传输的流量,而且也没必要。而UTF-8 采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以是由1~6 个字节组成。

UTF-8 有以下编码规则:

1. 如果一个字节,最高位(第8 位)为0,表示这是一个ASCII 字符(00 - 7F)。可见,所有ASCII

编码已经是UTF-8 了。

2. 如果一个字节,以11 开头,连续的1 的个数暗示这个字符的字节数,例如:110xxxxx 代表它

是双字节UTF-8 字符的首字节。

3. 如果一个字节,以10 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节

回页首Java 中需要编码的场景

前面描述了常见的几种编码格式,下面将介绍Java 中如何处理对编码的支持,什么场合中需要编码。

I/O 操作中存在的编码

我们知道涉及到编码的地方一般都在字符到字节或者字节到字符的转换上,而需要这种转换的场景主要是在I/O 的时候,这个I/O 包括磁盘I/O 和网络I/O,关于网络I/O 部分在后面将主要以Web 应用为例介绍。下图是Java 中处理I/O 问题的接口:

Reader 类是Java 的I/O 中读字符的父类,而InputStream类是读字节的父类,InputStreamReader类就是关联字节到字符的桥梁,它负责在I/O 过程中处理读取字节到字符的转换,而具体字节到字符的解码实现它由StreamDecoder去实现,在StreamDecoder解码过程中必须由用户指定Charset 编码格式。值得注意的是如果你没有指定Charset,将使用本地环境中的默认字符集,例如在中文环境中将使用GBK 编码。

写的情况也是类似,字符的父类是Writer,字节的父类是OutputStream,通过OutputStreamWriter转换字符到字节。如下图所示:

同样StreamEncoder类负责将字符编码成字节,编码格式和默认编码规则与解码是一致的。如下面一段代码,实现了文件的读写功能:

清单1.I/O 涉及的编码示例

String file = "c:/stream.txt";

String charset = "UTF-8";

// 写字符换转成字节流

FileOutputStreamoutputStream = new FileOutputStream(file); OutputStreamWriter writer = new OutputStreamWriter(

outputStream, charset);

try {

writer.write("这是要保存的中文字符");

} finally {

writer.close();

}

// 读取字节转换成字符

FileInputStreaminputStream = new FileInputStream(file); InputStreamReader reader = new InputStreamReader(

inputStream, charset);

StringBuffer buffer = new StringBuffer();

char[] buf = new char[64];

int count = 0;

try {

while ((count = reader.read(buf)) != -1) {

buffer.append(buffer, 0, count);

}

} finally {

reader.close();

}

以上这些提供字符和字节之间的相互转换只要我们设置编解码格式统一一般都不会出现问题。

回页首Java 中如何编解码

前面介绍了几种常见的编码格式,这里将以实际例子介绍Java 中如何实现编码及解码,下面我们以“I am 君山”这个字符串为例介绍Java 中如何把它以ISO-8859-1、GB2312、GBK、UTF-16、UTF-8 编码格式进行编码的。

清单2.String 编码

public static void encode() {

String name = "I am 君山";

toHex(name.toCharArray());

try {

byte[] iso8859 = name.getBytes("ISO-8859-1");

toHex(iso8859);

byte[] gb2312 = name.getBytes("GB2312");

toHex(gb2312);

byte[] gbk = name.getBytes("GBK");

toHex(gbk);

byte[] utf16 = name.getBytes("UTF-16");

toHex(utf16);

byte[] utf8 = name.getBytes("UTF-8");

toHex(utf8);

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

}

}

我们把name 字符串按照前面说的几种编码格式进行编码转化成byte 数组,然后以16 进制输出,我们先看一下Java 是如何进行编码的。

下面是Java 中编码需要用到的类图

图1. Java 编码类图

首先根据指定的charsetName通过Charset.forName(charsetName) 设置Charset 类,然后根据Charset 创建CharsetEncoder对象,再调用CharsetEncoder.encode对字符串进行编码,不同的编码类型都会对应到一个类中,实际的编码过程是在这些类中完成的。下面是String. getBytes(charsetName) 编码过程的时序图

图2.Java 编码时序图

从上图可以看出根据charsetName找到Charset 类,然后根据这个字符集编码生成CharsetEncoder,这个类是所有字符编码的父类,针对不同的字符编码集在其子类中定义了如何实现编码,有了

CharsetEncoder对象后就可以调用encode 方法去实现编码了。这个是String.getBytes编码方法,其它的如StreamEncoder中也是类似的方式。下面看看不同的字符集是如何将前面的字符串编码成byte 数组的?

如字符串“I am 君山”的char 数组为49 20 61 6d 20 541b 5c71,下面把它按照不同的编码格式转化成相应的字节。

按照ISO-8859-1 编码

字符串“I am 君山”用ISO-8859-1 编码,下面是编码结果:

从上图看出7 个char 字符经过ISO-8859-1 编码转变成7 个byte 数组,ISO-8859-1 是单字节编码,中文“君山”被转化成值是3f 的byte。3f 也就是“?”字符,所以经常会出现中文变成“?”很可能就是错误的使用了ISO-8859-1 这个编码导致的。中文字符经过ISO-8859-1 编码会丢失信息,通常我们称之为“黑洞”,它会把不认识的字符吸收掉。由于现在大部分基础的Java 框架或系统默认的字符集编码都是

ISO-8859-1,所以很容易出现乱码问题,后面将会分析不同的乱码形式是怎么出现的。

按照GB2312 编码

字符串“I am 君山”用GB2312 编码,下面是编码结果:

如果查到的码位值大于oxff则是双字节,否则是单字节。双字节高8 位作为第一个字节,低8 位作为第二个字节,如下代码所示:

从上图可以看出前 5 个字符经过编码后仍然是 5 个字节,而汉字被编码成双字节,在第一节中介绍到GB2312 只支持6763 个汉字,所以并不是所有汉字都能够用GB2312 编码。

按照GBK 编码

字符串“I am 君山”用GBK 编码,下面是编码结果:

你可能已经发现上图与GB2312 编码的结果是一样的,没错GBK 与GB2312 编码结果是一样的,由此可以得出GBK 编码是兼容GB2312 编码的,它们的编码算法也是一样的。不同的是它们的码表长度不一样,GBK 包含的汉字字符更多。所以只要是经过GB2312 编码的汉字都可以用GBK 进行解码,反过来则不然。

按照UTF-16 编码

字符串“I am 君山”用UTF-16 编码,下面是编码结果:

用UTF-16 编码将char 数组放大了一倍,单字节范围内的字符,在高位补0 变成两个字节,中文字符也变成两个字节。从UTF-16 编码规则来看,仅仅将字符的高位和地位进行拆分变成两个字节。特点是编

码效率非常高,规则很简单,由于不同处理器对 2 字节处理方式不同,Big-endian(高位字节在前,低位字节在后)或Little-endian(低位字节在前,高位字节在后)编码,所以在对一串字符串进行编码是需要指明到底是Big-endian 还是Little-endian,所以前面有两个字节用来保存BYTE_ORDER_MARK 值,UTF-16 是用定长16 位(2 字节)来表示的UCS-2 或Unicode 转换格式,通过代理对来访问BMP 之外的字符编码。

按照UTF-8 编码

字符串“I am 君山”用UTF-8 编码,下面是编码结果:

UTF-16 虽然编码效率很高,但是对单字节范围内字符也放大了一倍,这无形也浪费了存储空间,另外UTF-16 采用顺序编码,不能对单个字符的编码值进行校验,如果中间的一个字符码值损坏,后面的所有码值都将受影响。而UTF-8 这些问题都不存在,UTF-8 对单字节范围内字符仍然用一个字节表示,对汉字采用三个字节表示。它的编码规则如下:

return overflow(src, sp, dst, dp);

da[dp++] = (byte)(0xc0 | (c >> 6));

da[dp++] = (byte)(0x80 | (c & 0x3f));

} else if (Character.isSurrogate(c)) {

// Have a surrogate pair

if (sgp == null)

sgp = new Surrogate.Parser();

intuc = sgp.parse(c, sa, sp, sl);

if (uc< 0) {

updatePositions(src, sp, dst, dp);

return sgp.error();

}

if (dl - dp< 4)

return overflow(src, sp, dst, dp);

da[dp++] = (byte)(0xf0 | ((uc>> 18)));

da[dp++] = (byte)(0x80 | ((uc>> 12) & 0x3f));

da[dp++] = (byte)(0x80 | ((uc>> 6) & 0x3f));

da[dp++] = (byte)(0x80 | (uc& 0x3f));

sp++; // 2 chars

} else {

// 3 bytes, 16 bits

if (dl - dp< 3)

return overflow(src, sp, dst, dp);

da[dp++] = (byte)(0xe0 | ((c >> 12)));

da[dp++] = (byte)(0x80 | ((c >> 6) & 0x3f));

da[dp++] = (byte)(0x80 | (c & 0x3f));

}

sp++;

}

updatePositions(src, sp, dst, dp);

return CoderResult.UNDERFLOW;

}

UTF-8 编码与GBK 和GB2312 不同,不用查码表,所以在编码效率上UTF-8 的效率会更好,所以在存储中文字符时UTF-8 编码比较理想。

几种编码格式的比较

对中文字符后面四种编码格式都能处理,GB2312 与GBK 编码规则类似,但是GBK 范围更大,它能处理所有汉字字符,所以GB2312 与GBK 比较应该选择GBK。UTF-16 与UTF-8 都是处理Unicode 编码,它们的编码规则不太相同,相对来说UTF-16 编码效率最高,字符到字节相互转换更简单,进行字符串操作也更好。它适合在本地磁盘和内存之间使用,可以进行字符和字节之间快速切换,如Java 的内存编码就是采用UTF-16 编码。但是它不适合在网络之间传输,因为网络传输容易损坏字节流,一旦字节流损坏将很难恢复,想比较而言UTF-8 更适合网络传输,对ASCII 字符采用单字节存储,另外单个字符损

坏也不会影响后面其它字符,在编码效率上介于GBK 和UTF-16 之间,所以UTF-8 在编码效率上和编码安全性上做了平衡,是理想的中文编码方式。

回页首Java Web 涉及到的编码

对于使用中文来说,有I/O 的地方就会涉及到编码,前面已经提到了I/O 操作会引起编码,而大部分I/O 引起的乱码都是网络I/O,因为现在几乎所有的应用程序都涉及到网络操作,而数据经过网络传输都是以字节为单位的,所以所有的数据都必须能够被序列化为字节。在Java 中数据被序列化必须继承Serializable接口。

这里有一个问题,你是否认真考虑过一段文本它的实际大小应该怎么计算,我曾经碰到过一个问题:就是要想办法压缩Cookie 大小,减少网络传输量,当时有选择不同的压缩算法,发现压缩后字符数是减少了,但是并没有减少字节数。所谓的压缩只是将多个单字节字符通过编码转变成一个多字节字符。减少的是String.length(),而并没有减少最终的字节数。例如将“ab”两个字符通过某种编码转变成一个奇怪的字符,虽然字符数从两个变成一个,但是如果采用UTF-8 编码这个奇怪的字符最后经过编码可能又会变成三个或更多的字节。同样的道理比如整型数字1234567 如果当成字符来存储,采用UTF-8 来编码占用7 个byte,采用UTF-16 编码将会占用14 个byte,但是把它当成int型数字来存储只需要4 个byte 来存储。所以看一段文本的大小,看字符本身的长度是没有意义的,即使是一样的字符采用不同的编码最终存储的大小也会不同,所以从字符到字节一定要看编码类型。

另外一个问题,你是否考虑过,当我们在电脑中某个文本编辑器里输入某个汉字时,它到底是怎么表示的?我们知道,计算机里所有的信息都是以01 表示的,那么一个汉字,它到底是多少个0 和1 呢?我们能够看到的汉字都是以字符形式出现的,例如在Java 中“淘宝”两个字符,它在计算机中的数值10 进制是28120 和23453,16 进制是6bd8 和5d9d,也就是这两个字符是由这两个数字唯一表示的。Java 中一个char 是16 个bit 相当于两个字节,所以两个汉字用char 表示在内存中占用相当于四个字节的空间。

这两个问题搞清楚后,我们看一下Java Web 中那些地方可能会存在编码转换?

用户从浏览器端发起一个HTTP 请求,需要存在编码的地方是URL、Cookie、Parameter。服务器端接受到HTTP 请求后要解析HTTP 协议,其中URI、Cookie 和POST 表单参数需要解码,服务器端可能还需要读取数据库中的数据,本地或网络中其它地方的文本文件,这些数据都可能存在编码问题,当Servlet 处理完所有请求的数据后,需要将这些数据再编码通过Socket 发送到用户请求的浏览器里,再经过浏览器解码成为文本。这些过程如下图所示:

图3. 一次HTTP 请求的编码示例(查看大图)

如上图所示一次HTTP 请求设计到很多地方需要编解码,它们编解码的规则是什么?下面将会重点阐述一下:

URL 的编解码

用户提交一个URL,这个URL 中可能存在中文,因此需要编码,如何对这个URL 进行编码?根据什么规则来编码?有如何来解码?如下图一个URL:

图4.URL 的几个组成部分

中配置,PathInfo是我们请求的具体的Servlet,QueryString是要传递的参数,注意这里是在浏览器里直接输入URL 所以是通过Get 方法请求的,如果是POST 方法请求的话,QueryString将通过表单方式提交到服务器端,这个将在后面再介绍。

上图中PathInfo和QueryString出现了中文,当我们在浏览器中直接输入这个URL 时,在浏览器端和服务端会如何编码和解析这个URL 呢?为了验证浏览器是怎么编码URL 的我们选择FireFox浏览器并通过HTTPFox插件观察我们请求的URL 的实际的内容,以下是URL:

HTTP://localhost:8080/examples/servlets/servlet/ 君山?author= 君山在中文FireFox3.6.12 的测试结果

图5. HTTPFox的测试结果

君山的编码结果分别是:e5 90 9b e5 b1 b1,be fd c9 bd,查阅上一届的编码可知,PathInfo是UTF-8 编码而QueryString是经过GBK 编码,至于为什么会有“%”?查阅URL 的编码规范RFC3986 可知浏览器编码URL 是将非ASCII 字符按照某种编码格式编码成16 进制数字然后将每个16 进制表示的字节前加上“%”,所以最终的URL 就成了上图的格式了。

默认情况下中文IE 最终的编码结果也是一样的,不过IE 浏览器可以修改URL 的编码格式在选项->高级->国际里面的发送UTF-8 URL 选项可以取消。

从上面测试结果可知浏览器对PathInfo和QueryString的编码是不一样的,不同浏览器对PathInfo也可能不一样,这就对服务器的解码造成很大的困难,下面我们以Tomcat 为例看一下,Tomcat 接受到这个URL 是如何解码的。

解析请求的URL 是在org.apache.coyote.HTTP11.InternalInputBuffer 的parseRequestLine方法中,这个方法把传过来的URL 的byte[] 设置到org.apache.coyote.Request的相应的属性中。这里的URL 仍然是byte 格式,转成char 是在org.apache.catalina.connector.CoyoteAdapter的convertURI方法中完成的:

protected void convertURI(MessageBytesuri, Request request)

throws Exception {

ByteChunkbc = uri.getByteChunk();

int length = bc.getLength();

CharChunk cc = uri.getCharChunk();

cc.allocate(length, -1);

String enc = connector.getURIEncoding();

if (enc != null) {

B2CConverter conv = request.getURIConverter();

try {

if (conv == null) {

conv = new B2CConverter(enc);

request.setURIConverter(conv);

}

} catch (IOException e) {...}

if (conv != null) {

try {

conv.convert(bc, cc, cc.getBuffer().length -

cc.getEnd());

uri.setChars(cc.getBuffer(), cc.getStart(),

cc.getLength());

return;

} catch (IOException e) {...}

}

}

// Default encoding: fast conversion

byte[] bbuf = bc.getBuffer();

char[] cbuf = cc.getBuffer();

int start = bc.getStart();

for (inti = 0; i< length; i++) {

cbuf[i] = (char) (bbuf[i + start] & 0xff);

从上面的代码中可以知道对URL 的URI 部分进行解码的字符集是在connector 的中定义的,如果没有定义,那么将以默认编码ISO-8859-1 解析。所以如果有中文URL 时最好把URIEncoding设置成UTF-8 编码。

QueryString又如何解析?GET 方式HTTP 请求的QueryString与POST 方式HTTP 请求的表单参数都是作为Parameters 保存,都是通过request.getParameter获取参数值。对它们的解码是在

request.getParameter方法第一次被调用时进行的。request.getParameter方法被调用时将会调用

org.apache.catalina.connector.Request的parseParameters方法。这个方法将会对GET 和POST 方式传递的参数进行解码,但是它们的解码字符集有可能不一样。POST 表单的解码将在后面介绍,QueryString 的解码字符集是在哪定义的呢?它本身是通过HTTP 的Header 传到服务端的,并且也在URL 中,是否和URI 的解码字符集一样呢?从前面浏览器对PathInfo和QueryString的编码采取不同的编码格式不同可以猜测到解码字符集肯定也不会是一致的。的确是这样QueryString的解码字符集要么是Header 中ContentType中定义的Charset 要么就是默认的ISO-8859-1,要使用ContentType中定义的编码就要设置connector 的中的useBodyEncodingForURI设置为true。这个配置项的名字有点让人产生混淆,它并不是对整个URI 都采用BodyEncoding进行解码而仅仅是对QueryString使用BodyEncoding解码,这一点还要特别注意。

从上面的URL 编码和解码过程来看,比较复杂,而且编码和解码并不是我们在应用程序中能完全控制的,所以在我们的应用程序中应该尽量避免在URL 中使用非ASCII 字符,不然很可能会碰到乱码问题,当然在我们的服务器端最好设置中的URIEncoding和useBodyEncodingForURI两个参数。HTTP Header 的编解码

当客户端发起一个HTTP 请求除了上面的URL 外还可能会在Header 中传递其它参数如Cookie、redirectPath等,这些用户设置的值很可能也会存在编码问题,Tomcat 对它们又是怎么解码的呢?

对Header 中的项进行解码也是在调用request.getHeader是进行的,如果请求的Header 项没有解码则调用MessageBytes的toString方法,这个方法将从byte 到char 的转化使用的默认编码也是

ISO-8859-1,而我们也不能设置Header 的其它解码格式,所以如果你设置Header 中有非ASCII 字符解码肯定会有乱码。

我们在添加Header 时也是同样的道理,不要在Header 中传递非ASCII 字符,如果一定要传递的话,我们可以先将这些字符用org.apache.catalina.util.URLEncoder编码然后再添加到Header 中,这样在浏览器到服务器的传递过程中就不会丢失信息了,如果我们要访问这些项时再按照相应的字符集解码就好了。POST 表单的编解码

在前面提到了POST 表单提交的参数的解码是在第一次调用request.getParameter发生的,POST 表单参数传递方式与QueryString不同,它是通过HTTP 的BODY 传递到服务端的。当我们在页面上点击submit 按钮时浏览器首先将根据ContentType的Charset 编码格式对表单填的参数进行编码然后提交到服务器端,在服务器端同样也是用ContentType中字符集进行解码。所以通过POST 表单提交的参数一般不会出现问题,而且这个字符集编码是我们自己设置的,可以通过

request.setCharacterEncoding(charset) 来设置。

另外针对multipart/form-data 类型的参数,也就是上传的文件编码同样也是使用ContentType定义的字符集编码,值得注意的地方是上传文件是用字节流的方式传输到服务器的本地临时目录,这个过程并没有涉

访问数据库都是通过客户端JDBC 驱动来完成,用JDBC 来存取数据要和数据的内置编码保持一致,可以通过设置JDBC URL 来制定如MySQL:

url="jdbc:mysql://localhost:3306/DB?useUnicode=true&characterEncoding=GBK"。

回页首常见问题分析

在了解了Java Web 中可能需要编码的地方后,下面看一下,当我们碰到一些乱码时,应该怎么处理这些问题?出现乱码问题唯一的原因都是在char 到byte 或byte 到char 转换中编码和解码的字符集不一致导致的,由于往往一次操作涉及到多次编解码,所以出现乱码时很难查找到底是哪个环节出现了问题,下面就几种常见的现象进行分析。

中文变成了看不懂的字符

例如,字符串“淘!我喜欢!”变成了“D ? £ ?? ò ?2?? £ ?”编码过程如下图所示

字符串在解码时所用的字符集与编码字符集不一致导致汉字变成了看不懂的乱码,而且是一个汉字字符变成两个乱码字符。

一个汉字变成一个问号

例如,字符串“淘!我喜欢!”变成了“??????”编码过程如下图所示

将中文和中文符号经过不支持中文的ISO-8859-1 编码后,所有字符变成了“?”,这是因为用ISO-8859-1 进行编解码时遇到不在码值范围内的字符时统一用3f 表示,这也就是通常所说的“黑洞”,所有

ISO-8859-1 不认识的字符都变成了“?”。

一个汉字变成两个问号

例如,字符串“淘!我喜欢!”变成了“????????????”编码过程如下图所示

解析时取得的value 会是正确的汉字字符,这种情况是怎么造成的呢?看下如所示:

这种情况是这样的,ISO-8859-1 字符集的编码范围是0000-00FF,正好和一个字节的编码范围相对应。这种特性保证了使用ISO-8859-1 进行编码和解码可以保持编码数值“不变”。虽然中文字符在经过网络传输时,被错误地“拆”成了两个欧洲字符,但由于输出时也是用ISO-8859-1,结果被“拆”开的中文字的两半又被合并在一起,从而又刚好组成了一个正确的汉字。虽然最终能取得正确的汉字,但是还是不建议用这种不正常的方式取得参数值,因为这中间增加了一次额外的编码与解码,这种情况出现乱码时因为Tomcat 的配置文件中useBodyEncodingForURI配置项没有设置为”true”,从而造成第一次解析式用ISO-8859-1 来解析才造成乱码的。

回页首总结

本文首先总结了几种常见编码格式的区别,然后介绍了支持中文的几种编码格式,并比较了它们的使用场景。接着介绍了Java 那些地方会涉及到编码问题,已经Java 中如何对编码的支持。并以网络I/O 为例重点介绍了HTTP 请求中的存在编码的地方,以及Tomcat 对HTTP 协议的解析,最后分析了我们平常遇到的乱码问题出现的原因。

综上所述,要解决中文问题,首先要搞清楚哪些地方会引起字符到字节的编码以及字节到字符的解码,最常见的地方就是读取会存储数据到磁盘,或者数据要经过网络传输。然后针对这些地方搞清楚操作这些数据的框架的或系统是如何控制编码的,正确设置编码格式,避免使用软件默认的或者是操作系统平台默认的编码格式。

参考资料

学习

Unicode 编码规范,详细描述了Unicode 如何编码。

?ISO-8859-1 编码,详细介绍了ISO-8859-1 的一些细节。

?RFC3986 规范,详细描述了URL 编码规范

?HTTP 协议,W3C 关于HTTP 协议的详细描述。

?查看文章《Tomcat 系统架构与设计模式》(developerWorks,2010 年5 月):了解Tomcat 中容器的体系结构,基本的工作原理,以及Tomcat 中使用的经典的设计模式介绍。

?Servlet 工作原理解析,(developerWorks,2011 年2 月):以Tomcat 为例了解Servlet 容器是如何工作的?一个Web 工程在Servlet 容器中是如何启动的?Servlet 容器如何解析你

在web.xml 中定义的Servlet ?用户的请求是如何被分配给指定的Servlet 的?Servlet 容器如何管理Servlet 生命周期?你还将了解到最新的Servlet 的API 的类层次结构,以及Servlet 中一些难点问题的分析。

?developerWorks Java 技术专区:这里有数百篇关于Java 编程各个方面的文章。

讨论

?加入developerWorks中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他developerWorks用户交流。

javamath类常用方法

例如求平方根的(n),求a的b次方(a, b),求绝对值(n)等很多。下面是一些演示。publicclassMathTest { publicstaticvoidmain(String[]args) { intn=16; ? 3)); ? ? } } publicclassMathDemo{ publicstaticvoidmain(Stringargs[]){ /** *abs求绝对值 */ 的时候会取偶数 */ // // // // // // // // // // /** *round四舍五入,float时返回int值,double时返回long值 */ //10 //11 //11 //11 //-10 //-11 //-11 //-10 } }

函数(方法) 描述 IEEEremainder(double,double) 按照IEEE754标准的规定,对两个参数进行余数运算。 abs(inta) 返回int值的绝对值 abs(longa) 返回long值的绝对值 abs(floata) 返回float值的绝对值 abs(doublea) 返回double值的绝对值 acos(doublea) 返回角的反余弦,范围在到pi之间 asin(doublea) 返回角的反正弦,范围在-pi/2到pi/2之间 atan(doublea) 返回角的反正切,范围在-pi/2到pi/2之间 atan2(doublea,doubleb) 将矩形坐标(x,y)转换成极坐标(r,theta) ceil(doublea) 返回最小的(最接近负无穷大)double值,该值大于或等于参数,并且等于某个整数cos(double) 返回角的三角余弦 exp(doublea) 返回欧拉数e的double次幂的值 floor(doublea) 返回最大的(最接近正无穷大)double值,该值小于或等于参数,并且等于某个整数log(doublea) 返回(底数是e)double值的自然对数 max(inta,intb) 返回两个int值中较大的一个 max(longa,longb) 返回两个long值中较大的一个 max(floata,floatb) 返回两个float值中较大的一个 max(doublea,doubleb) 返回两个double值中较大的一个 min(inta,intb) 返回两个int值中较小的一个 min(longa,longb) 返回两个long值中较小的一个 min(floata,floatb)

JAVA中常用类的常用方法

JAVA中常用类的常用方法 一、类 1、clone()方法 创建并返回此对象的一个副本。要进行“ 克隆” 的对象所属的类必须实现. Cloneable接口。 2、equals(Object obj)方法 功能:比较引用类型数据的等价性。 等价标准:引用类型比较引用,基本类型比较值。 存在特例:对File、String、Date及封装类等类型来说,是比较类型及对象的内 容而不考虑引用的是否为同一实例。 3、finalize()方法 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 4、hashCode()方法 返回该对象的哈希码值。 5、notify()方法 唤醒在此对象监视器上等待的单个线程。 6、notifyAll()方法 唤醒在此对象监视器上等待的所有线程。 7、toString()方法 返回该对象的字符串表示。在进行String与其它类型数据的连接操作时,自动调用toString()方法。可以根据需要重写toString()方法。 8、wait()方法 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 二、字符串相关类 String类 charAt(int index) 返回指定索引处的 char 值。 compareTo(String anotherString) 按字典顺序比较两个字符串。 compareToIgnoreCase(String str) 按字典顺序比较两个字符串,不考虑大小写。 concat(String str) 将指定字符串连接到此字符串的结尾。 endsWith(String suffix) 测试此字符串是否以指定的后缀结束。 equals(Object anObject) 将此字符串与指定的对象比较。 equalsIgnoreCase(String anotherString) 将此 String 与另一个 String 比 较,不考虑大小写。 indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引。 indexOf(String str) 返回第一次出现的指定子字符串在此字符串中的索引。 lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引。 length() 返回此字符串的长度。 replace(char oldChar, char newChar)

JAVA中常用类的常用方法

JAVA屮常用类的常用方法 一.java?丨ang.Object 类 1、clone()方法 创建丼返M此对象的一个副木。要进行“克隆”的对象所属的类必须实现https://www.wendangku.net/doc/4c2483079.html,ng. Cloneable 接口。 2、equals(Objectobj)方法 0 功能:比较引用类型数据的等价性。 0 等价标准.?引用类型比较引用,基木类型比较值。 0 存在特例.?对File、String、Date及封装类等类型来说,是比较类型及对象的内稃而+ 考虑引用的是否为同一实例。 3、finalize〇方法 当垃圾丨"丨收器确定>(、存在对该对象的更多引用时,由对象的垃圾丨"丨收器调用此方法。 4、hashCode〇方法返 回该对象的哈希码值。 5、notify〇方法 唤醒在此对象监视器上等待的中?个线祝。 6、notifyAII〇方法 唤醒在此对象监视器上等待的所有线程= 7、toString()方法 返W该对象的字符串表示。在进行String与其它类型数据的连接操作时,&动调用tostringo 方法。可以根据耑要重写toStringO方法。 8、wait()方法 在其他线程调用此对象的n〇tify()方法或notifyAIIO方法前,异致当前线程等待。 二、字符串相关类 I String 类 charAt(int index)返回指定索引处的char值。compareTo{String anotherString)按字

典顺序比较两个字符串。compareTolgnoreCase(Stringstr)按字典顺序比较两个字 符串,不考虑人小写。concat(String str)将指定字符串连接到此字符串的结尾。 endsWith(String suffix)测试此字符串是否以指定的〗?缀结束。equals{Object anObject)将此字符串与指定的对象比较。 equalslgnoreCase(String anotherString)将此String 与另一个String 比较,考虑人小'与’。indexOf(int ch)返H指定字符在此字符串屮第一次出现处的索引。 indexOf(String str)返回第一次出现的指定子字符串在此字符串屮的索引, lastlndexOf(intch)返回指定字符在此字符串中最后??次出现处的索引。 length()返|n丨此字符串的长度。 replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用newChar替换此字符串中出现的所有oldChar得到的。 split(String regex)根据给定正则表达式的匹配拆分此字符串。startsWith{String prefix)测试此字符 串是否以指定的前缀开始。substring(int beginlndex) 返回一个新的字符串,它是此字符串的一个子字符串。该子字符串始于指定索引处的字符,一直到此字符串末尾。 substring(int beginlndex, int endlndex) 返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的beginlndex 处开始,一直到索引endlndex-1处的字符。 t〇CharArray()将此字符串转换为一个新的字符数组。

JAVA中常用类的常用方法

JAVA中常用类的常用方法 一、https://www.wendangku.net/doc/4c2483079.html,ng.Object类 1、clone()方法 创建并返回此对象的一个副本。要进行“克隆”的对象所属的类必须实现https://www.wendangku.net/doc/4c2483079.html,ng. Cloneable接口。 2、equals(Object obj)方法 ?功能:比较引用类型数据的等价性。 ?等价标准:引用类型比较引用,基本类型比较值。 ?存在特例:对、Date及封装类等类型来说,是比较类型及对象的内容而不考虑引用的是否为同一实例。 3、finalize()方法 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 4、hashCode()方法返回该对象的哈希码值。 5、notify()方法唤醒在此对象监视器上等待的单个线程。 6、notifyAll()方法唤醒在此对象监视器上等待的所有线程。 7、toString()方法 返回该对象的字符串表示。在进行String与其它类型数据的连接操作时,自动调用toString()方法。可以根据需要重写toString()方法。 8、wait()方法 在其他线程调用此对象的notify() 方法或notifyAll() 方法前,导致当前线程等待。 二、字符串相关类 l String类 charAt(int index) 返回指定索引处的char 值。 compareTo(String anotherString) 按字典顺序比较两个字符串。 compareToIgnoreCase(String str) 按字典顺序比较两个字符串,不考虑大小写。 concat(String str) 将指定字符串连接到此字符串的结尾。 endsWith(String suffix) 测试此字符串是否以指定的后缀结束。 equals(Object anObject) 将此字符串与指定的对象比较。 equalsIgnoreCase(String anotherString) 将此String 与另一个String 比较,不考虑大小写。indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引。 indexOf(String str) 返回第一次出现的指定子字符串在此字符串中的索引。 lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引。 length() 返回此字符串的长度。 replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用newChar 替换此字符串中出现的所有oldChar 得到的。split(String regex) 根据给定正则表达式的匹配拆分此字符串。 startsWith(String prefix) 测试此字符串是否以指定的前缀开始。 substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个子字符串。该子字符串始于指定索引处的字符,一直到此字符串末尾。 substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的beginIndex 处开

JavaMath类常用方法

例如求平方根的Math.sqrt(n),求a的b次方Math.pow(a, b),求绝对值Math.abs(n)等很多。下面是一些演示。 public class MathTest { public static void main(String[] args) { int n = 16; System.out.println(Math.sqrt(n)); System.out.println(Math.pow(2, 3)); System.out.println(Math.abs(-4)); System.out.println(Math.log10(100)); } } public class MathDemo { public static void main(String args[]){ /** * abs求绝对值 */ System.out.println(Math.abs(-10.4)); //10.4 System.out.println(Math.abs(10.1)); //10.1 /** * ceil天花板的意思,就是返回大的值,注意一些特殊值 */ System.out.println(Math.ceil(-10.1)); //-10.0 System.out.println(Math.ceil(10.7)); //11.0 System.out.println(Math.ceil(-0.7)); //-0.0 System.out.println(Math.ceil(0.0)); //0.0 System.out.println(Math.ceil(-0.0)); //-0.0 /** * floor地板的意思,就是返回小的值 */ System.out.println(Math.floor(-10.1)); //-11.0 System.out.println(Math.floor(10.7)); //10.0 System.out.println(Math.floor(-0.7)); //-1.0

java中常用的一些方法

java中常用的一些方法 字符串 1、获取字符串的长度 length() 2 、判断字符串的前缀或后缀与已知字符串是否相同 前缀startsWith(String s) 后缀endsWith(String s) 3、比较两个字符串 equals(String s) 4、把字符串转化为相应的数值 int型Integer.parseInt(字符串) long型Long.parseLong(字符串) float型Folat.valueOf(字符串).floatValue() double型Double.valueOf(字符串).doubleValue() 4、将数值转化为字符串 valueOf(数值) 5、字符串检索 indexOf(Srting s) 从头开始检索 indexOf(String s ,int startpoint) 从startpoint处开始检索 如果没有检索到,将返回-1 6、得到字符串的子字符串 substring(int startpoint) 从startpoint处开始获取 substring(int start,int end) 从start到end中间的字符 7、替换字符串中的字符,去掉字符串前后空格 replace(char old,char new) 用new替换old trim() 8、分析字符串 StringTokenizer(String s) 构造一个分析器,使用默认分隔字符(空格,换行,回车,Tab,进纸符)StringTokenizer(String s,String delim) delim是自己定义的分隔符

nextToken() 逐个获取字符串中的语言符号 boolean hasMoreTokens() 只要字符串还有语言符号将返回true,否则返回false countTokens() 得到一共有多少个语言符号 文本框和文本区 1、文本框 TextField() 构造文本框,一个字符长 TextField(int x) 构造文本框,x个字符长 TextField(String s) 构造文本框,显示s setText(String s) 设置文本为s getText() 获取文本 setEchoChar(char c) 设置显示字符为c setEditable(boolean) 设置文本框是否可以被修改 addActionListener() 添加监视器 removeActionListener() 移去监视器 2、文本区 TextArea() 构造文本区 TextArea(String s) 构造文本区,显示s TextArea(String s,int x,int y) 构造文本区,x行,y列,显示s TextArea(int x,int y) 构造文本区,x行,y列 TextArea(String s,int x,ing y,int scrollbar) scrollbar的值是: TextArea.SCROLLBARS_BOTH TextArea.SCROLLBARS_VERTICAL_ONLY TextArea.SCROLLBARS_HORIZONTAL_ONLY TextArea.SCROLLBARS_NONE setText(String s) 设置文本为s

java常用类知识点总结

java常用类知识点总结 Java常用类 要求: 1、掌握String和StringBuffer的区别,可以熟练使用String和StringBuffer的各 种方法进行相关操作。 2、能够自己编写一个得到日期的操作类,并将日期进行格式化操作。 3、掌握比较器及其基本原理,并可以通过比较器进行对象数组的比较操作。 4、掌握对象克隆技术及其实现 5、能够灵活应用正则表达式对字符串的组成进行判断 6、掌握Math、Random、基本数据类型的包装类的使用 7、描述出Object System对垃圾收集的支持 8、使用NumberFormat、DecimalFormat、BigInteger、BigDecimal进行数字的操 作 String和StringBuffer String的内容一旦声明不可改变,如果要改变,改变的是String的引用地址,如果一个字符串要经常改变,必须使用StringBuffer。 在一个字符串内容需要频繁修改时,使用StringBuffer可以提升操作性能,因为StringBuffer内容可以改变,而String内容不可改变。StringBuffer支持的方法大部分与String类似。 StringBuffer常见用法: (1) 字符串的连接操作

String类可以通过“+“进行字符串的连接,而StringBuffer中却只能使用append方法进行字符串的连接,而且此方法返回一个StringBuffer类的实例,这样就可以采用代码链的形式一直调用append方法。 (2) 在任意位置处为StringBuffer添加内容 可以使用insert方法在指定位置上为StringBuffer添加内容 字符串的反转操作(较为常见的操作,使用reverse方法) (3) 替换指定范围的内容 replace方法可对指定范围的内容进行替换。在String中如果要替换,使用的是replaceAll (4) 字符串截取(使用subString方法从指定范围中截取内容) (5) 删除指定范围的字符串(使用delete方法删除指定范围内容) (6) 查找指定内容是否存在(indexOf查找指定内容,查找到返回内容的位置, 没查到返回-1) 问题:(1)String s = "Hello";s = s + " world!";这两行代码执行后,原始的String对象中的内容到底变了没有, 没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段代码中,s原先指向一个String对象,内容是 "Hello",然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢,答案是没有。这时,s不指向原来那个对象了,而指向了另一个 String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改, 或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为 String对象建立之后不能再改变,所以对于每一个不同的字符

Java标准包中基本常用类和方法

Java标准包中基本常用类和方法 java.util.Vector; java.util.ArrayList; java.util.Date; java.util.Calendar; java.util.Hashtable; java.util.Properties; java.util.Random; java.util.Timer; java.awt.* javax.swing.* Thread String Java常用方法: 1、获取字符串的长度 length() 2 、判断字符串的前缀或后缀与已知字符串是否相同 前缀startsWith(String s) 后缀endsWith(String s) 3、比较两个字符串 equals(String s)

4、把字符串转化为相应的数值 int型Integer.parseInt(字符串) long型Long.parseLong(字符串) float型Folat.valueOf(字符串).floatValue() double型Double.valueOf(字符串).doubleValue() 4、将数值转化为字符串 valueOf(数值) 5、字符串检索 indexOf(Srting s) 从头开始检索 indexOf(String s ,int startpoint) 从startpoint处开始检索 如果没有检索到,将返回-1 6、得到字符串的子字符串 substring(int startpoint) 从startpoint处开始获取 substring(int start,int end) 从start到end中间的字符 7、替换字符串中的字符,去掉字符串前后空格 replace(char old,char new) 用new替换old trim() 8、分析字符串 StringTokenizer(String s) 构造一个分析器,使用默认分隔字符(空格,换行,回车,Tab,进纸符) StringTokenizer(String s,String delim) delim是自己定义的分隔符 nextToken() 逐个获取字符串中的语言符号

实验五:Java语言中的常用类

仲恺农业工程学院实验报告纸 计算科学与工程(院、系)网络工程专业班组Java语言程序设计课 学号姓名实验日期2010/11/19 教师评定 实验五:Java语言中的常用类 一、实验目的 1、掌握Java语言中的常用类。 2、学习用Java语言中的常用类编写程序。 二、实验要求 1、字符串调用public String toUpperCase()方法返回一个字符串,该字符串把当前字符串中的小写字母变成大写字母;字符串调用public String toUpperCase()方法返回一个字符串,该字符串把当前字符串的大写字母变成小写字母。String 类的String toUpperCase()方法返回一个字符串,该字符串是把调用该方法的字符串与参数指定的字符串连接。 2、String类的public charAt(int index)方法可以得到当前字符串index 位置上的一个字符。编写程序使用该方法得到一个字符串中的第一个和最后一个字符。 3、输出某年某月的日历页,通过键盘输入年份和月份。 二、实验程序和结果 1、 public class Stg{ public static void main(String args[]){

String s2=new String("LI ZHI DONG"); String s3=s1.concat(s2); System.out.println("连接后的字符:"+s3); char a[]=s3.toCharArray(); for(int n=0;njava Stg 连接后的字符:wo de ming zi shi LI ZHI DONG 大小写转换后的字符:WO DE MING ZI SHI li zhi dong 或者简单点程序代码: public class Stg{ public static void main(String args[]){

(完整版)Java常用类

常用类 Object类 它是Java所有类的根类.所以Java的所有对象都拥有Object类的成员. 1.一般方法 boolean equals(Object obj) //本对象与obj对象相同,返回true String toString() //返回本对象的字符串表达形式,默认返回类名称和十六进制数的编码,即:getClass().getName+’@’+Integer.toHexString(hashCode()); 如果要显示自定义的对象,则需要覆盖这个方法。 protected void finalize() throws Throwable //对象销毁时被自动调用, native int hashCode() 返回对象的Hash码,Hash码是表示对象的唯一值,故Hash码相同的对象是同一对象。 2.控制线程的方法 final native void wait() //等待对象 final native void notify() //通知等待队列中的对象进入就绪队列 final native void notifyAll()//通知所有处于等待队列中的对象进入就绪队列 3.关于Object类的几点说明: 3.1. native <方法> 是指用C++语言编写的方法。 3.2.“==”运算符一般用于基本数据类型的比较,如果用于两个引用对象的比较,则 只有当两个引用变量引用同一个对象时,才返回true,否则返回false. String s1=new Strng(“java”); String s2=new Strng(“java”); System.out.pritnln(s1==s2); //显示false 但是,假如不使用new 关键字,创建String 型对象s3,s4,则它们引用的是同一个对象。 String s3=“java”; String s4=“java”;因为没有使用new 关键字,所以s4 引用既存的对象 System.out.pritnln(s3==s4); //显示true, 3.3. 比较对象内容时,使用equals()函数 Object 类的equals()定义 Public boolean equals(Object obj){ return (this==obj); } 由于String 类覆盖了继承自Object类的equals()方法,它比较的是引用对象的内容. 所以,没有覆盖equals()方法的类的对象使用该方法与另一对象进行比较时,永远返 回false; 只是因为此时进行比较调用的是Object的equals方法. 4. Object引用型变量 Object引用型变量可以用来引用所有的对对象. Object[] obs=new Object[3]; obs[0]=new Strng(“12345”);//将String对象赋给obs[0] obs[0]=new Boolean(true); obs[0]=new Integer(100); 5. toString()方法

java常用类及用法

字符串操作:字符串的比较: boolean equals()比较此字符串与指定的对象。 Int compareTo() 比较源与()的大小,两串大小相等返回0 加IgnoreCase()不考虑大小写boolean contains(CharSequence s) 当且仅当此字符串包含 char 值的指定序列时,才返回 true。 字符串的长度与组合: char charAt(int index)返回指定索引处的 char 值。 String concat(String str)将指定字符串联到此字符串的结尾。 int length() 返回此字符串的长度。 获取子串: String substring(int beginIndex) 返回一个从beginIndex到末尾的子串 String substring(int beginIndex, int endIndex) 返回一个从beginIndex到endIndex-1的字串 字符串的转换、替换和分隔: char[] toCharArray() 将此字符串转换为一个新的字符数组。 String toString() 返回此对象本身(它已经是一个字符串!)。 String toLowerCase() 变为全小写 String toUpperCase() 变为全大写 String trim() 返回字符串的副本,忽略前导空白和尾部空白。 String replace(char oldChar, char newChar) 用 newChar 替换此字符串中出现的所有 oldChar,返回新字符 String replaceAll(String regex, String replacement) 使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的每个子字符串。 String replaceFirst(String regex, String replacement) 使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的第一个子字符串。 String[] split(String regex) 在给定字符处拆分原字符串为若干字串。 找出相应字符或字串(找不到返回-1) int indexOf() 返回(?)填入的字符(串)在源中的第一个索引;(?,num)从num索引开始查找 int lastIndexOf()返回最后一次出现的指定值索引。 将字符和数值转化为字符串: Static String valueOf() 将()填入的参数(任意、包括Obj)转化为字符串 StringBuilder类:构建: StringBuilder() 构建一个容量为16的空字符串生成器,()填入数字构造指定大小,填入String 构造含有指定字符。 int capacity() 返回当前容量。 char charAt(int index) 指定索引处的 char 值。添改: StringBuilder append() 将特定内容由()传入buffer StringBuilder insert(int offset,?) 将?变量插在此序列的offset 前 void setCharAt(int index, char ch) 改变索引处的字符为ch。 StringBuilder replace(int start, int end, String str) 替换从start至end-1处字符为str StringBuilder reverse() 反转字符序列。删除: StringBuilder delete(int start, int end) 移除从start索引到end-1索引的字符串。 StringBuilder deleteCharAt(int index) 移除此序列指定位置上的 char。查找: int indexOf(String str) 同String用法 int lastIndexOf()同String用法 int length() 返回长度(字符数)。返回值: int capacity() 返回当前容量。

java中常用类基础入门

1 API的概述 (1)应用程序编程接口。 (2)就是JDK提供给我们的一些提高编程效率的java类。 2 Object类 (1)Object是类层次结构的根类,所有的类都直接或者间接的继承自Object类。 (2)Object类的构造方法有一个,并且是无参构造 这其实就是理解当时我们说过,子类构造方法默认访问父类的构造是无参构造 (3)要掌握的方法: A:toString() 返回对象的字符串表示,默认是由类的全路径+'@'+哈希值的十六进制表示。 这个表示其实是没有意义的,一般子类都会重写该方法。 如何重写呢?过程我也讲解过了,基本上就是要求信息简单明了。 但是最终还是自动生成。 B:equals() 比较两个对象是否相同。默认情况下,比较的是地址值是否相同。 而比较地址值是没有意义的,所以,一般子类也会重写该方法。 重写过程,我也详细的讲解和分析了。 但是最终还是自动生成。 (4)要了解的方法: A:hashCode() 返回对象的哈希值。不是实际地址值,可以理解为地址值。 B:getClass() 返回对象的字节码文件对象,反射中我们会详细讲解

C:finalize() 用于垃圾回收,在不确定的时间 D:clone() 可以实现对象的克隆,包括成员变量的数据复制,但是它和两个引用指向同一个对象是有区别的。 (5)两个注意问题; A:直接输出一个对象名称,其实默认调用了该对象的toString()方法。 B:面试题 ==和equals()的区别? A:== 基本类型:比较的是值是否相同 引用类型:比较的是地址值是否相同 B:equals() 只能比较引用类型。默认情况下,比较的是地址值是否相同。 但是,我们可以根据自己的需要重写该方法。 3 API概述 常用类 ?Object类/Scanner类 ?String类/StringBuffer类/StringBuilder类 ?数组高级和Arrays类 ?基本类型包装类(Integer,Character) ?正则表达式(Pattern,Matcher) ?Math类/Random类/System类 ?BigInteger类/BigDecimal类 Date类/DateFormat类/Calendar类

Java常用方法大全

Java常用方法大全.txt3努力奋斗,天空依旧美丽,梦想仍然纯真,放飞自我,勇敢地飞翔于梦想的天空,相信自己一定做得更好。4苦忆旧伤泪自落,欣望梦愿笑开颜。5懦弱的人害怕孤独,理智的人懂得享受孤独Java常用方法大全 字符串 1、获取字符串的长度 length() 2 、判断字符串的前缀或后缀与已知字符串是否相同 前缀 startsWith(String s) 后缀 endsWith(String s) 3、比较两个字符串 equals(String s) 4、把字符串转化为相应的数值 int型 Integer.parseInt(字符串) long型 Long.parseLong(字符串) float型 Folat.valueOf(字符串).floatValue() double型 Double.valueOf(字符串).doubleValue() 4、将数值转化为字符串 valueOf(数值) 5、字符串检索 indexOf(Srting s) 从头开始检索 indexOf(String s ,int startpoint) 从startpoint处开始检索 如果没有检索到,将返回-1 6、得到字符串的子字符串 substring(int startpoint) 从startpoint处开始获取 substring(int start,int end) 从start到end中间的字符 7、替换字符串中的字符,去掉字符串前后空格 replace(char old,char new) 用new替换old trim() 8、分析字符串 StringTokenizer(String s) 构造一个分析器,使用默认分隔字符(空格,换行,回车,Tab,进纸符) StringTokenizer(String s,String delim) delim是自己定义的分隔符 nextToken() 逐个获取字符串中的语言符号 boolean hasMoreTokens() 只要字符串还有语言符号将返回true,否则返回false countTokens() 得到一共有多少个语言符号 文本框和文本区 1、文本框 TextField() 构造文本框,一个字符长 TextField(int x) 构造文本框,x个字符长 TextField(String s) 构造文本框,显示s setText(String s) 设置文本为s getText() 获取文本

java面向对象编程类的封装接口和基本类库练习题

面向对象编程基本知识练习 一、判断题 1.不需要定义类,就能创建对象。() 2.构造方法用于给类的private 实例变量赋值。() 3.对象一经声明就可以立即使用。() 4.在定义变量和方法时,必须清楚地在其面前标上访问权限修饰符。() 5.Java 程序中的参数传递都是把参数值传递给方法定义中的参数。() 6.在类定义中,成员访问权限修饰符不能多次出现。() 7.new 操作符动态地为对象按其指定的类型分配内存,并返回该类型的一个引用。() 8.类的方法通常设为public,而类的实例变量一般也设为public。() 9.构造方法在创建对象时被调用。() 10.通过点运算符与类对象的引用相连,可以访问此类的成员。() 11.声明为protected 的类成员只能被此类中的方法访问。() 12.同一个类的对象使用不同的内存段,但静态成员共享相同的内存空间。() 13.类的成员变量可以放在类体的任意位置。() 14.声明为protected 的类成员可以被此类作用域中的任何对象访问。() 15.没有返回值的方法可以用void 来表示,也可以不加。() 二、选择题 1. 下面对对象概念描述错误的是:( ) A 操作是对象的动态属性 B 任何对象都必须有继承性 C 对象间的通讯靠消息传递 D 对象是属性和方法的封装体 2.编译并运行下面的程序,运行结果为:( ) public class A { public static void main(String[] args) { A a=new A(); a.method(8); } void method(int i) { System.out.println(“int:“+i); } void method(long i) { System.out.println(“long:“+i); } } A 程序可以编译运行,输出结果为int:8 B 程序可以编译运行,输出结果为long:8

java常用类知识点总结

Java常用类 要求: 1、掌握String和StringBuffer的区别,可以熟练使用String和StringBuffer的各 种方法进行相关操作。 2、能够自己编写一个得到日期的操作类,并将日期进行格式化操作。 3、掌握比较器及其基本原理,并可以通过比较器进行对象数组的比较操作。 4、掌握对象克隆技术及其实现 5、能够灵活应用正则表达式对字符串的组成进行判断 6、掌握Math、Random、基本数据类型的包装类的使用 7、描述出Object System对垃圾收集的支持 8、使用NumberFormat、DecimalFormat、BigInteger、BigDecimal进行数字的操 作 String和StringBuffer String的内容一旦声明不可改变,如果要改变,改变的是String的引用地址,如果一个字符串要经常改变,必须使用StringBuffer。 在一个字符串内容需要频繁修改时,使用StringBuffer可以提升操作性能,因为StringBuffer内容可以改变,而String内容不可改变。StringBuffer支持的方法大部分与String类似。 StringBuffer常见用法: (1)字符串的连接操作 String类可以通过“+“进行字符串的连接,而StringBuffer中却只能使用append 方法进行字符串的连接,而且此方法返回一个StringBuffer类的实例,这样就可以采用代码链的形式一直调用append方法。 (2)在任意位置处为StringBuffer添加内容 可以使用insert方法在指定位置上为StringBuffer添加内容 字符串的反转操作(较为常见的操作,使用reverse方法) (3)替换指定范围的内容 replace方法可对指定范围的内容进行替换。在String中如果要替换,使用的是replaceAll (4)字符串截取(使用subString方法从指定范围中截取内容) (5)删除指定范围的字符串(使用delete方法删除指定范围内容) (6)查找指定内容是否存在(indexOf查找指定内容,查找到返回内容的位置,没查到返回-1) 问题:(1)String s = "Hello";s = s + " world!";这两行代码执行后,原始的String 对象中的内容到底变了没有? 没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段代码中,s原先指向一个String对象,内容是"Hello",然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,

JAVA中常用类的常用方法

J A V A中常用类的常用方 法 Newly compiled on November 23, 2020

JAVA中常用类的常用方法 一、类 1、clone()方法 创建并返回此对象的一个副本。要进行“克隆”的对象所属的类必须实现. Cloneable接口。 2、equals(Object obj)方法 功能:比较引用类型数据的等价性。 等价标准:引用类型比较引用,基本类型比较值。 存在特例:对File、String、Date及封装类等类型来说,是比较类型及对象的内容而不考虑引用的是否为同一实例。 3、finalize()方法 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 4、hashCode()方法返回该对象的哈希码值。 5、notify()方法唤醒在此对象监视器上等待的单个线程。 6、notifyAll()方法唤醒在此对象监视器上等待的所有线程。 7、toString()方法 返回该对象的字符串表示。在进行String与其它类型数据的连接操作时,自动调用toString()方法。可以根据需要重写toString()方法。 8、wait()方法 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 二、字符串相关类 l String类 charAt(int index) 返回指定索引处的 char 值。 compareTo(String anotherString) 按字典顺序比较两个字符串。compareToIgnoreCase(String str) 按字典顺序比较两个字符串,不考虑大小写。 concat(String str) 将指定字符串连接到此字符串的结尾。 endsWith(String suffix) 测试此字符串是否以指定的后缀结束。 equals(Object anObject) 将此字符串与指定的对象比较。 equalsIgnoreCase(String anotherString) 将此 String 与另一个 String 比较,不考虑大小写。indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引。 indexOf(String str) 返回第一次出现的指定子字符串在此字符串中的索引。 lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引。 length() 返回此字符串的长度。 replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 split(String regex) 根据给定正则表达式的匹配拆分此字符串。 startsWith(String prefix) 测试此字符串是否以指定的前缀开始。 substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个子字符串。该子字符串始于指定索引处的 字符,一直到此字符串末尾。 substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,一直到索引 endIndex - 1 处的字符。

相关文档