Java Reflection

1 Java 反射机制概述

Reflection反射被视为动态语言的关键,反射机制允许在运行期间借助于Reflection取得任何类的内部信息,并能直接操作任意对象的内部属性和方法

加载完类之后,在堆内存的方法区中就生成了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。对象就像是一个镜子,通过对象得到类的结构称之为“反射”。

反射相关的主要API
java.lang.class 代表一个类
java.lang.reflect.Method 代表类的方法
java.lang.reflect.Field 代表类的成员变量
java.lang.reflect.Constructor 代表类的构造器
        Class clazz = Person.class;
Constructor cons = clazz.getConstructor(int.class, String.class, String.class); Object obj = cons.newInstance(25, "lxg", "china");
Person p = (Person) obj;
System.out.println(p.toString()); Field age = clazz.getField("age");
age.set(p, 10);
System.out.println(p); Method method = clazz.getMethod("show");
method.invoke(p);

反射类的私有结构

        //私有构造器
Constructor<Person> cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = cons1.newInstance("ryuu");
System.out.println(p1.getName()); //私有属性
Field nation = clazz.getDeclaredField("nation");
nation.setAccessible(true);
nation.set(p1, "china");
System.out.println(p1.getNation()); //私有方法
Method method1 = clazz.getDeclaredMethod("showNation");
method1.setAccessible(true);
method1.invoke(p1);

1.1 对于反射机制的疑问

类的公共结构可以通过new的方式和反射的方式进行调用,开发中一般使用哪个?

直接new的方式,在不确定new哪个类的对象的时候采用放射的方式动态性地调用类的公共结构,比如前端页面的login和register传入后端解析,服务器启动时不能确定即将传入的是哪一个类,因此需要动态地判断。

反射机制和面向对象的封装性矛盾吗?

不矛盾,封装性对于结构的封装体现的是建议性的屏蔽使用,而反射机制是能够使用封装性不建议使用的类结构。

2 Class类的理解

2.1 类的加载过程

java程序编写完成后,经过javac.exe(编译)生成一个或多个字节码文件.class,再经java.exe命令对某个字节码文件解释运行,将其加载到内存中,这个过程称作类的加载。加载到内存中的类称为运行时类,就作为Class的一个实例:类本身也是一个Class的实例。换句话说,Class实例对应着一个运行时类,可以直接通过运行时类直接赋值

当程序主动使用某个类时,如果类还没有被加载到内存,就会通过如下三个步骤对类进行初始化:

①类的加载(Load)

将类的Class文件加载到内存,并为之创建一个java.lang.Class对象与之对应。此过程由类的加载器完成。

②类的链接(Link)

将类的二进制数据合并到JRE中。

验证: 确保加载的类信息符合JVM规范。

准备: 正式为类变量(static)分配内存设置变量初始化值的阶段,这些内存都在方法区中进行分配。

解析: 执行类构造器方法(),该方法不是构造类对象的构造器,而是为了给属性进行显示赋值操作。

在对一个类初始化时,如果父类还没有初始化,应该先初始化父类

虚拟机 会保证一个类的类构造器方法在多线程环境中,被正确加锁和同步

③类的初始化(Initialize)

JVM负责对类进行初始化。

2.2 如何获取Class的实例

//      方式一:调用运行时类的class属性
Class clazz1 = Person.class;
System.out.println(clazz1);
// 方式二:通过运行时类的对象,调用Object的getClass()方法
Person person = new Person();
Class clazz2 = person.getClass();
System.out.println(clazz2);
// 方式三:调用Class的静态方法
Class clazz3 = Class.forName("com.hikaru.java.Person");
System.out.println(clazz3);
// 本质都是指向了Person的运行时类,故地址相同
System.out.println(clazz1 == clazz2);
System.out.println(clazz2 == clazz3);

第三种使用的最多,编译时不确定加载类,更好地体现了反射的动态性,第二种由于反射本来就是为了通过运行时类不使用new的方式而创建对象因此使用得最少,第一种则相对于第三种写死了类名。

方式四:使用类加载器(了解)

2.2 哪些类可以有Class对象

class 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
interface 接口
[] 数组
enum 枚举类
annotation 注解
primitive type 基本数据类型
void

只要数组的类型和维度相同就是一个CLass

        int[] a = new int[10];
int[] b = new int[100]; Class clazz1 = a.getClass();
Class clazz2 = b.getClass(); // true
System.out.println(clazz1 == clazz2);

2.3 ClassLoader类加载器

类加载器分类
引导类加载器 负责Java核心库的加载,如String,该加载器无法直接获取
扩展类加载器 jar包装入工作库
系统类加载器 最常用的家加载器
        ClassLoader classLoader1 = ReflectionTest.class.getClassLoader();
System.out.println(classLoader1); ClassLoader classLoader2 = classLoader1.getParent();
System.out.println(classLoader2); ClassLoader classLoader3 = String.class.getClassLoader();
System.out.println(classLoader3); // sun.misc.Launcher$AppClassLoader@18b4aac2
// sun.misc.Launcher$ExtClassLoader@28a418fc
// null

Java核心类库的类加载器无法直接获取

通过类加载器读取properties,区别于File读取此时默认路径为module下的src。注意:开发时module下的配置文件不起作用。

        Properties properties = new Properties();

//        FileInputStream fis = new FileInputStream("\\jdbc.properties");
// properties.load(fis); ClassLoader classLoader = ReflectionTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("jdbc.properties");
properties.load(is); String username = properties.getProperty("username");
String password = properties.getProperty("password"); System.out.println(username + password);

Class实例创建对象:newInstance(),创建对应运行时类的对象

        Class<Person> clazz = Person.class;
Person person = clazz.newInstance();

实质还是通过构造器创建的对象,并且要求:

①类提供空参构造器,框架底层经常使用反射构建对象

②访问权限足够

在javabean中要求提供一个空参构造器的原因:

①便于通过反射,创建运行时的对象

②便于子类继承父类,调用super()方法时,保证父类有此构造器

2.4 体会反射机制的动态性

在编译时不确定类的类别,而在运行时才确定。

    @Test
public void test6() {
int choice =new Random().nextInt(3);
String class_path = null;
switch (choice) {
case 0:
class_path = "java.lang.Object";break;
case 1:
class_path = "java.util.Date";break;
case 2:
class_path = "com.hikaru.java.Person";break;
}
try {
Object obj = getInstance(class_path);
System.out.println(obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
} public Object getInstance(String class_path) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class clazz = Class.forName(class_path);
return clazz.newInstance();
}

2.5 获取运行时类的完整属性及内部结构

2.5.1 获取运行类属性
Class对象
Field[] getFields() 获取当前类及父类中声明为public访问权限的属性
Field[] getDeclaredFields() 获取当前运行类中声明的所有属性
Field对象获取属性的权限修饰符、数据类型、变量名
int getModifiers()
Class getType()
String getName()
        Class clazz = Person.class;
Field[] fields = clazz.getFields(); for(Field f : fields) {
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier)); Class type = f.getType();
System.out.print(" " + type); String name = f.getName();
System.out.println(" " + name);
}
        Class clazz = Person.class;
Method[] methods = clazz.getMethods(); for(Method m : methods) {
System.out.println(m);
} Method[] methods1 = clazz.getDeclaredMethods(); for(Method m : methods1) {
System.out.println(m);
}
2.5.2 获取运行类方法
Class对象方法
Method[] getMethods() 获取当前类及父类中声明为public访问权限的方法
Method[] getDeclaredMethod() 获取当前运行类中声明的所有方法
Method对象方法
Annotation[] getAnnotations()
Modifer getModifers()
String getName()
Class<?> getParamterType() 获取形参列表

获取方法的注解:getAnnotations()

    @Test
public void test8() {
Class clazz = Person.class;
// Method[] methods = clazz.getMethods();
//
// for(Method m : methods) {
// System.out.println(m);
// } Method[] methods1 = clazz.getDeclaredMethods(); for(Method m : methods1) {
// System.out.println(m);
Annotation[] annotations = m.getAnnotations();
for(Annotation annotation : annotations)
System.out.println(annotation);
}
}
注解生命周期参数
RetentionPolicy.SOURCE 源文件保留
RetentionPolicy.CLASS class保留,不会加载到内存中
RetentionPolicy.RUNTIME 运行时保留,只有声明为RUNTIME生命周期的注解,才能通过反射获取。

因此注解中需要声明为RUNTIME

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
2.5.3 获取运行时类的构造器:获取当前运行类中声明为public的构造器
        Class clazz = Person.class;
Constructor[] constructors = clazz.getConstructors();
2.5.4 获取父类以及带泛型的父类
Class getSuperClass() 获取父类
Type getGenericSuperClass() 获取带泛型的父类
        Class clazz = Person.class;

        Class superclass = clazz.getSuperclass();
System.out.println(superclass); Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
2.5.5 获取带泛型父类的泛型
        ParameterizedType genericSuperclass1 = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = genericSuperclass1.getActualTypeArguments();
System.out.println(((Class)actualTypeArguments[0]).getTypeName());

genericSuperclass1.getActualTypeArguments():获取泛型类型数组

获取运行时类实现的接口

//        获取运行时类的接口
Class[] interfaces = clazz.getInterfaces();
for(Class i : interfaces)
System.out.println(i); // 获取运行时类的父类的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for(Class i : interfaces1)
System.out.println(i);
2.5.6 获取运行时类的包
        Class clazz = Person.class;
Package aPackage = clazz.getPackage();
System.out.println(aPackage);
2.5.7 获取运行时类的注解
        Class clazz = Person.class;
Annotation[] annotations = clazz.getAnnotations();
for(Annotation annotation : annotations) {
System.out.println(annotation);
}

2.6 调用运行时类的指定结构

2.6.1 调用类的指定属性
        Class clazz = Person.class;
Field field = clazz.getField("age");
Person person = new Person(); field.set(person, 18);
int age = (int) field.get(person);
System.out.println(age);
        Class clazz = Person.class;
Field field = clazz.getDeclaredField("name");
// 保证当前属性可访问
field.setAccessible(true);
Person person = new Person();
field.set(person, "mike");
String name = (String) field.get(person);
System.out.println(name);

第一种只能访问当前运行类的public属性,第二种可以访问所有属性比较常用

2.6.2 调用类的指定方法
2.6.3 调用运行时类的指定构造器
        Class<Person> clazz = Person.class;
Person person1 = clazz.newInstance(); Constructor<Person> constructor = clazz.getConstructor();
Person person2 = constructor.newInstance(); Method display = clazz.getMethod("display", String.class);
String interest = (String) display.invoke(person1, "reading");
System.out.println(interest); Method show = clazz.getDeclaredMethod("show", String.class);
show.setAccessible(true);
show.invoke(person2, "CHN");

【Java SE】反射的更多相关文章

  1. Java SE练习 - 对dom4j解析、反射的综合练习

    原 Java SE练习 - 对dom4j解析.反射的综合练习 2017年12月13日 14:41:07 都说名字长不会被发现 阅读数 138 版权声明:本文为博主原创文章,遵循CC 4.0 by-sa ...

  2. Java SE教程

    第0讲 开山篇 读前介绍:本文中如下文本格式是超链接,可以点击跳转 >>超链接<< 我的学习目标:基础要坚如磐石   代码要十份规范   笔记要认真详实 一.java内容介绍 ...

  3. Java SE 5.0 - SE 8 的功能增强

    Table of Contents 前言 Java 5.0 Generics Enhanced for Loop Autoboxing Typesafe Enums Varargs Static Im ...

  4. java书籍推荐:《Java SE 6 技術手册》

    Java SE 6 技術手册 或  Java SE 6 技術手册 Java SE 6 技術手册 為什麼選擇用 Markdown?仅仅是單純把文件又一次排版太無聊了,不如趁這個機會學些新東西.所以我就藉 ...

  5. Java SE —— 专栏总集篇

    前言: Java 语言,是相对于其他语言而言,门槛低,而且功能还强大的一门编程语言,本人十分看好这一门语言,但是,它也是有深度的,看过本人的<数据结构与算法>专栏的同学们有福了,因为本人在 ...

  6. Java复习总结(二)Java SE 面试题

    Java SE基础知识 目录 Java SE 1. 请你谈谈Java中是如何支持正则表达式操作的? 2. 请你简单描述一下正则表达式及其用途. 3. 请你比较一下Java和JavaSciprt? 4. ...

  7. Java SE 9 模块化示例

    Java SE 9 模块化示例 作者:Grey 原文地址:Java SE 9 模块化示例 说明 Java SE 9引入了模块系统,模块就是代码和数据的封装体.模块的代码被组织成多个包,每个包中包含Ja ...

  8. Java SE 15 新增特性

    Java SE 15 新增特性 作者:Grey 原文地址:Java SE 15 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  9. Java SE 16 新增特性

    Java SE 16 新增特性 作者:Grey 原文地址:Java SE 16 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  10. JAVA的反射理解

    1----------------------------反射的概念----------------------------------------------- JAVA的反射机制是在运行状态中,对 ...

随机推荐

  1. Django 基础(二)

    cookie和session from django.shortcuts import render from django.http import HttpResponse # Create you ...

  2. oracle 白名单作用及配置教程

    出于提高数据安全性等目地,我们可能想要对oracle的访问进行限制,允许一些IP连接数据库或拒绝一些IP访问数据库. 当然使用iptables也能达到限制的目地,但是从监听端口变更限制仍可生效.只针对 ...

  3. linux 获取文件名

    https://blog.csdn.net/liuyuedechuchu/article/details/123778605

  4. Python使用socks代理

    Python使用socks代理 参考:How to make python Requests work via socks proxy - Stack Overflow 使用pysocks库 - Ji ...

  5. C语言转义序列

    转义序列 含义 \a 报警(ANSIC) \b 退格 \f 换页 \n 换行 \r 回车 \t 水平制表符 \v 垂直制表符 \\ 反斜杆\ \' 单引号 \" 双引号 \? 问号 \0oo ...

  6. 9.22 2020 实验 3:Mininet 实验——测量路径的损耗率

    一.实验目的 在实验 2 的基础上进一步熟悉 Mininet 自定义拓扑脚本,以及与损耗率相关的设定:初步了解 Mininet 安装时自带的 POX 控制器脚本编写,测试路径损耗率.   二.实验任务 ...

  7. 使用python来搭建一个简易的文件下载环境以及用droopy来实现一个文件上传环境

    ubuntu在安装的时候一般都是自带python环境的,大家可以查看一下查看demo如下 用于共享的命令很简单python2: python -m SimpleHTTPServer 8888pytho ...

  8. SQLServer自带备份优劣分析

    众所周知, SQL Server自身的"维护计划"可以实现自动备份数据库. 既然这样,那还有必要使用第三方专业备份软件吗? 本文以[护卫神·好备份专业版]为例,分析两者之间的优劣. ...

  9. 【LeetCode回溯算法#10】图解N皇后问题(即回溯算法在二维数组中的应用)

    N皇后 力扣题目链接(opens new window) n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击. 给你一个整数 n ,返回所有不同的 n 皇 ...

  10. ECharts连接数据库的具体实现

    相关描述 我们由之前的实例可以得知,要是不连接数据库的话,只是需要套用一下ECharts的相关模板即可,这部分内容我在前几篇中已经叙述过了: 现在,我们需要实现的是,将数据库里面的数据导入到web网页 ...