java反射

 

1.反射的概念

  反射就是把java中的各个成分映射成相应的java类。一个类中有多个组成部分,例如:成员变量,成员方法,构造方法等,反射就是加载类,并解剖出类的各个组成部分。

问题:编程时什么情况下才需要加载类,并解剖出类的各个组成部分呢?

(1)加载类

  在我们学习Class类时,我们知道Class类代表某个类的字节码,可以通过三种方法得到Class对象

(1)类.class

(2)对象.getClass()

(3)Class.forName(“类的完整路径”)

其中第三种方法比较常用,forName方法用于加载某个类字节码到内存中,并使用class对象进行封装。

 (2)解剖类

  在Class类中提供了一下的方法用于解析出某个类的构造方法,方法和成员变量,解剖出的成员分别使用ConstructorMethodField对象表示。

用于解析出构造函数的方法如下(解析出来的构造函数用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(nullnew 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.5Method.invoke(Object obj,Object...args);

JDK1.4Method.invoke(Object obj,Object[]args);

例如:new String[]{“aa”,”bb”},在JDK1.4时,他会把这个数组进行拆分。为了兼容,JDK1.5运行invoke时,会参考JDK1.4.此时m.invoke(nullnew 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

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值