2012年7月24日
版权信息: 可以任意转载, 转载时请务必以超链接形式标明文章原文出处, 即下面的声明.
原文出处:http://blog.chenlb.com/2009/06/java-classloader-architecture.html
jvm classLoader architecture:
- Bootstrap ClassLoader/启动类加载器
主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作。 - Extension ClassLoader/扩展类加载器
主要负责jdk_home/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作。 - System ClassLoader/系统类加载器
主要负责java -classpath/-Djava.class.path所指的目录下的类与jar包装入工作。 - User Custom ClassLoader/用户自定义类加载器(java.lang.ClassLoader的子类)
在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性。
类加载器的特性:
- 每个ClassLoader都维护了一份自己的名称空间, 同一个名称空间里不能出现两个同名的类。
- 为了实现java安全沙箱模型顶层的类加载器安全机制, java默认采用了 " 双亲委派的加载链 " 结构。
classloader-architecture
classloader-class-diagram
类图中, BootstrapClassLoader是一个单独的java类, 其实在这里, 不应该叫他是一个java类。因为,它已经完全不用java实现了。它是在jvm启动时, 就被构造起来的, 负责java平台核心库。
自定义类加载器加载一个类的步骤
classloader-load-class
ClassLoader 类加载逻辑分析, 以下逻辑是除 BootstrapClassLoader 外的类加载器加载流程:
- // 检查类是否已被装载过
- Class c = findLoadedClass(name);
- if (c == null ) {
- // 指定类未被装载过
- try {
- if (parent != null ) {
- // 如果父类加载器不为空, 则委派给父类加载
- c = parent.loadClass(name, false );
- } else {
- // 如果父类加载器为空, 则委派给启动类加载加载
- c = findBootstrapClass0(name);
- }
- } catch (ClassNotFoundException e) {
- // 启动类加载器或父类加载器抛出异常后, 当前类加载器将其
- // 捕获, 并通过findClass方法, 由自身加载
- c = findClass(name);
- }
- }
线程上下文类加载器
java默认的线程上下文类加载器是 系统类加载器(AppClassLoader)。
- // Now create the class loader to use to launch the application
- try {
- loader = AppClassLoader.getAppClassLoader(extcl);
- } catch (IOException e) {
- throw new InternalError(
- "Could not create application class loader" );
- }
-
- // Also set the context class loader for the primordial thread.
- Thread.currentThread().setContextClassLoader(loader);
以上代码摘自sun.misc.Launch的无参构造函数Launch()。
使用线程上下文类加载器, 可以在执行线程中, 抛弃双亲委派加载链模式, 使用线程上下文里的类加载器加载类.
典型的例子有, 通过线程上下文来加载第三方库jndi实现, 而不依赖于双亲委派.
大部分java app服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务。
还有一些采用 hotswap 特性的框架, 也使用了线程上下文类加载器, 比如 seasar (full stack framework in japenese).
线程上下文从根本解决了一般应用不能违背双亲委派模式的问题.
使java类加载体系显得更灵活.
随着多核时代的来临, 相信多线程开发将会越来越多地进入程序员的实际编码过程中. 因此,
在编写基础设施时, 通过使用线程上下文来加载类, 应该是一个很好的选择。
当然, 好东西都有利弊. 使用线程上下文加载类, 也要注意, 保证多根需要通信的线程间的类加载器应该是同一个,
防止因为不同的类加载器, 导致类型转换异常(ClassCastException)。
为什么要使用这种双亲委托模式呢?
- 因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
- 考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader。
java动态载入class的两种方式:
- implicit隐式,即利用实例化才载入的特性来动态载入class
- explicit显式方式,又分两种方式:
- java.lang.Class的forName()方法
- java.lang.ClassLoader的loadClass()方法
用Class.forName加载类
Class.forName使用的是被调用者的类加载器来加载类的。
这种特性, 证明了java类加载器中的名称空间是唯一的, 不会相互干扰。
即在一般情况下, 保证同一个类中所关联的其他类都是由当前类的类加载器所加载的。
- public static Class forName(String className)
- throws ClassNotFoundException {
- return forName0(className, true , ClassLoader.getCallerClassLoader());
- }
-
- /** Called after security checks have been made. */
- private static native Class forName0(String name, boolean initialize,
- ClassLoader loader)
- throws ClassNotFoundException;
上面中 ClassLoader.getCallerClassLoader 就是得到调用当前forName方法的类的类加载器
static块在什么时候执行?
- 当调用forName(String)载入class时执行,如果调用ClassLoader.loadClass并不会执行.forName(String,false,ClassLoader)时也不会执行.
- 如果载入Class时没有执行static块则在第一次实例化时执行.比如new ,Class.newInstance()操作
- static块仅执行一次
各个java类由哪些classLoader加载?
- java类可以通过实例.getClass.getClassLoader()得知
- 接口由AppClassLoader(System ClassLoader,可以由ClassLoader.getSystemClassLoader()获得实例)载入
- ClassLoader类由bootstrap loader载入
NoClassDefFoundError和ClassNotFoundException
- NoClassDefFoundError:当java源文件已编译成.class文件,但是ClassLoader在运行期间在其搜寻路径load某个类时,没有找到.class文件则报这个错
- ClassNotFoundException:试图通过一个String变量来创建一个Class类时不成功则抛出这个异常
一:使用场景
1)使用的地方:树形结构,分支结构等
2)使用的好处:降低客户端的使用,为了达到元件与组合件使用的一致性,增加了元件的编码
3)使用后的坏处:代码不容易理解,需要你认真去研究,发现元件与组合件是怎么组合的
二:一个实际的例子
画图形,这个模式,稍微要难理解一点,有了例子就说明了一切,我画的图是用接口做的,代码实现是抽象类为基类,你自己选择了,接口也可以。
1)先建立图形元件
package com.mike.pattern.structure.composite;
/**
* 图形元件
*
* @author taoyu
*
* @since 2010-6-23
*/
public abstract class Graph {
/**图形名称*/
protected String name;
public Graph(String name){
this.name=name;
}
/**画图*/
public abstract void draw()throws GraphException;
/**添加图形*/
public abstract void add(Graph graph)throws GraphException;
/**移掉图形*/
public abstract void remove(Graph graph)throws GraphException;
}
2)建立基础图形圆
package com.mike.pattern.structure.composite;
import static com.mike.util.Print.print;
/**
* 圆图形
*
* @author taoyu
*
* @since 2010-6-23
*/
public class Circle extends Graph {
public Circle(String name){
super(name);
}
/**
* 圆添加图形
* @throws GraphException
*/
@Override
public void add(Graph graph) throws GraphException {
throw new GraphException("圆是基础图形,不能添加");
}
/**
* 圆画图
*/
@Override
public void draw()throws GraphException {
print(name+"画好了");
}
/**
* 圆移掉图形
*/
@Override
public void remove(Graph graph)throws GraphException {
throw new GraphException("圆是基础图形,不能移掉");
}
}
3)建立基础图形长方形
package com.mike.pattern.structure.composite;
import static com.mike.util.Print.print;
/**
* 长方形
*
* @author taoyu
*
* @since 2010-6-23
*/
public class Rectangle extends Graph {
public Rectangle(String name){
super(name);
}
/**
* 长方形添加
*/
@Override
public void add(Graph graph) throws GraphException {
throw new GraphException("长方形是基础图形,不能添加");
}
/**
* 画长方形
*/
@Override
public void draw() throws GraphException {
print(name+"画好了");
}
@Override
public void remove(Graph graph) throws GraphException {
throw new GraphException("长方形是基础图形,不能移掉");
}
}
4)最后简历组合图形
package com.mike.pattern.structure.composite;
import java.util.ArrayList;
import java.util.List;
import static com.mike.util.Print.print;
/**
* 图形组合体
*
* @author taoyu
*
* @since 2010-6-23
*/
public class Picture extends Graph {
private List<Graph> graphs;
public Picture(String name){
super(name);
/**默认是10个长度*/
graphs=new ArrayList<Graph>();
}
/**
* 添加图形元件
*/
@Override
public void add(Graph graph) throws GraphException {
graphs.add(graph);
}
/**
* 图形元件画图
*/
@Override
public void draw() throws GraphException {
print("图形容器:"+name+" 开始创建");
for(Graph g : graphs){
g.draw();
}
}
/**
* 图形元件移掉图形元件
*/
@Override
public void remove(Graph graph) throws GraphException {
graphs.remove(graph);
}
}
5)最后测试
public static void main(String[] args)throws GraphException {
/**画一个圆,圆里包含一个圆和长方形*/
Picture picture=new Picture("立方体圆");
picture.add(new Circle("圆"));
picture.add(new Rectangle("长方形"));
Picture root=new Picture("怪物图形");
root.add(new Circle("圆"));
root.add(picture);
root.draw();
}
6)使用心得:的确降低了客户端的使用情况,让整个图形可控了,当是你要深入去理解,才真名明白采用该模式的含义,不太容易理解。
一:使用场景
1)使用的地方:我想使用两个不同类的方法,这个时候你需要把它们组合起来使用
2)目前使用的情况:我会把两个类用户组合的方式放到一起,编程思想think in java里已经提到个,能尽量用组合就用组合,继承一般考虑再后。
3)使用后的好处:你不需要改动以前的代码,只是新封装了一新类,由这个类来提供两个类的方法,这个时候:一定会想到facade外观模式,本来是多个类使用的情况,我新封装成一个类来使用,而这个类我采用组合的方式来包装新的方法。我的理解是,设计模式本身就是为了帮助解决特定的业务场景而故意把模式划分对应的模式类别,其实大多数情况,都解决了同样的问题,这个时候其实没有必要过多的纠缠到模式的名字上了,你有好的注意,你甚至取一个新的名字来概括这样的使用场景。
4)使用的坏处:适配器模式,有两种方式来实现。一个是组合一个是继承,我觉得,首先应该考虑组合,能用组合就不要用继承,这是第一个。第二个,你采用继承来实现,那肯定会加大继承树结构,如果你的继承关系本身就很复杂了,这肯定会加大继承关系的维护,不有利于代码的理解,或则更加繁琐。继承是为了解决重用的为题而出现的,所以我觉得不应该滥用继承,有机会可以考虑同样别的方案。
二:一个实际的例子
关联营销的例子,用户购买完商品后,我又推荐他相关别的商品
由于减少代码,方法我都不采用接口,直接由类来提供,代码只是一个范例而已,都精简了。
1)创建订单信息
public class Order {
private Long orderId;
private String nickName;
public Order(Long orderId,String nickName){
this.orderId=orderId;
this.nickName=nickName;
}
/**
* 用户下订单
*/
public void insertOrder(){
}
}
2)商品信息
public class Auction {
/**商品名称*/
private String name;
/**制造商*/
private String company;
/**制造日期*/
private Date date;
public Auction(String name,String company, Date date){
this.name=name;
this.company=company;
this.date=date;
}
/**
* 推广的商品列表
*/
public void commendAuction(){
}
}
3)购物
public class Trade {
/**用户订单*/
private Order order;
/**商品信息*/
private Auction auction;
public Trade(Order order ,Auction auction){
this.order=order;
this.auction=auction;
}
/**
* 用户产生订单以及后续的事情
*/
public void trade(){
/**下订单*/
order.insertOrder();
/**关联推荐相关的商品*/
auction.commendAuction();
}
}
4)使用心得:其实外面采用了很多继承的方式,order继承auction之后,利用super .inserOrder()再加一个auction.recommendAuction(),实际上大同小异,我到觉得采用组合更容易理解以及代码更加优美点。
一:使用场景
1)使用到的地方:如果你想创建类似汽车这样的对象,首先要创建轮子,玻璃,桌椅,发动机,外廓等,这些部件都创建好后,最后创建汽车成品,部件的创建和汽车的组装过程本身都很复杂的情况,希望把部件的创建和成品的组装分开来做,这样把要做的事情分割开来,降低对象实现的复杂度,也降低以后成本的维护,把汽车的部件创建和组装过程独立出两个对应的工厂来做,有点类似建立两个对应的部件创建工厂和汽车组装工厂两个工厂,而工厂只是创建一个成品,并没有把里面的步骤也独立出来,应该说Builder模式比工厂模式又进了一步。
2)采用Builder模式后的好处:把一个负责的对象的创建过程分解,把一个对象的创建分成两个对象来负责创建,代码更有利于维护,可扩性比较好。
3)采用Builder模式后的坏处:实现起来,对应的接口以及部件的对象的创建比较多,代码相对来讲,比较多了,估计刚开始你会有点晕,这个可以考虑代码精简的问题,增加代码的可读性。
二:一个实际的例子
汽车的组装
1)首先创建汽车这个成品对象,包含什么的成员
public class Car implements Serializable{
/**
* 汽车序列号
*/
private static final long serialVersionUID = 1L;
/**汽车轮子*/
private Wheel wheel;
/**汽车发动机*/
private Engine engine;
/**汽车玻璃*/
private Glass glass;
/**汽车座椅*/
private Chair chair;
public Wheel getWheel() {
return wheel;
}
public void setWheel(Wheel wheel) {
this.wheel = wheel;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public Glass getGlass() {
return glass;
}
public void setGlass(Glass glass) {
this.glass = glass;
}
public Chair getChair() {
return chair;
}
public void setChair(Chair chair) {
this.chair = chair;
}
}
2)创建对应汽车零部件
public class Wheel {
public Wheel(){
print("--汽车轮子构建完毕--");
}
}
public class Engine {
public Engine(){
print("--汽车发动机构建完毕--");
}
}
public class Glass {
public Glass(){
print("--汽车玻璃构建完毕--");
}
}
public class Chair {
public Chair(){
print("--汽车座椅构建完毕--");
}
}
3)开始重点了,汽车成品的组装过程
public interface Builder {
/**组装汽车轮子*/
public void buildWheel();
/**组装汽车发动机*/
public void buildEngine();
/**组装汽车玻璃*/
public void buildGlass();
/**组装汽车座椅*/
public void buildChair();
/**返回组装好的汽车*/
public Car getCar();
}
以及实现类
public class CarBuilder implements Builder {
/**汽车成品*/
private Car car;
public CarBuilder(){
car=new Car();
}
/**组装汽车轮子*/
@Override
public void buildChair() {
car.setChair(new Chair());
}
/**组装汽车发动机*/
@Override
public void buildEngine() {
car.setEngine(new Engine());
}
/**组装汽车玻璃*/
@Override
public void buildGlass() {
car.setGlass(new Glass());
}
/**组装汽车座椅*/
@Override
public void buildWheel() {
car.setWheel(new Wheel());
}
/**返回组装好的汽车*/
@Override
public Car getCar() {
buildChair();
buildEngine();
buildGlass();
buildWheel();
print("--整个汽车构建完毕--");
return car;
}
}
4)最后汽车创建测试
public static void main(String[] args) {
/**创建汽车组装*/
Builder carBuilder=new CarBuilder();
Car car=carBuilder.getCar();
}
最后输出:
--汽车座椅构建完毕--
--汽车发动机构建完毕--
--汽车玻璃构建完毕--
--汽车轮子构建完毕--
--整个汽车构建完毕--
5)体会心得:Builder模式实际的重点就把汽车的组装过程和零部件的生产分开来实现,零部件的生成主要靠自己的对象来实现,我上面只是在构造函数里创建了,比较简单,而重点汽车的组装则交给CarBuilder来实现,最终由builder来先负责零部件的创建,最后返回出成品的汽车。
一:使用场景
1)经常使用的地方:一个类只有一个实例,eg:页面访问统计pv,统计的个数就只能保证一个实例的统计。
2)我们目前使用的情况:比如我想创建一个对象,这个对象希望只有一份实例的维护,在内存的保存也只有一份,也就是在同一个jvm的java堆里只保存一份实例对象,所以你会想一办法,在创建这个对象的时候,就已经能保证只有一份。
3)怎么改进:定义该对象的时候,就保证是同一份实例,比如:定义为私有构造函数,防止通过new的方式可以创建对象,然后在对象里定义一个静态的私有成员(本身对象的一个实例),然后再创建一个外面访问该对象的方法就好了。
4)改进的好处:代码在编译代码这个级别就被控制了,不至于在jvm里运行的时候才来保证,把唯一实例的创建保证在编译阶段;jvm里内存只有一份,从而内存占有率更低,以及更方便java垃圾回收
5)改进后的坏处:只能是代码稍微需要更多点,其实大家最后发现改进后的坏处,都是代码定义比之间要多一点,但以后的维护代码就降下来了,也短暂的代码量偏大来换取以后代码的精简。
二:一个实际的例子
总体的例子
package com.mike.pattern.singleton;
/**
* 总统
*
* @author taoyu
*
* @since 2010-6-22
*/
public class President {
private President(){
System.out.println("总统已经选举出来了");
}
/**总统只有一个*/
private static President president=new President();
/**
* 返回总统
*/
public static President getPresident(){
return president;
}
/**
* 总统宣布选举成功
*/
public void announce(){
System.out.println("伟大的中国人民,我将成你们新的总统");
}
}
/**
* @param args
*/
public static void main(String[] args) {
President president=President.getPresident();
president.announce();
}
1.使用场景
1)子类过多,不容易管理;构造对象过程过长;精简代码创建;
2)目前我们代码情况: 编写代码的时候,我们经常都在new对象,创建一个个的对象,而且还有很多麻烦的创建方式,eg:HashMap<String,Float> grade=new HashMap<String,Float>(),这样的代码创建方式太冗长了,难道你没有想过把这个创建变的短一点么,比如:HashMap<String,Float>grade=HashMapFactory.new(),可以把你创建精简一点;你也可以还有别的需求,在创建对象的时候,你需要不同的情况,创建统一种类别的对象,eg:我想生成不同的汽车,创建小轿车,创建卡车,创建公交汽车等等,都属于同种类别:汽车,你难道没有想过,我把这些创建的对象在一个工厂里来负责创建,我把创建分开化,交给一人来负责,这样可以让代码更加容易管理,创建方式也可以简单点。
比如:Car BMW=CarFactory.create(bmw); 把创建new由一个统一负责,这样管理起来相当方便
3)怎么改进:这个时候,你会想到,创建这样同类别的东西,我把这个权利分出去,让一个人来单独管理,它只负责创建我的对象这个事情,所以你单独简历一个对象来创建同类的对象,这个时候,你想这个东西有点像工厂一样,生成同样的产品,所以取了个名字:工厂模式,顾名思义,只负责对象的创建
4)改进后的好处:代码更加容易管理了,代码的创建要简洁很多。
5)改进后的坏处:那就是你需要单独加一个工厂对象来负责创建,多需要写点代码。
2.一个实际的例子
创建宝马汽车与奔驰汽车的例子
1)先提取出一个汽车的公用接口Car
public interface Car{
/**行驶*/
public void drive();
}
2)宝马和奔驰汽车对象
public class BMWCar implements Car {
/**
* 汽车发动
*/
public void drive(){
System.out.println("BMW Car drive");
}
}
public class BengCar implements Car {
/**
* 汽车发动
*/
public void drive(){
System.out.println("BengChi Care drive");
}
}
3)单独一个汽车工厂来负责创建
public class FactoryCar {
/**
* 制造汽车
*
* @param company 汽车公司
* @return 汽车
* @throws CreateCarException 制造汽车失败异常
*/
public static Car createCar(Company company)throws CreateCarException{
if(company==Company.BMW){
return new BMWCar();
}else if(company==Company.Beng){
return new BengCar();
}
return null;
}
}
4)最后的代码实现:
Car BMWCar=FactoryCar.createCar(Company.BMW);
BMWCar.drive();
1. 我说下我对设计模式的理解:任何一样事物都是因为有需求的驱动才诞生的,所以设计模式也不例外,我们平时在编写代码的时候,随着时间的深入,发现很多代码很难维护,可扩展性级差,以及代码的效率也比较低,这个时候你肯定会想办法让代码变的优美又能解决你项目中的问题,所以在面向对象语言里,你肯定会去发现很多可以重用的公用的方法,比如:接口的存在,你自然就想到了,让你定义的方法与你的实现分开,也可以很方便把不同的类与接口匹配起来,形成了一个公用的接口,你会发现这样做,好处会是非常多的,解决了你平时想把代码的申明与逻辑实现的分开。
2. 这个时候,你发现了,本身面向对象的语言里,已经暗藏了很多好处,你肯定会仔细去分析面向对象这个语言,认真去挖掘里面更多的奥秘,最后,你发现了,原来你可以把面向对象的特性提取成一个公用的实现案例,这些案例里能帮助你解决你平时编写代码的困扰,而这样一群人,就是所谓gof的成员,他们从平时设计建筑方面找到了灵感,建筑的设计也可以公用化以及重用化,所以他们也提取了相关的软件设计方面的公用案例,也就有了下面的相关的所谓23种设计模式,而里面这么多模式,你也可以把他们归类起来,最后发现就几类模式:创建,结构,行为等模式类别,而这些现成的方案,也可以在实际应用中充分发挥作用,随着大家的使用以及理解,发现其实这些所谓的模式里,你的确可以让你的代码变的更加优美与简练。
3. 我比较喜欢把代码变的更加优美与简练,优美的代码就是一看就懂,结构很清晰,而简历就是一目了然,又可以解决你的问题,就是代码又少效率又高,所以平时要养成写java doc的习惯,这样的代码才为清晰,所以才会更加优美。
4. 这些就是我对设计模式的理解,所以这么好的宝贝,我们不去深入的了解,的确可惜了,这就叫站到巨人的肩膀上.....
一:网络配置
1.关掉防火墙
1) 重启后生效
开启: chkconfig iptables on
关闭: chkconfig iptables off
2) 即时生效,重启后失效
开启: service iptables start
关闭: service iptables stop
2.下载软件
wget curl
3.安装和解压
安装 rpm -ivh
升级 rpm -Uvh
卸载 rpm -e
tar -zxvf
二:网卡设置
1、 设置ip地址(即时生效,重启失效)
#ifconfig eth0 ip地址 netmask 子网掩码
2、 设置ip地址(重启生效,永久生效)
#setup
3、 通过配置文件设置ip地址(重启生效,永久生效)
#vi /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0 #设备名,与文件同名。
ONBOOT=yes #在系统启动时,启动本设备。
BOOTPROTO=static
IPADDR=202.118.75.91 #此网卡的IP地址
NETMASK=255.255.255.0 #子网掩码
GATEWAY=202.118.75.1 #网关IP
MACADDR=00:02:2D:2E:8C:A8 #mac地址
4、 重启网络服务
#service network restart //重启所有网卡
5、 禁用网卡,启动网卡
#ifdown eth0
#ifup eth0
6、 屏蔽网卡,显示网卡
#ifconfig eth0 down
#ifconfig eth0 up
7、 配置DNS客户端(最多三个)
#vi /etc/resolv.conf
nameserver 202.99.96.68
8、更改主机名(即时生效)
#hostname 主机名
9、更改主机名(重启计算机生效,永久生效)
#vi /etc/sysconfig/network
HOSTNAME=主机名
三:两台linux拷贝命令:scp
1.安装scp:yum install openssh-clients
2.scp -r 本地用户名@IP地址:文件名1 远程用户名@IP地址:文件名2