1.反射的概念
反射就是把java中的各个成分映射成相应的java类。一个类中有多个组成部分,例如:成员变量,成员方法,构造方法等,反射就是加载类,并解剖出类的各个组成部分。
问题:编程时什么情况下才需要加载类,并解剖出类的各个组成部分呢?
(1)加载类
在我们学习Class类时,我们知道Class类代表某个类的字节码,可以通过三种方法得到Class对象
(1)类.class (2)对象.getClass() (3)Class.forName(“类的完整路径”) |
其中第三种方法比较常用,forName方法用于加载某个类字节码到内存中,并使用class对象进行封装。
(2)解剖类
在Class类中提供了一下的方法用于解析出某个类的构造方法,方法和成员变量,解剖出的成员分别使用Constructor、Method、Field对象表示。
用于解析出构造函数的方法如下(解析出来的构造函数用Constructor表示):
(1)得到被public修饰的公共构造函数 public Constructor getConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 (2)如果构造函数被private修饰 public Constructor getDeclaredConstructor(Class... parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 |
用于解析出方法的方法如下(解析出来的方法用Method表示):
(1)如果方法被public修饰 public Method getMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法 (2)如果方法被private修饰 public Method getDeclaredMethod(String name,Class... parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 |
用于解析出成员变量的方法如下(解析出来的方法用Field表示):
(1)如果变量被public修饰 Public Field getField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段 (2)如果变量被private修饰 public Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 |
问题:通过上面的方法可以解析出一个类中的构造函数、方法和成员变量等等,那么解析出来有什么用途呢?
2.利用Constructor创建对象
通过查帮助文档我们了解,Constructor提供了如下方法,用于创建类的对象
public Object newInstance(Object... initargs) 其中initargs用于指定构造函数接收的参数 |
范例一:反射类无参、有参、私有的构造函数,创建类的对象
创建一个Student类
package com.itheima.reflect; import java.util.List; public class Student { public Student(){ System.out.println("无参构造函数"); } public Student(String name){ System.out.println("有一个参数构造函数"+name); } public Student(String name,int age){ System.out.println("有两个参数的构造函数"+name+”:”+age); } private Student(List list){ System.out.println("有参数并且为私有的构造函数"+list.size()); } } |
(1)反射出无参构造函数
/** * 反射无参构造函数public Student() */ @Test public void test1()throws Exception{ //获取Person的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); // 获取上面Class对象的无参构造函数 Constructor c=cl.getConstructor(null); //调用Constructor中的newInstance()方法创建Student对象 Student s1=(Student) c.newInstance(null); } |
运行结果:无参构造函数
(2)反射出有一个参数构造函数
/** * 反射有一个参数构造函数public Student(String name) */ @Test public void test2()throws Exception{ //获取Person的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //获取上面Class对象的无参构造函数 Constructor c=cl.getConstructor(String.class); //调用Constructor中的newInstance()方法创建Student对象 Student s1=(Student) c.newInstance("黑马_邵天强"); } |
运行结果:有一个参数构造函数黑马_邵天强
(3)反射出有两个参数构造函数
/** * 反射有两个个参数构造函数public Student(String name,int age) */ @Test public void test3()throws Exception{ //获取Person的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //获取上面Class对象的无参构造函数 Constructor c=cl.getConstructor(String.class,int.class); //调用Constructor中的newInstance()方法创建Student对象 Student s1=(Student) c.newInstance("黑马_邵天强",23); } |
运行结果:有两个参数的构造函数:黑马_邵天强:23
(4)反射出私有的构造函数
/* 反射有私有构造函数private Student(List list) */ @Test public void test4()throws Exception{ //获取Person的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //获取上面Class对象的无参构造函数 Constructor c=cl.getDeclaredConstructor(List.class); //设置访问权限 c.setAccessible(true); //创建一个List对象 List<String>list=new ArrayList<String>(); list.add("黑马_1");//向集合中添加对象 list.add("黑马_2");//向集合中添加对象 list.add("黑马_3");//向集合中添加对象 //调用Constructor中的newInstance()方法创建Student对象 Student s1=(Student) c.newInstance(list); } |
运行结果:有参数并且为私有的构造函数3
注意:为了简化开发人员创建对象,它在class对象中也提供了一个newInstance()方法,用于创建类的对象,这样可以避免每次都需要去反射Constructor类创建对象。
/** * 利用Class类中的方法创建对象 */ @Test public void test5()throws Exception{ //获取Person的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //利用Class类中的newInstance方法创建Student对象 Student s=(Student) cl.newInstance(); } |
3.利用Method执行方法
Method对象提供了如下方法,用于执行它所代表的方法:
public Object invoke(Object obj,Object... args) |
范例二:使用Method分别执行无参、有参、多个参(带数组和基本数据类型)、静态、私有的方法
Person类中加如下方法:
//没有参数没有返回值的方法 public void test1(){ System.out.println("没有参数的方法"); } //有参数没有返回值的方法 public void test1(String name,int age){ System.out.println(name+":"+age); } //有参数有返回值的方法 public Class[]test1(String name,int[]age){ return new Class[]{String.class}; } //私有方法 private void test(InputStream in){ System.out.println(in); } //静态方法 public static void test1(int age){ System.out.println(age); } |
(1)反射没有参数没有返回值的方法
@Test public void test6()throws Exception{ //获取Student的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //调用c1对象的getMethod方法获取Method对象 Method m=cl.getMethod("test1", null); //调用Method对象的invoke方法执行指定对象的方法 m.invoke(cl.newInstance(), null); } |
运行结果:无参构造函数
没有参数的方法
(2)反射有参数没有返回值的方法
@Test public void test7()throws Exception{ //获取Student的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //调用c1对象的getMethod方法获取Method对象 Method m=cl.getMethod("test1", String.class,int.class); //调用Method对象的invoke方法执行指定对象的方法 m.invoke(cl.newInstance(), "黑马_张三",23); } |
运行结果:无参构造函数
黑马_张三:23
(3)反射有参数有返回值的方法
@Test public void test8()throws Exception{ //获取Student的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //调用c1对象的getMethod方法获取Method对象 Method m=cl.getMethod("test1", String.class,int[].class); //调用Method对象的invoke方法执行指定对象的方法 Class[]clazz=(Class[]) m.invoke(cl.newInstance(), "黑马_张三",new int[]{23,45}); for(int i=0;i<clazz.length;i++){ System.out.println(clazz[0]); } } |
运行结果:无参构造函数
class java.lang.String
(4)反射私有方法
@Test public void test9()throws Exception{ //获取Student的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //调用c1对象的getDeclaredMethod方法获取Method对象 Method m=cl.getDeclaredMethod("test",InputStream.class); //暴力访问 m.setAccessible(true); //调用Method对象的invoke方法执行指定对象的方法 m.invoke(cl.newInstance(),newFileInputStream("c:\\user.txt")); } |
运行结果:无参构造函数
java.io.FileInputStream@9ced8e
(5)反射静态方法
@Test public void test10()throws Exception{ //获取Student的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //调用c1对象的getDeclaredMethod方法获取Method对象 Method m=cl.getMethod("test1",int.class); //因为是静态方法,所以不需要对象 m.invoke(null,23); } |
运行结果:23
(6)反射主函数
@Test public void test11()throws Exception{ //获取Student的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //调用c1对象的getDeclaredMethod方法获取Method对象 Method m=cl.getMethod("main",String[].class); //因为是静态方法,所以不需要对象 m.invoke(null, new String[]{"黑马_1","黑马_2"}); } |
上面的程序运行后有什么问题吗?
通过运行可知:出现了如下问题:
java.lang.IllegalArgumentException: wrong number of arguments:错误的参数个数
但是我明明传的就是String[]数组啊,上面的静态方就可以,而主函数也是静态方法,为什么就会报错呢?
答案如下:
这是因为sun公司在api升级的时候出现了一点问题。 如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题 在JDK1.5:Method.invoke(Object obj,Object...args); 在JDK1.4:Method.invoke(Object obj,Object[]args); 例如:new String[]{“aa”,”bb”},在JDK1.4时,他会把这个数组进行拆分。为了兼容,JDK1.5运行invoke时,会参考JDK1.4.此时m.invoke(null, new String[]{"黑马_1","黑马_2"});主函数编程了main("黑马_1","黑马_2")两个参数,所以出现错误 |
为了解决这个问题:主要有两种方法
方法一:
m.invoke(null,new Object[]{new String[]{"黑马_1","黑马_2"}}); 这样拆分以后{new String[]{"黑马_1","黑马_2"}就作为一个参数了 |
方法二:
m.invoke(null,(Object)new String[]{"黑马_1","黑马_2"}); 我假设告诉你我不是数组 |
4.利用Field访问属性
Field对象提供了如下方法,用于设置、获取对象属性的值
public void set(Object obj,Object value): 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。 public Object get(Object obj) 返回指定对象上此 Field 表示的字段的值 |
范例:利用Field分别设置和获取公有、私有的属性
Student类中添加如下字段
public String name="黑马_1"; private int age; private static int password; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public static int getPassword() { return password; } |
(1)反射公有字段
@Test public void test12()throws Exception{ Student s=new Student(); //获取Student的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //获取字段的对象 Field f=cl.getField("name"); //获取某个对象的指定字段 Object obj=f.get(s); //获取反射字段的类型 Class type=f.getType(); System.out.println(type+":"+obj); } |
运行结果:
无参构造函数
class java.lang.String:黑马_1
(2)反射私有字段
@Test public void test13()throws Exception{ Student s=new Student(); //获取Student的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //getDeclaredField获取字段的对象 Field f=cl.getDeclaredField("age"); //暴力访问 f.setAccessible(true); //设置值 f.set(s, 23); //获取某个对象的指定字段 Object obj=f.get(s); //获取反射字段的类型 Class type=f.getType(); System.out.println(type+":"+obj); } |
运行结果:
无参构造函数
int:23
(3)静态字段
@Test public void test14()throws Exception{ Student s=new Student(); //获取Student的Class对象 Class cl=Class.forName("com.itheima.reflect.Student"); //getDeclaredField获取字段的对象 Field f=cl.getDeclaredField("age"); //暴力访问 f.setAccessible(true); //设置值 f.set(s, 23); //获取某个对象的指定字段 Object obj=f.get(s); //获取反射字段的类型 Class type=f.getType(); System.out.println(type+":"+obj); } |
运行结果:无参构造函数
int:23
小练习:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”
创建一个Entire类
package com.itheima.reflect; public class Entire { public String filed1="babcdb"; public String filed2="acb"; public String filed3="b3"; } |
创建一个测试类
package com.itheima.reflect; import java.lang.reflect.Field; import org.junit.Test; public class ReflectDemo3 { @Test public void test() throws Exception{ //创建一个Entire对象 Entire e=new Entire(); //获取Entire的Class对象 Class cl=Class.forName("com.itheima.reflect.Entire"); //获取Entire中的所有字段 Field[]f=cl.getFields(); for(int i=0;i<f.length;i++){ if(f.getType()==String.class){ //获取指定对象中相关字段 String str=(String)f[i].get(e); //把相关字段中的‘b’替换成'a' str=str.replace('b', 'a'); //然后再设置 f[i].set(e, str); System.out.println(str); } } } } |
运行结果:
aaacda
aca