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. java注解-最通俗易懂的讲解

    来源:秒懂,Java 注解 (Annotation)你可以这样学 Annotation 中文译过来就是注解.标释的意思,在 Java 中注解是一个很重要的知识点,但经常还是有点让新手不容易理解. 我个 ...

  2. 【Unity】Timeline探索记(4)第二个例子——动作特写/子弹时间

    写在前面 这次例子参考这篇实现博文(附带项目下载),博文前面介绍非常具体,可惜后面特写轨实现代码不是按照我想要的标准四大件(data.mixer.clip.track)来组织的,所以这里我略过介绍,只 ...

  3. 版本不兼容(NoSuchMethodError: com.baomidou.mybatisplus.core.toolkit.StringUtils.isNotBlank)

    "C:\Program Files\Java\jdk1.8.0_221\bin\java.exe" -XX:TieredStopAtLevel=1 -noverify -Dspri ...

  4. YY播放器源码解析

    YY播放器使用Flutter编写的一个聚合播放器, 起因是看了 ZY-Player的源码, 发现实现挺有意思的, 也比较简单 地址: https://github.com/waifu-project/ ...

  5. JavaScript ES6 类和对象 简单记录

    一/*1.在ES6之前如果定义一个类?通过构造函数来定义一个类*/ function Person(myName, myAge) { // 实例属性 // this.name = "lnj& ...

  6. K8S—dashboard ui部署

    一.Dashboard UI概述 仪表板是基于Web的Kubernetes用户界面.您可以使用仪表板将容器化应用程序部署到Kubernetes集群,对容器化应用程序进行故障排除,并管理集群本身及其伴随 ...

  7. 获取UndeclaredThrowableException异常信息

    一.堆栈错误信息如下,要获取红框里的message 说明:ValidationException为自定义异常,继承自Exception 二.代码如下

  8. git 更换远程连接

    原来的git仓库不可用,更换远程仓库 查看远程仓库地址 git remote -v 删除远程仓库 git remote rm origin 添加远程仓库地址 git remote add origin ...

  9. Oracle coalesce函数 用于选取不为空的字段值

    coalesce(A,B)  若A为空则值为B 主流数据库系统都支持COALESCE()函数,这个函数主要用来进行空值处理,其参数格式如下: COALESCE ( expression,value1, ...

  10. Android笔记--添加联系人

    添加联系人(将联系人信息添加到手机的通讯录里面) 方式一:使用ContentResolver方法写入对象,每次一个字段 新创建一个需要加入通讯录的对象(我这里写的比较简单,并不是通讯录的标准格式,就是 ...