反射机制
一、反射机制概述
1.反射的相关概念
- Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
- 动态语言与静态语言:
java不是动态语言,由于有了反射机制,才具有动态性,故也叫做“准动态语言”。动态语言指的是某种语言在运行时能够发生结构的变化的语言。
2.反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
3.反射相关的主要API
java.lang.class将一个确定的类的结构信息装载进的类(代表一个类)
java.lang.Method代表类的方法
java.lang.Constructor代表类的构造器
java.lang.Field代表类的成员变量
二、反射的理解
1.对反射概念的理解
①通过直接new的方式还是通过反射的方式创建对象?
答:一般使用直接new的方式,没有特殊要求时,直接new的方法是普遍创建对象的方法。而反射创建对象的方法一般运用于当前运行的程序需要调用对象的某种私有属性的时候,或者程序在运行时不确定所运用的对象具体是哪一种的时候,运用反射机制创建对象。
②反射机制与面向对象中的封装性是否矛盾?
答:不矛盾。封装性的private代表的是属性或者方法不一定需要被外界引用,在内部声明好的方法中已经提供了,不需要外界设置,private封装性指的是“需不需要”的问题,而不是必须不让外界调用。而外界程序调用时,某些时刻需要调用私有属性,此时通过setAccessible(true)设置,即可访问对象的私有属性。
2.对class概念的理解
将具体的类用class获取,将类的结构(属性、对象、构造器、方法)获取到,正常new对象和将类的对象赋值给class类似于数学中函数和反函数的理解,或者可以理解为log函数。
Class的实例就对应着一个运行时类。获取Class实例的四种方式:
//获取class实例的四种方式(需要掌握)
Class<Person> clazz1 = Person.class;
System.out.println(clazz1);
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
Class<?> clazz3 = Class.forName("com.zry.java.Person");
(用的多)
System.out.println(clazz3);
//方式四:使用类的加载器
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class<?> clazz4 = classLoader.loadClass("com.zry.java.Person");
System.out.println(clazz4);
注:只要class的元素类型与维度一样那就是同一个class
三、类的加载和ClassLoader的理解
1.类的加载过程
2.ClassLoader的理解
- 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
- 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
可通过调用类的加载器的父类方法 查看引导类和扩展类加载器
3.ClassLoader的两种加载方式
//类的加载方式一(文件默认在当前module下)
Properties pros = new Properties();
FileInputStream fis = new FileInputStream("E:\\javasenior1\\Reflection\\jdbc.properties");
pros.load(fis);
//类的加载方式二(文件默认在当前module的src下)
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("jdbc.properties");
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
System.out.println("user=" + user + ",password=" + password);
四、通过反射创建运行时类的对象
要想此方法正常创建对象,就应该满足:
①运行时类必须提供空参的构造器。(一般在JavaBean中要用到空参构造器,因为保不齐会用到反射机制,而且归根结底是只有构造器才能构建对象。另外还有一个原因就是便于子类继承此运行时类时,保证父类有此构造器)
②空参的构造器访问权限得够,一般为public。
五、获取运行时类的完整结构
实现的全部接口: public Class<?>[] getInterfaces()
所继承的父类:public Class<? Super T> getSuperclass()
获取全部的public构造器:public Constructor[] getConstructors()
全部的构造器: public Constructor[] getDeclaredConstructors()
取得修饰符: public int getModifiers();
取得方法名称: public String getName();
取得参数的类型:public Class<?>[] getParameterTypes();
全部的方法:public Method[] getDeclaredMethods()
获取全部的public方法:public Method[] getMethods()
全部的Field:public Field[] getDeclaredFields()
获取全部的public的Field:public Field[] getFields()
六、调用运行时类的完整结构(主要是属性和方法)
①调用运行时类的对象的特定属性如下:
Class<Person> clazz = Person.class;
//1.创建运行时类的对象
Person p = clazz.newInst
//2.获取运行时类的的特定的属性
Field name = clazz.getDe
//3.保证运行时类的属性是可访问的
name.setAccessible(true)
//4.获取、设置指定对象的属性
name.set(p,"bilibili");
System.out.println(p);
②调用运行时类的对象的方法如下:
Class<Person> clazz = Person.class;
//1.创建运行时的类的对象
Person p1 = clazz.newInstance();
//2.获取运行时类的特定的方法
Method ct = clazz.getDeclaredMethod("na
//3.保证运行时类的方法是可访问的
ct.setAccessible(true);
//4.调用方法invoke()
Object nation = ct.invoke(p1, "CHN");
//或者无参方法
Method show = clazz.getMethod("show");
show.invoke(p1);
③调用运行时类的对象的构造器如下:
Class<Person> clazz = Person.class;
Constructor<Person> cs = clazz.getDeclaredConstructor(String.class);
cs.setAccessible(true);
Person tom =(Person) cs.newInstance("tom");
System.out.println(tom);