java基础加强
1 //java基础加强-课程价值与目标介绍 2 /* 3 JDK1.5中的新特性 4 JDK1.6中的新特性 5 */
1 //java基础加强_eclips及IDE开发工具介绍 2 /* 3 (1)MyEclips是eclipse的一个插件 4 (2)运行MyEclipse实际上是在运行java.exe,java.exe启动一个java类 5 (3)java ee,ide,jms,jmx,jndi都是什么的简写 6 ide:integrated development environment(集成开发环境) 7 (4)集成开发环境还有netbeans,是sun公司的开源的,免费的开发环境,另一个集成开发环境是JBuilder,不过它已经没落了(收费) 8 */
1 //java基础加强_eclipse工程管理与快捷键配置 2 /* 3 (1).MyEclipse中的编译器和运行器 4 Window-Perferences(参数选择)-Compier(编译器) 5 Window-Perferences(参数选择)-Installed JREs(安装的虚拟机运行环境) 6 (2).新建一个工作区,新建一个普通的工程,新建一个类 7 劲量让别人高兴,自己就会有很多机会 8 要求用英文写类名,不懂就查字典,一定要养成习惯 9 (3).工程名重构(refactor) 10 (4).为工作区设置打印快捷键 11 Window-Perferences-General-Keys-(输入content assist)-设置content assist快捷键为Alt+/ 12 当在界面输入syso然后按住Alt+/就可以 13 14 */
//java基础加强_eclipse视图管理与程序调试 /* (1).eclipse中的透视图(perspective)和视图(view)的关系 透视图由若干个视图组成的,透视图有主透视图,调试透视图等等 (2).如何调试变量 先在要调试的变量前打上断点,然后在要调试的变量上点击右键选择Debug As(调试)就进入了调试透视图 然后调试透视图中在要调试的变量上点击右键选择观察(Watch),并且可以选择单步调试 */
//java基础加强_配置eclipse的编译器与运行环境 /* (1)配置整个工作区的编译器和JRE Window-Perferences-Compiler Window-Perferences-Installed JREs (2)配置单个工程的javac和java 在工程名上单击右键,选择Properties-java Compiler 在工程名上单击右键,选择Properties-Run/Debug Settings-StaticImport(类名)-JRE 注意:高版本的java能够运行低版本javac编译出来的程序,低版本的java不能够运行高版本javac编译的程序 */
//java基础加强_在eclipse中配置java模板代码 /* (1)为代码添加模板 用鼠标左键选中需要添加模板的代码,单后点击鼠标右键,选择Surrounde with,然后选择需要的模板 (2)自定义模板 Window-Perferences-Java-Editor-Templates,然后点击new新建一个模板,写上模板的名字,写上模板的主体,可以点击Insert Variable插入一些参数 */
//java基础加强_在eclipse中导入已有工程 /* (1)导入已有工程 先将已有工程放到工作区中,然后点击eclipse中的File-Import,选择Exisiting Projects into Workspace, 然后点击next,再在Select root directory中指定工程文件路劲 (2)导入的工程与Eclipse的Jre冲突 在工程项目上点击鼠标右键,选择Build Path-Configure Build Path-Libraries,选择Add Library-选择JRE System Library */
//java基础加强_java5的静态导入与编译器语法设置 /* (1)jdk1.5的第一个新特新,静态导入 import static java.lang.Math.*; //导入java中lang包中的Math的所有静态方法方法 (2)静态导入只有jdk1.5之后才有,所以当版本低了需要将编译器设置高版本 Window-Perferences-Compiler,选择更高的JDK版本 (3)Alt+/快速打印 选择需要打印的语句,然后按Alt+/,然后选择sysout,就可以输出打印语句 */
//java基础加强_可变参数与OverLoad相关的面试题分析 /* 一.overload和override的区别,这个要做重点 override(重写) 1、方法名、参数、返回值相同。 2、子类方法不能缩小父类方法的访问权限。 3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。 4、存在于父类和子类之间。 5、方法被定义为final不能被重写。 overload(重载) 1、参数类型、个数、顺序至少有一个不相同。 2、不能重载只有返回值不同的方法名。 3、存在于父类和子类、同类中。 方法的重写(Overriding)和重载(Overloading)是Java多态性的不同表现。 重写(Overriding)是父类与子类之间多态性的一种表现,而重载(Overloading)是一个类中多态性的一种表现。 如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding) 。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被"屏蔽"了. 如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型或有不同 的参数次序,则称为方法的重载(Overloading)。不能通过访问权限、返回类型、抛出的异常 进行重载. 1. Override 特点 1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果; 2、覆盖的方法的返回值必须和被覆盖的方法的返回一致; 3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类; 4、被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。 2.Overload 特点 1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int, float), 但是不能为fun(int, int)); 2、不能通过访问权限、返回类型、抛出的异常进行重载; 3、方法的异常类型和数目不会对重载造成影响; 4、对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。 二.jdk1.5的第二个新特性可变参数 一个方法里接收的参数个苏不固定,这个时候就可以用到可变参数了 可变参数注意三点 (1)可变参数只能出现在参数列表的最后 (2)...位于变量类型和变量名之间,前后有无空格都可以 (3)在方法体中,可变参数的元素在数组中,可以遍历数组得到值 */ public static void main(String[] args) { System.out.println(add(2,3)); System.out.println(add(3, 4, 5, 5, 1)); } public static int add(int x, int... args){ int sum =x ; for(int y=0;y<args.length;y++){ //可变参数是以数组形式存在的 sum+=args[y]; } return sum; } }
//java基础视频加强_java5的增加for循环 /* (1)java5的第三个特性,增强for循环 语法:for(type 变量名 : 集合变量名或者数组) {...} (2)如何去获取知识的源头 下载官方的文档说明,然后去阅读官方文档而非去看什么野书野视频 */ public static void main(String[] args) { System.out.println(add(2,3)); System.out.println(add(3, 4, 5, 5, 1)); } public static int add(int x, int... args){ int sum =x ; for(int i : args) //jdk5的增强for循环 { sum += i; } return sum; } }
//java基础加强_基本数据的自动拆箱及享元模式 /* (1)自动拆箱和装箱 将一个整数类型赋给一个Integer对象就是自动装箱过程 将一个Integer对象和一个整数想运算,这个Integer会自动拆箱 (2)享元模式(flyweight) 如果有很多个小的对象,它们具有很多相同的属性,那么就把它们变成一个对象, 把哪些不同的属性作为方法的参数传传入,相同的属性称之为内部状态,不同的属性称之为外部状态 注意:数字只有-128~127(占一个字节),才能成为享元 */ public class autoBox { public static void main(String[] args) { Integer i1 = 2; //自动装箱 Integer i2 = new Integer(2); Integer i3 = new Integer(2); Integer i4 = 2; Integer i5 = 128; Integer i6 = 128; System.out.println(i1+i2); //自动拆箱 System.out.println(i1==i2); System.out.println(i2==i3); System.out.println(i1==i4); System.out.println(i5==i6); } }
//java基础加强_枚举的作用介绍 /* (1)枚举就是一个类,枚举的成员就是这个类的实例化对象以及这个类的子类匿名内部类, 其中枚举也可以定义构造方法,成员变量,普通方法和抽象方法 (2)枚举的元素必须位于枚举体重的最开始部位,并且枚举元素列表后面要有分号与其它成员分隔 */
//java基础加强_用普通类模拟枚举的实现原理 /* 一句话说不清楚,在一个类中定义固定的几个对象供外部调用 */ class EnumTest { public static void main(String[] argus) { WeekDay weekDay = WeekDay.MON; System.out.println(weekDay.nextDay()); System.out.println(weekDay.toString()); } } abstract class WeekDay { private WeekDay(){}; //防止外部创建对象 public final static WeekDay MON = new WeekDay(){ public WeekDay nextDay(){ return SUN; } }; //规定的值 public final static WeekDay SUN = new WeekDay(){ public WeekDay nextDay(){ return MON; } }; //规定的值 public abstract WeekDay nextDay(); /*public WeekDay nextDay() { if(this==MON) { return SUN; } else { return MON; } }*/ public String toString() { return this==MON? "MON":"SUM"; } }
//java基础加强_java5的枚举的基本应用 /* 本节课介绍了 (1)枚举的格式 public enum 类名{ 元素1,元素2,元素3...; //分号可要,可不要 } (2)枚举的实例化方法 name(); //返回枚举元素的名称 ordinal(); //返回枚举元素在枚举类中个排行 getClass(); //获取枚举类的名称 static valueOf(String str); //根据传入的字符返回指定的枚举元素 static values();//返回一个数组,这个数组中包含枚举中的元素 (3)MyEclipse中重构类名,在类文件上点击鼠标右键,选择rafactor-rename (4)MyEclipse中的多行注释,Ctrl+Shift+/ */ class { public static void main(String[] args) { WeekDay weekDay = WeekDay.FRI; System.out.println(weekDay.name()); //获取当前调用这个方法的的枚举元素名称 System.out.println(weekDay.ordinal()); //获取当前调用这个方法的枚举元素在枚举类中的排行位置 System.out.println(weekDay.getClass()); //获取枚举类的名称 System.out.println(WeekDay.valuOf("MON")); //更具给定的字符返回指定的枚举对象元素 System.out.println(WeekDay.values().length); //length只是一个属性,不是方法 } public enum WeekDay{ SUM,MON,TUE,WED,THI,FRI,SAT; //分号可要可不要 } }
//java基础加强_实现带构造方法的枚举 /* 本节课主要介绍了 (1)枚举类的构造方法必须放在枚举元素的后面,并且枚举元素要一分号结尾 (2)枚举类的构造方法必须私有化,防止外界创建对象 (3)枚举类的构造方法可以有多种重写方法,对应的枚举元素对象可以传入参数,默认是没有传入参数既带着空括号 */ class EnumTest1 { public static void main(String[] args) { WeekDay weekDay = WeekDay.SUM; } public enum WeekDay { SUM(1),MON(),TUE,WED,THI,FRI,SAT; //当枚举类中有构造函数时,必须放在枚举对象元素的后面,因为是先有枚举元素类加载才会调用相应的构造方法 private WeekDay(){System.out.println("first");} //枚举类的构造方法都要私有化 private WeekDay(int x){System.out.println("second");} //枚举类的构造方法都要私有化 } }
//java基础加强_实现带有抽象方法和构造方法的枚举 /* 本节课主要通过一个交通灯讲解了 (1)枚举可以是内部类 (2)枚举的元素可以是枚举类的实例化对象,也可以是这个枚举的匿名内部类 (3)当枚举中的元素是该枚举类的子类实例化时,可以调用父类的构造方法 (4)枚举只有一个成员时,就可以作为一种单例的实现方式 */ class EnumTest2 { public static void main(String[] args) { System.out.println("Hello World!"); } public enum TrafficLamp //这个枚举是一个内部类 { RED(30){ //这个是TrafficLamp的子类匿名内部类,而且调用了父类的构造方法 public TrafficLamp nextLamp() { return GREEN; } }, GREEN(40){ //每个灯都有等待时间 public TrafficLamp nextLamp() { return YELLOW; } }, YELLOW(5){ public TrafficLamp nextLamp() { return RED; } }; public abstract TrafficLamp nextLamp(); private int time; private TrafficLamp(int time){this.time=time;} //枚举的构造方法 } }
//java基础加强_透彻分析反射的基础_Class类 /* (1)java中对java类的描述也是一个类,这个类就是Class, 这类中描述了java类的共性,可以获取类名getName,获取包名getPackage, 获取方法这个类实现的接口getInterface,获取所有的方法类表getMethods等 (2)获取描述java类的对象(字节码)有三种方式 a.对象.getClass() b.类名.class c.Class.forName(String className)或则Class.forName(String name,boolean initialize,ClassLoader loader),其中initialize表示是否必须初始化类,loader表示指定的加载器 注意:第c种是两种情况,第一种指定的类已经加载到内存中了,则直接返回字节码,第二种指点的类还没有加载到内存中,则用指点的加载器加载然后在返回字节码 还要注意一点,这个两个方法都是静态的,而且要抛异常,并且传入的类要包含完整的包名 (3)八个基本数据类型以及关键字void都有自己独立的Class对象,就相当于把这个九个看做类,然后类.class,这九个都是基本数据类型 (4)isPrimitive()判断这个字节码是否为基本类型的字节码,九种基本类型 基本类型包装类.TYPE,代表这个包装类型的基本类型的字节码 数组也是一个数据类型,isArray()来判断是否为数组类型 总之,只要在程序中出现的类型,都有各自的Class实例对象 */ class ReflectDemo { public static void main(String[] args)throws Exception { String str = "haha"; Class cls1 = str.getClass(); Class cls2 = String.class; Class cls3 = Class.forName("java.lang.String"); //这个方法是一个Class的静态方法,而且要抛异常 System.out.println(cls1==cls2); System.out.println(cls2==cls3); System.out.println(int.class.isPrimitive()); //判断int.class这个字节码是不是基本数据类型字节码 System.out.println(int.class==Integer.TYPE); //判断Integer这个包装类的基本类型的基本类型的字节码是不是int类型的字节码 System.out.println(int[].class.isArray()); //判断int[].class是否为数组类型的字节码 } }
//java基础加强_理解反射的概念 /* (1)反射的定义 反射就是将java类中各种成分映射成相应的java类 */
//java基础加强_构造方法的反射应用 /* 本节课主要讲解 (1)通过字节码对象获取对应的构造方法对象 Constructor constructor = String.class.getConstructor(StringBuffer.class); 这里的StringBuffer.class表示要获取传参数为StringBuffer的那个构造方法 (2)通过构造方法获取对应的对象 String str1 = (String)constructor.newInstance(new StringBuffer("abc")); 这里的new StringBuffer("abc")表示要创建一个传递StringBuffer类型参数为abc的String对象 (3)Class中也有newInstance,这个方法是直接创建一个对应类的空参数的对象
(4)反射会导致程序性能下降,因为它会将第一次到的的数据缓存起来
*/ import java.lang.reflect.*; class ReflectDemo1 { public static void main(String[] args)throws Exception { Constructor constructor = String.class.getConstructor(StringBuffer.class); String str1 = (String)constructor.newInstance(new StringBuffer("abc")); String str2 = (String)String.class.newInstance(); //str2相当于new String() System.out.println(str1); System.out.println(str2); } }
//java基础加强_成员变量的反射 /* 本节课主要讲解了 (1)通过类的字节码对象获取公共的成员字段,然后获取特定对象里这个成员对应的值 Field fieldX = rp.getClass().getField("x"); //获取类字节码对象中公共的字段 System.out.println(fieldX.get(rp)); //打印出rp对象中对应的这个字段 (2)通过类的字节码对象获取私有的成员字段,然后设置成可以访问的,人后在获取特定对象里这个成员的值 Field fieldY = rp.getClass().getDeclaredField("y"); //获取类字节码对象所有声明的字段,包括私有的 fieldY.setAccessible(true); //将私有字段设置成可访问的 System.out.println(fieldY.get(rp)); //打印出rp对象中对应的这个字段 (3)为类快速添加构造方法 在代码去点击鼠标右键-Source-Generate Constructor using Fields */ import java.lang.reflect.*; class ReflectDemo2 { public static void main(String[] args) throws Exception { ReflectPoint rp = new ReflectPoint(2,5); Field fieldX = rp.getClass().getField("x"); //获取类字节码对象中公共的字段 System.out.println(fieldX.get(rp)); //打印出rp对象中对应的这个字段 Field fieldY = rp.getClass().getDeclaredField("y"); //获取类字节码对象所有声明的字段,包括私有的 fieldY.setAccessible(true); //将私有字段设置成可访问的 System.out.println(fieldY.get(rp)); //打印出rp对象中对应的这个字段 } } class ReflectPoint { public int x; private int y; ReflectPoint(int x,int y) { this.x = x; this.y = y; } }
//java基础加强_成员变量反射的综合案例 /* 本节课主要讲解了成员变量的综合案例 主要通过反射修改类中的String类型的值 步骤: (1)创建一个ReflectPoint类,里面创建几个String类型的字段,并且重写这个类的toString方法 class ReflectPoint { public int x; private int y; String str1 = "ball"; String str2 = "basketball"; String str3 = "ysfox"; ReflectPoint(int x,int y) { this.x = x; this.y = y; } public String toString() { return str1+":"+str2+":"+str3; } (2)创建一个实例化对象,并将对象传给ReflectPoint方法 ReflectPoint rp = new ReflectPoint(3,5); changeStringValue(rp); (3)最重要的一步:通过传入的字节码对象获取字节码中所有的公共(注意是公共)的字段, 然后遍历所有字段,然后塞选特定类型字段,然后替换字符,然后将替换后的字符设置成字段的新值 public static void changeStringValue(Object obj) throws Exception { Field[] fields = obj.getClass().getFields(); //注意:getFieds()获取的是字节码中所有公共的字段,所以字节码所属类的字段必须加上public for(Field field : fields) { //if(field.getType().equals(String.class)){} //一般反射用==,而不用equals,因为在内存中只有一个String.class字节码 //你用同一个String.class比较虽然可行,但是体现不出在这里的作用 if(field.getType()==String.class) { String oldValue = (String)field.get(obj); String newValue = oldValue.replace('b','a'); field.set(obj,newValue); } } } } } */ import java.lang.reflect.*; class ReflectDemo3 { public static void main(String[] args)throws Exception { ReflectPoint rp = new ReflectPoint(3,5); changeStringValue(rp); System.out.println(rp); } public static void changeStringValue(Object obj) throws Exception { Field[] fields = obj.getClass().getFields(); //注意:getFieds()获取的是字节码中所有公共的字段,所以字节码所属类的字段必须加上public for(Field field : fields) { //if(field.getType().equals(String.class)){} //一般反射用==,而不用equals,因为只有一个String.class, //你用同一个String.class比较虽然可行,但是体现不出在这里的作用 if(field.getType()==String.class) //塞选类型是String.class类型的字节码 { String oldValue = (String)field.get(obj); //获取字节码成员字段的值 String newValue = oldValue.replace('b','a'); //将成员字段中的b替换为a field.set(obj,newValue); //将成员字段设置成替换后的值 } } } } class ReflectPoint { public int x; private int y; public String str1 = "ball"; public String str2 = "basketball"; public String str3 = "ysfox"; ReflectPoint(int x,int y) { this.x = x; this.y = y; } public String toString() { return str1+":"+str2+":"+str3; } }
//java基础加强_成员方法的反射 /* 本节课主要通过反射获取字节码对象中的方法成员 (1)通过字节码对象获取指定的方法 Method methodCharAt = str.getClass().getMethod("charAt",int.class); //这里的参数charAt,表示要获取charAt()方法,这里的int.class表示要传入的参数为什么类型 (2)用指定的对象来调用获取到的方法,如果这个方法是静态方法则用null代替,并且传入方法的参数,如果没有参数,则用0或者null表示 System.out.println(methodCharAt.invoke(str,1)); //用指定的对象来调用charAt()方法,并且传递了参数,如果此处的str为null则表示这个方法是一个静态方法 System.out.println(methodCharAt.invoke(str,new Object[]{2})); */ import java.lang.reflect.*; class ReflectDemo4 { public static void main(String[] args)throws Exception { String str = "haha"; Method methodCharAt = str.getClass().getMethod("charAt",int.class); //这里的参数charAt,表示要获取charAt()方法,这里的int.class表示要传入的参数为什么类型 System.out.println(methodCharAt.invoke(str,1)); //用指定的对象来调用charAt()方法,并且传递了参数,如果此处的str为null则表示这个方法是一个静态方法 System.out.println(methodCharAt.invoke(str,new Object[]{2})); //jdk1.4没有可变参数则用Object数组来代替 } }
//java基础加强_对接收数组参数的成员方法进行反射 /* 本节课主要讲解了通过对一个类中的主方法的反射来说明如何对接收数组参数的成员方法进行反射 (1)与对普通方法反射最大的不同点在于,在传递数组参数的时候不能直接传数组,要先将数组打包成对象数组的一个元素,或者将这个数组直接 强制转换成Object,这样就避免直接传递数组,而数组被拆包后出现参数不匹配的的错误 */ import java.lang.reflect.*; class ReflectDemo5 { public static void main(String[] args)throws Exception { String testString = args[0]; //在运行时,为argus传递TestArguments Method method = Class.forName(testString).getMethod("main",String[].class); System.out.println(method.invoke(null,new Object[]{new String[]{"haha","hehe","heihei"}})); //最大区别点,需要将传入的数组包装成对象数组中的一个元素, 避免了编译器拆包 //method.invoke(null,(Object)new String[]{"111","222","333"}); //这是第二种写法,避免被拆包,注意,其实不用加输出语句,这里加了的话就会出现null,为什么加上输出语句会出现null } } class TestArguments { public static void main(String[] args) { for(String str : args) { System.out.println(str); } } }
//java基础加强_数组与Object的关系及其反射类型 /* 本节课主要讲解 (1)具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象 System.out.println(in1.getClass() == in2.getClass()); (2)Class中方法getName() public String getName(),以String的形式返字节码所属的类,接口,数组类,基本类型或者void的名称 (注意当是数组时,返回的是数组类型加元素类型加哈希值) (3)Class中的方法getSuperclass() public Class<? super T>getSuperclass(),获取字节码的父类字节码 (4)工具类Arrays中的asList()方法 public static Object List asList(Object[] obj) //jdk1.4 public static <T> List<T> asList(T... a) //jdk1.5 如果数组元素是基本数据类型用asList只能打印出类型和哈希值(按照jdk1.5的asList()方法运行),因为基本数据类型不是对象,而asList中必须传递对象 如果数组元素不是基本数据类型则用asList可以打印出数组中的值(按照jdk1.4的asList()方法运行) */ import java.lang.reflect.*; import java.util.*; class ReflectDemo6 { public static void main(String[] args)throws Exception { int[] in1 = new int[]{1,2,3}; int[] in2 = new int[4]; int[][] in3 = new int[2][4]; String[] str = new String[]{"hehe","haha","heihei"}; System.out.println(in1.getClass() == in2.getClass()); //具有相同的元素类型以及具有相同的维度的字节码相同 //System.out.println(in1.getClass() == in3.getClass()); //这两个对象的字节码是不同的 //System.out.println(in1.getClass() == in4.getClass()); //这两个对象的字节码也是不同的 System.out.println(in1.getClass().getName()); System.out.println(in1.getClass().getSuperclass().getName()); //它们父类名称都是Object System.out.println(str.getClass().getSuperclass().getName()); //它们父类名称都是Object Object obj1 = in1; Object obj2 = in2; //Object[] obj3 = in1; //基本数据类型不是对象 Object[] obj4 = in3; Object[] obj5 = str; System.out.println(in1); //直接打印数据会将类型和hashcode打印出来 System.out.println(str); System.out.println(Arrays.asList(in1)); //如果数组元素是基本数据类型用asList,这会把这个数组元素当做一个对象,只能打印出它类型和hashcode值,由于Object[] o = in1是错误的,所以向asList(in1)中传递in1时会按JDK1.5的方法走
System.out.println(Arrays.asList(str)); //如果数组元素不是基本数据类型用asList则能打印出数组中的元素 } }
//java基础加强_数组的反射应用 /* 本节课主要讲解 (1)通过反射将数组中的值打印出来 System.out.println(Array.get(obj,x)); (2)通过反射得出数组某一个元素然后判断这个元素的类型 System.out.println(Array.get(obj,x).getClass().getName()); */ import java.lang.reflect.*; class ReflectDemo7 { public static void main(String[] args) { String[] str1 = new String[]{"haha","hehe","heihei"}; Object[] obj = new Object[]{"你好",22,new int[]{12,23,4}}; String str2 = "hiahia"; printObject(obj); printObject(str2); } public static void printObject(Object obj) { Class clazz = obj.getClass(); if(clazz.isArray()) { int len = Array.getLength(obj); //通过反射获取数组长度 for (int x=0;x<len ;x++ ) { System.out.println(Array.get(obj,x)); //通过反射将数组中的值打印出来 System.out.println(Array.get(obj,x).getClass().getName()); //通过反射得出数组某一个元素然后判断这个元素的类型 } } else { System.out.println(obj); } } }
//java基础加强_ArrayList_HashSet的比较及Hashcode分析 /* 本节课主要讲解 (1)hashCode方法与HashSet类 HashSet集合不会存储相同元素的,原理是它将元素计算后的哈希值与对应存储区域的元素用equals来比较 如果有相同的则不会存储,没有相同的元素则会存储 (它内部采用对某个数字n进行取余的方式对哈希码进行分组和划分对象的存储区域) (2)内存泄露 通过改变HashSet中一个元素的哈希值从而导致内存泄露
面试题:java中有内存泄露吗,有,就是这个例子。 */ import java.util.*; class ReflectDemo8 { public static void main(String[] args) { Collection collection = new HashSet(); RflectPoint rp1 = new RflectPoint(3,3); RflectPoint rp2 = new RflectPoint(5,5); RflectPoint rp3 = new RflectPoint(3,3); collection.add(rp1); collection.add(rp2); collection.add(rp3); System.out.println(collection.size()); rp3.y=7; //将rp3中的y改变后,哈希值就改变了, collection.remove(rp3); //哈希值改变后,删除命令用改变后的哈希值到HshSet中去查找rp3却没有查到,所以造成了内存泄露 System.out.println(collection.size()); //结果任然是2个元素 } } class RflectPoint { private int x; public int y; RflectPoint(int x,int y) { this.x = x; this.y = y; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } public boolean equals(Object obj) { if(this == obj) return true; if(obj == null) return false; if(getClass() != obj.getClass()) return false; final RflectPoint other = (RflectPoint)obj; if(x != other.x) return false; if(y != other.y) return false; return true; } }
![]()
//java基础加强_框架的概念及用反射技术开发框架的原理
/*
学习反射就是为了写框架
*/ //java基础加强_用类加载器的方式管理资源和配置文件 /* (1)框架,工具类,自己写的类的关系 框架好比开发商开发的毛培房子,工具类就好比外面买的锁,自己写的类好比自己制造的门窗 框架调用你写的类,而你写的类可以调用工具类 (2)关于配置文件中的键值对 一般配置文件的名称为config.properties,特别要注意它的键值对就是纯字符,没有引号,没有分号,中间只用一个=连接 (3)Properties类一些方法(set和map集合不是太明白) Propertie有load(),setProperty(),getProperty(),list(),store(),propertyName(),StringPropertyName(),storeToXML (4)ips.close(),如果这个关闭不执行,也相当于内存泄露,这个是系统资源没有被关闭,而ips由GC(垃圾回收机制清理) (5)要动态的获取一个类的字节码文件,只能用Class.forName(传递进来的类名)这种形式,如果要获得这个类的一个实例用newStance,还要注意一点,newStance返回的是一个泛型,如果没有加泛型则必须强转型 Collection collection = (Collection)Class.forName(className).newInstance(); (1)在MyEclipse中存放在源文件中的java文件会编译成class文件,然后 传到class文件中,而源文件中的非java文件会原封不动的复制到class文件中 (2)第一种配置文件的加载的方法getRealPath(),这种既可以读也可以写 (3)第二种配置文件的加载的方法,用类加载器从classPath中查找出要查找的文件,然后用将资源作为流的方法将文件载入流中,遗憾的是这个方法只能读,不能写 InputStream ips = ReflectDemo9.class.getClassLoader().getResourceAsStream("resource/config.properties"); 注意括号中的路径相对于classPath路径,一般把配置文件放入resource包中 InputStream ips = ReflectDemo9.class.getResourceAsStream("config.properties");字节码本生也有getResourceAsStream方法,其实内部就是调用的类加载器,括号中既可以放绝对路径也可以放相对路径 */ import java.util.*; import java.lang.reflect.*; import java.io.*; class ReflectDemo9 { public static void main(String[] args) throws Exception { //InputStream ips = new FileInputStream("config.properties"); //InputStream ips = ReflectDemo9.class.getClassLoader().getResourceAsStream("config.properties"); //通过类加载器动态获取配置文件 InputStream ips = ReflectDemo9.class.getResourceAsStream("config.properties"); Properties props = new Properties(); props.load(ips); ips.close(); String className = props.getProperty("className"); Collection collection = (Collection)Class.forName(className).newInstance(); //Collection collection = (Collection)className.class.newInstance();这种方法是错误的,因为className.class中的这个className必须是一个类,不能待定传递类,如果要用待定传递值的话就用Class.forName(传递的类名) ReflectPoint rp1 = new ReflectPoint(3,3); ReflectPoint rp2 = new ReflectPoint(5,5); ReflectPoint rp3 = new ReflectPoint(3,3); collection.add(rp1); collection.add(rp2); collection.add(rp3); System.out.println(collection.size()); } } class ReflectPoint { private int x; private int y; ReflectPoint(int x,int y) { this.x = x; this.y = y; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ReflectPoint other = (ReflectPoint) obj; if (x != other.x) return false; if (y != other.y) return false; return true; } }
//java基础加强_由内省引出JavaBean的讲解 /* (1)JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法用于访问私有的字段,且方法名符合某种命名规则 (2)javaBean的属性是根据方法名推断出来的,它更本看不见java类内部的成员变量 如果get和set后面的字符第二字母是小写,则把第一个字母变成小的 */
//java基础加强_对JavaBean的简单内省操作 /* 本节课主要讲解了用内省来操作一个javaBean类中的数据 (1)PropertyDescriptor(String propertyName,Class<?> beanClass) 这个类用于来查找一个javaBnean类中的一个成员属性,第一个参数传递的是要查找的javaBean里面的成员属性的名称,第二个表示javaBean的字节码 (2)PropertyDescripttor类有一个获取读写javaBean成员属性的方法,getReadMethod()和getWriterMethod(),然会根据对象在调用这些方法就可以获取javaBean中的成员属性 (3)MyEclips中将一段代码快捷抽取成一个方法 选取要抽取成方法的代码,然后点击鼠标右键-(shift+alt+t)Refactor-Extract Method(抽取方法) (4)快速为一个javaBean创建getter和setter 在javaBean程序中点击鼠标右键-(shift+alt+s)Source - Creat getter and setter(快捷键r) */ import java.beans.*; import java.lang.reflect.*; class IntroSpectorTest { public static void main(String[] args)throws Exception { ReflectPoint rp = new ReflectPoint(2,5); String propertyName = "x"; /*//把这三段改成一个方法 PropertyDescriptor pd = new PropertyDescriptor(propertyName,rp.getClass()); Method methodGetX = pd.getReadMethod(); Object retValue = methodGetX.invoke(rp); */ Object retValue = getProperty(propertyName,rp); System.out.println(retValue); Object changeValue = 22; /*//把这个三段改成一个方法 PropertyDescriptor pd1 = new PropertyDescriptor(propertyName,rp.getClass()); Method methodSetX = pd1.getWriteMethod(); methodSetX.invoke(rp,changeValue); */ setProperty(propertyName,changeValue,rp); System.out.println(rp.getX()); } public static Object getProperty(String propertyName,Object rp)throws Exception { PropertyDescriptor pd = new PropertyDescriptor(propertyName,rp.getClass()); Method methodGetX = pd.getReadMethod(); Object retValue = methodGetX.invoke(rp); return retValue; } public static void setProperty(String propertyName,Object changeValue,Object rp)throws Exception { PropertyDescriptor pd = new PropertyDescriptor(propertyName,rp.getClass()); Method methodSetX = pd.getWriteMethod(); methodSetX.invoke(rp,changeValue); } } class ReflectPoint //javaBean类 { private int x; private int y; ReflectPoint(int x ,int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }
//java基础加强_对javaBean的复杂内省操作 /* 本节课主要讲解通过内省复杂获取javaBean中的成员属性 (1)获取javaBean中的信息的类 由于javaBean具有共通性,所以有专门的类来装它们的信息,这个类就是BeanInfo 获取这个对象的方法是IntroSpector.getBeanInfo(javaBean类的字节码); (2)BeanInfo中有获取所有属性描述的方法 getPropertyDescriptors();返回的是PropertyDescriptor[]数组,拿到想要的属性描述就可以获取读写的方法了,自然而然可以获取javaBean中的成员属性 */ import java.beans.*; import java.lang.reflect.*; class IntroSpectorTest1 { public static void main(String[] args)throws Exception { ReflectPoint rp = new ReflectPoint(35,33); String propertyName = "x"; Object retValue = getProperty(rp,propertyName); System.out.println(retValue); } public static Object getProperty(Object rp ,String propertyName)throws Exception { /*用内省简单获取javaBean中的成员属性 PropertyDescriptor pd = new PropertyDescriptor(propertyName,rp.getClass()); Method methodGetX = pd.getReadMethod(); return methodGetX.invoke(rp); */ //用内省复杂获取javaBean中的成员属性 BeanInfo beanInfo = Introspector.getBeanInfo(rp.getClass()); PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors(); Object retValue = null; for(PropertyDescriptor pd :pds) { if(pd.getName().equals(propertyName)) { Method methodGetX = pd.getReadMethod(); retValue = methodGetX.invoke(rp); break; } } return retValue; } } class ReflectPoint //javaBean类 { private int x; private int y; ReflectPoint(int x ,int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }
//java基础加强_使用BeanUtils工具包操作javaBean /* 此段代码在EditPlus中无法导入工具包,所以无法运行,只有在MyEclipse中运行 本节课在eclipse中到入两个工具类,主要介绍commons-beanutils中的一些操作的javaBean类中的成员的快捷方法, 另一个工具是commons-logging是第一个工具的必须要用到的工具包 (1)在MyEclips中导入工具包 先去apache官网去下载这两个工具包,然后解压将,再在MyEclipse中的项目工程中创建一个普通文件夹命名为lib, 然后将工具包解压后中的commons-beanutils.xxx.jar和commons-logging.xxx.jar放入创建的lib文件夹,然后分别在这两个jar 包上点击鼠标右键,选择Build Path-Add to Build Path,将这两个jar包添加到Build Path路径中 (2)介绍commons-beanutils工具中BeanUtils中的getProperty(Object bean,String name)和setProperty(Object bean,String name,Object value) getProperty(Object bean,String name)第一个参数传递的是javaBean对象,第二个参数是需要查询的属性的名称 setProperty(Object bean,String name,Object value),第一二个参数如上,第三个参数设置的值,以前的版本放的都是string类型,现在也改成了Object (3)介绍commons-beanutils工具中PropertyUtils中的方法getProperty(Object bean,String name)和setProPerty(Object bean,String name,Object value) 这两个方法也是同上,不同的是在早期setProperty的第三的个参数传递的是具体的类型,javaBean中是什么类型就传递什么类型 (4)JDK1.7中map的新特性 JDK1.7中map可以写成javaBean一样键值的类型,这样可以将它javaBean类装换成map类型,commons-beanutils中就提供这类方法 Map map = {name:"haha",age:33}; BeanUtils提供description(Object bean)将一个javaBean转换成Map populate(Object bean,Map properties)也是将一个javaBean中的属性转移到Map中 */ import java.beans.*; import java.lang.reflect.*; import java.util.*; class IntroSpectorTest2 { public static void main(String[] args) { ReflectPoint rp = new ReflectPoint(3,1); System.out.println(BeanUtils.getProperty(rp,"x")); BeanUtils.setProperty(rp,"x","22"); System.out.println(rp.getX()); BeanUtils.setProperty(rp,"birthday.time","32889498828874"); System.out.println(BeanUtils.getProperty(rp."birthday")); System.out.println(rp.getBirthday(rp,"x")); PropertyUtils.setProperty(rp,"y",333); System.out.println(PropertyUtils.getProperty(rp,"y")); } } class ReflectPoint //javaBean类 { private int x; private int y; private Date birthday = new Date(); //注意javaBean类中的成员变量必须是小写,因为getter和setter需要这种格式 ReflectPoint(int x ,int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
//java基础加强_了解和入门注解的应用 /* 本节课主要讲解了java.lang包中的三个注解SuppressWarnings,Deprecated,Override (1)当在程序中使用了一个已经废弃的方法或则属性时可以用@SuppressWarnings("deprecation")抑制警告 (2)当在程序中要标记某个方法或则属性已经废弃则在这个方法或者属性上添加@Deprecated (3)当在程序中要从写一个方法时,在这个方法上标记@Override可以避免重写错误, 注意:以上在MyEcloipes中有效,在EditPlus中没有实现注解 (4)复写对象的equals方法时,它传递的对象必须是Object,而非某个特定的类,这点特别要注意, */ class AnnotationTest { public static void main(String[] args) { @SuppressWarnings("deprecation") System.runFinalizersOnExit(true); } @Deprecated public static void sayHallo() { System.out.println("Hi,你没"); } } class RflectPoint { private int x; public int y; RflectPoint(int x,int y) { this.x = x; this.y = y; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } @Override public boolean equals(Object obj) { if(this == obj) return true; if(obj == null) return false; if(getClass() != obj.getClass()) return false; final RflectPoint other = (RflectPoint)obj; if(x != other.x) return false; if(y != other.y) return false; return true; } }
//java基础加强_注解的定义与反射调用 /* (1)注解的应用结构图 注解类-应用注解类的类-对应用注解类反射的类 (2)检查一个类中是否有某个注解,以及获取一个类中的注解 某个类的字节码文件.isAnnotationPresent(要查找的注解字节码文件) 某个类的字节码文件.getAnnotation(要获取的注解字节码文件) (3)注解的生命周期有三个阶段,java源文件阶段,class文件阶段,内存中字节码运行阶段,默认值是在class文件阶段 @Retention(RetentionPolicy.SOURCE) //java源文件阶段 @Retention(RetentionPolicy.CLASS) //class文件阶段,注意一点class文件不是字节码文件,字节码文件是类加载器将class文件加载到内存中转换成二进制的文件才是字节码文件 @Retention(RetentionPolicy.RUNTIME) //内存中字节码运行阶段 注意:这个注解是一个源注解 (4)设置一个注解可以放在那些地方@Target(value=ANNOTATION_TYPE) @Target(ElementType.ANNOTATION_TYPE) //可以放在注解上面 @Target(ElementType.CONSTRUCTOR) //可以放在构造方法上面 @Target(ElementType.FIELD) //可以放在字段上面 @Target(ElementType.LOCAL_VARIABLE) //可以放在局部变量上面 @Target(ElementType.METHOD) //可以放在方法上面 @Target(ElementType.PACKAGE ) //可以放在包名上面 @Target(ElementType.PARAMETER ) //可以放在参数上面 @Target(ElementType.TYPE ) //可以放在类,接口获取枚举上面 */ import java.lang.annotation.*; class AnnotationTest1 { public static void main(String[] args) { System.out.println("Hello World!"); } }
//java基础加强_为注解增加各种属性 /* 这章有些问题。 (1)注解中的成员可以是以下类型 原始数据类型,String,Class,enum,annotation,以前面为元素的数组 (2)注解中的属性可以设置默认值,default 后面加上默认值 (3)当注解中只有value属性需要设值时,其他属性都有默认值,可以在传递值时省略value= (4)当注解中的属性为数组时,而且数组只有一个值时就可以省略掉{} (3)在调用注解时,可以修改它们的值 */ import java.lang.annotation.*; @MyAnnotation(string="xixi",value="234",arrayAttr={3,4,5},weekDay=EnumTest.WeekDay.SUN,annotationAttr=@AnnnotationTest("oye")) class AnnotationTest2 { @MyAnnotation(2) public static void main(String[] args) { if(AnnotationTest2.class.isAnnotationPresent(MyAnnotation.calss)) { Annotation annotation = AnnotationTest2.class.getAnnottion(MyAnnotation.class); System.out.println(annotation) System.out.println(annotation.string()); System.out.println(annotation.value()); System.out.println(Arrays.asList(annotation.arrayAttr())); System.out.println(Arrays.asList(annotation.arrayStr())); System.out.println(annotation.weekDay().nextDay().name()); //name()和toString()方法相同 System.out.println(annotation.annotationAttr().value()); System.out.println(annotation.clazz()); } } } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD}) //这个注解的属性名为value,而且只有它需要设置,所以可以直接省略value=,而且这个属性值为数组类型 public @interface MyAnnotation { int value(); String string() default "haha"; int[] arrayAttr() default {1,2,3}; String[] arrayStr() default {"a","b","c"}; EnumTest.WeekDay weekDay() default EnumTest.WeekDay.MON; //成员为枚举 AnnotationTest annotationAttr() default @AnnotationTest("hiahia"); //成员为注解 Class clazz() default String.class; //成员为Class } public @interface AnnotationTest { String str() default "heihei"; }
//java基础加强_入门泛型的基本应用 /* 本节课通过jdk1.5前后使用集合来说明泛型 (1)jdk1.5之前没有使用泛型的时候,由于集合中元素类型不确定,在强制转换时就会出现错误 (2)jdk1.5之后使用了泛型,指定了集合中元素的类型,可以不用强制转换就可以取到值 (3)泛型只是给编译器用的,可以挡住源码中写入的非指定类型参数,编译之后的字节码就去掉了泛型 (4)由于编译之后的字节码会去掉泛型,没有了类型限定,所以可以通过反射为集合传入其它类型的元素 */ import java.util.*; class GenericTest { public static void main(String[] args) throws Exception { ArrayList collection1 = new ArrayList(); collection1.add(1); collection1.add(1L); collection1.add("abc"); //System.out.println((colletion)collection1.get(1)); //jdk1.5之前没有使用泛型的时候,由于集合中元素类型不确定,在强制转换时就会出现错误 ArrayList<String> collection2 = new ArrayList<String>(); collection2.add("haha"); collection2.add("hehe"); collection2.add("heihei"); System.out.println(collection2.get(1)); //jdk1.5之后使用了泛型,指定了集合中元素的类型,可以不用强制转换就可以取到值 ArrayList<Integer> collection3 = new ArrayList<Integer>(); System.out.println(collection2.getClass()==collection3.getClass()); //泛型只是给编译器用的,编译之后的字节码就去掉了泛型,所以这两个泛型不同的集合字节码却是相同的 collection3.getClass().getMethod("add",Object.class).invoke(collection3,"abc"); //通过反射为泛型集合添加其他类型说明泛型只在编译时有用,编译之后就去除了泛型 System.out.println(collection3.get(0)); } }
//java基础加强_泛型的内部原理及更深应用 /* 本节课讲解 (1)泛型的术语 整个ArrayList<E>称为泛型类型,E称为泛型变量 整个ArrayList<Integer>称为参数化的类型,Integer称为实际参数 整个ArrayList称为原始类型 (2)参数化类型和原始类型的兼容性 Collection<String> c = new Vector(); //正确,但会有警告,理由是新的JDK必须兼容老的JDK Collection c = new Vector<String>(); //正确,但会有警告,理由是原始类型包含各种类型,当然也包含String类型 (3)参数化类型不考虑参数的继承关系 Collection<String> c = new Vector<Object>(); Collection<Object> c = new Vector<String>(); (4)编译器不允许创建泛型变量的数组 Vector<Integer>[] vector = new Vector<Integer>[10]; //数组中的元素不能用泛型,不烦报错 (5)注意点,编译器只检查语法错误,不会去执行代码,所以下面的代码是正确的 Vector v = new Vector<String>(); Vector<Object> v1 = v; //这两部分开写就正确,如果写成一句就错误,因为编译器只检查语法错误,不会去运行代码 */ import java.util.*; class GenericTest1 { public static void main(String[] args) { Collection<String> c1 = new Vector(); //正确,但会有警告,理由是新的JDK必须兼容老的JDK c1.add("ddd"); Collection c2 = new Vector<String>(); //正确,但会有警告,理由是原始类型包含各种类型,当然也包含String类型 c2.add(333); c2.add("ddd"); //Collection<Object> c3 = new Vector<String>(); 参数化类型不存在继承关系 //Collection<String> c4 = new Vector<Object>(); 参数化类型不存在继承关系 //Vector<Integer> vector = new Vector<Integer>[10];数组中的元素不能用泛型,不烦报错 } }
//java基础加强_泛型的通配符扩展应用 /* (1)泛型中的?的使用以及注意事项 泛型中?代表可以传递很多种类型,具体哪种类型没有指定,由传递值传递 注意:既然没有指定具体类型,所以就不能用具体类型的方法,这点要特别注意 (2)泛型中?通配符的上界和下界 正确:Vector<? extends Number> x = new Vector<Integer>(); 错误:Vector<? extends Number> x = new Vector<String>(); 正确:Vector<? super Integer> x = new Vector<Number>(); 错误:Vector<? super Integer> x = new Vector<Byte>(); */ import java.util.*; class GenericTest2 { public static void main(String[] args) { System.out.println("Hello World!"); } public static void printCollection1(Collection<Object> cols) { for(Object obj : cols) { System.out.println(obj); } cols.add("haha"); //因为没有用泛型,指定了集合中的元素的类型为Object,所以可以用参数化相关的方法 //cols = new HashSet<Date>(); //会报错,因为集合中指定了接收元素的类型必须为Object,另外一点参数化的类型之间不存在继承关系 } public static void printCollection2(Collection<?> cols) { for(Object obj : cols) { System.out.println(obj); } //cols.add("haha"); //会报错,因为此处的集合用到了泛型中的通配符?,没有具体指明传进来的到底是什么类型的,你这里添加String,就会出错, System.out.println(cols.size()); //也就是说,当用到了泛型通配符的时候,就不能用具体类型的方法,但却可以用没有涉及到类型的方法,不如集合长度 cols = new HashSet<Date>(); //由于泛型没有指定是什么类型,所以可以这样赋值 } }
//java基础加强_泛型集合的综合应用案例 /* 本节课讲解 (1)map集合要转成set集合才能用迭代 (2)map集合转成set集合过程中用的方法entrySet() (3)然后要注意嵌套泛型 Set<Map.Entry<String,Integer>> entrySet = maps.entrySet(); */ import java.util.*; class GenericTest3 { public static void main(String[] args) { HashMap<String,Integer> maps = new HashMap<String,Integer>(); maps.put("haha",22); maps.put("hehe",33); maps.put("xixi",25); Set<Map.Entry<String,Integer>> entrySet = maps.entrySet(); Iterator<Map.Entry<String,Integer>> it = entrySet.iterator(); while(it.hasNext()) { Map.Entry<String, Integer> map = it.next(); System.out.println(map.getKey()+"..."+map.getValue()); } } }
//java基础加强_自定义泛型方法及应用 /* 本节课主要讲解自定义泛型方法 (1)java中泛型方法没有C++模板功能强大,是因为编译器指令集需要重写工程量非常大。 (2)定义泛型必须紧挨返回类型之前,并且泛型参数通常用单个大写字母表示 (3)对于多个泛型相加,得到的类型取他们的公约数 (4)将数组中的任意两个元素位置置换,注意,这个方法只能置换应用参数类型的数组,不能置换基本数据类型的数组 */ class GenericTest4 { public static void main(String[] args) { Integer in = add(1,2); Number nb = add(2.4,3); Object obj = add(3,"abc"); String[] strs = swap(new String[]{"123","456","789"},1,2); //int[] ints = swap(new int[]{1,2,3},1,2); 泛型只接受应用类型的数据,基本数据类型不接受 for(String str :strs) { System.out.println(str); } } public static <T> T add(T x,T y) { //return x+y //这种写法错误,因为有可能x或者y没有+号的方法 return null; } public static <T> T[] swap(T[] arr,int x,int y) //注意,这个方法只能置换应用参数类型的数组,不能置换基本数据类型的数组 { T temp = arr[x]; arr[x] = arr[y]; arr[y] = temp; return arr; } }
//java基础加强_自定义泛型方法的练习与类型推断总结 /* (1)编写一个泛型方法,自动将Object类型的对象转换成其他类型。 (2)定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。 (3)采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容 (4)定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中。 (5)定义一个方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另一个数组中。 */ class GenericTest5 { public static void main(String[] args) { String obj = autoCovert(new String("haha")); System.out.println(obj); copyToArray(new Vector<String>(),new String[10]); copyArrayToArray(new Date[10],new String[10]); //取最大公约数 } public static <E> E autoCovert(E obj) //)编写一个泛型方法,自动将Object类型的对象转换成其他类型。 { return (E)obj; } public static <T> void fillArray(T[] arrs,T obj) //定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。 { for(int x =0;x<arrs.length;x++) { arrs[x]=obj; } } public static <T> void printCollection(Collection<T> collection) //采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容 { System.out.println(collection.size()); for(Object obj : collection) { System.out.println(obj); } } public static <T> void copyToArray(Collection<T> collection,T[] arr) //定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中。 { } public static <T> void copyArrayToArray(T[] dest,T[] src) //定义一个方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另一个数组中。 { } }
//java基础加强_自定义泛型类的应用 /* //dao(data access object--->crud) 本节课主要讲解增删改查几个方法 */ class GenericDao<E> { public void add(E x) { } public E getById(int id) { return null; } public void delete(T obj) { } public void delete(int id) { } public void update(E obj) { } public Set<E> findByConditions(String where) { return null; } public E findByUserName(String name) { return null;s } //public static void update2(E obj) }
//java基础加强_通过反射获取泛型的实际类型参数 /* 我昨天思考的问题就是你明天思考的问题--张孝祥 黎活明的一段代码:通过反射实现获取Vector中装的到底是什么类型 要想获取一个泛型的类型,可以将这个泛型作为参数传递给一个方法,然后用反射方法可以得到参数的中泛型的类型 */ class GenericTest6 { public static void main(String[] args) { //Vector<Date> v1 = new Vector<Date>(); Method applayMethod = GenericTest6.class.getMethod("applyVector",Vector.class); Type[] types = applayMethod.getGenericParameterType(); //这个方法是获取方法中所有的参数泛型 类型,参数有多个,所以用数组表示 ParameterizedType pType = (ParameterizedType)type[0]; //获取第一个参数的类型 System.out.println(pType.getRawType()); //获取泛型的原始类型,泛型参数有多个,所以返回的 System.out.println(pType.getActualTypeArguments()[0]); //获取实际化泛型的参数。 } public static void applayVector(Vector<Date> v1) { } /*public stattic void applayVector(Vector<Integer> v1)这个和上面那个不是重载,因为类型相同,只是泛型实例化不同,但是类型却是相同的 { } */ }
注意:以下代码用MyEclipse,主要包含两部分,加载器
/** *(1)java虚拟机安装了多个加载器,系统默认的主要三个BootStrap,ExtClassLoader,AppClassLoader,每个加载器加载特定位置的类 *(2)加载器也是java类,也需要加载器来加载,而BootStrap不是java类,它是由C++写第一段二进制代码 *(3)获取类加载器的的名字 * System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName()); *(4)System类的加载器是BootStrap,BootStrap不是java类所以打印不出它的类名 *(5)查看加载器继承图BootStrap-ExtClassLoader-AppClassLoader *(6)不同的类加载器管辖不同的文件(BootStrap管辖JRE/lib/rt.jar)(ExtClassLoader管辖JRE/lib/ext/*.jar)(AppCLassLoader管辖CLASSPATH指定下的类,jar) *(7)将文件打成jar包,在要导出的文件上单机鼠标右键选择Export-java(JAR file)-目录写JDK所在目录中的(JRE/lib/ext/JAR包名.jar),lib是我们平时用到类的jar包,ext是扩展的jar包 *(8)类加载器的委托机制,java中在加载一个类的时候子类通常通过传递给父类,让父类去查找加载,如果父类没有查找到,父类在传递给它的父类,爷爷类去查找,如果爷爷是顶级了,则爷爷如果没找到则返回给父类,让父类去找,父类没找到返回给发起者,发起者没找到就抛出ClassNotFoundException异常,而不会继续交给子类去查找,这个就是类加载器的委托机制 *(9)我们如果想自己写加载器就必须继承ClassLoader这个类,然后挂载到AppClassLoader上去,只有用我们自己写的类加载器去加载我们自己的加密类才能将字节码读出来 *(10)在加载一个线程中的类时,系统默认派当前线程的类加载器去加载线程中的第一个类,加载完这个类,如果这个线程中还涉及到第二个类,java虚拟机任然会用第一个类加载器去加载第二个类,当然你也可以调用ClassLoader.LoaderClass()指定某个的类去加载类 *(11)面试题:能否自己写一个类叫java.lang.System * 答案:通常不可以,因为类加载器是采用的委托机制,即使你写了一个System类存放在classpath路径下, * AppClassLoader加载器也会将类委托为父类,在父类的BootStrap在JRE/lib/rt.jar中则加载了系统 * 自带的system类,而你自己写的却无法加载到,所以通常不可以,但是我可以自己写一个独立开来的加载器去单独加载自己写的System类 * *java基础加强_自定义类加载器的编写原理分析 *(1)首先我们自定义的加载器需要继承一个抽象类ClassLoader,这样默认就关在到AppClassLoader这个类上成为它的子类 *(2)loadClass(String name),给定一个字符串类名,它就会去找指定的类,并加载这个类,然会返回一个class, * loadClass()其实这个方法它的内部会去找它的父类,找完了父类,如果找不到父类就返回来调用findClass(String name),我们所要做的就是重写findClass,保留loadClass中的找父类的功能 * 这个就是模板方法设计模式(继承父类有相同的方法,实现父类抽象方法,实现父类的抽象方法内容不同) *(3)当我们得到了class文件之后如何将class文件装换成字节码?把class文件转换成字节码的方法definClass(),这个方法有多种形式,把一个字节数组变成一个class * * * class NetworkClassLoader extends ClassLoader { String host; int port; public Class findClass(String name) { byte[] b = loadClassData(name); return defineClass(name, b, 0, b.length); } private byte[] loadClassData(String name) { // load the class data from the connection . . . } } (1)编写一个自己的类加载器,只能用来加载自己加密的类 过程大概就是写一个类继承LoaderClass这个类,并重写findClass() java基础加强_类加载器的一个高级问题的实验分析 (1)一个类加载器的高级问题分析 这个是一个Web项目,需要用到Tomcat,Tomcat也是一个java程序,这个java程序内部编写了很多个类加载器, 并且把这些类加载器安装到了java虚拟机上面,Tomcat启动起来java虚拟机上就有很多类加载器,其中有很多就是tomcat提供的 我们在tomcat下开发的web程序就是severlet,serverlet也是java类,这个java类就是tomcat自己提供的加载器提供的 */ package cn.itcast.day2; import java.util.Date; public class ClassLoaderTest { /** * @param args */ public static void main(String[] args)throws Exception { // TODO Auto-generated method stub System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName()); //获取类加载器类的名字,结果为AppClassLoader //System.out.println(System.class.getClassLoader().getClass().getName()); //结果为空指针异常,表示getName()前面某个部分为null,由于类被加载就必定有getClass(),必定有getName() System.out.println(System.class.getClassLoader()); //所以出现null的可能性必定在类加载器上,所以注意System这个类的加载器为BootStrap,它不是java类所以没有class文件,也就没有类名 ClassLoader loader = ClassLoaderTest.class.getClassLoader(); //查看加载器继承图BootStrap-ExtClassLoader-AppClassLoader while(loader!=null){ System.out.println(loader.getClass().getName()); loader = loader.getParent(); } System.out.println(loader); //System.out.println(new ClassLoaderAttachment().toString()); //有包名的类不能调用无包名的类 //调用自己的类加载器,加载器调用字节的loadClass()方法,loadClass先去找父类如果父类无法加载,然后才找fineClass()方法 Class clazz = new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.classLoaderAttachment"); //父类只能加载报名+类名 //ClasssLoaderAttcachment d1 = (ClasssLoaderAttcachment)clazz.newInstance(); 编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类 Date d1= (Date)clazz.newInstance(); System.out.println(d1); } }
package cn.itcast.day2; import java.util.Date; public class ClassLoaderAttachment extends Date { public String toString(){ return "hello yoursister"; } }
package cn.itcast.day2; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class MyClassLoader extends ClassLoader{ //继承ClassLoader,覆盖findClass() /** * @param args */ public static void main(String[] args)throws Exception { // TODO Auto-generated method stub String srcPath = args[0]; String destDir = args[1]; FileInputStream fis = new FileInputStream(srcPath); String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1); //获取最后的文件名 String destPath = destDir+"\\"+destFileName; //拼凑成输出流目标路径 FileOutputStream fos = new FileOutputStream(destPath); cypher(fis,fos); fis.close(); fos.close(); } public static void cypher(InputStream ips,OutputStream ops) throws Exception{ //这个加密程序既可以加密也可以解密 int b = 0; while((b=ips.read())!=-1){ ops.write(b^0xff); } } private String classDir; /** * loadClass(String name),给定一个字符串类名,它就会去找指定的类,并加载这个类,然会返回一个class, * loadClass()其实这个方法它的内部会去找它的父类,找完了父类,如果找不到父类就返回来调用findClass(String name),我们所要做的就是重写findClass,保留loadClass中的找父类的功能 * 这个就是模板方法设计模式(继承父类有相同的方法,实现父类抽象方法,实现父类的抽象方法内容不同) * 当我们得到了class文件之后如何将class文件装换成字节码?把class文件转换成字节码的方法definClass(),这个方法有多种形式,把一个字节数组变成一个class * * */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { //子类不能抛比父类更广泛的异常 // TODO Auto-generated method stub String classFileName = classDir + "\\"+name.substring(name.lastIndexOf('.')+1)+".class"; try { FileInputStream fis = new FileInputStream(classFileName); ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节数组输出流 cypher(fis,bos); fis.close(); byte[] bytes = bos.toByteArray(); return defineClass(bytes,0,bytes.length); //defineClass()方法将一个字节数组转变成一个字节码文件 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return super.findClass(name); //如果父类在它所在管辖目录找到这个指定类则用父类去加载 } public MyClassLoader(){ } public MyClassLoader(String classDir){ this.classDir = classDir; } }
代理
/** * //java基础加强_分析代理类的作用与原理及AOP概念 /* (1)代理的概念与作用 程序中的代理:已经写好了一个类,这个类中有一些方法,现在别人(客户端)调用这个类中的方法,而且想在这个类中添加更多的方法 比如要增加,异常处理,日志,计算方法的运行时间,事物管理等等(前提是这个类的源代码没有给你) 那么我们就可以编写一个与原来目标类具有相同功能的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上 前置后置的系统功能的代码,现在别人(客户端)就可以直接调用代理类了,代理类和目标类实现了相同的接口,也就是对外有相同 的方法。 (2)使用代理的好处 如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类还是代理类,这样就很容易切换 在比如我写好了一个软件可以通过修改配置文件来实现到底是调试模式还是运行模式 (3)AOP Aspect Oriented Progream面向方面的编程,面向方面的编程就是把交叉业务模块化 代理技术就是实现AOP功能的核心和关键技术 (4)我们系统中有很多个类,如果要为这些类增加代理,这个实在是太累了,JVM可以再运行期间可以动态生成类的字节码, 这中动态生成的类往往被用作代理类即动态代理类 (5)JVM生成的动态类必须实现一个或多个接口,如果我们有一个目标类没有实现接口 ,这时候JVM就不能创建代理了,但是CGLB可以 今天不是标准的,是开源的,明天很可能就是标准的CGLIB库,它可以动态生成一个类的子类,也就是如果你的目标类没有实现接口,可以用 CGLIB生成那种没有接口的目标类的子类,这样生成目标类的代理 (6)代理类除了调用目标相应方法外还可以干点额外的事情,干额外的事情可以目标方法之前也可以再目标方法之后, 也可以再处理异常的语句块当中,也就是说要添加的系统功能代码可以再调用目标方法之前,也可以在调用目标方法之后或者在处理异常的 catch块中 java基础加强_创建动态类及查看其方法类表信息 (1)演示如何用JAVA虚拟机提供的API的来生成动态类,生成动态类一定要给他一个接口 (2)java.lang.reflect中的proxy类中的getProxyClass(ClassLoader loader,Class<?>...interfaces)返回的是一个class字节码 我们都知道每一类都必须由一个类加载器,就好像每个人都有一个妈妈,动态类也是如此 但是现在又一种类不是一开始就有夹杂器,因为这个类是在java虚拟机内存中创建的,这个就是动态类 现在在内存中创建一个字节码就需要指定一个加载器和接口 getProxyClass(ClassLoader loader,Class<?>...interfaces)为它指定一个加载器ClassLoaer loader, 为它指定多个接口Class<?>...interfaces,在为代理指定接口类,并且指定加载器,通常加载器为接口的加载器 这样我们就得到一个代理class (3)面试题:StringBuilder和StringBuffer的区别 在线程下使用StringBuilder效率高些,在多线程下使用StringBuffer效率高些 StringBuilder不考虑线程安全问题的影响,所以它在单线程下效率比StringBuffer高 StringBuffer要考虑线程的安全问题的影响,如果它用在单线程下相比StringBuilder效率就低 但是它在过线程中就比StringBuider效率就高些 (4)得到结果代理clazzProxy1的构造方法只有一个$Proxy0(java.lang.reflect.InvocationHandler) 参数是一个InvocationHandler 得到代理clazzProxy1的方法有很多 java基础加强_创建动态类的实例对象及调用其方法 (1)创建动态类的实例对象,只能搞到那个有参数的构造方法,没有参数的那个不能搞 Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class); (2)用构造方法创建创建具体的实例,并创建InvacationHandler接口的子类,然后将InvacationHandler的的子类作为参数传递进去 Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1()); //new出来的是一个对象,需要强制转型 java基础加强_完成InvocationHandler对象的内部功能 (1)改写动态类的三种方法 第一种将InvacationHandler接口的子类传递给动态类的构造方法 第二种将InvacationHandler接口的匿名内部类传递给动态类的构造方法 第三种写法,一步到位的方法,,让JVM创建动态类及其实例对象 用java.lang.reflect中的proxy提供的方法newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 这个方法传入了类加载器,指定的接口数组,以及参数InvocationHandler接口的匿名内部类 (2)为动态类挂上目标类,以及在目标代码前后添加系统代码 ArrayList target = new ArrayList(); long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); java基础加强_分析InvocationHandler对象的运行原理 (1)分析动态代理的内部原理 一般一个构造方法接收一个参数是为记住这个参数,然会在运用它 这里的动态类$Proxy0接受一个InvocationHandler接口的匿名内部类,也是为记住它,然后运用它 $Proxy0 implements Collection { InvocationHandler handler; public $Proxy0(InvocationHandler handler) { this.handler = handler; } //生成的Collection接口中的方法的运行原理 int size() { return handler.invoke(this,this.getClass().getMethod("size"),null); } void clear(){ handler.invoke(this,this.getClass().getMethod("clear"),null); } boolean add(Object obj){ handler.invoke(this,this.getClass().getMethod("add"),obj); } } (2)InvocationHandler的实现类中的invoke传递的三个参数分别是什么 当客户端(Client)程序调用代理的一个方法时,涉及到三个要素, 调用代理对象,调用代理的那个方法,以及为这个方法传递哪些参数 ,当我们去调用代理的某个方法时,代理就会去执行InvocationHandler内部的invoke方法,invoke方法就去调用目标类的这个方法 然后返回目标对象的这个方法的结果,就实现了代理 (3)在代理内部可以缺斤少两,也就是所可以取修改代理里面返回的数据,可以写 (4)代理返回的class为什么是$Proxy0,代理的方法实际上调用的是目标类的方法,返回的类理应是实际类的class,但是为什么返回的确实代理的class呢 System.out.println(proxy3.getClass().getName()); 理由是代理只是将equals,hashCode,toString三个方法委托给了InvocationHandler,其它方法有自己的实现 所以在在获取代理的getClass().getName()返回的是代理的名字 java基础加强_总结分析动态代理类的设计原理和结构 (1)将代理内部的硬编码转变成对象,我们只需要在代理内部传递这个对象就可以实现多种动态代理 也就是说,我这个代理不是为某个目标而服务的,而是为很多目标服务的,那么我们所要做的就是 将目标类代码和系统功能代码抽取出来封装成类然后传递给InvocationHandler,这个就可以将代码 写成一个框架 硬编码 long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+"running time of"+(endTime-beginTime)); java基础加强_编写可生成代理和插入通告的通用方法 (1)将代理中的目标类代码和功能代码抽取出来封装成对象,然后将代理做成一个框架 */ package cn.itcast.day3; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; public class ProxyTest { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { // TODO Auto-generated method stub //给这个动态代理类指定需要哪些接口,指定关联的类加载器 Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); //在为代理指定接口,并且指定加载器,通常加载器为接口的加载器,然后就可以得到指定的代理的class //getProxy是一个静态方法,用Collection这个接口就是用它的字节码,加载器通常用接口相同的加载器 System.out.println(clazzProxy1.getName()); //打印这个类的名字,结果为$Proxy0 System.out.println("--------------beging constructors list-------------"); /* *$Proxy0() *$Proxy0(InvocationHandler,int) * */ Constructor[] constructors = clazzProxy1.getConstructors(); //查看这个类上有什么样的构造方法 for(Constructor constructor : constructors){ String name = constructor.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append('('); Class[] clazzParams = constructor.getParameterTypes(); //返回构造函数中的所有参数类型 for(Class clazzParam : clazzParams){ sBuilder.append(clazzParam.getName()).append(','); } if(clazzParams.length!=0 && clazzParams!=null) //判断长度不为0并且不等于null,null表示根本没有构造方法 sBuilder.deleteCharAt(sBuilder.length()-1); //当构造方法中的个数不为0并且不为null时去除掉StringBuilder中最后那个逗号,length为总长度,最大索引号为length()-1 sBuilder.append(')'); System.out.println(sBuilder.toString()); } System.out.println("--------------beging methods list-------------"); Method[] methods = clazzProxy1.getMethods(); for(Method method : methods){ String name = method.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append('('); Class[] clazzParams = method.getParameterTypes(); //返回函数中的参数类型 for(Class clazzParam : clazzParams){ sBuilder.append(clazzParam.getName()).append(','); } if(clazzParams.length!=0 && clazzParams!=null) //怎么可以为null呢? sBuilder.deleteCharAt(sBuilder.length()-1); //当构造方法中的个数不为0并且不为null时去除掉StringBuilder中最后那个逗号 sBuilder.append(')'); System.out.println(sBuilder.toString()); } System.out.println("--------------beging creat instance list-------------"); //clazzProxy1.newInstance(); 这个方法不行,应为它的构造方法有一个参数InvocationHandler Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class); //获取构造方法 //第一种写法,先获取到构造方法,并创建一个构造的参数,然会创建构造方法的实例对象 class MyInvocationHandler1 implements InvocationHandler{ //InvocationHandler是一个接口,下面new的构造方法需要穿入它的实例化,这里我出创建一个它的是实例 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub return null; } } //用接口的类型作为应用,因为动态代理类的接口就是Collection Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1()); //获取到构造方法之后new一个实例对象,传入InvocationHandler,这个参数是一个接口,只有传入它的实现类 System.out.println(proxy1); //得到的结果为null System.out.println(proxy1.toString()); //原因是它的toString()方法返回的是null,而非对象是null,如果对象为null,调用这个方法会出现空指针异常 proxy1.clear(); //没有返回值的方法正确,清除集合,它是没有返回值的,clear返回的是void,代理内部返回的是null,null就相当于void //proxy1.size(); //有返回值的方法却错误,返回集合长度,它返回集合长度,但是你代理内部返回的确实null,要将null转换成数字就会出错 //第二种写法,获取到构造方法,然后创建构造方法的实例对象,并传入参数的匿名内部类 Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub return null; } }); //第三种写法,一步到位的方法,java.lang.reflect中的proxy提供的方法,让JVM创建动态类及其实例对象 //newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) //这个方法传入了类加载器,指定的接口数组,以及参数InvocationHandler Collection proxy3 = (Collection)Proxy.newProxyInstance( Collection.class.getClassLoader(), new Class[]{Collection.class}, new InvocationHandler(){ ArrayList target = new ArrayList(); //这里的ArrayList是一成员变量 public Object invoke(Object proxy, Method method, Object[] args) //invoke中的三个参数分别代表调用那个代理对象,调用代理的那个方法,以及为这个方法传递哪些参数 throws Throwable { // TODO Auto-generated method stub //ArrayList target = new ArrayList(); //如果我把目标放在这个里,则是一个局部变量,每个方法访问invoke都会创建一个全新的ArrayList long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+"running time of"+(endTime-beginTime)); return retVal; //return method.invoke(proxy,args); //这个会出现死循环,我们调用了代理之后在代理invoke方法内 //又去调用代理的这个方法,这样就出现了死循环 //Object retVal = method.invoke(target, args);如果在invoke内部去调用目标类的这个方法,然后返回结果就实现了代理 } }); proxy3.add("zxx"); //当代理调用它的一个方法时,代理会将这个方法,以及参数,以及代理名传递给内部的 //参数InvocationHandler的方法invoke,invoke方法内部会去调用目标对象的这个方法, //并将参数传递给方法,最后将返回值返回给代理,代理在返回给调用者,这样就实现了代理 proxy3.add("lhm"); proxy3.add("bxd"); System.out.println(proxy3.size()); System.out.println(proxy3.getClass().getName()); //将目标对象抽取出来 final ArrayList target = new ArrayList(); //匿名内部类要访问局部变量就要加final,匿名内部类中的Object retVal = method.invoke(target, args)访问到了它 Collection proxy4 =(Collection)getProxy(target,new MyAdvice()); proxy4.add("zxx"); proxy4.add("lhm"); proxy4.add("bxd"); System.out.println(proxy4.size()); System.out.println(proxy4.getClass().getName()); } //下面就是一个小型sprint的框架,就可以封装成一个黑盒了,就可以卖给别人了,以后玩sprint就写MyAdivce private static Object getProxy(final Object target,final Advice advice) { Object proxy4 = Proxy.newProxyInstance( /*Collection.class.getClassLoader(),*/ //加载器要改为目标对象的加载器 target.getClass().getClassLoader(), /*new Class[]{Collection.class},*/ //接口要改为目标对象的接口 target.getClass().getInterfaces(), new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) //invoke中的三个参数分别代表调用那个代理对象,调用代理的那个方法,以及为这个方法传递哪些参数 throws Throwable { // TODO Auto-generated method stub //ArrayList target = new ArrayList(); //如果我把目标放在这个里,则是一个局部变量,每个方法访问invoke都会创建一个全新的ArrayList /*long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+"running time of"+(endTime-beginTime)); return retVal;*/ advice.beforeMehod(method); Object retVal = method.invoke(target, args); advice.afterMethod(method); return retVal; } }); return proxy4; } }
package cn.itcast.day3; import java.lang.reflect.Method; public class MyAdvice implements Advice { long beginTime =0; @Override public void afterMethod(Method method) { // TODO Auto-generated method stub System.out.println("到传智播客来找对象啦!"); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+"running time of"+(endTime-beginTime)); } @Override public void beforeMehod(Method method) { // TODO Auto-generated method stub System.out.println("到传智播客来吃饭啦!"); beginTime = System.currentTimeMillis(); } }
//一般来说这个接口有四个方法,方法之前,方法之后,方法前后,方法异常 //而且好药传递三个参数,这里不写了到sprint里面可以学到 package cn.itcast.day3; import java.lang.reflect.Method; public interface Advice { void beforeMehod(Method method); void afterMethod(Method method); }
/** * java基础加强_实现类似spring的可以配置的AOP框架 (1)有一工厂BeanFactory,产生javaBean的工厂,它有一个方法叫getBean(),它接收一个参数,这个参数表示要 返回一个对应名字的javaBean对象,而这个参数的字符串就是一个配置文件里面配置的 (2)这个getBean()内部有两种写法,如果内部检查出传递的参数的Bean对象不是像ProxyFactoryBean这样的类 那么它就直接创建这个参数的实例对象,如果检测出这个参数的Bean对象是一个像ProxyFactoryBean这样的类 就会调用这个参数的Bean对象的方法创建代理 (3)如果getBean()内部检测出这个参数的Bean对象是一个像ProxyFactoryBean这样的类又如何去调用这个参数的 的Bean对象的方法去创建代理呢?第一个是要指定代理的目标,第二个要指定代理的Advice,配置文件就这样写 (4)那么我写这个框架就分两个部分,第一 个写BeanFactory,第二个写ProxyFacotryBean代理 第一不部分是专门创建Bean对象工厂,第二个写代理工厂 * */ package cn.itcast.day3.aopframework; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import cn.itcast.day3.Advice; public class BeanFactory { Properties props = new Properties(); public BeanFactory(InputStream ips){ //读取配置文件 try { props.load(ips); //做商业性项目80%就在做异常处理,所以以后做项目异常必须写完整 } catch (IOException e) { //因为正确的路线只有一条,错误的路线有千万条 // TODO Auto-generated catch block e.printStackTrace(); } } public Object getBean(String name){ String className = props.getProperty(name); //根据名字获取到类名 Object bean = null; try { Class clazz = Class.forName(className); //根据获取到的名字 创建字节码对象 bean = clazz.newInstance(); //创建一个对象,调用不带参数的构造方法,注意,对于javaBean来说必须要有一个不带参数的构造方法,因为在穿件javaBean的实例对象是需要用到不带参数的实例对象 } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } if(bean instanceof ProxyFactoryBean){ //有了这个对象以后来检测这个对象是否为ProxyFactoryBean的实例,在spring里面ProxyFactoryBean是一个接口,我们自己写就实现它,在这里就这样写这个类 Object proxy = null; ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean; try { //搞到代理的advice Advice advice = (Advice)Class.forName(props.getProperty(name + ".advice")).newInstance(); //配置文件是这样写 //搞到代理的target Object target = Class.forName(props.getProperty(name + ".target")).newInstance(); //将advice填充到ProxyFactoryBean proxyFactoryBean.setAdvice(advice); //将target填充到ProxyFactoryBean proxyFactoryBean.setTarget(target); proxy = proxyFactoryBean.getProxy(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } return proxy; } return bean; //否则就返回这个bean } }
package cn.itcast.day3.aopframework; import java.io.InputStream; import java.util.Collection; public class AopFrameworkTest { //测试AOP框的类 /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties"); //使用相对路径 Object bean = new BeanFactory(ips).getBean("xxx"); System.out.println(bean.getClass().getName()); //查看得到的bean是代理还是目标 //((Collection)bean).clear(); } }
package cn.itcast.day3.aopframework; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import cn.itcast.day3.Advice; public class ProxyFactoryBean { private Advice advice; private Object target; public Advice getAdvice() { return advice; } public void setAdvice(Advice advice) { this.advice = advice; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } public Object getProxy() { // TODO Auto=generated method stub Object proxy4 = Proxy.newProxyInstance( /*Collection.class.getClassLoader(),*/ //加载器要改为目标对象的加载器 target.getClass().getClassLoader(), /*new Class[]{Collection.class},*/ //接口要改为目标对象的接口 target.getClass().getInterfaces(), new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) //invoke中的三个参数分别代表调用那个代理对象,调用代理的那个方法,以及为这个方法传递哪些参数 throws Throwable { // TODO Auto-generated method stub //ArrayList target = new ArrayList(); //如果我把目标放在这个里,则是一个局部变量,每个方法访问invoke都会创建一个全新的ArrayList /*long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+"running time of"+(endTime-beginTime)); return retVal;*/ advice.beforeMehod(method); Object retVal = method.invoke(target, args); advice.afterMethod(method); return retVal; } }); return proxy4; } }


浙公网安备 33010602011771号