【黑马程序员】Java基础加强15:反射Reflect

来源:互联网 发布:网络选修课网站 编辑:程序博客网 时间:2024/06/01 11:40
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------


一、什么是反射

反射是程序在运行时期,对一个类的class文件进行解析,获取其构造方法、成员变量、成员方法,还能运行这些方法;

简单一句话:反射就是把Java类中的各种成分映射成相应的Java类,出现的目的是增强程序的扩展性。

 

反射的基石:Class

Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class

Class类代表Java类,它的各个实例对象对应各个类在内存中的字节码,比如String是一种类,它加载到内存中会反射为String.class的类。

 

得到Class对象(字节码)的三种方法:

1、对象.getClass(),例如,new Person().getClass()

2、已知类名.class,例如,System.classPerson.class

3Class.forName("完整类名"),例如,Class.forName("java.util.Date")Class.forName("java.lang.String");在反射之中,常用第三种方法,类名可以在需要时换成字符串变量,最后传入。

/*通过代码示例三种得到Class对象的方法Person p = new Person(); 第一种方法,通过对象获取  Class c1 = p.getClass();  System.out.println(c1);第二种方法,通过类的静态属性获取  Class c2 = Person.class;  System.out.println(c2);  System.out.println(c2 == c1);//true  System.out.println(c2.equals(c1));//true  第三种方法,通过Class.forName(完整类名)Person如果定义了包,应该为:包名.类名  Class c3 = Class.forName("Person");  System.out.println(c3);  */

九种预定义Class实例对象

1在Class类中共创建了九个预定义对象,八个基本类型boolean,byte,char,short,int,long,float,double和void(void也有对应的class);

2预定义对象有如下示例:int.class==Integer.TYPEvoid.class==Void.TYPE

3这些对象仅能通过声明为publicstaticfinal的变量访问。

//对class类型的判断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());//false,不是基本类型System.out.println(int[].class.isArray());//true,是数组类型

二、Java反射的三个重要类

1Constructor类:代表某个类中的构造方法

获取方法:

getConstructor(Class<?>... parameterTypes) 返回公共构造方法

getConstructors() 返回公共构造方法数组

getDeclaredConstructor(Class<?>... parameterTypes) 返回构造方法,包括私有

getDeclaredConstructors()返回构造方法数组,包括私有

代码示例:获得String类中所有的构造方法

package blog.itheima;import java.lang.reflect.Constructor;public class ReflectConstructor {public static void main(String[] args) throws Exception {//获得String类中的公有构造方法数组,也可以自定义类的构造方法,通过反射获取Constructor[] construtor = Class.forName("java.lang.String").getConstructors();//获得某一个构造方法:默认得到的是无参的构造方法Constructor constructor2 = Class.forName("java.lang.Thread").getConstructor();System.out.println(constructor2);//打印:public java.lang.Thread()//得到String类中的String(StringBuffer buffer)的构造方法//也可以通过可变参数,多传几个class类型,得到多个构造方法Constructor constructor = String.class.getConstructor(StringBuffer.class);System.out.println(constructor);/*打印:public java.lang.String(java.lang.StringBuffer)说明constructor是对应String类的StringBuffer构造方法的字节码对象再通过newInstance来创建实例化对象,里面需要传入同类型的对象StringBuffer,可以调用多次*/String string1 = (String) constructor.newInstance(new StringBuffer("abc"));System.out.println(string1.charAt(1));//Class.newInstance()方法等价于使用constructor,newInstance()无参构造器String string2 = (String) Class.forName("java.lang.String").newInstance();//该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象}}

2Field类:代表某个类中的成员变量

获取方法:

getField(String name) 返回公共成员字段

getFields() 返回公共成员字段数组

getDeclaredField(String nam) 返回成员字段,包括私有

getDeclaredFields()返回成员字段数组,包括私有

//获得某个类上的字节码对象,并取得对应的值package blog.itheima;import java.lang.reflect.Field;import java.util.Date;public class ReflectFieldGet {public static void main(String[] args) throws Exception {ReflectTest rt = new ReflectTest(3,5);//要得到一个类身上的字段,需要先得到类的字节码Field fieldY = rt.getClass().getField("y");System.out.println(fieldY);/*打印结果:public int blog.itheima.ReflectTest.yfieldY只是代表类字节码身上的变量,没有对应到对象身上的值要取得某个对象上对应的值,需要指定相应的对象*/System.out.println(fieldY.get(rt));//想要取x字段,用与y相同的方法//Field fieldX = rt.getClass().getField("x");//但是运行结果会报错,因为x是私有字段,要用如下方法获取Field fieldX = rt.getClass().getDeclaredField("x");//因为是私有 字段,要更改属性为可以访问,称为暴力反射//注意,Constructor, Field, Method 三个常用类都有暴力反射方法fieldX.setAccessible(true);System.out.println(fieldX.get(rt));}}class ReflectTest {private int x;public int y;public ReflectTest(int x, int y) {super();this.setX(x);this.y = y;}public int getX() {return x;}public void setX(int x) {this.x = x;}}

//字段的更改//需求:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"n"改成"a"。package blog.itheima;import java.lang.reflect.Field;public class ReflectFieldReplace {public static void main(String[] args) throws Exception {ReflectTest2 rt = new ReflectTest2();changeStringValue(rt);System.out.println(rt);}private static void changeStringValue(Object obj) throws Exception {Field[] fields = obj.getClass().getFields();//得到所有字段的数组for(Field field : fields){//if(field.getType().equals(String.class)){//字节码只有一分,用等号比更加专业if(field.getType() == String.class){//得到字节码对应对象的值,强转成StringString oldValue = (String)field.get(obj);String newValue = oldValue.replace('n', 'a');/*void set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值*/field.set(obj, newValue);}}}//字段反射的作用:可以更改对象里的字段信息,或者更改配置文件的信息}class ReflectTest2 {public String str1 = "baidu";public String str2 = "Tencent";public String str3 = "sina";@Overridepublic String toString() {return "ReflectTest2 [str1=" + str1 + ", str2=" + str2 + ", str3="+ str3 + "]";}}

3Method类:代表某个类中的成员方法

获取方法:

getMethod(String name, Class<?>...parameterTypes)公共成员方法

getMethods() 公共成员方法数组

getDeclaredMethod(String name, Class<?>...parameterTypes)所有成员方法

getDeclaredMethods() 所有成员方法数组,包括私有

//获取String中的charAt方法package blog.itheima;import java.lang.reflect.Method;public class ReflectMethod {public static void main(String[] args) throws Exception {String s = "abcde";/*getMethod(String name,Class<?>... parameterTypes)name - 方法名      parameterTypes - 参数列表,charAt返回的是int型*/Method methodCharAt = String.class.getMethod("charAt", int.class);/*Object invoke(Object obj, Object... args)   对带有指定参数的对象调用由此 Method 对象表示的底层方法,即调用charAt方法*/System.out.println(methodCharAt.invoke(s, 1));//string.charAt(1)/*注意:如果传递给Method对象的invoke方法的第一个参数为null,说明该Method对象对应的是一个静态方法*///1.4语法调用,把2当作Integer对象,传入一个元素到Object数组中System.out.println(methodCharAt.invoke(s, new Object[]{2}));}}

//用反射方式执行某个类中的main方法package blog.itheima;import java.lang.reflect.Method;public class ReflectMethodMain {public static void main(String[] args) throws Exception {//用静态代码方法直接调用,传统的调用方法//TestArguments.main(new String[]{"111","222","333"});/*用反射方式去调用main方法的原因?因为有可能开始并不知道类名,最后才传入类名参数,为了不更改源代码,可以用反射*/String startingClassName = args[0];//main方法参数形式是String的数组,只有一个参数Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);//main是静态方法,第一个参数为null,//把new String[]打包成一个Object数组,让系统拆包成一个,只让拆一次//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});//也可以把new String[]数组直接强制转成Object,变成一个对象,不让拆包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);}}}
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------
0 0
原创粉丝点击