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

Java 反射机制

2018-02-01 21:46 274 查看
在我们使用形如 ClassName object = new ClassName(); 的语句进行创建实例的时候,我们默认 ClassName 已经在系统内存在的了,所以直接使用new 的方式进行创建。
事实上,在启动虚拟机的时候,我们定义在系统内的类通过类的加载器(class loader)加载到系统内。所以当系统运行的时候,我们就可以使用该类。
有时候,在系统运行的时候,类并没有存在在系统内,我们需要手动加载类文件到系统内,然后再使用它(创建对象或者使用其类方法等等)。


类的加载

在运行时期加载类,是通过 JVM 内的Class对象 的 Class.forName()方法将类定义加载到系统内,返回一个对应的类文件的表示对象。



例如,创建一个Animal的类,然后通过手动加载到内存中,代码如下:

[java] view
plain copy

print?

package com.lou;

public abstract class Animal {

private Integer age;

private double weight;

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

public double getWeight() {

return weight;

}

public void setWeight(double weight) {

this.weight = weight;

}

public abstract void walk();

}

[java] view
plain copy

print?

package com.lou;

public class Client {

public static void main(String[] args) {

try {

Class clazz = Class.forName("com.lou.Animal");

System.out.println("成功加载类文件 "+clazz.getSimpleName());

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}

}


一个class 定义有什么? 对应的class 对象有哪些组件?

我们定义一个Java 类,基本上会具有:注解(Annotation),成员字段(Field),Constructor(构造器),方法(Method),定义的内部类,自己的父类,实现的接口等。




构造器(Constructor)及使用Class 对象创建 实例对象

构造器的作用就是创建对象。一般创建构造器时,会有相应的描述符:public private 等;参数列表;抛出的异常信息。
我们通过Class 对象取得构造器对象时,要提供相应的构造器的参数列表。参数列表构造器的"id",每个构造器都不一样。如果提供没有定义过的参数列表,将会抛出异常。
构造器的价值就是用于创建出实例对象和初始化。使用 构造器的 newInstance(Object ... varArgs) 方法可以创建出一个实例对象。不过该方法返回的是Object 对象,要根据相应的Class对象向下转型。



如下面所示的例子,获取Worker 的构造器对象,并且创建对象

[java] view
plain copy

print?

package com.lou;

import java.math.BigDecimal;

public class Worker {

public String id;//工号

public String name;//姓名

public BigDecimal salary;//工资

private Worker()

{

}

public Worker(String id,String name)

{

this.id = id;

this.name = name;

}

public Worker(String id,String name,BigDecimal salary)

{

this(id,name);

this.salary = salary;

}

public void work()

{

System.out.println("Working......");

}

}

[java] view
plain copy

print?

package com.lou;

import java.lang.reflect.Constructor;

import java.lang.reflect.InvocationTargetException;

public class Client1 {

public static void main(String[] args) {

try {

Class clazz = Class.forName("com.lou.Worker");

System.out.println("成功加载类文件 "+clazz.getSimpleName());

//返回clazz 的公共构造器

Constructor[] constructors = clazz.getConstructors();

System.out.println("公共构造器共有 "+ constructors.length +" 个。");

System.out.println("它们分别是:");

for(Constructor cons : constructors)

{

System.out.println(cons.toGenericString());

}

//返回clazz 的指定公共构造器

Constructor constructor = clazz.getConstructor(String.class,String.class);

//通过newInstance(Object ...varArgs) 创建对象

Worker worker=(Worker)constructor.newInstance(new String("1234"),new String("louis"));

System.out.println(worker.id);

} catch (Exception e) {

e.printStackTrace();

}

}

}


成员字段(Field)及其操作

Field 域作为字段域,可以获取其 修饰符,字段的类型,还有变量名;通过field对象,可以设置和某个 实例对象对应的字段的值。



以上面的Worker类为例,获取和设置其实例对象的值:

[java] view
plain copy

print?

package com.lou;

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.math.BigDecimal;

public class Client1 {

public static void main(String[] args) {

try {

Class clazz = Class.forName("com.lou.Worker");

System.out.println("成功加载类文件 "+clazz.getSimpleName());

// 获取指定名称的可访问的field

Field field = clazz.getField("id");

System.out.println("Field " + field.getName() +"获取成功。");

Object o = clazz.getConstructor(String.class,String.class,BigDecimal.class).newInstance(new String("123"),new String("louis"),new BigDecimal(23));

// 取出对象内对应字段的值

String value = (String) field.get(o);

System.out.println("对象对应字段的值是:"+ value);

// 设置某个对象内对应字段的值

field.set(o, "321");

System.out.println("对象对应的字段的值被设定为:"+field.get(o));

} catch (Exception e) {

e.printStackTrace();

}

}

}


Method 及其相关操作

Method 对象代表类内的某个方法,可以获取其修饰符、返回的数据类型、方法名、抛出的异常类型、触发某个实例对象的这个方法。



[java] view
plain copy

print?

package com.lou;

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.math.BigDecimal;

public class Client1 {

public static void main(String[] args) {

try {

Class clazz = Class.forName("com.lou.Worker");

System.out.println("成功加载类文件 "+clazz.getSimpleName());

// 获取指定名称的可访问的field

Field field = clazz.getField("id");

System.out.println("Field " + field.getName() +"获取成功。");

Object o = clazz.getConstructor(String.class,String.class,BigDecimal.class).newInstance(new String("123"),new String("louis"),new BigDecimal(23));

// 获取类的某个可访问的方法:

Method method = clazz.getMethod("work", null);

System.out.println("Method " + method.getName() +"获取成功。");

// 触发调用Worker类对应的方法

method.invoke(o, null);

} catch (Exception e) {

e.printStackTrace();

}

}

}


附录:一个完整的例子:

一个Human 类继承自 抽象类Animal,实现接口Inventor,并且其上有注解 Author 和Function,现在通过以下的代码对Human类进行解析:

[java] view
plain copy

print?

package com.lou;

@Author(id = "4321",name ="luan",date="2014")

public abstract class Animal {

private Integer age;

private double weight;

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

public double getWeight() {

return weight;

}

public void setWeight(double weight) {

this.weight = weight;

}

public abstract void walk();

}

[java] view
plain copy

print?

package com.lou;

public interface Inventor {

// 发明者的实现方法

public void invent();

}

[java] view
plain copy

print?

package com.lou;

// 注解区域

@Author(id = "1234",name ="louis",date="2014")

@Function(value ="人类的类定义")

public class Human extends Animal implements Inventor{

//成员变量区 Fields

private String name;

private String color;

private String nationality;

public String birthDate;

// 构造器部分 Constructor

public Human()

{

}

private Human(String name)

{

this.name= name;

}

@Function(value = "构造器,带有三个参数")

public Human(String name,String color,String nationality) throws Exception

{

this(name);

this.color = color;

this.nationality = nationality;

}

public Human(String name,String color,String nationality,String birthDate) throws Exception

{

this(name,color,nationality);

this.birthDate = birthDate;

}

//方法区域

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getColor() {

return color;

}

public void setColor(String color) {

this.color = color;

}

@Override

public void invent() {

System.out.println("人类发明。。。。。");

}

@Override

public void walk() {

System.out.println("Using two legs");

}

@Author(id = "123",name ="luan",date="2014")

public void speak()

{

System.out.println("You are in "+ nationality +",You can speak "+nationality);

}

class Mind{}

}

[java] view
plain copy

print?

package com.lou;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

// 对书写的类或者方法进行标识

@Retention(value=RetentionPolicy.RUNTIME)

public @interface Author {

public String id() default "none";

public String name() default "none";

public String date();

}

[java] view
plain copy

print?

package com.lou;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

// 功能描述

@Retention(value=RetentionPolicy.RUNTIME)

@interface Function {

public String value() default "none";

}

Client 类的方法内对 Human 的 Annotation,Constructor,Field,Method 相关的操作进行了细致的示范。

[java] view
plain copy

print?

package com.lou;

import java.lang.annotation.Annotation;

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.lang.reflect.Modifier;

import java.lang.reflect.Type;

public class Client {

public static void main(String[] args) throws Exception {

// 注解操作

annotationsOperations();

//构造器操作

constructorOperations();

//字段操作

fieldOperations();

//方法操作

methodOperations();

}

/*

* 注解相关 操作

*/

private static void annotationsOperations() {

Class clazz = Human.class;

// 如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。

Author author = (Author) clazz.getAnnotation(Author.class);

System.out.println("这个类的作者是:" + author.name());

// 获取运用到当前class 上的所有 注解

Annotation[] annotations = clazz.getDeclaredAnnotations();

// Annotation[] annotations = clazz.getAnnotations();

System.out.println("Class " + clazz.getSimpleName() + " has "

+ annotations.length + " annotations。");

}

/*

* 构造器操作

*/

private static void constructorOperations() throws SecurityException,

NoSuchMethodException, IllegalArgumentException,

InstantiationException, IllegalAccessException,

InvocationTargetException {

Class clazz = Human.class;

/*

* Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个

* Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。

*/

Constructor constructor = clazz.getConstructor(null);// 无参构造器

Constructor constructor2 = clazz.getConstructor(String.class,

String.class, String.class); // 三个个String 类型的参数构造器

/*

* Constructor<?>[] getConstructors() 返回一个包含某些 Constructor 对象的数组,这些对象反映此

* Class 对象所表示的类的所有公共构造方法。

*/

Constructor[] cons = clazz.getConstructors();

System.out.println("公共构造器数量为:" + cons.length);

/*

* Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

* 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。

* 这个可以得到所有在这个类内定义的构造器,不管是不是public 的

*/

Constructor[] constructs = clazz.getDeclaredConstructors();

System.out.println("公共构造器数量为:" + constructs.length);

Constructor cons2 = clazz.getDeclaredConstructor(String.class,

String.class, String.class);

/*

* 构造器上的注解

*/

Annotation[] annos = cons2.getAnnotations();

System.out.println("此构造器上的注解个数为:" + annos.length);

Function function = (Function) cons2.getAnnotation(Function.class);

System.out.println("Function 注解:" + function.value());

/*

* 获取此构造器抛出来的异常信息

*/

Class[] exceptions = cons2.getExceptionTypes();

System.out.println("-----构造器抛出来的异常共有:" + exceptions.length

+ "个。如下:----");

for (Class c : exceptions) {

System.out.println(c.getSimpleName());

}

/*

* Type[] getGenericExceptionTypes() 返回一组 Type 对象,这些对象表示声明要由此

* Constructor 对象抛出的异常

*/

System.out.println("----------构造器的Generic Exception Types-----------");

Type[] types = cons2.getGenericExceptionTypes();

for (Type t : types) {

System.out.println(t.toString());

}

/*

* Type[] getGenericParameterTypes() 按照声明顺序返回一组 Type 对象,这些对象表示此

* Constructor 对象所表示的方法的形参类型。

*/

System.out.println("------构造器方法形参的数据类型数组为:-------------");

Type[] types2 = cons2.getGenericParameterTypes();

for (Type t : types2) {

System.out.println(t.toString());

}

/*

* Class<?>[] getParameterTypes() 按照声明顺序返回一组 Class 对象,这些对象表示此

* Constructor 对象所表示构造方法的形参类型。

*/

/*

* public int getModifiers() 以整数形式返回此 Constructor 对象所表示构造方法的 Java

* 语言修饰符。应该使用 Modifier 类对这些修饰符进行解码。

*/

System.out.println("---------构造器的修饰符是:-----------");

System.out.println(cons2.getModifiers());

if (Modifier.isPublic(cons2.getModifiers())) {

System.out.println("Modifier is public ");

}

/*

* String getName() 以字符串形式返回此构造方法的名称。

*/

String constructorName = cons2.getName();

System.out.println("------此构造器的名称是:" + constructorName);

/*

* Class<T> getDeclaringClass() 返回 Class 对象,该对象表示声明由此 Constructor

* 对象表示的构造方法的类。 即返回这个构造器所在的类

*/

Class parent = cons2.getDeclaringClass();

System.out.println("----------此构造器所在的类是:");

System.out.println(parent.getCanonicalName());

/*

* boolean isVarArgs() 如果声明此构造方法可以带可变数量的参数,则返回 true;否则返回 false。

*/

System.out.println("参数列表是否支持可变变量:" + cons2.isVarArgs());

/*

* 通过constructor对象创建对象 T newInstance(Object... initargs)

*/

Human human = (Human) cons2.newInstance(new String("louis"),

new String("yellow"), new String("nationality"));

System.out.println("Human's name is " + human.getName());

}

/*

* Field 相关操作

*/

private static void fieldOperations() throws Exception

{

Class clazz = Human.class;

//获取类内定义的所有成员字段,返回一个数组,包括私有成员字段

Field[] fields = clazz.getDeclaredFields();

System.out.println(clazz.getSimpleName()+" 定义的成员字段共有:"+fields.length);

//获取类内定义的 指定名称的成员字段 包括私有成员字段

Field field = clazz.getDeclaredField("name");

//返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。

//包括从父类继承过来的可访问的字段

Field[] fields1 = clazz.getFields();

//返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。

//包括从父类继承过来的可访问的字段

Field field2 = clazz.getField("birthDate");

Human human = new Human("lou","yellow","China","1991-10");

/*

* 获取human对象的对应字段的值

* Object get(Object obj)

* XXX getXXX(Object obj) XXX Boolean,Byte,Char,Double,Long,Float,Int,Short 等

*/

Object value = field2.get(human);

System.out.println("human对应字段 " + field2.getName() +" 的值为:"+value);

/*

* 设置human对象的对应字段的值

* void set(Object obj, Object value)

* void setXXX(Object obj, XXX z) XXX Boolean,Byte,Char,Double,Long,Float,Int,Short 等

*/

field2.set(human, "1990-1");

System.out.println("human 对象的值已经被设定为:"+field2.get(human));

/*

* 获取成员字段的数据类型

* Class<?> getType()

* Type getGenericType()

*/

Class type = field2.getType();

System.out.println("字段"+field2.getName()+" 的类型是:"+type.getName());

}

/*

* Method 相关操作

*/

private static void methodOperations() throws Exception

{

Class clazz = Human.class;

//返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。

Method[] methods = clazz.getMethods();

//返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。

Method method = clazz.getMethod("speak", null);

//返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

Method[] methods2 = clazz.getDeclaredMethods();

//返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法

Method method2 = clazz.getDeclaredMethod("walk", null);

/*

* Object invoke(Object obj, Object... args)

* 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。

* 调用某个对象的这个方法

*/

Human human = new Human("lou","yellow","China","1991-10");

method2.invoke(human, null);

}

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