Java反射机制

Java反射机制

反射机制在Java中是很重要的,所谓反射,就是把Java类中的各个部分(例如:成员方法,成员变量,构造方法等等)抽取出来,把它们在抽象成一些类,然后就可以在要用的时候动态的调用了。下面我们开始来认识一下它们吧:

1.   反射机制的基石—>Class类

1)        Class类用于表示编译后的.class文件,是所有加载进内存的字节码对象的父类,所以可以通过Class得到运行时的类。

2)        获得字节码对应的实体对象的方法

a)        使用        类名.class

b)        使用        对象实例.getClass()方法

c)        使用        Class.forName(类名)方法

3)        这里有九个Java预定义的Class实例对象,分别对应的是八个Java基本数据类型(boolean、byte、char、short、int、long、float、double)和void类型

代码示例:

package learn.reflection;
public class ReflectDemo {
 
       public static void main(String[] args) {
 
              /*int.class*/
              System.out.println("int基本数据类型和Integer的基本数据类型是同一个Class对象"
                            + (int.class == Integer.TYPE));
              /*int.class是基本数据类型*/
              System.out.println("int.class是基本数据类型:" + int.class.isPrimitive());
              /*数组类型*/
              System.out.println("数组类型:" + int[].class.isArray());
              System.out.println("布尔类型:"+(boolean.class==Boolean.TYPE));
              /*........*/
       }
}


了解了这个最基本的Class类之后呢,我们就可以一级一级的往后学习了,接着来看一下我们的构造方法类

2.   构造方法类(Coustructor类)

构造方法我们肯定不陌生,一个Coustructor类的实例就可以对应着相应类的一个构造方法。

1)        我们可以通过以下几种方式获得Coustructor的实例:

a)        得到某个类空参数构造方法:

Constructorconstructor = Class.forName("java.lang.String").getConstructor();

b)        得到某个类所有的构造方法:

Constructor [] constructors= Class.forName("java.lang.String").getConstructors();

c)        得到某一个带参数的构造方法:

Constructor constructor=Class.forName("类名").getConstructor(参数类.class);  

2)        下面我们试着用构造方法类来创建一个类的实例:

package learn.reflection;
 
import java.lang.reflect.Constructor;
 
public class ReflectDemo {
 
       public static void main(String[] args) throws Exception {
              /* 加载String类 */
              Class clszz = Class.forName("java.lang.String");
              /* 获得String类的构造方法类的实例 */
              Constructor constructor = clszz.getConstructor(StringBuffer.class);
              /* 通过constructor获得一个String类的实例 */
              String str = (String) constructor.newInstance(new StringBuffer("你好"));
              System.out.println(str);
       }
}


我们知道一个类可能有多个构造方法,用什么方式可以区分清楚想得到其中的哪个方法呢?我们要根据参数的个数和类型,例如,Class.getMethod(name,Class...args)中的args参数就代表所要获取的那个方法的各个参数的类型的列表;并且参数类型用用Class实例对象来表示。

3.   成员变量/字段类(Filed类)

和构造方法类一样, Field类代表反射某个类中的一个成员变量;不过这里有一个问题容易被弄糊涂,就是这个Field对应的是对应到类上面的成员变量,还是对应到对象上的成员变量呢?其实仔细想一下就知道了,Field其实对应的是类里面的成员变量而不是类对象中的成员变量,举个例子来说就好比:人对应的是Person类,Age(年龄)是Person成员变量/属性,那么小明是一个人,他15岁,Field对应的就是Age这个属性,而不是小明的15岁;

说了这么多,上段儿代码就明白了:

package learn.reflection;
 
import java.lang.reflect.Field;
 
class Person {
       public String name;// 姓名
       public int age;// 年龄
 
       public Person(String name, int age) {
              this.name = name;
              this.age = age;
       }
}
 
public class ReflectDemo {
       public static void main(String... args) throws Exception {
              Person p = new Person("小明", 15);
              /*获取Person类的name成员*/
              Field fieldName = p.getClass().getField("name");
              /*获取Person实例p的name成员的值*/
              System.out.println(fieldName.get(p));
       }
}


这里有几点需要注意的:

1)  如果类的某个成员变量的修饰符是private(即该成员变量是私有的),那么直接通过getField方法获取Field类型的对象就会出现“NoSuchFieldException”(无匹配的成员变量异常),

2)  如果类的某个成员变量的修饰符是private,那么直接通过getDeclaredField方法获取Field类型的对象就会出现“IllegalAccessException”。 

要解决这个问题,可以通过暴力反射的方式解决,也就是使用Filed.setAccessible(true)使private类型的成员变量也可以被获取值。

说完了成员变量,下面该轮到成员方法了

4.   成员方法类(Method类)

同样,Method类代表某个类中的一个成员方法。

1)        成员方法的获取方式:

通过         类的字节码对象.getMethod(方法名,参数类.class)

例如:Method charAt=Class.forName("java.lang.String").getMethod("charAt",int.class);

2)        成员方法的调用方式:

3)        通过         Method对象.invoke(对象实例,此方法的参数列表)

例如:

package learn.reflection;
 
import java.lang.reflect.Method;
 
public class ReflectDemo {
       public static void main(String... args) throws Exception {
              String str = "abcdef";
              Method method = str.getClass().getMethod("charAt", int.class);
              System.out.println("charAt常规调用方式:"+str.charAt(0));
              /* method.invoke(str, 0)中的0就是调用String类的charAt方法是的参数*/
              System.out.println("charAt反射调用方式:"+method.invoke(str, 0));
       }
}


输出结果为:

 

charAt常规调用方式:a
charAt反射调用方式:a

 

5.   其它知识

1)        数组与Object的关系及其反射类型

具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。

 例如

 

package learn.reflection;
 
public class ReflectTest {
 
       public static void main(String[] args) throws Exception {
              int[] a1 = new int[3];
              int[] a2 = new int[4];
              String[][] a3 = new String[2][3];
              String[][] a4 = new String[3][];
              System.out.println(a1.getClass() == a2.getClass());
              System.out.println(a3.getClass() == a4.getClass());
       }
}

输出为

true
true


注意:如果元素类型不同或者维数不同的话,那么编译器直接不通过。

代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。

用类加载器的方式管理资源和配置文件,实例:

package learn.reflection;
 
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
 
public class ReflectDemo {
       public static void main(String[] args) throws Exception {
              // InputStream is =new FileInputStream("config.properties");
 
              // 方式一:采用类加载器进行加载,使用相对路径的方式
              /*
               * InputStreamis=ReflectTest.class.getClassLoader().
               *getResourceAsStream("learn/reflect/config.properties");
               */
              // 方式二:利用Class方式进行加载,使用相对路径的方式
              // InputStream is =
              //ReflectTest.class.getResourceAsStream("config.properties");
 
              // 方式三:利用Class方式进行加载,使用绝对路径的方式
              InputStream is = ReflectDemo.class
                            .getResourceAsStream("/learn/reflection/config.properties");
 
              Properties p = new Properties();
              p.load(is);
              is.close();
 
              String className = p.getProperty("className");
              Collection collection = (Collection)Class.forName(className)
                            .newInstance();
              collection.add("字符串1");
              collection.add("字符串2");
              collection.add("字符串3");
              collection.add("字符串4");
              System.out.println(collection.size());
       }
}


配置文件如下:

className= java.util.ArrayList



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值