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

关于Java的反射

2015-10-10 10:57 393 查看
I. 前言:

1> 反射相关的主要API:

java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法

2> 对于Class类的理解:

Class本身也是一个类Class 对象只能由系统建立对象

一个类在 JVM 中只会有一个Class实例 

一个Class对象对应的是一个加载到JVM中的一个.class文件

每个类的实例都会记得自己是由哪个 Class 实例所生成

通过Class可以完整地得到一个类中的完整结构 

3> 实例化Class类对象(四种方法)
1)前提:若已知具体的类,通过类的class属性获取,该方法 

                    最为安全可靠,程序性能最高

       实例:Class clazz = String.class;

2)前提:已知某个类的实例,调用该实例的getClass()方法获

                    取Class对象

       实例:Class clazz = “www.atguigu.com”.getClass();

3)前提:已知一个类的全类名,且该类在类路径下,可通过

       Class类的静态方法forName()获取,可能抛出ClassNotFoundException

       实例:Class clazz = Class.forName(“java.lang.String”);

4)其他方式(不做要求)

ClassLoader cl = this.getClass().getClassLoader();

Class clazz4 = cl.loadClass(“类的全类名”);

4> 创建类对象并获取类的完整结构
    4.1>调用Class对象的newInstance()方法
            要  求:1)类必须有一个无参数的构造器。
                        2)类的构造器的访问权限需要足够。
    4.2> 指定形参类型的构造器,步骤如下:
        1)通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
        2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
        3)通过Constructor实例化对象。

5> 全部的构造器
public Constructor<T>[] getConstructors()
返回此 Class 对象所表示的类的所有public构造方法,并不能获取父类的构造器。
public Constructor<T>[] getDeclaredConstructors()
返回此 Class 对象表示的类声明的所有构造方法,并不能获取父类的构造器

Constructor类中:
取得修饰符: public int getModifiers();
取得方法名称: public String getName();
取得参数的类型:public Class<?>[] getParameterTypes();

6> 全部的方法
public Method[] getDeclaredMethods()
可以获取运行时类及其父类的所有声明为public的方法
public Method[] getMethods()  
可以获取运行时类本身声明的所有的方法

Method类中:
public Class<?> getReturnType()取得全部的返回值
public Class<?>[] getParameterTypes()取得全部的参数
public int getModifiers()取得修饰符
public Class<?>[] getExceptionTypes()取得异常信息

7> 全部的Field
public Field[] getFields() 
只能获取到运行时类的所有的声明为public的属性,包括其父类的public的属性
public Field[] getDeclaredFields() 
只能获取到运行时类身的所有的属性,而不能得到父类的属性

Field方法中:
public int getModifiers()  以整数形式返回此Field的修饰符
public Class<?> getType()  得到Field的属性类型
public String getName()  返回Field的名称。

8> 调用指定方法
通过反射,调用类中的方法,通过Method类完成。步骤:

8.1>通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

8.2>之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

    8.2.1> Object 对应原方法的返回值,若原方法无返回值,此时返回null

    8.2.2> 若原方法若为静态方法,此时形参Object obj可为null

    8.2.3> 若原方法形参列表为空,则Object[] args为null

    8.2.4> 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

9> 调用指定属性

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
public Field getField(String name)
只能获取到运行时类的所有的声明为public的属性,包括其父类的
public Field getDeclaredField(String name)只能获取到运行时类本身的所有的属性,而不能得到父类的属性

在Field中:
public Object get(Object obj) 取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容

注:在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。
public void setAccessible(true)访问私有属性时,让这个属性可见。

II. 代码示例如下:

1> 实例化Class类对象(四种方法)


代码示例如下:
package com.atguigu.java;
/*
* 1.如何创建Class的实例(掌握)
* 2.如何获取Class对象对应的运行时类的完整的结构(理解)
* 3.如何调用Class对象对应的运行时类的指定的属性、方法(掌握)
* 4.动态代理(反射的一个应用)
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;

import org.junit.Test;

public class TestReflection {
// 创建Class实例的方法(四种方法)(重点)
@Test
public void test4() throws ClassNotFoundException {
// 1.调用运行时类的.class属性
Class clazz1 = Person.class;
System.out.println(clazz1);

Class clazz2 = Creator.class;
System.out.println(clazz2);
// 2.通过运行时类的对象,调用其getClass()方法
Person p = new Person();
Class clazz3 = p.getClass();
System.out.println(clazz3);
// 3.调用Class的静态方法forName(String className)。此方法报ClassNotFoundException
String className = "com.atguigu.java.Person";
Class clazz4 = Class.forName(className);
System.out.println(clazz4);
// 4.了解:通过类的加载器
ClassLoader loader = this.getClass().getClassLoader();
Class clazz5 = loader.loadClass(className);
System.out.println(clazz5);
}

/*
* java.lang.Class类 1.Class类是反射的源头!
* 2.Class的一个对象,对应着一个运行时类。相当于一个运行时类本身充当了Class的一个实例。
* 3.过程:源文件经过编译(javac.exe)以后,得到一个或多个.class文件。
* .class文件经过运行(java.exe)这步,就需要进行类的加载(通过JVM的类的加载器),
* 记载到内存中的缓存。每一个放入缓存中的.class文件就是一个Class的实例!
*/
@Test
public void test3() {
Person p = new Person();
Class clazz = p.getClass();// 获取p对应对应的类:Person类。相当于Person类本身充当了Class的实例
}

// 有了反射以后
// 看成是关于反射的一个应用
@Test
public void test2() throws Exception {
Class clazz = Person.class;
// 使用反射可以创建一个类的对象
Object obj = clazz.newInstance();
Person p = (Person) obj;
System.out.println(p);
// 可以通过反射,获取对应的运行时类的结构:属性、方法、。。。
Field name = clazz.getField("name");
name.set(p, "李雷");

Field age = clazz.getField("age");
age.set(p, 24);
System.out.println(p);

Method show = clazz.getMethod("show");
show.invoke(p);
}

// 反射以前,如何创建一个类的对象
@Test
public void test1() {
Person p = new Person();
p.setName("韩梅梅");
p.setAge(23);
System.out.println(p);
}
}




2> 有了Class实例之后,我们可以做如下事情:

2.1> 创建对应的类的对象

代码示例一如下:
package com.atguigu.java;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import org.junit.Test;

public class TestClass{

//不带参的
@Test
public void test1() throws Exception{

//1.根据全类名获取对应的Class对象
String className = "com.atguigu.java.Person";
Class clazz = Class.forName(className);

//2.通过Class的实例创建对应类的对象
Object obj = clazz.newInstance();   //创建对应类的对象,在本例中,newInstance()调用的就是空参的person构造器
Person p1 = (Person)obj;
System.out.println(p1);
}

//带参的
@Test
public void test2() throws Exception{
//1.根据全类名获取对应的Class对象
String name = "com.atguigu.java.Person";
Class clazz = null;
clazz = Class.forName(name);
//2.调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通过Constructor的实例创建对应类的对象,并初始化类属性
Person p2 = (Person)con.newInstance("Peter",20);  ////创建对应类的对象,在本例中,newInstance("Peter","20")调用的就是带参的person构造器
System.out.println(p2);
}
}


注://不管是对于不带参还是不带参的,若想要对应的对象能够创建成功,首先需要有对应的构造器,
//然后还需要构造器的权限修饰符要足够,足够其意思是:在本类中可以访问得到对应类的构造器



2.2> 获取及调用属性
代码示例如下:
package com.atguigu.java;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import org.junit.Test;

public class TestFields {
//获取运行时类的属性

@Test
public void test1(){
Class clazz = Person.class;
//getFields():只能获取到运行时类的所有的声明为public的属性,包括其父类的
Field[] fields = clazz.getFields();
for(int i = 0;i < fields.length;i++){
System.out.println(fields[i].getName());
}
//getDeclaredFields():可以获取到运行时类本身的所有的属性
System.out.println();
Field[] fields1 = clazz.getDeclaredFields();
for(int i = 0;i < fields1.length;i++){
System.out.println(fields1[i].getName());
}
}
//权限修饰符  数据类型  变量名
@Test
public void test2(){
Class clazz = Person.class;
Field[] fields = clazz.getDeclaredFields();
for(int i = 0;i < fields.length;i++){
//属性的权限修饰符
int m = fields[i].getModifiers();
String mm = Modifier.toString(m);
System.out.print(mm + "\t");
//数据类型
Class type = fields[i].getType();
System.out.print(type.getName() + "\t");
//变量名
System.out.print(fields[i].getName());
System.out.println();
}
}
//调用对应运行时类的指定的属性(掌握)
@Test
public void test3() throws Exception{
Class clazz = Person.class;

Person p = (Person)clazz.newInstance();
System.out.println(p);
//1.getField(String fieldName):获取指定属性名的属性(仅限于public声明的)
Field name = clazz.getField("name");
//set(Object obj,fieldVal):将obj对象的当前属性指定值为fieldVal
name.set(p, "高鹏");
System.out.println(p);
//get(Object obj):获取obj对象的当前属性的值
System.out.println(name.get(p));

//***************
//getDeclaredField(String fieldName):获取对应的运行时类的名为fieldName的属性(不用考虑权限修饰符)
Field age = clazz.getDeclaredField("age");
//setAccessible():要想操作声明为非public的属性,需要在调用前执行此方法,操作为true
age.setAccessible(true);
age.set(p, 23);
System.out.println(p);
}
}


2.3> 获取及调用方法

代码示例如下:
package com.atguigu.java;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.junit.Test;

public class TestMethods {
//获取运行时类的所有的方法
@Test
public void test1(){
Class clazz = Person.class;
//getMethods():可以获取运行时类及其父类的所有声明为public的方法
Method[] m1 = clazz.getMethods();
for(int i = 0;i < m1.length;i++){
System.out.println(m1[i].getName());
}
System.out.println();
//getDeclaredMethods():可以获取运行时类本身声明的所有的方法
Method[] m2 = clazz.getDeclaredMethods();
for(int i = 0;i < m2.length;i++){
System.out.println(m2[i]);
}
}
//注解  权限修饰符  返回值类型 方法名 (参数类型1  参数名1,参数类型2 参数名2 ,。。。)异常
@Test
public void test2(){
Class clazz = Person.class;
Method[] methods = clazz.getDeclaredMethods();
for(int i = 0;i < methods.length;i++){
//0.注解
Annotation[] annos = methods[i].getAnnotations();
for(int j = 0;j < annos.length;j++){
System.out.println(annos[j]);
}
//1.权限修饰符
System.out.print(Modifier.toString(methods[i].getModifiers()) + "\t");
//2.返回值类型
Class returnType = methods[i].getReturnType();
System.out.print(returnType.getName() + "\t");
//3.方法名
System.out.print(methods[i].getName() + "(");
//4.参数列表
Class[] paras = methods[i].getParameterTypes();
for(int j = 0;j < paras.length;j++){
System.out.print(paras[j].getName() + " args-" + j);
}
System.out.print(")");
//5.抛出的异常
Class[] excep = methods[i].getExceptionTypes();
for(int j = 0;j < excep.length;j++){
System.out.print(excep[j].getName());
}
System.out.println();
}
}

//调用指定的方法(掌握)
@Test
public void test3() throws Exception{
Class clazz = Person.class;

Person p = (Person)clazz.newInstance();
//getMethod(String methodName,ParaType...t):获取运行时类指定名为methodName,参数列表为t的方法(仅限于public)
Method m1 = clazz.getMethod("display", String.class);
//Object invoke(Object obj,RealVal... r):调用obj对象的当前方法,并且传入实参:r
//此invoke()方法的返回值即为当前名为methodName的返回值。
Object returnVal = m1.invoke(p,"中华人民共和国");//类似于:对象.方法(实参)
System.out.println(returnVal);

Method m2 = clazz.getMethod("getAge");
Object obj = m2.invoke(p);
System.out.println(obj);
//***********
//getDeclaredMethod(String methodName,ParaType...t):可以获取运行时类任何权限修饰符修饰的方法
Method m3 = clazz.getDeclaredMethod("info");
//setAccessible(true):对于非public的方法,如果想调用,需要进行此步骤
m3.setAccessible(true);
Object returnVal1 = m3.invoke(p);
System.out.println(returnVal1);
}
} <strong>
</strong>


2.4> 获取及调用构造器

代码示例如下:
package com.atguigu.java;

import java.lang.reflect.Constructor;

import org.junit.Test;

public class getConstructors {
//获取运行时类所有的构造器
@Test
public void test1() throws Exception{
Class clazz = Class.forName("com.atguigu.java.Person");
//getConstructors():只能获取本类中声明为public的构造器
Constructor[] cons1 = clazz.getConstructors();
for(int i = 0;i < cons1.length;i++){
System.out.println(cons1[i]);
}
//getDeclaredConstructors():获取本类中声明的所有的构造器
System.out.println();
Constructor[] cons2 = clazz.getDeclaredConstructors();
for(int i = 0;i < cons2.length;i++){
System.out.println(cons2[i]);
}
}

//调用运行时类的构造器
@Test
public void test2() throws Exception{
String className = "com.atguigu.java.Person";
Class clazz = Class.forName(className);
//1.根据全类名获取对应的Class对象
String name = "com.atguigu.java.Person";
Class clazz = null;
clazz = Class.forName(name);
//2.调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class,Integer.class);
con.setAccessible(true);  //
//3.通过Constructor的实例创建对应类的对象,并初始化类属性
Person p2 = (Person)con.newInstance("Peter",20);  ////创建对应类的对象,在本例中,newInstance("Peter","20")调用的就是带参的person构造器
System.out.println(p2);
}
}


2.5>获取其他信息

代码示例如下:
package com.atguigu.java;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.annotation.Annotation;
import org.junit.Test;

public class TestOther {

//获取运行时类的内部类
@Test
public void test7() throws Exception{
Class clazz = Class.forName("com.atguigu.java.Person");
Class[] innerClass = clazz.getDeclaredClasses();
for(int i = 0;i < innerClass.length;i++){
System.out.println(innerClass[i]);
}
}
//获取运行时类的接口
@Test
public void test6() throws Exception{
Class clazz = Class.forName("com.atguigu.java.Person");
Class[] interfaces = clazz.getInterfaces();
for(int i = 0;i < interfaces.length;i++){
System.out.println(interfaces[i].getName());
}
}
//获取类的注解
@Test
public void test5(){
Class clazz = Person.class;
Annotation[] anns = clazz.getAnnotations();
for(int i = 0;i < anns.length;i++){
System.out.println(anns[i]);
}
}

//获取运行时类所在的包
@Test
public void test4(){
Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
}

//获取运行时类的父类的泛型
@Test
public void test3(){
Class clazz = Person.class;
Type t = clazz.getGenericSuperclass();
ParameterizedType p = (ParameterizedType)t;
Type[] t1 = p.getActualTypeArguments();
System.out.println(((Class)t1[0]).getName());
}

//获取带泛型的父类
@Test
public void test2(){
Class clazz = Person.class;
Type t = clazz.getGenericSuperclass();
System.out.println(t);
}
//获取运行时类的父类
@Test
public void test1(){
Class clazz = Person.class;
Class clazz1 = clazz.getSuperclass();
System.out.println(clazz1);
}
}


Person类的代码如下:
package com.atguigu.java;

@MyAnnotation(values = "atguigu")
public class Person extends Creator<String> implements Comparable,MyInterface{
public String name;
private int age = 12;
int id;

private Person(String name){
this.name = name;
}

public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}

@MyAnnotation(values = "hello")
public void show() throws Exception{
System.out.println("我是一个人");
}

public void display(String nation){
System.out.println("我的国籍是:" + nation);
}
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
return 0;
}

private String info(){
return "今天北京天气不错!";
}
//内部类
class Bird{

}
}


Creator的代码示例如下:
package com.atguigu.java;

public class Creator<T> {
public int legs;
public void breath(){
System.out.println("呼吸。。。");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: