反射
反射是动态性的
反射机制允许,在程序运行期间,使用反射来获取任何类的内部信息,并能操作任何类的属性和方法,框架中常有用到
在java中,加载完之后,会在堆内存中的方法区中会生成一个 Class类型对象(一个类只有一个Class对象)这个对象包含了这个类完整的结构信息
一般情况下
我们都是引入需要的包类名称 -> 然后通过 new 实例化->取得实例化对象
而在反射情况下
实例化对象-> getClass -> 得到完整的包类名称
关于java中 java.lang.Class类的理解
从类的加载过程来看:
程序在经过javac 命令以后会生成一个或多个 class(字节码)文件
接着我们使用java 这个命令对字节码文件进行解释运行,将其加载到内存中,(加载到内存中的过程,就称为类的加载)
加载到内存中的类,我们称之为运行时类,此运行时类就作为 Class 的一个实例
换句话说,Class对象对应着一个运行时类
获取 Class 对象的方式
前三个要求掌握,其中,第三个使用频率更多
//方式一Class clazz = Person.class;//Class<Person> clazz = Person.class;System.out.println(clazz);//方式二Person p1 = new Person();Class clazz1 = p1.getClass();System.out.println(clazz1);//方式三//调用Class 的静态方法 forName ( String classPath)Class<?> clazz2 = Class.forName("reflection.Person");System.out.println(clazz2);//方式四,作了解,不要求掌握ClassLoader classLoader = ReflectionTest.class.getClassLoader();Class clazz4 = classLoader.loadClass("reflection.Person");System.out.println(clazz4);
通过反射创建运行时类的对象
使用反射创建运行时对象,可以使用 Class.forName(String classPath)
这个方法来获取运行时对象 然后通过 newInstance() 方法获取类的对象。
Class<Person> clazz = (Class<Person>) Class.forName("reflection.Person");Person o = clazz.newInstance();System.out.println(o);
体现反射的动态性
public void main() throws ClassNotFoundException, IllegalAccessException, InstantiationException {//体现反射的 动态性int random = new Random().nextInt(3);//0,1,2String classPath = "";switch (random){case 0:classPath = "java.lang.String";break;case 1:classPath = "reflection.Person";break;case 2:classPath = "java.lang.Object";break;}Object instance = getInstance(classPath);System.out.println(instance.getClass());}public Object getInstance(String classPath) throws ClassNotFoundException, IllegalAccessException, InstantiationException {Class clazz = Class.forName(classPath);return clazz.newInstance();}
反射机制与对象的封装行矛盾吗?
不矛盾
封装里private 更想告诉你,这个方法你是用不到的,就不要用了,一般是我自己内部使用的。
我给你公开提供的方法,更好
尽管我把权限私有起来了,但是你非要调用的话,也不是不可以,但一般你要通过反射之后自己做的操作,我在公开方法里都给你做好了。那又何必呢
通过反射获取运行时类的完整结构
获取运行时类的属性
public void TestField() throws ClassNotFoundException {Class clazz = Class.forName("reflection.Test.Person");Field[] fields = clazz.getFields();//会发现都是 public 标识的成员变量,且包含父类属性for (Field f : fields){System.out.println(f);}System.out.println("------------------");//获取当前类自己定义的所有(不看权限修饰符)属性,但不包括父类属性Field[] declaredFields = clazz.getDeclaredFields();for (Field f : declaredFields){System.out.println(f);}System.out.println("------------------");for (Field f : declaredFields){//权限修饰符,但是返回的是 int 类型,这个时候我们要转为string类型 Modifier.toString()System.out.print(Modifier.toString(f.getModifiers()) + "\t");System.out.print(f.getType().getName() + "\t");System.out.println(f.getName());}}
其他的可以不加演示,以获取属性为例:你可以从代码里发现
直接getXXX() 获取的是包含父类的,但是权限修饰符 必须是 public
如果 getDeclaredXXX()方法的话, 获取的是自己所定义的所有的,不包含父类,不受访问权限约束
获取运行时类的方法结构同理
获取运行时类的方法
public void TestMethod() throws ClassNotFoundException {Class clazz = Class.forName("reflection.Test.Person");//获取 当前类和父类 public权限的方法Method[] methods = clazz.getMethods();for (Method method :methods){System.out.println(method);}System.out.println("---------------");//获取当前运行时类 当中的所有方法, 不包含父类,不在乎权限Method[] declaredMethods = clazz.getDeclaredMethods();for (Method method:declaredMethods){System.out.println(method);}System.out.println("---------------");//获取方法具体结构, 权限符 返回值 方法名字 (参数类型 参数名 )for (Method method:declaredMethods){//获取注解Annotation[] annotations = method.getAnnotations();for (Annotation annotation :annotations) {System.out.print(annotation + "\t");}//获取权限修饰符int modifiers = method.getModifiers();System.out.print(Modifier.toString(modifiers) + "\t");//返回值类型System.out.print(method.getReturnType().getName() + "\t");//方法名System.out.print(method.getName() + "\t(");//形参列表Class<?>[] parameterTypes = method.getParameterTypes();if (!(parameterTypes.length == 0 || parameterTypes == null )) {for(int i = 0; i < parameterTypes.length; i++){if (i == parameterTypes.length - 1){System.out.print(parameterTypes[i].getName() + " args_" + i);}else {System.out.print(parameterTypes[i].getName() + " args_" + i + ",");}}}System.out.print(")");System.out.println();}}
获得运行时类的指定结构
Class<Person> clazz = Person.class;
//通过反射创建对象
//获取构造器
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
Object tom = constructor.newInstance("tom", 12);
System.out.println(tom);
//通过反射。调用对象指定的属性和方法,这个时候,还不能直接获取private 修饰的属性
Field age = clazz.getDeclaredField("age");
age.set(tom,10);
System.out.println(tom);
//通过反射调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(tom);
//通过反射调用私有结构
Constructor constructor1 = clazz.getDeclaredConstructor(String.class);
constructor1.setAccessible(true);
Person jerry = (Person) constructor1.newInstance("jerry");
System.out.println(jerry);
//调用私有的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(tom,"jerry");
System.out.println(tom);
//调用私有方法,有参数的话,要指定参数类型
Method hello = clazz.getDeclaredMethod("hello", String.class);
hello.setAccessible(true);
String returnValue = (String) hello.invoke(jerry, "tom");
System.out.println(returnValue);
这个与获取完整结构不同之处就是参数不同,指定出你要的方法名。
但是注意,如果你要获取的是 private 要设置 XXX.setAccessible(true);来保证当前方法可以被访问,否则会报错。
当你获取方法时,如果需要参数,那么就要指定参数类型,如果你想调用这个方法,那么就需要使用 invoke() 这个方法去执行,如果被调用的这个方法有返回值,那么 invoke方法会帮你返回这个值
invoke() 第一个参数是 调用类,也就是你需要调用哪个对象的该方法
举例来讲: hello.invoke(jerry, “tom”); 要调用的是 jerry.hello();
如果该类里面没有 hello 这个方法,则报错。
