黑马程序员---Java反射
2015-10-15 09:44
423 查看
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
2015.10.15
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对象:
打印结果均为:class java.lang.String
理解了上述之后,再来看下反射机制:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
**所以反射的使用过程大致如下步骤:
A:获取类的Class对象
B:获取需要的属性:构造属性(Constructor对象)字段属性:(Field对象)和方法属性:(Method对象)
C:同过属性对象调方法来创建对象、获取字段或调用方法**
例如:
我们知道这个JVM加载时把被加载类的所有信息加载到内存,那么private修饰的字段或方法能不能被获取
例如获取当把Person类的”name”私有化后要怎么获取:
上面的例子演示了获取私有字段的方法,获取私有方法私有构造的等同。
下面探讨下private 、protected 、public 三种权限的方法属性的获取差别:
由以上例子可以总结:
public修饰的方法,直接获取。
private修饰的方法,需要getDeclaredMethod()获取,并关闭语法检查。
protected修饰的方法,需要getDeclaredMethod()获取,无需关闭语法检查。
默认权限的方法,需要getDeclaredMethod()获取,无需关闭语法检查。
在使用反射的时候,一般我们不知道被反射使用的类型信息,所以etDeclaredMethod()和setAccessible()联用,同理Filed和Constructor的获取一致。
例如:用反射原理制作一个修改任意类对象的字段的方法。
用到的方法:
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()耗时:
由此我们可以总结出代理的基本使用步骤:
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()
示例
下面的代码利用反射技术打印出一个类的信息:
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(); } }
相关文章推荐
- 2015年11月 面试题总结
- 黑马程序员——9.正则表达式(爬虫)
- PHP面试题之文件目录操作
- PHP经典面试题之设计模式(经常遇到)
- 程序员的10大职业谏言
- 程序员如何在十年内创造身价百倍
- 程序员必读经典书籍
- 十道海量数据处理面试题与十个方法大总结
- 找工作之面试题(1)
- 黑马程序员一一递归、IO流
- 黑马程序员——集合
- 黑马程序员一一常见的排序跟查找原理图
- 黑马程序员一一Java语言基础(五)
- 黑马程序员一一Java基础语法(四)
- 黑马程序员-hashCode()的作用
- 黑马程序员一一Java基础语法(三)
- 黑马程序员_反射
- 黑马程序员——Java基础语法(二)
- 黑马程序员_JAVA笔记之(File类,过滤器,递归练习)
- 黑马程序员——Java基础语法(一)