您的位置:首页 > 职场人生

黑马程序员---Java反射

2015-10-15 09:44 423 查看
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

2015.10.15

Class类

Class类的实例表示正在运行的类和接口。Class实例不可以显示创建,而是在类被加载时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

JVM的内存区,除了包含栈,堆外,还有方法区,这是系统分配给JVM的一个存储类型信息的逻辑内存。存储的类型信息包括以下几部分:

1:类的基本信息:使用的JDK版本号。

2:类的详细信息:

(1)常量池:存储与该类有关的所有常量,例如final变量、类名、方法名等。

(2)字段信息:类中声明的每一个字段的信息。如名称、类型、修饰符。例如:private String name=”Most_want” ;字段名是name,类型是String,修饰符是private。

(3)方法信息:类中声明的每一个方法。包含修饰符、返回值类型、方法名、参数类型、异常、方法的字节码文件。

(4)静态区:类中声明的静态变量。

(5)类的ClassLoader引用:该类的类加载器的引用。

(6)Class实例:该类在被JVM加载时生成的Class实例,用来代表被加载的类。

JVM启动后,要使用的类的.class二进制文件就会被加载到方法区生成以上信息。所以可以看到,Class实例是在JVM加载类时候就被自动创建了。在Java中数组也有Class实例,所有具有相同元素类型、维度的数组都共享一个Class实例。基本的Java数据类型(boolean byte short char int long float double)和关键字void也表示为一个Class对象。

获取这个实例有以下几个方法:

通过Object类的getClass()获取。

通过Class类的静态方法:forName()得到。

通过.class获取。

例如获取String类的Class对象:

public class Test{
public static void main(String[] args)throws Exception{
String str="Most_want";
Class c1=str.getClass();
Class c2=Class.forName("java.lang.String");
Class c3=String.class;

System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
}
}


打印结果均为:class java.lang.String

反射

Java中的所有类都是Class类的对象。我们知道类是结构相同的对象的一种抽象,类中包含字段和方法。同理,Class类是所有类的抽象,所以它包含每个类的构造属性(Constructor)字段属性(Field)和方法属性(Method)。其实,Constructor、Field、Method都是一个类,它们以数组存储Class实例代表的类型信息。Filed类中包含有有关类或接口的单个字段的信息,Constructor类提供关于类的单个构造方法的信息以及对它的访问权限,Method类提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。因此,通过Class实例访问该Class实例对象所代表的的类的类型信息就是反射的基础。

理解了上述之后,再来看下反射机制:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

**所以反射的使用过程大致如下步骤:

A:获取类的Class对象

B:获取需要的属性:构造属性(Constructor对象)字段属性:(Field对象)和方法属性:(Method对象)

C:同过属性对象调方法来创建对象、获取字段或调用方法**

例如:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {
public static void main(String[] args) throws Exception {
// 获取Person类的Class实例
Person p = new Person();
Class c = p.getClass();
// 获取Person类的有参构造属性对象
Constructor con = c.getConstructor(String.class);
// 新建一个Person对象
Person p2 = (Person) con.newInstance("Most_want");
p2.show();
// 获取Person类的名为"name"字段属性对象
Field field = c.getField("name");
// 把p2的name字段设置为“小李”
field.set(p2, "小李");
p2.show();
// 获取Person类的名为"show",无参的方法
Method method = c.getMethod("show", null);
//通过p2调用Person类的method所表示的方法
method.invoke(p2, null);
}
}
//自定义Person类
class Person {
public String name;

public Person() {
}

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

public void show() {
System.out.println("Person  name=" + name);
}
}


我们知道这个JVM加载时把被加载类的所有信息加载到内存,那么private修饰的字段或方法能不能被获取

例如获取当把Person类的”name”私有化后要怎么获取:

import java.lang.reflect.*;

public class Test {
public static void main(String[] args) throws Exception {
Person p = new Person();
Class c = p.getClass();
//直接获取会抛出异常
//Field field = c.getField("name");
//用下面的方法可以获取任何Method属性
Field field=c.getDeclaredField("name");
//设置忽略语法检查
field.setAccessible(true);
field.set(p, "小李");
p.show();
}
}
class Person {
pricate String name;

public Person() {
}

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

public void show() {
System.out.println("Person  name=" + name);
}
}


上面的例子演示了获取私有字段的方法,获取私有方法私有构造的等同。

下面探讨下private 、protected 、public 三种权限的方法属性的获取差别

public class Test {
public static void main(String[] args) throws Exception {
// 获取Person类的Class实例
Person p = new Person();
Class<? extends Person> c = p.getClass();
Method m1 = null;
Method m2 = null;
Method m3 = null;
Method m4 = null;
// 私有方法的获取
m1 = c.getDeclaredMethod("show1", null);
m1.setAccessible(true);
m1.invoke(p, null);

// 受保护方法的获取
m2 = c.getDeclaredMethod("show3", null);
// 无需进行语法设置
// m2.setAccessible(true);
m2.invoke(p, null);

// 最高权限方法的获取
// 普通方法即可
m3 = c.getMethod("show2", null);
// m3 = c.getDeclaredMethod("show2", null);
// m3.setAccessible(true);
m3.invoke(p, null);

// 默认权限方法的获取
// 此方法抛出异常
// m4=c.getMethod("show4", null);
m4 = c.getDeclaredMethod("show4", null);
// 无需语法设置
// m4.setAccessible(true);
m4.invoke(p, null);
}
}

class Person {
public Person() {
}

private void show1() {
System.out.println("这是 private viod show1()");
}

public void show2() {
System.out.println("这是 public void show2()");
}

protected void show3() {
System.out.println("这是 protected void show2()");
}

void show4() {
System.out.println("这是 void show2()");
}

}


由以上例子可以总结:

public修饰的方法,直接获取。

private修饰的方法,需要getDeclaredMethod()获取,并关闭语法检查。

protected修饰的方法,需要getDeclaredMethod()获取,无需关闭语法检查。

默认权限的方法,需要getDeclaredMethod()获取,无需关闭语法检查。

在使用反射的时候,一般我们不知道被反射使用的类型信息,所以etDeclaredMethod()和setAccessible()联用,同理Filed和Constructor的获取一致。

例如:用反射原理制作一个修改任意类对象的字段的方法。

public static void setField(Object obj,String fieldname,Object args)throws Exception{
Class c=obj.getClass();
Field field=c.getDeclaredField(fieldname);
field.setAccessible(true);
field.set(obj, args);
}


动态代理

利用代理可以提供原对象类不具有的额外的功能,例如日志记录,权限检查。Java中利用反射实现的代理就是动态代理。Java中默认包中提供了对接口的代理。代理主要使用java.lang.reflect包中的proxy类和HandlerInvocation接口

用到的方法:

Object invoke(Object proxy, Method method,Object[] args)throws Throwable

在代理实例上处理方法调用并返回结果。proxy是在其上调用方法的代理实例,method是被代理的方法,args是方法的参数如果有的话。

Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

loader是被代理对象的类加载器,interfaces是被代理类的接口数组,h是实现HandlerInvocation接口的引用变量。

以下例子演示了利用代理统计List集合add()耗时:

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
//自定义InvocationHandler实现类
class MyInvocationHandler implements InvocationHandler {
//被代理的类
Object target;

public MyInvocationHandler(Object target){
this.target=target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.setAccessible(true);
//添加自定义语句
long start=System.currentTimeMillis();
//调用被代理类的方法
Object result=method.invoke(target, args);
//添加自定义语句
long end=System.currentTimeMillis();
System.out.println("方法调用耗时: "+(end-start)+" (millisecond)");
return result;
}
}

public class 静态方法对象调用 {
public static void main(String[] args) throws Exception {
//创建接口引用类变量
List list=new ArrayList();
//创建自定义InvocationHandler引用变量
InvocationHandler i=new MyInvocationHandler(list);
//获取list的代理类
List proxy=(List)Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), i);
//用代理对象调用方法
proxy.add("Most_want");

}
}


由此我们可以总结出代理的基本使用步骤:

A:创建自定义InvocationHandler实现类并重写invoke();

B:创建InvocationHandler引用变量并实例化;

C:创建Proxy实例对象

D:通过C步骤获取的代理对象调用方法。

Class类的 API中常用的几个方法:

创建一个此Class对象代表的类的一个新实例。此方法和调用该类型的空的构造方法创建实例效果一样。

public T newInstance()例如:String a=String.class.newInstance();

获取Class实例代表的(类、接口、数组、基本类型或void)的名称:

String getName()

获取Class实例代表的实体的类加载器:

ClassLoader getClassLoader()

判断Class实例代表的类是否为数组

boolean isArray()

判断Class实例代表的类是否为枚举类

boolean isEnum ( )

获取Class实例代表的实体的超类

Class< ? super T > getSuperclass()

示例

下面的代码利用反射技术打印出一个类的信息:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//输入一个完全限定类名,打印出该类的所有字段
public class PrintClass {
// 被反射的Class对象
private Class target;

// 带Class类型参数的构造器
public PrintClass(Class target) {
this.target = target;
}

// 方法参数为String的构造器,如果指定的字符串无法转换为Class对象,那么抛出异常
public PrintClass(String target) throws IllegalStateException {
try {
this.target = Class.forName(target);
} catch (Exception e) {
throw new IllegalStateException("cannot found this Class");
}
}

// 打印该Class的所有信息
public void printAllMsg() {
printFields();
System.out.println("\n\n");
printConstructors();
System.out.println("\n\n");
printMethods();
}

// 打印target对象代表的类的所有构造方法
public void printConstructors() {
// 获取target的构造器对象数组
Constructor[] constructors = target.getDeclaredConstructors();
if (constructors.length == 0)
return;
// 遍历该数组,打印每一个构造器的信息
for (Constructor con : constructors) {
// 对该构造器对象取消语法检查
con.setAccessible(true);
// 构造器的修饰符
int mod = con.getModifiers();
String mods = Modifier.toString(mod);
System.out.print("  " + mods);// 打印修饰符
// 构造器的名字
String name = con.getName();
System.out.print("  " + name);
// 构造器的参数
Class[] paramClasses = con.getParameterTypes();
System.out.print("(");
// 参数至少为1个才进行打印
if (paramClasses.length > 0) {
for (int i = 0; i < paramClasses.length; i++) {
Class c = paramClasses[i];
if (c.isArray()) {
Class componentType = c.getComponentType();
String[] names = componentType.toString().split("\\.");
int index = names.length - 1;
String arrName = names[index];
System.out.print(" " + arrName + "[]");
} else {
int off = c.toString().lastIndexOf('.') + 1;
String paramClassName = c.toString().substring(off);
System.out.print(" " + paramClassName);
}
if (i < paramClasses.length - 1) {
System.out.print(",");
}
}
}
System.out.println(");");
}

}

// 打印target对象代表的类的所有字段
public void printFields() {
// 获取target的字段对象数组
Field[] fields = target.getDeclaredFields();
if (fields.length == 0)
return;
// 遍历该数组,打印每一个字段的信息
for (Field fie : fields) {
// 对该字段对象取消语法检查
fie.setAccessible(true);
// 字段的修饰符
int mod = fie.getModifiers();
String mods = Modifier.toString(mod);
System.out.print("  " + mods);// 打印修饰符
// 字段的类型
Class c = fie.getType();
if (c.isArray()) {
Class componentType = c.getComponentType();
String[] names = componentType.toString().split("\\.");
int index = names.length - 1;
String arrName = names[index];
System.out.print(" " + arrName + "[]");
} else {
String fieldClassName = c.toString();
Pattern p = Pattern.compile("[a-zA-Z]+$");
Matcher m = p.matcher(fieldClassName);
if (m.find())
fieldClassName = m.group();
System.out.print(" " + fieldClassName);
}
// 字段的名字
String name = fie.getName();
System.out.println(" " + name + ";");

}

}

// 打印target对象代表的类的所有方法
public void printMethods() {
// 获取target的方法对象数组
Method[] methods = target.getDeclaredMethods();
if (methods.length == 0)
return;
// 遍历该数组,打印每一个方法信息
for (Method method : methods) {
// 对该方法对象取消语法检查
method.setAccessible(true);

// 方法的修饰符
int mod = method.getModifiers();
String modifiers = Modifier.toString(mod);
System.out.print("  " + modifiers);// 打印修饰符

// 方法的返回类型
Class returnClass = method.getReturnType();
String returnClassName = null;
if (returnClass.equals(void.class)) {
returnClassName = "void";
} else if (returnClass.isArray()) {
Class componentType = returnClass.getComponentType();
String[] names = componentType.toString().split("\\.");
int index = names.length - 1;
String arrName = names[index];
returnClassName = arrName + "[]";
} else {
int offset = returnClass.toString().lastIndexOf('.') + 1;
returnClassName = returnClass.toString().substring(offset);
}
System.out.print(" " + returnClassName);

// 方法名
String name = method.getName();
System.out.print(" " + name);

// 方法参数
Class[] paramClasses = method.getParameterTypes();
System.out.print("(");
// 参数至少为1个才进行打印
if (paramClasses.length > 0) {
for (int i = 0; i < paramClasses.length; i++) {
Class c = paramClasses[i];
if (c.isArray()) {
Class componentType = c.getComponentType();
String[] names = componentType.toString().split("\\.");
int index = names.length - 1;
String arrName = names[index];
System.out.print(" " + arrName + "[]");
} else {
int off = c.toString().lastIndexOf('.') + 1;
String paramClassName = c.toString().substring(off);
System.out.print(" " + paramClassName);
}
if (i < paramClasses.length - 1) {
System.out.print(",");
}
}
}
System.out.println(" )");

}
}

public static void main(String[] args) throws IllegalStateException {
new PrintClass(String.class).printAllMsg();
// new PrintClass("java.lang.Object").printAllMsg();
}

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