黑马程序员—Java基础加强(反射)
2014-04-16 16:17
453 查看
------- android培训、java培训、期待与您交流! ----------
反射:
反射的基石是Class类:java程序中的各个java类属于同一类事物,而描述这类事物的java类名就是Class。
比如说从多的人我们用Person类来表示,众多的动物我们用Animal类来表示,那么众多的java类也应该用一个类来表示,这个类就是Class。
Class的实例对应着各个java类在内存中的字节码,例如,Person类的字节码,Animal类的字节码。
字节码:一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的
记住:一个类在虚拟机中只有一份字节码。
那么如何获取Class对象呢?
有三种方式:
1、调用某个类的class属性获取Class对象,如Date.class会返回Date类对应的Class对象(其实就是得到一个类的一份字节码文件);Class
clazz = Date.class;
2、使用Class类的forName(StringclassName)静态方法,className表示全限定名;如String的全限定名:java.lang.String;
Class clazz = Class.forName("java.lang.String");
3、调用某个对象的getClass()方法。该方法属于Object类;
Class<?> clazz = new Date().getClass();
基本的 Java 类型(boolean、byte、char、short、int、long、float
和 double)和关键字 void通过class属性也表示为 Class
对象;
Class类中boolean isPrimitive() :判定指定的 Class
对象是否表示一个基本类型。
包装类和Void类的静态TYPE字段;
Integer.TYPE == int.class ;
Integer.class == int.class;
数组类型的Class实例对象:
Class<String[]> clz = String[].class;
数组的Class对象如何比较是否相等?
数组的维数和数组的类型;
Class类中boolean isArray() :判定此 Class 对象是否表示一个数组类型。
反射:
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念:
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
反射机制的优点:就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发。大大的增强了程序的扩展性。
反射机制的缺点:是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
反射的基本步骤:
1、获得Class对象,就是获取到指定的名称的字节码文件对象。
2、实例化对象,获得类的属性、方法或构造函数。
3、访问属性、调用方法、调用构造函数创建对象。
反射的用法:
1)、需要获得java类的各个组成部分,首先需要获得类的Class对象。
2)、反射类的成员方法:
Method类用于描述类中的方法:
Method getMethod(String name, Class<?> ...parameterTypes)
返回该Class对象表示类和其父类的指定的public方法;
示例:Class clazz = Person.class;
Methodmethod = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();
Method[] getMethods():
返回该Class对象表示类和其父类的所有public方法;
Method getDeclaredMethod(String name, Class<?>...parameterTypes)
返回该Class对象表示类的指定的方法。和访问权限无关,但不包括继承的方法;
Method[] getDeclaredMethods():
获得类所有的方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法
3)、反射类的构造函数:
Constructor类用于描述类中的构造方法:
Constructor<T> getConstructor(Class<?>...parameterTypes)
返回该Class对象表示类的指定的public构造方法;
例如:Constructor con = clazz.getConstructor(new Class[]{paramClazz1,paramClazz2,...})
con.newInstance(params...)
Constructor<?>[] getConstructors()
返回该Class对象表示类的所有public构造方法;
Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes)
返回该Class对象表示类的指定的构造方法,和访问权限无关;
Constructor<?>[] getDeclaredConstructors()
返回该Class对象表示类的所有构造方法,和访问权限无关;
4)、反射类的属性:
Fieldfield = clazz.getField(fieldName);
field.setAccessible(true);
field.setObject(value);
代码示例:
先创建一个需要被反射的类ReflectPoint:
然后对ReflectPoint类进行反射操作以及反射的一些常见的用法代码示例:
------- android培训、java培训、期待与您交流! ----------
反射:
反射的基石是Class类:java程序中的各个java类属于同一类事物,而描述这类事物的java类名就是Class。
比如说从多的人我们用Person类来表示,众多的动物我们用Animal类来表示,那么众多的java类也应该用一个类来表示,这个类就是Class。
Class的实例对应着各个java类在内存中的字节码,例如,Person类的字节码,Animal类的字节码。
字节码:一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的
记住:一个类在虚拟机中只有一份字节码。
那么如何获取Class对象呢?
有三种方式:
1、调用某个类的class属性获取Class对象,如Date.class会返回Date类对应的Class对象(其实就是得到一个类的一份字节码文件);Class
clazz = Date.class;
2、使用Class类的forName(StringclassName)静态方法,className表示全限定名;如String的全限定名:java.lang.String;
Class clazz = Class.forName("java.lang.String");
3、调用某个对象的getClass()方法。该方法属于Object类;
Class<?> clazz = new Date().getClass();
九个预定义Class对象
基本的 Java 类型(boolean、byte、char、short、int、long、float
和 double)和关键字 void通过class属性也表示为 Class
对象;
Class类中boolean isPrimitive() :判定指定的 Class
对象是否表示一个基本类型。
包装类和Void类的静态TYPE字段;
Integer.TYPE == int.class ;
Integer.class == int.class;
数组类型的Class实例对象:
Class<String[]> clz = String[].class;
数组的Class对象如何比较是否相等?
数组的维数和数组的类型;
Class类中boolean isArray() :判定此 Class 对象是否表示一个数组类型。
反射:
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念:
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
反射机制的优点:就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发。大大的增强了程序的扩展性。
反射机制的缺点:是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
反射的基本步骤:
1、获得Class对象,就是获取到指定的名称的字节码文件对象。
2、实例化对象,获得类的属性、方法或构造函数。
3、访问属性、调用方法、调用构造函数创建对象。
反射的用法:
1)、需要获得java类的各个组成部分,首先需要获得类的Class对象。
2)、反射类的成员方法:
Method类用于描述类中的方法:
Method getMethod(String name, Class<?> ...parameterTypes)
返回该Class对象表示类和其父类的指定的public方法;
示例:Class clazz = Person.class;
Methodmethod = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();
Method[] getMethods():
返回该Class对象表示类和其父类的所有public方法;
Method getDeclaredMethod(String name, Class<?>...parameterTypes)
返回该Class对象表示类的指定的方法。和访问权限无关,但不包括继承的方法;
Method[] getDeclaredMethods():
获得类所有的方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法
3)、反射类的构造函数:
Constructor类用于描述类中的构造方法:
Constructor<T> getConstructor(Class<?>...parameterTypes)
返回该Class对象表示类的指定的public构造方法;
例如:Constructor con = clazz.getConstructor(new Class[]{paramClazz1,paramClazz2,...})
con.newInstance(params...)
Constructor<?>[] getConstructors()
返回该Class对象表示类的所有public构造方法;
Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes)
返回该Class对象表示类的指定的构造方法,和访问权限无关;
Constructor<?>[] getDeclaredConstructors()
返回该Class对象表示类的所有构造方法,和访问权限无关;
4)、反射类的属性:
Fieldfield = clazz.getField(fieldName);
field.setAccessible(true);
field.setObject(value);
代码示例:
先创建一个需要被反射的类ReflectPoint:
//导入Date类 import java.util.Date; //需要被反射的类ReflectPoint public class ReflectPoint { //私有的成员Dete类的对象birthday private Date birthday = new Date(); //私有成员属性x private int x; //公有的成员属性y public int y; //一系列公有的成员属性 public String str1 = "bass"; public String str2 = "basketball"; public String str3 = "itheima"; //构造函数 public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } //getX方法,获取x属性的值 public int getX() { return x; } //setX方法,设置x属性的值 public void setX(int x) { this.x = x; } //getY方法,设置y属性的值 public int getY() { return y; } //setY,设置y属性的值 public void setY(int y) { this.y = y; } //getBirthday方法,获取birthday对象 public Date getBirthday() { return birthday; } //setBirthday方法,设置birthday public void setBirthday(Date birthday) { this.birthday = birthday; } //重写toString方法 public String toString() { return "ReflectPoint [x=" + x + ", y=" + y + ", str1=" + str1 + ", str2=" + str2 + ", str3=" + str3 + "]"; } //重写hashCode方法 public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } //重写equals方法 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; } }
然后对ReflectPoint类进行反射操作以及反射的一些常见的用法代码示例:
import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Arrays; public class ReflectTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException { //创建ReflectPoint类的一个对象,向构造函数中传入参数 ReflectPoint rep1 = new ReflectPoint(3, 5); //获取ReflectPoint类字节码的 y 字段属性的对象 Field fieldY = rep1.getClass().getField("y"); //fieldY的值是多少呢?是5吗?错!他只是ReflectPoint类字节码的 y 这个属性的对象,而不是具体的某个ReflectPoint的对象的y属性 //fieldY不是对象身上的变量,而是类上的 y 字段属性。要用它来获取具体对象的 y 字段的值。 //获取rep1这个具体对象的 y字段的值 System.out.println(fieldY.get(rep1)); //获取ReflectPoint类字节码的 x 字段属性的对象,因为x是private修饰的变量, //所以要用到getDeclaredField方法,该方法可以获取到类中定义的private修饰的成员。 Field fieldX = rep1.getClass().getDeclaredField("x"); //设置ReflectPoint类中的 x 属性是可以被访问的 fieldX.setAccessible(true); //打印具体的ReflectPoint的对象rep1的具体 x 字段的值, //fieldX只是ReflectPoint类字节码的 x 属性对象,通过get方法传入具体的ReflectPoint类的对象来获取具体对象的 x 字段的值 System.out.println(fieldX.get(rep1)); String str1 = "abc"; //获取String的字节码的三种方法 Class cls1 = str1.getClass(); Class cls2 = String.class; Class cls3 = Class.forName("java.lang.String"); System.out.println(cls1 == cls2);//true System.out.println(cls1 == cls3);//true System.out.println(cls1.isPrimitive());//查看String是否是基本类型 false System.out.println(int.class.isPrimitive());//查看int是否是基本类型 true System.out.println(int.class == Integer.class);//查看int和Integer字节码是否相同 false System.out.println(int.class == Integer.TYPE);//true System.out.println(int[].class.isPrimitive());//int[]数组为引用类型所以为false System.out.println(int[].class.isArray());//判断int[]数组是否为Array数组 //获取String类的字节码对象,通过字节码对象调用getConstructor获取String带StringBuilder的构造函数对象 Constructor con1 = String.class.getConstructor(StringBuilder.class); //new String(new StringBuilder("abc")) //获取String类的字节码对象,通过字节码对象调用getConstructor获取String带StringBuilder的构造函数对象 Constructor con2 = String.class.getConstructor(StringBuilder.class); //通过构造函数对象调用newInstance方法,传入StringBuilder对象。实例化出一个带StringBuilder对象参数的String对象。 String str2 = (String) con2.newInstance(new StringBuilder("abc")); //打印输出str2对象中2角标的元素 System.out.println(str2.charAt(2)); //Class类也有newInstance()方法,该方法只能使用类的空参数构造函数获取一个类的对象。 String str3 = String.class.newInstance(); //调用替换字符串的反射功能 changeStringValue(rep1); //打印替换后的结果 System.out.println(rep1); //str1.charAt(1); //用反射的方式得到String的字节码里面的方法 Method methodCharAt = String.class.getMethod("charAt", int.class); //再用这个方法来作用于指定的对象,并传入指定的参数 char ch = (char) methodCharAt.invoke(str1, 2);//如果是invoke(null, 2)那么调用的是静态方法,因为静态方法不需要具体的对象 //打印该方法作用于指定对象和参数的返回值 System.out.println(ch); //JDK1.4语法,new Object[]{2}这是一个数组,数组里面装了一个int型的值,并且值为2,那么数组的length为1。可以理解为 new int[]{2} //其实就是这个参数就是一个int型的 2 char ch2 = (char) methodCharAt.invoke(str1, new Object[]{2}); //打印该方法作用于指定对象和参数的返回值 System.out.println(ch2); //mainTest.main(new String[]{"itcast", "itheima", "ZOL"}); //在执行本类的main方法时,运行 java ReflectTest 传入一个参数,该参数是要被执行的其他的类的字符串表现形式 String startingClassName = args[0]; //获取那个需要运行的其他类的字节码,并通过getMethod方法获取那个类的main方法 Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class); //执行需要运行那个类的main方法,mian方法是静态的,所以不需要对象,故对象参数的位置为null。后面传入一个字符串参数给main方法 //用new Object[]{} 数组包住String数组是因为main函数会对传入的String数组进行拆包,被它拆包之后就成为了3个参数而不是一个数组 //而用new Object[]{} 会给String数组再加一层包装,是main函数在拆包后发现,传入的的确是一个数组 // mainMethod.invoke(null, new Object[]{new String[]{"itcast", "itheima", "ZOL"}}); //这是告诉main函数,就把传入的参数当成是Object,让它不要拆包 mainMethod.invoke(null, (Object)new String[]{"itcast", "itheima", "ZOL"}); int[] i1 = new int[]{3, 5, 6};//int型数组 int[] i2 = new int[5];//int型数组 int[][] i3 = new int[2][3];//int型二维数组 String[] s1 = new String[]{"itcast", "itheima", "ZOL"};//String类型数组 System.out.println(i1.getClass() == i2.getClass());//比较i1和i2字节码,true // System.out.println(i1.getClass() == i3.getClass());//比较i1和i3字节码,由于维数不同,所以不相同 // System.out.println(i1.getClass() == s1.getClass());//比较i1和s1字节码,由于不是同一类型数组,所以不相同 System.out.println(i1.getClass().getName());//获取i1所在类的字节码,通过字节码获取类名称并打印 System.out.println(i1.getClass().getSuperclass().getName());//获取i1所在类的字节码,再通过该字节码获取其父类的字节码,再获取父类名称并打印 System.out.println(i3.getClass().getSuperclass().getName());//获取i3所在类的字节码,再通过该字节码获取其父类的字节码,再获取父类名称并打印 System.out.println(s1.getClass().getSuperclass().getName());//获取s1所在类的字节码,再通过该字节码获取其父类的字节码,再获取父类名称并打印 Object obji1 = i1;//i1是引用型,是Object类型,所以可以转换 Object obji3 = i3;//i3也是引用型,是Object类型,所以可以转换 Object objs1 = s1;//s1也是引用类型,是Object类型,所以可以转换 // Object[] obj1 = i1;//i1是引用型,但里面是int型,转换成Object数组,里面却不是Object类型,所以不可以转换 Object[] obj2 = i3;//i3是二维数组,转换成Object数组后,里面还是一维数组,还是Object,所以可以转换 Object[] obj3 = s1;//s1是数组,转换成Object数组后,里面是String类型,String类型还是Object类型,所以可以转换 //这里不能转换成功,public static <T> List<T> asList(Object[] a),这个(Object[] a)表示某个引用类型的数组 //int数组不能转换成Object数组,所以会走这个asList(T... a),那么传入的int数组就变成一个参数了,也就不能转换里面的int整数了 System.out.println(Arrays.asList(i1)); //这里能够转换成功,public static <T> List<T> asList(Object[] a),这个(Object[] a)表示某个引用类型的数组 //如果是String类型数组的话,就可以转换成Object数组,因为String数组里面还是String,属于引用型。就可以转换数组里面的元素了,那么就可以转换成功 System.out.println(Arrays.asList(s1)); //调用打印对象功能,int型数组i1 printObject(i1); //调用打印对象功能,字符串 printObject("itheima"); } //打印对象功能 private static void printObject(Object obj) { //获取obj对象的字节码对象 Class clas = obj.getClass(); //判断obj是否为数组 if (clas.isArray()){ //如果obj是数组就获取数组的长度 int len = Array.getLength(obj); //遍历数组 for (int i = 0; i < len; i++){ //打印数组 System.out.println(Array.get(obj, i)); } }else{ //如果不是数组就直接打印 System.out.println(obj); } } //替换字符串的反射功能,需要调用者传入一个对象 private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException { // TODO Auto-generated method stub //获取obj对象所在的类的字节码对象,字节码对象又获取了该类中定义的字段,并存入了字段数组 Field[] fields = obj.getClass().getFields(); //遍历该类中所有的字段 for (Field field : fields){ // if (field.getType().equals(String.class)){ //比较字节码应该用 == 号比较,因为字节码只有一份,而又是同一份字节码。虽然用equals也可以比较,但是语义不准确。 //如果字段类型是String的话,执行if里面的代码 if (field.getType() == String.class){ //获取该类的obj对象的该字段的具体值,用oldValue记录 String oldValue = (String) field.get(obj); //把oldValue中的具体值字符串中的 b 都替换成 r,并用新的变量newValue来记录 String newValue = oldValue.replace('b', 'r'); //设置该obj对象的该字段的被替换后的值 field.set(obj, newValue); } } } } //mainTest类 class mainTest { public static void main(String[] args){ //遍历调用main方法时传入的String数组 for (String arg : args){ //打印String数组中的每个元素 System.out.println(arg); } } }
------- android培训、java培训、期待与您交流! ----------
相关文章推荐
- 黑马程序员--JAVA基础加强之反射技术
- 黑马程序员--Java基础加强--10.【PropertyDescriptor操作JavaBean VS 反射操作Java类】【个人总结】
- 黑马程序员_JAVA基础加强——反射
- 黑马程序员--Java基础加强--07.【反射创建对象 操作字段 调用方法的异同】【个人总结】
- 黑马程序员 20 Java基础加强-02-反射篇
- 黑马程序员-JAVA基础加强之反射
- 黑马程序员---Java基础加强---反射
- 黑马程序员-JAVA基础加强-反射2
- 黑马程序员_张孝祥_Java基础加强_反射
- 黑马程序员_Java基础加强反射
- 黑马程序员 —— Java基础加强 - 反射
- 【黑马程序员】Java基础加强15:反射Reflect
- 黑马程序员 Java基础加强 反射
- 黑马程序员-java基础加强-反射的深入讲解
- 黑马程序员Java基础加强成员方法的反射
- 黑马程序员------java基础加强中反射
- 【黑马程序员】java基础加强_反射
- 黑马程序员---java基础加强_成员变量的反射
- 黑马程序员_java基础加强_静态导入_反射_枚举_注解_内省_泛型_代理
- 『黑马程序员』---java--基础加强--反射+内省