——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();
}
}