------- android培训、java培训、期待与您交流! ----------
高新
一、java相关Java EE(JavaPlatform,Enterprise Edition)是sun公司推出的企业级应用程序版本
IDE(IntegratedDevelopment Environment,集成开发环境)
Jms即Java消息服务(Java Message Service)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于 在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
JMX(JavaManagement Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
JNDI(JavaNaming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API。命名服务将名称和对象联系起来,使得我们可以用名称访问对象。目录服务是一种命名服务,在这种服务里,对象不但有名称,还有属性。
IDE开发工具都支持使用工程化方式管理一个项目的程序开发过程,一般来说一个相对独立的项目就是一个工程,一个项目中涉及的多个java文件,资源文件等用一个工程进行管理。(在这里可以看看以前工作间中的某个工程的结构),在不使用工程管理的情况下,如果一个项目中包括多个Java源文件,编程人员需要精心维护这些源文件之间、以及源文件与其它文件的目录关系,需要逐一编译这些源文件,需要手工启动运行编译后的结果。如果将一个程序的所有源文件用一个工程来组织,开发工具能对所有源文件集中管理,记住每个源文件的位置和相互关系。工程中有哪几个源文件、启动类是哪个、启动参数设置等配置信息在工程中都记录。
Workspace与project
一个workspace可以包含多个project,一个workspace保留了eclipse的一套环境配置,例如,所使用的javac和java命令,等等,细节请查看window->preferences。如果要eclipse再配置一套环境选项,可以再创建一个workspace。Package explorer视图窗口中的filters菜单项,可以显示空的父包(此功能是默认关闭的)。
设置单个工程的javac与java
选择工程,右键->properties可以设置javac,右键->run asopen run dialog可以设置java。
先用新的工作间,然后创建新工程,默认的语言即为5.0。先使用Integer x = 3;调整编译器的语法版本为1.4,看到eclipse窗口报错了。然后将这个工程的语言设置为6.0,马上又看到bad version .class运行错误了,这是因为myeclise自带的java为1.5 。然后再将整个工作间的javac设置为6.0(eclipse自带的是jdk1.5),然后看新建工程的javac,也随之改成了6.0,运行则又报bad version .class错误。将工程的编译语言再单独改为5.0,运行则没了问题。整个工作间的语言设置为6.0后,再将整个工作间的java也设置为自己安装的java6
快捷键配置
快捷键的位置:General->keys,设置alt+/键(丁:content a 就会搜索出)进行内容提示时,要注意解除alt+/键原来的绑定关系,直接输入alt+/就可以找到它的绑定关系,删除绑定关系时也可以使用remove binding这个按钮。
在eclipse中。代码模板的设置位置:java->editor->Templates
Perspective透视图与view视图
每个小窗口就是一个视图,切换到透视图上面,就可以看到所有需要的视图
在eclipse打个断点,debug调试运行选中变量右键watch 就可以查看变量在每个断点的值
高版本的java能否运行低版本的javac编译的程序?(能)
低版本的java能否运行高版本的javac编译的程序?(不能)
二、Class类 反射的基石
Java程序在运行时,系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。其实例对象是对应各个类在内存中的字节码。
虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。
各个字节码对应的实例对象(Class类型)获取方法:
1.类名.class,例如,System.class;
2.对象.getClass(),例如,new Date().getClass();
3.Class.forName("类名"),例如,Class.forName("java.util.Date");
九个预定义Class对象
基本的Java类型(boolean、byte、char、short、int、long、float和double)和关键字void,每一个都对应一个Class对象
涉及到的方法:isPrimitive():判定指定的Class对象是否表示一个基本类型。
包装类型各有各的常量代表其基本类型,如:int.class == Integer.TYPE
判定是否为数组类型的Class实例对象:Class.isArray()(如:int[].class.isArray())
代码示例:
String str1="abc";
//获取str1的字节码文件
Class cls1=str1.getClass();
//获取String的字节码文件
Class cls2=String.class;
//返回与带有给定字符串名的类或接口相关联的 Class 对象
Class cls3=Class.forName("java.lang.String");
System.out.println(cls1==cls2);//true
System.out.println(cls1==cls3);//true
//isPrimitive()cls1是不是基本类型(字符串不是基本类型)
System.out.println(cls1.isPrimitive());
//判断int是不是基本类型.true
System.out.println(int.class.isPrimitive());
//判断int和Integer两个类的字节码是否相同false
System.out.println(int.class == Integer.class);
//TYPE表示基本类型 int 的 Class 实例。 true
System.out.println(int.class == Integer.TYPE);
//数组不是基本类型false
System.out.println(int[].class.isPrimitive());
//判断是否是一个数组类.isArray();true
System.out.println(int[].class.isArray());
三.反射
概念
反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field(成员字段)、Method(方法)、Contructor(构造方法)、Package(包)等等。
反射是java中的一种运行机制,是在运行状态中:
对于任意一个类,都能够获取到这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法;
这种动态获取的以及动态调用对象的方法的功能称为java语言的反射机制。
反射作用:
1.在运行时判定任意一个类所具有的成员变量和方法;
2.在运行时构造任意一个类的对象;
3.在运行时判定任意一个对象所属的类;
4.在运行时调用任意一个对象的方法;
5.生成动态代理。
Constructor类
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);//获得方法时要用到类型
创建实例对象:
1.通常方式:String str = new String(new StringBuffer("abc"));
2.反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));//调用获得的方法时要用到上面相同类型的实例对象
Class.newInstance()方法:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。
代码示例:
//用字节码获取获取Stirng的StringBuffer构造方法.返回Constructor类型
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
//再用Constructor类中的newInstance方法给构造方法实例化.
String str2=(String)constructor1.newInstance(new StringBuffer("abc"));
//打印返回的字符串角标位置上的字符
System.out.println(str2.charAt(2));
Filed类
Field类代表某个类中的一个成员变量
//成员变量的反射
ReflectPoint pt1 = new ReflectPoint(3, 6);
//成员变量时公有的可以正常反射
Field filedY = pt1.getClass().getField("y");
System.out.println(filedY.get(pt1));
//如果成员变量是私有的要进行暴力反射getDeclaredField
Field fieldX = pt1.getClass().getDeclaredField("x");
//调用逆转方法setAccessible(true);
fieldX.setAccessible(true);
//获取
System.out.println(fieldX.get(pt1));
将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"
public class Reflectest {
public static void main(String[] args) throws Exception {
ReflectPoint pt1=new ReflectPoint();
changeStringValue(pt1);
System.out.println(pt1);
}
private static void changeStringValue(Object obj) throws Exception{
Field[] fields=obj.getClass().getFields();//获取所有的成员变量
//遍历成员变量
for(Field field:fields){
//比较字节码用 ==
if(field.getType()==String.class){
String oldValue=(String)field.get(obj);//获取obj的String类型的成员变量
String newValue=oldValue.replace('b', 'a');//将b换成a
field.set(obj, newValue);//将此 Field表示的字段设置为指定的新值
}
}
}
}
class ReflectPoint {
public String str1="ball";
public String str2="basketball";
public String str3="itcast";
//重写toString方法
public String toString(){
return str1+" "+str2+" "+str3+" ";
}
}
Method类
Method类代表某个类中的一个成员方法
得到类中的某一个方法:Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
1.通常方式:System.out.println(str.charAt(1));
2.反射方式: System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:
jdk1.5:public Object invoke(Object obj,Object... args)(可变参数)
jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
用反射的方式执行某个类的main方法
首先要明白,为什么要用反射调用一个类中的main方法?
答:在程序的运行过程中,用户可能会指定调用哪个类的main方法,那么我们写代码的时候,就不能知道用户要调用哪个类的main方法,这个时候只有用反射,将用户传入进来的类名,进行反射调用那个类的main方法。
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决办法:
1.mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
2.mainMethod.invoke(null,(Object)new String[]{"xxx"}); 编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了。
数组的反射
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值无关)。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
int[] a1=new int[]{1,2,3};
int[] a2=new int[4];
int[][] a3=new int[3][4];
String[] a4=new String[]{"aaa","bbb","ccc"};
System.out.println(a1.getClass()==a2.getClass());//true
System.out.println(a1.getClass()==a3.getClass());//false
System.out.println(a1.getClass()==a4.getClass());//false
printObject(a1);
private static void printObject(Object obj){
//判断传入参数类型是否为数组
if(obj.getClass().isArray()){
//获取数组的长度用所遍历使用
int len = Array.getLength(obj);
//然后遍历obj并打印数组中的元素
for(int i=0;i<len;i++){
System.out.println(Array.get(obj, i));
}
}
else{
//如果不是数组直接打印
System.out.println(obj);
}
}
四.反射的作用--实现框架功能
框架
我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
框架要解决的核心问题
我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。
public class ReflectTest2 {
public static void main(String[] args) throws Exception {
//加载配置文件
InputStream is = ReflectTest2.class.getResourceAsStream("config.properties");
Properties prop=new Properties();
prop.load(is);
is.close();//关闭流
String className = prop.getProperty("className");//得到类名
Collection pt1=(Collection)Class.forName(className).newInstance();//调用空参数构造方法新建对象
ConstrucPoint cons1=new ConstrucPoint(3,3);
ConstrucPoint cons2=new ConstrucPoint(5,5);
ConstrucPoint cons3=new ConstrucPoint(3,3);
pt1.add(cons1);
pt1.add(cons2);
pt1.add(cons3);
pt1.add(cons1);
System.err.println(pt1.size());
}
}
五、JavaBean 内省
JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问,大家觉得这些方法的名称叫什么好呢?JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。如果方法名为setId,中文意思即为设置id,至于你把它存到哪个变量上,用管吗?如果方法名为getId,中文意思即为获取id,至于你从哪个变量上取,用管吗?去掉set前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。
如:
setId()的属性名id
isLast()的属性名last
setCPU的属性名是什么?CPU
getUPS的属性名是什么?UPS
总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。
一个符合JavaBean特点的类可以当作普通类一样进行使用,但把它当JavaBean用肯定需要带来一些额外的好处,我们才会去了解和应用JavaBean!好处如下:
在Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作,别人都这么用和要求这么做,那你就没什么挑选的余地!
JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。如果要你自己去通过getX方法来访问私有的x,怎么做,有一定难度吧?用内省这套api操作JavaBean比用普通类的方式更方便。
代码示例:
ReflectPoint pt1 = new ReflectPiont(5,5);//新建一个JavaBean类的对象实例
String propertyName = "x";//用户指定的一个类中的属性
//新建一个PropertyDescriptor类的实例,将指定属性与的指定类的Class文件传入
PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass));
Method methodGetX = pd.getReadMethod();//得到里面getX()方法
Object retVal = methodGetX.invoke(pt1);//调用上面获取到的方法
System.out.println(retVal);//打印字符串
Method methodSetX = pd.getWriteMethod();//得到里面setX()方法
methodSteX.invole(pt1,8);//调用方法
System.out.println(pt1.getX());//打印设置之后的值
稍等复杂点的方法:
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());//得到这个JavaBean的信息
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();//得到里面所有的属性信息
Object retVal = null;
for(PropertyDescriptor pd : pds){ //遍历
if(pd.getName().equals(propertyName)){
Method methodGetX = pd.getReadMethod();//得到成员变量的get方法
retVal = methodGetX.invoke(pt1);//调用方法
break;
}
}
return retVal;
Beanutils工具包
Beanutils工具包是由apache开发,方便于对JavaBean的进行一些操作, 在使用Beanutils工具包的时候,记得导入logging包,不然会运行出错
方法:
BeanUtils.getProperty()
BeanUtils.setProperty(pt1,"x","9");set里的X是int 但是我们用Beanutils设置值的时候是用String类型设置进去的,返回也是String类型的。
PropertiesUtils类
PropertiesUtils.setProperty(pt1,"x",9);
BeanUtils是以字符串的形式对javabean进行操作,
而PropertiesUtils是以属性本身的类型进行操作。
代码示例:
//BeanUtils是义字符串形式对JavaBean进行操作
System.out.println(BeanUtils.getProperty(n, propertyName));//属性是String型
BeanUtils.setProperty(n, propertyName, "11");//设置属性值
BeanUtils.setProperty(n, "birthday.time", "111");//对象n里面有个birthday对象,birthday对象有time属性
//JavaBean支持延级操作
System.out.println(BeanUtils.getProperty(n, "birthday.time"));//获取属性值
//PropertyUtils以属性本身类型对JavaBean进行操作
PropertyUtils.setProperty(n, propertyName, 9);//设置的属性值是int型,与JavaBean本身属性类型一致
PropertyUtils.getProperty(n,"x");
一.类加载器
定义与作用:与普通程序不同的是,Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader。
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。
BootStrap:用本地代码实现的(C++的一段二进制代码),它负责加载核心JavaClass(即所有java.*开头的类);
ExtClassLoader:负责加载扩展的Javaclass(例如所有javax.*开头的类和存放在JRE的ext目录下的类);
AppClassLoader:负责加载应用程序自身的类;
注:ExtClassLoader与AppClassLoader它们都是用Java语言编写的,由Bootstrap加载的
Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自java.lang.ClassLoader。
扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
类加载器的委托机制
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
1.首先当前线程的类加载器去加载线程中的第一个类。
2.如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
3.还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上级类加载器。
1.当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,
2.对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。
每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。
面试细节:有一道面试,能不能自己写个类叫java.lang.System,为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,也就是总是使用爸爸们能找到的类,这样总是使用java系统提供的System。
编写自己的类加载器
知识讲解:
1.自定义的类加载器的必须继承ClassLoader
2.loadClass方法(直接继承,省去委托机制的编写)与findClass方法(覆盖这个就行了)
3.defineClass方法
编程步骤:
1.编写一个对文件内容进行简单加密的程序。
2.编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。
3.编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。
实验步骤:
1.对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如: java MyClassLoader MyTest.class F:\itcast
2.运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader:java MyClassLoader MyTest F:\itcast
3.用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。
4.删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。
public class MyClassLoader extends ClassLoader {
private String path = null;
public MyClassLoader(String path) throws Exception{ //检查文件是否存在
File f = new File(path);
if(!f.isDirectory()){
throw new RuntimeException(path + " is not a directory");
}
this.path = path;
}
public Class findClass(String name) { //为什么不能抛出
try{
File f = new File(path,name.substring(0,name.lastIndexOf('.')) + ".class");
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
byte [] buf = bos.toByteArray();
fis.close();
bos.close();
return defineClass(name,buf,0,buf.length);
}catch(Exception e){
throw new ClassNotFoundException(name + " is not found!");
}
return null;
}
public static void cypher(InputStream istream,OutputStream ostream) throws Exception {
//下面这段代码可能遇到255的字节,当成byte就成了-1
/*byte b = 0;
while((b = (byte)istream.read()) != -1)
{
ostream.write(b ^ 0xff);
}*/
int b = 0;
while((b = istream.read()) != -1)
{
ostream.write(((byte)b) ^ 0xff);
}
}
public static void main(String [] args) throws Exception{
//下面省略了错误检查
if(!args[0].endsWith("class")){
ClassLoader loader = new MyClassLoader(args[1]);
Class cls = loader.loadClass(args[0]);
/*
让自定义类继承Date类
System.out.println(cls.getClassLoader().getClass().getName());
java.util.Date d = (java.util.Date)cls.newInstance();
System.out.println(d.toString());
*/
//Method m = cls.getMethod("test",null);//在jdk1.5中报警告,为什么?
Method m = cls.getMethod("test");
//m.invoke(cls.newInstance(),null);
m.invoke(cls.newInstance());
//((Test)cls.newInstance()).test();
return;
}
else{
FileInputStream fis = new FileInputStream(args[0]);
File f = new File(args[1], new File(args[0]).getName());//不用检查目录最后是否有目录分割符
FileOutputStream fos = new FileOutputStream(f);
cypher(fis,fos);
fis.close();
fos.close();
}
}
}
//类加载器不能加载这种非public的类
/*
Exception in thread "main" java.lang.IllegalAccessException: Class MyClassLoader
can not access a member of class MyTest with modifiers ""
*/
/*
class MyTest{
public void test(){
System.out.println("hello,www.it315.org");
}
}
*/
二.代理
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 (参看下页的原理图)
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
面向方面编程(AOP)
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业
务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,
如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
安全,事务,日志等功能要贯穿到好多个模块中,所以,它们就是交叉业务
重要原则:不要把供货商暴露给你的客户
动态代理
为什么要出现动态代理:要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累,JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
注意:JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
如果一个类没有实现接口,可以用CGLIB库动态生成一个类的子类,用这个子类来做这个类的代理类。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
利用InvocationHandler接口创建代理类代码示例:
[java] view plaincopy
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);//创建代理类
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);//获取代理类的构造方法
class MyInvocationHandler1 implements InvocationHandler//内部类,实现InvocationHandler接口
{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
}
//代理类通过反射调用方法,参数为内部类的实例对象
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());
System.out.println(proxy1.toString());
//运用匿名内部类
Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler()
{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});
//用Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Collection proxy3=(Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
ArrayList target= new ArrayList();
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+" running time:"+(endTime - startTime));
return retVal;
}
});
动态代理类框架:
//事务接口
public interface Advice {
void beforeMethod(Method method);//方法执行之前执行的方法
void afterMethod(Method method);//方法执行之后执行的方法
}
//实现事务接口
public class MyAdvice implements Advice {
long startTime=0;
@Override
public void beforeMethod(Method method) { //覆盖父类方法
startTime = System.currentTimeMillis();
}
@Override
public void afterMethod(Method method) { //覆盖父类方法
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+" running time:"+(endTime - startTime));
}
}
//创建代理框架
public static Object getProxy(final Object target,final Advice advice) {//传入目标类与事务类
Object proxy3 =Proxy.newProxyInstance(
target.getClass().getClassLoader(), //类加载器
target.getClass().getInterfaces(),//实现的接口
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
advice.beforeMethod(method);//执行事务类方法
Object retVal = method.invoke(target, args);//执行目标类的方法
advice.afterMethod(method);//执行事务类方法
return retVal;
}
});
return proxy3;
}