您的位置:首页 > 编程语言 > Java开发

Java反射机制的学习

2016-06-24 20:34 453 查看

概念

反射就是把Java的各种成分映射成相应的Java类。

  Class类的构造方法是private,由JVM创建。

  反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取他所有的成员变量和方法并且显示出来。Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。 (来自Sun)

  JavaBean 是 reflection 的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。

  反射是从1.2就有的,后面的三大框架都会用到反射机制,涉及到类”Class”,无法直接new CLass(),其对象是内存里的一份字节码.  

  Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。 

从Class类说起 

如果你使用Java,那么你应该知道Java中有一个Class类。Class类本身表示Java对象的类型,我们可以通过一个Object(子)对象的getClass方法取得一个对象的类型,此函数返回的就是一个Class类。当然,获得Class对象的方法有许多,但是没有一种方法是通过Class的构造函数来生成Class对象的。

也许你从来没有使用过Class类,也许你曾以为这是一个没什么用处的东西。不管你以前怎么认为,Class类是整个Java反射机制的源头。一切关于Java反射的故事,都从Class类开始。

因此,要想使用Java反射,我们首先得到Class类的对象。下表列出了几种得到Class类的方法,以供大家参考。

//运用getClass()
String str = "abc";
Class c1 = str.getClass();
//运用Class.getSuperclass()

Button b = new Button();

Class c1 = b.getClass();

Class c2 = c1.getSuperclass();

//运用static method Class.forName()

//(最常被使用)
Class c1 = Class.forName ("java.lang.String");

Class c2 = Class.forName ("java.awt.Button");

Class c3 = Class.forName ("java.util.LinkedList$Entry");
Class c4 = Class.forName ("I");
//[I,中括号表示数组,I表示整数
Class c5 = Class.forName ("[I");
//运用 .class 语法
Class c1 = String.class;

Class c2 = java.awt.Button.class;

Class c3 = Main.InnerClass.class;

Class c4 = int.class;

Class c5 = int[].class;

//运用 primitive wrapper classes 的TYPE 语法
Class c1 = Boolean.TYPE;

Class c2 = Byte.TYPE;

Class c3 = Character.TYPE;

Class c4 = Short.TYPE;

Class c5 = Integer.TYPE;

Class c6 = Long.TYPE;

Class c7 = Float.TYPE;

Class c8 = Double.TYPE;

Class c9 = Void.TYPE;


获取一些基本信息

在我们得到一个类的Class类对象之后,Java反射机制就可以大施拳脚了。首先让我们来了解下如何获取关于某一个类的一些基本信息。



上表中,列出了一些Java class内部信息的获取方式。所采用的方法几乎都是调用Class对象的成员方法(由此你就可以了解到Class类的用处了吧)。当然,表中所列出的信息并不是全部,有很大一部分没有列出,你可以通过查阅Java文档得到更全面的了解。另外,下面将重点介绍一下类的构造函数、域和成员方法的获取方式。

类中最重要的三个信息

如果要对一个类的信息重要性进行排名的话,那么这三个信息理应获得前三的名次。它们分别是:构造函数、成员函数、成员变量。

下面,让我们分别了解一下这三个重要信息的获取方式。

构造函数

如果我们将Java对象视为一个二进制的生活在内存中生命体的话,那么构造函数无疑可以类比为Java对象生命体的诞生过程。我们在构造函数调用时为对象分配内存空间,初始化一些属性,于是一个新的生命诞生了。

Java是纯面向对象的语言,Java中几乎所有的一切都是类的对象,因此可想而知构造函数的重要性。

Java反射机制能够得到构造函数信息实在应该是一件令人惊喜的事情。正因为此,反射机制实质上才拥有了孵化生命的能力。换句话言之,我们可以通过反射机制,动态地创建新的对象。

获取构造函数的方法有以下几个:

Constructor getConstructor(Class[] params)

Constructor[] getConstructors()

Constructor getDeclaredConstructor(Class[] params)

Constructor[] getDeclaredConstructors()


我们有两种方式对这四个函数分组。

首先可以由构造函数的确定性进行分类。我们知道,一个类实际上可以拥有很多个构造函数。那么我们获取的构造函数是哪个呢?我们可以根据构造函数的参数标签对构造函数进行明确的区分,因此,如果我们在Java反射时指定构造函数的参数,那么我们就能确定地返回我们需要的那个“唯一”的构造函数。getConstructor(Class[] params) 和getDeclaredConstructor(Class[] params)正是这种确定唯一性的方式。但是,如果我们不清楚每个构造函数的参数表,或者我们出于某种目的需要获取所有的构造函数的信息,那么我们就不需要明确指定参数表,而这时返回的就应该是构造函数数组,因为构造函数很可能不止一个。getConstructors()和getDeclaredConstructors()就是这种方式。

另外,我们还可以通过构造函数的访问权限进行分类。在设计类的时候,我们往往有一些构造函数需要声明为“private”、“protect”或者“default”,目的是为了不让外部的类调用此构造函数生成对象。于是,基于访问权限的不同,我们可以将构造函数分为public和非public两种。

getConstructor(Class[] params) 和getConstructors()仅仅可以获取到public的构造函数,而getDeclaredConstructor(Class[] params) 和getDeclaredConstructors()则能获取所有(包括public和非public)的构造函数。

成员函数

如果构造函数类比为对象的诞生过程的话,成员函数无疑可以类比为对象的生命行为过程。成员函数的调用执行才是绝大多数对象存在的证据和意义。Java反射机制允许获取成员函数(或者说成员方法)的信息,也就是说,反射机制能够帮助对象践行生命意义。通俗地说,Java反射能使对象完成其相应的功能。

和获取构造函数的方法类似,获取成员函数的方法有以下一些:

Method getMethod(String name, Class[] params)

Method[] getMethods()

Method getDeclaredMethod(String name, Class[] params)

Method[] getDeclaredMethods()

其中需要注意,String name参数,需要写入方法名。关于访问权限和确定性的问题,和构造函数基本一致。

成员变量

成员变量,我们经常叫做一个对象的域。从内存的角度来说,构造函数和成员函数都仅仅是Java对象的行为或过程,而成员变量则是真正构成对象本身的细胞和血肉。简单的说,就是成员变量占用的空间之和几乎就是对象占用的所有内存空间。

获取成员变量的方法与上面两种方法类似,具体如下:

Field getField(String name)

Field[] getFields()

Field getDeclaredField(String name)

Field[] getDeclaredFields()

其中,String name参数,需要写入变量名。关于访问权限和确定性的问题,与前面两例基本一致。

测试

首先,准备工作先做好

测试用的类

package com.classdemo.test;

public interface MyInterface {

}


package com.classdemo.test.hh;

public class Father<T> {
public String say(String h){
return h;
}

}


package com.classdemo.test;

import java.io.Serializable;

import com.classdemo.test.hh.Father;
import com.fanshe.test.Book;

public class Person<Stirng> extends Father<Book> implements MyInterface, Serializable {

public String name;

private int age;

public Person() {
}

private Person(String name, int age) {
super();
this.name = name;
this.age = age;
}

public Person(String name) {
super();
this.name = name;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}

public void setName(String name) {
this.name = name;
}

public void setAge(int age) {
this.age = age;
}

public static void show() {
System.out.println("我是一个人");
}

// 静态方法
private static String display(String nation, int i) {
return "我的国籍是:" + nation + ",我的年龄:" + i;
}

@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}

// 公有内部类
public class Child01 {

}

// 公有内部类
public class Child02 {
}

// 私有内部类
private class Child03 {
}

}


得到公用方法

public void test01() {
Class clazz = Person.class;
Method[] method = clazz.getMethods();
System.out.println("打印出公有方法");
for (Method m : method) {
System.out.println(m);
}
}


我们看到包括父类的一些方法都被打印了出来

打印出公有方法
public java.lang.String com.classdemo.test.Person.toString()
public java.lang.String com.classdemo.test.Person.getName()
public void com.classdemo.test.Person.setName(java.lang.String)
public static void com.classdemo.test.Person.show()
public void com.classdemo.test.Person.setAge(int)
public int com.classdemo.test.Person.getAge()
public java.lang.String com.classdemo.test.hh.Father.say(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()


得到类中的所有方法

public void test02()
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
Class clazz = Person.class;
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods) {
// 获取 权限修饰符
/*这里我们不直接通过
* System.out.println(m);
* 的方法来输出方法的属性,我们来分别
* 通过以下方法去获取方法的属性
* */

System.out.print(Modifier.toString(m.getModifiers()) + " ");

// 返回值类型
Class reType = m.getReturnType();
System.out.print(reType.getName() + " ");
// 方法名
String methodName = m.getName();
System.out.print(methodName);
// 形参
System.out.print("(");
Class[] params = m.getParameterTypes();
for (int i = 0; i < params.length; i++) {
System.out.print(params[i].getName() + " args" + i + " ");
}
System.out.print(")");
System.out.println();
}

}


public java.lang.String com.classdemo.test.Person.toString()
public java.lang.String com.classdemo.test.Person.getName()
public void com.classdemo.test.Person.setName(java.lang.String)
private static java.lang.String com.classdemo.test.Person.display(java.lang.String,int)
public static void com.classdemo.test.Person.show()
public void com.classdemo.test.Person.setAge(int)
public int com.classdemo.test.Person.getAge()


这里通过clazz.getDeclaredMethods()的方式获取方法只能得到本类的方法,关于
Modifier.toString(m.getModifiers()


类Class、Method、Constructor、Field都有一个public方法int getModifiers()。该方法返回一个int类型的数,表示被修饰对象( Class、 Method、 Constructor、 Field )的修饰类型的组合值。

在开发文档中,可以查阅到,Modifier类中定义了若干特定的修饰域,每个修饰域都是一个固定的int数值,列表如下:



该类不仅提供了若干用于判断是否拥有某中修饰域的方法boolean isXXXXX(int modifiers),还提供一个String toString(int modifier)方法,用于将一个表示修饰域组合值的int数转换成描述修饰域的字符串。

Modifier.toString(m.getModifiers()


构造函数

// 测试构造器
public void test03() throws NoSuchMethodException, SecurityException {
Class clazz = Person.class;
Constructor[] con = clazz.getDeclaredConstructors();
for (Constructor c : con) {
System.out.println(c);
}
// 得到具体的某个构造器 其参数是 string int
Constructor c = clazz.getDeclaredConstructor(String.class, int.class);

/*
* 反射机制通过void setAccessible(boolean
* flag)方法可以得到一个类的private的方法和属性,使用这些private
* 的方法和属性,已经可以做一些超越限制的事情了。所以在
* 使用过程,还需谨慎啊!
*/
c.setAccessible(true); // 抑制Java对修饰符的检查
// 接下来我们就可以使用私有的构造函数 来创建对象了
try {
Person p = (Person) c.newInstance("peter", 12);
System.out.println(p);
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {

e.printStackTrace();
}

}


public com.classdemo.test.Person(java.lang.String)
private com.classdemo.test.Person(java.lang.String,int)
public com.classdemo.test.Person()
Person [name=peter, age=12]


运行时父类

public void test04() throws ClassNotFoundException {
Class clazz = Class.forName("com.classdemo.test.Person");
// 该方法可以得到父类的class对象
Class clazzParent = clazz.getSuperclass();
System.out.println(clazzParent.getName());
// 带泛型的父类
// 可以在得到父类class对象时同时得到父类的泛型对象
Type type = clazz.getGenericSuperclass();
System.out.println(type);
}


com.classdemo.test.hh.Father
com.classdemo.test.hh.Father<com.fanshe.test.Book>


获取父类的泛型

// 获取父类的泛型
public void test05() throws ClassNotFoundException {
Class clazz = Class.forName("com.classdemo.test.Person");

Type superClass = clazz.getGenericSuperclass();
if(superClass instanceof ParameterizedType){
ParameterizedType parameterizedType = (ParameterizedType) superClass;
Type[] typeArgs = parameterizedType.getActualTypeArguments();
if(typeArgs != null && typeArgs.length>0){
if(typeArgs[0] instanceof Class){
System.out.println("dao类中的构造方法泛型类型:"+((Class)typeArgs[0]).getName());
}
}
}

}


dao类中的构造方法泛型类型:com.fanshe.test.Book


获取实现的接口

// 获取实现的接口
public void test06() throws ClassNotFoundException {
ClassLoader cl = this.getClass().getClassLoader();
Class clazz = cl.loadClass("com.classdemo.test.Person");
Class[] clazzes = clazz.getInterfaces();
for (Class c : clazzes) {
System.out.println(c.getName());
}
}


com.classdemo.test.MyInterface
java.io.Serializable


获取所在的包

// 获取所在的包
public void test07() throws ClassNotFoundException {
Class clazz = Class.forName("com.classdemo.test.Person");
Package pack = clazz.getPackage();
System.out.println(pack.getName());
}


com.classdemo.test


内部类

// 获取  内部类
public void test08() {
Class clazz = Person.class;
Class[] clazzes = clazz.getClasses();
for (Class c : clazzes) {
System.out.println(c.getName());
}
System.out.println();
Class[] clazzes02 = clazz.getDeclaredClasses(); // 返回所有权限的内部类
for (Class c : clazzes02) {
System.out.print(Modifier.toString(c.getModifiers()) + " "); // 权限修饰符
System.out.println(c.getName());

}
}


com.classdemo.test.Person$Child01
com.classdemo.test.Person$Child02

public com.classdemo.test.Person$Child01
public com.classdemo.test.Person$Child02
private com.classdemo.test.Person$Child03


运行类的指定的属性

// 调用运行类的指定的属性
public void test09()
throws NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException {
Class clazz = Person.class;
Field field = clazz.getDeclaredField("name");
Field age = clazz.getDeclaredField("age"); // 该属性为私有属性要访问需
age.setAccessible(true);
Person p = (Person) clazz.newInstance();
field.set(p, "jim");
age.set(p, 10);
System.out.println(p);
}
// Person [name=jim, age=10]


调用指定的方法

/**
* 调用指定的方法
*/
public void test10() throws Exception, InvocationTargetException {
Class clazz = Person.class;
Person p = (Person) clazz.newInstance();
Method method = clazz.getDeclaredMethod("show");
Object o = method.invoke(p);
System.out.println(o); // 若无返回值则为null
Method methoddisplay = clazz.getDeclaredMethod("display", String.class, int.class);
methoddisplay.setAccessible(true); // 抑制抑制Java对修饰符的检查
o = methoddisplay.invoke(p, "中国", 10);
System.out.println(o); // 我的国籍是:中国,我的年龄:10

}


我是一个人
null
我的国籍是:中国,我的年龄:10


数组的反射

Array工具类用于完成数组的反射操作。

  同类型同纬度有相同的字节码。

  int.class和Integer.class不是同一份字节码,Integer.TYPE,TYPE代表包装类对应的基本类的字节码 int.class==Integer.TYPE

import java.util.Arrays;

/*
* 从这个例子看出即便字节码相同但是对象也不一定相同,根本不是一回事
*
*/
public class TestReflect {
public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
int[] a = new int[3];
int[] b = new int[]{4,5,5};//直接赋值后不可以指定长度,否则CE
int[][] c = new int[3][2];
String[] d = new String[]{"jjj","kkkk"};
System.out.println(a==b);//false
System.out.println(a.getClass()==b.getClass());//true
//System.out.println(a.getClass()==d.getClass());    //比较字节码a和cd也没法比
System.out.println(a.getClass());//输出class [I
System.out.println(a.getClass().getName());//输出[I,中括号表示数组,I表示整数

System.out.println(a.getClass().getSuperclass());//输出class java.lang.Object
System.out.println(d.getClass().getSuperclass());//输出class java.lang.Object

//由于父类都是Object,下面都是可以的
Object obj1 = a;//不可是Object[]
Object obj2 = b;
Object[] obj3 = c;//基本类型的一位数组只可以当做Object,非得还可以当做Object[]
Object obj4 = d;

//注意asList处理int[]和String[]的区别
System.out.println(Arrays.asList(b));//1.4没有可变参数,使用的是数组,[[I@1bc4459]
System.out.println(Arrays.asList(d));//[jjj, kkkk]

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: