java基础增强---反射
来源:互联网 发布:沈阳培训 盘古网络 编辑:程序博客网 时间:2024/05/08 08:32
——- android培训、java培训、期待与您交流! ———-
一、反射的基石–Class类
1、Java程序中的各个java类属于同一事物,描述这些类事物的java类名就是Class。
2、对比提问:从多的人用一个什么类表示?从多的java类用一个什么类表示?
人类—Person
java类–Class
注意这里Class是大写的,不是关键字class。
3、对比提问:
1)Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,
2)Class类代表java类,它的各个实现对象又分别对应什么呢?
对应各个类在内存中的字节码,例如Person类的字节码,ArrayList类的字节码,等待。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,
不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?
4、得到字节码对象的三种方法
1)类名.class,例如System.class
2)对象.getClass(),例如new Date().getClass()
3)Class.forName(“完整类名”),例如 Class.forName(“java.util.Data”);
反射时主要用第三种。它是静态方法。
面试题:Class.forName()的的作用是什么?
获取一个类的字节码对象,如果该类的字节码已经在内存中存在,就可以直接调用,
如果还没有存在,就调用类加载器进行加载,然后获取该类的字节码对象。
5、九个预定义的Class对象:
参看 Class.isPrimitive方法的帮助文档
八个基本类型和void,分别是:boolean、byte、char、short、int、long、float、double和void。
int.class == Integer.TYPE
6、数组类型的Class实例对象用的方法是:
Class.isArray()
7、总之,只要是在源程序中出现的类型,都有各自的Class实例对象,
例如int[],void。
例:获取String类的字节码的三种方法
class Demo1{ public static void main(String[] args) throws Exception{ String str1 = "abc"; 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()); //false System.out.println(int.class.isPrimitive()); //true System.out.println(int.class == Integer.class); //false System.out.println(int.class == Integer.TYPE); //true } }
二、反射的概念
反射,就是把java类中的各种成分映射成相应的java类。
也可以理解成反射就是程序自己能够检测自身信息,就像人会通过镜子来查看自己的身体。
例如,一个java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也是用一个个java类来表示。
就像骑车是一个类,骑车中的发动机,变速箱等等也是一个个类,表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,他们是Field、Method、Contructor、Package等等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象胡有什么用?怎么使用?这这是学习和应用反射的要点
例:
class Demo2{ public static void sop(Object obj){System.out.println(obj);} public static void main(String[] args) throws Exception { String s1 = "1234"; Class c1 = s1.getClass(); Class c2 = String.class; Class c3 = Class.forName("java.lang.String"); sop(c1==c2); //c1与c2是否是同一个对象true sop(c1==c3); //c1与c3是否是同一个对象true sop(String.class.isPrimitive());//String是否是基本类型false sop(int.class.isPrimitive()); //int是否是基本类型true sop(int.class==Integer.class); //int与Integer的字节码是否是同一个对象false sop(int.class==Integer.TYPE); //int与Integer.TYPE的字节码是否是同一个对象true sop(int[].class.isPrimitive()); //int[]是否是基本类型false sop(int[].class.isArray()); //int[]是否是数组类型true }}
三、构造方法的反射
Constructor 类
1、Constructor类代表某个类中的一个构造方法。
2、得到某个类所有的构造方法:
例子:
Constructor constructor[] =
Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
3、创建实例对象:
通常方式:String str = new String(new StringBuffer(“abc”));
反射方式:String str = (String)constructor.newInstance(new StringBuffer(“abc”));
调用获得的方法时要用到上面相同类型的实例对象。
4、Class.newInstance()方法:
例子:String obj = (String)Class.forName(“java.lang.String”).newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎么样写的呢?用到了缓冲机制来保存默认构造方法的实例对象。
一个类有多个构造方法,用什么方式可以区分清楚想要得到其中的哪个方法呢?根据参数的个数和类型,
例如,Class.getMethod(name.Class…args)中的args参数就代表索要获取的那个方法的各个参数的类型的列表。
重点:参数类型用什么方式表示?用Class实例对象。
例如:
int.class,(int []).class
int [] ints = new int[0];
ints.getClass();
需求:反射String类的 String(StringBuffer buffer) 这个构造方法
/***思路:*1、通过String类的字节码对象调用getConstructor方法获取这个类的构造方法。*2、具体要获得哪个构造方法,就给这个方法传递一个参数类型。这个参数类型是Class对象的一个数组。*3、用第一步返回的一个Constructor对象调用newInstance方法,创建StringBuffer的实例对象。*/import java.lang.reflect.Constructor;public class D19_ReflectConstructor { public static void main(String[] args) throws Exception { Constructor<String> c = String.class.getConstructor(StringBuffer.class); String s = (String)c.newInstance(new StringBuffer("abc")); System.out.println(s); }}/*结果abc*/
四、成员变量的反射
Field类
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
反射的字段可能是一个类(静态)字段或实例字段。
部分方法:
Object get(Object obj) 返回指定对象上此 Field 表示的字段的值。
void set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
String getName() 返回此 Field 对象表示的字段的名称。
Class
import java.lang.reflect.Field;//定义一个用来反射的类class ClassPoint { public int x; private int y; public ClassPoint(int x, int y) { this.x = x; this.y = y; }}public class D20_ReflectField { public static void main(String[] args) throws Exception { ReflectField(); } //对ClassPoint继承成员变量反射 public static void ReflectField() throws Exception { //创建该类的实例对象 ClassPoint cp = new ClassPoint(3,5); //反射变量 x //获取对象的字节码,根据字节码获得x对应的成员字段 Field fieldX = cp.getClass().getField("x"); //通过字段fieldX获取它对应的值 System.out.println("变量x的值:"+fieldX.get(cp)); //反射私有成员变量y--暴力反射 //getDeclaredField()获取声明的字段,不管是被什么修饰的,但只要不是继承的。 Field fieldY = cp.getClass().getDeclaredField("y"); fieldY.setAccessible(true); //setAccessible()取消的字段的权限检查,false表示要检查 fieldY.setAccessible(true); System.out.println("变量y类型:"+fieldY.getType());//获取该字段对应的变量类型 System.out.println("变量y名称:"+fieldY.getName());//获取该字段对应的变量名 System.out.println("变量y的值:"+fieldY.get(cp)); //获取该字段对应的变量的值 }}/*结果变量x的值:3变量y类型:int变量y名称:y变量y的值:5*/
五、成员变量反射的综合实例
需求:反射某个类中所有的String类型的成员变量,并将该变量的值中指定的字符替换成新的字符。
分析:其实就是通过反射用用新字符串替换所有String类型的原来的字符串
思想:
1、定义StringDemo类,类里定义多种类型的成员变量,有的被public修饰。
2、另外定义一个类实现对StringDemo类的反射和其他操作,该类首先创建StringDemo的实例对象。
3、用getFields方法返回一个Field数组,用getFields方法是限制了只能反射被public修饰的成员字段。
4、变量该Field数组,取出每个字段,然后用该字段获取它的声明类型的Class对象与String.class比较。
5、如果是同一份字节码,就用set方法把该字段的值用新的值替换掉。
实例:通过反射把String类型的变量的值替换成新值
import java.lang.reflect.Field;class StringDemo { public int x = 0; public String str1 = "wuguangxin"; public String str2 = "howareyou"; String str3 = "jiewin";}public class D21_ReflectFieldTest { public static void main(String[] args) throws Exception { changeStringValue(); } public static void changeStringValue() throws Exception{ //创建对象 StringDemo str = new StringDemo(); //用getFields()方法返回一个所有声明的公共字段数组 Field[] fields = str.getClass().getFields(); //变量该数组,获取每一个字段进行 System.out.println("把u替换成*"); for (Field field : fields){ //如果该字段的字节码和String的字节码相同,说明是同一份字节码。 //字节码最适合用==比较,不建议用equals if(field.getType() == String.class){ //获取原来的字段值 String oldValue = (String)field.get(str); //把原来的值的指定字符替换成指定字符 String newValue = oldValue.replace("u", "*"); //设置该字段对应的变量的值为新的值 field.set(str, newValue); System.out.println(oldValue+" --> "+newValue);//测试 } } } }/*结果:把u替换成*wuguangxin --> w*g*angxinhowareyou --> howareyo**/
六、成员方法的反射
Method 类
Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。
所反映的方法可能是类方法或实例方法(包括抽象方法)。
方法:
String getName() 返回此 Method 对象表示的方法名称。
boolean isVarArgs() 如果将此方法声明为带有可变数量的参数,则返回 true;否则,返回 false。
boolean isSynthetic() 如果此方法为复合方法,则返回 true;否则,返回 false。
String toGenericString() 返回描述此 Method 的字符串,包括类型参数。
Object invoke(Object obj, Object… args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
invoke方法参数:
obj - 从中调用底层方法的对象
args - 用于方法调用的参数
返回:使用参数 args 在 obj 上指派该对象所表示方法的结果
例:成员方法的反射
import java.lang.reflect.Method;public class D22_ReflectMethod { public static void main(String[] args) throws Exception { reflectMethod(); } /** * 利用反射调用String类的charAt方法来获取字符串str的指定值。 * Method代表字节码的方法 * 从String获取字节码,根据字节码获取方法,参数(要获取的方法名称,参数列表) * @throws Exception */ public static void reflectMethod() throws Exception { String str = "abc"; Method methodCharAt = String.class.getMethod("charAt", int.class); System.out.println(methodCharAt.invoke(str,1)); }}/*结果b*/
七、 对接收数组参数的成员方法进行反射
例:对接收数组参数的成员方法进行反射
package zxx.enhance;import java.lang.reflect.Method;public class D23_ReflectArrayMethod { public static void main(String[] args) throws Exception { reflectArrayMethod(args); } public static void reflectArrayMethod(String[] args) throws Exception { //普通方式调用main方法 //TestArguments.main(new String[]{"abcd","123","eeeee"}); //反射方式调用main方法。 String srartingClassName = args[0]; Method mainMethod = Class.forName(srartingClassName).getMethod("main", String[].class); //null:因为main方法是静态的,调用静态方法不需要对象 //这样会报错,说参数个数不对,怎么解决这个问题? //mainMethod.invoke(null, new String[]{"abcd","123","eeeee"}); //上面语句报错的原因是:因为接收的参数是1个,而new String[]{"abcd","123","eeeee"}是一个数组, //会被拆成单个的元素,于是就出现了3个参数,为了把以上的写法当做一个参数,有2种方法: //解决方法1:把这个数组在进行一次包装,把它作为一个元素封装到Object数组中。 //mainMethod.invoke(null, new Object[]{new String[]{"abcd","123","eeeee"}}); //解决方法2:类型转换,在前面加上(Object)转换类型,告诉编译器,这是一个Object类型的参数,不要拆包。此效率比较高 //main方法是静态的,所以invoke方法的参数用null mainMethod.invoke(null, (Object)new String[]{"abcd","123","eeeee"}); }}//注意:在运行时首先要获取此类的完整路径“zxx.enhance.TestArguments”,就是包名+类名。//然后点右键-->Run As-->Run Configurations-->(x)=Arguments,在里面粘贴刚才复制的。保存后在运行。class TestArguments{ public static void main(String[] args) throws Exception { for(String arg : args){ System.out.println(arg); } }}/*结果abcd123eeeee*/
八、数组与Object的关系及其反射类型
package zxx.enhance;public class D24_ArrayAndObject { //反射数组。打印对象中成员方法 public static void main(String[] args) { int[] a1 = new int[3]; int[] a2 = new int[4]; int[][] a3 = new int[2][3]; String[] a4 = new String[3]; System.out.println(a1.getClass() == a2.getClass()); //System.out.println(a1.getClass() == a4.getClass()); //System.out.println(a1.getClass() == a3.getClass()); System.out.println(a1.getClass().getName()); //获取他们的父类的字节码名称 System.out.println(a1.getClass().getSuperclass().getName()); System.out.println(a2.getClass().getSuperclass().getName()); System.out.println(a3.getClass().getSuperclass().getName()); System.out.println(a4.getClass().getSuperclass().getName()); Object aObj1 = a1; Object aObj2 = a4; //基本类型的一维数组不能转换为Object类型数组。 //因为Object这个数组里面装的是int类型的数组,不是Object //Object[] aObj3 = a1; Object[] aObj4 = a3; Object[] aObj5 = a4; System.out.println(aObj1); System.out.println(aObj2); System.out.println(aObj4); System.out.println(aObj5); }}/*结果true[Ijava.lang.Objectjava.lang.Objectjava.lang.Objectjava.lang.Object[I@65690726[Ljava.lang.String;@525483cd[[I@2a9931f5[Ljava.lang.String;@525483cd*/
八、数组与Object的关系及其反射类型
package zxx.enhance;public class D24_ArrayAndObject { //反射数组。打印对象中成员方法 public static void main(String[] args) { int[] a1 = new int[3]; int[] a2 = new int[4]; int[][] a3 = new int[2][3]; String[] a4 = new String[3]; System.out.println(a1.getClass() == a2.getClass()); //System.out.println(a1.getClass() == a4.getClass()); //System.out.println(a1.getClass() == a3.getClass()); System.out.println(a1.getClass().getName()); //获取他们的父类的字节码名称 System.out.println(a1.getClass().getSuperclass().getName()); System.out.println(a2.getClass().getSuperclass().getName()); System.out.println(a3.getClass().getSuperclass().getName()); System.out.println(a4.getClass().getSuperclass().getName()); Object aObj1 = a1; Object aObj2 = a4; //基本类型的一维数组不能转换为Object类型数组。 //因为Object这个数组里面装的是int类型的数组,不是Object //Object[] aObj3 = a1; Object[] aObj4 = a3; Object[] aObj5 = a4; System.out.println(aObj1); System.out.println(aObj2); System.out.println(aObj4); System.out.println(aObj5); }}/*结果true[Ijava.lang.Objectjava.lang.Objectjava.lang.Objectjava.lang.Object[I@65690726[Ljava.lang.String;@525483cd[[I@2a9931f5[Ljava.lang.String;@525483cd*/
九、数组的反射
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用,
非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异。
Array工具类用于完成对数组的反射操作。
例:数组的反射
package zxx.enhance;import java.lang.reflect.Array;public class D25_ReflectArray { public static void main(String[] args) { reflectArray(); } //反射数组。打印对象中成员方法 private static void reflectArray() { String[] obj = {"A","B","C"}; //String obj ="ABC"; Class<? extends String[]> cla = obj.getClass(); //如果是一个数组,就拆成单个元素打印出来。 if(cla.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); } }}/*结果ABC*/
十、集合的反射
*框架的概念及用反射技术开发框架的原理
*用类加载器的方式管理资源和配置文件
反射的作用 – 实现框架功能
1、框架
我做房子卖给用户,由用户自己安装门窗和空调,我做的房子就是框架,
用户需要使用我的框架,把门窗插入进我的框架中。
框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
2、框架要解决的核心问题
我在写框架(房子)时,你这个用户可能还在读小学,还不会写程序,
那么我写的框架程序怎么调用你以后写的类(门窗)呢?
因为在写程序时无法知道要被调用的类名,所以,
在程序中无法直接new某个类的实例对象,而是要用凡是的方式来做。
3、综合实例:
先直接用new语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成
Reflection类的equals和hashcode方法,比较两个集合的运行结果差异。
然后改为采用配置文件加反射的凡是创建ArrayList和HashSet的实例对象,
比较观察运行结果差异。
需求:利用反射实现框架功能
思路:
1)通过文件输出流,创建一个文件Config.properties,
2)获取”className=java.util.ArrayList“的字节码后存入文件中。文件可以手动创建。
3)创建文件输入流和一个属性集,读取指定文件内容,加载到属性集中。
4)用键”className“在属性集中收索。
5)获得新集合,往集合中添加元素。
6)打印集合
例:
package zxx.enhance;import java.io.InputStream;import java.util.Collection;import java.util.Properties;public class D27_ReflectCollcetion { public static void main(String[] args) throws Exception { reflectCollcetion(); } //反射集合 public static void reflectCollcetion() throws Exception { //实际开发中,配置文件的路径不是这么写的,必须要写完整的路径。 //InputStream is = new FileInputStream("config.properties"); InputStream is = ReflectDemo.class.getResourceAsStream("config.properties"); Properties props = new Properties(); props.load(is); //关闭的是is对象关联的那个物理资源,对象本身并没有关闭, //而是有java的垃圾回收机制处理的。 is.close(); //反射做法 String className = props.getProperty("className"); Collection<ClassPoint> collections = (Collection)Class.forName(className).newInstance(); //原始做法 //Collection<ClassPointDemo> collections = new HashSet<ClassPointDemo>(); ClassPoint cp1 = new ClassPoint(3,3); ClassPoint cp2 = new ClassPoint(5,5); ClassPoint cp3 = new ClassPoint(3,3); collections.add(cp1); collections.add(cp2); collections.add(cp3); collections.add(cp1); //更改config.properties配置文件里的ArrayList为HashSet后,打印结果将不同 //因为集合的存储方式不同,ArrayList可以有重复元素,是有序的 //而HashSet集合是无序的,不可以重复存储,因为该集合会判断hashCode和equals方法。 System.out.println("集合元素个数:"+collections.size()); }}/*结果集合元素个数:4*/
十一、通过反射获得泛型的实际类型参数
分析:
比如:Vector v = new Vector();
那么通过v是无法知道定义它的那个泛型类型的,那么可以把这个v交给一个方法当做参数或者返回值类型来使用,
然后通过Method类的getGenericParameterTypes()方法来获得该方法的参数列表,从而获得参数实际间类型。
import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.sql.Date;import java.util.Vector;/** * 通过反射获得泛型的实际类型参数 * @author Administrator */public class GenericTest { public static void main(String[] args) throws Exception { //通过v对象是无法得到的,值能通过方法来获取,那么就要定义一个方法如下面applyVector方法。 //Vector<Date> v = new Vector<Date>(); //获得字节码,通过字节码获得方法,参数是一个方法名,Vector的字节码 Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class); //通过Method类的getGenericParameterTypes()方法 //反射applyMethod方法的参数化类型,可能有多个,所以是数组。 Type[] types = applyMethod.getGenericParameterTypes(); //返回types的第一个参数,返回的是ParameterizedType类型。 ParameterizedType pType = (ParameterizedType)types[0]; //获得原始类型 System.out.println(pType.getRawType()); //获得实际类型。 System.out.println(pType.getActualTypeArguments()[0]); } //需要定义这个方法,通过这个方法的参数列表来反射他的参数类型。 public static void applyVector(Vector<Date> v) { }}/*结果class java.util.Vectorclass java.sql.Date*/
十二、反射接口中的成员变量
package zxx.enhance;import java.lang.reflect.Field;interface Inter{ public int z = 8;}class ClassPoint1 implements Inter{ public int x; @SuppressWarnings("unused") private int y; public ClassPoint1(int x, int y){ this.x = x; this.y = y; }}public class D28_ReflectIntefaceField{ public static void main(String[] args) throws Exception{ ClassPoint1 cp = new ClassPoint1(3, 5); //先得到接口的Class对象数组 for(Class<?> ca : cp.getClass().getInterfaces()){ //得到Field对象数组 ca.getFields(); //遍历数组得到所有对象的名字 for(Field fd : ca.getFields()){ System.out.println(fd.getType()); //获得类型 System.out.println(fd.getName()); //获得名称 System.out.println(fd.get(cp)); //获得值 } } }}/*结果intz8*/
——- android培训、java培训、期待与您交流! ———-
- java基础增强---反射
- Java基础&增强 反射 注解 类加载器
- 黑马程序员——java基础增强反射的学习
- Java基础增强3-反射,内省,beanutils,泛型
- java基础增强(泛型,反射, 注解,日志)
- Java - 基础增强 - 增强for - 可变参数 - 枚举 - 反射 - 内省 - 泛型
- Java基础增强
- Java基础增强
- java 基础增强
- java基础增强
- Java基础增强
- Java基础&增强 枚举
- Java基础&增强 并发
- java基础增强
- java基础增强1
- Java基础增强
- Java基础增强---枚举
- java 基础增强
- Pranav Mistry第六感装置
- 堆与栈的区别-----超详细总结
- mysql安装图解 mysql图文安装教程(详细说明) 5.0版本
- 运行servlet时出现404错误
- 让Nginx处理Django的静态文件
- java基础增强---反射
- unknown command-line argument or argument value build错误
- Working out - CODEFORCES, 429B 动态规划
- Java面向对象的理解3
- GoogleVis包生成的网页没有图像的解决办法
- 机器学习算法-Adaboost
- CentOS+nginx+Django+Postgresql web环境搭建
- 黑马程序员——类和对象
- Matlab优化问题03—fminsearch