java技术_反射
来源:互联网 发布:红米note清空用户数据 编辑:程序博客网 时间:2024/06/13 23:19
一,反射的基石(Class类)
1.1Class概述
1,java程序中的各个java类属于同一类事物,java提供了一个类来用于描述这类事物,这个类就是Class。
2,Class类代表java类,它的各个实例对象又分别对应什么呢?
答:对应各个类在内存中的字节码
3,一个类被加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,
所以他们再内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象具有相同的类型,就是Class类型。
1.2获得Class对象
1,使用Class类的forName(String className)静态方法。传入的字符串参数值是某个类的权限定类名(必须添加完整包名)
(该方法可能抛出ClassNotFoundException异常)
2,调用某个类的class属性来获取该类对应的Class对象。
3,调用某个对象的getClass()方法,该方法是java.lang.Object类中的一个方法,所有的对象都可以调用这个方法。
会返回该对象所属类对应的Class对象。
String str1 = "abc";//三种得到字节码的方式Class cls1 = str1.getClass();Class cls2 = String.class;Class cls3 = Class.forName("java.lang.String");//抛出异常。
以上三种得到的String字节码都是同一份。
System.out.println(cls1 == cls2);//trueSystem.out.println(cls1 == cls3);//true
关于字节码的其他相关判断。
System.out.println(cls1.isPrimitive());// 判定指定的 <code>Class</code> 对象是否表示一个基本类型。、false、System.out.println(int.class.isPrimitive());//trueSystem.out.println(int.class == Integer.class);//falseSystem.out.println(int.class == Integer.TYPE);//trueSystem.out.println(int[].class.isPrimitive());//falseSystem.out.println(int[].class.isArray());//判断是否是数组
三种方式的区别:第三种在源程序时不用知道要获得的字节码的类名称,而是临时传进来的。
对反射的一个总结:反射就是把java类的各种成分映射成相应的java类。
1.3Class的获取信息的常用方法
1.3.1获取Class对应类所包含的构造器
Constructor<T> getConstructor(Class<?>...parameterTypes):
Constructor<?>[] getConstructors():
Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes); 与访问权限无关
Constructor<?>[] getDeclaredConstructos();
1.3.2 获取Class对应类所包含的方法
Method getMethod(String name,Class<?>...parameterTypes);Method[] getMethods();Method getDeclaredeMethod(String name,Class<?>...parameterTypes); 与访问权限无关Method[] getDeclaredeMethods();
1.3.3访问Class对应类所包含的Field
Field getField(String name);Field[] getFields();Field getDeclaredeField(String name); 与访问权限无关Fidle getDeclaredeFields();
1.3.4获取方法和构造函数的注意要点
String.class.getMethod("String",Stirng.class);
如果需要获取第三个indexOf方法,则使用如下代码:
//前一个参数指定方法名,后面是个数可变的Class参数指定形参类型列表String.class.getMethod("String",Stirng.class,int.class);
而获取构造器时无须传入构造器名,因为同一个类的所有构造器的名字都是相同的,所以要确定一个构造器只要指定形参列表即可,
String(StringBuffer buffer)这个构造函数,则使用如下代码:
Class.forName("java.lang.String").getConstructor(StringBuffer.class);
二,创建对象(Constructor类)
2.1第一种创建方式
2.2第二种创建方式
(1) 获取该类的Class对象(2) 利用Class对象的getConstructor()方法来获取指定的构造器(3) 调用Constructor的newInstance()方法来创建java对象。
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);String str2 = (String)constructor1.newInstance(new StringBuffer("abc"));//编译时不知道constructor是String的构造方法,返回的是Object所以上面要强转
通常没有必要使用反射来创建该对象,毕竟通过反射创建对象时性能要稍低一些,实际上,只有当程序需要动态创建某个类的对象时才会考虑使用反射,
三,调用方法(Method类:(代表类在内存的字节码的一个成员方法))
1,得到类中的某一个方法
例子:Method methodCharAt = String.class.getMethod("charAt",int.class);
获取String类的charAt(int index);
2,方法的调用
例子:char s = methodCharAt.invoke(str1,1);
等价char s = str1.charAt(1)
注:如果传递给Method对象的invoke()方法的第一个参数为null,说明该method对象对应的是一个静态方法。
例:用反射方法执行某个类的main方法。
class TestArguments{public static void main(String[] args){for(String arg : args){System.out.println(arg);}}}public class ReflectTest {//普通方式调用main方法,//TestArguments.main(new String[]{"111","222","333"});//为什么要用反射的方式调用mian,不知道要调用那个类的main,等外界通过传递参数,告诉我要调用哪个类。String startingClassNmae = args[0];Method mainMethod = Class.forName(startingClassNmae).getMethod("main",String[].class );//mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});mainMethod.invoke(null,(Object)new String[]{"111","222","333"});}
四,访问属性值(Field类:(代表字节码的某个变量))
1,Field类代表某个类中的一个成员变量
public class ReflectPoint {private int x;public int y;public ReflectPoint(int x, int y) {super();this.x = x;this.y = y;}}
ReflectPoint pt1 = new ReflectPoint(3,5);//getField()只能得到publlic修饰的变量Field fieldY = pt1.getClass().getField("y");//对应字节码的变量。没有对应到对象身上。System.out.println("fieldY"+fieldY);//field的值是public int cn.itcast.day1.ReflectPoint.y // fieldY的值是多少?是5 错!fieldY不是对象的变量,而是类上的变量。要用它取某个对象上的Y的值System.out.println(fieldY.get(pt1));//private修饰的变量可以使用getDeclareField();,public也可以Field fieldX = pt1.getClass().getDeclaredField("x");fieldX.setAccessible(true);//暴力反射,给你看到钱,就不让你用,那就只能抢。System.out.println(fieldX.get(pt1));
练习(Field):将任意一个对象的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”;
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);String newValue = oldValue.replace('b','a'); //(old,new)field.set(obj, newValue);}}}
五,数组的反射。
1,具有相同维数和元素类型的数组属于同一个类型,即具有相同的class实例对象。
class ArrayReflect{public static void main(String[] args) { int [] a1 = new int []{1,2,3}; int [] a2 = new int [4]; int [][] a3 = new int [2] [3]; String [] a4 = new String []{"a","b","c"};//具有相同维数和元素类型的数组属于同一个类型,即具有相同的class实例对象。 System.out.println(a1.getClass() == a2.getClass());//true System.out.println(a1.getClass() == a3.getClass());//false System.out.println(a1.getClass() == a4.getClass());//false System.out.println(a1.getClass().getName());//[I}}
2,代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class
//代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的ClassSystem.out.println(a1.getClass().getSuperclass().getName());//java.lang.ObjectSystem.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object
3,基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用,非基本类型的一维数组,
既可以当做Object类型使用,又可以当做Objcet[]类型使用。
Object aObj1 = a1;Object aObj2 = a4;//Object[] aObj3 = a1;//基本类型不是Object,int不属于Object。 Object[] 有一个数组,里面装的是ObjectObject[] aObj4 = a3;Object[] aObj5 = a4;
4,Array.asList()方法处理int[]和String[]时的差异。
//JDK1.4 asList(Object[] a) 当成一个Object[]处理
//JDK1.5 asList(T... a) 当成一个Object处理
System.out.println(a1);//[I@170bea5System.out.println(a4);//[Ljava.lang.String;@f47396System.out.println(Arrays.asList(a1));//[[I@170bea5] 为什么?jdk1.4和jdk1.5的接收参数的区别System.out.println(Arrays.asList(a4));//[a, b, c]
5,对接收数字参数的成员方法进行反射
例:在一个类里调用另外一个类的main()方法
public class Reflect{public static void mian(String[] srgs){//普方式调用 TestArguments.main(new String[]{"111","222","333"}); /*用反射的方式调用*/ //在运行该类时,传入一个参数作为被调用的类名,用一个变量接收被调用类名 String startingClassNmae = args[0]; //获取被调用类的main()方法 Method mainMethod = Class.forName(startingClassNmae).getMethod("main",String[].class ); //1,/下面传入的方式不对,JDK1.5兼容JDK1.4的处理方式 //因为String数组属于Object数组,按照jdk1.4的处理方式,所以相当于传了3个参数进去,所以会出现参数个数不对的错误。 //mainMethod.invoke(null,new Stirng[]{"111","222","333"}); //2,以下就是创建一个Object数组,将要传的参数作为Object数组的第一个元素,这样就想相当于传进去一个参数 //mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}}); mainMethod.invoke(null,(Object)new String[]{"111","222","333"});}}class TestArguments{public static void main(String[] args){for(String arg : args){System.out.println(arg);}}}
6,Array工具类用于完成对数组的反射操作。(反射)
实例:打印一个Object对象的内容
private static void printObject(Object obj) {Class clazz = obj.getClass();if(clazz.isArray()){int len = Array.getLength(obj);for(int i=0;i<len;i++){System.out.println(Array.get(obj, i));}}else{System.out.println(obj);}}
六,反射的作用(实现框架的功能)
1,对框架的认识
如房地产商造房子用户住,门窗和空调等等内部都是由用户自己安装,房地产商造毛呸房就是框架,用户需使用此框架,安好门窗等放入到房地产商提供的毛呸房(框架)。框架和工具类的区别:工具类被用户类调用,而框架是调用用户提供的类。
2,框架要解决的核心问题
因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象,而要用反射方式来做
3,实现一个简单框架步骤:
(1)创建一个配置文件config.properties,然后写入配置信息。如键值对:className=java.util.ArrayList。
(2)代码实现,加载此文件:
1将文件读取到读取流中,要写出配置文件的绝对路径。
2用Properties类的load()方法将流中的数据存入集合。
3关闭流:关闭的是读取流,因为流中的数据已经加载进内存。
(3)通过getProperty()方法获取className,即配置的值,也就是某个类名。
(4)用反射的方式,创建对象newInstance()。
(5)执行程序主体功能
实例:
public class ReflectTest2 {public static void main(String[] args) throws Exception {InputStream ips = new FileInputStream("config.properties");Properties props = new Properties();props.load(ips);ips.close();String className = props.getProperty("className");//通过上面获取的className类名,创建一个ArrayList对象Collection collections = (Collection)Class.forName(className).newInstance();ReflectPoint pt1 = new ReflectPoint(3,3);ReflectPoint pt2 = new ReflectPoint(5,5);ReflectPoint pt3 = new ReflectPoint(3,3);collections.add(pt1);collections.add(pt2);collections.add(pt3);collections.add(pt1);System.out.println(collections.size());//4}}
4,类加载器加载资源文件
4.1,类加载器:
当一个类被使用的时候,会被类加载器加载到内存中,当然,它也可以加载普通文件。
4.2,eclipse编译,加载功能
在eclipse中保存一个.java文件后,eclipse会自动将该文件编译成.class文件,并把它放到classpath指定的目录中,
当然,eclipse也会把源程序目录下的一个非.java文件编译成.class文件,也把它放到classpath指定的目录中。
所以当classPath指定的目录下需要某个文件,那可以将该文件放在源程序目录下,eclipse会帮你复制过去。
4.3,使用类加载器加载配置文件
4.3.1使用类加载器来加载配置文件,需要先通过getClassLoader()获得类加载器,然后使用getResourceAsStream(),获得与配置文件相关的输入流。
利用类加载器来加载配置文件,需把配置文件放置的包名一起写上。这种方式只有读取功能!
InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
4.3.2使用类提供的简便方法加载的时候,配置文件路径可以相对也可以是绝对。
InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/config.properties");
实例:
public class ReflectTest2 {public static void main(String[] args) throws Exception {//InputStream ips = new FileInputStream("config.properties");//类加载器,没有OutputStream//一定要记住,用完整的路径,但完整的路径不是硬编码,而是运算出来的//在classpath根目录下面去逐一找这个文件,cn/itcast/day1 在cn前面不要加斜杠//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");//InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/config.properties");Properties props = new Properties();props.load(ips);ips.close();String className = props.getProperty("className");Collection collections = (Collection)Class.forName(className).newInstance();//Collection collections = new HashSet();ReflectPoint pt1 = new ReflectPoint(3,3);ReflectPoint pt2 = new ReflectPoint(5,5);ReflectPoint pt3 = new ReflectPoint(3,3);collections.add(pt1);collections.add(pt2);collections.add(pt3);collections.add(pt1);System.out.println(collections.size());}}
- java技术_反射
- java高级技术学习_反射
- 技术总结_反射
- 黑马程序员_高薪技术一(java反射)
- 黑马程序员_反射技术
- 黑马程序员_反射技术
- 黑马程序员_反射技术
- Java_反射技术_简述
- Java基础知识_反射
- Java入门_反射
- java学习_反射
- Java笔记 _反射
- JAVA反射_代理
- JAVA反射_注解
- java反射技术
- java反射技术(2)
- Java的反射技术
- Java中反射技术
- Redis入门-技术数据类型常用操作
- 实战 SSH 端口转发
- 使用异步任务下载图片并显示在UI上
- 【iOS开发】图像处理之获取RGB
- 西数各色硬盘用途差异
- java技术_反射
- Python之八皇后问题
- Maven学习:Eclipse使用maven构建web项目
- 杨辉三角形
- java使用不同厂商驱动进行数据库连接(配置文件)
- vagrant up error: Valid states are 'starting, running'. The machine is in the 'poweroff' state 解决
- 逆序对
- LoadRunner 技巧之THML 与 URL两种录制模式分析
- 虚函数实现多态---C++