在Java中,String.getBytes(String decode)Ҏ会根据指定的decode~码q回某字W串在该~码下的byte数组表示Q如
byte[] b_gbk = "?.getBytes("GBK");
byte[] b_utf8 = "?.getBytes("UTF-8");
byte[] b_iso88591 = "?.getBytes("ISO8859-1");
分别返?#8220;?#8221;q个汉字在GBK、UTF-8和ISO8859-1~码下的byte数组表示Q此时b_gbk的长度ؓ2Qb_utf8的长度ؓ3Qb_iso88591的长度ؓ1?/p>
而与getBytes相对的,可以通过new String(byte[], decode)的方式来q原q个“?#8221;字时Q这个new String(byte[], decode)实际是用decode指定的编码来byte[]解析成字W串?/p>
String s_gbk = new String(b_gbk,"GBK");
String s_utf8 = new String(b_utf8,"UTF-8");
String s_iso88591 = new String(b_iso88591,"ISO8859-1");
通过打印s_gbk、s_utf8和s_iso88591Q会发现Qs_gbk和s_utf8都是“?#8221;Q而只有s_iso88591是一个不认识 的字W,Z么用ISO8859-1~码再组合之后,无法q原“?#8221;字呢Q其实原因很单,因ؓISO8859-1~码的编码表中,Ҏ没有包含汉? 字符Q当然也无法通过"?.getBytes("ISO8859-1");来得到正的“?#8221;字在ISO8859-1中的~码gQ所以再通过new String()来还原就无从谈v了?/p>
因此Q通过String.getBytes(String decode)Ҏ来得到byte[]Ӟ一定要定decode的编码表中确实存在String表示的码|q样得到的byte[]数组才能正确被还原?/p>
有时候,Z让中文字W适应某些Ҏ要求Q如http header头要求其内容必须为iso8859-1~码Q,可能会通过中文字W按照字节方式来~码的情况,?/p>
String s_iso88591 = new
String("?.getBytes("UTF-8"),"ISO8859-1")Q这样得到的s_iso8859-1字符串实际是三个?
ISO8859-1中的字符Q在这些字W传递到目的地后Q目的地E序再通过相反的方式String s_utf8 = new
String(s_iso88591.getBytes("ISO8859-1"),"UTF-8")来得到正的中文汉字“?#8221;。这样就既保证了遵守?
议规定、也支持中文?/p>
查这个类有没有被装蝲q,如果已经装蝲q,则直接返回;
调用父Classloader去装载类Q如果装载成功直接返回;
调用自n的装载类的方法,如果装蝲成功直接q回Q?/span>
上述所有步骤都没有成功装蝲到类Q抛出ClassNotFoundExceptionQ?/span>
每一层次的Classloader都重复上q动作?/span>
单说Q当Classloader链上的某一Classloader收到c装载请求时Q会按顺序向上询问其所有父节点Q直x端QBootstrapClassLoaderQ,M一个节Ҏ功受理了此请求,则返回,如果所有父节点都不能受理,q时候才ph的Classloader自n来装载这个类Q如果仍然不能装载,则抛出异常?br />
c装载的方式
c装载的方式主要有两U:昑ּ的和隐式的?/span>
昑ּc装?/span>
发生在用以下方法调用进行装载类的时候:
ClassLoader.loadClass()Q用指定的Classloaderq行装蝲Q?/span>
Class.forName()Q用当前类的Caller Classloaderq行装蝲Q?/span>
当调用上q方法的时候,指定的ClassQ以cd为参敎ͼ由Classloader装入?span style="color: red">q两个方法的行ؓ有轻微的区别QClass.forName()在类装蝲完成后,会对c进行初始化Q而ClassLoader.loadClass()只负责装载类?/span>
隐式c装?/span>
发生在由于引用、实例化或承导致需要装载类的时候。隐式类装蝲是在q后启动的,JVM会解析必要的引用q装载类?/span>
cȝ装蝲通常l合了显式和隐式两种方式。例如,Classloader可能先显式地装蝲一个类Q然后再隐式地装载它引用的其它类?br />
一个基本的Classloader的层ơ结?br />
上图昄了一个基本的Classloader的层ơ结构?span style="color: red; background-color: #ffff00">在给定层ơ上的Classloader不能引用M层次低于它的ClassloaderQ另外,它的子Classloader装蝲的类对于其是不可见的?/span>在上图中Q如果Foo.class是由ClassLoaderB装蝲的,q且Foo.class依赖于Bar.classQ那么Bar.class必须由ClassLoaderA或B装蝲。如果Bar.class只是对ClassLoaderC和D可见Q那么将会发生ClassNotFoundException或者NoClassDefFoundError异常?/span>
如果Bar.class分别对于两个q的Classloader可见Q例如C和DQ,但对于它们的父Classloader不可见,那么当类装蝲h发送到q两个ClassloaderӞ每一个Classloader会装载自q本的cRClassLoaderC装蝲的Bar.class的实例将不兼容于ClassLoaderD装蝲的Bar.class的实例。如果对Classloader的层ơ结构不了解Q试图用由ClassLoaderC装蝲的类去造型一个ClassLoaderD装蝲的Bar.class的实例,则会发生造型p|QClassCastExceptionQ?/span>
基本的Classloader
最基本的Classloader是Bootstrap Classloader和System ClassloaderQ也有hUC为AppClassLoaderQ,只要写过javaE序Q都会用到这两个Classloader?/span>
Bootstrap Classloader
q个Classloader装蝲Java虚拟机提供的基本q行时刻c($JAVA_HOME/jre/libQ,q包括放|在pȝ扩展目录Q?JAVA_HOME/jre/lib/extQ内的JAR文g中的cR这个Classloader是javaE序最层的ClassloaderQ只有它没有父Classloader。如果你一个自己写的类或第三方jar包放q?JAVA_HOME/jre/lib/ext目录中,那么它将被Bootstrap Classloader装蝲?/span>
System Classloader
System Classloader通常负责装蝲pȝ环境变量CLASSPATH中设|的cR由System Classloader装蝲的类对于Apusic服务器内部的cd部v在Apusic服务器上的J2EE应用Q通常打包成earQ都是可见的?APUSIC_HOME%/lib目录下的jar文g是Apusic应用服务器的核心c,一般把q些jar文g都加在系lCLASSPATH中。另外,一些公用类也可以加在系lCLASSPATH中,如JDBC驱动E序{?/span>
Caller Classloader
Caller Classloader指的是当前所在的c装载时使用的ClassloaderQ它可能是System ClassloaderQ也可能是一个自定义的ClassloaderQ这里,我们都称之ؓCaller Classloader。我们可以通过getClass().getClassLoader()来得到Caller Classloader。例如,存在Ac,是被AClassLoader所加蝲QA.class.getClassLoader()为AClassLoader的实例,它就是A.class的Caller Classloader?/span>
如果在AcM使用new关键字,或者Class.forName(String className)和Class.getResource(String resourceName)ҎQ那么这时也是用Caller Classloader来装载类和资源。比如在AcM初始化Bc:
/**
* A.java
*/
...
public void foo() {
B b = new B();
b.setName("b");
}
那么QBcȝ当前ClassloaderQ也是AClassloader装蝲。同LQ修改上q的fooҎQ其实现改ؓQ?/span>
最l获取到的clazzQ也是由AClassLoader所装蝲?/span>
那么Q如何用指定的Classloaderd成类和资源的装蝲呢?或者说Q当需要去实例化一个Caller Classloader和它的父Classloader都不能装载的cLQ怎么办呢Q?/span>
一个很典型的例子是JAXPQ当使用xerces的SAX实现Ӟ我们首先需要通过rt.jar中的javax.xml.parsers.SAXParserFactory.getInstance()得到xercesImpl.jar中的org.apache.xerces.jaxp.SAXParserFactoryImpl的实例。由于JAXP的框架接口的class位于JAVA_HOME/lib/rt.jar中,由Bootstrap Classloader装蝲Q处于Classloader层次l构中的最层Q而xercesImpl.jar׃层的Classloader装蝲Q也是说SAXParserFactoryImpl是在SAXParserFactory中实例化的,如前所qͼ使用SAXParserFactory的Caller Classloader(q里是Bootstrap Classloader)是完成不了这个Q务的?/span>
q时Q我们就需要了解一下线E上下文Classloader?br />
U程上下文Classloader
每个U程都有一个关联的上下文Classloader。如果用new Thread()方式生成新的U程Q新U程承其父线E的上下文Classloader。如果程序对U程上下文Classloader没有M改动的话Q程序中所有的U程都使用System Classloader作ؓ上下文Classloader?/span>
当用Thread.currentThread().setContextClassLoader(classloader)ӞU程上下文Classloader变成了指定的Classloader了。此Ӟ在本U程的Q意一处地方,调用Thread.currentThread().getContextClassLoader()Q都可以得到前面讄的Classloader?/span>
回到JAXP的例子,假设xercesImpl.jar只有AClassLoader能装载,现在A.class内部要用JAXPQ但是A.class却不是由AClassLoader或者它的子Classloader装蝲的,那么在A.class中,应该q样写才能正得到xercesImpl的实玎ͼ
AClassLoader aClassLoader = new AClassLoader(parent);
最q看代码一直碰到这个类Q先做一个ȝ?br />
先看PropertyChangeSupportcȝ官方文档解释Q?br />
This is a utility class that can be used by beans that support bound properties. You can use an instance of this class as a member field of your bean and delegate various work to it.
1Q关联属?br />
在JavaBean的设计中Q按照属性的不同作用又细分ؓ四类Q单值属性、烦引属性、关联属性、限制属性。关联属性,也称之ؓl定属性。绑定属性会在属性值发生变化时Q通知所有相关的监听器。ؓ了实C个绑定属性,必须实现两个机制?br />
1Q? 无论何时Q只要属性的值发生变化,该bean必须发送一个PropertyChange事gl所有已注册的监听器?br />
2Q? Z使监听器能够注册Qbean必须实现以下两个ҎQvoid addPropertyChangeListener(PropertyChangeListener listener);
void removePropertyChangerListener(PropertyChangerListener listener);
2Q用PropertyChangeSupport理监听?br />
可以通过java.bean包下的PropertyChangeSupportcL理监听器。要使用q个c,bean必须有一个此cȝ数据域。private PropertyChangeSupport listeners = new PropertyChangeSupport(this);
q样可以添加和U除监听器的d交给q个对象。public void addPropertyChangeListener(PropertyChangeListener listener) {
listeners.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
listeners.removePropertyChangeListener(listener);
}
当bean的属性发生变化时Q用PropertyChangeSupport对象的firePropertyChangeҎQ会一个事件发送给所有已l注册的监听器。该Ҏ有三个参敎ͼ属性的名字、旧的g及新的倹{属性的值必L对象Q如果是单数据类型,则必进行包装。listeners.firePropertyChange("ourString", oldString, newString);
所有注册的监听器实现PropertyChangeListener接口Q该接口中只有一个方法。public void propertyChange(PropertyChangeEvent e)
当bean的属性值发生变化时Q该Ҏ中的代码׃被触发。可以通过e.getOldValue();
e.getNewValue();
来得到changes.firePropertyChange("ourString", oldString, newString);中的oldString和newString?br />
3Qؓ什么要使用PropertyChangeSupport
使用q个cȝ理监听器的好处是Q它是线E安全的。如果用一个@环体来set Bean的属性,则这个类可以保证所有监听器执行触发事g的有序?br />
q有一个好处是Q这个类支持fire带烦引的属性改变事Ӟ详见java.bean.IndexedPropertyChangeEventQ。此时向注册的监听器发送一个PropertyChangeEvent的方法ؓQvoid fireIndexedPropertyChange(String PropertyName,int index,Object oldValue,Object newValue)
4Q实例代?
public class SomeBean {
private String property;
private PropertyChangeSupport changeSupport;
public void setProperty(String newValue) {
String oldValue = property;
property = newValue;
changeSupport.firePropertyChange("property", oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
changeSupport.add(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
changeSupport.remove(l);
}
}
最q看代码一直碰到这个类Q先做一个ȝ?br />
先看PropertyChangeSupportcȝ官方文档解释Q?br />
This is a utility class that can be used by beans that support bound properties. You can use an instance of this class as a member field of your bean and delegate various work to it.
1Q关联属?br />
在JavaBean的设计中Q按照属性的不同作用又细分ؓ四类Q单值属性、烦引属性、关联属性、限制属性。关联属性,也称之ؓl定属性。绑定属性会在属性值发生变化时Q通知所有相关的监听器。ؓ了实C个绑定属性,必须实现两个机制?br />
1Q? 无论何时Q只要属性的值发生变化,该bean必须发送一个PropertyChange事gl所有已注册的监听器?br />
2Q? Z使监听器能够注册Qbean必须实现以下两个ҎQvoid addPropertyChangeListener(PropertyChangeListener listener);
void removePropertyChangerListener(PropertyChangerListener listener);
2Q用PropertyChangeSupport理监听?br />
可以通过java.bean包下的PropertyChangeSupportcL理监听器。要使用q个c,bean必须有一个此cȝ数据域。private PropertyChangeSupport listeners = new PropertyChangeSupport(this);
q样可以添加和U除监听器的d交给q个对象。public void addPropertyChangeListener(PropertyChangeListener listener) {
listeners.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
listeners.removePropertyChangeListener(listener);
}
当bean的属性发生变化时Q用PropertyChangeSupport对象的firePropertyChangeҎQ会一个事件发送给所有已l注册的监听器。该Ҏ有三个参敎ͼ属性的名字、旧的g及新的倹{属性的值必L对象Q如果是单数据类型,则必进行包装。listeners.firePropertyChange("ourString", oldString, newString);
所有注册的监听器实现PropertyChangeListener接口Q该接口中只有一个方法。public void propertyChange(PropertyChangeEvent e)
当bean的属性值发生变化时Q该Ҏ中的代码׃被触发。可以通过e.getOldValue();
e.getNewValue();
来得到changes.firePropertyChange("ourString", oldString, newString);中的oldString和newString?br />
3Qؓ什么要使用PropertyChangeSupport
使用q个cȝ理监听器的好处是Q它是线E安全的。如果用一个@环体来set Bean的属性,则这个类可以保证所有监听器执行触发事g的有序?br />
q有一个好处是Q这个类支持fire带烦引的属性改变事Ӟ详见java.bean.IndexedPropertyChangeEventQ。此时向注册的监听器发送一个PropertyChangeEvent的方法ؓQvoid fireIndexedPropertyChange(String PropertyName,int index,Object oldValue,Object newValue)
4Q实例代?
public class SomeBean {
private String property;
private PropertyChangeSupport changeSupport;
public void setProperty(String newValue) {
String oldValue = property;
property = newValue;
changeSupport.firePropertyChange("property", oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
changeSupport.add(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
changeSupport.remove(l);
}
}