Reflection

反射是被视为动态语言(在运行时代码可以根据某些条件改变自身结构)的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。通过这个对象看到类的结构

关于java.lang.Class类的理解

字节码

Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口
编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中, 这种供虚拟机理解的代码叫做字节码(即扩展名为.class的文件), 它不面向任何特定的处理器,只面向虛拟机。每一种平台的解释器是不同的,但是实现的虚拟机是相同的。Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行。这也就是解释了Java的编译与解释并存的特点
Java源代码—->编译器—>JVM可执行的字节码(即虚批指令—->jvm中解释—机器可执行的二进制机器码—->程序运行

采用字节码的好处:
Java语言通过字节码的方式,在一定程度 上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行(跨平台)

类的加载过程:

  1. 程序经过**javac.exe**命令以后,会生成一个或多个**字节码文件(.class文件)**。接着使用**java.exe**命令对某个字节码文件进行解释运行。相当于**将某个字节码文件加载到内存中。此过程就称为类的加载**。**加载到内存中的类,称为运行时类**,**此运行时类,就作为Class的一个实例**<br />2.Class的实例就对应着一个运行时类<br />3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,可以通过不同的方式来获取此运行时类

获取Class类的实例

法一 调用运行时类的属性 .class

  1. public void test(){
  2. Class<User> clazz = User.class;
  3. System.out.println(clazz);
  4. }

法二 通过运行时类的对象

  1. public void test2() {
  2. User user = new User();
  3. Class clazz = user.getClass();
  4. System.out.println(clazz);
  5. }

法三 调用 Class 的静态方法 forname(String classPath)

  1. public void test3() throws ClassNotFoundException {
  2. Class clazz = Class.forName("User");
  3. System.out.println(clazz);
  4. }

法四 使用类的加载器:ClassLoader

  1. public void test4() throws ClassNotFoundException {
  2. ClassLoader classLoader = reflectionTest.class.getClassLoader();
  3. Class clazz4 = classLoader.loadClass("User");
  4. System.out.println(clazz4);
  5. }

注:只要数组的元素类型与维度一样,就是同一个Class

类的加载

类加载的作用

将class文件字节码内容加载到内存中,并将这些静态数据转换成方 法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为 方法区中类数据的访问入口

类缓存
标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器 中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象

ClassLoader

类加载器作用是用来把类(class)装载进内存的

ClassLoader加载配置文件

  1. public void test2() throws Exception {
  2. Properties pros = new Properties();
  3. //此时的文件默认在当前的module下。
  4. //读取配置文件的方式一:
  5. // FileInputStream fis = new FileInputStream("jdbc.properties");
  6. // FileInputStream fis = new FileInputStream("src/jdbc.properties");
  7. // pros.load(fis);
  8. //读取配置文件的方式二:使用ClassLoader
  9. //配置文件默认识别为:当前module的src下
  10. ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
  11. InputStream is = classLoader.getResourceAsStream("jdbc.properties");
  12. pros.load(is);
  13. String user = pros.getProperty("user");
  14. String password = pros.getProperty("password");
  15. System.out.println("user = " + user + ",password = " + password);
  16. }

通过反射创建运行时类的对象

  1. #调用 newInstance()方法 创建对应的运行时类的对象
  2. public void test3() throws Exception {
  3. Class<User> clazz=User.class;
  4. User instance = clazz.newInstance();
  5. System.out.println(instance);
  6. }
  1. //获取运行时类的父类
  2. @Test
  3. public void test2(){
  4. Class clazz = Person.class;
  5. Class superclass = clazz.getSuperclass();
  6. System.out.println(superclass);
  7. }
  8. //获取运行时类的带泛型的父类
  9. @Test
  10. public void test3(){
  11. Class clazz = Person.class;
  12. Type genericSuperclass = clazz.getGenericSuperclass();
  13. System.out.println(genericSuperclass);
  14. }
  15. //获取运行时类的带泛型的父类的泛型 功能性代码 模板固定
  16. @Test
  17. public void test4(){
  18. Class clazz = Person.class;
  19. Type genericSuperclass = clazz.getGenericSuperclass();
  20. //带参数的类型
  21. ParameterizedType paramType = (ParameterizedType) genericSuperclass;
  22. //获取泛型类型
  23. Type[] actualTypeArguments = paramType.getActualTypeArguments();
  24. // System.out.println(actualTypeArguments[0].getTypeName());
  25. System.out.println(((Class)actualTypeArguments[0]).getName());
  26. }

调用运行时类中的指定的属性

  1. public void testField1() throws Exception {
  2. Class clazz = Person.class;
  3. //创建运行时类的对象
  4. Person p = (Person) clazz.newInstance();
  5. //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
  6. Field name = clazz.getDeclaredField("name");
  7. //2.保证当前属性是可访问的
  8. name.setAccessible(true);
  9. //3.获取、设置指定对象的此属性值
  10. name.set(p,"Tom");
  11. System.out.println(name.get(p));
  12. }

调用运行时类中的指定的方法

  1. public void testMethod() throws Exception {
  2. Class clazz = Person.class;
  3. //创建运行时类的对象
  4. Person p = (Person) clazz.newInstance();
  5. //1.获取指定的某个方法
  6. //getDeclaredMethod():参数1 :指明获取的方法的名称
  7. 参数2:指明获取的方法的形参列表
  8. Method show = clazz.getDeclaredMethod("show", String.class);
  9. show.setAccessible(true);
  10. //3.调用方法的invoke():参数1:方法的调用者
  11. 参数2:给方法形参赋值的实参
  12. //invoke()的返回值即为对应类中调用的方法的返回值
  13. Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
  14. System.out.println(returnValue);
  15. //调用静态方法
  16. Method showDesc = clazz.getDeclaredMethod("showDesc");
  17. showDesc.setAccessible(true);
  18. //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
  19. // Object returnVal = showDesc.invoke(null);
  20. Object returnVal = showDesc.invoke(Person.class);
  21. System.out.println(returnVal);
  22. }