一.反射的基本概念
1.类的加载:
将一个.class文件加载到内存中,形成一个对象,并执行起来;
Java 虚拟机内使用类加载器将.class文件加载到内存中,形成一个对象;这个对象在内存中只有一份;由虚拟机类加载器负责创建,程序员只能获取使用,程序员不能自己创建;
进一步细分,可以分为:加载,连接,初始化;
加载:将.class读取到内存中的过程;
连接:检查语法格式与关键代码,及常量的赋值与常量的运算;
初始化:创建出来对象,和以前面向对象的初始化过程一致;
2.类加载器:用于将所有的.class文件(磁盘上的或者网络上的)加载到内存中,并为之生成对应的java.lang.Class 对象
Bootstrap ClassLoader:根(原始/引导)类加载器,负责加载JAVA的核心类;不是java.lang.ClassLoader 的子类,而是由JVM自身实现的.
Extension ClassLoader:扩展类加载器,负责加载jre的扩展目录(%JAVA_HOME%jre/lib/ext)中JAR包的类.
System ClassLoader:系统(应用)类加载器.
3.字节码:程序的一种低级表示,可以运行于JAVA虚拟机.
4.类初始化的时机:
1:使用关键字new,创建一个类的对象;
2:使用一个类中的静态的方法或属性;
3:使用反射强制加载一个类;
4:初始化一个类(A)的子类(B)的时候;
5:直接使用java.exe命令运行一个.class文件;
5.反射概述:
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
二.Class 类介绍及获取字节码对象的三种方式:
Class:描述类的类;
所有的数据类型都有对应的.class文件,由.class文件生成的对象,我们称为字节码文件对象!
所有的字节码文件对象的数据类型都是 Class 类型;
方式1:
使用一个数据类型的静态属性,属性名是:class
方式2:
使用 Object 类的方法, getClass() 方法;通过实例化对象调用
方式3:(重点)
使用 Class 类的静态方法,forName(要加载的类的全路径);全路径就是带包的路径;由 Class 类直接调用.如果不使用eclipse,则类的全路径名为(类所在的文件夹名.类名??(绝对路径?))
Class 类 (java.lang)
继承关系:java.lang.Object--java.lang.Class<T>
定义:public final class Class<T> extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement
常用方法:
public static Class<?> forName(String className) throws ClassNotFoundException{}:返回与带有给定字符串名的类或接口相关联的 Class 对象
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException{}:返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。parameterTypes:代表的是参数数据类型的字节码文件对象!
public Constructor<?>[] getConstructors() throws SecurityException{}:获取到所有的public权限的构造方法,一般不使用该方法
public Method getMethod(String name,Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException{}: name所赋值(实参)的类型的字节码文件
public Method[] getMethods() throws SecurityException{}:
public T newInstance() throws InstantiationException, IllegalAccessException{}:创建此 Class 对象所表示的类的一个新实例。(普通对象)调用该方法的时候,必须保证字节码文件对象中有空参数的public权限的构造方法;否则执行报错
public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException{}:
public Field[] getDeclaredFields() throws SecurityException{}:
代码演示:
1 import java.lang.reflect.Constructor; 2 import java.lang.reflect.Method; 3 import java.lang.reflect.Field; 4 class Person{ 5 private String name; 6 private int age; 7 //无参构造 8 public Person(){ 9 10 } 11 //全参构造 12 public Person(String name, int age){ 13 this.name = name; 14 this.age = age; 15 } 16 //getter/setter 17 public String getName(){ 18 return name; 19 } 20 public int getAge(){ 21 return age; 22 } 23 public void setName(String name){ 24 this.name = name; 25 } 26 public void setAge(int age){ 27 this.age = age; 28 } 29 @Override 30 public String toString(){ 31 return "["+"name="+name+",age="+age+"]"; 32 } 33 } 34 35 public class Ref{ 36 public static void main(String[] args) throws Exception{ 37 //方式一获取字节码文件:使用数据类型的静态属性 38 Class c1 = Person.class; 39 System.out.println(c1);//class Person 40 //获取String类型的字节码文件 41 String str = "helloworld"; 42 System.out.println(String.class);//class java.lang.String 43 System.out.println(str.getClass());//class java.lang.String 44 //方式二:Object类的方法,getClass方法 45 Person p = new Person(); 46 System.out.println(p.getClass());//class Person 47 //方式三:使用Class类的静态方法,forName(要加载的类的全路径) 48 Class c2 = Class.forName("Person"); 49 System.out.println(c2); 50 51 //获取构造方法一:Class类的getConstructor()方法 52 Constructor co1 = c1.getConstructor(); 53 System.out.println(co1);//public Person() 54 //获取构造方法二:Class类的getConstructors()方法 55 Constructor[] co2 = c1.getConstructors(); 56 for(Constructor c : co2){ 57 System.out.println(c);//public Person() public Person(java.lang.String,int) 58 } 59 //获取构造方法三:利用Constructor类的newInstance()方法初始化Object对象 60 Class c4 = Class.forName("Person"); 61 Constructor co4 = c4.getConstructor(String.class, int.class); 62 Object obj = co4.newInstance("Jack",18); 63 System.out.println(obj); 64 65 //利用newInstance()方法快速获取空对象 66 Object obj1 = c4.newInstance(); 67 System.out.println(obj1); 68 69 //获取普通方法--setName方法 70 Class c5 = Class.forName("Person");//获取字节码文件 71 Object obj5 = c5.newInstance();//获取字节码文件的普通对象 72 Method m5 = c5.getMethod("setAge",int.class);//面向普通对象获取方法 73 Object obj6 = m5.invoke(obj5,22);//面向方法,传入对象,及方法需要的实参 74 System.out.println(obj5); 75 76 //获取属性 77 Class c6 = Class.forName("Person");//获取字节码文件对象 78 Field f6 = c6.getDeclaredField("name");//获取name属性 79 80 f6.setAccessible(true); 81 Object obj7 = c6.newInstance(); 82 f6.set(obj7,"rongrong"); 83 84 Field age = c6.getDeclaredField("age"); 85 age.setAccessible(true); 86 age.set(obj7,24); 87 88 System.out.println(obj7); 89 } 90 } 91
三.获取构造方法:只有从字节码文件中才能获取构造方法
1.通过 Class 类的普通方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException{}:返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
parameterTypes:代表的是参数数据类型的字节码文件对象!
public Constructor<?>[] getConstructors() throws SecurityException{}:获取到所有的public权限的构造方法,一般不使用该方法
2.通过 Construct 类的 newInstance(),类中必须包含无参构造
public T newInstance(Object... initargs) throws InstantiationException,IllegalAccessException,IllegalArgumentException,
InvocationTargetException{}:
四.获取普通方法:
1.通过 Class 类的普通方法
public Method getMethod(String name,Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException{}: name所赋值(实参)的类型的字节码文件
public Method[] getMethods() throws SecurityException{}:
public T newInstance() throws InstantiationException, IllegalAccessException{}:创建此 Class 对象所表示的类的一个新实例。(普通对象)调用该方法的时候,必须保证字节码文件对象中有空参数的public权限的构造方法;否则执行报错
2.通过 Method 类的方法
public Object invoke(Object obj,Object... args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException{}:对带有指定参数的指定对象调用由此 Method 对象表示的底层方法.Obj:代表的是该方法对象所在的字节码文件对象创建出来的实例化对象;Args:方法执行的时候,需要的实际参数;Object:返回的是方法执行的结果,如果方法是void,那么默认该方法的返回值是null
五.获取属性:
1.概述:
由于属性私有,可使用暴力反射(不推荐!!),因此,实际开发中都是通过反射属性的 getXxx 与 setXxx 方法,来替代反射属性;
通过字节码文件对象,使用反射提取出来的属性或成员变量,称为 Field 类的对象;
Field 类的对象可以用于保存数据值和获取数据值;
成员变量随着对象的创建而产生,随着对象的死亡而死亡,因此在使用Field对象的时候,必须有普通对象;
定义:public final class Field extends AccessibleObject implements Member
2.通过 Class 类的普通方法
public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException{}:
public Field[] getDeclaredFields() throws SecurityException{}:
3.AccessibleObject 类的方法:执行之前,忽略权限检查
public void setAccessible(boolean flag) throws SecurityException{}:权限设为true,通过检查
4.Field 类的方法
public void set(Object obj,Object value) throws IllegalArgumentException,IllegalAccessException{}:
public Object get(Object obj) throws IllegalArgumentException,IllegalAccessException{}:
六.综合案例:
1 //在eclipse中执行,并在工程中创建相应环境,包括configure.properties文件和Person类(String name, int age); 2 package case01; 3 import java.io.FileReader; 4 import java.lang.reflect.Method; 5 import java.util.Properties; 6 import java.util.Set; 7 /* 8 使用反射的方式创建一个类的对象,并对对象的属性赋值,打印对象;给对象赋值的时候,属性值从配置文件中读取; 9 */ 10 public class ReflectCase { 11 public static void main(String[] args) throws Exception { 12 // 创建Property集合,用于存储配置文件信息 13 Properties p = new Properties(); 14 FileReader fr = new FileReader("configure.properties"); 15 p.load(fr); 16 // 获取反射路径 17 String path = p.getProperty("classPath"); 18 // 根据路径获取字节码对象 19 Class c = Class.forName(path); 20 // 面向字节码对象创建普通对象 21 Object obj = c.newInstance(); 22 // 获取键集 23 Set<String> keySet = p.stringPropertyNames(); 24 // 迭代,并拼接成set方法 25 for (String s : keySet) { 26 // 判断如果不是name或age,就跳过 27 if (!("name".equals(s) || "age".equals(s))) { 28 continue; 29 } 30 // 拼接成set方法,我们需要反射set方法,需要一个方法名,即setName,setAge 31 StringBuilder builder = new StringBuilder(); 32 builder.append("set").append(s.substring(0, 1).toUpperCase()).append(s.substring(1)); 33 if (builder.toString().endsWith("ame")) { 34 // 反射方法 35 Method setNameMethod = c.getMethod(builder.toString(),String.class); 36 // 执行方法 37 Object obj1 = setNameMethod.invoke(obj, p.getProperty(s)); 38 } else { 39 // 反射方法 40 Method setAgeMethod = c.getMethod(builder.toString(), int.class); 41 // 执行方法 42 Object obj2 = setAgeMethod.invoke(obj,Integer.parseInt(p.getProperty(s))); 43 } 44 } 45 System.out.println(obj); 46 } 47 }
七.//需求,返回传入数据的类型
1 public class Check{ 2 public static void main(String[] args){ 3 System.out.println(test(-15%4)); 4 } 5 6 public static Object test(Object obj){ 7 return obj.getClass(); 8 } 9 10 }