反射:一个类有多个组成部分。例如:成员变量,方法,构造方法等。反射就是加载类,并解剖出类的各个组成部分。反射多用在框架的制作上。
加载类:Java中有一个Class类用于代表某一个类的字节码,它提供加载某个类字节码的方法:forName()。forName()方法用于加载某个类的字节码到内存中,并使用class对象进行封装
下面是加载类的三种方法
1 //加载类的方法一(这个方法里面类的路径必须是带包的完整路径) 2 try { 3 Class c1 = Class.forName("Test/src/day4025/Person"); 4 } catch (ClassNotFoundException e) { 5 // TODO Auto-generated catch block 6 e.printStackTrace(); 7 } 8 9 //加载类的方法二 10 Class c2 = new Person().getClass(); 11 12 //加载类的方法三 13 Class c3 = Person.class;
解剖类:Class对象提供了一系列的解剖方法,分别用于解剖出构造方法,方法和成员变量。解剖出的成员分别使用Constructor,Method,Field对象表示。
1.下面先给出加载类,然后解剖出构造方法对象产生对象的例子
首先是Person类
1 public class Person { 2 3 public String name = "??"; 4 5 public Person(){ 6 System.out.println("Person"); 7 } 8 9 public Person(String name){ 10 System.out.println(name); 11 } 12 13 public Person(String name, int age){ 14 System.out.println(name + "," + age); 15 } 16 17 private Person(int age){ 18 System.out.println(age); 19 } 20 21 }
然后是Junit测试演示
1 Class c1 = null; 2 3 @Before 4 public void testBefore() throws Exception{ 5 c1 = Class.forName("day4025.Person"); 6 } 7 8 @Test 9 //获得public Person()构造函数 10 public void test1() throws ClassNotFoundException, Exception, Exception{ 11 12 /*//加载类 13 Class c1 = Class.forName("day4025.Person");*/ 14 //获得构造方法对象 15 Constructor cs = c1.getConstructor(null); 16 //利用获得的构造方法对象来生成对象 17 Person p1 = (Person)cs.newInstance(null); 18 System.out.println(p1.name); 19 System.out.println(); 20 } 21 22 @Test 23 //获得public Person(String name)构造函数 24 public void test2() throws Exception{ 25 /*//加载类 26 Class c1 = Class.forName("day4025.Person");*/ 27 //获得构造方法对象 28 Constructor cs = c1.getConstructor(String.class); 29 //利用获得的构造方法对象来生成对象 30 Person p = (Person)cs.newInstance("hello"); 31 System.out.println(p.name); 32 System.out.println(); 33 } 34 35 @Test 36 //获得public Person(String name, int age)构造函数对象 37 public void test3() throws Exception{ 38 /*//加载类 39 Class c1 = Class.forName("day4025.Person");*/ 40 //获得构造方法对象 41 Constructor cs = c1.getConstructor(String.class,int.class); 42 //利用获得的构造方法对象来生成对象 43 Person p = (Person) cs.newInstance("hello",666); 44 System.out.println(p.name); 45 System.out.println(); 46 } 47 48 @Test 49 //获得private Person(int age)构造函数对象 50 public void test4() throws Exception{ 51 /*// 加载类 52 Class c1 = Class.forName("day4025.Person");*/ 53 // 获得构造方法对象getConstructor只能调用public,想要调用私有的要用getDeclaredConstructor 54 Constructor cs = c1.getDeclaredConstructor(int.class); 55 //私有的构造函数在外部是不能访问的但是我们可以(暴力反射),强行进行访问 56 cs.setAccessible(true); 57 // 利用获得的构造方法对象来生成对象 58 Person p = (Person) cs.newInstance(666); 59 System.out.println(p.name); 60 System.out.println(); 61 62 } 63 64 @Test 65 //创建对象的另外一种方法(效果和test1一样,不过Java帮我们写好了,直接调用即可) 66 public void test5() throws Exception{ 67 68 /*//加载类 69 Class c1 = Class.forName("day4025.Person");*/ 70 Person p = (Person)c1.newInstance(); 71 72 }
可以看出当我们加载类后,可以解剖出构造方法对象(根据构造函数的参数类型和个数确定出解剖出的是哪一个构造函数的对象),然后产生Person对象。
上列代码结果如下
1 Person 2 ?? 3 4 hello 5 ?? 6 7 hello,666 8 ?? 9 10 666 11 ?? 12 13 Person
2.解剖方法对象,然后使用方法
首先是Animal类
1 public class Animal { 2 3 public void eat(){ 4 System.out.println("eat"); 5 } 6 7 public int sum(int a,int b){ 8 return a + b; 9 } 10 11 public static void happly(){ 12 System.out.println("happly"); 13 } 14 15 private void play(){ 16 System.out.println("play"); 17 } 18 19 }
然后是Junit测试演示
1 public class Demo5 { 2 3 Class c = null; 4 Animal a = new Animal(); 5 6 @Before 7 public void testBefore() throws Exception{ 8 c = Class.forName("day4025.Animal"); 9 } 10 11 @Test 12 //测试方法 public void eat() 13 public void test1() throws Exception{ 14 15 Method method = c.getMethod("eat", null); 16 method.invoke(a, null); 17 18 } 19 20 @Test 21 //测试方法 public int sum(int a,int b) 22 public void test2() throws Exception{ 23 24 Method method = c.getMethod("sum", int.class,int.class); 25 int x = (int) method.invoke(a, 1, 2); 26 System.out.println(x); 27 28 } 29 30 @Test 31 //测试方法 public static void happly() 32 public void test3() throws Exception{ 33 34 Method method = c.getMethod("happly", null); 35 method.invoke(null, null); 36 37 } 38 39 @Test 40 //测试方法 private void play() 41 public void test4() throws Exception{ 42 43 Method method = c.getDeclaredMethod("play", null); 44 //暴力放射使private的方法强行被访问 45 method.setAccessible(true); 46 method.invoke(a, null); 47 48 } 49 50 51 }
解剖构造函数和普通函数的区别,一:是我们在获得普通方法对象的时候,还需要写入方法名,构造方法不需要,因为构造方法名字一样,普通方法对象获取写入方法名,帮助我们区分不同的方法。二:解剖出普通函数后,要运行函数要需要指定对象,是运行什么对象的方法。
运行结果
1 eat 2 3 3 happly 4 play
3.main方法的反射
先贴main函数代码
public static void main(String[] args) { System.out.println("main"); }
然后是Junit测试演示
1 @Test 2 //测试方法 public static void main(String[] args) 3 public void test5() throws Exception{ 4 5 Method method = c.getDeclaredMethod("main", String[].class); 6 method.invoke(null, (Object)new String[]{"1","2","3"}); 7 method.invoke(null, new Object[]{new String[]{"1","2","3"}}); 8 9 }
结果如下
1 main 2 main
运行成功了,但是代码和我们上面写的不一样,按照上面写的话我们应该这样写
1 method.invoke(null, new String[]{"1","2","3"});
但是这样写报错了,说参数数量错误,为什么会这样呢。
因为在Java1.4我们还没有可变参数,我们是用数组来储存方法的多个参数的,所以这里Java会以为我们的main函数有3个String类型的参数,其实只有一个String类型数组的参数,所以会报参数数量错误。(Java1.5要兼容Java1.4这就是为什么出现这个错误的原因)
解决方法 一.我们可以把这个String数组放在Object数组里面,当Java把Object数组拆开后,就会得到我们需要的String数组参数。
方法二.可以将数组包装成Object对象,Java就不会将它拆开。
4.解剖字段对象
测试的字段
1 public int a = 10; 2 public static int b = 20; 3 private int c = 30;
Junit测试演示
1 Class c = null; 2 Animal b = new Animal(); 3 4 @Before 5 public void testBefore() throws Exception{ 6 c = Class.forName("day4025.Animal"); 7 } 8 9 @Test 10 //测试 public int a = 10; 11 public void test7() throws Exception{ 12 Field f = c.getField("a"); 13 int x = (int) f.get(b); 14 System.out.println(x); 15 f.set(b, 100); 16 int y = (int) f.get(b); 17 System.out.println(y); 18 } 19 20 @Test 21 //测试 public static int b = 20; 22 public void test8() throws Exception{ 23 24 Field f = c.getField("a"); 25 int x = (int) f.get(b); 26 System.out.println(x); 27 f.set(b, 200); 28 int y = (int) f.get(b); 29 System.out.println(y); 30 31 } 32 33 @Test 34 //测试 private int d = 30; 35 public void test9() throws Exception{ 36 37 Field f = c.getDeclaredField("d"); 38 f.setAccessible(true); 39 int x = (int) f.get(b); 40 System.out.println(x); 41 f.set(b, 300); 42 int y = (int) f.get(b); 43 System.out.println(y); 44 }