下面给你提供一些在JAVA程序的设计和编码中,经常采用的一些方法和技巧,可以提高JAVA程序的性能:
1.对象的生成和大小的调整。
JAVA程序设计中一个普遍的问题就是没有好好的利用JAVA语言本身提供的函数,从而常常会生成大量的对象(或实例)。由于系统不仅要花时间生成对象,以后可能还需花时间对这些对象进行垃圾回收和处理。因此,生成过多的对象将会给程序的性能带来很大的影响。
例1:关于String ,StringBuffer,+和append
JAVA语言提供了对于String类型变量的操作。但如果使用不当,会给程序的性能带来影响。如下面的语句:
String name=new String("HuangWeiFeng");
Systemoutprintln(name+"is my name");
看似已经很精简了,其实并非如此。为了生成二进制的代码,要进行如下的步骤和操作:
(1) 生成新的字符串 new String(STR_1);
(2) 复制该字符串;
(3) 加载字符串常量"HuangWeiFeng"(STR_2);
(4) 调用字符串的构架器(Constructor);
(5) 保存该字符串到数组中(从位置0开始);
(6) 从javaioPrintStream类中得到静态的out变量;
(7) 生成新的字符串缓冲变量new StringBuffer(STR_BUF_1);
(8) 复制该字符串缓冲变量;
(9) 调用字符串缓冲的构架器(Constructor);
(10) 保存该字符串缓冲到数组中(从位置1开始);
(11) 以STR_1为参数,调用字符串缓冲(StringBuffer)类中的append方法;
(12) 加载字符串常量"is my name"(STR_3);
(13) 以STR_3为参数,调用字符串缓冲(StringBuffer)类中的append方法;
(14) 对于STR_BUF_1执行toString命令;
(15) 调用out变量中的println方法,输出结果。
由此可以看出,这两行简单的代码,就生成了STR_1,STR_2,STR_3,STR_4和STR_BUF_1五个对象变量。这些生成的类的实例一般都存放在堆中。堆要对所有类的超类,类的实例进行初始化,同时还要调用类极其每个超类的构架器。而这些操作都是非常消耗系统资源的。因此,对对象的生成进行限制,是完全有必要的。
经修改,上面的代码可以用如下的代码来替换。
StringBuffer name=new StringBuffer("HuangWeiFeng");
Systemoutprintln(nameappend("is my name")toString());
系统将进行如下的操作:
(1) 生成新的字符串缓冲变量new StringBuffer(STR_BUF_1);
(2) 复制该字符串缓冲变量;
(3) 加载字符串常量"HuangWeiFeng"(STR_1);
(4) 调用字符串缓冲的构架器(Constructor);
(5) 保存该字符串缓冲到数组中(从位置1开始);
(6) 从javaioPrintStream类中得到静态的out变量;
(7) 加载STR_BUF_1;
(8) 加载字符串常量"is my name"(STR_2);
(9) 以STR_2为参数,调用字符串缓冲(StringBuffer)实例中的append方法;
(10) 对于STR_BUF_1执行toString命令(STR_3);
(11)调用out变量中的println方法,输出结果。
由此可以看出,经过改进后的代码只生成了四个对象变量:STR_1,STR_2,STR_3和STR_BUF_1你可能觉得少生成一个对象不会对程序的性能有很大的提高。但下面的代码段2的执行速度将是代码段1的2倍。因为代码段1生成了八个对象,而代码段2只生成了四个对象。
代码段1:
String name= new StringBuffer("HuangWeiFeng");
name+="is my";
name+="name";
代码段2:
StringBuffer name=new StringBuffer("HuangWeiFeng");
nameappend("is my");
nameappend("name")toString();
因此,充分的利用JAVA提供的库函数来优化程序,对提高JAVA程序的性能时非常重要的其注意点主要有如下几方面;
(1) 尽可能的使用静态变量(Static Class Variables)
如果类中的变量不会随他的实例而变化,就可以定义为静态变量,从而使他所有的实例都共享这个变量。
例:
public class foo
{
SomeObject so=new SomeObject();
}
就可以定义为:
public class foo
{
static SomeObject so=new SomeObject();
}
(2) 不要对已生成的对象作过多的改变。
对于一些类(如:String类)来讲,宁愿在重新生成一个新的对象实例,而不应该修改已经生成的对象实例。
例:
String name="Huang";
name="Wei";
name="Feng";
上述代码生成了三个String类型的对象实例。而前两个马上就需要系统进行垃圾回收处理。如果要对字符串进行连接的操作,性能将得更差,因为系统将不得为此生成更多得临时变量,如上例1所示。
(3) 生成对象时,要分配给它合理的空间和大小JAVA中的很多类都有它的默认的空间分配大小。对于StringBuffer类来讲,默认的分配空间大小是16个字符。如果在程序中使用StringBuffer的空间大小不是16个字符,那么就必须进行正确的初始化。
(4) 避免生成不太使用或生命周期短的对象或变量。对于这种情况,因该定义一个对象缓冲池。以为管理一个对象缓冲池的开销要比频繁的生成和回收对象的开销小的多。
(5) 只在对象作用范围内进行初始化。JAVA允许在代码的任何地方定义和初始化对象。这样,就可以只在对象作用的范围内进行初始化。从而节约系统的开销。
例:
SomeObject so=new SomeObject();
If(x==1) then
{
Foo=sogetXX();
}
可以修改为:
if(x==1) then
{
SomeObject so=new SomeObject();
Foo=sogetXX();
}
2.异常(Exceptions)
JAVA语言中提供了try/catch来发方便用户捕捉异常,进行异常的处理。但是如果使用不当,也会给JAVA程序的性能带来影响。因此,要注意以下两点:
(1) 避免对应用程序的逻辑使用try/catch
如果可以用if,while等逻辑语句来处理,那么就尽可能的不用try/catch语句。
(2) 重用异常
在必须要进行异常的处理时,要尽可能的重用已经存在的异常对象。以为在异常的处理中,生成一个异常对象要消耗掉大部分的时间。
3 线程(Threading)
一个高性能的应用程序中一般都会用到线程。因为线程能充分利用系统的资源。在其他线程因为等待硬盘或网络读写而 时,程序能继续处理和运行。但是对线程运用不当,也会影响程序的性能。
例2:正确使用Vector类
Vector主要用来保存各种类型的对象(包括相同类型和不同类型的对象)。但是在一些情况下使用会给程序带来性能上的影响。这主要是由Vector类的两个特点所决定的。第一,Vector提供了线程的安全保护功能。即使Vector类中的许多方法同步。但是如果你已经确认你的应用程序是单线程,这些方法的同步就完全不必要了。第二,在Vector查找存储的各种对象时,常常要花很多的时间进行类型的匹配。而当这些对象都是同一类型时,这些匹配就完全不必要了。因此,有必要设计一个单线程的,保存特定类型对象的类或集合来替代Vector类用来替换的程序如下(StringVectorjava):
public class StringVector
{
private String [] data;
private int count;
public StringVector()
{
this(10); // default size is 10
}
public StringVector(int initialSize)
{
data = new String[initialSize];
}
public void add(String str)
{
// ignore null strings
if(str == null) { return; }
ensureCapacity(count + 1);
data[count++] = str;
}
private void ensureCapacity(int minCapacity)
{
int oldCapacity = datalength;
if (minCapacity > oldCapacity)
{
String oldData[] = data;
int newCapacity = oldCapacity 2;
data = new String[newCapacity];
Systemarraycopy(oldData, 0, data, 0, count);
}
}
public void remove(String str)
{
if(str == null) { return; // ignore null str }
for(int i = 0; i < count; i++)
{
// check for a match
if(data[i]equals(str))
{
Systemarraycopy(data,i+1,data,i,count-1); // copy data
// allow previously valid array element be gc′d
data[--count] = null;
return;
}
}
}
public final String getStringAt(int index)
{
if(index < 0) { return null; }
else if(index > count) { return null; // index is > # strings }
else { return data[index]; // index is good }
}
}
因此,代码:
Vector Strings=new Vector();
Stringsadd("One");
Stringsadd("Two");
String Second=(String)StringselementAt(1);
可以用如下的代码替换:
StringVector Strings=new StringVector();
Stringsadd("One");
Stringsadd("Two");
String Second=StringsgetStringAt(1);
这样就可以通过优化线程来提高JAVA程序的性能。用于测试的程序如下(TestCollectionjava):
import javautilVector;
public class TestCollection
{
public static void main(String args [])
{
TestCollection collect = new TestCollection();
if(argslength == 0)
{
Systemoutprintln("Usage: java TestCollection [ vector | stringvector ]");
Systemexit(1);
}
if(args[0]equals("vector"))
{
Vector store = new Vector();
long start = SystemcurrentTimeMillis();
for(int i = 0; i < 1000000; i++)
{
storeaddElement("string");
}
long finish = SystemcurrentTimeMillis();
Systemoutprintln((finish-start));
start = SystemcurrentTimeMillis();
for(int i = 0; i < 1000000; i++)
{
String result = (String)storeelementAt(i);
}
finish = SystemcurrentTimeMillis();
Systemoutprintln((finish-start));
}
else if(args[0]equals("stringvector"))
{
StringVector store = new StringVector();
long start = SystemcurrentTimeMillis();
for(int i = 0; i < 1000000; i++) { storeadd("string"); }
long finish = SystemcurrentTimeMillis();
Systemoutprintln((finish-start));
start = SystemcurrentTimeMillis();
for(int i = 0; i < 1000000; i++) {
String result = storegetStringAt(i);
}
finish = SystemcurrentTimeMillis();
Systemoutprintln((finish-start));
}
}
}
关于线程的操作,要注意如下几个方面:
(1) 防止过多的同步
如上所示,不必要的同步常常会造成程序性能的下降。因此,如果程序是单线程,则一定不要使用同步。
(2) 同步方法而不要同步整个代码段
对某个方法或函数进行同步比对整个代码段进行同步的性能要好。
(3) 对每个对象使用多”锁”的机制来增大并发。
一般每个对象都只有一个”锁”,这就表明如果两个线程执行一个对象的两个不同的同步方法时,会发生”死锁”。即使这两个方法并不共享任何资源。为了避免这个问题,可以对一个对象实行”多锁”的机制。如下所示:
class foo
{
private static int var1;
private static Object lock1=new Object();
private static int var2;
private static Object lock2=new Object();
public static void increment1()
{
synchronized(lock1)
{
var1++;
}
}
public static void increment2()
{
synchronized(lock2)
{
var2++;
}
}
}
4.输入和输出(I/O)
输入和输出包括很多方面,但涉及最多的是对硬盘,网络或数据库的读写操作。对于读写操作,又分为有缓存和没有缓存的;对于数据库的操作,又可以有多种类型的JDBC驱动器可以选择。但无论怎样,都会给程序的性能带来影响。因此,需要注意如下几点:
(1) 使用输入输出缓冲
尽可能的多使用缓存。但如果要经常对缓存进行刷新(flush),则建议不要使用缓存。
(2) 输出流(Output Stream)和Unicode字符串
当时用Output Stream和Unicode字符串时,Write类的开销比较大。因为它要实现Unicode到字节(byte)的转换因此,如果可能的话,在使用Write类之前就实现转换或用OutputStream类代替Writer类来使用。
(3) 当需序列化时使用transient
当序列化一个类或对象时,对于那些原子类型(atomic)或可以重建的原素要表识为transient类型。这样就不用每一次都进行序列化。如果这些序列化的对象要在网络上传输,这一小小的改变对性能会有很大的提高。
(4) 使用高速缓存(Cache)
对于那些经常要使用而又不大变化的对象或数据,可以把它存储在高速缓存中。这样就可以提高访问的速度。这一点对于从数据库中返回的结果集尤其重要。
(5) 使用速度快的JDBC驱动器(Driver)
JAVA对访问数据库提供了四种方法。这其中有两种是JDBC驱动器。一种是用JAVA外包的本地驱动器;另一种是完全的JAVA驱动器。具体要使用哪一种得根据JAVA布署的环境和应用程序本身来定。
5一些其他的经验和技巧
(1) 使用局部变量。
(2) 避免在同一个类中动过调用函数或方法(get或set)来设置或调用变量。
(3) 避免在循环中生成同一个变量或调用同一个函数(参数变量也一样)。
(4) 尽可能的使用static,final,private等关键字。
(5) 当复制大量数据时,使用Systemarraycopy()命令。
"陈可辛"getBytes("utf-8"), ==>0xE9 0x99 0x88 0xE5 0x8F 0xAF 0xE8 0xBE 0x9B
new String("陈可辛"getBytes("utf-8"),"GBK") ==> 上面9个byte数字,从左到右,两个byte组成一个汉字,但最后一个是0x9B,在gbk里面没有这个,所以用一个“�”表示,识别不了。==> 闄埚彲杈�
“闄埚彲杈�”getBytes("GBK") ==> 每个汉字拆成两个字节,最后一个因为识别不了,所以用 表示,即byte中的0x3F,也就是63,你可以查一下ASCII表。==>
0xE9 0x99 0x88 0xE5 0x8F 0xAF 0xE8 0xBE 0x3F
然后上面9个byte,按utf-8的规则解,由于最后一个都变了,所以最后一个解错了
这种编码问题真是很tricky的问题。说它tricky是因为这至少涉及到以下4种编码选取的排列组合(有时甚至更多),更有时乃至会发生错进错出,负负得正,中间过程错了但反而到不是乱码的情况。
(1)源代码的编码
(2)编译时告诉java编译器的源代码编码
(3)运行时jvm参数fileencoding
(4)输出终端对输出字节流的解码所采用的码组
在这简单情况下(1)和(2)一致,(3)和(4)一致就不会因为编解码映射错误(当然字符向终端字体映射的错误是另一回事,如字体缺失之类)。而(1)(2)和(3)(4)不必一致,这样就使得不必强求开发编译环境和运行应用环境的编码必须一致。
源代码的录入与编译若在在一个平台上时,大多数情况没有问题(反而用聪明的Idea IDE设置错误时会乱套,越是简陋的开发环境越不太会错)。但是如果你在中文GBK编码平台上的源代码在别人的unicode编码平台上编译,就有问题了。所以和别人,特别是和不同母语的人合作编程时,建议要么约定一律用unicode作为源文件编码;要么只用ASCII字符,反正其他编码一般都和ASCII兼容的,对于非ASCII字符,用Java的/uxxxx表示机制,比如"中国"就表示为"\u4e2d\u56fd"。4e2d和56fd分别是中国二字的unicode十六进制编码。
但我认为楼主在这里其实主要关心的是运行时的编码一致问题,即(3)和(4)。所以言归正传,让我们来检查它们是否一致。
由于正如上述,iso8859-1编码集其实是被其他所有公认的编码集所兼容的,也就是说它是所有公认编码集的公共子集。所以以iso8859-1为基础可以外延到任何一个公认编码集。事实上大多数情况也是这样做的。比如java System property里设定了encoding为iso8859-1,事实上不仅仅是一个Latin字母的映射,在非Latin区域按JVM宿主操作系统的编码扩展。即选iso8859-1其实是选择了宿主操作系统的默认编码。
假设楼主的操作系统编码是GBK,那么fileencoding=iso8859-1相当于选择了fileencoding=GBK。那么Systemoutprintln()这个核心类方法会将china字符转换为fileencoding指定的编码(GBK)字节由out流输出给最终out所绑定的终端。比如console一般采用系统默认编码也是GBK的话,那就和fileencoding一致,能正常解码,不会乱码。
至于Systemoutwrite()直接写字节流。由于该字节流是由chinagetBytes()得到的,在不指定编码的时候使用fileencoding指定的默认值的(即GBK),因此Str->Byte的编码方法GBK和console采用的解码方法GBK又是一致的,所以也不是乱码。
但是这时候用toHexString打印出的两个字节串是不一样的。先直接把china逐字强行转换为int的情况,不涉及输出编码,总是unicode的。(JVM规范规定class里字串必须unicode编码)只要上述(1) (2)匹配,java编译器会自动从各种编码的源文件正确转成class文件里统一unicode编码的字串。相反,作为一个题外话提一下,当(1)(2)不匹配时会在特定的一种配合(1)(2)的(3)(4)也不匹配的情况下会负负得正输出正常,但这是绝对错误的做法,因为任何要求(1)(2)和(3)(4)有匹配关系的要求都是在应用中可能无法满足的。java编译器对这种情况也会报告warning,但不fail。
综上,一旦fileencoding设成宿主操作系统默认而系统consle也采用操作系统默认编解码的话,(3)(4)总是一致的,无论系统选择的是GBK还是utf-8等等。
那么如果fileencoding不选系统默认呢?比如utf-8。那就很可能出现乱码了。但是,慢着,试验的结果还是没有乱码。那是因为fileencoding是静态的JVM系统参数,在程序里像楼主那样设定是不起作用的(我不知道有没有办法发一个什么通知让这种程序改变生效的)。必须作为JVM参数直接传给java程序让它构造虚拟机的时候就得到这个参数,否则JVM会去拿宿主系统的默认值,就相当于又回到设fileencoding=iso8859-1了。
java -Dfileencoding=utf-8 A
这下终于乱码了,而且两个都乱了。打印出的字节串一个还是unicode,另一个从GBK变到utf-8了。
如果你发现试验的现象和我上面说的正好相反,请注意检查console的编码设置,我们上面假设它也采用了宿主系统默认编码,但有些console很高级的嘞,可以设置成不通编码的(其实几乎所有的都可以)。那么分析的方法和上面一样,结果可能正好相反。
首先,我们看getBytes(); JDK文档注释为:
Returns a new byte array containing the characters of this string encoded using the system's default charset
即根据系统的默认编码方式,返回一个byte数组,byte和char一样吗,显然不一样。下面我们测试一下不同字符集,getBytes()的行为。
以下是我运行的结果,我的系统是linux,字符串就是你的哈哈哈:
字符集:Big5 getBytes()长度6
数组内容:
-85 -94 -85 -94 -85 -94
字符集:Big5-HKSCS getBytes()长度6
数组内容:
-85 -94 -85 -94 -85 -94
字符集:EUC-JP getBytes()长度6
数组内容:
-46 -3 -46 -3 -46 -3
字符集:EUC-KR getBytes()长度6
数组内容:
-7 -21 -7 -21 -7 -21
字符集:GB18030 getBytes()长度6
数组内容:
-71 -2 -71 -2 -71 -2
字符集:GB2312 getBytes()长度6
数组内容:
-71 -2 -71 -2 -71 -2
字符集:GBK getBytes()长度6
数组内容:
-71 -2 -71 -2 -71 -2
字符集:IBM-Thai getBytes()长度3
数组内容:
63 63 63
字符集:IBM00858 getBytes()长度3
数组内容:
63 63 63
字符集:IBM01140 getBytes()长度3
数组内容:
63 63 63
字符集:IBM01141 getBytes()长度3
数组内容:
63 63 63
字符集:IBM01142 getBytes()长度3
数组内容:
63 63 63
字符集:IBM01143 getBytes()长度3
数组内容:
63 63 63
字符集:IBM01144 getBytes()长度3
数组内容:
63 63 63
字符集:IBM01145 getBytes()长度3
数组内容:
63 63 63
字符集:IBM01146 getBytes()长度3
数组内容:
63 63 63
字符集:IBM01147 getBytes()长度3
数组内容:
63 63 63
字符集:IBM01148 getBytes()长度3
数组内容:
63 63 63
字符集:IBM01149 getBytes()长度3
数组内容:
63 63 63
字符集:IBM037 getBytes()长度3
数组内容:
63 63 63
字符集:IBM1026 getBytes()长度3
数组内容:
63 63 63
字符集:IBM1047 getBytes()长度3
数组内容:
63 63 63
字符集:IBM273 getBytes()长度3
数组内容:
63 63 63
字符集:IBM277 getBytes()长度3
数组内容:
63 63 63
字符集:IBM278 getBytes()长度3
数组内容:
63 63 63
字符集:IBM280 getBytes()长度3
数组内容:
63 63 63
字符集:IBM284 getBytes()长度3
数组内容:
63 63 63
字符集:IBM285 getBytes()长度3
数组内容:
63 63 63
字符集:IBM297 getBytes()长度3
数组内容:
63 63 63
字符集:IBM420 getBytes()长度3
数组内容:
63 63 63
字符集:IBM424 getBytes()长度3
数组内容:
63 63 63
字符集:IBM437 getBytes()长度3
数组内容:
63 63 63
字符集:IBM500 getBytes()长度3
数组内容:
63 63 63
字符集:IBM775 getBytes()长度3
数组内容:
63 63 63
字符集:IBM850 getBytes()长度3
数组内容:
63 63 63
字符集:IBM852 getBytes()长度3
数组内容:
63 63 63
字符集:IBM855 getBytes()长度3
数组内容:
63 63 63
字符集:IBM857 getBytes()长度3
数组内容:
63 63 63
字符集:IBM860 getBytes()长度3
数组内容:
63 63 63
字符集:IBM861 getBytes()长度3
数组内容:
63 63 63
字符集:IBM862 getBytes()长度3
数组内容:
63 63 63
字符集:IBM863 getBytes()长度3
数组内容:
63 63 63
字符集:IBM864 getBytes()长度3
数组内容:
63 63 63
字符集:IBM865 getBytes()长度3
数组内容:
63 63 63
字符集:IBM866 getBytes()长度3
数组内容:
63 63 63
字符集:IBM868 getBytes()长度3
数组内容:
63 63 63
字符集:IBM869 getBytes()长度3
数组内容:
63 63 63
字符集:IBM870 getBytes()长度3
数组内容:
63 63 63
字符集:IBM871 getBytes()长度3
数组内容:
63 63 63
字符集:IBM918 getBytes()长度3
数组内容:
63 63 63
可以看出,不同编码集下,不仅getBytes长度不同,连数组的内容都不一样。
下面测试"aaa":
字符集:Big5 getBytes()长度3
数组内容:
97 97 97
字符集:Big5-HKSCS getBytes()长度3
数组内容:
97 97 97
字符集:EUC-JP getBytes()长度3
数组内容:
97 97 97
字符集:EUC-KR getBytes()长度3
数组内容:
97 97 97
字符集:GB18030 getBytes()长度3
数组内容:
97 97 97
字符集:GB2312 getBytes()长度3
数组内容:
97 97 97
字符集:GBK getBytes()长度3
数组内容:
97 97 97
字符集:IBM-Thai getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM00858 getBytes()长度3
数组内容:
97 97 97
字符集:IBM01140 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM01141 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM01142 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM01143 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM01144 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM01145 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM01146 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM01147 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM01148 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM01149 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM037 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM1026 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM1047 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM273 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM277 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM278 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM280 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM284 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM285 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM297 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM420 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM424 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM437 getBytes()长度3
数组内容:
97 97 97
字符集:IBM500 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM775 getBytes()长度3
数组内容:
97 97 97
字符集:IBM850 getBytes()长度3
数组内容:
97 97 97
字符集:IBM852 getBytes()长度3
数组内容:
97 97 97
字符集:IBM855 getBytes()长度3
数组内容:
97 97 97
字符集:IBM857 getBytes()长度3
数组内容:
97 97 97
字符集:IBM860 getBytes()长度3
数组内容:
97 97 97
字符集:IBM861 getBytes()长度3
数组内容:
97 97 97
字符集:IBM862 getBytes()长度3
数组内容:
97 97 97
字符集:IBM863 getBytes()长度3
数组内容:
97 97 97
字符集:IBM864 getBytes()长度3
数组内容:
97 97 97
字符集:IBM865 getBytes()长度3
数组内容:
97 97 97
字符集:IBM866 getBytes()长度3
数组内容:
97 97 97
字符集:IBM868 getBytes()长度3
数组内容:
97 97 97
字符集:IBM869 getBytes()长度3
数组内容:
97 97 97
字符集:IBM870 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM871 getBytes()长度3
数组内容:
-127 -127 -127
字符集:IBM918 getBytes()长度3
数组内容:
-127 -127 -127
可以看出对于字母,我这里支持的字符集都返回了3,而数组内容有些是97,另外一些是127,97是我们熟悉的ASCii值,127我就不得而知了。
以上测试说明:不同字符集对于字母和汉字(还包括其他非拉丁文字)编码方式是不同的,包括使用单字节还是双字节,也包括如何编码。
测试代码如下:
Map<String, Charset> map = CharsetavailableCharsets();
Set<String> keySet = mapkeySet();
for(String charSetName: keySet)
{
Systemoutprintln("字符集:" + charSetName + " getBytes()长度" + sgetBytes(mapget(charSetName))length);
Systemoutprintln("数组内容:");
for(byte b: sgetBytes(mapget(charSetName)))
{
Systemoutprint(b + " ");
}
Systemoutprintln();
}
下面的问题是char:
int c = 'A';
int d = '哈';
char e = (char) 12345678;
c = e;
Systemoutprintln(e);
12345678显然是超过了char范围了,不过呢它被截断成2字节,并且根据当前的字符集,打印出来一个汉字。
大概就是这样了
总结,如果你写char ,不管赋值为字母还是汉字还是数字,都占两个字节,只不过高位有些为0
对于字符串,getBytes返回默认编码集下的数组,你可以指定参数获取其他字符集的结果,结果是五花八门的。
如果要了解字符集的编码规范,还请百度相关主题,我这里我不做更多的验证了。
public class Test1 {
public static void main(String[] srgs) {
int i = 10;
int count = 1;
while (i > 1) {
count = eat(count);
i--;
Systemoutprintln(i + ":" + count);
}
}
public static int eat(int i) {
int before = (i + 1) 2;
return before;
}
}
java初学者都会接触到一个概念,既java的默认编码是uincode,但书上也就出现这句话而已,究竟是什么意思就没再说。其实对于一个程序员来说,一个平台的编码方式是不用了解的,因为这是他内部处理字符的方式,和我们顶层设计程序是没有多大关系(如果真要说有关系的话,一个就是你对这个平台的熟悉程度,另一个就只能是你要处理的字符奇葩到要考虑编译器有没有包括这个字符)。但这并不是指我们在编程的时候完全不用考虑编码问题,恰恰相反,编码问题是跨系统交流的基本。
那java哪里会用到编码问题呢?最常见的是流,下面有两个例子。1在linux下用java创建了一个文件(这里默认代码里没有指定编码),里面包括英文和中文,然后在windows下同样用java读取这个文件,并输出,结果中文出现了乱码;2android手机和电脑的两个java程序进行类似qq的信息交流,中文都是乱码。疑惑来了,java不是跨平台吗,而且默认编码就是unicode,为什么会有编码? 正如上面所说,java的系统编码是管理内部变量等信息的,是统一不能变的,但上面两个例子出现乱码的原因在于这些字符信息是从外界读取的,编码方式直接影响到字符的显示,比如gbk一个字符是1或2个字节,中文是2个,而utf8是1到4个字节不定,中文是3个,utf16是2个字节固定不变,所以很明显了,同样字节数的源信息可以每2个或者每3个字节表达一个中文,不同编码当然不同了,而且即使gbk和utf16都是两个字节表示一个中文,同样的二进制也对应不同的字符。所以从外部读取到这些byte信息后,就要指定编码,比如new
String(byte[],charset),当然,也可以在构建流的时候就指定,像new
InputStreamReader(InputStream,charset)等,但像BufferedReader等没有相应的构造函数,就只能把上面的InputStreamReader作为参数了。
总结:
1String和流(包括控制台的输出输入)的默认编码是根据系统而定,即jvm假设这些信息是当前系统创建的,windows默认中文是gbk,linux和mac是utf8(这里又来了,utf8和unicode是什么意思,简单地说,unicode是把每个字符和一个唯一的二进制码对应的标准,而utf是unicode
transformation
format,即如何表示每个唯一的二进制码,utf8,utf16和utf32是不同的编码方式);
2IDE设置的编码方式用于存取java源文件,对于在不同系统平台上共享代码很重要;
3java编译器采用utf8,即class文件的存储是用utf8,因为相对于utf16,utf8在处理英文占用内存小,而程序大部分都是英文;
4jvm运行时的编码方式是utf16,即jvm用utf8从class文件读取程序后再转化为utf16编码的字符串,因为utf16是2个字节,统一的长度更方便jvm申请数组等操作;
5网页大部分是用utf8编码的,在html头几行有charset的信息,在对下载下来的网页进行解析时,要注意编码,谷歌百度在对搜索结果的解析时也是用utf8的,所以在涉及到网络时编码问题非常重要,本人曾经栽得很惨,当然了,谁叫windows的编码不是utf8;
6不知大家有没有经历过,如果编码弄错了,一般只有中文会出现乱码,而中文后面的英文是正确的,不合理啊,这不是类似多骨诺米牌吗,一个错了,后面不是全倒吗。所以别小看那些制定编码的专家,像utf8每个字节的前几位都用来表示一些信息,不同字节还不一样,而utf16也有,所以弄出了utf16le和utf16be
String str="测试";
byte[] byteArray = strgetBytes("utf-16");
String newStr = new String (byteArray, "utf-16");
先来看看表白神器,点击这里下载,来表白吧!
不如就把这些感性的细枝末节放大吧,用代码写下520的“告白书”
“自从遇见了你,就不停地想你。
编一个死循环,
让我们一直走,一直走。”
“我就是我,是颜色不一样的烟火
每个人都是独一无二的,写给自己的情诗。”
“我一直在寻找
找到了你
便找到了整个世界”
“我是个说谎者
我总是说,我不爱你”
“我曾是孤单的飞鸟,飘荡在远方的天空
如今我已飞得太久
才知道你就是春天”
“自从遇见了你,就不停地想你。”
“找到你的****,想约你出来吃饭
什么?你居然拒绝我了!!!
哼!那我把你删掉了!!!”
“曾经沧海难为水
除却巫山不是云
你对我而言就是沧海和巫山”
“如果允许的话
我想回到过去
对你说
我爱你”
“我一生都在等待
直到你闯进我的世界
我只想对你说:余生请多指教”
一封长长的表白书,再加上表白神器,你明白了我的心意了吗?
欢迎分享,转载请注明来源:表白网
评论列表(0条)